Calculating Variable Cash-flow IRR in Python (pandas)

前端 未结 1 878
一整个雨季
一整个雨季 2021-01-13 13:56

I have a DataFrame of unpredictable cashflows and unpredictable period lengths, and I need to generate a backward-looking IRR.

Doing it in Excel is pretty straightfo

相关标签:
1条回答
  • 2021-01-13 14:32

    You can use scipy.optimize.fsolve:

    Return the roots of the (non-linear) equations defined by func(x) = 0 given a starting estimate.

    First define the function that will be the func parameter to fsolve. This is NPV as a result of your IRR, cash flows, and years. (Vectorize with NumPy.)

    import numpy as np
    def npv(irr, cfs, yrs):  
        return np.sum(cfs / (1. + irr) ** yrs)
    

    An example:

    cash_flow = np.array([-2., .5, .75, 1.35])
    years = np.arange(4)
    
    # A guess
    print(npv(irr=0.10, cfs=cash_flow, yrs=years))
    0.0886551465064
    

    Now to use fsolve:

    from scipy.optimize import fsolve
    def irr(cfs, yrs, x0):
        return np.asscalar(fsolve(npv, x0=x0, args=(cfs, yrs)))
    

    Your IRR is:

    print(irr(cfs=cash_flow, yrs=years, x0=0.10))
    0.12129650313214262
    

    And you can confirm that this gets you to a 0 NPV:

    res = irr(cfs=cash_flow, yrs=years, x0=0.10)
    print(np.allclose(npv(res, cash_flow, years), 0.))
    True
    

    All code together:

    import numpy as np
    from scipy.optimize import fsolve
    
    def npv(irr, cfs, yrs):  
        return np.sum(cfs / (1. + irr) ** yrs)
    
    def irr(cfs, yrs, x0, **kwargs):
        return np.asscalar(fsolve(npv, x0=x0, args=(cfs, yrs), **kwargs))
    

    To make this compatible with your pandas example, just use

    cash_flow = df.cash_flow.values
    years = df.years_ago.values
    

    Update: the values in your question seem a bit nonsensical (your IRR is going to be some astronomical number if it even exists) but here is how you'd run:

    cash_flow = np.array([-3.60837e+06, 31462, 1.05956e+06, -1.32718e+06, -4.46554e+06])    
    years_ago = np.array([4.09167, 4.09167, 3.63333, 3.28056, 3.03889])
    
    print(irr(cash_flow, years_ago, x0=0.10, maxfev=10000))
    1.3977721900669127e+82
    

    Second update: there are a couple minor typos in your code, and your actual flows of $ and timing work out to nonsensical IRRs, but here's what you're looking to do, below. For instance, notice you have one id with one single negative transaction, a negatively infinite IRR.

    for i, df in df_tran.groupby('id'):
       cash_flow = df.cash_flow.values
       years = df.years.values
       print('id:', i, 'irr:', irr(cash_flow, years, x0=0.))
    
    id: 978237 irr: 347.8254979851405
    id: 1329483 irr: 3.2921314448062817e+114
    id: 1365051 irr: 1.0444951674872467e+25
    
    0 讨论(0)
提交回复
热议问题