流式对数收益 NAV 路径的运行最大回撤
Running Maximum Drawdown of a Streamed Log-Return NAV Path
开始编码实现 solution(log_returns: list[float]) -> list[float]。给定一段逐期对数收益序列,定义累计对数 NAV:L[0] = 0,L[i] = L[i-1] + log_returns[i-1](i ≥ 1),(连续复利)NAV:P[i] = exp(L[i])。在观测到第 i 根收益(即得到 i = 1..N 的 P[i])之后,输出 到目前为止的运行最大回撤 —— 即 (P[peak_k] - P[k]) / P[peak_k] 在 k ∈ 1..i 上的最大值,其中 peak_k = argmax_{j ∈ 0..k} L[j]。**argmax 取在 j ∈ 0..k 上,包括 j = 0,因此隐式起点 P[0] = 1 参与运行峰**。输出长度为 N,每个值在 [0, 1] 内,对 i 单调非降。
例:solution([0.02, -0.05, -0.03, 0.04, 0.10, -0.20, 0.05]) 返回大约 [0.0, 0.048771, 0.076884, 0.076884, 0.076884, 0.181269, 0.181269]。手工演算:cur_log 路径 0 → 0.02 → -0.03 → -0.06 → -0.02 → 0.08 → -0.12 → -0.07。初始 peak_log=0;i=1 cur_log=0.02 把 peak_log 抬到 0.02。i=2 cur_log=-0.03,瞬时回撤 = 1 - exp(-0.05) ≈ 0.0488。i=3 cur_log=-0.06,瞬时 = 1 - exp(-0.08) ≈ 0.0769,刷新运行最大回撤。i=5 cur_log=0.08 创新高,瞬时为 0,但运行最大回撤保持 0.0769 不变。i=6 cur_log=-0.12,瞬时 = 1 - exp(-0.20) ≈ 0.1813,成为新的运行最大值。i=7 cur_log=-0.07 给出瞬时 1 - exp(-0.15) ≈ 0.1393(更小),运行最大回撤继续保持 0.1813。
复杂度上一次线性扫描 O(N)、O(N) 输出空间即可。所有运算都在 cur_log 与 peak_log 上完成;回撤为 1 - exp(cur_log - peak_log),指数非正所以天然有界于 1 且数值稳定。空输入返回空列表。
函数骨架见 stubs/stub.py。
实践背景
实时回撤面板是交易桌上最朴素也最高频被盯的一个监控:每根 bar 打出来,面板就更新策略自起算(或某个可配置锚点)以来的最差峰值-谷底跌幅。它是 *已实现* 的,不是前瞻的——这点跟 VaR/ES 这类前瞻性最差损失估计不同;回撤是真实发生的、无法假装没看见的峰谷下挫。生产里最容易踩的两个合约细节,正是本题考的两个:隐式起点 P[0] = 1 这个峰(如果忘了,新策略上线后第一根负 bar 的回撤会被误判为 0,恰好把面板要暴露的那个"开盘即破净"信号给抹掉),以及运行最大回撤的语义(NAV 反弹到新高时 *瞬时* 回撤会归零,但运行最大值绝不能被擦除——这是 PM 评判策略好坏时盯的那个数;面板若在反弹时把它复位是误导用户)。在 log 空间里运算是标准的工程卫生——数千根 bar 上累乘的 NAV 比率会积累乘性浮点漂移,而 log-return 累加在全 0 输入下保持位级精确,整体也良态得多。
约束条件
- 0 <= len(log_returns) <= 2000;每个元素为有限浮点数,绝对值不超过 10
- 累计对数 NAV 起点 L[0] = 0(即 P[0] = 1),运行峰值 *始终* 包含这一隐式起点——peak_log 初始化为 0,不是 log_returns[0]
- 输出的是 *运行* 最大回撤(每一步累计观测到的最差),不是瞬时回撤——序列在 i 上单调非降
- 每个输出值都在 [0.0, 1.0] 内;回撤公式 1 - exp(cur_log - peak_log) 因指数非正天然有界于 1
- 空输入返回空列表;单步非负 r 输出 [0.0];单步负 r 输出 [1 - exp(r)]
样例
Case 1 · statement-example: short stream with one drawdown then partial recovery then deeper drawdown
输入: [[0.02,-0.05,-0.03,0.04,0.1,-0.2,0.05]]
期望: [0,0.048770575499285984,0.07688365361336424,0.07688365361336424,0.07688365361336424,0.18126924692201818,0.18126924692201818]
L 路径 0,0.02,-0.03,-0.06,-0.02,0.08,-0.12,-0.07;峰值 L 在 i=5 (0.08)。前期峰值 0.02,i=2 触及 1-exp(-0.08)≈0.0769,i=5 后峰值跳到 0.08,最终在 i=6 触及 1-exp(-0.20)≈0.181。运行最大回撤序列只增不减。
Case 2 · statement-example: empty stream -> empty output
输入: [[]]
期望: []
空输入直接返回空列表,无需考虑 P[0]=1 的隐式起点。
Case 3 · statement-example: single negative return — drawdown from the implicit P[0]=1 peak
输入: [[-0.1]]
期望: [0.09516258196404048]
N=1 且 r0<0:cur_log=-0.10,peak_log 仍为 0(L[0]=0 是隐式起点),瞬时回撤 1-exp(-0.10)≈0.0952。这是与“仅看 P[1..N] 的局部峰”实现的关键差异。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · statement-example: short stream with one drawdown then partial recovery then deeper drawdown
输入: [[0.02,-0.05,-0.03,0.04,0.1,-0.2,0.05]]
期望: [0,0.048770575499285984,0.07688365361336424,0.07688365361336424,0.07688365361336424,0.18126924692201818,0.18126924692201818]
L 路径 0,0.02,-0.03,-0.06,-0.02,0.08,-0.12,-0.07;峰值 L 在 i=5 (0.08)。前期峰值 0.02,i=2 触及 1-exp(-0.08)≈0.0769,i=5 后峰值跳到 0.08,最终在 i=6 触及 1-exp(-0.20)≈0.181。运行最大回撤序列只增不减。
Case 2 · statement-example: empty stream -> empty output
输入: [[]]
期望: []
空输入直接返回空列表,无需考虑 P[0]=1 的隐式起点。
Case 3 · statement-example: single negative return — drawdown from the implicit P[0]=1 peak
输入: [[-0.1]]
期望: [0.09516258196404048]
N=1 且 r0<0:cur_log=-0.10,peak_log 仍为 0(L[0]=0 是隐式起点),瞬时回撤 1-exp(-0.10)≈0.0952。这是与“仅看 P[1..N] 的局部峰”实现的关键差异。