Files

402 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)
23 diverse finalists
MetaTrader 5 Strategy Tester (gold standard)
Python-vs-MT5 gap table ─► registry/ (append-only)
```
- **Tier 1Python 镜像引擎)**:把 EA 的填单 / 出场逻辑 bar-by-bar 重写,跑在
Parquet 历史数据上,2 年回测几秒钟。
- **Tier 2MT5 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. 在根目录建 .envgitignored,凭证不入库)
# 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_*.parquettrailing/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 trialsIS H1 2025<2 分钟验证全链路通
python scripts/optimize.py --smoke
# B. 全量搜索:500 trialsIS = 2025 全年,~1020 分钟
python scripts/optimize.py --trials 500
# 断点续跑(load_if_exists=True),中途 Ctrl+C 不丢
# 产物:studies/optuna/gold_scalper_pro_is2025.db + .log
# C. 用 finalist 参数重跑 Pythonwarmup 修复版),落 JSON
python scripts/reeval_finalist_forward.py
# 产物:studies/finalists/gold_scalper_pro_is2025-2026.json
# D. 生成中文 Optuna 交互式 dashboardHTML
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 ← 知识库 §3bar-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 ← 知识库 §6Optuna 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/ ← 入场过滤 maskregime / 时段 / exhaustion
│ ├── indicators/ ← 纯函数:RSI / ATR / EMA / SMA
│ ├── instruments/ ← per-symbol configtick_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/ ← gluedata → signals → stops → engine → metrics
│ ├── instruments.py ← XAUUSD_REAL config 对象
│ ├── params.py
│ ├── scalper_engine.py ← ScalperEngineengine.run 的 wrapper
│ ├── search_space.py ← 19 个可调参数 + 13 个冻结基线 + 约束
│ ├── set_mappings.py ← Python 参数名 ↔ MQL5 input 名映射
│ ├── signals.py ← build_signalsEMA 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 HTMLcommitted
│ ├── IS-ReportTester-52845377.html
│ ├── OOS-ReportTester-52845377.html
│ └── optuna_dashboard_gold_scalper_pro_is2025.html
├── registry/ ← 已批准 finalistappend-only,真相源)
│ ├── gold_scalper_pro_xauusd_*_f1_t491.md ← finalist #1trial #491
│ ├── gold_scalper_pro_xauusd_*_f2_t297.md ← finalist #2trial #297
│ └── gold_scalper_pro_xauusd_*_f3_t492.md ← finalist #3trial #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 finalistgreedy max-distance
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ scripts/reeval_finalist_forward.py │
│ 用 finalist params 重跑 Pythonwarmup 修复版) │
│ ─► 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 HTML5 主图 + 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 | 内容 | 状态 |
|-------|------|------|
| 01 | 设备 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 + PROMOTEregistry 第一条) | ✅ |
| — | Optuna warmup bug 修复(2026-06-26 重跑) | ✅ |
### 6.2 当前 finalistwarmup 修复后,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 #1trial #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 本体。