Commit Graph
8 Commits
Author SHA1 Message Date
Justin Gu 6b51487477 test: 修复 test_task_runner_lru_eviction 的竞态 flaky
根因:测试用 max_workers=1 串行执行 5 个任务,断言「前 2 个被淘汰」。
但 LRU 淘汰发生在 submit() 时,淘汰的是「当时最旧的非 running 任务」。
快机器上 t0 还在 running(被跳过),实际淘汰 t1/t2;慢机器(CI windows
3.12)上 t0 已完成,淘汰 t0/t1——取决于提交速度 vs 执行速度的竞态。

修复:不断言特定 task_id 被淘汰,改为验证不变量:
(1) 最后提交的任务一定存活(LRU 最近)
(2) 至少淘汰 2 个(5 - max_results 3)
(3) 存活任务数 ≤ max_results

本地跑 10 次全过,不再 flaky。
2026-07-07 15:47:13 +08:00
Justin Gu cd790c9404 release: v1.17.8 — 修复 windows 3.10 flaky 测试超时
test_task_runner_does_not_evict_running 用 release.wait(timeout=5) 钉住慢任务保持
running,但 CI 慢环境(windows 3.10)下整个测试执行超过 5s 后任务因超时自动完成、
状态变 done,掩盖了「running 任务被 LRU 错误淘汰」的回归断言。

改为 timeout=30 留足 CI 慢环境余量(release.set() 仍是确定性释放点)。该测试用于
守护 LRU 淘汰跳过 running 任务的并发正确性,与港股逐笔成交无关,是 v1.17.2 引入的
预先存在问题。

本地连跑 5 次稳定通过,867 单测全绿。
2026-07-04 02:24:48 +08:00
Justin Gu f6ae69845d 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 单测全绿)
2026-07-04 00:08:38 +08:00
GitHub 9245bb2ce9 chore(tests): 修复 ruff UP038 (isinstance 元组 -> int | float)
test_web_backtest.py:556 的 isinstance(x, (int, float)) 触发 ruff UP038。
requires-python>=3.10 支持 PEP 604 的 int | float 运行时语法, 转换安全。
该报错会导致 GitHub Actions CI 失败。
2026-07-03 15:42:24 +08:00
Justin Gu 2903d8c800 feat(strategies): 内置策略从 5 个扩充到 18 个
新增 13 个经典策略,覆盖趋势/通道/震荡/均线四大类:

趋势类:
- ema_cross  EMA 双线交叉(比 MA 反应更灵敏)
- triple_ma  三均线系统(多头/空头排列)
- dmi        DMI 趋向指标(+DI/-DI 交叉)
- trix       TRIX 三重平滑(过滤短期波动)

通道/突破类:
- donchian   唐安奇通道突破(海龟交易法)
- keltner    肯特纳通道(ATR-based)
- atr_breakout ATR 通道突破(均线±K×ATR)

震荡/反转类:
- cci        CCI 超卖反弹
- wr_reversal WR 威廉超卖
- bias_reversal BIAS 乖离反弹
- emv        EMV 简易波动(量价结合)
- dpo        DPO 区间震荡

均线类:
- bbi        BBI 多空指标(4 均线综合)

全部基于 MyTT 现成指标实现,每个策略声明参数 schema 供 Web 表单动态渲染。
18/18 策略冒烟测试通过(合成数据回测无运行时错误)。
2026-07-03 04:36:04 +08:00
Justin Gu ff3476f83a 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 列表端点)
2026-07-03 04:11:29 +08:00
Justin Gu 87fafe9131 feat(backtest): 参数网格寻优(optimizer + 前端寻优页)
对单个策略的 1-2 个参数做网格搜索,遍历用户指定的取值列表笛卡尔积,
每个组合跑一次回测,按 total_return 排序,返回排名表 + 热力图。

后端:
- ParamGridOptimizer(backtest/optimizer.py):itertools.product 遍历网格,
  每点 entry.build(params) + BacktestEngine.run(df),复用同一 DataFrame
- 网格大小上限 200 防组合爆炸,单点失败容错(跳过不中断)
- 2 参数时生成热力图矩阵(x/y 轴取值 + cell 收益率)
- POST /backtest/optimize/run/async 端点(后台任务)
- OptimizeBacktestRequest schema(param_grid 1-2 参数)

前端(/optimize 寻优页):
- ParamGridPicker:勾选 1-2 个寻优参数,逗号分隔填取值列表
- OptimizeResultTable:网格点排名表(按收益降序,最优高亮)
- OptimizeHeatmap:2 参数热力图(ECharts heatmap,绿→红映射收益)
- 最优点「查看」按钮跳转单标的页用该参数回测

测试:821 passed(+10 寻优器单测 + 3 寻优路由测试)
2026-07-03 03:55:35 +08:00
Justin Gu 38731114b6 feat(backtest): 回测 REST API + 策略注册表 + 组合回测引擎
后端回测系统完整实现:

策略注册表(backtest/strategies/):
- Param schema 声明机制,支持动态表单渲染
- 5 个内置策略:MA交叉/MACD/布林/RSI/KDJ
- 参数校验(含 NaN/Inf 拦截、范围检查、类型强制转换)

REST API(web/routers/backtest.py):
- GET  /backtest/strategies  策略枚举 + 参数 schema
- POST /backtest/run         同步回测(内联 OHLCV)
- POST /backtest/run/async   后台任务回测(含 symbol 取行情)
- POST /backtest/portfolio/run/async  组合回测(多标的)
- GET  /backtest/tasks/{id}  任务轮询

后台任务执行器(web/task_runner.py):
- ThreadPoolExecutor + 进程内 LRU 任务表
- status-aware 淘汰(不淘汰 running 任务)
- 线程安全单例 + lifespan shutdown 接入

组合回测引擎改造(portfolio_engine.py):
- 接受策略实例,参数透传到每个标的
- 新增组合净值曲线(按日期并集 forward-fill 对齐求和)

审计修复(/check 三轮):
- Param.validate 拦截 NaN/Inf/giant-int(防 DoS)
- ohlcv max_length=2000(防内存耗尽)
- LRU 淘汰跳过 running 任务(修复结果丢失竞态)
- get_runner double-checked locking(修复单例竞态)
- shutdown 接入 lifespan(修复资源泄漏)

测试:808 passed(含 39 回测路由 + 8 组合引擎 + 安全回归)
2026-07-03 03:34:34 +08:00