raydecampo / java-xirr Goto Github PK
View Code? Open in Web Editor NEWJava implementation of xirr with bonus Newton-Raphson implementation
License: MIT License
Java implementation of xirr with bonus Newton-Raphson implementation
License: MIT License
-4.625, 2019-03-14
-4.375, 2019-03-15
-3.975, 2019-03-18
-4.350, 2019-03-19
-4.725, 2019-03-20
-5.050, 2019-03-22
-5.000, 2019-03-25
-4.750, 2019-03-26
-3.800, 2019-04-02
-3.650, 2019-04-03
-3.500, 2019-04-04
-3.350, 2019-04-05
-3.200, 2019-04-08
-3.050, 2019-04-09
-2.900, 2019-04-10
-2.800, 2019-04-11
-2.700, 2019-04-12
-2.600, 2019-04-15
45.000, 2019-04-16
causes this on the latest master commit 5cfe3c2
Exception in thread "main" java.lang.ArithmeticException: Newton-Raphson failed due to zero-valued derivative.
at org.decampo.xirr.NewtonRaphson.inverse(NewtonRaphson.java:93)
at org.decampo.xirr.NewtonRaphson.findRoot(NewtonRaphson.java:67)
at org.decampo.xirr.NewtonRaphson$Builder.findRoot(NewtonRaphson.java:147)
at org.decampo.xirr.Xirr.xirr(Xirr.java:109)
Hi, can you please tell me whether there is an easy way to import your library into an Android app?
double xirr = new org.decampo.xirr.Xirr(Arrays.asList(
new Transaction(-1750000.00, "2017-01-06"),
new Transaction(-2500000.00, "2017-01-06"),
new Transaction(-5625000000.00, "2017-01-09"),
new Transaction(-1000000.00, "2017-01-18"),
new Transaction(228159246.58, "2017-11-20"),
new Transaction(119665.77, "2017-12-22"),
new Transaction(170951.11, "2017-12-22"),
new Transaction(-1750000.00, "2018-01-08"),
new Transaction(281250000.00, "2019-01-30"),
new Transaction(680010.61, "2019-03-12"),
new Transaction(952014.86, "2019-03-12"),
new Transaction(355381.27, "2019-11-20"),
new Transaction(281250000.00, "2019-11-20"),
new Transaction(497533.00, "2019-11-20"),
new Transaction(15667873.77, "2020-03-31")
)).xirr();
Exception in thread "main" org.decampo.xirr.NonconvergenceException: Newton-Raphson failed to converge within 10000 iterations.
at org.decampo.xirr.NewtonRaphson$Calculation.solve(NewtonRaphson.java:216)
at org.decampo.xirr.NewtonRaphson.inverse(NewtonRaphson.java:89)
at org.decampo.xirr.NewtonRaphson.findRoot(NewtonRaphson.java:70)
at org.decampo.xirr.NewtonRaphson$Builder.findRoot(NewtonRaphson.java:136)
at org.decampo.xirr.Xirr.xirr(Xirr.java:155)
at test.IRRTest.main(IRRTest.java:58)
Run throws the exception, Change to 50000 iterations and still throw this exception.
When I change the bold part to new Transaction(497532.99, "2019-11-20"),, the calculation is successful.
When the bold part is changed to new Transaction(497533.00, "2019-11-21"),, the calculation is also successful.
Currently, the pom.xml
of this project adds mockito-core
as dependency. This causes two problems:
mockito-core
in projects that add java-xirr
as dependency.mockito-core
used by projects may conflict with version introduced by java-xirr
.Request to add <scope>test</scope>
for mockito-core
.
Hi @RayDeCampo, thank you for bringing this lib to the world. Are you planning to tackle the leap year problem?
Has anyone compared this implementation to the one in Excel?
Apache POI lacks an implementation of XIRR and it's possible that this code might be a good starting point for solving that - https://bz.apache.org/bugzilla/show_bug.cgi?id=60307
2019/8/29 -500000000
2019/10/20 6882638.89
2020/1/20 11947222.22
2020/4/20 11817361.11
2020/7/20 11817361.11
2020/8/29 50000000
2020/8/29 506458.33
2020/10/20 10752500
2021/1/20 10752500
2021/2/28 50000000
2021/2/28 506458.33
2021/4/20 9350000
2021/7/20 9453888.89
2021/8/30 532430.56
2021/8/30 50000000
2021/10/20 8363055.56
2022/1/20 8363055.56
2022/2/28 506458.33
2022/2/28 50000000
2022/4/20 7012500
2022/7/20 7090416.67
2022/8/29 3116666.67
2022/8/29 300000000
Convergence issue with some data. See RayDeCampo/nodejs-xirr#1
Expose the Newton-Raphson options for tolerance, iterations and initial guess to the client of the Xirr class.
From a quick look at the example it looks like you are getting "annualized" rate of return.
Do you also get "Actual Rate of Return" not annualized?
Hi, i have a use case where i know a calculated xirr value (using your library) and i know a changed / new current value. Is it possible to recalculate the xirr without knowing the rest of the transactions?
.
The loop variable in the N-R implementation is an int while the number of iterations is specified as a long. The loop variable should also be a long.
At the moment only actual over 365 is supported, but many contracts specify actual over 360. It should be possible to override the default value, and specify a custom number of days per year. I'll raise a PR and include a test case
Could not calculate negative Xirr, as below values
new Xirr(new Transaction(-10000d, "2022-01-24"), new Transaction(9800d, "2022-01-28")).xirr();
gives below error
Exception in thread "main" org.decampo.xirr.OverflowException: Candidate overflow: {guess=-1.825, iteration=159, candidate=-Infinity, value=-2.3355275185815405E7, derivative=-1.0631116397667295E-302}
at org.decampo.xirr.NewtonRaphson$Calculation.setCandidate(NewtonRaphson.java:166)
at org.decampo.xirr.NewtonRaphson$Calculation.solve(NewtonRaphson.java:213)
at org.decampo.xirr.NewtonRaphson.inverse(NewtonRaphson.java:89)
at org.decampo.xirr.NewtonRaphson.findRoot(NewtonRaphson.java:70)
at org.decampo.xirr.NewtonRaphson$Builder.findRoot(NewtonRaphson.java:136)
at org.decampo.xirr.Xirr.xirr(Xirr.java:155)
public XirrDetails combine(final XirrDetails other) {
start = start.isBefore(other.start) ? start : other.start;
end = end.isAfter(other.end) ? end : other.end;
minAmount = Math.min(minAmount, other.minAmount);
maxAmount = Math.max(maxAmount, other.maxAmount);
total += other.total;
return this;
}
My code test that is with error and version xirr is latest(1.2):
`
@test
public void xirrTestXirrTestTest(){
Double rate = new Xirr(getMockTransactionsXirr()).xirr();
System.out.println(rate);
}
private List<Transaction> getMockTransactionsXirr() {
List<Transaction> listTransaction = new ArrayList<>();
var listMain = Arrays.asList(getMockAnticipations().split("\n"));
Map<String, Long> mapTransactions = new HashMap<>();
for (String line : listMain) {
var listA = Arrays.asList(line.split(";"));
if (mapTransactions.containsKey(listA.get(1))) {
var value = mapTransactions.get(listA.get(1));
value += Long.valueOf(listA.get(0));
mapTransactions.put(listA.get(1), value);
} else {
mapTransactions.put(listA.get(1), Long.valueOf(listA.get(0)));
}
}
mapTransactions.forEach((k, v) -> listTransaction.add(new Transaction(v, k)));
listTransaction.add(new Transaction(-22900, "2022-01-04"));
return listTransaction;
}
private String getMockAnticipations() {
return "1646;2022-11-18\n" +
"52;2022-01-20\n" +
"1358;2022-09-19\n" +
"1793;2022-12-19\n" +
"248;2022-01-17\n" +
"973;2022-01-17\n" +
"1502;2022-10-19\n" +
"1221;2022-08-22\n" +
"1833;2022-02-02\n" +
"461;2022-03-23\n" +
"299;2022-01-24\n" +
"19570;2022-02-02\n" +
"915;2022-06-21\n" +
"1617;2022-01-31\n" +
"770;2022-05-23\n" +
"1064;2022-07-21\n" +
"24;2022-01-21\n" +
"614;2022-04-22";
}`
Below is an answer is given by you of my last question.
You pass guess value (-.5), I want to know how you calculate guess value.?
@test
public void xirr_issue14() {
double rate = Xirr.builder()
.withGuess(-.5)
.withTransactions(
new Transaction(-1498500, "2018-08-30"),
new Transaction(-1500, "2018-08-30"),
new Transaction(-499500, "2019-01-10"),
new Transaction(-500, "2019-01-10"),
new Transaction(-499500, "2019-03-07"),
new Transaction(-500, "2019-03-07"),
new Transaction(-44955000, "2019-03-19"),
new Transaction(-45000, "2019-03-19"),
new Transaction(44630158.4897253, "2019-03-31"),
new Transaction(44674.8333230484, "2019-03-31")
).xirr();
assertEquals(-0.7246366, rate, TOLERANCE);
}
This data set causes an overflow with java-xirr but not with Google Sheets and LibreOffice:
2020-01-31 | 2821703.69
2020-03-06 | 12554.96
2020-03-06 | -12554.96
2020-03-27 | -24208.19
2020-03-27 | -437308.92
2020-03-27 | -2598.07
2020-03-27 | -93054.6
2020-03-27 | -26665.85
2020-03-27 | -14531.04
2020-04-01 | -9807.79
2020-04-01 | -97605
2020-04-01 | -21304.64
2020-04-01 | -89577.85
2020-04-01 | -12191.24
2020-04-01 | -7176.47
2020-04-01 | -11337.45
2020-04-01 | -12917.64
2020-04-01 | -11530.63
2020-04-01 | -34625.18
2020-04-01 | -6485.98
2020-04-01 | -21439.77
2020-04-03 | -442.35
2020-05-04 | 186.98
2020-05-04 | -5843.2
2020-06-03 | -3195.16
2020-06-03 | -780315.21
2020-06-08 | 3225.17
2020-06-08 | -759649.19
2020-06-08 | -3225.17
2020-06-08 | 759649.19
2020-06-18 | -116794.53
The result in Google Sheets or Libre Office is: -0.809918434570599
This is the code to test the dataset in Java:
public static void main(String[] args) {
Object[][] data = { { "2020-01-31", 2821703.69d }, { "2020-03-06", 12554.96d }, { "2020-03-06", -12554.96d },
{ "2020-03-27", -24208.19d }, { "2020-03-27", -437308.92d }, { "2020-03-27", -2598.07d },
{ "2020-03-27", -93054.6d }, { "2020-03-27", -26665.85d }, { "2020-03-27", -14531.04d },
{ "2020-04-01", -9807.79d }, { "2020-04-01", -97605d }, { "2020-04-01", -21304.64d },
{ "2020-04-01", -89577.85d }, { "2020-04-01", -12191.24d }, { "2020-04-01", -7176.47d },
{ "2020-04-01", -11337.45d }, { "2020-04-01", -12917.64d }, { "2020-04-01", -11530.63d },
{ "2020-04-01", -34625.18d }, { "2020-04-01", -6485.98d }, { "2020-04-01", -21439.77d },
{ "2020-04-03", -442.35d }, { "2020-05-04", 186.98d }, { "2020-05-04", -5843.2d },
{ "2020-06-03", -3195.16d }, { "2020-06-03", -780315.21d }, { "2020-06-08", 3225.17d },
{ "2020-06-08", -759649.19d }, { "2020-06-08", -3225.17d }, { "2020-06-08", 759649.19d },
{ "2020-06-18", -116794.53d } };
List<Object[]> list = Arrays.asList(data);
Xirr xirr = new Xirr(
list.stream().map(m -> new Transaction((double) m[1], (String)m[0])).collect(Collectors.toList()));
double rate = xirr.xirr();
System.out.println(rate);
}
Exception in thread "main" org.decampo.xirr.OverflowException: Candidate overflow: {guess=0.9845005934908324, iteration=1459, candidate=Infinity, value=-5.34085707426003E123, derivative=2.24120486056538E-185}
at org.decampo.xirr.NewtonRaphson$Calculation.setCandidate(NewtonRaphson.java:166)
at org.decampo.xirr.NewtonRaphson$Calculation.solve(NewtonRaphson.java:213)
at org.decampo.xirr.NewtonRaphson.inverse(NewtonRaphson.java:89)
at org.decampo.xirr.NewtonRaphson.findRoot(NewtonRaphson.java:70)
at org.decampo.xirr.NewtonRaphson$Builder.findRoot(NewtonRaphson.java:136)
at org.decampo.xirr.Xirr.xirr(Xirr.java:155)
Hi
Tried calculating xirr as follows -
final double xirr = new Xirr(
new Transaction(-516250000, "2019-12-01"),
new Transaction(6676867.348, "2019-12-31"),
new Transaction(-23985279.65, "2020-03-31"),
new Transaction(-9226199.498, "2020-06-30"),
new Transaction(-4756239.031, "2020-09-30"),
new Transaction(9424601.751, "2020-12-31"),
new Transaction(13316322.85, "2021-03-31"),
new Transaction(42283061.51, "2021-06-30"),
new Transaction(48107917.48, "2021-09-30"),
new Transaction(73370583.37, "2021-12-31"),
new Transaction(78071059.19, "2022-03-31"),
new Transaction(88001127.23, "2022-06-30"),
new Transaction(93155837.61, "2022-09-30"),
new Transaction(98048409.57, "2022-12-31"),
new Transaction(102678843.1, "2023-03-31"),
new Transaction(91850303.81, "2023-06-30"),
new Transaction(36640855.3, "2023-09-30")
).xirr();
Got following error -
org.decampo.xirr.NonconvergenceException: Newton-Raphson failed to converge within 10000 iterations.
at org.decampo.xirr.NewtonRaphson.inverse(NewtonRaphson.java:102)
at org.decampo.xirr.NewtonRaphson.findRoot(NewtonRaphson.java:70)
at org.decampo.xirr.NewtonRaphson$Builder.findRoot(NewtonRaphson.java:149)
at org.decampo.xirr.Xirr.xirr(Xirr.java:155)
at org.decampo.xirr.XirrTest.xirr_vs_spreadsheet_1(XirrTest.java:82)
Any help/pointers where I am going wrong? Will providing guess help?
Best
Ashutosh
Hi,
First of all thanks for this project, it's really great!
I came across an issue while using this library in my app because it contain usages of multiple classes (for ex Instant) that is only available with API 26 and that means only a few percentage of devices will support it :/
XIRR calculation is failing with error Transactions must not all be on the same day
When the yield is below -0.64, it will throw "IllegalArgumentException: Newton-Raphson failed to converge within 10000 iterations."
I also tried many other numbers with a yield higher than -0.64, and the code didn't throw an exception.
But once the yield is lower than -0.64, it will throw an exception.
Below is my test code
@Test
public void xirrTestXirrTestTest(){
double rate = new Xirr(
new Transaction(-2610, "2001-06-22"),
new Transaction(-2589, "2001-07-03"),
new Transaction(-5110, "2001-07-05"),
new Transaction(-2550, "2001-07-06"),
new Transaction(-5086, "2001-07-09"),
new Transaction(-2561, "2001-07-10"),
new Transaction(-5040, "2001-07-12"),
new Transaction(-2552, "2001-07-13"),
new Transaction(-2530, "2001-07-16"),
new Transaction(29520, "2001-07-17")
).xirr();
System.out.println(rate);
}
When trying to calculate Xirr for this values:
new Xirr(new Transaction(-1500.0, LocalDate.parse("2019-07-25")), new Transaction(148.56, LocalDate.parse("2019-08-25")), new Transaction(148.56, LocalDate.parse("2019-09-25")), new Transaction(148.56, LocalDate.parse("2019-10-25")), new Transaction(148.56, LocalDate.parse("2019-11-25")), new Transaction(148.56, LocalDate.parse("2019-12-25")), new Transaction(148.56, LocalDate.parse("2020-01-25")), new Transaction(148.56, LocalDate.parse("2020-02-25")), new Transaction(148.56, LocalDate.parse("2020-03-25")), new Transaction(148.56, LocalDate.parse("2020-04-25")), new Transaction(148.56, LocalDate.parse("2020-05-25")), new Transaction(148.56, LocalDate.parse("2020-06-25")), new Transaction(148.56, LocalDate.parse("2020-07-25")), new Transaction(148.56, LocalDate.parse("2020-08-25")), new Transaction(148.56, LocalDate.parse("2020-09-25")), new Transaction(148.56, LocalDate.parse("2020-10-25")), new Transaction(148.56, LocalDate.parse("2020-11-25")), new Transaction(148.56, LocalDate.parse("2020-12-25")), new Transaction(148.56, LocalDate.parse("2021-01-25")), new Transaction(148.56, LocalDate.parse("2021-02-25")), new Transaction(148.56, LocalDate.parse("2021-03-25")), new Transaction(148.56, LocalDate.parse("2021-04-25")), new Transaction(148.56, LocalDate.parse("2021-05-25")), new Transaction(148.56, LocalDate.parse("2021-06-25")), new Transaction(148.56, LocalDate.parse("2021-07-25")), new Transaction(148.56, LocalDate.parse("2021-08-25")), new Transaction(148.56, LocalDate.parse("2021-09-25")), new Transaction(148.56, LocalDate.parse("2021-10-25")), new Transaction(148.56, LocalDate.parse("2021-11-25")), new Transaction(148.56, LocalDate.parse("2021-12-25")), new Transaction(148.56, LocalDate.parse("2022-01-25")), new Transaction(148.56, LocalDate.parse("2022-02-25")), new Transaction(148.56, LocalDate.parse("2022-03-25")), new Transaction(148.56, LocalDate.parse("2022-04-25")), new Transaction(148.56, LocalDate.parse("2022-05-25")), new Transaction(148.56, LocalDate.parse("2022-06-25")), new Transaction(148.56, LocalDate.parse("2022-07-25")), new Transaction(148.56, LocalDate.parse("2022-08-25")), new Transaction(333.65, LocalDate.parse("2022-09-25")) ).xirr()
I got an exception Newton-Raphson failed to converge within 10000 iterations.
After some investigation I concluded that the value and derivative calculated for all but first iteration is NaN (Java is not capable of calculating pow(-double, double)). I think that the exception should be thrown as soon as the value or derivative is NaN, because there is no reason to think that it will change in any following iteration. If I got such exception I can rerun it with different guess without any performance penalty.
The test case is:
4/2/2023 -7.0049467
4/3/2023 -0.0000041
4/3/2023 6.94963
In google sheet, XIRR returns -0.9446451026
, but this library returns org.decampo.xirr.OverflowException: Candidate overflow
.
List list = Arrays.asList(
new Transaction(-275112000,"2015-07-15"),
new Transaction(-57258697.67,"2017-05-02"),
new Transaction( 2577101.75,"2017-11-10"),
new Transaction(-2184516955.15,"2018-10-10"),
new Transaction(-660239840,"2019-01-29"),
new Transaction(-26567773295.82,"2021-05-13")
);
com.littlefox.xirr.NonconvergenceException: Newton-Raphson failed to converge within 1000000000 iterations.
I tried with excel, the same transaction returned rate = -0.724636607244611.
But code is throwing 'org.decampo.xirr.NonconvergenceException: Newton-Raphson failed to converge within 10000 iterations' .
Below is my transactions with code.
@test
public void xirrTestXirrTestTest(){
double rate = new Xirr(
new Transaction(-1498500, "2018-08-30"),
new Transaction(-1500, "2018-08-30"),
new Transaction(-499500, "2019-01-10"),
new Transaction(-500, "2019-01-10"),
new Transaction(-499500, "2019-03-07"),
new Transaction(-500, "2019-03-07"),
new Transaction(-44955000, "2019-03-19"),
new Transaction(-45000, "2019-03-19"),
new Transaction(44630158.4897253, "2019-03-31"),
new Transaction(44674.8333230484, "2019-03-31")
).xirr();
System.out.println(rate);
}
Hi, I'm getting error with these values.
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
double r = new Xirr(
new Transaction(984290.64, dateFormat.parse("25-10-2019")),
new Transaction(-156452.14, dateFormat.parse("25-10-2020")),
new Transaction(-156000.00, dateFormat.parse("25-10-2021")),
new Transaction(-156000.00, dateFormat.parse("25-10-2022")),
new Transaction(-319306.62, dateFormat.parse("25-10-2023")),
new Transaction(-319306.62, dateFormat.parse("25-10-2024")),
new Transaction(-319306.62, dateFormat.parse("25-10-2025")),
new Transaction(-319306.62, dateFormat.parse("25-10-2026")),
new Transaction(-582400.00, dateFormat.parse("25-10-2027"))).xirr();
System.out.println(r*100);
Error:
Exception in thread "main" java.lang.IllegalArgumentException: Newton-Raphson failed to converge within 10000 iterations.
at org.decampo.xirr.NewtonRaphson.inverse(NewtonRaphson.java:99)
at org.decampo.xirr.NewtonRaphson.findRoot(NewtonRaphson.java:67)
at org.decampo.xirr.NewtonRaphson$Builder.findRoot(NewtonRaphson.java:147)
at org.decampo.xirr.Xirr.xirr(Xirr.java:106)
It should by 18.84%
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.