过滤历史模拟 VaR
Filtered Historical Simulation VaR
开始编码过滤历史模拟(Filtered Historical Simulation, FHS)VaR 是非平稳波动率场景下,对等权重历史 VaR 的经典升级。它不再把每个历史观测都当成同等信息量,而是先把每笔历史 PnL 的*创新*——「相对于当时所处的波动率制度,这笔变动有多意外」——抽出来,再注入今天的波动率预测。结果是一个能响应当前波动率制度的 VaR:刚刚经历完波动率冲击的几天里,FHS 比朴素历史 VaR 读得更高(因为今天的制度更热);波动率刚刚回落时,FHS 读得更低。请实现 solution(pnl_samples: list[float], volatility_estimates: list[float], current_vol: float, alpha: float) -> float。输入是按时间排序的 PnL 序列、与之等长的「当时适用的逐日波动率预测」(为正)、即将到来一期的波动率预测 current_vol(为正)、以及 VaR 置信度 alpha。返回 FHS VaR 作为*损失数值*——即阈值重缩放 PnL 的相反数:当所选的尾部观测是亏损时为正(一般情形),当所选的尾部观测本身是盈利时为负(例如 PnL 序列全为正、或 alpha 较小时)。在 N=0 哨兵以及 volatility_estimates[t] == 0 前置条件被违反时返回 float('nan')。
参考算法分三步。(1) 标准化:u[t] = pnl_samples[t] / volatility_estimates[t],让每个历史观测变成一个波动率调整后的创新。(2) 重缩放:pnl_rescaled[t] = u[t] * current_vol,重新注入今天的波动率制度。代数上化简为每元素一次乘除:pnl_rescaled[t] = pnl_samples[t] * (current_vol / volatility_estimates[t])。(3) 在重缩放后的分布上跑标准历史 VaR 分位数:升序排序、取 k = max(1, ceil(N * (1 - alpha) - 1e-12))、返回 -pnl_rescaled_sorted[k - 1]。整体复杂度是标准化与重缩放 O(N),加上排序 O(N log N)。
例
solution([-0.05, 0.02, 0.01, -0.03, -0.02], [0.04, 0.02, 0.02, 0.02, 0.02], 0.02, 0.8) 应返回 0.03。最差的那笔历史观测(-0.05)当时处于高波动率制度(vol = 0.04,是其他天的两倍);标准化后创新 u = -0.05 / 0.04 = -1.25,再用 current_vol = 0.02 缩放得 -0.025。其余天的 vol = 0.02 = current_vol,原样不变。重缩放后的序列升序为 [-0.03, -0.025, -0.02, 0.01, 0.02]。n = 5, alpha = 0.8 时 k = max(1, ceil(5 * 0.2 - 1e-12)) = 1,阈值是 pnl_rescaled_sorted[0] = -0.03,VaR 等于 0.03。同一份数据上,朴素的等权重历史 VaR 是 0.05(最坏一笔的原始亏损);FHS 把那笔高波动制度下的贡献归一化掉了。
实践背景
五个常见正确性陷阱。第一,*两步变换*:先标准化、*再*重缩放。如果跳过第 1 步直接对 pnl_samples 跑历史 VaR,得到的就是朴素历史 VaR,FHS 的整个升级目标都丢了。第二,*顺序很要紧*:(pnl[t] / vol[t]) * current_vol 等价于 pnl[t] * (current_vol / vol[t]),但 vol[t] / (pnl[t] * current_vol) 不等价——代数等价的是「乘性重加权」那个写法。第三,*尾部大小公式*:k = max(1, ceil(N * (1 - alpha) - 1e-12))。1e-12 epsilon 兜底用来吸收 IEEE-754 漂移——200 * 0.05 浮点下是 10.000000000000009,朴素 ceil 会返回 11。少了兜底或用 floor 在边界情形会偏一位(与等权重历史 VaR 兄弟题口径一致)。第四,*符号约定*:VaR 是正的损失数值,所以阈值 PnL 要取负号;当尾部仍是盈利时,结果会是负数——这是约定。第五,*波动率单位归调用方*:函数不校验 volatility_estimates 与 current_vol 是否同一尺度。
三个值得记牢的 sanity-check 边界。(a) 当所有 volatility_estimates 都等于 current_vol 时,重缩放是恒等,FHS 退化为标准历史 VaR。(b) 当 current_vol 远大于历史 vols 时,每个重缩放值都被放大,VaR 按比例上调——这正是 FHS 要应对的「波动率制度上抬」模式。(c) 当 current_vol 远小于历史 vols 时,VaR 按比例下调——FHS 抓住了朴素历史 VaR 漏掉的制度回落。N=0 的情形返回 float('nan');若任意 volatility_estimates[t] 恰好为零,标准化无定义,参考实现也返回 NaN(而不是 inf 或抛异常)。区分 FHS 与 EWMA 加权历史 VaR 兄弟题:那个用*时间衰减*权重作用于原始 PnL,而 FHS 用*波动率制度*重缩放——拉杆不同、升级目标相同。
约束条件
- 0 ≤ len(pnl_samples) == len(volatility_estimates) ≤ 1500。空输入返回 `float('nan')` 作为「无观测」哨兵。
- |pnl_samples[t]| ≤ 1e6,均为有限浮点数。符号约定:正值=盈利,负值=亏损。
- 0.0001 ≤ volatility_estimates[t] ≤ 1e6,为正数。0.0001 ≤ current_vol ≤ 1e6。
- 0.5 ≤ alpha < 1.0(常见 VaR 置信度:0.5、0.9、0.95、0.99、0.999)。
- 尾部大小规则:`k = max(1, ceil(N * (1 - alpha) - 1e-12))`;返回 `-pnl_rescaled_sorted[k - 1]`。
- 波动率单位前置条件由调用方保证;vols 与 current_vol 必须在同一尺度。
- 浮点容忍:rel_tol=1e-9,abs_tol=1e-9。
样例
Case 1 · statement-example, n=5 alpha=0.8 vol-shifted worst day
输入: [[-0.05,0.02,0.01,-0.03,-0.02],[0.04,0.02,0.02,0.02,0.02],0.02,0.8]
期望: 0.03
n=5、alpha=0.8 时 k = ceil(5*0.2 - 1e-12) = 1。最坏一天 (-0.05) 当时波动率 0.04,是其他天 (0.02) 的两倍;标准化得 u = -0.05/0.04 = -1.25,再用 current_vol=0.02 缩放得 -0.025。重缩放后的序列升序为 [-0.03, -0.025, -0.02, 0.01, 0.02],k-1=0 处取 -(-0.03)=0.03。等权重历史 VaR 在同一份数据上是 0.05,FHS 把那笔高波动样本的影响归一化掉了。
Case 2 · visible, identity vols collapse to historical VaR
输入: [[-0.04,-0.02,0.01,0.005],[0.02,0.02,0.02,0.02],0.02,0.95]
期望: 0.04
所有 volatility_estimates 都等于 current_vol,重缩放是恒等。n=4、alpha=0.95,k = ceil(4*0.05 - 1e-12) = 1;排序后取最差一笔 -0.04,VaR = 0.04。
Case 3 · visible, all-zero historical PnL gives zero VaR
输入: [[0,0,0,0,0],[0.01,0.02,0.015,0.018,0.012],0.02,0.95]
期望: 0
全零 PnL 标准化后仍然是零,重缩放后是零;任何分位数都是 0,VaR = 0。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example, n=5 alpha=0.8 vol-shifted worst day
输入: [[-0.05,0.02,0.01,-0.03,-0.02],[0.04,0.02,0.02,0.02,0.02],0.02,0.8]
期望: 0.03
n=5、alpha=0.8 时 k = ceil(5*0.2 - 1e-12) = 1。最坏一天 (-0.05) 当时波动率 0.04,是其他天 (0.02) 的两倍;标准化得 u = -0.05/0.04 = -1.25,再用 current_vol=0.02 缩放得 -0.025。重缩放后的序列升序为 [-0.03, -0.025, -0.02, 0.01, 0.02],k-1=0 处取 -(-0.03)=0.03。等权重历史 VaR 在同一份数据上是 0.05,FHS 把那笔高波动样本的影响归一化掉了。
Case 2 · visible, identity vols collapse to historical VaR
输入: [[-0.04,-0.02,0.01,0.005],[0.02,0.02,0.02,0.02],0.02,0.95]
期望: 0.04
所有 volatility_estimates 都等于 current_vol,重缩放是恒等。n=4、alpha=0.95,k = ceil(4*0.05 - 1e-12) = 1;排序后取最差一笔 -0.04,VaR = 0.04。
Case 3 · visible, all-zero historical PnL gives zero VaR
输入: [[0,0,0,0,0],[0.01,0.02,0.015,0.018,0.012],0.02,0.95]
期望: 0
全零 PnL 标准化后仍然是零,重缩放后是零;任何分位数都是 0,VaR = 0。