← 返回编程题库
coding-pv-of-post-default-recovery-stream中等免费版2000ms未尝试

Present Value of a Post-Default Recovery Cash-Flow Stream

Present Value of a Post-Default Recovery Cash-Flow Stream

开始编码

实现 solution(exposure_at_default: float, recovery_rates_by_year: list[float], workout_discount_factors: list[float]) -> float。借款人在 t = 0 违约后,银行不会损失全部 EAD——它会在多年的清收期内(处置抵押、法庭和解、债务重组等)按一定比例分批收回原始违约风险敞口。第 t 年的回收现金流为 exposure_at_default * recovery_rates_by_year[t] 美元,银行用每年的折现因子 workout_discount_factors[t] 把该现金流折回到 t = 0。返回整条回收流的现值,单浮点数(美元、非负)。

公式是一个长度为 T 的内积,再乘以 EAD:

recovery_pv = exposure_at_default * sum_{t=0..T-1} recovery_rates_by_year[t] * workout_discount_factors[t]

这是 IRB / IFRS 9 的标准分解:回收率向量描述每个清收年"能拿回 EAD 的多少",折现因子向量描述"第 t 年到账的钱相当于今天的多少"。两者按年相乘后求和,得到回收流的现值;再乘以 EAD,把分数化的答案换算回美元。

Example

solution(1000.0, [0.3, 0.2, 0.1], [0.97, 0.94, 0.91]) 返回 570.0。逐项展开:0.3 * 0.97 = 0.2910.2 * 0.94 = 0.1880.1 * 0.91 = 0.091。求和 = 0.291 + 0.188 + 0.091 = 0.570。PV = 1000 * 0.570 = 570.0。在 $1000 的 EAD 下,银行预计能收回 $570 的现值;剩下 $430 的折现损失流入 LGD / IFRS 9 减值准备。

易错点与边界

本题在公式上有四个高杠杆错误:

  1. 逐年 DF,不是单一全局 DF。 每一年的回收率乘的是"该年自己的"DF。先把 recovery_rates_by_year 求和再乘一个代表性 DF 会丢掉时点折现,把所有不同的清收节奏定一样的价。
  2. EAD 乘的是"求和",不是每一项都再乘一次。 pv = EAD * sum(rate * df)sum(EAD * rate * df) 等价;真正的 BUG 是 EAD * sum(EAD * rate * df)——把 EAD 乘了两次,PV 多出一个 EAD 因子。
  3. 回收率之和可以 < 1。 部分回收是受损债权的常态;不要把 recovery_rates_by_year 标准化到总和为 1,否则会悄悄把场景改成"全额回收"。
  4. DF 随时间递减,不要写 1/DF。 workout_discount_factors[t](0, 1]、且随 t 减小;1/DF 是把过去推向未来的放大因子,方向反了,错用会让 PV 沿错误方向放大。

边界情形都通过同一个内积自然处理:

  • T = 0(空输入):空和为 0pv = 0
  • 所有 recovery_rates_by_year[t] = 0:每一项都为 0pv = 0
  • exposure_at_default = 0:外层因子为 0pv = 0
  • 单年回收(T = 1):pv = EAD * rate[0] * df[0]
  • 回收率之和为 1 且所有 df = 1pv = EAD(名义全额回收、无折现)。

函数不抛异常。在所有输入都在声明范围内时,pv >= 0。复杂度 O(T) 时间,O(1) 额外空间——一遍线性扫描算内积。

完整函数骨架见 stubs/stub.py

实务背景

在典型的信贷风险条线上,回收现金流图谱是 LGD / EL 模型的两大支柱之一(另一支柱是 PD 期限结构)。违约发生后,清收团队基于抵押处置回款、法庭判赔、债务重组安排,估出一条"逐年回收率"向量——典型担保贷款 1-3 年,复杂破产 5 年或更长。折现因子向量来自银行自身的资金曲线(或 IRB 框架下监管指定的曲线)。回收流的现值直接进入 (1 - LGD) 用于美元化预期损失计算、进入 IFRS 9 第二/三阶段终身 EL 现金流模型、以及抵押管理决策中的"清收 NPV"输入。任何一个逐年乘错(单一全局 DF、把 EAD 乘了两遍、强制把回收率归一化、或把 DF 写成 1/DF)都会直接传导到对外披露的 LGD、Pillar 1 监管资本以及 IFRS 9 准备金计提。

