单一交易对手集中度:标出突破 Basel 大额风险暴露限额的对手方
Single-Name Concentration: Flag Counterparties Breaching the Basel Large-Exposure Limit
开始编码实现 solution(exposures: list[float], counterparty_id: list[int], num_counterparties: int, tier1_capital: float, limit_pct: float) -> list[int]。该行的信用风险官每日跑一次本函数,把已突破 Basel III 单一交易对手集中度限额的对手方挑出来。第 i 条记录代表一笔逐笔 Exposure-at-Default exposures[i](美元),记在 counterparty_id[i] 这个交易对手账下。对手 id 取值范围 [0, num_counterparties);从未在记录里出现的对手聚合敞口为零。tier1_capital 是该行 Tier-1 监管资本(美元);limit_pct 是单一对手限额相对 Tier-1 资本的比例(Basel 默认 0.25 = 25%)。
输出列表由如下可观测公式定义:
- 逐对手聚合:
agg[cp] = sum(exposures[i] for i in range(N) if counterparty_id[i] == cp),其中cp in [0, num_counterparties)。从未出现在记录里的对手agg[cp] = 0.0。 - 阈值:
T = limit_pct * tier1_capital。 - 判定:当且仅当
agg[cp] > T(严格大于,不是>=)时把cp写进输出。 - 排序:把被标出的 id 升序返回。无人越线时返回空列表
[]。
例
solution([100.0, 200.0, 300.0, 80.0, 80.0], [0, 1, 2, 0, 1], 4, 1000.0, 0.25) 返回 [1, 2]。tier1_capital = 1000、limit_pct = 0.25,阈值 T = 250。聚合:cp 0 的记录 100 + 80 = 180;cp 1 的 200 + 80 = 280;cp 2 的 300;cp 3 没有任何记录,所以 0。逐个对 T = 250 比:cp 0 = 180(未越线),cp 1 = 280(越线——标),cp 2 = 300(越线——标),cp 3 = 0(未越线)。按升序输出 [1, 2]。
一个逐记录看不见、聚合后才越线的算例:solution([100.0, 100.0, 100.0], [0, 0, 0], 1, 1000.0, 0.25) 返回 [0]。三笔记录单笔都是 100,远低于 250 阈值。但三笔全在 cp 0 名下,聚合敞口 300 越线。逐笔判定的写法会得到 [];正确答案是 [0]。这正是集中度限额的经济意义——监管关心银行对单一对手的总敞口,不是任何一笔的大小。
边界:exposures 为空时返回 [](任何 cp 都没有记录可聚合)。tier1_capital = 0 时 T = 0;任何聚合敞口为正的对手都会被标(严格 >),但聚合恰为 0 的对手不会被标。聚合恰好等于 T 的对手同样不会被标(再次:严格 >)。当 num_counterparties 大于实际出现过的 id 数时,没出现过的对手聚合为 0,永远过不了正阈值。输出始终升序;exact 比较器会拒绝按记录遍历顺序得到的列表,哪怕集合本身是对的。
实现细节由 stubs/stub.py 提供。
实践背景
在 Basel III 大额风险暴露框架下,每家银行必须把跨账户、跨产品、跨实体聚合后超过监管限额(历史标准是合格 Tier-1 资本的 25%)的所有交易对手报送出来。本函数 solution(...) 的输出就是信用风险官每日交给前台的那张越线名单:用于驱动减仓动作(平掉越线的那本账、把头寸换约到别的对手)、资本补充(新发 Tier-1 把分母拉高)、或者在持续越线时升级到监管沟通。聚合-判定-排序这三段是模板化的:任何一个产生"逐实体越线名单"的姊妹度量(关联方大额暴露、国别敞口限额、行业上限监控)都共用同样的形状。本函数刻意锁住的几条概念约定——先聚合再判、严格大于因为限额本身允许、升序输出便于按 id 扫读、未出现过的对手聚合为 0 不会越正阈值——正是工业集中度监控管线和监管自查里反复出现的方向性错误。一遍线性扫描聚合、一遍线性扫描判定、再对(小规模的)越线集合做最终排序,就是该日度合计的标准模式。
约束条件
- 0 <= len(exposures) == len(counterparty_id) <= 200
- 1 <= num_counterparties <= 50;0 <= counterparty_id[i] < num_counterparties
- 0.0 <= exposures[i] <= 1e9;0.0 <= tier1_capital <= 1e10
- 0.0 < limit_pct <= 1.0(Basel 默认 0.25)
- 输出:升序的 list[int],元素是 [0, num_counterparties) 范围内互不相同的对手方 id;无人越线时返回空列表 [];列表使用 exact 比较器
样例
Case 1 · statement-example: cp 1 and 2 exceed 25 percent
输入: [[100,200,300,80,80],[0,1,2,0,1],4,1000,0.25]
期望: [1,2]
聚合后 cp 0 = 180、cp 1 = 280、cp 2 = 300、cp 3 = 0;阈值 250;280 > 250 且 300 > 250,按升序输出 [1, 2]。
Case 2 · visible: empty exposures yields no flags
输入: [[],[],3,500,0.25]
期望: []
没有任何敞口记录,所有 cp 的聚合值为 0,阈值 125 > 0,无人越线,返回 []。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example: cp 1 and 2 exceed 25 percent
输入: [[100,200,300,80,80],[0,1,2,0,1],4,1000,0.25]
期望: [1,2]
聚合后 cp 0 = 180、cp 1 = 280、cp 2 = 300、cp 3 = 0;阈值 250;280 > 250 且 300 > 250,按升序输出 [1, 2]。
Case 2 · visible: empty exposures yields no flags
输入: [[],[],3,500,0.25]
期望: []
没有任何敞口记录,所有 cp 的聚合值为 0,阈值 125 > 0,无人越线,返回 []。