hscredit.core.financial.advanced 源代码

"""高级金融计算函数.

提供基于现金流序列的投资评价指标:净现值、内部收益率、修正内部收益率。
约定与 ``numpy_financial`` 一致:现金流序列 ``values[0]`` 位于第 0 期(当前时点,
不折现),``values[k]`` 位于第 k 期期末。

**引用**

- numpy-financial 文档:https://numpy.org/numpy-financial/latest/
- NPV/IRR/MIRR 概念参考:https://en.wikipedia.org/wiki/Net_present_value
"""

import numpy as np


[文档] def npv(rate, values): """计算净现值 (Net Present Value). 将一段现金流序列按固定折现率折算到第 0 期并求和,用于判断项目是否创造价值 (NPV > 0 表示在该折现率下项目可接受)。 .. note:: 采用 ``numpy_financial`` 约定:``values[0]`` 位于第 0 期、**不折现**, 与 Excel ``NPV``(从第 1 期开始折现所有值)相差一个 ``(1+rate)`` 因子。 :param rate: 每期折现率,小数表示(如 0.05 表示每期 5%) :param values: 现金流序列(类数组),``values[0]`` 通常为初始投资(负值), 其后为各期回报 :return: 净现值,正值表示在该折现率下项目盈利 **参考样例** 初始投入 1000,其后四期回报 300/400/400/300,折现率 5%:: >>> npv(0.05, [-1000, 300, 400, 400, 300]) 240.87185894766066 **引用** 对应 ``numpy_financial.npv``: https://numpy.org/numpy-financial/latest/functions/npv.html """ values = np.asarray(values) rate = np.asarray(rate) # 计算现值 n = np.arange(len(values)) pv = values / (1 + rate) ** n return np.sum(pv, axis=0)
[文档] def irr(values): """计算内部收益率 (Internal Rate of Return). 求解使净现值(NPV)恰好为零的每期折现率,即项目隐含的真实回报率。 内部使用二分法(bisection)在 ``[-0.99, +∞)`` 区间搜索,要求现金流同时包含 正值与负值(至少一次变号)以保证解存在。 .. note:: IRR 仅在现金流方向单次变号时唯一;多次变号可能存在多个解,本实现返回 二分法在默认区间内找到的第一个根。 :param values: 现金流序列(类数组),通常 ``values[0]`` 为初始投资(负值), 且序列中至少各有一个正值与一个负值 :return: 内部收益率(每期,小数表示) :raises ValueError: 现金流未同时包含正负值、无法确定搜索上界或迭代不收敛时抛出 **参考样例** 初始投入 1000,其后四期回报 300/400/400/300 的内部收益率:: >>> irr([-1000, 300, 400, 400, 300]) 0.14895028127237311 **引用** 对应 ``numpy_financial.irr``: https://numpy.org/numpy-financial/latest/functions/irr.html """ values = np.asarray(values) # 使用 numpy_financial 的实现思路 # 解决 NPV = 0 的方程 # 检查现金流符号变化 signs = np.sign(values) if not ((signs > 0).any() and (signs < 0).any()): raise ValueError("现金流必须至少包含一个正值和一个负值") # 定义 NPV 函数 def _npv(rate): if rate <= -1: return float('inf') return np.sum(values / (1 + rate) ** np.arange(len(values))) # 使用二分法寻找 IRR # 确定搜索范围 low, high = -0.99, 1.0 # 调整 high 直到 NPV 变号 max_iter = 100 for _ in range(max_iter): npv_high = _npv(high) if npv_high < 0: break high *= 2 if high > 1e10: raise ValueError("无法找到合适的利率搜索上界") # 二分搜索 tol = 1e-8 for _ in range(max_iter): mid = (low + high) / 2 npv_mid = _npv(mid) if abs(npv_mid) < tol: return mid if _npv(low) * npv_mid < 0: high = mid else: low = mid raise ValueError(f"迭代 {max_iter} 次后仍未收敛")
[文档] def mirr(values, finance_rate, reinvest_rate): """计算修正内部收益率 (Modified Internal Rate of Return). MIRR 修正了 IRR 隐含"按 IRR 自身再投资"的不现实假设:正现金流按再投资收益率 复利至末期,负现金流按融资成本折现至初期,再由两者之比反解年化收益。相比 IRR 更贴近真实资金成本,且对单变号以外的现金流也唯一。 公式:``MIRR = (FV(正现金流) / -PV(负现金流)) ** (1/(n-1)) - 1``,其中 ``n`` 为现金流期数。 :param values: 现金流序列(类数组),需同时包含正值与负值 :param finance_rate: 融资成本率(每期),用于将负现金流折现到第 0 期 :param reinvest_rate: 再投资收益率(每期),用于将正现金流复利到末期 :return: 修正内部收益率(每期,小数表示) :raises ValueError: 现金流中不含负值或不含正值时抛出 **参考样例** 现金流 -1000/300/400/400/300,融资成本 5%、再投资收益 8%:: >>> mirr([-1000, 300, 400, 400, 300], 0.05, 0.08) 0.1205253227096672 **引用** 对应 Excel ``MIRR`` 与 ``numpy_financial.mirr``: https://numpy.org/numpy-financial/latest/functions/mirr.html """ values = np.asarray(values) n = len(values) # 分离正负现金流 negative = values < 0 positive = values > 0 # 计算负现金流的现值 (使用融资成本) pv_negative = np.sum(values[negative] / (1 + finance_rate) ** np.arange(n)[negative]) # 计算正现金流的未来值 (使用再投资收益率) fv_positive = np.sum(values[positive] * (1 + reinvest_rate) ** (n - 1 - np.arange(n)[positive])) # MIRR 公式 if pv_negative == 0: raise ValueError("现金流中不存在负值") if fv_positive == 0: raise ValueError("现金流中不存在正值") return (fv_positive / -pv_negative) ** (1 / (n - 1)) - 1