具有xirr和xnpv功能的金融python库?

问题描述 投票:9回答:4

numpy具有irr和npv函数,但是我需要xirr和xnpv函数。

该链接指出xirr和xnpv即将推出。http://www.projectdirigible.com/documentation/spreadsheet-functions.html#coming-soon

是否有具有这两个功能的python库? tks。

python financial
4个回答
9
投票

借助于我在网上找到的各种实现,我想出了一个python实现:

def xirr(transactions):
    years = [(ta[0] - transactions[0][0]).days / 365.0 for ta in transactions]
    residual = 1
    step = 0.05
    guess = 0.05
    epsilon = 0.0001
    limit = 10000
    while abs(residual) > epsilon and limit > 0:
        limit -= 1
        residual = 0.0
        for i, ta in enumerate(transactions):
            residual += ta[1] / pow(guess, years[i])
        if abs(residual) > epsilon:
            if residual > 0:
                guess += step
            else:
                guess -= step
                step /= 2.0
    return guess-1

from datetime import date
tas = [ (date(2010, 12, 29), -10000),
    (date(2012, 1, 25), 20),
    (date(2012, 3, 8), 10100)]
print xirr(tas) #0.0100612640381

11
投票

这是实现这两个功能的一种方法。

import scipy.optimize

def xnpv(rate, values, dates):
    '''Equivalent of Excel's XNPV function.

    >>> from datetime import date
    >>> dates = [date(2010, 12, 29), date(2012, 1, 25), date(2012, 3, 8)]
    >>> values = [-10000, 20, 10100]
    >>> xnpv(0.1, values, dates)
    -966.4345...
    '''
    if rate <= -1.0:
        return float('inf')
    d0 = dates[0]    # or min(dates)
    return sum([ vi / (1.0 + rate)**((di - d0).days / 365.0) for vi, di in zip(values, dates)])

def xirr(values, dates):
    '''Equivalent of Excel's XIRR function.

    >>> from datetime import date
    >>> dates = [date(2010, 12, 29), date(2012, 1, 25), date(2012, 3, 8)]
    >>> values = [-10000, 20, 10100]
    >>> xirr(values, dates)
    0.0100612...
    '''
    try:
        return scipy.optimize.newton(lambda r: xnpv(r, values, dates), 0.0)
    except RuntimeError:    # Failed to converge?
        return scipy.optimize.brentq(lambda r: xnpv(r, values, dates), -1.0, 1e10)

0
投票

[通过Pandas,我可以完成以下工作:(请注意,我使用的是ACT / 365约定)

rate = 0.10
dates= pandas.date_range(start=pandas.Timestamp('2015-01-01'),periods=5, freq="AS")
cfs = pandas.Series([-500,200,200,200,200],index=dates)

# intermediate calculations( if interested)
# cf_xnpv_days = [(cf.index[i]-cf.index[i-1]).days for i in range(1,len(cf.index))]
# cf_xnpv_days_cumulative = [(cf.index[i]-cf.index[0]).days for i in range(1,len(cf.index))]
# cf_xnpv_days_disc_factors = [(1+rate)**(float((cf.index[i]-cf.index[0]).days)/365.0)-1   for i in range(1,len(cf.index))]

cf_xnpv_days_pvs = [cf[i]/float(1+(1+rate)**(float((cf.index[i]-cf.index[0]).days)/365.0)-1)  for i in range(1,len(cf.index))]

cf_xnpv = cf[0]+ sum(cf_xnpv_days_pvs)

0
投票

此答案是对@uuazed答案的改进,并由此得出。但是,有一些更改:

  1. 它使用熊猫数据框而不是元组列表
  2. 这与现金流方向无关,即,您将流入视为负数,将流出视为正数,反之亦然,只要对所有交易的处理都是一致的,结果将是相同的。
  3. XIRR使用此方法进行的计算无法按日期排序现金流量。因此,我已经在内部处理了数据框的排序。
  4. 在较早的答案中,有一个隐含的假设,即XIRR将大部分为正。在另一条评论中指出,这造成了问题,即无法计算出介于-100%和-95%之间的XIRR。此解决方案解决了该问题。
import pandas as pd
import numpy as np

def xirr(df, guess=0.05, date_column = 'date', amount_column = 'amount'):
    '''Calculates XIRR from a series of cashflows. 
       Needs a dataframe with columns date and amount, customisable through parameters. 
       Requires Pandas, NumPy libraries'''

    df = df.sort_values(by=date_column).reset_index(drop=True)
    df['years'] = df[date_column].apply(lambda x: (x-df[date_column][0]).days/365)
    step = 0.05
    epsilon = 0.0001
    limit = 1000
    residual = 1

    #Test for direction of cashflows
    df['disc_val'] = df[[amount_column, 'years']].apply(
                lambda x: x[amount_column]/((1+guess)**x['years']), axis=1)

    df['disc_val_2'] = df[[amount_column, 'years']].apply(
                lambda x: x[amount_column]/((1.05+guess)**x['years']), axis=1)

    mul = 1 if df['disc_val'].sum() > df['disc_val_2'].sum() else -1

    #Calculate XIRR    
    for i in range(limit):
        prev_residual = residual
        df['disc_val'] = df[[amount_column, 'years']].apply(
                lambda x: x[amount_column]/((1+guess)**x['years']), axis=1)
        residual = df['disc_val'].sum()
        if abs(residual) > epsilon:
            if np.sign(residual) != np.sign(prev_residual):
                step /= 2
            guess = guess + step * np.sign(residual) * mul   
        else:
            return guess

说明:

在测试块中,它检查增加折现率是增加折现值还是减少折现值。根据此测试,确定猜测应该向哪个方向移动。该块使该功能可以处理现金流量,而与用户所采用的方向无关。

np.sign(residual) != np.sign(prev_residual)检查猜测何时增加/减少到所需的XIRR速率以上,因为那是当残差从负变为正,反之亦然。此时将减小步长。

numpy包不是绝对必要的。如果不使用numpy,则np.sign(residual)可以替换为residual/abs(residual)。我使用了numpy使代码更易读和直观]

我已经尝试使用各种现金流量来测试此代码。如果您发现此功能无法处理的任何情况,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.