how to calculate XIRR dynamically in excel and in google sheets - excel
I am trying to calculate XIRR for data that I have in the below format
PurchaseDate Script No.ofunits PurchNAVrate NetAmount ForXIRR TotalReturn
17/11/2014 A 2241 33 75000 -75000 96000
8/1/2015 B 53 649 35000 -35000 43000
14/1/2015 B 75 658 50000 -50000 61500
14/10/2014 C 2319 32 75000 -75000 108000
8/1/2015 D 318 109 35000 -35000 40000
14/1/2015 D 450 110 50000 -50000 57000
8/6/2015 D 175 114 20000 -20000 22000
I tried lots of different permutations and combinations but not able to calculate XIRR for each Fund. Is there a way I can do it perhaps using offset or search.
This is a dynamic list which keeps getting changed based on new funds that are bought or sold
Values for Fund A should be around 14%
Values for Fund B should be around 13%
Values for Fund C should be around 21%
Values for Fund D should be around 8%
Update
Given that solution mentioned by #xor-lx was working very nicely in ms excel but not in google sheets, I have now modified the structure of my data based on #kyle advice and now I can calculate XIRR easily for each fund as a whole. Refer below
PurchaseDate Today Script No.ofunits PurchNAVrate NetAmount ForXIRR TotalReturn XIRR
17/11/14 31/08/2016 A 2241 33 75000 -75000 96000 "=XIRR(G2:H2,A2:B2)"
Total for above fund "=XIRR(G2:H3,A2:B3)"
8/1/2015 31/08/2016 B 53 649 35000 -35000 43000 "=XIRR(G4:H4,A4:B4)"
14/1/2015 31/08/2016 B 75 658 50000 -50000 61500 "=XIRR(G5:H5,A5:B5)"
Total for above fund "=XIRR(G4:H6,A4:B6)"
14 Oct 14 31/08/2016 C 2319 32 75000 -75000 108000 "=XIRR(G7:H7,A7:B7)"
Total for above fund "=XIRR(G7:H8,A7:B8)"
8 Jan 15 31/08/2016 D 318 109 35000 -35000 40000 "=XIRR(G9:H9,A9:B9)"
14 Jan 15 31/08/2016 D 450 110 50000 -50000 57000 "=XIRR(G10:H10,A10:B10)"
8/6/2015 31/08/2016 D 175 114 20000 -20000 22000 "=XIRR(G11:H11,A11:B11)"
Total for above fund "=XIRR(G9:H12,A9:B12)"
Assuming your table is in A1:G8 (with headers in row 1), and that your Fund of choice, e.g. "B", is in J2, array formula**:
=XIRR(INDEX(F:G,N(IF(1,MODE.MULT(IF(B$2:B$8=J2,{1,1}*ROW(B$2:B$8))))),N(IF(1,{1,2}))),CHOOSE({1,2},INDEX(A:A,N(IF(1,MODE.MULT(IF(B$2:B$8=J2,{1,1}*ROW(B$2:B$8)))))),TODAY()))
Copy down to give similar results for Funds in J3, J4, etc.
I tend to prefer this to set-ups involving OFFSET; not only is it briefer (and therefore more efficient), but also, more importantly, non-volatile.
See here for more if you're interested:
https://excelxor.com/2016/02/16/criteria-with-statistical-functions-growth-linest-logest-trend/
Regards
**Array formulas are not entered in the same way as 'standard' formulas. Instead of pressing just ENTER, you first hold down CTRL and SHIFT, and only then press ENTER. If you've done it correctly, you'll notice Excel puts curly brackets {} around the formula (though do not attempt to manually insert these yourself).
XIRR requires two arrays of arguments -- values and dates (with an optional third "guess" argument). To try to stitch together those arrays from non-contiguous ranges of purchase dates, total returns, purchase amounts, and a number of "today's dates" would be quite a challenge for an Excel formula. Hence I provide a User Defined Function written in VBA.
The arguments for the function are the fund or script (sFund) and your data table range laid out as you have in your example above.
As suggested in one of your comments, I used TODAY as the date corresponding to the Total Return column. Easily changed if that is not the case.
Also, note that I did not use the "forXIRR" column, rather I just used the negative of the NetAmount column. If you delete the forXIRR column, you will need to change the reference to .TotalReturn
I created a custom Class in order to make things a bit more understandable.
After you insert the Class Module, you must rename it cFUND
Class Module
Option Explicit
'Rename cFUND
Private pFund As String
Private pPurchDt As Date
Private pPurchDts As Collection
Private pPurchAmt As Double
Private pPurchAmts As Collection
Private pTotalReturn As Double
Private pTotalReturns As Collection
Public Property Get Fund() As String
Fund = pFund
End Property
Public Property Let Fund(Value As String)
pFund = Value
End Property
Public Property Get PurchDt() As Date
PurchDt = pPurchDt
End Property
Public Property Let PurchDt(Value As Date)
pPurchDt = Value
End Property
Public Function ADDPurchDt(Value As Date)
pPurchDts.Add Value
End Function
Public Property Get PurchDts() As Collection
Set PurchDts = pPurchDts
End Property
Public Property Get PurchAmt() As Double
PurchAmt = pPurchAmt
End Property
Public Property Let PurchAmt(Value As Double)
pPurchAmt = Value
End Property
Public Function ADDPurchAmt(Value As Double)
pPurchAmts.Add Value
End Function
Public Property Get PurchAmts() As Collection
Set PurchAmts = pPurchAmts
End Property
Public Property Get TotalReturn() As Double
TotalReturn = pTotalReturn
End Property
Public Property Let TotalReturn(Value As Double)
pTotalReturn = Value
End Property
Public Function ADDTotalReturn(Value As Double)
pTotalReturns.Add Value
End Function
Public Property Get TotalReturns() As Collection
Set TotalReturns = pTotalReturns
End Property
Public Function CalcXIRR()
Dim v, d, w
Dim I As Long
With Me
ReDim d(.PurchDts.Count * 2 - 1)
ReDim v(UBound(d))
I = 0
For Each w In .PurchAmts
v(I) = w
I = I + 1
Next w
For Each w In .TotalReturns
v(I) = w
I = I + 1
Next w
I = 0
For Each w In .PurchDts
d(I) = w
I = I + 1
Next w
Do Until I > UBound(d)
d(I) = Date
I = I + 1
Loop
End With
CalcXIRR = WorksheetFunction.XIRR(v, d)
End Function
Private Sub Class_Initialize()
Set pPurchDts = New Collection
Set pPurchAmts = New Collection
Set pTotalReturns = New Collection
End Sub
Regular Module
Option Explicit
Function fundXIRR(sFund As String, rTable As Range) As Double
Dim vSrc As Variant
Dim cF As cFUND
Dim I As Long
vSrc = rTable
Set cF = New cFUND
For I = 2 To UBound(vSrc, 1)
If vSrc(I, 2) = sFund Then
With cF
.PurchDt = vSrc(I, 1)
.ADDPurchDt .PurchDt
.Fund = vSrc(I, 2)
.PurchAmt = -vSrc(I, 5)
.ADDPurchAmt .PurchAmt
.TotalReturn = vSrc(I, 7)
.ADDTotalReturn .TotalReturn
End With
End If
Next I
fundXIRR = cF.CalcXIRR
End Function
Given your data above, XIRR for B would be calculated as:
XIRR({-35000;-50000;43000;61500},{"1/8/2015";"1/14/2015";"8/29/2016";"8/29/2016"}
Here are the results for your data above:
I will assume based on your table that the date is column A, the fund (or "Script") is column B and the "ForXIRR" is column F. The below will do it for you:
{=XIRR(IF(B:B="A",F:F,0),IF(B:B="A",A:A,0))}
Make sure you enter this with Shift + Ctrl + Enter since it is an array formula.
Note: The above will calculate for "A", but substitute that part of the formula for any other fund, or use a cell reference to calculate for others.
EDIT:
I realized the above will only work for the first letter in the sequence, please use below, and again, adjust the "B" to whatever you need. This will work for any of the letters present. Also, adjust all the ranges to match your needs, I only did rows 1:10 for my test purposes. The below adjusts the start of the range so it is always the first value for whichever fund is input (ie "A", "B", "C") to allow this to calculate. Without this change, the above will always start with a zero unless it is the first in the list ("A" in the OPs example), and XIRR only calculates with a negative number as the first value.
=XIRR(IF(OFFSET($B$1:$B$10,MATCH("B",$B$1:$B$10,0)-1,0,ROWS($B$1:$B$10)-MATCH("B",$B$1:$B$10,0)+1,1)="B",OFFSET($F$1:$F$10,MATCH("B",$B$1:$B$10,0)-1,0,ROWS($B$1:$B$10)-MATCH("B",$B$1:$B$10,0)+1,1),0),IF(OFFSET($B$1:$B$10,MATCH("B",B1:B10,0)-1,0,ROWS($B$1:$B$10)-MATCH("B",$B$1:$B$10,0)+1,1)="B",OFFSET($A$1:$A$10,MATCH("B",$B$1:$B$10,0)-1,0,ROWS($B$1:$B$10)-MATCH("B",$B$1:$B$10,0)+1,1),0))
Related
Find value in column where running total is equal to a certain percentage
In Excel I have a list of values (in random order), where I want to figure out which values that comprise 75% of the total value; i.e. if adding the largest values together, which ones should I include in order to get to 75% of the total (largest to smallest). I would like to find the "cut-off value", i.e. the smallest number to include in the group of values (that combined sum up to 75%). However I want to do this without first sorting my data. Consider below example, here we can see that the cutoff is at "Company 6", which corresponds to a "cut-off value" of 750. The data I have is not sorted, hence I just want to figure out what the "cut-off value" should be, because then I know that if the amount in the row is above that number, it is part of group of values that constitute 75% of the total. The answer can be either Excel or VBA; but I want to avoid having to sort my table first, and I want to avoid having a calculation in each row (so ideally a single formula that can calculate it). Row number Amount Percentage Running Total Company 1 1,000 12.9% 12.9% Company 2 950 12.3% 25.2% Company 3 900 11.6% 36.8% Company 4 850 11.0% 47.7% Company 5 800 10.3% 58.1% Company 6 750 9.7% 67.7% Company 7 700 9.0% 76.8% Company 8 650 8.4% 85.2% Company 9 600 7.7% 92.9% Company 10 550 7.1% 100.0% Total 7,750 75% of total 5,813 EDIT: My initial thought was to use percentile/quartile function, however that is not giving me the expected results. I have been trying to use a combination of percentrank, sort, sum and aggregate - but cannot figure out how to combine them, to get the result I need. In the example I want to include Companies 1 through 6, as that summarize to 5250, hence the smallest number to include is 750. If I add Company 7 I get above the 5813 (which is where 75% is).
VBA bubble sort - no changes to sheet. Option Explicit Sub calc75() Const PCENT = 0.75 Dim rng, ar, ix, x As Long, z As Long, cutoff As Double Dim n As Long, i As Long, a As Long, b As Long Dim t As Double, msg As String, prev As Long, bFlag As Boolean ' company and amount Set rng = Sheet1.Range("A2:B11") ar = rng.Value2 n = UBound(ar) ' calc cutoff ReDim ix(1 To n) For i = 1 To n ix(i) = i cutoff = cutoff + ar(i, 2) * PCENT Next ' bubble sort For a = 1 To n - 1 For b = a + 1 To n ' compare col B If ar(ix(b), 2) > ar(ix(a), 2) Then z = ix(a) ix(a) = ix(b) ix(b) = z End If Next Next ' result x = 1 For i = 1 To n t = t + ar(ix(i), 2) If t > cutoff And Not bFlag Then msg = msg & vbLf & String(30, "-") bFlag = True If i > 1 Then x = i - 1 End If msg = msg & vbLf & i & ") " & ar(ix(i), 1) _ & Format(ar(ix(i), 2), " 0") _ & Format(t, " 0") Next MsgBox msg, vbInformation, ar(x, 1) & " Cutoff=" & cutoff End Sub
So, set this up simply as I suggested. You can add or change the constraints as you wish to get the results you need - I chose Binary to start but you could limit to integer and to 1, 2 or 3 for example. I included the roundup() I used as well as the sumproduct. I used Binary as that gives a clear indication of the ones chosen, integer values will also do the same of course.
Smallest Value of a Running Total... =LET(Data,B2:B11,Ratio,0.75, Sorted,SORT(Data,,-1),MaxSum,SUM(Sorted)*Ratio, Scanned,SCAN(0,Sorted,LAMBDA(a,b,IF((a+b)<=MaxSum,a+b,0))), srIndex,XMATCH(0,Scanned)-1, Result,INDEX(Sorted,srIndex),Result) G2: =SORT(B2:B11,,-1) H2: =SUM(B2:B11)*0.75 I2: =SCAN(0,G2#,LAMBDA(a,b,IF((a+b)<$H$2,a+b,0))) J2: =XMATCH(0,I2#) K2: =INDEX(G2#,XMATCH(0,I2#)-1) The issue that presents itself is that there could be duplicates in the Amount column when it wouldn't be possible to determine which of them is the correct result. If the company names are unique, an accurate way would be to return the company name. =LET(rData,A2:A11,lData,B2:B11,Ratio,0.75, Data,HSTACK(rData,lData),Sorted,SORT(Data,2,-1), lSorted,TAKE(Sorted,,-1),MaxSum,SUM(lSorted)*Ratio, Scanned,SCAN(0,lSorted,LAMBDA(a,b,IF((a+b)<=MaxSum,a+b,0))), rSorted,TAKE(Sorted,,1),rIndex,XMATCH(0,Scanned)-1, Result,INDEX(rSorted,rIndex),Result) Note that you can define a name, e.g. GetCutOffCompany with the following part of the LAMBDA version of the formula: =LAMBDA(rData,lData,Ratio,LET( Data,HSTACK(rData,lData),Sorted,SORT(Data,2,-1), lSorted,TAKE(Sorted,,-1),MaxSum,SUM(lSorted)*Ratio, Scanned,SCAN(0,lSorted,LAMBDA(a,b,IF((a+b)<=MaxSum,a+b,0))), rSorted,TAKE(Sorted,,1),rIndex,XMATCH(0,Scanned)-1, Result,INDEX(rSorted,rIndex),Result)) Then you can use the name like any other Excel function anywhere in the workbook e.g.: =GetCutOffCompany(A2:A11,B2:B11,0.75)
Extract time slot based on interval value
In cell A1 I have an interval value which is an integer which can either be 1, 5 or 15 which means 1 minute, 5 minutes or 15 minutes In range "B1:B12", I have the sample data (it will be more) 12:03 12:03 12:06 12:06 12:09 12:11 12:14 12:15 12:15 12:16 12:31 12:32 Now in column C, I need to extract time slots based on number is available in cell A1. If cell A1 has 15 then the column C would contain below 12:15 12:30 because first 15th minute in column starts at 12:15 then increment it by 15 mins until last available data which falls in that 15 min range. If cell A1 has 5 then the column C would contain below 12:05 12:10 12:15 12:30 because first 5th minute starts at 12:05 then increment it by 5 mins until last available data which falls in that 15 min range. and if cell A1 has 1 then extract everything except duplicates. I hope I have explained the scenario properly. I am not able to think of a logic to do this in excel vba and need help with how to start so that I can try to apply the logic and comeup with a code. Thank you EDIT I am adding a pic of the desired result. if A1 contains 1 then copy all timestamps in column C without the duplicates if A1 contains 5 then show only 5 times of 5 min interval if A1 contains 15 then show only 3 times of 15 min interval
Using VBA (1) returning an array You will need to ctrl-shif-enter Option Explicit Function get_unique_time_interval(ByVal interval_size As Double, ByVal input_data As Range) As Variant interval_size = interval_size / 60# / 24# Dim d Set d = CreateObject("Scripting.Dictionary") Dim r As Range For Each r In input_data Dim t As Double t = Int(r.Value / interval_size) * interval_size If Not d.exists(t) Then d.Add t, t Next r get_unique_time_interval = Application.WorksheetFunction.Transpose(d.keys) End Function (2) hard-coded outputting to C1 option explicit Sub get_unique_time_interval_sub() Dim interval_size As Double: interval_size = ActiveSheet.Range("a1").Value Dim input_data As Range: Set input_data = Range(ThisWorkbook.Sheets(1).Range("b1"), ThisWorkbook.Sheets(1).Range("b1").End(xlDown)) interval_size = interval_size / 60# / 24# Dim d Set d = CreateObject("Scripting.Dictionary") Dim r As Range For Each r In input_data Dim t As Double t = Int(r.Value / interval_size) * interval_size If Not d.exists(t) Then d.Add t, t Next r Dim v As Variant Dim i As Long i = 0 If Not IsEmpty(Range("c1").Value) Then Range(Range("c1"), Range("c1").End(xlDown)).ClearContents For Each v In d.keys ThisWorkbook.Sheets(1).Range("c1").Offset(i, 0).Value = v i = i + 1 Next v End Sub
this works as a single cell formula in C1: =UNIQUE(INT(B1:B12/(A1/60/24))*(A1/60/24)) breaking it down: A1/60/24 is the time interval you want (A1 in minutes so divide by 60 mins per hour and 24 hours per day to get to Excel time interval units). Then: INT(B1:B12/(A1/60/24)) rounds you down to the start of the time period. Then: *(A1/60/24) converts you back up to the time of the interval in which the input time falls. Then: UNIQUE( pulls out the unique values. The helpful thing here is that by including B1:B12 as a range it makes everything (helpfully) spill
If you accept a formula based answer then This should work for you: A1: some inteval value. B1: =0.5+A1/(24*60) Where 0.5 mean 12:00 in Excel. In B2: =B1+$A$1/(24*60) And drag this formula down to where you need it. Exampls:
Assign values in large scale into a data base. Formula (libreOffice calc)
I am trying to create a data base in libreOffice spreed-sheet application. And what I need is, the first column to be Id's, but each Id has to fill 100 cells. So I would like to have 2000 Id's and each Id takes up 100 cells, we have 200 000 cells. (Id's values = range(1,2000)) row#1 : row#100 = Id#1 // row#101 : row#200 = Id#2 ....// row#199900 : row#200000 = Id#2000 What I simply want is to assign the value 1 to the first 100 cells in the first column, the value 2 to the next 100 cells in the same column and so on, until I have the 2000 Id's in the first column. So I would like to find a formula to achieve that with out having to select and scroll manually 2000 times the sheet. Thanks
If the ID is in A column: =QUOTIENT(ROW(A1);100)+1 The formula adds 1 to integer part of the number of row divided by 100.
Apply with a loop? Public Sub test() Dim i As Long For i = 1 To 2000 Range("A1:A100").Offset((i - 1) * 100, 0) = i Next End Sub
Auto calculate average over varying number values row by row
I have an Excel file with several columns in it and many rows. One column, say A has ID numbers. Another column, say G has prices. Column A has repeating ID numbers, however not all numbers repeat the same amount of times. Sometimes just once, other times 2, 3 or several times. Each column G for that row has a unique price. Basically, I need to average those prices for a given ID in column A. If each ID was repeated the same number of times, this would be quite simple, but because they are not I have to manually do my average calculation for each grouping. Since my spreadsheet has many many rows, this is taking forever. Here is an example (column H is the average that I am currently calculating manually): A ... G H 1 1234 3.00 3.50 2 1234 4.00 3 3456 2.25 3.98 4 3456 4.54 5 3456 5.15 11 8890 0.70 0.95 13 8890 1.20 ... So in the above example, the average price for ID# 1234 would be 3.50. Likewise, the average price for ID# 3456 would be 3.98 and for #8890 would be 0.95. NOTICE how rows are missing between row 5 and 11, and row 12 is missing too? That is because they are filtered out for some other reason. I need to exclude those hidden rows from my calculations and only calculate the average for the rows visible. Im trying to write a VBA script that will automatically calculate this, then print that average value for each ID in column H. Here is some code I have considered: Sub calcAvg() Dim rng As Range Set rng = Range("sheet1!A1:A200003") For Each Val In rng Count = 0 V = Val.Value '''V is set equal to the value within the range If Val.Value = V Then Sum = Sum + G.Value V = rng.Offset(1, 0) '''go to next row Count = Count + 1 Else '''V = Val.Value '''set value in this cell equal to the value in the next cell down. avg = Sum / Count H = avg '''Column G gets the avg value. End If Next Val End Sub I know there are some problems with the above code. Im not too familiar with VBA. Also this would print the avg on the same line everytime. Im not sure how to iterate the entire row. This seems overly complicated. Its a simple problem in theory, but the missing rows and differing number of ID# repetitions makes it more complex. If this can be done in an Excel function, that would be even better. Any thoughts or suggestions would be greatly appreciated. thanks.
If you can add another row to the top of your data (put column Headers in it) its quite simple with a formula. Formula for C2 is =IF(A2<>A1,AVERAGEIFS(B:B,A:A,A2),"") copy this down for all data rows. This applies for Excel 2007 or later. If using Excel 2003 or earlier, use AVERAGEIF instead, adjusting ranges accordingly If you can't add a header row, change the first formula (cell C1) to =AVERAGEIFS(B:B,A:A,A1)
In my way .. Sub calcAvg() Dim x, y, i, y2, t, Count, Mount As Integer Dim Seek0 As String x = 1 '--> means Col A y = 1 '--> means start - Row 1 y2 = 7 '--> means end - Row 19 For i = y To y2 If i = y Then Seek0 = Cells(i, x) t = i Count = Cells(i, x + 6) Mount = 1 Else If Cells(i, x) <> Seek0 Then Cells(t, x + 7) = Count / Mount Count = Cells(i, x + 6) Mount = 1 t = i Seek0 = Cells(i, x) Else Count = Count + Cells(i, x + 6) Mount = Mount + 1 End If End If Next End Sub Hope this helps ..
Run a simulation several times with different parameters and store values
Consider a spreadsheet which performs some computations based on a fixed value (in the rxample below, D3) and is iterative. E.g. D3: 4 B3: 12 B4: 58 (=B3*$D$3+10) B5: 242 (=B4*$D$3+10) B6: 978 (=B5*$D$3+10) Total = 1290 (=sum(B2:B5)) Now, suppose I wanted to try out different values of D3 (let's call this P) and store the different totals I get, i.e. P Total 4 1290 5 2252 6 3618 7 5460 How would I do this with Excel? A macro? Please note that the above example is a simplified version of the real thing. It should be clear that I need to compute B3-B6 so I can compute the sum. Update: Each computation requires several columns. E.g. we would use values on B3,B4, .. and C3,C4, ... .
A macro can do this. If B7 contains the sum formula, try this Sub RunSimulation() Dim p as long for p = 4 to 7 Range("D3")=p Debug.Print Range("B7") Range("L" & (p-1)) = Range("B7").Value next End Sub EDIT: added a line for storing the results, as requested. If you don't want to enter the sum formula in your sheet, you can calculate the total in VBA either: Dim total as Long Dim row as long total = 0 for row = 2 to 5 total = total + Range("B" & row) Next Debug.Print total (Use Double instead of Long for total if you are dealing with floating point numbers.)
Usually it is done in following manner: A B C D 1 x tmp1 tmp2 Total 2 3 $A2+10 $B2*10+10 $C2*$B2 The formulae are just for example. 3 $A2+1 $A3+10 $B3*10+10 $C3*$B3 4 $A3+1 $A4+10 $B4*10+10 $C4*$B4 Excel has capabilities to automatically increment indices in formulae.