Skip to content

Instantly share code, notes, and snippets.

@dylansalim3
Created February 9, 2022 08:36
Show Gist options
  • Save dylansalim3/da1594775ff4835bd4076fef74b0d9dd to your computer and use it in GitHub Desktop.
Save dylansalim3/da1594775ff4835bd4076fef74b0d9dd to your computer and use it in GitHub Desktop.
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