← 返回编程题库
coding-historical-es-multi-alpha中等免费版2000ms未尝试

多置信度历史 ES 一次性计算

Historical Expected Shortfall at Multiple Confidence Levels

开始编码

风控部门的日终仪表板上,同一份组合 PnL 历史经常需要同时给出多个置信度的 ES(也就是 CVaR)——例如 95%、97.5%、99%——以满足不同监管口径与内部风险委员会的报告需要。请实现 solution(pnl_samples: list[float], alphas: list[float]) -> list[float]:给定有符号的 PnL 历史与一组置信度,对每个 alphas[k] 用历史模拟法返回对应的 ES,输出顺序与 alphas 的输入顺序严格一致。

每个 alpha 的算法分三步。(a) 把 pnl_samples 升序排序得到 s[0..N-1];(b) 计算尾部大小 k = max(1, ceil(N * (1 - alpha) - 1e-12));(c) 报告 ES = -(sum(s[0..k-1]) / k),把最差 k 档的负 PnL 平均后取负号得到正损失。当 N == 0 时,每个 alpha 输出 float('nan');当输入的 alphas 为空,则返回空列表。-1e-12 是 IEEE-754 边界的「epsilon 收窄」:当 N*(1-alpha) 在浮点上算出来比真实整数大一点点时,去掉它就会多算一档。

最优写法是排序一次后再做一次前缀和数组 P[0..N],其中 P[k] = s[0] + s[1] + ... + s[k-1]。这样每个 alpha 的尾部均值就是 P[k] / k,O(1) 查表即可,整体复杂度是 O(N log N + N) 的预处理加 O(K) 的查表,排序和前缀和都只算一次,与 K 无关。举例:solution([0.012, -0.018, 0.005, -0.025, 0.014, -0.040, 0.002, -0.011, 0.008, -0.022], [0.95, 0.99])。N=10,升序排序为 [-0.040, -0.025, -0.022, -0.018, -0.011, 0.002, 0.005, 0.008, 0.012, 0.014]。alpha=0.95 时 k = ceil(10*0.05 - 1e-12) = 1,ES = -(-0.040)/1 = 0.040;alpha=0.99 时 k = ceil(10*0.01 - 1e-12) = 1,ES 同样是 0.040

实现层面有三个反复出现的坑。第一,*别把排序和尾部累加放进 alpha 循环*:那是 O(K · (N log N + N)),虽然不会出错但浪费明显,正确做法是 K 个 alpha 共用同一份排序加前缀和。第二,*输出顺序要保留*:内部把 alphas 排序后再按升序输出会破坏接口契约——下游会按 alphas[k] 的位置读取 ES。第三,*重复 alpha 各自给一份*:如果输入是 [0.95, 0.95, 0.99],输出应是三个值,前两位相同;不能去重再补回。再加上 epsilon 收窄那一档,ES 用正数报告(最差 k 档负 PnL 的均值取相反数),就齐了。

约束条件

  • 0 ≤ len(pnl_samples) ≤ 1500,每个样本满足 |pnl_samples[t]| ≤ 1e6。
  • 0 ≤ len(alphas) ≤ 20,每个 alpha 满足 0.5 ≤ alpha < 1.0,可以乱序、可以有重复。
  • 当 len(pnl_samples) == 0 时,输出长度等于 len(alphas),每个元素都是 `float('nan')`。
  • 当 len(alphas) == 0 时,输出为空列表 `[]`,无论 pnl_samples 是否为空。
  • ES 以正损失约定报告:`ES = -(sum(worst_k) / k)`,最差 k 档是负的 PnL,取负号变成正数。
  • 浮点容忍:rel_tol=1e-9,abs_tol=1e-9。

样例

Case 1 · worked example, two alphas

输入: [[0.012,-0.018,0.005,-0.025,0.014,-0.04,0.002,-0.011,0.008,-0.022],[0.95,0.99]]

期望: [0.04,0.04]

N=10。升序排序后 sorted = [-0.040, -0.025, -0.022, -0.018, -0.011, 0.002, 0.005, 0.008, 0.012, 0.014],前缀和 P[1]=-0.040。对 alpha=0.95:k = ceil(10*0.05 - 1e-12) = 1,ES = -(P[1]/1) = -(-0.040) = 0.040。对 alpha=0.99:k = ceil(10*0.01 - 1e-12) = 1,ES = 0.040。排序与前缀和都只算一次。

