Dates of coupon payments of a bond's portfolio - Excel - vba

I'm a little bit newbie about excel-vba but I am trying to construct a function that gives me the dates of coupon payments for a bond.
Giving a simple example I have this two bonds:
Nominal value: 100;300
Coupon rate: 0,06 ; 0,05
CPN_Freq:
4;
2
Months:
3; 6
Settlement:
01-11-2001;
01-11-2001
Maturity:
15-12-2003;
15-05-2005
Basis: 0;1
And what I want is the date of the coupon payments for each bond, for the first it will be:
15-12-2003 15-09-2003 15-06-2003 15-03-2003 15-12-2002 15-09-2002 15-06-2002 15-03-2002 15-12-2001 01-11-2001 (each in a cell)
I made this code but is not working.
Function CouponDate( Maturity As Date, Settlement As Date, Months
As Date)
For i= Maturity - Months
If Maturity - Months > Settlement
CouponDate = i - Months
Else
CouponDate= Settlement
End if
End Function
Could u give me a help please? Thanks :)

You cannot use a function because you have multiple return values.
You can use a Sub (without return value) that then writes the return values directly in the desired cells. That's what the additional parameter FirstOutputCell is for: it defines the first cell that should be written.
Public Sub CouponDate(Maturity As Date, Settlement As Date, Months As Long, ByRef FirstOutputCell As Range)
Dim i As Long
Dim CouponDate As Date
i = 0
Do
CouponDate = DateAdd("m", -i * Months, Maturity)
If CouponDate <= Settlement Then
CouponDate = Settlement
End If
FirstOutputCell.Offset(i, 0) = CouponDate
i = i + 1
Loop While CouponDate > Settlement
End Sub
If you have Maturity, Settlement and Months in the cells B1, B2 and B3 respectively a call of CouponDate Range("B1"), Range("B2"), Range("B3"), Range("B4") would fill the cells B4 through B7 with the coupon dates (if you want the output in columns not rows simple switch the parameters of the Offset() function).
Of course you can also call the function directly specifying the parameters: CouponDate #5/30/2003#, #11/1/2001#, 3, Range("B4")
Make sure the first two parameters are of type Date. Passing the string "15-12-2003" might work, depending on the locale that is set for your OS. Better is to either use an Excel cell formatted as date or - as shown above - date literals of the form #m/d/yyyy#.
The above code will work correctly even for maturity dates at the last day of month. If you want to change the code to be more flexible so that you can specify the days between two coupons you would also have to take into account the appropriate day count convention, making this a whole lot more complicated.

Related

User-defined function calculating production growth with variable start dates and growth profile

