Files
sanguo_vnpy_v2/docs/design/backtest-ashare-adapter-plan.md
T
claude_dev 8d55e414fa fix(backtest): A股适配层—定寸/做空拦截/真实费用/口径统一(Phase1+2)
审计发现包装层系统性失真(2 CRITICAL+7 HIGH),vnpy底座可信但A股场景未适配:
- C1 定寸: engine.size=N(满仓手数),策略volume=1手=N股,开平对称(pos归零)
- C2 做空拦截: SHORT+OPEN拒单,long-only,SHORT+CLOSE平多允许
- H3 A股费用: AShareDailyResult重算(佣金保底5元/印花税卖方/过户费沪市)
- H4 收益口径: simple return从balance算(不再用vnpy log return喂empyrical)
- H5+口径: benchmark ffill对齐不缩样本; sizing_shares_per_lot暴露
- H7 退化检测: 零成交/空数据标degenerate不静默done
- H8 task_id: optimize/factor用uuid4(原id()内存地址)
- 静默except改warning

验证: 容器内真实vnpy DoubleMa 600000 2022-2024, total_return 1e-6→42.3%,
end_balance 100万→142万, SHORT+OPEN成交0笔, N=7800股/手.
22 backtest测试全绿(含集成测试), API健康200.
2026-07-12 23:39:45 +08:00

5.4 KiB
Raw Blame History

回测引擎 A 股适配层 — Phase 1+2 实施计划

起因:审计发现包装层在"A 股股票回测"场景系统性失真(2 个 CRITICAL + 7 个 HIGH)。 见 memory/backtest-engine-soundness.md(待写)+ 审计 agent 报告。 vnpy 源码(项目实际 import 份):容器内 site-packages;参考副本 ~/.openclaw/knowledge_base/vnpy_ctastrategy/。 约束:vnpy_v4.4.0 源码零修改,全部用子类化/包装在外层实现。Mac 无 vnpy_ctastrategy,测试在 NAS 容器内跑。

目标(Phase 1+2

回测结果诚实(不虚构做空盈亏、不 1 股空转)且准确(A 股真实费用、收益/年化口径一致)。


Phase 1 — 诚实(3 项)

C1 定寸:按资金 + 价格算手数

  • 机制:策略传 volume 是"满仓单位数"vnpy 模板默认 1 = 1 个满仓),包装层换算成实际股数。
  • 公式(每次下单按当时 price 重算):shares_per_unit = floor(capital * position_pct / price / 100) * 100(按手取整 100 股);actual_volume = volume * shares_per_unit
  • 落点:AShareBacktestingEngine.send_order 覆写,重算 volume 再 super。

C2 做空拦截:long-only

  • 机制:SSE/SZSE 标的,direction==SHORT and offset==OPEN 直接拒单(返回 [],warning 日志)。允许 SHORT+CLOSE(平多)。
  • 落点:AShareBacktestingEngine.send_order 覆写开头判断。
  • T+1:日 bar 策略层面影响小(信号收盘、次日执行),Phase 3 再处理。

H9 真实集成测试

  • 新增 tests/backtest/test_integration_ashare.py@pytest.mark.integration,容器内跑真 vnpyDoubleMa 600000 2024-01~2024-06,断言:
    • end_balance != capital(非空转)
    • 全部 trade direction != SHORT or offset == CLOSE(做空拦截)
    • 存在 trade.volume > 100(定寸生效,满仓手数)
    • total_return 绝对值 > 1e-3(非噪声)

Phase 2 — 准确(4 项)

H3 A 股费用模型

  • AShareDailyResult(DailyResult) 覆写 calculate_pnl,每笔 trade
    • turnover = volume * size * pricesize=1
    • commission += max(turnover * rate, min_commission)(双边,rate 默认 0.00025 万 2.5min_commission 默认 5 元)
    • stamp_duty += turnover * stamp_duty_rate if direction==SHORT else 0(卖方 0.0005
    • transfer_fee += turnover * transfer_fee_rate if 沪市 else 00.00001
    • net_pnl = total_pnl - commission - stamp_duty - transfer_fee - slippage
    • 把 stamp_duty/transfer_fee 存为实例属性(持久化可见)。
  • 落点:AShareBacktestingEngine 覆写 DailyResult 创建处用 AShareDailyResultagent 读源码定位 self.daily_results[date] 创建点,可能需覆写 run_backtesting 里的工厂或设类属性)。

H4 log→simple return

  • vnpy df["return"]np.log(...)backtesting.py:353)。empyrical 期望 simple return。
  • 修:compute_metrics 入参改为从 daily_df["balance"] 自算 s = balance.pct_change().fillna(0),不再依赖 vnpy 的 log 列。删 cta_engine:198-204 的三路 fallback。

H5 年化统一 252

  • empyrical period='daily' 内部 252,已对。确认 statistics 最终用的是 empyrical 那套 scalarscta_engine:210 statistics.update(metrics_result.scalars) 覆盖 vnpy 键)。前端展示字段映射到 empyrical scalars。

口径统一(MEDIUM 顺带)

  • benchmark 对齐:metrics.py:32 dropna()benchmark.reindex(daily_df.index).ffill().fillna(0),不丢策略日期。
  • equity 单一源:/equity-curve(绝对 balance)与 /benchmark-curve(相对)尺度对齐——统一改相对净值 balance/capitalbenchmark 用 cum_returns,两图同尺度。

顺带修(成本几乎为零,同文件)

  • H8 runner.py:66,93id(grid)/id(factor_names)uuid4().hex[:8]
  • H7 cta_engine:零成交/空数据 → status="degenerate" + statistics["degenerate_reason"],不静默 done。
  • 静默吞错改 warningstrategy_registry.py import except、cta_engine:127-134 config except、:229-232 metrics except —— 加 logging.warning + 失败时 statistics 塞错误字段。

schemas/routes 改动(最小)

  • CtaBacktestRequestcapital: float = 1_000_000position_pct: float = 0.95+ 可选 commission/stamp_duty/transfer_fee/min_commission,给默认值,前端先不暴露)。
  • routes.py run 端点透传 capital/position_pct → run_cta_backtest
  • run_cta_backtest 签名加这些参数,传给 AShareBacktestingEngine

文件清单

文件 动作
sanguo_backtest/ashare_engine.py AShareDailyResult + AShareBacktestingEngine
sanguo_backtest/metrics.py 改:simple return、ffill 对齐、单一 equity
sanguo_backtest/cta_engine.py 改:换 AShare 引擎、传参、删 fallback、degenerate 检测、静默吞改 warning
sanguo_api/schemas.py 改:加 capital/position_pct
sanguo_api/routes.py 改:透传参数
sanguo_orchestrator/runner.py 改:task_id uuid
sanguo_backtest/strategy_registry.py 改:except 加 warning
tests/backtest/test_integration_ashare.py :真实集成测试

不做(Phase 3 候选)

T+1、组合回测(需 vnpy_portfoliostrategy)、optimize parent 分组、SQLite WAL、MockExchange 重构、滑点/年化可配置化、rolling alpha/beta 口径。