繁体   English   中英

R 中计算 XIRR 时 uniroot 出错

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM