用作 IRB 信用 RWA 输入的现值加权有效期限 M
PV-Weighted Effective Maturity M for IRB Credit RWA Input
开始编码实现 solution(cash_flow_times_years: list[float], cash_flows: list[float], discount_factors: list[float]) -> float。某行的信用 RWA 引擎需要为每一笔贷款敞口计算一个有效期限 M(单位年)作为 IRB 监管风险权重公式的输入,与 PD、LGD、EAD 并列。Basel II §320 / CRR Art. 162 的标准定义使用合同现金流(不折现);本题实现该行引擎的现值加权变体(一种 Macaulay 久期式、以每期现金流现值为权重的期限度量),是引擎内部约定,且通常比合同现金流形式更保守。给定 per-period 现金流时刻(自今日起的年数)、per-period 现金流金额与 per-period 折现因子,返回按现金流和 DF 加权的平均时间,并按 Basel 强制要求 clamp 到 [1, 5] 年区间。
形式上:
weighted_time = sum_t (cash_flow_times_years[t] * cash_flows[t]
* discount_factors[t])
weighted_total = sum_t (cash_flows[t] * discount_factors[t])
raw_M = weighted_time / weighted_total
M = max(1.0, min(5.0, raw_M))注意结构:M 是各期时间的加权均值,因此分子把每个时间和对应的现值权重 cash_flow * DF 相乘求和,分母则把同样的权重求和(只是不带时间因子)。Basel [1, 5] clamp 在加权均值之后应用。
当 T = 0(空合约)或 weighted_total = 0(所有现金流为 0——M 未定义)时,参考实现返回 NaN。函数永远不抛异常。
算例
考虑一笔 5 年期、年付现金流的等额贷款:
cash_flow_times_years = [1.0, 2.0, 3.0, 4.0, 5.0]cash_flows = [200_000, 200_000, 200_000, 200_000, 200_000]discount_factors = [0.97, 0.94, 0.91, 0.88, 0.85]
逐期计算权重和时间-权重:
t=0:weight = 200_000 * 0.97 = 194_000;time*weight = 1.0 * 194_000 = 194_000t=1:weight = 200_000 * 0.94 = 188_000;time*weight = 2.0 * 188_000 = 376_000t=2:weight = 200_000 * 0.91 = 182_000;time*weight = 3.0 * 182_000 = 546_000t=3:weight = 200_000 * 0.88 = 176_000;time*weight = 4.0 * 176_000 = 704_000t=4:weight = 200_000 * 0.85 = 170_000;time*weight = 5.0 * 170_000 = 850_000
求和:weighted_total = 910_000、weighted_time = 2_670_000。raw_M = 2_670_000 / 910_000 ≈ 2.9340659...。位于 [1, 5] 内,无需 clamp,M ≈ 2.934065934065934。
注意:等额平摊现金流的 raw_M < 3.0——略小于简单平均时间 (1+2+3+4+5)/5 = 3.0。这是 DF 效应:远期现金流 DF 更小,在 PV 加权均值里贡献更少。把 DF 丢掉、只用 cash_flow 加权的写法会得到 3.0——那是合同现金流形式(Basel §320 基线),不是本题要求的现值加权变体。
六个易错点
第一个易错点是 **PV 加权(cash_flow * DF)**。权重是 cash_flow[t] * discount_factor[t]——每期现金流的现值——不是单独的 cash_flow[t]。把 DF 丢掉的写法会得到非现值加权版本,高估远期现金流的权重(远期 PV 更小、应当贡献更少)。在算例中,丢 DF 得到 raw_M = sum(t * cf) / sum(cf) = (1+2+3+4+5) * 200_000 / (5 * 200_000) = 3.0——与正确值 ≈ 2.934 不同。注意:无 DF 的 sum(t * cf) / sum(cf) 形式正是 Basel II §320 合同现金流定义;本题要求的是该行引擎采用的现值加权 IRB 输入变体,故 DF 必须出现。
第二个易错点是 **time * weight 在分子、weight 在分母**。分子 = sum(time * weight);分母 = sum(weight)。M 是各期时间的加权均值——标准的概率加权均值结构。把分子分母对调(计算 sum(weight) / sum(time * weight),倒数均值形式)得到的是单位为 1/年 的量,量级大致是 1/M,对任何合理期限都 clamp 到 1.0。简单的健全性检验:单笔 bullet 现金流于 t*,公式必须坍塌为 M = clamp(t*, 1, 5);倒着写得到 1/t*(或其 clamp),是显眼的量纲错误。
第三个易错点是 **Basel [1, 5] clamp**。Basel IRB 把 M 上界设为 5 年(资本要求并非无界单增地依赖期限——长期限敞口按 5 年处理)、下界设为 1 年(避免通过隔夜合约滚动绕开几乎为零的期限调整)。跳过 clamp 的写法返回未截断的 raw_M,凡是 clamp 起作用的测试都失败:单笔 bullet 在 t = 7y 应得 M = 5.0 而非 7.0;在 t = 0.5y 应得 M = 1.0 而非 0.5。clamp 在加权均值之后应用。
第四个易错点是 分母与分子使用同一种加权。weighted_total = sum(cash_flow * DF)——不是 sum(cash_flow)(漏 DF),不是 sum(time * cash_flow * DF)(多一个时间因子)。其它任何形式都破坏加权均值结构——单笔 bullet 时 M 不再回归到该期时间。在 bullet 健全性检验里(除 t* 外现金流均为 0,金额 c、DF d):weighted_time = t* * c * d、weighted_total = c * d、raw_M = t*——只当分母是 c * d 时成立。把 DF 从分母里漏掉,bullet 测就崩。
第五个易错点是时间单位是年。规范说 cash_flow_times_years[t] 单位为年(如 0.25 = 3 个月、5.0 = 5 年)。把输入按天处理得到的 M 等于 年×365——所有输出 clamp 到 5.0,整个测试套坍缩成单一值。务必看清字段单位标注。
第六个易错点是**两种未定义情形返回 NaN**。T = 0(空合约——根本没有现金流)和 weighted_total = 0(所有现金流为 0——除以 0)。参考实现两种情形都返回 NaN——直接除以 0 会抛异常,但契约规定函数不抛。用 float('nan') 即可,由比较器的 NaN-aware float 校验处理相等。
边界
T == 0:NaN(无任何现金流;M 未定义)。- 所有
cash_flows[t] == 0:weighted_total = 0,返回NaN。 - 单笔 bullet 于
t = 3y、DF = 0.9:raw_M = 3 * c * 0.9 / (c * 0.9) = 3.0,位于[1, 5],M = 3.0。 - 单笔 bullet 于
t = 0.5y:raw_M = 0.5,clamp 升到1.0。 - 单笔 bullet 于
t = 7y:raw_M = 7.0,clamp 降到5.0。 - 等额现金流于
t = 1, 2, 3、DF 全为 1.0:raw_M = (1 + 2 + 3) / 3 = 2.0,M = 2.0。 - 部分期现金流为 0:零现金流的期权重为 0,从两个求和中自然脱出;结果是剩余非零期上的加权均值再 clamp。
函数不抛异常——调用方保证三个列表长度一致、所有输入在声明范围内。
期望算法:O(T) 双遍(或单遍维护两个累加和)。逐 t 累加 weight = cash_flows[t] * discount_factors[t] 到 weighted_total、cash_flow_times_years[t] * weight 到 weighted_time;循环结束后做除法和 clamp。O(1) 额外空间。T <= 60 足够小,浮点累加顺序在 1e-9 容差下无关紧要。
实现细节由 stubs/stub.py 提供。
实践背景
在 Basel IRB(内部评级法)资本框架下,公司、主权与银行类敞口的信用风险加权资产(RWA)由监管公式给出:
RWA_unit = K(PD, LGD, M) * 12.5 * EAD其中 K 是单位资本要求,是借款人违约概率(PD)、违约损失率(LGD)与有效期限 M 的函数(外加一个资产类别相关的期限调整因子 b(PD))。期限调整使得资本对 M 单调递增——长期限敞口在每美元 EAD 上更危险,因为更长视野意味着更多的滚动与信用恶化风险。Basel II §320(CRR Art. 162)的标准定义为 M = sum(t * CF_t) / sum(CF_t),使用合同现金流(不折现);本题实现的是该行引擎使用的现值加权变体——以每期现金流现值为权重的 Macaulay 久期式期限——作为通常更保守的内部输入(监管允许银行在个案基础上采用更保守的度量)。无论哪种变体,都使用相同的 [1, 5] 年截断:上界 5 年(资本充足性意义上不再无界增长——监管公式中期限调整在 5y 之外几乎平坦)、下界 1 年(避免通过滚动 sub-1y 敞口绕开几乎为零的期限调整、做资本套利)。本题是 M 计算的构件;下游银行把 M 与 PD、LGD、EAD 一并代入 IRB 监管 K-公式,产出每日资本要求与 RWA 监管报表。本题库中其它 credit-risk-basics 姊妹题:coding-survival-weighted-ee-profile-for-cva 做 CVA 的存活加权步骤;coding-portfolio-expected-loss-by-bucket 做单期组合 EL 聚合;coding-cumulative-default-prob-from-marginal-hazard-rates 做 PD 期限结构的连乘。[1, 5] clamp 是它区别于通用 Macaulay 久期的最显著特征,也是从久期公式移植到 IRB-M 实现时最常见的作者错误。
约束条件
- 0 <= T == len(cash_flow_times_years) == len(cash_flows) == len(discount_factors) <= 60
- 0.001 <= cash_flow_times_years[t] <= 30.0(per-period 现金流时刻、单位年、升序、严格正)
- 0.0 <= cash_flows[t] <= 1e9(per-period 现金流金额、美元、非负)
- 0.0 < discount_factors[t] <= 1.0(per-period 现值因子、严格正、上界 1.0)
- 输出为 float——`M` 取值于 `[1.0, 5.0]` 或 `NaN`,按 rel_tol=1e-9、abs_tol=1e-9 比对
- 函数名为 `solution`,复杂度 O(T) 时间、O(1) 额外空间
样例
Case 1 · statement-example: 5y amortising loan with declining DFs
输入: [[1,2,3,4,5],[200000,200000,200000,200000,200000],[0.97,0.94,0.91,0.88,0.85]]
期望: 2.934065934065934
5 年等额贷款:weighted_total=910000,weighted_time=2670000,raw_M≈2.934 在[1,5]内,无 clamp。
Case 2 · typical: single bullet at t=3y returns M=3 (no clamp)
输入: [[3],[1000000],[0.9]]
期望: 3
单笔 bullet:raw_M = 3*c*d/(c*d) = 3.0,位于 [1,5] 内,M=3.0。
Case 3 · typical: equal flows at t=1,2,3 with DF=1 returns M=2
输入: [[1,2,3],[100,100,100],[1,1,1]]
期望: 2
等额、DF=1:raw_M = (1+2+3)/3 = 2.0。
Case 4 · typical: 10-period semi-annual bond with exp-decay DFs
输入: [[0.5,1,1.5,2,2.5,3,3.5,4,4.5,5],[50000,50000,50000,50000,50000,50000,50000,50000,50000,1050000],[0.9801986733067553,0.9607894391523232,0.9417645335842487,0.9231163463866358,0.9048374180359595,0.8869204367171575,0.8693582353988059,0.8521437889662113,0.835270211411272,0.8187307530779818]]
期望: 4.174312056396272
10 期半年付债券:尾期含本金 1.05M,PV 加权后 raw_M ≈ 4.174 年。
Case 5 · boundary: T=0 returns NaN
输入: [[],[],[]]
期望: "NaN"
T=0:M 未定义,返回 NaN。
Case 6 · boundary: all cash flows zero returns NaN
输入: [[1,2,3],[0,0,0],[0.95,0.9,0.85]]
期望: "NaN"
全部 cash_flow=0:weighted_total=0,返回 NaN。
Case 7 · boundary: single bullet at t=0.5y clamped UP to 1.0
输入: [[0.5],[500000],[0.99]]
期望: 1
单笔 0.5y:raw_M=0.5,clamp 升到 1.0(IRB 下限)。
Case 8 · boundary: single bullet at t=7y clamped DOWN to 5.0
输入: [[7],[500000],[0.7]]
期望: 5
单笔 7y:raw_M=7.0,clamp 降到 5.0(IRB 上限)。
Case 9 · boundary: bullet at t=0.001y clamped UP to 1.0 (extreme floor)
输入: [[0.001],[100],[1]]
期望: 1
极短 bullet:0.001y clamp 升到 1.0。
Case 10 · boundary: bullet at t=30y clamped DOWN to 5.0 (extreme cap)
输入: [[30],[100],[0.05]]
期望: 5
极长 bullet:30y clamp 降到 5.0。
Case 11 · boundary: bullet at t=1.0 returns exactly 1.0
输入: [[1],[12345],[0.97]]
期望: 1
bullet 在 1.0y:raw_M=1.0,无 clamp 影响。
Case 12 · boundary: bullet at t=5.0 returns exactly 5.0
输入: [[5],[777.7],[0.85]]
期望: 5
bullet 在 5.0y:raw_M=5.0,无 clamp 影响。
Case 13 · boundary: zero cash flows in some periods drop out
输入: [[1,2,3,4],[0,100,0,100],[0.95,0.9,0.85,0.8]]
期望: 2.9411764705882355
零现金流期被自然剔除:M 由 t=2 与 t=4 的加权均值决定。
Case 14 · boundary: T=1 with zero cash flow returns NaN
输入: [[2.5],[0],[0.9]]
期望: "NaN"
T=1,cf=0:weighted_total=0,返回 NaN。
Case 15 · typical: 3y annual loan, raw_M near 2
输入: [[1,2,3],[333333,333333,333334],[0.96,0.92,0.88]]
期望: 1.971015477000673
3y 年付贷款:DF 衰减微调,raw_M 略小于 2。
Case 16 · typical: bullet at t=2.5 with DF=0.92
输入: [[2.5],[50000],[0.92]]
期望: 2.5
单笔 bullet 2.5y:raw_M=2.5,无 clamp 影响。
Case 17 · typical: zero-coupon bond pays single cf at 4y
输入: [[4],[1000000],[0.82]]
期望: 4
零息债:单一 4y 现金流,raw_M=4.0。
Case 18 · typical: front-loaded cashflows give shorter M
输入: [[1,2,3,4,5],[800000,50000,50000,50000,50000],[0.96,0.96,0.96,0.96,0.96]]
期望: 1.5
现金流前置:M 接近 1(DF 同等情况下)。
Case 19 · typical: back-loaded cashflows give longer M (clamps to 5 if >5)
输入: [[1,2,3,4,5],[50000,50000,50000,50000,800000],[0.96,0.96,0.96,0.96,0.96]]
期望: 4.5
现金流后置:M 接近 5(仍在区间内)。
Case 20 · boundary: T=1 bullet at t=1.5 returns 1.5
输入: [[1.5],[10],[0.99]]
期望: 1.5
T=1,bullet 在 1.5y:raw_M=1.5。
Case 21 · boundary: tiny but nonzero weighted_total computes finite M
输入: [[2],[1e-9],[0.001]]
期望: 2
weighted_total = 1e-12 严格大于 0,正常返回 raw_M=2.0。
Case 22 · boundary: all DFs = 1.0 (zero-rate scenario)
输入: [[1,2,3,4],[10,20,30,40],[1,1,1,1]]
期望: 3
DF 全为 1.0:M 为简单加权(仅按 cf 权重)。
Case 23 · boundary: max-magnitude cash flow with smallest valid DF
输入: [[3],[1000000000],[1e-9]]
期望: 3
极端:cf=1e9、DF=1e-9 仍构成有限 weighted_total,M=3.0。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example: 5y amortising loan with declining DFs
输入: [[1,2,3,4,5],[200000,200000,200000,200000,200000],[0.97,0.94,0.91,0.88,0.85]]
期望: 2.934065934065934
5 年等额贷款:weighted_total=910000,weighted_time=2670000,raw_M≈2.934 在[1,5]内,无 clamp。
Case 2 · typical: single bullet at t=3y returns M=3 (no clamp)
输入: [[3],[1000000],[0.9]]
期望: 3
单笔 bullet:raw_M = 3*c*d/(c*d) = 3.0,位于 [1,5] 内,M=3.0。
Case 3 · typical: equal flows at t=1,2,3 with DF=1 returns M=2
输入: [[1,2,3],[100,100,100],[1,1,1]]
期望: 2
等额、DF=1:raw_M = (1+2+3)/3 = 2.0。
Case 4 · typical: 10-period semi-annual bond with exp-decay DFs
输入: [[0.5,1,1.5,2,2.5,3,3.5,4,4.5,5],[50000,50000,50000,50000,50000,50000,50000,50000,50000,1050000],[0.9801986733067553,0.9607894391523232,0.9417645335842487,0.9231163463866358,0.9048374180359595,0.8869204367171575,0.8693582353988059,0.8521437889662113,0.835270211411272,0.8187307530779818]]
期望: 4.174312056396272
10 期半年付债券:尾期含本金 1.05M,PV 加权后 raw_M ≈ 4.174 年。
Case 5 · boundary: T=0 returns NaN
输入: [[],[],[]]
期望: "NaN"
T=0:M 未定义,返回 NaN。
Case 6 · boundary: all cash flows zero returns NaN
输入: [[1,2,3],[0,0,0],[0.95,0.9,0.85]]
期望: "NaN"
全部 cash_flow=0:weighted_total=0,返回 NaN。
Case 7 · boundary: single bullet at t=0.5y clamped UP to 1.0
输入: [[0.5],[500000],[0.99]]
期望: 1
单笔 0.5y:raw_M=0.5,clamp 升到 1.0(IRB 下限)。
Case 8 · boundary: single bullet at t=7y clamped DOWN to 5.0
输入: [[7],[500000],[0.7]]
期望: 5
单笔 7y:raw_M=7.0,clamp 降到 5.0(IRB 上限)。
Case 9 · boundary: bullet at t=0.001y clamped UP to 1.0 (extreme floor)
输入: [[0.001],[100],[1]]
期望: 1
极短 bullet:0.001y clamp 升到 1.0。
Case 10 · boundary: bullet at t=30y clamped DOWN to 5.0 (extreme cap)
输入: [[30],[100],[0.05]]
期望: 5
极长 bullet:30y clamp 降到 5.0。
Case 11 · boundary: bullet at t=1.0 returns exactly 1.0
输入: [[1],[12345],[0.97]]
期望: 1
bullet 在 1.0y:raw_M=1.0,无 clamp 影响。
Case 12 · boundary: bullet at t=5.0 returns exactly 5.0
输入: [[5],[777.7],[0.85]]
期望: 5
bullet 在 5.0y:raw_M=5.0,无 clamp 影响。
Case 13 · boundary: zero cash flows in some periods drop out
输入: [[1,2,3,4],[0,100,0,100],[0.95,0.9,0.85,0.8]]
期望: 2.9411764705882355
零现金流期被自然剔除:M 由 t=2 与 t=4 的加权均值决定。
Case 14 · boundary: T=1 with zero cash flow returns NaN
输入: [[2.5],[0],[0.9]]
期望: "NaN"
T=1,cf=0:weighted_total=0,返回 NaN。
Case 15 · typical: 3y annual loan, raw_M near 2
输入: [[1,2,3],[333333,333333,333334],[0.96,0.92,0.88]]
期望: 1.971015477000673
3y 年付贷款:DF 衰减微调,raw_M 略小于 2。
Case 16 · typical: bullet at t=2.5 with DF=0.92
输入: [[2.5],[50000],[0.92]]
期望: 2.5
单笔 bullet 2.5y:raw_M=2.5,无 clamp 影响。
Case 17 · typical: zero-coupon bond pays single cf at 4y
输入: [[4],[1000000],[0.82]]
期望: 4
零息债:单一 4y 现金流,raw_M=4.0。
Case 18 · typical: front-loaded cashflows give shorter M
输入: [[1,2,3,4,5],[800000,50000,50000,50000,50000],[0.96,0.96,0.96,0.96,0.96]]
期望: 1.5
现金流前置:M 接近 1(DF 同等情况下)。
Case 19 · typical: back-loaded cashflows give longer M (clamps to 5 if >5)
输入: [[1,2,3,4,5],[50000,50000,50000,50000,800000],[0.96,0.96,0.96,0.96,0.96]]
期望: 4.5
现金流后置:M 接近 5(仍在区间内)。
Case 20 · boundary: T=1 bullet at t=1.5 returns 1.5
输入: [[1.5],[10],[0.99]]
期望: 1.5
T=1,bullet 在 1.5y:raw_M=1.5。
Case 21 · boundary: tiny but nonzero weighted_total computes finite M
输入: [[2],[1e-9],[0.001]]
期望: 2
weighted_total = 1e-12 严格大于 0,正常返回 raw_M=2.0。
Case 22 · boundary: all DFs = 1.0 (zero-rate scenario)
输入: [[1,2,3,4],[10,20,30,40],[1,1,1,1]]
期望: 3
DF 全为 1.0:M 为简单加权(仅按 cf 权重)。
Case 23 · boundary: max-magnitude cash flow with smallest valid DF
输入: [[3],[1000000000],[1e-9]]
期望: 3
极端:cf=1e9、DF=1e-9 仍构成有限 weighted_total,M=3.0。