ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

各期的内部收益率的计算工具

2022-08-17 09:30:42  阅读:188  来源: 互联网

标签:idx tday inp 各期 str rec 工具 收益率 def


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
# Copyright © 2022, 飞麦 <fitmap@qq.com>, All rights reserved.

# frozen_string_literal: true

require 'clipboard'
require 'xirr'

# 根据剪贴板中的日期(可选)、交费与现金价值计算各期的内部收益率
module XirrGo
  module_function

  # 结构: 日期, 交费, 现金价值
  RecEx = Struct.new(:tday, :fee, :curr)

  # 从匹配字符串中删除分隔千位数的逗号后获取数值
  def positive_value(str)
    str && !str.empty? && str != '-' ? str.gsub(',', '').to_f : nil
  end

  # 从匹配字符串中删除分隔千位数的逗号后获取数值, 然后转换为负值
  def negative_value(str)
    tmp = positive_value(str)
    tmp ? -tmp : nil
  end

  # 判断行的有效性(包含交费和/或现金价值)
  def valid(rec)
    rec.fee || rec.curr
  end

  # 获取计算内部收益率的输入数组
  def deal(rec_a, o_idx)
    tday = Date.today
    inp_a = [] # 内部收益率输入数组
    rec_a.each_with_index do |rec, i_idx|
      next unless valid(rec)

      tday = rec.tday if rec.tday
      inp_a << [tday, rec.fee] if rec.fee # 日期、发生金额(交费转换为负数, 现金价值保持为正数)
      if i_idx >= o_idx
        inp_a << [tday, rec.curr] if rec.curr
        break
      end
      tday >>= 12 # 下一保险年度
    end
    inp_a
  end

  # 根据资金发生序列计算各年的内部收益率
  def calc(rec_a)
    gain_a = [] # 收益率数组
    rec_a.each_with_index do |rec, o_idx|
      if rec.curr
        inp_a = deal(rec_a, o_idx) # 获取计算内部收益率的输入数组
        gain = (Xirr.xirc(inp_a)**365.2425) - 1.0 # 计算内部收益率
        gain_a << "#{format('%.4f', gain * 100)}%" # 转换为百分数
      else
        gain_a << '' # 标题行或空行
      end
    end
    gain_a.join("\n") # 所有行合并转换为一段文本
  end

  # 从字符串行中提取交费、现金价值等数据
  def pick2(line)
    md = line.match(/^\s*((\d+(,\d+)*(\.\d+)?)|-?)\s*\t\s*((\d+(,\d+)*(\.\d+)?)|-?)\s*$/)
    if md
      RecEx.new(nil, negative_value(md[1]), positive_value(md[5])) # 交费、现金价值
    else
      puts "\n#{line}: 本行没有日期[可选]、交费、现金价值等数据"
      RecEx.new # 不予处理
    end
  end

  # 分析日期字符串
  def date_parse(str)
    Date.parse(str)
  rescue Date::Error
    nil
  end

  # 从字符串行中提取日期、交费、现金价值等数据
  def pick3(line)
    md = line.match(/^\s*(\d+-\d+-\d+)\s*\t\s*((\d+(,\d+)*(\.\d+)?)|-?)\s*\t\s*((\d+(,\d+)*(\.\d+)?)|-?)\s*$/)
    if md
      datey = date_parse(md[1])
      if datey
        RecEx.new(datey, negative_value(md[2]), positive_value(md[6])) # 日期、交费、现金价值
      else
        puts "\n#{line}: #{md[1]} 日期非法"
        RecEx.new # 不予处理
      end
    else
      pick2(line)
    end
  end

  # 根据剪贴板的日期(可选)、交费与现金价值序列,计算内部收益率
  def run
    text = Clipboard.paste # 从剪贴板获取日期列(可选)、交费列、现金价值列
    rec_a = [] # 日期(可选)、交费、现金价值序列(若无日期,则假设每年一行)
    # 对剪贴板内容中的每一行
    text.each_line { |line| rec_a << pick3(line) }
    Clipboard.copy(calc(rec_a)) # 将计算结果拷贝回剪贴板
    puts "\n【内部收益率列已拷贝到剪贴板中】"
  end
end

# 用<Ctrl>-<鼠标左键>点选交费列、现金价值列后,拷贝到剪贴板
# 或用<Ctrl>-<鼠标左键>点选日期(YYYY-MM-DD)列、交费列、现金价值列后,拷贝到剪贴板
# 运行后剪贴板包含内部收益率列
$PROGRAM_NAME == __FILE__ && XirrGo.run

标签:idx,tday,inp,各期,str,rec,工具,收益率,def
来源: https://www.cnblogs.com/fitmap/p/16593806.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有