peliot / XIRR-and-XNPV

python implementation of Microsoft Excel's XNPV and XIRR
54 stars 23 forks source link

ValueError: negative number cannot be raised to a fractional power #1

Closed chrisspen closed 10 years ago

chrisspen commented 10 years ago
>>> from datetime import date
>>> xirr(cashflows=[(date(2014, 3, 1), 1124.0), (date(2014, 3, 31), -885.4110394559999)])
Traceback (most recent call last):
  File "/usr/local/myproject/financial.py", line 81, in xirr
    return optimize.newton(lambda r: xnpv(r,cashflows),guess)
  File "/usr/local/lib/python2.7/site-packages/scipy/optimize/zeros.py", line 159, in newton
    q1 = func(*((p1,) + args))
  File "/usr/local/myproject/financial.py", line 81, in <lambda>
    return optimize.newton(lambda r: xnpv(r,cashflows),guess)
  File "/usr/local/myproject/financial.py", line 57, in xnpv
    return sum([cf/(1+rate)**((t-t0).days/365.0) for (t,cf) in chron_order])
ValueError: negative number cannot be raised to a fractional power
peliot commented 10 years ago

Chris, One of the weaknesses in the code (that I will address at some point) is that it doesnt give useful error messages when there is no solution. In this case, the reason it is failing is that you start with a positive cash flow. Trying the same series of cash flows in Excel or LibreOffice Calc will also yield an error. If you meant to show the IRR of investing $1,124 and getting $885 back a month later, the signs should be reversed. I will run some more tests (and add a test suite) to make sure there is not a more serious underlying issue. I have tested it a lot and so far it has always performed correctly when there was an answer to be found. In cases where there is no answer, such as yours, it throws an error. I will look into adding more informative error messages in those cases. In any case, glad to see other people are actually using it! Thanks, Philip

On Mon, Mar 03, 2014 at 05:32:51PM -0800, Chris Spencer wrote:

>>> from datetime import date
>>> xirr(cashflows=[(date(2014, 3, 1), 1124.0), (date(2014, 3, 31), -885.4110394559999)])
Traceback (most recent call last):
  File "/usr/local/myproject/financial.py", line 81, in xirr
    return optimize.newton(lambda r: xnpv(r,cashflows),guess)
  File "/usr/local/lib/python2.7/site-packages/scipy/optimize/zeros.py", line 159, in newton
    q1 = func(*((p1,) + args))
  File "/usr/local/myproject/financial.py", line 81, in <lambda>
    return optimize.newton(lambda r: xnpv(r,cashflows),guess)
  File "/usr/local/myproject/financial.py", line 57, in xnpv
    return sum([cf/(1+rate)**((t-t0).days/365.0) for (t,cf) in chron_order])
ValueError: negative number cannot be raised to a fractional power

Reply to this email directly or view it on GitHub: https://github.com/peliot/XIRR-and-XNPV/issues/1

Philip Eliot git.peliot@gmail.com

chrisspen commented 10 years ago

I did notice that Libreoffice threw an error code for the same data. However, reversing the signs still results in the same error.

>>> xirr(cashflows=[(date(2014, 3, 1), -1124.0), (date(2014, 3, 31), 885.4110394559999)])
ValueError: negative number cannot be raised to a fractional power

Your understanding of what I'm trying to accomplish is correct. I thought a positive value indicated "investing" whereas a negative value represent a "withdrawal". That's what my original signs attempted to indicate.

Why would the reversed signs encounter the same problem?

peliot commented 10 years ago

Chris, Thanks for the followup. The reason it is failing is that the secant method implemented by scipy.optimize is hitting its maximum number of iterations (50). The reason it has trouble is that the solution to your cash flow is -95%. NPV(rate) (the function IRR is the zero of) is very steep as rate approaches -100%, so the numerical solvers can bounce back and forth a lot before they converge. LibreOffice Calc also fails to produce a solution to your cash flow, probably for the same reason since the method it uses is the same as the one I implemented. Nonetheless, I think it is a good idea to increase the number of allowed iterations (and produce a more informative error message when it fails in that way). Let me make the change and run some more test cases. You may also get the right answer with a better starting guess (the optional parameter to XIRR). Try -0.9. Thanks, Philip

chrisspen commented 10 years ago

Thanks for looking into this. How did you find the value of -95%?

chrisspen commented 10 years ago

Oh, I see, you calculated the compound annual growth rate. Nice workaround. If I calculate that first and pass that in as a guess, it solves the problem.