# Backtesting + Optuna + MT5 Stack 一个把 **MetaTrader 5 Strategy Tester 当作"金标准"**、用 **Python 镜像引擎**做高速贝叶斯 搜索的个人量化策略研究实验室。当前已对 **GoldScalperPro**(XAUUSD M5 trailing/BE EA) 跑通完整闭环:**假设 → 搜索 → MT5 验证 → 入注册表**。 > 接手者请按本文件的 §2 → §3 → §7 顺序读完即可上手。详细的架构 / 坑 / 扩展指南 > 在 [PROJECT_GUIDE.md](PROJECT_GUIDE.md) 和 `01-08-*.md` 里。 --- ## 1. 项目一句话 ``` Idea ─► Python mirror engine ─► Optuna search (hundreds of trials, seconds each) │ ▼ 2–3 diverse finalists │ ▼ MetaTrader 5 Strategy Tester (gold standard) │ ▼ Python-vs-MT5 gap table ─► registry/ (append-only) ``` - **Tier 1(Python 镜像引擎)**:把 EA 的填单 / 出场逻辑 bar-by-bar 重写,跑在 Parquet 历史数据上,2 年回测几秒钟。 - **Tier 2(MT5 Strategy Tester)**:只对 finalist 跑真实 tester。**MT5 数字才是 live 决策依据**;Python 数字只负责排序。 - **trailing/BE EA 必须 M1 tick-level 模拟**——bar-level 会产生 −40% 到 −50% 的隐藏 gap(详见 [PROJECT_GUIDE.md §1.3](PROJECT_GUIDE.md))。 --- ## 2. 快速开始(接手者从这里读起) ### 2.1 环境要求 - **Windows**(MetaTrader5 Python 包只在 Windows 上装得了) - **Python 3.12+**(开发用 3.12.10) - **MetaTrader 5 终端**已安装并登录一个 demo 账户 - **Git**(commit 历史就是项目文档的一部分) ### 2.2 一次性准备 ```powershell # 1. clone 后建 venv 装依赖 python -m venv .venv .\.venv\Scripts\activate pip install -r requirements.txt # 2. 在根目录建 .env(gitignored,凭证不入库) # MT5_DEMO_LOGIN=52845377 # MT5_DEMO_PASSWORD=... # MT5_DEMO_SERVER=ICMarketsSC-Demo # MT5_TERMINAL_PATH=C:\Program Files\MetaTrader 5 IC Markets Global\terminal64.exe # 3. 打开 MT5 终端、登录 demo、确认 XAUUSD 历史已下载(M5 + M1) python scripts/download_xauusd_history.py # → data/XAUUSD_M5_*.parquet python scripts/download_xauusd_m1.py # → data/XAUUSD_M1_*.parquet(trailing/BE EA 必需) # 4. 查 symbol 规格(确认 tick_value / contract_size / lot_step) python scripts/query_xauusd_spec.py ``` 历史 parquet 在 `.gitignore` 里(`data/` 整个忽略),所以**接手后必须重跑下载脚本**, 否则后续 Optuna / 评估脚本会 `sys.exit("missing M5 data")`。 ### 2.3 5 分钟跑通一遍 ```powershell # A. 烟雾测试:30 trials,IS H1 2025,<2 分钟验证全链路通 python scripts/optimize.py --smoke # B. 全量搜索:500 trials,IS = 2025 全年,~10–20 分钟 python scripts/optimize.py --trials 500 # 断点续跑(load_if_exists=True),中途 Ctrl+C 不丢 # 产物:studies/optuna/gold_scalper_pro_is2025.db + .log # C. 用 finalist 参数重跑 Python(warmup 修复版),落 JSON python scripts/reeval_finalist_forward.py # 产物:studies/finalists/gold_scalper_pro_is2025-2026.json # D. 生成中文 Optuna 交互式 dashboard(HTML) python scripts/build_optuna_dashboard.py # 产物:reports/optuna_dashboard_gold_scalper_pro_is2025.html # E. 生成 ML 特征数据集(trade-level + trial-level) python scripts/build_feature_datasets.py # 产物:studies/features/{trade,trial}_features_*.parquet + .csv # F. 自动生成 registry markdown 条目(每 finalist 一份) python scripts/build_registry_entry.py # 产物:registry/gold_scalper_pro_xauusd_*_f{idx}_t{trial}.md ``` **MT5 验证是手动步骤**(Strategy Tester 跑完导 HTML 到 `reports/`),跑完后用: ```powershell python scripts/build_registry_entry.py ` --mt5-is-html reports\IS-ReportTester-52845377.html ` --mt5-oos-html reports\OOS-ReportTester-52845377.html ``` 补齐 registry 条目里的 MT5 指标和 gap 表。 --- ## 3. 目录结构 ``` backtesting-optuna-mt5-stack/ ├── README.md ← 本文件,项目入口 ├── PROJECT_GUIDE.md ← 项目实例说明(架构+坑+使用+扩展,10 节) ├── CLAUDE.md ← 给 AI 助手的分阶段搭装 playbook ├── requirements.txt ← Python 依赖清单 ├── .gitignore ← data/ + .env 忽略;studies/*.db + reports/*.html 入库 │ ├── 01-stack-and-install.md ← 知识库 §1:技术栈 + 每个系统的安装命令 ├── 02-architecture.md ← 知识库 §2:单向依赖分层 + 数据流 ├── 03-engine-design.md ← 知识库 §3:bar-by-bar engine + intra-bar 4-sub-tick ├── 04-isolation-rules.md ← 知识库 §4:8 条隔离铁律 ├── 05-config-and-inputs.md ← 知识库 §5:4 个声明输入源 ├── 06-optimization-and-robustness.md ← 知识库 §6:Optuna objective + 反过拟合 ├── 07-mt5-bridge.md ← 知识库 §7:编译 EA / .set / .ini / 解析报告 ├── 08-workflow-cycle.md ← 知识库 §8:8 步可重复循环 │ ├── GoldScalperPro.mq5 ← EA 源码(MQL5) ├── GoldScalperPro.ex5 ← EA 编译产物 │ ├── shared/ ← 9 个单向依赖层(低调高、高不知低) │ ├── core/ ← engine(冻结)+ metrics │ ├── data/ ← Parquet loaders + MT5 HTML parser │ ├── gates/ ← 入场过滤 mask(regime / 时段 / exhaustion) │ ├── indicators/ ← 纯函数:RSI / ATR / EMA / SMA │ ├── instruments/ ← per-symbol config(tick_value / spread / swap) │ ├── mt5_pipeline/ ← .set / .ini / compile / runner / compare │ ├── optimizer/ ← objective / search_space / diverse top-N selector │ ├── robustness/ ← 反过拟合分析(只读 over results) │ ├── wizard/ ← 运行时 Q&A → wizard-answers.yaml │ └── config.py ← .env 加载(含 plain fallback) │ ├── strategies/ │ └── gold_scalper_pro/ ← glue:data → signals → stops → engine → metrics │ ├── instruments.py ← XAUUSD_REAL config 对象 │ ├── params.py │ ├── scalper_engine.py ← ScalperEngine(engine.run 的 wrapper) │ ├── search_space.py ← 19 个可调参数 + 13 个冻结基线 + 约束 │ ├── set_mappings.py ← Python 参数名 ↔ MQL5 input 名映射 │ ├── signals.py ← build_signals(EMA cross + RSI + ATR) │ └── wizard_questions.py │ ├── scripts/ ← 一次性可执行脚本(按用途分组见 §5) │ ├── optimize.py ← Phase 6 主入口(smoke / full) │ ├── reeval_finalist_forward.py ← finalist 参数 + warmup 修复版重跑 │ ├── build_optuna_dashboard.py ← 中文 plotly 交互式 HTML │ ├── build_feature_datasets.py ← ML 特征 parquet │ ├── build_registry_entry.py ← 自动生成 registry markdown(命令驱动,禁手写) │ ├── walk_forward.py │ ├── prepare_mt5_verify.py ← 生成 .set / .ini 给 MT5 tester │ ├── verify_mt5.py │ ├── compare_finalist.py ← Python vs MT5 差距表 │ ├── diag_*.py ← 9 个诊断脚本(ATR / 时区 / 引擎 trace / 信号) │ └── download_xauusd_*.py ← 历史 M5 / M1 数据下载 │ ├── studies/ ← Optuna 研究产物(committed) │ ├── optuna/ ← SQLite DB + 运行日志(resumable) │ │ ├── gold_scalper_pro_is2025.db │ │ ├── gold_scalper_pro_is2025.db.before-warmup-fix.bak ← 旧 study 备份 │ │ └── gold_scalper_pro_is2025.log │ ├── finalists/ ← 3 个 finalist 的 IS/OOS 指标 JSON │ │ └── gold_scalper_pro_is2025-2026.json │ └── features/ ← ML 特征数据集(parquet + csv 副本) │ ├── trade_features_*.parquet ← trade-level 35 列 │ └── trial_features_*.parquet ← trial-level 聚合 │ ├── reports/ ← MT5 + dashboard HTML(committed) │ ├── IS-ReportTester-52845377.html │ ├── OOS-ReportTester-52845377.html │ └── optuna_dashboard_gold_scalper_pro_is2025.html │ ├── registry/ ← 已批准 finalist(append-only,真相源) │ ├── gold_scalper_pro_xauusd_*_f1_t491.md ← finalist #1(trial #491) │ ├── gold_scalper_pro_xauusd_*_f2_t297.md ← finalist #2(trial #297) │ └── gold_scalper_pro_xauusd_*_f3_t492.md ← finalist #3(trial #492) │ └── data/ ← gitignored,历史 parquet(接手后需重下) ├── XAUUSD_M5_2024-06-26_2026-06-26.parquet └── XAUUSD_M1_2024-06-26_2026-06-26.parquet ``` --- ## 4. 核心数据流(一个 iteration 的闭环) ``` ┌──────────────────────────────────────────────────────┐ │ scripts/optimize.py │ │ load_m5() ─► slice_window(IS_START, IS_END) │ │ load_m1() ─► slice_window(IS_START, IS_END) │ │ full_m5 ───────────────────┐ (warmup, signals_full_bars)│ │ ▼ │ │ ObjectiveConfig(bars_is, m1_is, signals_full_bars=full_m5)│ │ │ │ │ ▼ │ │ build_objective(cfg) ─► trial loop │ │ │ │ │ ▼ │ │ build_signals(full_m5) ─► slice to IS │ │ engine_kwargs_from_params(params) │ │ ScalperEngine.run(bars, signals, sl, tp, m1_bars) │ │ │ │ │ ▼ │ │ compute_metrics + Constraints gate │ │ study.optimize() ─► trials 0..499 │ └──────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────┐ │ select_diverse_topn(study, n=3) │ │ └─► 3 个 diverse finalist(greedy max-distance) │ └──────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────┐ │ scripts/reeval_finalist_forward.py │ │ 用 finalist params 重跑 Python(warmup 修复版) │ │ ─► studies/finalists/gold_scalper_pro_is2025-2026.json│ └──────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────┐ │ MT5 Strategy Tester(手动) │ │ prepare_mt5_verify.py 生成 .set + .ini │ │ Forward mode: IS=2025, OOS=2026 H1 │ │ 导 HTML 报告到 reports/ │ └──────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────┐ │ scripts/build_registry_entry.py │ │ finalist JSON + Optuna study + MT5 HTML │ │ ─► registry/gold_scalper_pro_xauusd_*_f{idx}.md │ │ (9 节自文档化 markdown,含差距表) │ └──────────────────────────────────────────────────────┘ ``` --- ## 5. 常用脚本速查(按场景) | 场景 | 脚本 | 命令 | |------|------|------| | **跑 Optuna 搜索** | `optimize.py` | `--smoke`(30 trials,<2 分钟) / `--trials 500`(全量) | | **重跑 finalist** | `reeval_finalist_forward.py` | 用 finalist 参数在 IS+OOS 重跑 Python,落 JSON | | **走前验证(forward)** | `walk_forward.py` | finalist 在 OOS 窗口独立验证 | | **生成 dashboard** | `build_optuna_dashboard.py` | 中文 plotly HTML(5 主图 + 18 单参数 + 15 等高线) | | **生成 ML 特征** | `build_feature_datasets.py` | trade-level 35 列 + trial-level parquet | | **生成 registry** | `build_registry_entry.py` | 命令驱动生成 finalist 条目(**禁手写**) | | **准备 MT5 验证** | `prepare_mt5_verify.py` | 生成 `.set` + `.ini`,复制到 MT5 tester profiles | | **比对 finalist** | `compare_finalist.py` | Python vs MT5 差距表 | | **诊断(9 个)** | `diag_*.py` | ATR / 时区 / 引擎 trace / 信号触发 / 仓位 mismatch 等 | | **查 study** | `inspect_study.py` | 打印 trials / best params | | **查 MT5 报告** | `inspect_report.py` / `diag_mt5_summary.py` / `diag_mt5_trades.py` | 解析 HTML | | **下历史数据** | `download_xauusd_history.py`(M5)/ `download_xauusd_m1.py`(M1) | 必须 MT5 终端已登录 | | **查 symbol 规格** | `query_xauusd_spec.py` | tick_value / contract_size / lot_step | --- ## 6. 当前项目状态 ### 6.1 已完成的 Phase | Phase | 内容 | 状态 | |-------|------|------| | 0–1 | 设备 profile + Python 栈安装 | ✅ | | 2 | 仓库骨架(shared/ 9 子包 + strategies/) | ✅ | | 3 | MT5 连接(分步 initialize+login)+ EA 资产导入 | ✅ | | 4 | ScalperEngine + M1 tick-level 模拟路径 | ✅ | | 5 | XAUUSD_REAL instrument + GoldScalperPro search space | ✅ | | 6 | Optuna 500-trial 搜索 + 3 diverse finalist | ✅ | | 7 | MT5 forward mode IS/OOS 验证(旧 finalist)+ 差距根因 | ✅ | | 8 | APPROVAL + PROMOTE(registry 第一条) | ✅ | | — | Optuna warmup bug 修复(2026-06-26 重跑) | ✅ | ### 6.2 当前 finalist(warmup 修复后,2026-06-27 重跑) 来自 [studies/finalists/gold_scalper_pro_is2025-2026.json](studies/finalists/gold_scalper_pro_is2025-2026.json): | # | trial | score | IS net | OOS net | registry | |---|-------|------:|-------:|--------:|----------| | 1 | #491 | 186,075 | $263,527 | $10,632 | [f1_t491.md](registry/gold_scalper_pro_xauusd_2025-01-01_2026-06-26_f1_t491.md) | | 2 | #297 | 68,772 | $94,197 | $11,153 | [f2_t297.md](registry/gold_scalper_pro_xauusd_2025-01-01_2026-06-26_f2_t297.md) | | 3 | #492 | 129,575 | $165,740 | $6,969 | [f3_t492.md](registry/gold_scalper_pro_xauusd_2025-01-01_2026-06-26_f3_t492.md) | > 这三个 finalist **尚未通过 MT5 验证**——registry 条目标记为 > "已记录(无 MT5 验证 — 待 Phase 7 MT5 验证)"。需要先在 MT5 Strategy Tester > 跑出 IS+OOS HTML 报告,再用 `build_registry_entry.py --mt5-is-html ... --mt5-oos-html ...` > 补齐 MT5 部分和差距表。 --- ## 7. 接手者必读(续做指南) ### 7.1 三条铁律(违反任一条等于把项目搞烂) 1. **trailing/BE EA 必须传 `m1_bars=` 给 `engine.run()`**——否则 net profit 会有 −40% 到 −50% 的隐藏 gap。这不是噪声,是 bug。详见 [03-engine-design.md §7](03-engine-design.md)。 2. **`ObjectiveConfig` 必须传 `signals_full_bars=full_m5`**——否则 EMA/RSI/ATR 在 IS_START 才开始预热,首笔交易会比 MT5 晚 14 小时,整个 finalist 集合都 是错的。详见 [scripts/optimize.py](scripts/optimize.py) 的 `make_objective_config()` docstring。 3. **Engine 一旦验证就冻结,要试新想法就 fork**——直接改 validated engine 可能让所有已信任的数字都失效。Fork + default-OFF 实验 hook + 证明 fork-with-change-off == 原版 1:1 + 再 A/B。详见 [04-isolation-rules.md Rule 2](04-isolation-rules.md)。 ### 7.2 已知坑(接手时大概率还会遇到) | 坑 | 症状 | 修复 | |----|------|------| | MT5 连接 IPC 超时(-10005) | `mt5.initialize()` 直接传所有参数 | **分步**:先 `mt5.initialize(path=...)`,再 `mt5.login(login, password, server)` | | 终端路径反斜杠 | IPC 超时 / 连不上 | 路径必须用**正斜杠** `/`,不是 `\` | | MT5 进程残留 | 新连接失败 | 任务管理器清掉所有 `terminal64.exe` 进程后重试 | | `.set` 文件加载失败 | MT5 tester 找不到参数 | 文件必须以 **BOM 开头**(`write_set_file` 已处理) | | Plotly 图表看不见 | HTML 渲染 0 高度 | `fig.update_layout(height=<像素>)` + CDN + 单列 flex(见 `build_optuna_dashboard.py`) | | 参数切片图标签重叠 | 5400px 宽复合图挤一起 | 拆成每参数独立 900×420 小图(`render_slice_grid`) | | Optuna warmup bug | IS 起点附近 14h 没信号 | `signals_full_bars` 字段,详见 §7.1 第 2 条 | ### 7.3 急需做的下一步(按优先级) 1. **跑 MT5 验证新 finalist #1(trial #491)** —— 用 `scripts/prepare_mt5_verify.py` 生成 `.set` + `.ini`,在 MT5 Strategy Tester forward mode 跑 IS=2025 + OOS=2026 H1,导 HTML 到 `reports/`,然后用 `build_registry_entry.py --mt5-is-html ... --mt5-oos-html ...` 补齐 registry。 2. **重新诊断 ATR 数据差异** —— 旧的 `diag_atr_check.py` / `diag_mt5_trades.py` 是针对 trial #324 的,新 finalist 需重跑。 3. **实现自动化 MT5 pipeline** —— `shared/mt5_pipeline/runner.py` 现在只是骨架, 把"打开 tester → 等跑完 → 拷 HTML"自动化掉。 4. **补全 robustness 层** —— `shared/robustness/layers.py` 只有 `stability_region`,缺 doc 06 §5 描述的其它层(parameter stability、 monte-carlo perturbation、walk-forward aggregate)。 5. **MT5 数据对齐** —— Python parquet M5 OHLC 与 MT5 tester 内部 history 在 IS 起点附近有微差,复利下被指数放大。需要决定是接受差距(旧 finalist #324 已接受)还是修对齐。 ### 7.4 不要做的事 - **不要手写 registry 条目**——必须用 `scripts/build_registry_entry.py` 生成。 脚本会从 finalist JSON + Optuna study + MT5 HTML 三源自动提取,包含 9 节内容(标识 / 参数表含百分位 / 三个 finalist 对比 / Python 指标 / MT5 指标 / 差距分析 / 搜索统计 / 复现命令 / 已知限制)。 - **不要编辑已批准的 registry 条目**——append-only。新发现是新条目, 旧条目就算被超越也保留(负面结果也是数据)。 - **不要在 `engine.run()` 里加策略逻辑**——engine 只决定"价格是否触到 止损",**从不决定止损放在哪**。换策略改 caller 和数组,engine 不动。 - **不要在 `mt5.initialize()` 里塞所有参数**——分步连接,详见 §7.2 第 1 条。 - **不要给单个 strategy import 另一个 strategy**——copy,不要 import。 iteration 各自拥有 snapshot,几个月前的 iteration 仍要能跑。 ### 7.5 改动约定 - 改了 engine / objective / search space → 跑 `--smoke` 先验证全链路通 - 改了 finalist 挑选逻辑 → 重跑 `reeval_finalist_forward.py` + 重生 dashboard - 改了 MT5 报告解析 → 用 `diag_mt5_summary.py` 对比新旧解析结果 - commit 时分小步、写清楚 why(不只是 what),git log 就是项目文档的一部分 --- ## 8. 文档地图 | 你想做的事 | 读这个 | |------------|--------| | 跑一遍流程 | 本文件 §2 + §5 | | 理解架构为什么这样搭 | [PROJECT_GUIDE.md §1](PROJECT_GUIDE.md) + [02-architecture.md](02-architecture.md) | | 看具体踩过的坑 | [PROJECT_GUIDE.md §5](PROJECT_GUIDE.md) + 本文件 §7.2 | | 写新策略 | [02-architecture.md §4](02-architecture.md) + [08-workflow-cycle.md](08-workflow-cycle.md) | | 改 engine | [03-engine-design.md](03-engine-design.md) + [04-isolation-rules.md Rule 2](04-isolation-rules.md) | | 改 Optuna objective | [06-optimization-and-robustness.md](06-optimization-and-robustness.md) + [shared/optimizer/objective.py](shared/optimizer/objective.py) | | 接 MT5 | [07-mt5-bridge.md](07-mt5-bridge.md) + [shared/mt5_pipeline/](shared/mt5_pipeline/) | | 看 finalist 参数 | [studies/finalists/gold_scalper_pro_is2025-2026.json](studies/finalists/gold_scalper_pro_is2025-2026.json) + [registry/](registry/) | | 看 Optuna trials | `python scripts/inspect_study.py` 或直接打开 `studies/optuna/gold_scalper_pro_is2025.db` | --- ## 9. License & Disclaimer 个人研究项目,不构成投资建议。EA 源码 `GoldScalperPro.mq5` 属于其原作者; 本项目仅用于参数优化方法论研究,不重新分发 EA 本体。