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
+51 -2
View File
@@ -191,7 +191,7 @@ def test_backtest_request_defaults():
from easy_tdx.web.backtest_schemas import BacktestRequest
req = BacktestRequest(strategy="ma_cross", symbol="SZ:000001")
assert req.cash == 100000.0
assert req.cash == 1_000_000.0
assert req.commission == 0.0003
assert req.execution == "next_open"
assert req.category == "DAY"
@@ -704,7 +704,7 @@ def test_portfolio_request_defaults():
from easy_tdx.web.backtest_schemas import PortfolioBacktestRequest
req = PortfolioBacktestRequest(strategy="ma_cross", stocks=["SZ:000001"])
assert req.cash == 200000.0
assert req.cash == 1_000_000.0
assert req.category == "DAY"
@@ -899,6 +899,55 @@ def test_optimize_single_param_no_heatmap(client, sample_ohlcv):
assert final["result"]["heatmap"] is None
def test_optimize_all_endpoint(client, sample_ohlcv):
"""POST /backtest/optimize-all/run/async 端到端:逐策略预设网格寻优 + 全局排名。"""
resp = client.post(
"/api/v1/backtest/optimize-all/run/async",
json={
"cash": 1_000_000,
"ohlcv": sample_ohlcv,
},
)
assert resp.status_code == 202, resp.text
task_id = resp.json()["task_id"]
final = None
for _ in range(400):
poll = client.get(f"/api/v1/backtest/tasks/{task_id}")
final = poll.json()
if final["status"] in ("done", "failed"):
break
time.sleep(0.05)
assert final["status"] == "done", final
result = final["result"]
# 排名按 total_return 降序、best 指向第一名、各策略最优点齐全
assert "ranking" in result and len(result["ranking"]) > 0
assert "best" in result and result["best"] is not None
assert "per_strategy" in result and len(result["per_strategy"]) == len(result["ranking"])
assert "total_grid_points" in result and result["total_grid_points"] > 0
# ranking 降序校验
returns = [r["total_return"] for r in result["ranking"]]
assert returns == sorted(returns, reverse=True)
# best == ranking[0]
assert result["best"]["strategy"] == result["ranking"][0]["strategy"]
# 合计网格点 == 各策略 grid_points 之和
assert result["total_grid_points"] == sum(r["grid_points"] for r in result["ranking"])
def test_optimize_all_request_validation():
"""optimize-all 请求必须提供数据源。"""
from easy_tdx.web.backtest_schemas import OptimizeAllBacktestRequest
# 缺数据源
with pytest.raises(ValueError):
OptimizeAllBacktestRequest()
# 合法
req = OptimizeAllBacktestRequest(symbol="SZ:000001")
assert req.cash == 1_000_000.0
assert req.execution == "next_open"
# ---------------------------------------------------------------------------
# Phase 5: 任务列表端点(对比页用)
# ---------------------------------------------------------------------------