Struggling with an excel user defined function to calculate a total production generated by different bacteria patches starting at different times but following the same growth pattern; I have tried to simplify my problem below:
Example tables
PROFILE table: I have a 2 columns x 6 rows Table (A2:B9) showing a bacteria production per week depending on the age of the colony (column A gives the period, column B the production: in the first 3 weeks the bacteria produce 1/week in the next 3 weeks they produce 2 per week etc); I call this table my production PROFILE, this may vary depending on the type of bacteria I have (and other environmental parameters). I have decided in this particular example, and to keep it simple to show the population growth per period in a table, the growth values could of course be generated by a function (linear, exponential, with a decay factor etc) I guess that if I can crack the problem, adding a level of complexity with a growth function shouldn't be an issue.
RESULT table: I then have a 3 columns 20 rows table (A14:C33) which shows over a 20 week period (my 20 rows, numbered 1 to 20 in column A) when I start some bacteria cultures (1 in week 3, 2 in week 6 etc), I call it my RESULT table
I'd like to show in column C of the RESULT the total production of the colonies for each week.
I tried creating a PROD(week, PROFILE) function where I defined both "week" and "PROFILE" as variants and where "PROFILE" actually relates to my PROFILE table. It works fine when "week" is an individual cells (ie PROD(A18,PROFILE)=2) but doesn't work with ranges (PROD(A14:A33,PROFILE) returns an error message)
Function PROD(period As Variant, profile As Variant) As Variant
r = profile.Rows.Count
If profile(1, 1) >= period Then
PROD = profile(1, 2)
Else
For i = 2 To r
If profile(i, 1) >= period Then
If profile(i - 1, 1) < period Then
PROD = profile(i, 2)
End If
End If
Next i
End If
PROD = Application.Round(PROD, 2)
End Function
is there an elegant solution to populate column C of RESULT?
I did a similar thing on a previous assignment (finance) combining a sumproduct with the excel pmt function ( -pmt(rate, nper, pv,..) where pv was a range) and this did work, I managed to get a nice calculation of my total depreciation cost on a given period when I could have had items purchased in different quantities and at variable prices over the previous periods. the formula I used back then, shown on the attached Example of DepTable&Formula is
SUMPRODUCT(-PMT($C$7,$C6,$C$3:$V$3),N($C$4:$V$4<=C$2),N(($C$4:$V$4+$C6)>C$2))
I tried to replicate it here with a custom function with my bacteria population but I am really stuck.
I can't quite imagine what your profile table might look like nor, in fact, what exactly you want to extract. Perhaps my attempt below gives you some ideas. Please study it and let me know where it needs improvement.
Function PROD(period As Variant, profile As Variant) As Variant
Dim Fun As Variant
Dim R As Long
R = 1 ' should this always be 1?
Do
Fun = Fun + profile(R, 2).Value
R = R + 1
If R > profile.Rows.Count Then Exit Do
Loop While profile(R, 1) >= period
PROD = Application.Round(Fun, 2)
End Function
I wouldn't use this as a UDF, however. Instead, I would modify it slightly to write directly into the C column. Let the function be called from the Worksheet_Change event, linked to the profile table, or perhaps both tables, so that the data update automatically whenever there is a change. With the UDF updating will take place only when the sheet is recalculated, and I found that not to be easily controlled.
If this isn't what you want then it should be a lot nearer to it than what I did this morning. Take a look.
Function PROD(Period As Range) As Single
Dim Fun As Single
Dim Periods As Integer
Dim Weeks As Integer
Dim A As Integer
Dim R As Long
Periods = Period.Cells.Count
For A = 1 To Periods
Weeks = CInt(Period.Cells(A).Value)
With Range("Profile")
For R = 1 To (.Rows.Count - 1)
If .Cells(R, 1).Value >= Weeks Then Exit For
Next R
Fun = Fun + .Cells(R, 2).Value
End With
Next A
PROD = Application.Round(Fun / Periods, 2)
End Function
Call this function with =Prod(A14:A20) or =Prod(A14:A14) or = Prod(A14)
It will extract one result from the Profile table for each of the specified weeks. A14:A20 will have 7 results, and so would A16:A22. The function draws an average. So, with 7 results, these seven are added up and divided by 7. Perhaps this isn't yet what you want, but you might be able to manipulate the result of the function to meet your requirements.

Iterate over range of dates with values by period, how to use as formula

