[英]Error in uniroot while calculating XIRR in R
可重现的例子:
v <- c(-400000.0,-200000.0, 660636.7)
d <- c("2021-10-27","2022-12-23","2023-01-04")
d1 <- as.Date(d, format="%Y-%m-%d")
tvm::xirr(v, d1) # gives the error below
Error in uniroot(xnpv, interval = interval, cf = cf, d = d, tau = tau, :
f.lower = f(lower) is NA
Excel XIRR 返回 0.125,这似乎是正确的。
uniroot文档说“必须指定间隔或下限和上限”,我不确定 tvm::xirr 是否这样做。 我猜它确实如此,因为它适用于许多其他数据集。
无论如何,在这种情况下,我可以通过提供下限和上限(现在我通过 Excel 知道答案)并进行一些试验和错误来使其正常工作,如下所示。 但我不确定我的界限是否会一直存在。
> tvm::xirr(v, d1, f.lower = -0.2, f.upper=0.5)
[1] 10
> tvm::xirr(v, d1, f.lower = -0.2, f.upper=5)
[1] -1
> tvm::xirr(v, d1, lower = -0.99, upper=0.99)
[1] 0.1244512
这是 tvm::xirr 的错误或限制,还是我遗漏了什么?
让我们 go 掉入兔子洞。 首先,让我们阅读 tvm::xirr 的源代码:
xirr = function (cf, d, tau = NULL, comp_freq = 1, interval = c(-1, 10), ...)
{
uniroot(xnpv, interval = interval, cf = cf, d = d, tau = tau,
comp_freq = comp_freq, extendInt = "yes", ...)$root
}
Xirr 调用 uniroot 来识别 function xnpv 在什么 cf 区间 c(-1, 10) 中等于零。 默认参数值为 tau = NULL 和 comp_freq = 1。其次,让我们看看 xnpv 的源代码:
xnpv = function (i, cf, d, tau = NULL, comp_freq = 1)
{
if (is.null(tau))
tau <- as.integer(d - d[1])/365
delta <- if (comp_freq == 0) {
1/(1 + i * tau)
}
else if (comp_freq == Inf) {
exp(-tau * i)
}
else {
1/((1 + i/comp_freq)^(tau * comp_freq))
}
sum(cf * delta)
}
我们可以将 xnpv 及其根可视化如下:
library(tvm)
v = c(-400000.0,-200000.0, 660636.7)
d = c("2021-10-27","2022-12-23","2023-01-04")
d1 = as.Date(d, format="%Y-%m-%d")
x = seq(-0.8, 10, 0.01)
y = sapply(x, function(x) xnpv(i = x, cf = v, d = d1, tau = as.integer(d1 - d1[1])/365))
plot(x, y, type = 'l', ylab = "xnpv", xlab = "cf"); abline(h = 0, lty = 2); abline(v = 0.1244512, lty = 2)
如您所见,对于 comp_freq = 1,因子 1/(1 + i/comp_freq)(在 delta 的定义中)在 i = -1 处有垂直渐近线,对于不同于 0 的指数(R 中的 0^0 = 1) ). 此外,对于 i < -1,此表达式在 R 中未定义(负数的小数幂等于 R 中的 NaN)。
要解决此问题,假设 comp_freq 不同于 0 或 +Inf,您可以按如下方式调用 xirr:
offset = 0.001; comp_freq = 1
tvm::xirr(v, d1, lower = -comp_freq+offset, upper = 10, comp_freq = comp_freq, tol = 1e-7) # I also changed the numerical tolerance for increased accuracy.
这假设 cf <= 10。最后,假设 comp_freq = 1 是默认值,xirr 在默认设置下总是失败(因此:这个 function 没有被它的开发者正确测试)。
我是 package 的创作者。 在这种情况下,uniroot 算法试图越过i=-1
点,但失败了。 您可以像 OP 所做的那样轻松地用下限来引导它。 我本可以设置一个默认的下限 >= 0 来处理这个问题,但由于负利率的存在,我决定不这样做。 一个可能的解决方案是在复利频率不是 0(单利)或 inf(连续复利)并且 function 调用不包括显式界限的情况下设置一个 > -1 的下限。 感谢您的报告。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.