diff --git a/backend/app/backtest/engine.py b/backend/app/backtest/engine.py index 8af1b6f..d9be4d6 100644 --- a/backend/app/backtest/engine.py +++ b/backend/app/backtest/engine.py @@ -1325,6 +1325,9 @@ class BacktestEngine: 大样本 (如 full 模式数千笔) 时按 2M 单元上限压降模拟次数, 防止瞬时数组 OOM。 """ pnls = pnls[np.isfinite(pnls)] # 剔除 inf/nan, 否则 cumprod 传播 nan 导致分位为 nan + # 防御: 单笔 pnl <= -100% 时 (1+pnl) <= 0 会让 cumprod 符号翻转/得非正净值, 回撤失真。 + # 回测有止损, 实际不会发生; 兜底 clip 到 -99.99% 保证 (1+pnl) 恒正。 + pnls = np.clip(pnls, -0.9999, None) n = len(pnls) if n < 3: return {"mc_maxdd_p50": None, "mc_maxdd_p95": None} @@ -1407,7 +1410,8 @@ class BacktestEngine: # 夏普 — 用交易收益标准差近似 sharpe = float(np.mean(pnls) / np.std(pnls)) * np.sqrt(252) if np.std(pnls) > 0 else 0.0 - # Sortino — 与 sharpe 同基准 (逐笔收益), 仅惩罚下行波动 + # Sortino: 刻意沿用本函数 sharpe 的逐笔收益 x sqrt(252) 基准。逐笔年化非严格正确, + # 但保证同一函数内 sharpe/sortino 口径一致可比 (内部一致 > 局部绝对)。仅惩罚下行波动。 sortino = BacktestEngine._sortino_ratio(pnls) # Calmar diff --git a/backend/tests/backtest/test_robustness_metrics.py b/backend/tests/backtest/test_robustness_metrics.py index bcd4900..8f3d7e4 100644 --- a/backend/tests/backtest/test_robustness_metrics.py +++ b/backend/tests/backtest/test_robustness_metrics.py @@ -81,6 +81,16 @@ def test_mc_drawdown_ignores_non_finite(): assert BacktestEngine._mc_drawdown_percentiles(dirty) == BacktestEngine._mc_drawdown_percentiles(_MC_INPUT) +def test_mc_drawdown_clips_sub_minus_100pct_pnl(): + """防御: 单笔 pnl <= -100% 会让 (1+pnl)<=0 使 cumprod 符号翻转; clip 后分位仍有限。""" + pnls = np.array([0.05, -1.5, 0.08, -0.06, 0.02, -0.04]) # -1.5 = -150%, 现实不会有 + r = BacktestEngine._mc_drawdown_percentiles(pnls) + assert r["mc_maxdd_p50"] is not None + for v in (r["mc_maxdd_p50"], r["mc_maxdd_p95"]): + assert v == v # 非 nan + assert -1.0 <= v <= 0.0 # 回撤有界在 (-100%, 0], 未因符号翻转失真 + + def test_mc_drawdown_all_positive_has_zero_drawdown(): """全正收益: 任何重排都无回撤 → 分位均为 0。""" pnls = np.array([0.01, 0.02, 0.03, 0.04, 0.05]) diff --git a/frontend/src/pages/backtest/StrategyBacktest.tsx b/frontend/src/pages/backtest/StrategyBacktest.tsx index e79bb39..7c9c667 100644 --- a/frontend/src/pages/backtest/StrategyBacktest.tsx +++ b/frontend/src/pages/backtest/StrategyBacktest.tsx @@ -1665,7 +1665,7 @@ export function StrategyBacktest() { color="#34d399" /> -