对手方毛敞口聚合器:按规模排序的汇总表
Counterparty Gross-Notional Aggregator: Magnitude-Sorted Roll-Up
开始编码实现 solution(cp_ids: list[int], signed_notionals: list[float]) -> list[list]。某交易台的合规面板接收两条长度均为 N 的平行数组:cp_ids[i] 是第 i 笔交易的整数对手方 ID,signed_notionals[i] 是该笔交易的带符号 float notional——正值为买边 notional、负值为卖边 notional。同一对手方的交易在输入中出现的位置任意。
对于 cp_ids 中每个不同的对手方 ID,计算毛敞口总额:所有满足 cp_ids[i] == cp 的下标 i 上的 abs(signed_notionals[i]) 之和。返回长度等于不同对手方 ID 个数的 [cp_id, total_abs_notional] 行表,按 total_abs_notional 降序排列,相等时按 cp_id 升序破除并列。
例
solution([7, 3, 7, 5, 3], [100.0, -200.0, 50.0, 400.0, 150.0]) 返回 [[5, 400.0], [3, 350.0], [7, 150.0]]。逐项扫描:CP 7 经过 +100 与 +50 两笔,毛敞口总额 100 + 50 = 150。CP 3 经过 -200 与 +150,总额 200 + 150 = 350——我们对绝对值求和,所以卖边 -200 与买边 +150 都计入毛敞口、不会抵消为 -50。CP 5 仅一笔 +400,总额为 400。按总额降序得 5 (400) > 3 (350) > 7 (150),本例无并列、无需触发次键。
并列时的次键示例:solution([2, 1], [50.0, 50.0]) 返回 [[1, 50.0], [2, 50.0]]。两 CP 的毛总均为 50.0,按 cp_id 升序破并列、CP 1 先于 CP 2 输出。
需要明确的细节:N == 0 返回 []。同一 cp_id 的所有交易塌缩为单行。total 字段恒为 float(从 0.0 累加),即便所有输入 notional 都是整数值。本函数不抛异常——调用方保证 len(cp_ids) == len(signed_notionals) 与约束区间内的取值,输出始终是良构表。
期望算法:单遍 O(N) 配 dict[cp_id] -> 当前毛总,再对 (N_distinct) 项做一次 sorted、键为 (-total, cp_id),两向排序一并完成。循环对象是交易而不是 CP,所以没有需要担心的边界 off-by-one。
实现细节由 stubs/stub.py 提供。
实践背景
每个交易日盘后,合规面板会把交易台对每个对手方的毛敞口汇总一次——对今日做过单的每个 CP,今日总共流过这条关系的 notional 是多少,买卖两边对称看待。汇总取毛而非净,是因为合规关心的问题是"今日有多少经济流量穿过该 CP"而不是"与该 CP 的剩余持仓是多少"——同一 CP 上买入 5 亿、卖出 5 亿仍然搬动了 10 亿的流量,即便净额为 0,毛额才是逐 CP 敞口上限要看的指标。输出按规模降序让交易台最大的对手方排在报表第一行——合规官员的目光最先落处——而 ID 升序的确定性破并列规则保证两个总额相同的 CP 在每次重跑中顺序一致,昨日报表与今日报表的 diff 不会被一次"仅排序顺序漂动"的偶发交换污染。基于 dict 的单遍聚合是盘后流水的标准模式:键为对手方 ID、值为累计总额、输出为按复合键稳定排序的表。
约束条件
- 0 <= N <= 5000,其中 N 为 len(cp_ids) == len(signed_notionals)
- 0 <= cp_ids[i] <= 1e9(非负整数对手方 ID)
- abs(signed_notionals[i]) <= 1e9(带符号 float;正值为买边 notional、负值为卖边 notional)
- 调用方保证 len(cp_ids) == len(signed_notionals)
- 输出为长度等于 cp_ids 中不同 cp_id 数的 list[list[int, float]],float 字段按 rel_tol=1e-9 比对
样例
Case 1 · statement-example: 5-trade aggregator with sell rolling into gross
输入: [[7,3,7,5,3],[100,-200,50,400,150]]
期望: [[5,400],[3,350],[7,150]]
CP 7: |100|+|50|=150;CP 3: |-200|+|150|=350;CP 5: |400|=400。按总额降序:5 (400) > 3 (350) > 7 (150)。
Case 2 · visible: tied totals broken by ascending cp_id
输入: [[2,1],[50,50]]
期望: [[1,50],[2,50]]
两 CP 总额都是 50.0,按 cp_id 升序破并列,CP 1 在前。
Case 3 · visible: empty input returns empty list
输入: [[],[]]
期望: []
N=0 没有交易,返回空列表。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example: 5-trade aggregator with sell rolling into gross
输入: [[7,3,7,5,3],[100,-200,50,400,150]]
期望: [[5,400],[3,350],[7,150]]
CP 7: |100|+|50|=150;CP 3: |-200|+|150|=350;CP 5: |400|=400。按总额降序:5 (400) > 3 (350) > 7 (150)。
Case 2 · visible: tied totals broken by ascending cp_id
输入: [[2,1],[50,50]]
期望: [[1,50],[2,50]]
两 CP 总额都是 50.0,按 cp_id 升序破并列,CP 1 在前。
Case 3 · visible: empty input returns empty list
输入: [[],[]]
期望: []
N=0 没有交易,返回空列表。