约束条件

  • 0 <= len(recovery_rates_by_year) == len(workout_discount_factors) <= 30
  • 0.0 <= exposure_at_default <= 1e9
  • 0.0 <= recovery_rates_by_year[t] <= 1.0(每年的回收率,相对 EAD 的比例;总和可以 < 1,对应部分回收)
  • 0.0 < workout_discount_factors[t] <= 1.0(DF 是第 t 年 1 元的今天现值,随 t 递减)
  • 输出是非负浮点数:以美元计的 PV
  • 比较使用 rel_tol=1e-9, abs_tol=1e-9

样例

Case 1 · statement-example: 3-year recovery profile, EAD=1000, dot=0.570

输入: [1000,[0.3,0.2,0.1],[0.97,0.94,0.91]]

期望: 570

逐年内积:0.3*0.97=0.291, 0.2*0.94=0.188, 0.1*0.91=0.091;和=0.570;PV=1000*0.570=570.0。

Case 2 · typical: 3-year realistic profile EAD=1e8 (institutional scale)

输入: [100000000,[0.3,0.15,0.05],[0.97,0.94,0.91]]

期望: 47750000

1e8 * (0.30*0.97 + 0.15*0.94 + 0.05*0.91) = 1e8 * 0.4775 = 4.775e7。

Case 3 · boundary: T=0 empty inputs returns 0.0

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

期望: 0

空输入:求和为空 → pv = 0.0。无需特判分支。

Case 4 · boundary: exposure_at_default=0 zeroes out the sum

输入: [0,[0.5,0.3],[0.95,0.9]]

期望: 0

EAD=0 → 外层因子为 0;无论内积是多少,pv = 0.0。

Case 5 · boundary: single-year recovery pv = EAD * rate * DF

输入: [500,[0.4],[0.95]]

期望: 190

T=1:pv = EAD * rate[0] * DF[0] = 500 * 0.4 * 0.95 = 190.0。

Case 6 · boundary: rates sum to 1 and all DF=1 give pv = EAD

输入: [2000,[0.4,0.6],[1,1]]

期望: 2000

回收率之和=1 且所有 DF=1(无折现)→ pv = EAD = 2000。完全名义回收。

最近提交

还没有提交记录。

编码区

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

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

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

Case 1 · statement-example: 3-year recovery profile, EAD=1000, dot=0.570

输入: [1000,[0.3,0.2,0.1],[0.97,0.94,0.91]]

期望: 570

逐年内积:0.3*0.97=0.291, 0.2*0.94=0.188, 0.1*0.91=0.091;和=0.570;PV=1000*0.570=570.0。

Case 2 · typical: 3-year realistic profile EAD=1e8 (institutional scale)

输入: [100000000,[0.3,0.15,0.05],[0.97,0.94,0.91]]

期望: 47750000

1e8 * (0.30*0.97 + 0.15*0.94 + 0.05*0.91) = 1e8 * 0.4775 = 4.775e7。

Case 3 · boundary: T=0 empty inputs returns 0.0

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

期望: 0

空输入:求和为空 → pv = 0.0。无需特判分支。

Case 4 · boundary: exposure_at_default=0 zeroes out the sum

输入: [0,[0.5,0.3],[0.95,0.9]]

期望: 0

EAD=0 → 外层因子为 0;无论内积是多少,pv = 0.0。

Case 5 · boundary: single-year recovery pv = EAD * rate * DF

输入: [500,[0.4],[0.95]]

期望: 190

T=1:pv = EAD * rate[0] * DF[0] = 500 * 0.4 * 0.95 = 190.0。

Case 6 · boundary: rates sum to 1 and all DF=1 give pv = EAD

输入: [2000,[0.4,0.6],[1,1]]

期望: 2000

回收率之和=1 且所有 DF=1(无折现)→ pv = EAD = 2000。完全名义回收。