簡體   English   中英

將 for() 循環轉換為 R 中的 apply() 函數,用於自定義 XIRR function

[英]Converting for() loops to apply() functions in R for a custom XIRR function

我目前一直在尋找計算 R 中大型現金流/余額數據庫的 XIRR 的函數,我遇到了這個function,我一直在嘗試修改它以適合我的代碼:

library(tidyverse)

xirr2 <- function(exflow, date) {
    if(as.numeric(max(date) - min(date)) <= 2) {
        return(0)
    } else if(abs(sum(exflow, na.rm = TRUE)) < 1e-12) {
        return(0)
    } else {
        npv <- function(range, exflow, date){
            for(test.rate in range) {
                temp <- as.data.frame(cbind(exflow, date)) %>%
                    mutate(npv = exflow * ((1 + test.rate/100)^(as.numeric(max(date) - date)/365))) %>%
                    select(npv) %>%
                    .[1]
                if(sum(exflow, na.rm = TRUE) > 0) {
                    if(sum(temp, na.rm = TRUE) > 0) {
                        min.rate <- test.rate
                        next
                    } else {
                        max.rate <- test.rate
                        break
                    }
                } else {
                    if(sum(temp, na.rm = TRUE) < 0) {
                        min.rate <- test.rate
                        next
                    } else {
                        max.rate <- test.rate
                        break
                    }
                }
            }
            return(list(min.rate = min.rate, max.rate = max.rate))
        }
        max.rate <- c()
        min.rate <- c()
        if(sum(exflow, na.rm = TRUE) >= 1e-12) {
            range <- seq(from = 0, to = 1e8, by = 1e3)    
            hundreds <- npv(range, exflow, date)
            range <- seq(from = hundreds$min.rate, to = hundreds$max.rate, by = 10)
            tens <- npv(range, exflow, date)
            range <- seq(from = tens$min.rate, to = tens$max.rate, by = 1)
            ones <- npv(range, exflow, date)
            range <- seq(from = ones$min.rate, to = ones$max.rate, by = 0.01)
            decimals <- npv(range, exflow, date)
            return(mean(unlist(decimals))/100)
        } else {
            range <- seq(from = 0, to = -1e8, by = -1e3)
            hundreds <- npv(range, exflow, date)
            range <- seq(from = hundreds$min.rate, to = hundreds$max.rate, by = -10)
            tens <- npv(range, exflow, date)
            range <- seq(from = tens$min.rate, to = tens$max.rate, by = -1)
            ones <- npv(range, exflow, date)
            range <- seq(from = ones$min.rate, to = ones$max.rate, by = -0.01)
            decimals <- npv(range, exflow, date)
            return(mean(unlist(decimals))/100) 
        }
    }
}

基本上,給定現金流量向量和相應日期向量,這個 function 返回投資的年化 XIRR。

雖然它工作得很好並且在與 MS Excel 和 LibreOffice Calc 交叉引用時生成始終如一的正確答案,但它有點慢,我覺得可以通過將for()循環替換為apply() function 來改進它或者來自data.table package 的東西。速度問題在小例子中幾乎不明顯,但在像我這樣有大量邊緣情況的大型數據集上,速度下降可能相當大。

對於它的價值,我已經嘗試了來自各種包的大量其他 XIRR 函數,包括tvmFinancialMathFinCal 無論出於何種原因,這些功能往往會隨着時間的推移而崩潰:解決方案最終會停止收斂並變得不准確,尤其是在現金流量大且正/負回報之間快速變化的情況下。 這可能是由於普遍依賴 R 中的uniroot()polyroot()函數來計算 XIRR,但我不確定。

無論如何,上面的 function 實際上得到了我想要的數字——我只需要一些幫助來優化它以適應更大的數據集。 先感謝您!

編輯

感謝您到目前為止的幫助。 以下是一些最低限度的示例:

一些存款,一些提款,然后完全提款以獲得正回報。 MS Excel 顯示 XIRR = 15.32%:

> flow1 <- c(-1000,-100,100,1200)
> date1 <- as.Date(c("2018-01-01","2018-10-31","2019-03-31","2019-03-31"), format = "%Y-%m-%d")
> tvm::xirr(flow1,date1)
Error in uniroot(xnpv, interval = interval, cf = cf, d = d, tau = tau,  : 
  f.lower = f(lower) is NA
> xirr2(flow1,date1)
[1] 0.15315

一個接受定期捐款但回報不佳的賬戶。 MS Excel 顯示 XIRR = -27.54%:

> flow2 <- c(-200,-200,-200,-200,-200,800)
> date2 <- as.Date(c("2018-01-01","2018-03-01","2018-06-01","2018-09-01","2019-01-01","2019-03-01"), format = "%Y-%m-%d")
> tvm::xirr(flow2,date2)
Error in uniroot(xnpv, interval = interval, cf = cf, d = d, tau = tau,  : 
  f.lower = f(lower) is NA
> xirr2(flow2,date2)
[1] -0.27535

也許我只是在使用tvm::xirr()錯了? 我不確定如何糾正uniroot()錯誤。

好的,由於這個答案,我明白了。 事實證明tvm::xirr()確實運行良好(並且比上述函數快得多),但我一直在錯誤地調用它。 這是一個工作示例:

> flow2 <- c(-200,-200,-200,-200,-200,800)
> date2 <- as.Date(c("2018-01-01","2018-03-01","2018-06-01","2018-09-01","2019-01-01","2019-03-01"), format = "%Y-%m-%d")
> tvm::xirr(flow2, date2, comp_freq = 1, maxiter = 100, tol = 1e-8, lower = -0.999, upper = 100)
[1] -0.2753857

我之前用lower = -1試過這個,它給出了與上面相同的錯誤。 所以我偏離了 0.001... 非常接近。 再次感謝大家的幫助!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM