Calibrating the Minimum Leverage Cap for a Target Risk Budget
Calibrating the Minimum Leverage Cap for a Target Risk Budget
开始编码风险管理岗负责一本 个资产的持仓 book:每个资产有一个非负的单位波动率 和一个带符号权重 (正 = 多头,负 = 空头)。账面总风险定义为按毛敞口加权的波动率:
合规给出每日风险预算 ,desk 需要通过单一的逐资产杠杆上限 来落实它:每个毛权重被按 收紧后,再把原始符号贴回去。交易员的关切是校准——*「我们至少需要把上限收到多紧,才能让收紧后的组合仍然合规?」* 等价地说,找到使 post-cap 风险仍 预算的最大 ;那就是「最不严苛的合规上限」,也即合规可以要求的最小杠杆扣减。
实现 solution(vols, weights, target_risk, abs_tol) -> float。返回 ,使得在执行 之后,post-cap 总风险满足 ,且 收敛到阈值的 abs_tol 邻域内。
post-cap 风险函数
在 上非递减:每一项都是常数与 线性增函数的逐点最小,乘上非负波动率,因此 在 上是非递减的分段线性函数。可行集 是左端锚定的区间 ,校准答案就是右端点 。由于判定单调,对 做二分就能在 内找到 —— 不需要闭式解,也不会在分段线性的 knee 处出问题。
Edge 情形。 若 ,未收紧的组合已经合规,最大可行上限是 —— 任何 该值的上限都不会改写权重。若 且初始风险为正,唯一能把每个 压到 0 的上限是 。若 请求不可行,函数抛 ValueError。
示例
solution([0.20, 0.15, 0.10], [1.0, -2.0, 3.0], 0.5, 1e-9) 返回约 1.2。初始风险 = 0.2 + 0.3 + 0.3 = 0.8 > 0.5,需要收紧上限。在 时第一资产 已被锁在原权重,另两资产还在线性段(), 恰好等于预算。
solution([0.1, 0.2, 0.3], [1.0, -2.0, 3.0], 100.0, 1e-9) 返回 3.0。初始风险 = 1.4 < 100,未收紧的组合已经合规,最大不绑定的上限就是 。
solution([0.5, 0.5], [1.0, 1.0], 0.0, 1e-9) 返回 0.0。 时唯一把每一项压到 0 的上限是 。
参考 stubs/stub.py 中的函数骨架。
Practical context
杠杆上限校准是风险团队在策略中途突破预算时第一道伸手的杠杆。比起重跑优化器,他们问一个严格更便宜的问题:*「我们能加的最宽松的逐资产毛敞口上限是多少,使账面回到限额内?」* 答案是 上一维单调判定式的阈值——正是「对答案做二分」最擅长的形状。也可以用 Newton 或区间根求解器去解 ,但 在每个 处都有 knee(每越过一个 knee 斜率就掉 )—— Newton 可能正好在 knee 上停滞,而二分无论答案落在哪儿都按对半收敛。同样的「单调判定 + 二分」模式在 desk 上反复出现:换手率上限校准(让聚合换手率 预算的最宽松上限)、抢仓削减幅度(把 top-N 抢仓评分压回阈值的最小 haircut)、清算 / 净额结算模拟里的 netting-band 选择、以及压力测试约束下的资本缓冲尺寸。每个的 不同,但算法核——可行性判定加上对连续阈值的二分——完全一样,工程收益也一样:在 O(N) 判定上叠 O(log(1/tol)),无参数易碎性,无需闭式解。本题训练这个范式的标准实例:在真金白银的风险约束上对一个杠杆上限做校准,全是 knee,毫无歧义。
约束条件
- 1 <= N <= 10^4,其中 N = len(vols) = len(weights)
- 0 <= vols[i] <= 1.0
- -10 <= weights[i] <= 10
- target_risk >= 0(target_risk < 0 抛 ValueError)
- 1e-12 <= abs_tol <= 1e-3
- Edge:target_risk >= initial_risk → 返回 max(|weights|)(无需收紧上限;合规已成立时给出最大可行上限)
- Edge:target_risk == 0 且 vols/weights 非全零 → 返回 0.0(唯一能把所有项压到 0 的上限)
- Edge:vols 全 0 或 weights 全 0 → initial_risk = 0 落入第一条 edge,返回 max(|weights|),权重全 0 时即为 0
- 浮点比较器:测试中 rel_tol = abs_tol = 1e-6
样例
Case 1 · statement example: 3 vols mixed signs, moderate cap
输入: [[0.2,0.15,0.1],[1,-2,3],0.5,1e-9]
期望: 1.1999999997206032
初始风险 = 0.2*1+0.15*2+0.1*3 = 0.8 > 目标 0.5。f(C) = 0.2*min(1,C) + 0.15*min(2,C) + 0.1*min(3,C)。在 C=1.2 处三项均未触顶(1<1.2 不触顶,第一项保持 0.2;其余两项还在线性段),f(1.2)=0.2+0.18+0.12=0.5 恰好等于预算,C* ≈ 1.2 是满足约束的最大杠杆上限。
Case 2 · two-asset symmetric: cap binds both
输入: [[0.1,0.2],[2,-3],0.4,1e-9]
期望: 1.3333333327900618
初始 0.1*2+0.2*3=0.8。在 C<2 时两项都在线性段,f(C)=(0.1+0.2)C=0.3C,要 ≤0.4 → C≤4/3。bisection 在 [0,3] 上约 32 步把宽度压到 1e-9。
Case 3 · target above initial risk: no capping needed
输入: [[0.1,0.2,0.3],[1,-2,3],100,1e-9]
期望: 3
初始风险 = 0.1+0.4+0.9 = 1.4 ≤ 100,已经合规,无需收紧上限。返回 max(|w|)=3 — 任何 ≥3 的上限都不会改写权重。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement example: 3 vols mixed signs, moderate cap
输入: [[0.2,0.15,0.1],[1,-2,3],0.5,1e-9]
期望: 1.1999999997206032
初始风险 = 0.2*1+0.15*2+0.1*3 = 0.8 > 目标 0.5。f(C) = 0.2*min(1,C) + 0.15*min(2,C) + 0.1*min(3,C)。在 C=1.2 处三项均未触顶(1<1.2 不触顶,第一项保持 0.2;其余两项还在线性段),f(1.2)=0.2+0.18+0.12=0.5 恰好等于预算,C* ≈ 1.2 是满足约束的最大杠杆上限。
Case 2 · two-asset symmetric: cap binds both
输入: [[0.1,0.2],[2,-3],0.4,1e-9]
期望: 1.3333333327900618
初始 0.1*2+0.2*3=0.8。在 C<2 时两项都在线性段,f(C)=(0.1+0.2)C=0.3C,要 ≤0.4 → C≤4/3。bisection 在 [0,3] 上约 32 步把宽度压到 1e-9。
Case 3 · target above initial risk: no capping needed
输入: [[0.1,0.2,0.3],[1,-2,3],100,1e-9]
期望: 3
初始风险 = 0.1+0.4+0.9 = 1.4 ≤ 100,已经合规,无需收紧上限。返回 max(|w|)=3 — 任何 ≥3 的上限都不会改写权重。