参数排名从单窗样本内得分改为 K 段折外稳健分 R = 0.6×中位 + 0.4×最差 (设计: docs/robust-optimizer-plan.md), 四阶段全部落地: - guards 共享护栏: 主目标白名单 + 最小交易数门槛 (fail-closed, 缺交易数 按不达标处理), 两模式一处实现 - robust_optimizer: 等分切窗 (2-6 段, 每段≥14天) / 稳健分 / 冠军收敛诊断 (unanimous/majority/scattered) / Top-20 全窗 IS 细化与退化率 / 高原判据 (邻域得分衰减, 零额外回测); worker 接线 robust_optimize 任务 - 滚动样本外检验补强: 每折 OOS 最小交易数门槛 (无效折透明跳过), 前端折预览 / 参数漂移轨迹 / 采用最新折参数 - 前端: 冠军卡 (收敛/高原徽章/乐观偏差) + K 段排名表 + 验证窗预览 + 应用参数闭环 (POST /strategies/config, 可逆); SSE 重连与跨设备接回 - P4 摒弃旧路径: 删 /optimize/stream 与 /optimize/cancel 端点及 worker optimize 任务类型; 白名单全面生效 (optimizer.VALID_OBJECTIVES 收敛到 guards; walkforward 端点校验退化目标 400) - 附带修复: 叠加条件×矩阵路径 SignalMatrix 只读违规 (np.where 产物 可写), optimizer_trial 轻量统计恒保留 n_trades 实测: 126 组合全市场网格 1.71× 旧版耗时 (视图复用优化自 2.01×; 差额 为引擎层每 run 固定开销, 详见方案文档第九节); 全量回归 3131 通过。 api.ts 同时带入稳健优化折预览客户端与声效语音包 API 客户端 (语音包功能随后续提交落地界面)。 Signed-off-by: shy3130 <shy3130@qq.com>
6.2 KiB
稳健参数优化与滚动样本外检验 · 重设计方案
状态: 设计已定稿, 待实施 (P1-P4)。取代原"参数优化"的样本内网格排名实现。
一、问题定义
原"参数优化"(optimizer.py) 在用户选定的整段区间上对每组参数回测并按单一目标排序—— 排名即"对这段历史噪声的拟合度排名", 选出的必然是尖峰参数, 未来失效是设计使然。 原"步进优化"方法论骨架正确 (防前视折切分 + 复利 OOS 净值 + IS→OOS 退化), 但缺护栏与闭环。
二、命名 (已定稿)
| 旧名 | 新名 | 职责 |
|---|---|---|
| 参数优化 | 稳健优化 | 选参数: 多段互不重叠验证窗上的稳健分排名, 产出一组参数 |
| 步进优化 | 滚动样本外检验 | 验流程: 每折重优化→紧邻样本外检验, 产出流程可信度判据 (Walk-Forward Analysis) |
命名原则: 按产出物命名。稳健优化的产出是参数, 叫优化合理; 滚动样本外检验的优化只是 内部工序, 产出是判定, 不叫优化。内部路由/任务类型 id 不改 (保任务注册表断线重连), 只改展示名与文档。
三、稳健优化 · 评估协议
K 段锚定验证 + 两阶段预算。区间 [S,E]:
- 切窗: 等分为 K 个互不重叠验证段 V₁…V_K (默认 K=4, 可配 3-6)。每组参数固定不变 地在每个 Vᵢ 上独立回测 (复用 walkforward 折工具 + prepare_matrix_optimization 矩阵缓存)。
- 稳健分: R(p) = 0.6 × median(oos₁…K) + 0.4 × worst(oos₁…K) —— 中位数扛单段运气, 最差段惩罚"只在一段行情好用"; 各段得分与离散度并排展示 (离散度本身就是过拟合警报)。
- 两阶段预算: 阶段一全网格只跑 K 个短验证窗 (总数据量 ≈ 一次全窗, 计算量与旧版持平); 阶段二仅 Top-20 跑全窗 IS 得分 (算 IS→OOS 退化率) + 邻域细化。
- 免费诊断: 阶段一持有"每组×每段"完整得分矩阵 → 各段冠军收敛度直接可得。 各段冠军收敛 → 参数可信; 散落 → 策略本身参数不稳定, 任何"最优"都别信。
- 高原判据: 冠军周围网格点的得分衰减率 (数据来自阶段一, 零额外计算)。
四、护栏 (两模式共用一处实现)
- 最小交易数门槛 (稳健优化默认 30 笔整体 / 滚动检验默认 10 笔每折 OOS 段): 不足的组合/折标记降序垫底, 不静默剔除。
- 主目标白名单收紧: sortino / sharpe / calmar / 年化 / 盈亏比 / 蒙卡回撤P95 / 最大回撤。 删除 win_rate、平均持仓天数作主目标 (退化目标), 降级为展示列。
- 每段数据覆盖检查 (段内无数据/长停牌明确标注)。
五、滚动样本外检验 · 设计补强
保留: generate_folds 滚动切窗 (+1 天防前视隔断)、每折独立网格重优化、 aggregate_oos (复利 OOS 净值 / 退化 / 一致性)。补:
- 每折 OOS 段最小交易数门槛 → 无效折排除出聚合 (沿用"只聚合有效折"机制)。
- 折预览: 运行前实时显示折数/窗口/覆盖; 折数 < 3 警告"统计意义不足"。
- 闭环「采用最新折参数」: 滚动检验的标准实践是拿最后一折训练参数上实盘, 走 /strategy/config override 通道 (带确认弹窗, 注明来源折)。
- 参数漂移轨迹: 各折 best_params 逐参数轨迹 (如 ma_fast: 5→5→8→10) + 收敛度徽章; 与稳健优化的"各段冠军收敛度"同一诊断模块的两种呈现。
不做: 滚动检验不加"参数固定模式" — 那正是稳健优化阶段一 (避免重复), 文档写清分工。
六、闭环 (应用通道)
两模式的落地动作统一走现有 POST /strategy/config override 通道 (与优化器运行时同路,
策略页可查看/还原, 可逆、零新端点):
稳健优化 ──选参──▶ 滚动样本外检验 ──验流程──▶ 应用(override) ──▶ 回测/监控
└────────── 也可直接应用(带退化/高原判据) ──────────────┘
七、共享件 (一处实现, 两模式使用)
折工具函数 / 目标白名单+护栏 / 稳健分组件(median/worst/离散度) / 参数漂移诊断 / 矩阵缓存与 worker 隔离 / SSE+断线重连框架 / override 应用通道 / paramSweep 网格 UI。
八、落地阶段 (每阶段独立提交、独立验证)
| 阶段 | 内容 | 验证标准 |
|---|---|---|
| P1 | 后端 robust_optimizer 模块: 切窗/稳健分/护栏/SSE v2; 共享护栏与漂移诊断模块; 滚动检验折有效性门槛 | 单测覆盖切窗边界/稳健分/门槛/取消; 实测 200 组×4 折耗时与 worker 内存 |
| P2 | 两阶段细化 + 稳定性诊断 + 高原比 + 折预览 | 性能 ≤ 旧版全量跑的 1.3 倍 |
| P3 | 前端: 稳健优化结果面板(排名表/冠军卡/高原视图/应用按钮) + 滚动检验新组件(折预览/漂移轨迹/采用最新折参数) | tsc/构建/vitest + 端到端 |
| P4 | 摒弃旧逻辑: 删 optimizer.py 单窗全量排名路径; 目标白名单收紧生效; README/docs 同步 | 全量回归绿 |
摒弃清单: 单窗样本内排名 / 退化主目标 / 纯展示无闭环的结果面板。 复用清单: worker 隔离 / 矩阵缓存 / SSE 任务框架与断线重连 / 网格 UI / override 通道 / 折工具函数。
九、已知权衡
- 验证段比全窗短 → 单段统计更噪; median+worst 聚合为此设计, 且这正是"未来任意一段行情" 的真实压力测试。
- 结果不再有单一"历史冠军"的爽感 — 特性而非缺陷, UI 文案引导解读。
- Top-20 细化增加约 10% 计算, 换高原判据。
- 性能实测偏离 1.3× 目标 (P2 验收): 126 组合×全市场网格实测稳健优化 (K=4+阶段二)
为旧版全量跑的 1.71× (视图复用优化后, 优化前 2.01×)。数据量比只有 1.16×
(146 窗口当量 / 126), 差额来自引擎层每 run 固定开销 (~28ms/段: 信号延迟矩阵重建
_delay_signal_matrix+ 矩阵缓存 token 哈希_array_token), 与窗口长度无关。 修它属于引擎级手术 (影响所有回测路径), 不在本次重构范围; 绝对值 44s/126 组合 完全可用。若未来引擎把 per-run 固定开销降到 ~10ms, 比值自然回到 ~1.3×。