Search code examples
pythonpandasfinanceirrxirr

Python IRR Function giving different result than Excel XIRR


I am using the following functions to perform IRR-Calculations with Python:

from scipy.optimize import newton

def xnpv(rate, values, dates):
    
    if rate <= -1.0:
        return float('inf')
    min_date = min(dates)
    return sum([
        value / (1 + rate)**((date - min_date).days / 365)
        for value, date
        in zip(values, dates)
     ])


def xirr(values, dates):
    return newton(lambda r: xnpv(r, values, dates), 0)

Source for functions: https://2018.pycon.co/talks/personal-pynance/personal-pynance.pdf

For months this functions worked perfectly with all kind of different cash flows & dates and I got the same result as with Excel's XIRR function. However, suddently with the below list of cash flows & dates it stopped working and I get a different result than with Excel's IRR Formula (which is the correct& expected one):

import pandas as pd
import datetime
import numpy as np
from decimal import *

# Input Data
dates = [datetime.date(2020, 8, 31), datetime.date(2020, 5, 5), datetime.date(2020, 2, 28), datetime.date(2020, 8, 31),datetime.date(2018, 6, 30)]
values = [50289.0, -75000.0, 0.0, 0.0, 0.0]

# Create Dataframe from Input Data
test = pd.DataFrame({"dates" : dates, "values" : values})

# Filter all rows with 0 cashflows
test = test[test['values'] != 0]

# Sort dataframe by date
test = test.sort_values('dates', ascending=True)
test['values'] = test['values'].astype('float')

# Create separate lists for values and dates
test_values = list(test['values'])
test_dates = list(test['dates'])

# Calculate IRR
xirr(test_values, test_dates)

The result I get in Python is 0.0001 whereas in Excel I get -0.71 and I have no clue what I am missing here. Maybe someone has an idea?!??!


Solution

  • Scipy optimization functions are fallable to local minima. Change optimization method to something diferent, e.g. anderson, and get what you expect to.

    Proof

    from scipy.optimize import anderson
    
    def xnpv(rate, values, dates):
        
        if rate <= -1.0:
            return float('inf')
        min_date = min(dates)
        return sum([
            value / (1 + rate)**((date - min_date).days / 365)
            for value, date
            in zip(values, dates)
         ])
    
    
    def xirr(values, dates):
        return anderson(lambda r: xnpv(r, values, dates), 0)
    
    import datetime
    from decimal import *
    
    # Input Data
    dates = [datetime.date(2020, 8, 31), datetime.date(2020, 5, 5), datetime.date(2020, 2, 28), datetime.date(2020, 8, 31),datetime.date(2018, 6, 30)]
    values = [50289.0, -75000.0, 0.0, 0.0, 0.0]
    
    # Create Dataframe from Input Data
    test = pd.DataFrame({"dates" : dates, "values" : values})
    
    # Filter all rows with 0 cashflows
    test = test[test['values'] != 0]
    
    # Sort dataframe by date
    test = test.sort_values('dates', ascending=True)
    test['values'] = test['values'].astype('float')
    
    # Create separate lists for values and dates
    test_values = list(test['values'])
    test_dates = list(test['dates'])
    
    # Calculate IRR
    xirr(test_values, test_dates)
    array(-0.70956212)