← 返回编程题库
coding-lgd-from-collateral-recovery-waterfall中等免费版2000ms未尝试

基于抵押—无担保瀑布的违约损失率(LGD)

LGD via Collateral-Then-Unsecured Recovery Waterfall

开始编码

实现 solution(ead: float, collateral_value: float, collateral_haircut: float, unsecured_recovery_rate: float) -> float。某信用风险口按贷款(或对手方)计算违约损失率(LGD),方法是跑标准的监管瀑布:先用抵押(按监管 haircut 折扣)、再对剩余无担保敞口套用无担保回收率。输入均为标量浮点:ead 是违约时的美元敞口,collateral_value 是抵押的美元毛额,collateral_haircut 是抵押的监管 haircut(不可实现的比例——0.20 haircut 意为折掉 20%、剩 80%),unsecured_recovery_rate 是仅作用于无担保残值的回收率。返回 [0.0, 1.0] 区间内的浮点 LGD。

瀑布共六步。第 1 步:effective_collateral = collateral_value * (1.0 - collateral_haircut)。第 2 步:collateral_offset = min(effective_collateral, ead)——超额抵押不会产生额外回收,抵扣在 EAD 处封顶。第 3 步:unsecured_exposure = ead - collateral_offset——抵押侧之后留下的无担保残值。第 4 步:unsecured_recovery = unsecured_exposure * unsecured_recovery_rate——无担保回收率仅作用于残值、不作用于毛 EAD。第 5 步:total_recovery = collateral_offset + unsecured_recovery。第 6 步:lgd = (ead - total_recovery) / ead——LGD 是未回收敞口与毛敞口之比,是 [0, 1] 内的分数,不是美元金额。

solution(100.0, 60.0, 0.10, 0.40) 返回 0.276。逐步走瀑布:有效抵押 = 60 * (1 - 0.10) = 54。抵押抵扣 = min(54, 100) = 54。无担保敞口 = 100 - 54 = 46。无担保回收 = 46 * 0.40 = 18.4。总回收 = 54 + 18.4 = 72.4。LGD = (100 - 72.4) / 100 = 0.276。模型瀑布下,每 100 美元敞口可回收 72.4 美元,因此模型损失 = 敞口的 27.6%。

哨兵与陷阱

ead == 0 时无敞口可失,LGD 比率 0/0 数学上无定义。规约要求做除法前检测此情形并返回 float('nan')——本函数永不抛 ZeroDivisionError。比对器配 nan_equals_nan: true,测试框架内 NaN == NaN

四个高杠杆陷阱:

  1. haircut 方向。有效抵押是 * (1 - haircut)不是 * haircut。haircut=0.20 表示折掉 20%、剩 80%。把 (1-haircut) 写成 haircut 会让 60 美元抵押在 0.20 haircut 下"可实现 12 美元"(错),正解为 48 美元。
  2. 抵押在 EAD 处封顶。超额抵押不会产生负 LGD 或额外回收。min(effective_collateral, ead) 让 100 美元敞口下持有 1000 美元抵押的抵扣只到 100、不是 1000。漏封顶会悄悄得到 LGD<0。
  3. 无担保回收率只作用于残值unsecured_recovery = (ead - collateral_offset) * unsecured_recovery_rate,不是 ead * unsecured_recovery_rate。后者把同一笔美元算了两遍:抵押覆盖一次、按无担保率再"回收"一次。
  4. LGD 是分数、不是美元。返回 (ead - total_recovery) / ead,不是 (ead - total_recovery)。规约要的是标准 LGD 比率、无量纲、被界定在 [0, 1]

更多细节:所有输入位于声明区间且封顶到位时,LGD 在数学上自然位于 [0, 1],无需 max(0, min(1, lgd)) 防御性截断;加上截断在良性输入上不改结果,但会掩盖底层公式里的 bug。函数 O(1) 时间、O(1) 空间——无循环、无分配。

实现细节由 stubs/stub.py 提供。

实践背景

每个汇报周期,企业信贷台或对手方信用风险口都会按贷款或按对手方计算 LGD:先用抵押(按监管 haircut 折扣以反映清算摩擦、市场波动与操作摩擦),再对剩余敞口套用无担保回收率。两侧均由 Basel II/III 内评法(IRB)框架与 IFRS 9 / CECL 预期信用损失方法显式要求。solution(...) 的输出是喂入风险台每日预期损失聚合器 EL = PD * LGD * EAD(兄弟题 coding-portfolio-expected-loss-by-bucket)的 LGD。四个陷阱中任何一个写错——haircut 方向、抵押封顶、无担保残值范围、分数 vs 美元——都会直接传导到上报的 EL 数字、Pillar 1 监管资本、以及 IFRS 9 stage-2/stage-3 拨备建仓上。

约束条件

  • 0.0 <= ead <= 1e9(违约时敞口、美元、非负)
  • 0.0 <= collateral_value <= 1e10(毛抵押、可超过 EAD)
  • 0.0 <= collateral_haircut <= 1.0(被**折扣**的比例,不是可实现比例)
  • 0.0 <= unsecured_recovery_rate <= 1.0(无担保残值上的回收率)
  • 输出为 float:LGD 分数 ∈ [0.0, 1.0],或 ead == 0 时 float('nan')
  • 全部按 rel_tol=1e-9、abs_tol=1e-9 比对;nan_equals_nan 为 true

