首页 > 其他分享 >内部收益率的计算

内部收益率的计算

时间:2022-08-15 17:03:34浏览次数:55  
标签:end 内部 tday mid limit 变化率 计算 收益率 inout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# Copyright © 2022, 飞麦 <[email protected]>, All rights reserved.

# frozen_string_literal: true

require 'date'

# IRC = Internal Rate of Change, 内部变化率, 资产(含后续投入、抽回)基于货币的时间价值的真实变化率.
# IRR = Internal Rate of Return, 内部收益率, 资产(含后续投入、抽回)基于货币的时间价值的真实收益率.
# IRR = IRC - 1.0
# 参见: https://www.investopedia.com/terms/i/irr.asp
# 现金价值: 保险术语; 净值: 基金术语; 本质均为资产在特定时间的可变现价值.
# 日收益率 = 日变化率 - 1.0
# 年收益率 = 年变化率 - 1.0
# 年变化率 = 日变化率**365.2425
# 本模块的 xirr 函数类似于 Excel 的 xirr 函数
# 现金价值盈亏, 试算多少, 日变化率, 日变化率上限, 日变化率下限, 盈利时上限锁定或亏损时下限锁定, 是否退出迭代
MidEx = Struct.new(:cv_cmp, :try_cmp, :daily_rate, :dr_up_limit, :dr_down_limit, :limit_lock, :quit_iter)

# 内部日变化率计算模块(日收益率 = 日变化率 - 1.0)
module Xirr
  module_function

  # 根据不定期不定额资金收(+)支(-)情况,递交各日期之间的天数与收(+)支(-)金额
  def gen_inout_series(tday_inout_a)
    pday = nil
    tday_inout_a.each do |tday, inout|
      tday = Date.parse(tday) unless tday.is_a?(Date)
      day_num = pday ? (tday - pday).round : 0
      yield day_num, inout
      pday = tday
    end
  end

  # 根据假设的日变化率试算现金价值
  def try_cash_value(tday_inout_a, daily_rate)
    try_cv = 0.0
    gen_inout_series(tday_inout_a) do |day_num, inout|
      try_cv *= daily_rate**day_num
      try_cv -= inout
    end
    try_cv
  end

  # 根据不定期不定额资金收(+)支(-)情况及现金价值,计算整体是盈利(1)、亏损(-1)还是持平(0)
  def compare_cash_value(tday_inout_a)
    static_cv = 0.0 # 忽略现金的时间价值时的结果
    tday_inout_a.each { |_tday, inout| static_cv += inout }
    raise "Invalid static_cv=#{static_cv}" unless static_cv.finite?

    static_cv <=> 0.0
  end

  # 日变化率调整比率
  DAY_RATE_ADJUST = 1.001

  # 根据试算比较情况,设定日成长率的上下限
  def decide_rate_limit(mid)
    case mid.cv_cmp
    when 1
      mid.dr_down_limit = 1.0
      mid.dr_up_limit = 1.0 * DAY_RATE_ADJUST
    when -1
      mid.dr_down_limit = 1.0 / DAY_RATE_ADJUST
      mid.dr_up_limit = 1.0
    when 0
      mid.dr_down_limit = mid.dr_up_limit = 1.0
    else
      raise "Invalid mid.cv_cmp=#{mid.cv_cmp}"
    end
  end

  # 盈利时根据试算结果调整
  def win_adjust(mid)
    case mid.try_cmp
    when 1 # 试算现金价值偏多
      mid.dr_up_limit = mid.daily_rate
      mid.limit_lock = true # 上限锁定了
    when -1 # 试算现金价值偏少
      mid.dr_down_limit = mid.daily_rate
      # 上限未锁定时继续放大
      mid.dr_up_limit *= DAY_RATE_ADJUST unless mid.limit_lock
    when 0 # 试算现金价值正好
      mid.quit_iter = true
    else
      raise "Invalid mid.try_cmp=#{mid.try_cmp}"
    end
  end

  # 亏损时根据试算结果调整
  def loss_adjust(mid)
    case mid.try_cmp
    when -1 # 试算现金价值偏少
      mid.dr_down_limit = mid.daily_rate
      mid.limit_lock = true # 下限锁定了
    when 1 # 试算现金价值偏多
      mid.dr_up_limit = mid.daily_rate
      # 下限未锁定时继续缩小
      mid.dr_down_limit /= DAY_RATE_ADJUST unless mid.limit_lock
    when 0 # 试算现金价值正好
      mid.quit_iter = true
    else
      raise "Invalid mid.try_cmp=#{mid.try_cmp}"
    end
  end

  # 根据整体盈亏情况及试算结果, 决定日变化率的上下限
  def adjust_limit(mid)
    case mid.cv_cmp
    when 1 # 盈利时变化率为正
      win_adjust(mid)
    when -1 # 亏损时变化率为负
      loss_adjust(mid)
    when 0 # 不盈不亏时无需迭代
      mid.quit_iter = true
    else
      raise "Invalid mid.cv_cmp=#{mid.cv_cmp}"
    end
  end

  # 构造初始值
  def gen_mid
    mid = MidEx.new
    # 初始为未锁定
    mid.limit_lock = false
    # 初始化日变化率为不变
    mid.daily_rate = 1.0
    # 初始为未退出迭代
    mid.quit_iter = false
    mid
  end

  # 计算并比较试算结果与现金价值
  def try_and_compare(mid, tday_inout_a)
    # 设置 日变化率 = 日变化率上限与下限的几何平均
    mid.daily_rate = Math.sqrt(mid.dr_up_limit * mid.dr_down_limit)
    # 根据假设的日变化率试算现金价值结果
    try_cv = try_cash_value(tday_inout_a, mid.daily_rate)
    # 比较试算结果与现金价值
    mid.try_cmp = try_cv <=> 0.0
  end
