需求:我需要计算一个大数据量的分组XIRR(人话:我想要一次性计算不同投资者的XIRR)
方法:
1、EXCEL
存在的问题:没法一次性计算,并且结果表示的是年化收益率,如果一个投资者1天内赚了2%,那年化收益率将会达到137640.83%,而excel中的结果明显不对,同时对于极短期间的负收益,excel会出错。
适合:数据量小,且资金流间隔较长
2、使用程序:R
这里找到三种方法,下面一一解释:
一、“tvm"包的xirr函数
函数用法:
xirr(cf, d, tau = NULL, comp_freq = 1, interval = c(-1, 10), ...),更为具体的可以参考使用说明。这里提供一个例子(https://stackoverflow.com/questions/55954200/how-to-loop-the-xirr-function-in-r-over-multiple-ids)
library(tvm)
exampledf %>%
mutate(Date = as.Date(Date)) %>%
group_by(ID) %>%
summarise(
IRR = xirr(cf =CashFlow, d = Date,
tau = NULL, comp_freq = 12, interval = c(-1, 10)))
缺点:函数计算并不准确,结果和excel中存在差异,与excel中类似,对于极短期间xirr计算的值不对。
二、任熊发表于知乎(https://zhuanlan.zhihu.com/p/36268182)上面分析的程序
缺点:分组计算的时候程序报错,也可能是我分组的方法不对(我使用的是tidverse包中的group_by这种分组方法)。
三、SunilVeeravalli发布在github上的程序(https://github.com/SunilVeeravalli/XIRR_in_R/blob/master/Xirr%20Code.R)
缺点:对数据格式要求严格,数据集中只能有日期和资金流两项。和我们需求不符
有点:对整体收益为负的收益率,也就是收益率为负,进行了更好的计算
本来推荐的是第一种方法,我还得意洋洋的给大家分享第一种方法的寻找经验(还有点用,放在最后了),但是我在检查的时候发现了计算错误,所以我裂开了啊!真的是裂开了啊!经过一番挣扎,我还是回头去看了具体的源码,真的是,小孩子吃糖——绝了。下面介绍整个代码的编辑思路以及代码,看完后就应该能了解为什么上面会出现问题了
整个代码的运行思路:分割试值。我们需要的内部收益率是使方程等于0的值(-流出的资金*(1+r)+流入的资金=0),所以先给内部收益率设定一个范围,然后把这个范围分割成更小的范围,不断缩小这个范围,最后求解均值就可以了。
提醒:先大致考虑一下,计算的收益率的范围,符合要求的情况下,初始的范围越小越好。还有就是最后要呈现的是年化收益率还是日度收益率,如果需要计算极小日期范围,建议不要使用年化收益率,因为太大了,真的是太大了。还有就是流入资金与流出资金的符号相反,需要注意。
代码:借鉴SunilVeeravalli发布在github上的程序,并进行了修改
- library(tidyverse)#加载包
- #建立xirr函数
- xirr <- function(date,flow) {#从你的数据集中提取日期和资金流变量
- dataset<-bind_cols(date,flow)#把日期和资金流量封装进一个数据框中
- npv <- function(range, dataset){#定义一个净现值函数
- for(test.rate in range) {#从range定义的范围内开始循环测试利率test.rate
- max.date <- max(dataset$dates)#在日期中找到最大的那一天
- temp <- dataset %>%
- mutate(npv = amount * ((1 + test.rate)^(as.numeric(max.date - dates)))) %>%#在原本的数据集(只有日期和资金流)中添加一列npv,对应的是每个日期现金流的现值
- select(npv) %>%#把npv这一列提取出来
- .[1]#提取第一列的值
- if(sum(dataset$amount) > 0) {#如果资金流总和为正,也就是说内部收益率是一个正数
- if(sum(temp) > 0) {#如果此时计算的每个日期的npv的总和大于0,也就是说流出的资金*(1+r)小了,也即是r与真实值小
- min.rate <- test.rate#就把这个测试值定义为最小的利率
- next#继续
- } else {#如果npv的和小于0,也就是说流出的资金*(1+r)大了,也即是r与真实值大
- max.rate <- test.rate#把这个测试值定义为最大的利率
- break
- }
- } else {#资金流总的为负数,即内部收益率为负数,下面的意义与上面类似
- if(sum(temp) < 0) {
- min.rate <- test.rate
- next
- } else {
- max.rate <- test.rate
- break
- }
- }
- }
- return(list(min.rate = min.rate, max.rate = max.rate))#整个循环返回一个列表,列表中包含的是测试出的最大和最小利率
- }
- names(dataset) <- c("dates", "amount")#命名
- max.rate <- c()
- min.rate <- c()
- #上面定义了npv的值,下面对将要分割的范围进行设定。这里将利率分成了正数和负数,如果资金流总值=0,这里没法运行,建议单独考虑,因为利率肯定是0
- if(sum(dataset$amount) > 0) {#如果总的资金流是正数
- range <- seq(from = 0, to = 1, by = 0.1)#产生一个从0到1,间隔为0.1的序列,比如0,0.1,0.2...1
- hundreds <- npv(range, dataset)#调用上面定义的npv函数,分割范围是0-1,数据集为dataset。返回的数是测试得到的最大最小利率
- range <- seq(from = hundreds$min.rate, to = hundreds$max.rate, by = 0.01)#将上面返回来的最大最小利率,在进行0.01大小的分割
- tens <- npv(range, dataset)
- range <- seq(from = tens$min.rate, to = tens$max.rate, by = 0.001)#进行0.001大小的分割
- ones <- npv(range, dataset)
- range <- seq(from = ones$min.rate, to = ones$max.rate, by = 0.0001)#进行0.0001大小的分割
- decimals <- npv(range, dataset)
- return(mean(unlist(decimals)))#返回测试的最大最小利率的均值,这里是0.00001这个小数点位的值,感觉太小了,最后那一个分割可以不要,节省程序运行时间
- } else {#如果总的资金流量是负数
- range <- seq(from = 0, to = -1, by = -0.1)#产生一个从0到-1的序列,间隔位-0.1,这里一定要注意这样写,-1,-0.9,-0.8....0
- hundreds <- npv(range, dataset)
- range <- seq(from = hundreds$min.rate, to = hundreds$max.rate, by = -0.01)#与上文中类似
- tens <- npv(range, dataset)
- range <- seq(from = tens$min.rate, to = tens$max.rate, by = -0.001)
- ones <- npv(range, dataset)
- range <- seq(from = ones$min.rate, to = ones$max.rate, by = -0.0001)
- decimals <- npv(range, dataset)
- return(mean(unlist(decimals)))
- }
- }
这个也不是没有缺点:缺点就是运行速度慢,好处灵活设定(只能这样安慰自己了)
#==========================================================================
经验:关于如何解决出现的问题,先百度中文答案,如果还没有解决问题,使用英语的关键词进行谷歌搜索,问题基本就能解决了。关于如何查找合适的包或者函数,可以使用这个网站”https://www.rdocumentation.org/“,使用方法与搜索引擎一样,很方便。


雷达卡


京公网安备 11010802022788号