样例

Case 1 · statement-example: ead=100, coll=60, hc=0.10, urr=0.40 mixed waterfall

输入: [100,60,0.1,0.4]

期望: 0.27599999999999997

有效抵押 = 60*(1-0.10)=54;抵押抵扣 min(54,100)=54;无担保敞口 100-54=46;无担保回收 46*0.40=18.4;总回收 54+18.4=72.4;LGD=(100-72.4)/100=0.276。

Case 2 · boundary: ead=0 returns NaN sentinel (LGD undefined when no exposure)

输入: [0,50,0.2,0.4]

期望: "NaN"

ead=0 触发哨兵:LGD 在无敞口时无定义,返回 NaN(不能让 Python 抛 ZeroDivisionError)。

Case 3 · boundary: no collateral, urr=0 yields lgd=1.0 (total loss)

输入: [1000,0,0,0]

期望: 1

无抵押 + 无担保回收率 0 = 全部损失,LGD=1.0。

Case 4 · boundary: no collateral, urr=1 yields lgd=0.0 (full unsecured recovery)

输入: [1000,0,0,1]

期望: 0

无抵押但无担保回收率 1.0 表示无担保部分全额回收,LGD=0.0。

Case 5 · boundary: no collateral, urr=0.40 yields lgd=0.60

输入: [1000,0,0.5,0.4]

期望: 0.6

无抵押则有效抵押=0、抵押抵扣=0、无担保敞口=ead;LGD = 1 - urr = 1 - 0.40 = 0.60。haircut 在没有抵押时与结果无关。

Case 6 · typical: full haircut hc=1.0 collapses collateral; lgd=1-urr

输入: [500,200,1,0.3]

期望: 0.7

haircut=1.0 表示 100% 折扣,全部不可实现:有效抵押=200*(1-1)=0;与无抵押情形一致;LGD = 1 - 0.30 = 0.70。

Case 7 · typical: zero haircut hc=0.0 full collateral realisable, partial cover

输入: [1000,400,0,0.5]

期望: 0.3

haircut=0 表示无折扣:有效抵押=400;抵扣=400;无担保=600;无担保回收=600*0.5=300;总回收=400+300=700;LGD=(1000-700)/1000=0.30。

Case 8 · boundary: over-collateralised effective>=ead yields lgd=0.0 (cap at EAD)

输入: [100,500,0,0.2]

期望: 0

有效抵押=500 > ead=100;抵押抵扣按 min(500,100)=100 封顶;总回收=100;LGD=0;不会因为超额抵押而出现负 LGD。

Case 9 · boundary: effective EXACTLY equals EAD (boundary of cap, no residual)

输入: [100,200,0.5,0.3]

期望: 0

有效抵押=200*0.5=100,正好等于 ead=100;抵扣=100,无担保敞口=0,总回收=100,LGD=0。

Case 10 · typical: ead=1e6, coll=4e5, hc=0.20, urr=0.40 (industry-scale exemplar)

输入: [1000000,400000,0.2,0.4]

期望: 0.408

有效抵押=400000*0.80=320000;抵扣=320000;无担保=680000;无担保回收=680000*0.40=272000;总回收=592000;LGD=(1e6-592000)/1e6=0.408。

Case 11 · large: max-scale ead=1e9, coll=1e10 over-collateralised => lgd=0

输入: [1000000000,10000000000,0,0]

期望: 0

ead=1e9 边界、抵押=1e10 远超:抵押抵扣按 min(1e10,1e9)=1e9 封顶;总回收=1e9;LGD=0。

Case 12 · adversarial: haircut-direction trap — hc=0.20 means 80% remains, NOT 20%

输入: [1000,1000,0.2,0]

期望: 0.2

若误用 *haircut(=*0.20)会得到 effective=200,LGD=0.80(错);正解 effective=1000*(1-0.20)=800,抵扣=800,urr=0 故 LGD=200/1000=0.20。

Case 13 · adversarial: collateral-cap trap — over-collateralised must NOT yield negative LGD

输入: [100,1000,0,0.5]

期望: 0

若不对抵押封顶则 effective=1000、'unsecured'=-900、'recovery'=1000+(-450)=550、LGD=(100-550)/100=-4.5(错)。正解 min(1000,100)=100,无担保=0,LGD=0。

Case 14 · adversarial: unsecured-recovery applies to RESIDUAL only, not gross EAD

输入: [1000,600,0,0.5]

期望: 0.2

若误用 ead*urr 则 recovery=600+1000*0.5=1100、LGD 为负(错)。正解 unsecured=1000-600=400;unsecured_recovery=400*0.5=200;总回收=800;LGD=(1000-800)/1000=0.20。

Case 15 · adversarial: LGD is a FRACTION not dollars — output must be in [0,1]

输入: [1000000,100000,0,0]

期望: 0.9

