由 PIT 序列估计跨周期 PD:存活率的几何平均
Through-the-Cycle PD from a Point-in-Time Series: Geometric Mean of Survivals
开始编码实现 solution(pit_pds: list[float]) -> float。某信用风险口在最近若干期上跑了一个 Point-in-Time(PIT)PD 模型,得到序列 pit_pds,其中 pit_pds[t] 是模型在第 t 期的 PIT 违约概率估计。该口需要把这条序列折成一个跨周期(TTC)PD——能复现整段周期存活的"代表性单期违约率"——以输入终生预期信用损失瀑布。TTC 约定为"1 减去各期存活概率的几何平均":
对每个 t: survival[t] = 1 - pit_pds[t]
geometric_mean_survival = (prod_t survival[t]) ** (1 / T)
ttc_pd = 1 - geometric_mean_survival若输入序列为空(T = 0),返回 NaN(无观测;长期均值未定义)。若任意 pit_pds[t] == 1.0(某期必违约),存活几何平均为 0,ttc_pd = 1.0,精确。
**几何平均取在"存活" 1 - pit_pds[t] 上,不是直接取在 pit_pds[t] 上。** 原因是乘法的:穿越整段周期意味着每期都活下来,所以周期存活是各期存活的乘积;T 次方后能复现该乘积的"代表性单期速率",是各期存活的几何平均。直接对 pit_pds 取几何平均会得到一个不同、有偏的数(仅在常数 p 等退化情形下与存活法一致)。
例
solution([0.04, 0.16]) 返回约 0.10200222717425411。手算:survival = [0.96, 0.84];乘积 0.8064;几何平均 sqrt(0.8064) = 0.8979977728...;ttc_pd = 1 - 0.8979977728... = 0.1020022271...。对比错误"对 pit_pds 取几何平均":sqrt(0.04 * 0.16) = sqrt(0.0064) = 0.08,明显不同且有偏。
常数序列 sanity check:solution([0.05, 0.05, 0.05, 0.05]) 精确返回 0.05(相等存活 0.95 的几何平均仍是 0.95,1 - 0.95 = 0.05)。T = 1 sanity check:solution([0.07]) 精确返回 0.07(单值的几何平均就是它本身)。空输入:solution([]) 返回 NaN。必违约:solution([0.10, 1.0, 0.05]) 返回 1.0(第二期保证违约,周期存活为 0)。
期望算法 O(T),并用对数避免长序列下小存活值的乘积下溢:log_geo = (1/T) * sum_t log(1 - pit_pds[t]),再 ttc_pd = 1 - exp(log_geo)。先短路"任意 pit_pds[t] == 1.0 分支",避免去算 log(0) = -inf;显式守卫比依赖 -inf 在 exp 与减法中的传播更干净。
实现细节由 stubs/stub.py 提供。
实践背景
某信贷台持有长久期贷款组合,标定的 PIT PD 模型对宏观周期高度敏感:估值在衰退时陡升、扩张时回落。监管(巴塞尔)与会计准则(IFRS 9、CECL)有时要求一个 TTC PD——一个稳定的、跨周期平均的代表性单期违约率——作为终生预期损失瀑布的输入,或作为对照 PIT 估计是否反应过度的基准。"存活率几何平均"约定是把一条 PIT 时间序列折成单一 TTC 数字的标准做法:它保留了存活概率的乘法复合逻辑,常数周期上与 PIT 完全相等,混合序列下则落在最小与最大 PIT 之间。直接对 pit_pds 取算术平均会高估长期速率(存活在 PD 上凹,Jensen 不等式生效),直接对 pit_pds 取几何平均则混淆了量纲——它仅在退化的常数 p 情形下恰巧与存活法重合,对任何非常数序列都会得到有偏数。solution(...) 的输出就是该口在终生预期损失瀑布里要乘 LGD 与 EAD 的"TTC PD"那一列。
约束条件
- 0 <= T == len(pit_pds) <= 60
- 0.0 <= pit_pds[t] <= 1.0(是概率,不是百分数)
- T >= 1 时输出为 [0.0, 1.0] 内的 float;T == 0 时输出 NaN;按 rel_tol=1e-9、abs_tol=1e-9 比对
样例
Case 1 · statement-example: T=2 pit=[0.04, 0.16] gives ttc ≈ 0.10200
输入: [[0.04,0.16]]
期望: 0.10200222717425411
存活=[0.96,0.84],乘积 0.8064,几何平均 sqrt(0.8064)≈0.8980,TTC=1-0.8980≈0.10200。
Case 2 · visible: T=1 returns pit_pds[0] exactly
输入: [[0.07]]
期望: 0.07000000000000006
T=1 时单值的几何平均就是其本身,TTC = pit_pds[0] = 0.07。
Case 3 · visible: empty input returns NaN
输入: [[]]
期望: "NaN"
T=0 无观测,长期均值未定义,返回 NaN。
Case 4 · visible: constant series pit=[0.05]*4 gives ttc=0.05 exactly
输入: [[0.05,0.05,0.05,0.05]]
期望: 0.050000000000000044
存活均为 0.95,几何平均 0.95,TTC = 1 - 0.95 = 0.05。
Case 5 · visible: any pit_pd == 1.0 collapses ttc to 1.0
输入: [[0.1,1,0.05]]
期望: 1
中间一期必违约,周期存活为 0,TTC = 1.0。
Case 6 · typical: T=3 pit=[0.02,0.03,0.05] mixed small PDs
输入: [[0.02,0.03,0.05]]
期望: 0.03341406408115222
存活 [0.98,0.97,0.95],几何平均 ((0.98*0.97*0.95))^(1/3),TTC = 1 - 该值。
Case 7 · typical: T=5 cycle PDs spanning recession-expansion
输入: [[0.02,0.04,0.12,0.08,0.03]]
期望: 0.058743672977418115
中间一期反映衰退,TTC 在最小与最大 PIT 之间。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example: T=2 pit=[0.04, 0.16] gives ttc ≈ 0.10200
输入: [[0.04,0.16]]
期望: 0.10200222717425411
存活=[0.96,0.84],乘积 0.8064,几何平均 sqrt(0.8064)≈0.8980,TTC=1-0.8980≈0.10200。
Case 2 · visible: T=1 returns pit_pds[0] exactly
输入: [[0.07]]
期望: 0.07000000000000006
T=1 时单值的几何平均就是其本身,TTC = pit_pds[0] = 0.07。
Case 3 · visible: empty input returns NaN
输入: [[]]
期望: "NaN"
T=0 无观测,长期均值未定义,返回 NaN。
Case 4 · visible: constant series pit=[0.05]*4 gives ttc=0.05 exactly
输入: [[0.05,0.05,0.05,0.05]]
期望: 0.050000000000000044
存活均为 0.95,几何平均 0.95,TTC = 1 - 0.95 = 0.05。
Case 5 · visible: any pit_pd == 1.0 collapses ttc to 1.0
输入: [[0.1,1,0.05]]
期望: 1
中间一期必违约,周期存活为 0,TTC = 1.0。
Case 6 · typical: T=3 pit=[0.02,0.03,0.05] mixed small PDs
输入: [[0.02,0.03,0.05]]
期望: 0.03341406408115222
存活 [0.98,0.97,0.95],几何平均 ((0.98*0.97*0.95))^(1/3),TTC = 1 - 该值。
Case 7 · typical: T=5 cycle PDs spanning recession-expansion
输入: [[0.02,0.04,0.12,0.08,0.03]]
期望: 0.058743672977418115
中间一期反映衰退,TTC 在最小与最大 PIT 之间。