← 返回编程题库
coding-historical-scenario-replay-worst-k中等免费版2000ms未尝试

历史情景回放:滚动窗口最劣 K 条累计 PnL

Historical Scenario Replay: Worst-K Rolling-Window PnLs

开始编码

实现 solution(daily_pnl: list[float], window: int, k: int) -> list[float]。给定一段日 PnL 历史,枚举每一段连续的 window 日滚动切片、把每段切片求和得到一个情景 PnL,然后返回最负的 k 条情景和(升序)。这就是历史回放的最劣情景集合:每条滚动切片代表"假如当前账本经历了从第 i 天起的那 W 日"。

具体地,

scenario_pnl[i]  =  j=ii+W1daily_pnl[j],i=0,1,,NW. \text{scenario\_pnl}[i] \;=\; \sum_{j=i}^{i+W-1} \text{daily\_pnl}[j], \qquad i = 0, 1, \ldots, N - W.

总共有 S=NW+1S = N - W + 1 条情景 PnL。升序排序后取前 kk 条;当 S<kS < k 时把全部 SS 条升序返回;当 S0S \le 0(即 W>NW > N)或 k=0k = 0 时返回 []

输出值带符号:累计亏损为。若历史段恰好全为正,那么最劣的 k 条就是最小的 k 条收益——这些正值仍是正确输出,不要过滤"只保留负数"。

朴素的 O(N*W) 双重循环在题目约束内是可行的,但更规范的写法是:构造长度 N+1 的前缀和数组,再对长度 S 的窗口和向量调用 heapq.nsmallest,总复杂度 O(N + S log k)。

实现细节由 stubs/stub.py 提供。

示例

solution([-0.5, -0.8, 0.2, -1.2, -0.3, 0.4, -2.5, 0.1, -0.4, 0.6], 3, 4) 返回 [-2.8, -2.4, -2.0, -1.8](在浮点容差内)。

window = 3 时一共 8 条滚动情景,分别对应 daily_pnl[i:i+3] 的累加和(i = 0..7):

  • i=0:-0.5 + -0.8 + 0.2 = -1.1
  • i=1:-0.8 + 0.2 + -1.2 = -1.8
  • i=2:0.2 + -1.2 + -0.3 = -1.3
  • i=3:-1.2 + -0.3 + 0.4 = -1.1
  • i=4:-0.3 + 0.4 + -2.5 = -2.4
  • i=5:0.4 + -2.5 + 0.1 = -2.0
  • i=6:-2.5 + 0.1 + -0.4 = -2.8
  • i=7:0.1 + -0.4 + 0.6 = 0.3

升序排列得到 [-2.8, -2.4, -2.0, -1.8, -1.3, -1.1, -1.1, 0.3];最劣的 4 条——也就是台账最优先回放的 4 个历史片段——就是排在最前面的那四个。

实践背景

历史情景回放是与参数化 VaR、合成冲击并列的主流压力测试方法:不再人为构造冲击向量,而是让一段长 PnL 历史的每一个 window 日切片都套到当前账本上跑一遍,看哪些历史片段在今天的持仓下伤得最重(一段几年的日 PnL ≈ 几百到几千条窗口)。用 W 日窗口而非单日,正是为了刻画多日回撤路径效应——单日冲击体现不出来的连续亏损形态;只把最劣的 K 条挑出来(而不是把所有窗口都列出)能给风控官一份可控的"代表性历史片段"清单。升序排列让最劣亏损排在第一行,正好对应压力面板表头的渲染顺序;当历史不够长、K 大于情景数时,把全部 S 条返回也让契约对短历史保持稳定,不会崩。

边界情况

  • window > N 时不存在任何情景——返回 [],不要抛异常。
  • k = 0 不论 daily_pnlwindow 都返回 []
  • N = 0 不论 windowk 都返回 []
  • window = 1 退化为"最小的 k 条单日 PnL"——仍是合法回放。
  • window = N 刚好对应 1 条情景(整段历史的累计和);只要 k >= 1,输出为长度 1 的列表。
  • k > S 时把全部 S 条按升序返回——不补 0、不只取负数。

约束条件

  • 0 <= N <= 4000,其中 N = len(daily_pnl) 为日 PnL 序列长度
  • 至少存在一条情景时 1 <= window;若 window > N 返回 `[]`
  • 0 <= k;若 k == 0 返回 `[]`
  • 每个 daily_pnl[i] 为绝对值不超过 1e6 的有限浮点数
  • 输出为长度 min(k, max(0, N - window + 1)) 的 `list[float]`,按升序排列(最劣亏损在前);浮点比对容差 rel_tol = 1e-9,abs_tol = 1e-9

样例

Case 1 · statement-example: 10-day history W=3 K=4

输入: [[-0.5,-0.8,0.2,-1.2,-0.3,0.4,-2.5,0.1,-0.4,0.6],3,4]

期望: [-2.8000000000000003,-2.3999999999999995,-2,-1.7999999999999998]

题面例子:8 条滚动 3 日窗口和升序为 [-2.8, -2.4, -2.0, -1.8, -1.3, -1.1, -1.1, 0.3],取最小 4 条。

Case 2 · visible: window > N returns []

输入: [[1,-2,3],5,4]

期望: []

N=3 但 window=5 > N,没有任何情景拟合,按契约返回空列表。

最近提交

还没有提交记录。

编码区

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

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

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

Case 1 · statement-example: 10-day history W=3 K=4

输入: [[-0.5,-0.8,0.2,-1.2,-0.3,0.4,-2.5,0.1,-0.4,0.6],3,4]

期望: [-2.8000000000000003,-2.3999999999999995,-2,-1.7999999999999998]

题面例子:8 条滚动 3 日窗口和升序为 [-2.8, -2.4, -2.0, -1.8, -1.3, -1.1, -1.1, 0.3],取最小 4 条。

Case 2 · visible: window > N returns []

输入: [[1,-2,3],5,4]

期望: []

N=3 但 window=5 > N,没有任何情景拟合,按契约返回空列表。