若返回美元损失=900000(错);按合约 LGD = 损失/敞口 = 900000/1000000 = 0.90,必须归一化。

最近提交

还没有提交记录。

编码区

实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。

加载编辑器...
计时0:00

默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。

Case 1 · statement-example: ead=100, coll=60, hc=0.10, urr=0.40 mixed waterfall

输入: [100,60,0.1,0.4]

期望: 0.27599999999999997

有效抵押 = 60*(1-0.10)=54;抵押抵扣 min(54,100)=54;无担保敞口 100-54=46;无担保回收 46*0.40=18.4;总回收 54+18.4=72.4;LGD=(100-72.4)/100=0.276。

Case 2 · boundary: ead=0 returns NaN sentinel (LGD undefined when no exposure)

输入: [0,50,0.2,0.4]

期望: "NaN"

ead=0 触发哨兵:LGD 在无敞口时无定义,返回 NaN(不能让 Python 抛 ZeroDivisionError)。

Case 3 · boundary: no collateral, urr=0 yields lgd=1.0 (total loss)

输入: [1000,0,0,0]

期望: 1

无抵押 + 无担保回收率 0 = 全部损失,LGD=1.0。

Case 4 · boundary: no collateral, urr=1 yields lgd=0.0 (full unsecured recovery)

输入: [1000,0,0,1]

期望: 0

无抵押但无担保回收率 1.0 表示无担保部分全额回收,LGD=0.0。

Case 5 · boundary: no collateral, urr=0.40 yields lgd=0.60

输入: [1000,0,0.5,0.4]

期望: 0.6

无抵押则有效抵押=0、抵押抵扣=0、无担保敞口=ead;LGD = 1 - urr = 1 - 0.40 = 0.60。haircut 在没有抵押时与结果无关。

Case 6 · typical: full haircut hc=1.0 collapses collateral; lgd=1-urr

输入: [500,200,1,0.3]

期望: 0.7

haircut=1.0 表示 100% 折扣,全部不可实现:有效抵押=200*(1-1)=0;与无抵押情形一致;LGD = 1 - 0.30 = 0.70。

Case 7 · typical: zero haircut hc=0.0 full collateral realisable, partial cover

输入: [1000,400,0,0.5]

期望: 0.3

haircut=0 表示无折扣:有效抵押=400;抵扣=400;无担保=600;无担保回收=600*0.5=300;总回收=400+300=700;LGD=(1000-700)/1000=0.30。

Case 8 · boundary: over-collateralised effective>=ead yields lgd=0.0 (cap at EAD)

输入: [100,500,0,0.2]

期望: 0

有效抵押=500 > ead=100;抵押抵扣按 min(500,100)=100 封顶;总回收=100;LGD=0;不会因为超额抵押而出现负 LGD。

Case 9 · boundary: effective EXACTLY equals EAD (boundary of cap, no residual)

输入: [100,200,0.5,0.3]

期望: 0

有效抵押=200*0.5=100,正好等于 ead=100;抵扣=100,无担保敞口=0,总回收=100,LGD=0。

Case 10 · typical: ead=1e6, coll=4e5, hc=0.20, urr=0.40 (industry-scale exemplar)

输入: [1000000,400000,0.2,0.4]

期望: 0.408

有效抵押=400000*0.80=320000;抵扣=320000;无担保=680000;无担保回收=680000*0.40=272000;总回收=592000;LGD=(1e6-592000)/1e6=0.408。

Case 11 · large: max-scale ead=1e9, coll=1e10 over-collateralised => lgd=0

输入: [1000000000,10000000000,0,0]

期望: 0

ead=1e9 边界、抵押=1e10 远超:抵押抵扣按 min(1e10,1e9)=1e9 封顶;总回收=1e9;LGD=0。

Case 12 · adversarial: haircut-direction trap — hc=0.20 means 80% remains, NOT 20%

输入: [1000,1000,0.2,0]

期望: 0.2

若误用 *haircut(=*0.20)会得到 effective=200,LGD=0.80(错);正解 effective=1000*(1-0.20)=800,抵扣=800,urr=0 故 LGD=200/1000=0.20。

Case 13 · adversarial: collateral-cap trap — over-collateralised must NOT yield negative LGD

输入: [100,1000,0,0.5]

期望: 0

若不对抵押封顶则 effective=1000、'unsecured'=-900、'recovery'=1000+(-450)=550、LGD=(100-550)/100=-4.5(错)。正解 min(1000,100)=100,无担保=0,LGD=0。

Case 14 · adversarial: unsecured-recovery applies to RESIDUAL only, not gross EAD

输入: [1000,600,0,0.5]

期望: 0.2

若误用 ead*urr 则 recovery=600+1000*0.5=1100、LGD 为负(错)。正解 unsecured=1000-600=400;unsecured_recovery=400*0.5=200;总回收=800;LGD=(1000-800)/1000=0.20。

Case 15 · adversarial: LGD is a FRACTION not dollars — output must be in [0,1]

输入: [1000000,100000,0,0]

期望: 0.9

若返回美元损失=900000(错);按合约 LGD = 损失/敞口 = 900000/1000000 = 0.90,必须归一化。