Case 2 · dashboard alphas N=20

输入: [[0.005,-0.012,0.018,-0.035,0.022,-0.008,0.011,-0.044,0.029,-0.017,0.006,-0.025,0.014,-0.031,0.019,-0.009,0.024,-0.052,0.013,-0.021],[0.95,0.975,0.99]]

期望: [0.052,0.052,0.052]

N=20。升序前几档 sorted[0..3] = [-0.052, -0.044, -0.035, -0.031, ...]。alpha=0.95:k = ceil(20*0.05 - 1e-12) = 1,ES = -(-0.052) = 0.052。alpha=0.975:k = ceil(20*0.025 - 1e-12) = 1,ES = 0.052。alpha=0.99:k = ceil(20*0.01 - 1e-12) = 1,ES = 0.052。三个 alpha 共享同一次排序和同一份前缀和。

Case 3 · tail-mean matters at alpha=0.7 vs 0.9

输入: [[-0.1,-0.08,-0.05,-0.03,0.01,0.02,0.04,0.06,0.07,0.09],[0.7,0.9]]

期望: [0.07666666666666666,0.1]

N=10。升序 sorted = [-0.10, -0.08, -0.05, -0.03, 0.01, 0.02, 0.04, 0.06, 0.07, 0.09],前缀和 P = [0, -0.10, -0.18, -0.23, -0.26, ...]。alpha=0.7:k = ceil(10*0.3 - 1e-12) = 3,ES = -(P[3]/3) = -(-0.23/3) = 0.07666... 。alpha=0.9:k = ceil(10*0.1 - 1e-12) = 1,ES = -(P[1]/1) = 0.10。两个 alpha 共用前缀和。

最近提交

还没有提交记录。

编码区

实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。

加载编辑器...
计时0:00

默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。

Case 1 · worked example, two alphas

输入: [[0.012,-0.018,0.005,-0.025,0.014,-0.04,0.002,-0.011,0.008,-0.022],[0.95,0.99]]

期望: [0.04,0.04]

N=10。升序排序后 sorted = [-0.040, -0.025, -0.022, -0.018, -0.011, 0.002, 0.005, 0.008, 0.012, 0.014],前缀和 P[1]=-0.040。对 alpha=0.95:k = ceil(10*0.05 - 1e-12) = 1,ES = -(P[1]/1) = -(-0.040) = 0.040。对 alpha=0.99:k = ceil(10*0.01 - 1e-12) = 1,ES = 0.040。排序与前缀和都只算一次。

Case 2 · dashboard alphas N=20

输入: [[0.005,-0.012,0.018,-0.035,0.022,-0.008,0.011,-0.044,0.029,-0.017,0.006,-0.025,0.014,-0.031,0.019,-0.009,0.024,-0.052,0.013,-0.021],[0.95,0.975,0.99]]

期望: [0.052,0.052,0.052]

N=20。升序前几档 sorted[0..3] = [-0.052, -0.044, -0.035, -0.031, ...]。alpha=0.95:k = ceil(20*0.05 - 1e-12) = 1,ES = -(-0.052) = 0.052。alpha=0.975:k = ceil(20*0.025 - 1e-12) = 1,ES = 0.052。alpha=0.99:k = ceil(20*0.01 - 1e-12) = 1,ES = 0.052。三个 alpha 共享同一次排序和同一份前缀和。

Case 3 · tail-mean matters at alpha=0.7 vs 0.9

输入: [[-0.1,-0.08,-0.05,-0.03,0.01,0.02,0.04,0.06,0.07,0.09],[0.7,0.9]]

期望: [0.07666666666666666,0.1]

N=10。升序 sorted = [-0.10, -0.08, -0.05, -0.03, 0.01, 0.02, 0.04, 0.06, 0.07, 0.09],前缀和 P = [0, -0.10, -0.18, -0.23, -0.26, ...]。alpha=0.7:k = ceil(10*0.3 - 1e-12) = 3,ES = -(P[3]/3) = -(-0.23/3) = 0.07666... 。alpha=0.9:k = ceil(10*0.1 - 1e-12) = 1,ES = -(P[1]/1) = 0.10。两个 alpha 共用前缀和。