压力 VaR — 最差滚动窗口历史 VaR
Stressed VaR — Worst Rolling-Window Historical VaR
开始编码在巴塞尔 III 框架下,计算市场风险资本的银行需同时报告普通 VaR(用最近 1Y 数据计算)与压力 VaR(sVaR,在历史样本中找出"重大金融压力期"的 1Y 滚动窗口计算)。资本附加项取两者最大值。标准的量化口径就是「在历史 PnL 样本所有长度-W 滚动窗口上取历史 VaR 的最大值」。请实现 solution(pnl_samples: list[float], window_size: int, alpha: float) -> float:给定历史区间 PnL 序列(正数=盈利、负数=亏损)、滚动窗口长度 W 与 VaR 置信度 alpha,返回压力 VaR 并以正损失数值报告。当 len(pnl_samples) < window_size(没有完整长度窗口能放下)时,返回 float('nan') 作为哨兵。
每个窗口的历史 VaR 沿用与 ES 管线 (coding-historical-expected-shortfall) 相同的边界 ceil 约定。对长度 W 的窗口,先升序排序,取 k = max(1, ceil(W * (1 - alpha) - 1e-9)),再令 VaR_window = -sorted_window[k - 1]——升序后第 k 小的相反数;亏损尾部下为正数。压力 VaR 是 VaR_window 在所有 (N - W + 1) 个滚动窗口上的最大值。风控部门把这个数值与普通历史 VaR、期望损失一起送入资本归因仪表盘。
例
solution([0.005, -0.012, 0.008, -0.030, 0.002, -0.045, 0.010, -0.008, 0.006, -0.020], 4, 0.75) 应返回 0.045。W = 4、alpha = 0.75 时 k = ceil(4 * 0.25 - 1e-9) = 1,所以每个窗口的 VaR 都是该窗口最小值的相反数。7 个滚动窗口的最小值为 [-0.030, -0.030, -0.045, -0.045, -0.045, -0.045, -0.020];最差(最负)是 -0.045,故 sVaR = +0.045。注意这与「把 W 改成 N 后算单次 VaR」的结果可能不同——sVaR 的全部意义就是把压力窗口单挑出来,而不是和全样本平均。
实践背景
四个常见正确性陷阱。第一,k 必须用*边界 ceil* 约定:与 历史 VaR/ES 兄弟题相同的 ceil(W * (1 - alpha) - 1e-9) 兜底。W = 100、alpha = 0.99 时 100 * (1 - 0.99) 的浮点值是 1.0000000000000009,朴素 ceil 会得 2,而 100 个观测下 99% 置信度 VaR 的教科书 k 是 1——ceil 前先减 1e-9 才能恢复教科书答案。用 int(W * (1 - alpha)) 或 floor 的作者会在边界上得到 k=0(空尾部)或 off-by-one。第二,*下标*:升序数组里第 k 小的观测在 0 索引 Python 中位于下标 k - 1。写成 sorted_window[k] 的作者报告的是第 (k+1) 差——在对抗输入下相差好几个百分点。第三,*最差窗口 vs 全样本*:sVaR 是滚动窗口内 VaR 的最大值,而不是全样本一次 VaR。压力期输入下全样本会把坏窗口稀释到整段样本里、低估约一个数量级——实现滚动最大值正是该指标的核心。第四,*符号约定*:VaR 报告为正损失数;升序后第 k 小的 PnL 是第 k 大亏损(最负),所以 VaR = -sorted_asc[k-1] 取反。若所有窗口的所有观测都是正数(无任何亏损),每个窗口的 VaR 是负数、sVaR 也为负数——这是正确的,不是 bug。
参考实现就是教科书算法。先一次性算出 k。对每个起点 i ∈ 0..N-W,取 window = pnl_samples[i:i+W] 排序,算 VaR_window = -window_sorted[k - 1],并跟踪最大值。朴素的 O(N * W log W) 在 N ≤ 1200、W ≤ N 范围内 2s 预算绰绰有余。如果想过度工程化,sortedcontainers.SortedList 的滚动维护可达 O(N log W),但在给定上下界完全没必要。
约束条件
- 1 ≤ window_size ≤ len(pnl_samples) ≤ 1200。若 `len(pnl_samples) < window_size`,返回 `float('nan')`——没有完整长度窗口能放下,结果未定义。
- |pnl_samples[i]| ≤ 1e6,均为有限浮点数。
- 0.5 ≤ alpha < 1.0(常见 VaR 置信度:0.5、0.9、0.95、0.99、0.999)。
- 窗口内 k = ceil(W * (1 - alpha))(数学口径);IEEE-754 浮点下用 `1e-9` 量级 epsilon 兜底,并 clamp 到 `k ≥ 1`。
- 损失尾部典型样本下输出 sVaR 为正损失数;若所有窗口都没有任何亏损,sVaR 会是负数(这是正确的,不是 bug)。
- 浮点容忍:rel_tol=1e-9,abs_tol=1e-9。
样例
Case 1 · statement-example, N=10 W=4 alpha=0.75 k=1
输入: [[0.005,-0.012,0.008,-0.03,0.002,-0.045,0.01,-0.008,0.006,-0.02],4,0.75]
期望: 0.045
N=10、W=4、alpha=0.75。k=ceil(4*0.25-eps)=1,所以每个窗口的 VaR = -窗口最小值。从下标 2 开始的窗口 [0.008, -0.030, 0.002, -0.045] 最小值为 -0.045;该窗口(以及邻近三个)实现了 sVaR = +0.045。
Case 2 · statement-example, sVaR vs full-sample VaR — worst window dominates
输入: [[0.001,-0.005,0.003,-0.008,0.002,-0.01,0.005,-0.003,0.001,-0.006,0.004,-0.04,0.002,-0.035,0.001,-0.03,0,0.005,-0.001,0.002,0.003,-0.002,0.001,-0.004,0.002],10,0.8]
期望: 0.035
N=25、W=10、alpha=0.8、k=2。全样本 VaR(用 25 个观测、k=5)只有 0.008,但下标 [10..19] 的滚动窗口聚集了三笔大损失 [-0.040, -0.035, -0.030],升序排序后第 2 项为 -0.035,故该窗口 VaR = +0.035。sVaR 把这段压力期挑出来。
Case 3 · visible, small worked-example check
输入: [[0.01,-0.02,0.005,-0.015,0.008,-0.03],3,0.9]
期望: 0.03
N=6、W=3、alpha=0.9 → k=1。长度 3 的窗口:[0.01,-0.02,0.005] min=-0.02、[-0.02,0.005,-0.015] min=-0.02、[0.005,-0.015,0.008] min=-0.015、[-0.015,0.008,-0.030] min=-0.030。sVaR = +0.030。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example, N=10 W=4 alpha=0.75 k=1
输入: [[0.005,-0.012,0.008,-0.03,0.002,-0.045,0.01,-0.008,0.006,-0.02],4,0.75]
期望: 0.045
N=10、W=4、alpha=0.75。k=ceil(4*0.25-eps)=1,所以每个窗口的 VaR = -窗口最小值。从下标 2 开始的窗口 [0.008, -0.030, 0.002, -0.045] 最小值为 -0.045;该窗口(以及邻近三个)实现了 sVaR = +0.045。
Case 2 · statement-example, sVaR vs full-sample VaR — worst window dominates
输入: [[0.001,-0.005,0.003,-0.008,0.002,-0.01,0.005,-0.003,0.001,-0.006,0.004,-0.04,0.002,-0.035,0.001,-0.03,0,0.005,-0.001,0.002,0.003,-0.002,0.001,-0.004,0.002],10,0.8]
期望: 0.035
N=25、W=10、alpha=0.8、k=2。全样本 VaR(用 25 个观测、k=5)只有 0.008,但下标 [10..19] 的滚动窗口聚集了三笔大损失 [-0.040, -0.035, -0.030],升序排序后第 2 项为 -0.035,故该窗口 VaR = +0.035。sVaR 把这段压力期挑出来。
Case 3 · visible, small worked-example check
输入: [[0.01,-0.02,0.005,-0.015,0.008,-0.03],3,0.9]
期望: 0.03
N=6、W=3、alpha=0.9 → k=1。长度 3 的窗口:[0.01,-0.02,0.005] min=-0.02、[-0.02,0.005,-0.015] min=-0.02、[0.005,-0.015,0.008] min=-0.015、[-0.015,0.008,-0.030] min=-0.030。sVaR = +0.030。