feat(backtest): 回测结果对比页

选 2-4 个已完成的回测 task,叠加净值曲线 + 横向指标对比。

后端(最小增量):
- task_runner.list_recent(limit):返回最近 N 个任务摘要(LRU 倒序)
- GET /backtest/tasks?limit=20:任务摘要列表端点(不含完整 result)
- TaskSummary / TaskListResponse schema

前端(/compare 对比页):
- CompareView:左栏勾选已完成 task,右栏叠加对比
- CompareChart:多 task 净值叠加图(归一化为初始=1)
- CompareTable:多 task 指标横向对比表(8 项指标)
- 按 result 结构判断可对比性(仅单标的 BacktestResult 可对比)

复用现有 task_runner LRU 表,无新增存储。测试:823 passed(+2 列表端点)
This commit is contained in:
Justin Gu
2026-07-03 04:11:29 +08:00
parent 87fafe9131
commit ff3476f83a
11 changed files with 501 additions and 1 deletions
+42
View File
@@ -897,3 +897,45 @@ def test_optimize_single_param_no_heatmap(client, sample_ohlcv):
time.sleep(0.05)
assert final["status"] == "done"
assert final["result"]["heatmap"] is None
# ---------------------------------------------------------------------------
# Phase 5: 任务列表端点(对比页用)
# ---------------------------------------------------------------------------
def test_list_tasks_endpoint(client, sample_ohlcv):
"""GET /backtest/tasks 返回最近任务摘要列表。"""
for _ in range(2):
client.post(
"/api/v1/backtest/run/async",
json={"strategy": "ma_cross", "ohlcv": sample_ohlcv},
)
import time as _time
_time.sleep(0.5)
resp = client.get("/api/v1/backtest/tasks?limit=20")
assert resp.status_code == 200
body = resp.json()
assert body["count"] >= 2
task = body["tasks"][0]
assert "task_id" in task
assert "status" in task
assert "description" in task
assert "result" not in task # 摘要不含完整 result
def test_list_tasks_limit(client, sample_ohlcv):
"""limit 参数应限制返回数量。"""
for _ in range(3):
client.post(
"/api/v1/backtest/run/async",
json={"strategy": "ma_cross", "ohlcv": sample_ohlcv},
)
import time as _time
_time.sleep(0.5)
resp = client.get("/api/v1/backtest/tasks?limit=2")
assert resp.json()["count"] <= 2