簡體   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