← 返回编程题库
coding-credit-portfolio-hhi-and-effective-n中等免费版2000ms未尝试

信贷组合集中度:由对手方 EAD 计算 HHI 与有效对手方数

Credit Portfolio Concentration: HHI and Effective Number of Counterparties from Per-Obligor EAD

开始编码

实现 solution(ead: list[float]) -> list[float]。信贷风险官每日需要监控公司贷款账面对手方集中度。ead[i] 是第 i 个对手方的违约风险敞口(Exposure-at-Default,单位美元,非负)。函数返回长度-2 列表 [hhi, effective_n],刻画该账面在对手方维度的集中程度。

输出由如下可观测公式定义:

  1. 总 EADtotal_ead = sum(ead)。一遍遍历。
  2. 每个对手方的份额share[i] = ead[i] / total_ead。每个份额是占总组合 EAD 的比例;按定义份额之和为 1。
  3. HHIhhi = sum_i share[i] ** 2。Herfindahl-Hirschman 指数求和的是份额的平方,不是份额本身。
  4. 有效对手方数effective_n = 1.0 / hhi。这是 order-2 的 Hill 数——产生相同 HHI 的等权对手方数。
  5. 输出:长度 2 的列表 [hhi, effective_n],顺序固定。

退化输入哨兵:

  • len(ead) == 0 -> [float('nan'), float('nan')]。根本没有组合。
  • total_ead == 0(每个 EAD 都是零) -> [float('nan'), float('nan')]。没有可分散的敞口;两个指标都数学未定义。

一旦 total_ead > 0,至少一个份额为正,hhi > 01.0 / hhi 这一步除法就是良定义的。

solution([10.0, 10.0, 10.0, 10.0]) 返回 [0.25, 4.0]。四户等权,total_ead = 40,每个 share[i] = 0.25hhi = 4 * 0.25 ** 2 = 0.25。有效对手方数 1 / 0.25 = 4 ——恰好等于输入头数,因为账面处于最分散极限 1/N

一户独占极限算例:solution([100.0, 0.0, 0.0, 0.0]) 返回 [1.0, 1.0]。总 EAD 为 100,只有第一户有非零份额(1.0),其余三户为零。hhi = 1.0 ** 2 + 0 + 0 + 0 = 1.0effective_n = 1 / 1 = 1。函数把账面认作一户单名押注——它就是。

混合集中度算例:solution([60.0, 30.0, 10.0]) 返回 [0.46, 1/0.46] = [0.46, 2.1739130434782608]。份额为 0.6, 0.3, 0.1hhi = 0.36 + 0.09 + 0.01 = 0.46effective_n = 1 / 0.46 ~= 2.174。即使账面上挂着三户,整个账面表现像 2.17 户等权——60% 的主导户把有效对手方数压到了头数以下。

边界:空输入返回 [NaN, NaN]。全零 EAD(如 [0.0, 0.0, 0.0])返回 [NaN, NaN]。只有一个非零 EAD 其余为零(如 [100.0, 0.0, 0.0])返回 [1.0, 1.0] ——一户独占极限。仅含一个正 EAD 元素的输入返回 [1.0, 1.0],同理。HHI 对 EAD 尺度不变:solution([10.0, 10.0])solution([1e9, 1e9]) 都返回 [0.5, 2.0],因为美元单位在份额内抵消。本函数对约束区间内任何输入都不抛异常;NaN 哨兵是这两个退化情形的合同输出。

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

实践背景