I have few start/end dates, few prices per month, and table with dates and course for that dates one per month, so i need to iterate over course values depending on how long period between two dates, I think alot about standard formulas nothing is matches, so I try to put my PHP knowledge, learn suntax and write my first vba code, I place it here:
http://pastebin.com/XXLKvdA4
excel data mean table of contents looks like this:
[startDate]| [endDate] |[inMonth]| [totalByCourse]
11.01.2010 | 20.02.2011 | 200 | =ConvertCourse( A1, B1, C1, myRange )
15.05.2010 | 25.03.2011 | 400 | =ConvertCourse( A2, B2, C2, myRange )
... so on for about 100 times
# [date] |[course]
1 30.08.2010 | 5
...
5 30.12.2010 | 18
6 10.01.2011 | 2
...
10 10.05.2011 | 6
... so on for about 20 times
The range of date | course presented to macro is named as myRange it isn't begins from first month of year and not ends as last month of year so it make some lot's complexity to calc algorithm, and made me to write some bruteforce loops, thats why code have many if check statenements.
Excel compiler crushes at first line of ConvertCourse function call and tell nothing more than the comments is may appear etc, its also write strange output directly to module like
ActiveCell.FormulaR1C1 = _
"=ConvertCourse(R[-1]C[-6],RC[-5],R[-1]C[-3],CourseRange)"
Range("K3").Select
I just want to use it as formula withour output to modules with functions, whats wrong with call and how to do it right?
The function takes the [totalByCourse] / 30 and multiplies it by the [Course] cost for each month between two dates while adjusting the cost for a short month.
I recommend replicating the function with built-in Excel WorkSheet functions. I could not do it without complete sample data and the actual expected results to compare the output.
Two new named ranges are hard coded into the formula.
CourseDates =OFFSET( Sheet1!$H$1,1,0,COUNTA(Sheet1!$H:$H)-1,1)
Courses =OFFSET(Sheet1!$I$1,1,0,COUNTA(Sheet1!$I:$I)-1,1)
Formula
SUMPRODUCT(--(CourseDates>=--"2011-07-20"),--(CourseDates<=--"2011-8-20"),Courses*DailyCost)
Function ConvertCourse(StartDate As Date, EndDate As Date, pSumMonthly As Double) As Double
Const dLength As Integer = 30
Dim x As Long
Dim periodInDays As Integer, DailyCost As Double, result As Double
Dim NewEndDate As Date
DailyCost = pSumMonthly / dLength
periodInDays = DateDiff("d", StartDate, EndDate)
NewEndDate = StartDate + Int(periodInDays / dLength) * 30
result = getCourseTotal(StartDate, NewEndDate, DailyCost)
If NewEndDate < EndDate Then
result = result + (EndDate - NewEndDate) * DailyCost * getCourseTotal(StartDate, NewEndDate, 1)
End If
ConvertCourse = result
End Function
'SUMPRODUCT(--(CourseDates>=--"2011-07-20"),--(CourseDates<=--"2011-8-20"),Courses*130)
Function getCourseTotal(StartDate As Date, EndDate As Date, DailyCost As Double)
Dim Formula As String
Formula = "=SUMPRODUCT(--(CourseDates>=--" & Format(StartDate, "\""YYYY-MM-DD\""") & _
"),--(CourseDates<=--" & Format(EndDate, "\""YYYY-MM-DD\""") & "),Courses)"
getCourseTotal = Evaluate(Formula) * DailyCost
End Function

VBA-Excel return the latest date

I am a newbie to using VBA to program functions in Excel (just started this morning actually). Here is my problem including my almost but non-working solution.
Problem outline:
I have 2 columns of data, a userID and a Date column. I want to create a function that returns the maximum date (latest) for a userID. A userID may appear once, or many times (number varies from user to user, but doesn't exceed 20).
Now to make this problem a easier for my little brain, I pre-sort the userIDs to appear one after another, and then created a third column to mark the first instance of a userID (not necessarily the one with the earliest/latest date) using a simple excel If statement: If its the first instance it is the userID, else it is empty.
Then I created a function that takes in a range of cells (userIDs), and a single other value (the third column cell containing a single value of userID)
A typical excel worksheet will look like this:
userID Date First Instance
1 3/3/12 1
1 3/2/11
1 2/3/14
2 2
2 3/4/15
2
2 5/6/15
3 5/5/16 3
4 4/4/14 4
4
4 6/7/08
...
I wrote the function below, which takes in userID as the iRange (I select more than 20 so that for other users which may have 20 instances of userID are also included), and idCell as the single identifier of the first instance of userId.
The logic behind the code is:
Loop through iRange and compare it to idCell
If it's equal, check that the next cell (next to the element from iRange is a date) and if it is
Check whether it is larger than the maxDate, store it if true
At the end I return maxDate
The result:
I get a number back, but it isn't a date and I don't think it has any bearing on a date.
Function returnDate2(iRange As Range, idCell As Range) As Date
Dim elem As Variant
Dim i As Long
Dim maxDate As Date
For Each elem In iRange
If elem = idCell Then
If IsDate(elem.Offset(0, 1)) Then
If elem.Offset(0, 1) > maxDate Then
maxDate = DateValue(elem.Offset(0, 1))
End If
returnDate2 = maxDate
End If
End If
Next elem
End Function
Thanks in advance!

Translating results by day to results by week with excel vba

In my excel sheet I have an varying number of dates in column A with associated totals for each date in column B. The dates are somewhat random but I want to find the sum of the associated totals per week. I'm new to vba and I'm having a bit of trouble figuring out how to calculate the totals per week.
The psudo code for what I'm thinking is:
buttonClicked()
Dim sum As Integer, tempDate As Date, current As Date, i As Integer, j As Integer, match As Boolean
sum = 0
tempDate = Range("A1").Value
current = tempDate
For i = 1 To Rows.Count
for j = 0 to 7
tempDate = addDate(d, i, tempDate)
If cell(i,1).Value = tempDate Then sum = sum + cell(i, 2).Value
match = true
Break
End If
Next j
If match = true Then tempDate = current
Else
`next open space in column D is current
current = tempDate
`next open space in column E is sum
sum = 0
Next i
end
Please let me know if there's any better way to solve this problem other than iterating through the entire list and counting 7 days ahead. I should note that A1 is already assumed to be a sunday and I'm not sure how the commented out lines should be implemented. I would be very grateful for any advice at all.
In other column assign the corresponding Year.WeekNumber to each row using the following formula:
=CONCATENATE(YEAR(A1),".",TEXT(WEEKNUM(A1),"00"))
The totals per Year.Week can be obtained using a SUMIF formula or via a PivotTable. The PivotTable can be auto resize if you use a Dynamic Range as DataSource
If you are looking for a series of 7 row sums from column B, then this formula using native worksheet functions should do.
=SUM(INDEX(B:B, (ROW(1:1)-1)*7+1):INDEX(B:B, (ROW(1:1)-1)*7+7))
Fill down to catch successive week periods.
      
If you can supply a starting date (e.g. =MIN(A:A)) and add 7 to it in successive rows, then the weekly starting date could be a reference point for a SUMIFS function. In E9 as,
=SUMIFS(B:B,A:A, ">="&D9,A:A, "<"&D9+7)
Fill down as necessary.
If a VBA solution is necessary, that logic can be brought into a VBA sub or function.

PivotCache Refresh - Cell formula return #Value

I have a pivot table created from external data source(access) that contain number of records for each hour on a weekly basis for an average period of 3 months. Just beside this pivot table, I've created a column that calculates the average number record for each hour for a day. (A week here is just 5 days - sat & sun don't count).
To achieve the above, I have created a UDF that counts the number of weeks from the pivot data field (Week_Ending). To ensure the code don't count ghost or non-existent weeks, I have used the pivotcache refresh in the UDF. This works perfectly, except it now gives #value (A value used in the formula is the wrong data type) in the cells where I expect the daily average. I have found no issue with the cell formula and works if a single week is selected from the field "Week_Ending" instead of ALL.
I have attached the code and cell function and an image of the issue.
Cells formula in Cell E6 and it is similar upto cell E29 (Hourly cell reference is incremented by 1 for each cell)
=IF($E$4=1,GETPIVOTDATA("CountOfCase_Id",$A$4,"HOURLY",A6)/5,GETPIVOTDATA("CountOfCase_Id",$A$4,"HOURLY",A6)/($E$4*5))
Vba UDF function
Option Explicit
Function WeekCount(InputVal As Variant) As Integer
Dim book1 As String, PivotName As String
book1 = ThisWorkbook.Name
With Workbooks(book1).ActiveSheet
If InputVal = "(All)" Then
PivotName = .PivotTables(1).Name
.PivotTables(PivotName).PivotCache.MissingItemsLimit = xlMissingItemsNone
.PivotTables(PivotName).PivotCache.Refresh
WeekCount = .PivotTables(PivotName).PivotFields("WEEK_ENDING").PivotItems.Count
Else
WeekCount = 1
End If
End With
End Function
I appreciate any help. Excel version is 2003.
The problem turned out to be with
.PivotTables(PivotName).PivotCache.Refresh
in the UDF and not fixed with Application.Volatile. However #GSerg’s solution without p.update seems to have worked.