简体   繁体   English

R 中计算 XIRR 时 uniroot 出错

[英]Error in uniroot while calculating XIRR in R

Reproducible example:可重现的例子:

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 returns 0.125 which seems correct. Excel XIRR 返回 0.125,这似乎是正确的。

The uniroot documentation says "Either interval or both lower and upper must be specified", and I'm not sure if tvm::xirr does so. uniroot文档说“必须指定间隔或下限和上限”,我不确定 tvm::xirr 是否这样做。 I guess it does because it works well for many other sets of data.我猜它确实如此,因为它适用于许多其他数据集。

Anyway, I could get it to work correctly in this case by providing a lower and upper (now that I know the answer via Excel) with some trial and error as below.无论如何,在这种情况下,我可以通过提供下限和上限(现在我通过 Excel 知道答案)并进行一些试验和错误来使其正常工作,如下所示。 But I'm not sure if my bounds will always hold.但我不确定我的界限是否会一直存在。

> 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

Is this a bug or limitation of tvm::xirr or am I missing something?这是 tvm::xirr 的错误或限制,还是我遗漏了什么?

Let us go down the rabbit hole.让我们 go 掉入兔子洞。 Firstly, let us read the source code for tvm::xirr:首先,让我们阅读 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 calls uniroot to identify at what cf the function xnpv is equal to zero in the interval c(-1, 10). Xirr 调用 uniroot 来识别 function xnpv 在什么 cf 区间 c(-1, 10) 中等于零。 Default parameter values are tau = NULL and comp_freq = 1. Secondly, let us see the source code for xnpv:默认参数值为 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)
}

We can visualize xnpv and its root as follows:我们可以将 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)

As you can see, for comp_freq = 1, the factor 1/(1 + i/comp_freq) (in the definition of delta) has a vertical asymptote at i = -1 for exponents different than 0 (0^0 = 1 in R).如您所见,对于 comp_freq = 1,因子 1/(1 + i/comp_freq)(在 delta 的定义中)在 i = -1 处有垂直渐近线,对于不同于 0 的指数(R 中的 0^0 = 1) ). Moreover, for i < -1, this expression is undefined in R (negative number raised to decimal powers equals NaN in R).此外,对于 i < -1,此表达式在 R 中未定义(负数的小数幂等于 R 中的 NaN)。

To solve this issue, assuming comp_freq different than 0 or +Inf, you can call xirr as follows:要解决此问题,假设 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.

This assumes that cf <= 10. Finally, given that comp_freq = 1 is the default value, xirr always fails under default settings (thus: this function has not been properly tested by its developer(s)).这假设 cf <= 10。最后,假设 comp_freq = 1 是默认值,xirr 在默认设置下总是失败(因此:这个 function 没有被它的开发者正确测试)。

I'm the package creator.我是 package 的创作者。 In this case, the uniroot algo tries to move past the i=-1 point, and fails.在这种情况下,uniroot 算法试图越过i=-1点,但失败了。 You can easily guide it with the lower bound as the OP has done.您可以像 OP 所做的那样轻松地用下限来引导它。 I could have set up a default lower bound >= 0 to deal with this, but due to the existence of negative interest rates, I decided to not to do it.我本可以设置一个默认的下限 >= 0 来处理这个问题,但由于负利率的存在,我决定不这样做。 A possible solution would be to set a lower bound > -1 in the case that the compounding frequency is not 0 (simple interest) or inf (continuous compounding) and that the function call doesn't include explicit bounds.一个可能的解决方案是在复利频率不是 0(单利)或 inf(连续复利)并且 function 调用不包括显式界限的情况下设置一个 > -1 的下限。 Thanks for the report.感谢您的报告。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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