Herfindahl-Hirschman 指数是信贷风险与反垄断分析里标准的集中度度量。对一个信贷组合而言,HHI 接近 1/N 表示账面在对手方维度高度分散;HHI 接近 1 表示存在单名集中度风险——一两户对手方主导了 EAD。effective_n = 1 / HHI 把 HHI 翻译成非量化人员也能直读的数:「这个账面表现得像我手里有 X 户等权对手方」,与实际头数无关。一个挂名 N = 100effective_n = 5 的账面不是百名账面——它是五名账面加九十五条小尾巴。solution(...) 输出的 [hhi, effective_n] 是信贷组合看板上的每日集中度诊断,与单名限额检查、按桶预期损失合计并列。本函数刻意把以下几条概念约定显式化——份额必须先按总 EAD 归一化再平方;正是平方这一步把指标从平凡的求和-为-一变成集中度指数;输出顺序是 [hhi, effective_n] 不是反过来;两个 NaN 哨兵在不抛异常的前提下覆盖空输入和全零 EAD 两个退化情形——这些正是生产监控代码反复抓到的方向性错误。纯 O(N) 实现(一遍求和、一遍累加平方份额)是小账面的标准模板;面对百万户对手方的生产系统按同一递推式做单遍流式累加,但合同完全相同。

约束条件

  • 0 <= len(ead) <= 200
  • 0.0 <= ead[i] <= 1e9
  • 输出:长度 2 的 list[float],顺序固定为 [hhi, effective_n]
  • 当 N >= 1 且 total_ead > 0 时,hhi 在 [1/N, 1] 范围、effective_n 在 [1, N] 范围;否则为 [NaN, NaN]
  • 浮点比较器使用 rel_tol=1e-9, abs_tol=1e-9, nan_equals_nan=true

样例

Case 1 · statement-example: four equal obligors max-diversified limit

输入: [[10,10,10,10]]

期望: [0.25,4]

total_ead = 40;每个 share[i] = 0.25;hhi = 4 * 0.25**2 = 0.25;effective_n = 1 / 0.25 = 4。账面处于 N = 4 时的最分散极限 1/N。

Case 2 · statement-example: all-in-one-name limit

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

期望: [1,1]

total_ead = 100;只有第一个 share 非零 (1.0);hhi = 1.0**2 = 1.0;effective_n = 1 / 1 = 1。账面是单名押注。

Case 3 · statement-example: mixed concentration three-name book

输入: [[60,30,10]]

期望: [0.45999999999999996,2.173913043478261]

total_ead = 100;shares = [0.6, 0.3, 0.1];hhi = 0.36 + 0.09 + 0.01 = 0.46;effective_n = 1 / 0.46 ~= 2.174。60% 的主导户把 effective_n 拉到 3 户头数以下。

Case 4 · visible: empty input returns NaN sentinel pair

输入: [[]]

期望: ["NaN","NaN"]

len(ead) == 0 -> [NaN, NaN];根本没有组合。

Case 5 · visible: all-zero EAD returns NaN sentinel pair

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

期望: ["NaN","NaN"]

total_ead == 0 -> [NaN, NaN];没有可分散的敞口。

最近提交

还没有提交记录。

编码区

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

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

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

Case 1 · statement-example: four equal obligors max-diversified limit

输入: [[10,10,10,10]]

期望: [0.25,4]

total_ead = 40;每个 share[i] = 0.25;hhi = 4 * 0.25**2 = 0.25;effective_n = 1 / 0.25 = 4。账面处于 N = 4 时的最分散极限 1/N。

Case 2 · statement-example: all-in-one-name limit

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

期望: [1,1]

total_ead = 100;只有第一个 share 非零 (1.0);hhi = 1.0**2 = 1.0;effective_n = 1 / 1 = 1。账面是单名押注。

Case 3 · statement-example: mixed concentration three-name book

输入: [[60,30,10]]

期望: [0.45999999999999996,2.173913043478261]

total_ead = 100;shares = [0.6, 0.3, 0.1];hhi = 0.36 + 0.09 + 0.01 = 0.46;effective_n = 1 / 0.46 ~= 2.174。60% 的主导户把 effective_n 拉到 3 户头数以下。

Case 4 · visible: empty input returns NaN sentinel pair

输入: [[]]

期望: ["NaN","NaN"]

len(ead) == 0 -> [NaN, NaN];根本没有组合。

Case 5 · visible: all-zero EAD returns NaN sentinel pair

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

期望: ["NaN","NaN"]

total_ead == 0 -> [NaN, NaN];没有可分散的敞口。