Backtest
把规格冻结、回测执行和诊断放在一个 skill 内完成。始终把 backtest_spec.md 作为策略意图的单一事实源;禁止在执行阶段静默补参数、替换信号或改用代理池。
工作流
需求或已有代码
→ Stage 0 路由
→ Stage 1 规格冻结与校验
→ Stage 2 编写、执行和结果校验
→ Stage 2.5 向用户交付核心结果并确认是否继续
→ Stage 3 可选六维数据分析
→ Stage 4 可选综合诊断报告
先从 skill loader 获取当前 skill 的真实目录,不要依赖工作目录或机器专属路径:
BACKTEST_SKILL_DIR="<当前实际加载的 backtest skill 目录>"
SPEC_VALIDATOR="$BACKTEST_SKILL_DIR/scripts/spec_validator.py"
RESULT_VALIDATOR="$BACKTEST_SKILL_DIR/scripts/result_validator.py"
test -f "$SPEC_VALIDATOR"
test -f "$RESULT_VALIDATOR"
所有生成文件写到用户工作目录下的 strategies/,禁止写入 BACKTEST_SKILL_DIR。
Stage 0:路由输入
按以下顺序处理:
- 用户提供
backtest_spec.md:确认文件非空,运行spec_validator.py;通过后进入 Stage 2。 - 用户提供完整
backtest.py:从代码反向抽取参数,按references/spec_template.md生成 spec 草案并让用户确认;确认后写盘、校验,再执行原代码。 - 用户只有口语需求或零散规则:进入 Stage 1。
- 用户只要求解释已有结果:读取
backtest_result.json、trade_records.csv和 spec;不要重跑,除非用户要求或文件校验失败。
已有 spec 也必须运行:
SPEC_PATH="<path/to/backtest_spec.md>"
test -s "$SPEC_PATH"
python3 "$SPEC_VALIDATOR" "$SPEC_PATH"
退出码非 0 时留在 Stage 1,修复同一个 spec 后重跑;禁止边写代码边猜缺失项。
Stage 1:规格冻结
1.1 识别原型并读取对应参考
读取 references/strategy_archetypes.md,把策略归入 timing、selection、event_driven、dca、hedge 或 grid。跨原型策略按最复杂原型展开,再补充其他原型字段。
遇到“机构买、密集调研、评级上调、业绩超预期、ROE、目标价、资金流入、估值低位、回购、解禁、分红”等词时,读取 references/signal_definitions.md,把原话映射到 endpoint、API、字段、阈值、窗口和可见日。
1.2 执行严格可回测性硬门
在确认参数前判断原始信号是否存在 point-in-time 历史数据:
- 有严格数据:写明 endpoint、API、字段、可见日和 lag。
- 无严格数据但有代理:向用户展示代理定义和非等价风险;只有用户确认后才能设置
proxy_used: true、strict_backtestable: false。 - 无严格数据且没有可靠代理:设置
execution_status: NOT_EXECUTED、strict_backtestable: false、proxy_used: false,完整记录not_executed_reason和data_gap。
禁止把 EPS 超预期换成利润同比、把 ROE 换成利润增长、把机构调研换成评级数量,或把全市场换成手写样本池而不披露。
1.3 对齐 Tier 1、Tier 2 和 Tier 3
完整读取 references/spec_template.md,使用其中的原字段名:
- Tier 1 必须由用户明确:标的或股票池、起止日、频率、资金、基准、策略原型。
- Tier 2 使用
references/canonical_conventions.md的市场默认值,但在草案中逐项展示复权、分红、佣金、印花税、滑点、手数、T+1、交易日数和无风险利率。 - Tier 3 按原型问清信号、成交时点、仓位、重复信号、满仓行为、出场、数据缺失处理和风控。
如果一个字段存在两种合理解释,先询问。用户明确说“你定”时才使用 assistant_default,并在 unresolved 中记录默认值、原因和用户可见风险。
对于“至今”或宽泛股票池,同时记录用户请求和实际可执行范围:requested_end/actual_end/end_adjustment_reason、requested_pool/actual_scan_pool/proxy_pool/proxy_reason。
1.4 Echo 并等待确认
按 references/spec_template.md 的 Echo 模板展示完整草案。必须让用户看到 Tier 2 数值、模糊词翻译、代理风险和关键 if/else 决策。
在用户确认前不要写盘、取行情或进入 Stage 2。用户已经提供并明确确认完整 spec 时,不重复询问。
1.5 写盘并循环校验
确认后写到:
{workdir}/strategies/{strategy_name}/backtest/{start}-{end}/backtest_spec.md
只保存这一份 spec,不创建 spec_v2.md、spec.json 等副本。写完连续运行:
test -s "$SPEC_PATH"
python3 "$SPEC_VALIDATOR" "$SPEC_PATH"
只要 validator 输出 FAILED,按原字段名修复同一文件并重跑,直到退出码为 0。不得把对话中的 Markdown 或 UI 完成态当成落盘证据。
Stage 2:执行回测
2.1 读取 spec 并执行忠实度硬门
从 spec 的 yaml 代码块读取全部参数。开始写代码前检查:
execution_status: NOT_EXECUTED:不生成伪收益;写声明性backtest_result.json和只有表头的trade_records.csv,再运行结果 validator。strict_backtestable: false、proxy_used: false且不是NOT_EXECUTED:回到 Stage 1 修正。proxy_used: true且strict_backtestable: true:回到 Stage 1 修正。proxy_for_original_signal、因子proxy_for或proxy_pool非空:在脚本、JSON、图表和摘要中一致披露代理关系。
执行中发现数据接口不支持 spec 字段时,记录 SPEC_VIOLATION,停止最终结果,回到 Stage 1 更新 spec。禁止直接换字段继续跑。
2.2 选择模板并写代码
先完整读取 references/canonical_conventions.md,再按策略选择:
- 一般择时、趋势、选股、对冲、网格、定投:读取
references/backtest_template.md。 - 事件触发型:读取
references/event_driven_template.md,同时读取references/neodata_api_inventory.md。 - 使用财务、估值或事件信号:同时读取
references/signal_definitions.md,确保 point-in-time 和 provenance 正确。
生成:
{workdir}/strategies/{strategy_name}/backtest/{start}-{end}/backtest.py
在文件头记录 spec 路径和 hash。所有配置必须从 spec 派生。通过 BACKTEST_SCRIPTS 或当前 skill 的真实目录定位 scripts/;禁止引用其他回测 skill 或用户绝对路径。
2.3 数据与数值规则
使用 scripts/neodata_kline.py 加载 K 线,使用 scripts/neodata_client.py 获取其他 neodata 数据。接口库存见 references/neodata_api_inventory.md。
严格执行 references/canonical_conventions.md,尤其是:
- 为 MA、MACD、ATR、Beta 等滚动指标加载回测开始日前的 warm-up 数据。
- 价格水平策略使用 spec 指定的真实价或复权价;不得把 qfq 价格标成 raw price。
- DCA 使用外部注资和修正净值,基准同步 DCA,并同时披露真实资金收益。
- 财务、估值和事件字段按披露日或可见日对齐,禁止未来函数。
- 建模或披露 T+1、涨跌停、特殊板块限制、融券、保证金、分红和公司行动。
- 为事件型交易写入
source_api/source_field/observed_value/signal_date/source_refprovenance。
2.4 执行并验证输出
在回测目录运行 python3 backtest.py。普通已执行回测必须生成:
backtest.py
backtest_spec.md
backtest_result.json
backtest_result.png
backtest_eda.html
trade_records.csv
无交易也要写 CSV 表头。JSON 只放交易预览时,设置 trade_records_truncated 并提供 trade_records_csv。
运行:
python3 "$RESULT_VALIDATOR" backtest_result.json
退出码非 0 时修复 backtest.py 并完整重跑。JSON、CSV、PNG、HTML 和摘要必须来自同一次最终运行;禁止用临时复算结果覆盖口头摘要。
最终摘要至少披露:请求区间与实际区间、请求池与实际/代理池、严格或代理策略、数据缺口、分红与交易制度建模情况、核心收益风险指标、基准对比,以及不可与其他平台直接横比的原因。
Stage 2.5:暂停并确认下一步
完成回测后先交付核心指标、基准对比和简短结论,然后询问用户是否继续:
- 进入 Stage 3 和 4:六维分析加综合诊断。
- 只进入 Stage 4:基于现有回测结果生成简版诊断,并注明未做六维数据分析。
- 停在 Stage 2。
- 修改 spec 后重跑。
只有用户原始请求已明确要求完整诊断时,才直接进入 Stage 3。不得因深度诊断而默认增加外部请求和运行开销。
Stage 3:六维分析
读取 backtest_result.json 中的标的、期末持仓、实际区间和最大回撤区间。使用 scripts/fetch_dimension.py 并行获取 financial、industry、events、macro、institutional、research 六个维度,分别写 raw_<dim>.json 和日志。
读取 references/agent_prompts.md,基于原始 JSON 顺序生成:
analysis_financial.md
analysis_industry.md
analysis_events.md
analysis_macro.md
analysis_institutional.md
analysis_research.md
数据为空时明确标注覆盖缺口;不要编造结论或用当前截面解释历史时点。
Stage 4:综合诊断
读取通过校验的结果、spec 和六份分析(若用户选择跳过 Stage 3,则只使用现有证据)。按 references/report_template.md 生成 strategy_diagnosis.md,包括策略概述、指标与基准、归因、最大回撤事件、风险、未来情景、六维评分和可落实的优化建议。
把事实、推断和建议分开。每个结论指向对应 JSON 字段、交易记录或分析文件;缺少证据时降低置信度。
资源路由
| 文件 | 何时读取 |
|---|---|
| references/spec_template.md | 创建、修改或反向抽取 spec 时必读 |
| references/strategy_archetypes.md | 识别原型和补齐 Tier 3 时必读 |
| references/signal_definitions.md | 需求包含模糊、事件、财务或估值信号时必读 |
| references/canonical_conventions.md | 写任何回测代码前必读;它是唯一数值规范 |
| references/backtest_template.md | 编写一般回测脚本时读取 |
| references/event_driven_template.md | 编写事件型脚本时读取 |
| references/neodata_api_inventory.md | 选择 K 线以外的数据接口时读取 |
| references/agent_prompts.md | 执行 Stage 3 时读取 |
| references/report_template.md | 执行 Stage 4 时读取 |
| references/openclaw_tips.md | 遇到 OpenClaw、TLS、并行或发布问题时读取 |
| 脚本 | 用途 |
|---|---|
| scripts/spec_validator.py | 校验 spec 字段与跨字段约束 |
| scripts/result_validator.py | 校验回测结果、指标一致性和策略审计字段 |
| scripts/neodata_kline.py | 加载与聚合 K 线 |
| scripts/neodata_client.py | 调用 neodata 通用接口 |
| scripts/backtest_metrics.py | 计算收益风险和交易指标 |
| scripts/backtest_visualizer.py | 生成主图 |
| scripts/generate_eda.py | 生成交互式 EDA |
| scripts/fetch_dimension.py | 获取六维诊断原始数据 |
禁止事项
- 不跳过用户确认就把模糊策略写盘或执行。
- 不在 spec 与代码之间引入隐藏默认值。
- 不把当前截面数据回灌历史,也不静默使用代理字段或样本池。
- 不在 skill 目录内保存策略样本、回测产物、缓存、临时探针或
__pycache__。 - 不把 validator 失败、零候选或零交易包装成普通成功回测。
- 不在修复代码后继续交付旧 JSON、PNG 或 HTML。
Scan to join WeChat group