end

# 内部日变化率计算模块(日收益率 = 日变化率 - 1.0)
module Xirr
  module_function

  # 显示某行内容
  def show(idx, tday, inout)
    "idx=#{idx} tday=#{tday} inout=#{inout}"
  end

  # 检查输入有效性
  def check(tday_inout_a)
    pday = nil
    tday_inout_a.each_with_index do |(tday, inout), idx|
      tday = Date.parse(tday) unless tday.is_a?(Date)
      raise "Invalid tday: #{show(idx, tday, inout)}" unless tday.is_a?(Date)
      raise "Invalid inout: #{show(idx, tday, inout)}" unless inout.finite?
      raise "Invalid tday sequence: prev=#{pday}#{show(idx, tday, inout)}" if pday && tday < pday

      pday = tday
    end
  end

  # 计算不定期不定额资金收(+)支(-)情况下达到现金价值的内部变化率
  # tday_inout_a 是个(日期、收支)的数组, 数组的每个元素也是数组, 包含: 日期、收支这两个元素
  # 买入资产或追加买入资产为支(-), 抽回资产及最终现金价值(净值)为收(+)
  def xirc(tday_inout_a)
    # 检查输入有效性
    check(tday_inout_a)
    # 构造初始值
    mid = gen_mid
    # 根据不定期不定额资金收(+)支(-)情况及现金价值,计算整体是盈利(1)、亏损(-1)还是持平(0)
    mid.cv_cmp = compare_cash_value(tday_inout_a)
    # 根据盈亏情况决定日成长率的上下限
    decide_rate_limit(mid)
    # 当日变化率的上下限之间仍然有双精度浮点数时不断迭代
    while mid.dr_up_limit > mid.dr_down_limit.next_float
      # 计算并比较试算结果与现金价值
      try_and_compare(mid, tday_inout_a)
      # 根据试算结果比较情况, 调整日变化率的上下限
      adjust_limit(mid)
      break if mid.quit_iter
    end
    mid.daily_rate # 年变化率 = 日变化率**365.2425, 年收益率 = 年变化率 - 1.0
  end

  # 计算不定期不定额资金收(+)支(-)情况下达到现金价值的内部收益率
  # tday_inout_a 是个(日期、收支)的数组, 数组的每个元素也是数组, 包含: 日期、收支这两个元素
  # 买入资产或追加买入资产为支(-), 抽回资产及最终现金价值(净值)为收(+)
  def xirr(tday_inout_a)
    xirc(tday_inout_a) - 1.0
  end
end

标签:end,内部,tday,mid,limit,变化率,计算,收益率,inout
From: https://www.cnblogs.com/fitmap/p/16588864.html

相关文章

  • 成员变量不需要初始化,方法内部变量需要初始化
    importjava.util.*;publicclassVolatileTest{publicstaticvoidmain(String[]args){Innerinner=newInner();inner.show1();......
  • mysql 容器内部初始化
    mysql容器内部初始化我推到了dockerhub上一个镜像dockerpullliwenchao1995/mysql8:empty这个镜像是官方提供的mysql8版本,在容器内部把init的命令和要导入的sql放到......
  • 根据两点经纬度计算出航向
    最近开发一款导航的项目需要行驶方向,这里一般是gps会给我返回航向的,但是公司老系统的数据库没有这个数据,就只能自己计算咯getAngle(lng_a,lat_a,lng_b,lat_b){var......
  • 【Java】List排序方法(包括对象、Map等内部排序实现)
    前言日常开发中经常会对List集合做排序操作,JDK为我们提供了强大的排序方法,可以针对对象、Map、基本类型等进行正/倒排序操作。参考博客:JAVA列表排序方法sort和reversed......
  • 计算机操作系统秋招学习第一轮(二)
    主要参考书籍:《CSAPP》、《图解操作系统》上回学到了写传播在解决这个缓存一致性的问题,其实我们只需要做到写传播和事务串行化写传播就是指一个核心的cache发生了数据变......
  • 计算机操作系统秋招学习第一轮(四)
    #主要参考书籍:《CSAPP》、《图解操作系统》、《MOS现代操作系统》上一篇主要就是进程概念的阐述与拓展本篇我们来学习一下:如何控制进程控制进程,即控制进程的......
  • 计算机操作系统秋招学习第一轮(三)
    主要参考书籍:《CSAPP》、《图解操作系统》、《MOS现代操作系统》本篇着重学习线程与进程进程小林coding对进程的解释:代码以二进制的文件形式存储到内存当中,CPU读取并执......
  • [模板] 计算几何
    #include<bits/stdc++.h>#definedebug(x)std::cerr<<"["<<__LINE__<<"]:"<<#x<<"="<<x<<"\n"usingi64=longlong;#defineUP1#defineDOW......
  • Java一次计算简易计算器
    小白简易计算器第一次尝试写代码publicclassCalculator{publicstaticvoidmain(String[]args){//创建扫描对象Scannerscanner=newS......
  • 使用OfficeQuery快速搭建企业内部信息查询系统
    前言 OfficeQuery是新锐科技新推出的一款软件产品,主要用于企业的报表、EPR流程、订单等各种信息的查询,能够快速满足企业运营中的各种信息查询需求。与EasySQLMAIL不同,Off......