楼主: 逆风也要浪
1992 1

[学习分享] R语言计算XIRR [推广有奖]

  • 0关注
  • 1粉丝

已卖:29份资源

硕士生

17%

还不是VIP/贵宾

-

威望
0
论坛币
33 个
通用积分
15.6803
学术水平
3 点
热心指数
3 点
信用等级
3 点
经验
257 点
帖子
16
精华
0
在线时间
259 小时
注册时间
2018-7-25
最后登录
2025-8-24

楼主
逆风也要浪 发表于 2021-4-11 16:52:50 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币
更新:
需求:我需要计算一个大数据量的分组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上的程序,并进行了修改

  1. library(tidyverse)#加载包
  2. #建立xirr函数
  3. xirr <- function(date,flow) {#从你的数据集中提取日期和资金流变量
  4.   dataset<-bind_cols(date,flow)#把日期和资金流量封装进一个数据框中
  5.   npv <- function(range, dataset){#定义一个净现值函数
  6.     for(test.rate in range) {#从range定义的范围内开始循环测试利率test.rate

  7.       max.date <- max(dataset$dates)#在日期中找到最大的那一天

  8.       temp <- dataset %>%
  9.         mutate(npv = amount * ((1 + test.rate)^(as.numeric(max.date - dates)))) %>%#在原本的数据集(只有日期和资金流)中添加一列npv,对应的是每个日期现金流的现值
  10.         select(npv) %>%#把npv这一列提取出来
  11.         .[1]#提取第一列的值
  12.       if(sum(dataset$amount) > 0) {#如果资金流总和为正,也就是说内部收益率是一个正数
  13.         if(sum(temp) > 0) {#如果此时计算的每个日期的npv的总和大于0,也就是说流出的资金*(1+r)小了,也即是r与真实值小
  14.           min.rate <- test.rate#就把这个测试值定义为最小的利率
  15.           next#继续
  16.         } else {#如果npv的和小于0,也就是说流出的资金*(1+r)大了,也即是r与真实值大
  17.           max.rate <- test.rate#把这个测试值定义为最大的利率
  18.           break
  19.         }
  20.       } else {#资金流总的为负数,即内部收益率为负数,下面的意义与上面类似
  21.         if(sum(temp) < 0) {
  22.           min.rate <- test.rate
  23.           next
  24.         } else {
  25.           max.rate <- test.rate
  26.           break
  27.         }
  28.       }
  29.     }
  30.     return(list(min.rate = min.rate, max.rate = max.rate))#整个循环返回一个列表,列表中包含的是测试出的最大和最小利率
  31.   }


  32.   names(dataset) <- c("dates", "amount")#命名

  33.   max.rate <- c()
  34.   min.rate <- c()
  35. #上面定义了npv的值,下面对将要分割的范围进行设定。这里将利率分成了正数和负数,如果资金流总值=0,这里没法运行,建议单独考虑,因为利率肯定是0
  36.   if(sum(dataset$amount) > 0) {#如果总的资金流是正数

  37.     range <- seq(from = 0, to = 1, by = 0.1)#产生一个从0到1,间隔为0.1的序列,比如0,0.1,0.2...1   
  38.     hundreds <- npv(range, dataset)#调用上面定义的npv函数,分割范围是0-1,数据集为dataset。返回的数是测试得到的最大最小利率

  39.     range <- seq(from = hundreds$min.rate, to = hundreds$max.rate, by = 0.01)#将上面返回来的最大最小利率,在进行0.01大小的分割
  40.     tens <- npv(range, dataset)

  41.     range <- seq(from = tens$min.rate, to = tens$max.rate, by = 0.001)#进行0.001大小的分割
  42.     ones <- npv(range, dataset)

  43.     range <- seq(from = ones$min.rate, to = ones$max.rate, by = 0.0001)#进行0.0001大小的分割
  44.     decimals <- npv(range, dataset)

  45.     return(mean(unlist(decimals)))#返回测试的最大最小利率的均值,这里是0.00001这个小数点位的值,感觉太小了,最后那一个分割可以不要,节省程序运行时间  

  46.   } else {#如果总的资金流量是负数

  47.     range <- seq(from = 0, to = -1, by = -0.1)#产生一个从0到-1的序列,间隔位-0.1,这里一定要注意这样写,-1,-0.9,-0.8....0
  48.     hundreds <- npv(range, dataset)

  49.     range <- seq(from = hundreds$min.rate, to = hundreds$max.rate, by = -0.01)#与上文中类似
  50.     tens <- npv(range, dataset)

  51.     range <- seq(from = tens$min.rate, to = tens$max.rate, by = -0.001)
  52.     ones <- npv(range, dataset)

  53.     range <- seq(from = ones$min.rate, to = ones$max.rate, by = -0.0001)
  54.     decimals <- npv(range, dataset)

  55.     return(mean(unlist(decimals)))
  56.   }
  57. }
复制代码


这个也不是没有缺点:缺点就是运行速度慢,好处灵活设定(只能这样安慰自己了)
#==========================================================================
经验:关于如何解决出现的问题,先百度中文答案,如果还没有解决问题,使用英语的关键词进行谷歌搜索,问题基本就能解决了。关于如何查找合适的包或者函数,可以使用这个网站”https://www.rdocumentation.org/“,使用方法与搜索引擎一样,很方便。





二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:IRR R语言 questions Interval Cashflow

已有 3 人评分论坛币 学术水平 热心指数 信用等级 收起 理由
Sunknownay + 3 + 3 + 3 鼓励积极发帖讨论
llb_321 + 5 精彩帖子
cheetahfly + 30 观点有启发

总评分: 论坛币 + 35  学术水平 + 3  热心指数 + 3  信用等级 + 3   查看全部评分

本帖被以下文库推荐

沙发
逆风也要浪 发表于 2021-4-14 12:25:39
裂开了,经验都写完了,我的代码还没跑完,这万一最后再报个错,那真的是无语妈妈给无语开门——无语到家了

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群
GMT+8, 2025-12-22 22:52