Created
February 9, 2022 08:36
-
-
Save dylansalim3/da1594775ff4835bd4076fef74b0d9dd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Date; | |
public class XirrEngine { | |
// settings that can be changes | |
private final double TOLERATE_RANGE = 0.001; // the smaller the amount, the more accurate. Use to check how near the result to equal 0 | |
private final double GUESING_RATE = 0.1; // the bigger the amount, the more loop to try to calculate XIRR. Assume min ROI is 0.1 | |
private final double DEFAULT_XIRR = -99999; | |
private final double DEFAULT_UNDEFINED_DOUBLE = 1e+100; | |
private final double DAY_IN_YEAR = 365.0; // XIRR use default 365 in a year. No leap year consideration | |
public double calculateXirr(double[] cashFlow, Date[] cashFlowDate) { | |
double xirr = DEFAULT_XIRR; | |
// min 2 cashflow required for calculation | |
if (cashFlow == null || cashFlowDate == null || cashFlow.length < 2 || cashFlowDate.length < 2) { | |
return xirr; | |
} | |
try { | |
// calculate XIRR and deannualise the rate if not 1 year | |
xirr = repeatGuessing(cashFlow, cashFlowDate); | |
System.out.println("xirr is " + xirr); | |
double xirrPeriod = dateDiff(cashFlowDate[(cashFlowDate.length - 1)], cashFlowDate[0]); | |
System.out.println("xirr period " + xirrPeriod); | |
if (xirrPeriod < DAY_IN_YEAR) { | |
xirr = deAnnualiseXirr(xirr, xirrPeriod); | |
} | |
} catch (Exception ex) { | |
// throw error | |
} | |
return xirr; | |
} | |
private double repeatGuessing(double[] cashflow, Date[] days) { | |
double rate = GUESING_RATE; | |
double newRate = 0.0; | |
double err = DEFAULT_UNDEFINED_DOUBLE; | |
int i = 1; | |
while (err > TOLERATE_RANGE) { | |
double totalXirrFutureValue = calculateTotalXirrFutureValue(cashflow, days, rate); | |
double totalXirrPresentValue = calculateTotalXirrPresentValue(cashflow, days, rate); | |
newRate = rate - totalXirrFutureValue / totalXirrPresentValue; | |
err = Math.abs(newRate - rate); | |
System.out.println("i is " + i); | |
System.out.println("totalXirrPresentValue is " + totalXirrPresentValue); | |
System.out.println("totalXirrFutureValue " + totalXirrFutureValue); | |
System.out.println("rate is " + rate); | |
System.out.println("new rate is " + newRate); | |
System.out.println("number of loop " + i); | |
System.out.println("err is " + err); | |
System.out.println(); | |
rate = newRate; | |
i++; | |
} | |
// System.out.println("number of loop " + i); | |
return rate; | |
} | |
private double calculateTotalXirrFutureValue(double[] cashflow, Date[] days, double rate) { | |
double total = 0.0; | |
for (int i = 0; i < cashflow.length; i++) { | |
total += calculateXirrFutureValue(cashflow[i], days[i], days[0], rate); | |
} | |
return total; | |
} | |
private double calculateTotalXirrPresentValue(double[] cashflow, Date[] days, double rate) { | |
double total = 0.0; | |
for (int i = 0; i < cashflow.length; i++) { | |
double tempValue = calculateXirrPresentValue(cashflow[i], days[i], days[0], rate); | |
total += tempValue; | |
} | |
return total; | |
} | |
private double calculateXirrFutureValue(double cashflow, Date endDate, Date startDate, double rate) { | |
System.out.println((1.0 + rate)); | |
System.out.println((dateDiff(startDate, endDate) / DAY_IN_YEAR)); | |
return cashflow * Math.pow((1.0 + rate), (dateDiff(startDate, endDate) / DAY_IN_YEAR)); | |
} | |
private double calculateXirrPresentValue(double cashflow, Date endDate, Date startDate, double x) { | |
return (1.0 / DAY_IN_YEAR) * dateDiff(startDate, endDate) * cashflow * Math.pow((x + 1.0), ((dateDiff(startDate, endDate) / DAY_IN_YEAR) - 1.0)); | |
} | |
private double dateDiff(Date dateEnd, Date dateStart) { | |
return (dateEnd.getTime() - dateStart.getTime()) / (24 * 60 * 60 * 1000); // use time to calculate for accuracy. Future, cut off time and etc may affect | |
} | |
private double deAnnualiseXirr(double xirr, double xirrPeriod) { | |
return Math.pow((1 + xirr), (xirrPeriod / DAY_IN_YEAR)) - 1; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment