7

I need to calculate XIRR of financial investments made over a period of time. Is there any function to do this in numpy, pandas or plain python?

Reference: What is XIRR?

The accepted answer in the original question is not correct and can be improved.

DaSarfyCode
  • 385
  • 2
  • 5
  • 11
  • 3
    Possible duplicate of [financial python library that has xirr and xnpv function?](https://stackoverflow.com/questions/8919718/financial-python-library-that-has-xirr-and-xnpv-function) – Bharath M Shetty Oct 10 '17 at 13:43
  • I agree this is a possible duplicate. How do I delete this question? I see people are very 'down vote' happy and do not want more damage. – DaSarfyCode Oct 10 '17 at 14:09
  • Once the question is answered it cant be deleted. Answerer put his time and effort to answer the question so. – Bharath M Shetty Oct 10 '17 at 14:10
  • @Bharathshetty I answered before I saw that, should I delete my answer here and move it to the linked one? – pyCthon Oct 12 '17 at 12:09
  • I don't think that would be appropriate. Your answer is clearly better than the one given in the original question. – DaSarfyCode Oct 27 '17 at 11:59

3 Answers3

7

Created a package for fast XIRR calculation, PyXIRR

It doesn't have external dependencies and works faster than any existing implementation.

from datetime import date
from pyxirr import xirr

dates = [date(2020, 1, 1), date(2021, 1, 1), date(2022, 1, 1)]
amounts = [-1000, 1000, 1000]

# feed columnar data
xirr(dates, amounts)

# feed tuples
xirr(zip(dates, amounts))

# feed DataFrame
import pandas as pd
xirr(pd.DataFrame({"dates": dates, "amounts": amounts}))
Alexander Volkovsky
  • 2,588
  • 7
  • 13
3

Here's an implementation taken from here.

import datetime
from scipy import optimize

def xnpv(rate,cashflows):
    chron_order = sorted(cashflows, key = lambda x: x[0])
    t0 = chron_order[0][0]
    return sum([cf/(1+rate)**((t-t0).days/365.0) for (t,cf) in chron_order])

def xirr(cashflows,guess=0.1):
    return optimize.newton(lambda r: xnpv(r,cashflows),guess)
jeteon
  • 3,471
  • 27
  • 40
pyCthon
  • 11,746
  • 20
  • 73
  • 135
  • 1
    As it might be confusing that the `cashflows` variable is not used in `xnpv`, at the moment (2018-02-13) the method looks a bit different on the repo. Can you update your answer? – leberknecht Feb 13 '18 at 11:26
0

This implementation calculates the time delta once and then vectorizes the NPV calculation. It should run much faster than @pyCthon's solution for larger datasets. The input is a pandas series of cashflows with dates for the index.

Code

import pandas as pd
import numpy as np
from scipy import optimize

def xirr2(valuesPerDate):
  """  Calculate the irregular rate of return.
  valuesPerDate is a pandas series of cashflows with index of dates.
  """

  # Clean values
  valuesPerDateCleaned = valuesPerDate[valuesPerDate != 0]

  # Check for sign change
  if valuesPerDateCleaned.min() * valuesPerDateCleaned.max() >= 0:
    return np.nan

  # Set index to time delta in years
  valuesPerDateCleaned.index = (valuesPerDateCleaned.index - valuesPerDateCleaned.index.min()).days / 365.0

  result = np.nan
  try:
    result = optimize.newton(lambda r: (valuesPerDateCleaned / ((1 + r) ** valuesPerDateCleaned.index)).sum(), x0=0, rtol=1e-4)
  except (RuntimeError, OverflowError): 
    result = optimize.brentq(lambda r: (valuesPerDateCleaned / ((1 + r) ** valuesPerDateCleaned.index)).sum(), a=-0.999999999999999, b=100, maxiter=10**4)

  if not isinstance(result, complex):
    return result
  else:
    return np.nan

Tests

valuesPerDate = pd.Series()
for d in pd.date_range(start='1990-01-01', end='2019-12-31', freq='M'):
  valuesPerDate[d] = 10*np.random.uniform(-0.5,1)
valuesPerDate[0] = -100

print(xirr2(valuesPerDate))
mattf1216
  • 1
  • 1