baodan/api/insurance/ppt/irr.py

104 lines
2.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""IRR 精算计算模块 — Modified-Actuarial NPV IRR二分法"""
# HK IA 监管上限
HK_IA_CAP_HKD = 0.06 # HKD 6.0%
HK_IA_CAP_OTHER = 0.065 # 非 HKD 6.5%
def ia_irr_cap(currency: str = "HKD") -> float:
"""HK IA 监管上限。"""
return HK_IA_CAP_HKD if currency.upper() == "HKD" else HK_IA_CAP_OTHER
def compute_irr_ma(
annual_prem: float,
pay_yrs: int,
sv: float,
yr: int,
cap: float = HK_IA_CAP_HKD,
) -> float | None:
"""Modified-Actuarial NPV IRR二分法 200 次迭代)。
Args:
annual_prem: 年缴保费
pay_yrs: 缴费年期
sv: 第 yr 年的退保价值
yr: 保单年度
cap: IRR 上限HK IA 监管)
Returns:
IRR 值0~cap 之间),无法计算返回 None
"""
if yr <= 0 or annual_prem <= 0 or sv <= 0:
return None
def npv(r):
"""计算净现值。"""
if r <= -1:
return float("inf")
total = 0.0
for t in range(1, min(yr, pay_yrs) + 1):
total += annual_prem / ((1 + r) ** t)
# 退保价值折现到第 yr 年
total -= sv / ((1 + r) ** yr)
return total
# 二分法求解
low, high = 0.0, cap
for _ in range(200):
mid = (low + high) / 2
if npv(mid) > 0:
low = mid
else:
high = mid
return round((low + high) / 2, 6)
def compute_irr_ma_withdraw(
annual_prem: float,
pay_yrs: int,
start_yr: int,
annual_wd: float,
sv: float,
yr: int,
cap: float = HK_IA_CAP_HKD,
) -> float | None:
"""含退保场景的 Modified-Actuarial NPV IRR。
Args:
annual_prem: 年缴保费
pay_yrs: 缴费年期
start_yr: 开始退保年度
annual_wd: 每年退保金额
sv: 第 yr 年的退保价值
yr: 保单年度
cap: IRR 上限
Returns:
IRR 值,无法计算返回 None
"""
if yr <= 0 or annual_prem <= 0:
return None
def npv(r):
if r <= -1:
return float("inf")
total = 0.0
for t in range(1, min(yr, pay_yrs) + 1):
total += annual_prem / ((1 + r) ** t)
# 退保收入折现
for t in range(start_yr, yr + 1):
total -= annual_wd / ((1 + r) ** t)
# 最终退保价值折现
total -= sv / ((1 + r) ** yr)
return total
low, high = -0.01, cap
for _ in range(200):
mid = (low + high) / 2
if npv(mid) > 0:
low = mid
else:
high = mid
return round((low + high) / 2, 6)