我需要计算一段时间内的金融投资的XIRR。在numpy、pandas或纯python中有没有相应的函数可以实现?
参考:什么是XIRR?
原问题的被接受答案是不正确的,需要改进。
创建了一个用于快速计算XIRR的包:PyXIRR
它没有外部依赖项,并且比任何现有的实现都更快。
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}))
这里是从这个链接中获取的实现。
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)
xnpv
中没有使用cashflows
变量,这可能会让人感到困惑。目前(2018-02-13)该方法在存储库中的外观有些不同。你能更新一下你的答案吗? - leberknecht这个实现只计算一次时间差,然后向量化 NPV 计算。对于较大的数据集,它应该比 @pyCthon 的解决方案运行得更快。输入是一个带有日期索引的 pandas 系列现金流。
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
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))