feat(backtest): 成交价精简 + 一键寻优全策略 + 预设网格

- 成交价精简为 next_open/next_close,删除 this_close/worst/best 三种
  非真实模式(this_close 有未来函数偏差,worst/best 为压力测试边界)
- 初始资金默认统一为 1,000,000(原 10万/20万)
- 新增 presets.py:18 策略各配 1-2 参数的预设寻优网格(笛卡尔积≤200)
- registry.to_schema() 返回 preset_grid 字段供前端自动填充
- 新增 POST /backtest/optimize-all/run/async:逐策略预设网格寻优 +
  全局排名(OptimizeAllResult:ranking/best/per_strategy/total_grid_points)
- 新增 optimize-all 端到端单测 2 例(838 单测全绿)
This commit is contained in:
Justin Gu
2026-07-04 00:08:38 +08:00
parent c15bd8232f
commit f6ae69845d
10 changed files with 346 additions and 130 deletions
+101
View File
@@ -0,0 +1,101 @@
"""策略参数寻优预设网格。
每个内置策略 1-2 个关键参数的合理取值列表,供:
1. Web UI 参数寻优页(``ParamGridPicker``)切换策略时自动填充——用户开箱即用,
仍可编辑/取消勾选。
2. 「一键寻优所有策略」(``/backtest/optimize-all/run/async``)逐策略做网格
寻优再全局排名。
约束:单策略笛卡尔积 ≤ 200``ParamGridOptimizer.MAX_GRID_POINTS``)。行尾注释
标注该策略的网格点数。修改本文件即同步生效,无需重启策略注册表。
新增策略时:在 :data:`STRATEGY_PRESETS` 增加同名 key 即可;未登记的策略在
「一键寻优」中会被跳过并记录 warning。
"""
from __future__ import annotations
from typing import Any
# 策略名 → {参数名: 取值列表}
STRATEGY_PRESETS: dict[str, dict[str, list[Any]]] = {
# ── 均线类 ──────────────────────────────────────────────────────────────
"ma_cross": {
# 快线 < 慢线才有意义;笛卡尔积去重快≥慢的无效组合后约 30+
"fast": [5, 10, 15, 20, 30, 60],
"slow": [10, 20, 30, 60, 120, 250],
}, # 36
"ema_cross": {
"fast": [5, 10, 12, 20],
"slow": [20, 26, 30, 60],
}, # 16
"triple_ma": {
"short": [5, 10, 15],
"long": [60, 120, 250],
}, # 9mid 用默认 20
"bbi": {
"m1": [3, 6],
"m4": [20, 30, 50],
}, # 6
# ── 通道/突破类 ─────────────────────────────────────────────────────────
"boll_breakout": {
"n": [10, 15, 20, 30],
"p": [1.5, 2.0, 2.5, 3.0],
}, # 16
"donchian": {
"n": [10, 15, 20, 30, 40, 55],
}, # 6
"keltner": {
"n": [10, 15, 20, 30],
"m": [7, 10, 14, 20],
}, # 16
"atr_breakout": {
"n_ma": [10, 20],
"k": [1.5, 2.0, 2.5, 3.0],
}, # 8
# ── 振荡/反转类 ─────────────────────────────────────────────────────────
"macd": {
"short": [8, 10, 12, 15],
"long": [20, 26, 30, 40],
}, # 16
"rsi_reversal": {
"n": [7, 14, 21],
"oversold": [20, 25, 30],
}, # 9
"kdj_cross": {
"n": [5, 9, 14, 18, 21],
}, # 5
"cci": {
"n": [14, 20],
"oversold": [-150, -100, -50],
}, # 6
"wr_reversal": {
"n": [7, 14],
"oversold": [-90, -80, -70],
}, # 6
"bias_reversal": {
"n": [6, 12],
"threshold": [3.0, 5.0, 7.0, 10.0],
}, # 8
# ── 趋向/平滑类 ─────────────────────────────────────────────────────────
"dmi": {
"m1": [14, 20],
"m2": [6, 10, 14],
}, # 6
"trix": {
"m1": [12, 18],
"m2": [12, 20, 30],
}, # 6
"emv": {
"n": [10, 14, 20, 28],
}, # 4
"dpo": {
"m1": [15, 20, 30],
}, # 3
}
def get_preset(strategy_name: str) -> dict[str, list[Any]]:
"""返回某策略的预设网格;未登记返回空 dict。"""
return STRATEGY_PRESETS.get(strategy_name, {})