回测基本一致
This commit is contained in:
+34
-4
@@ -193,6 +193,31 @@ For a brand-new symbol you must first **let MT5 cache its history** (open a char
|
||||
`Tools → Options → Charts → Max bars: Unlimited`, scroll back) so the tester and the export have enough
|
||||
bars. Confirm the exact broker symbol code (it varies: indices and metals especially) before exporting.
|
||||
|
||||
### 6a. Pull M1 even if your signal timeframe is higher — and pass it to the engine
|
||||
|
||||
A common mistake: download only the signal timeframe (e.g. M5/M15/H1) and assume that's enough. It is
|
||||
not, **if your EA moves its SL during a trade** (break-even, trailing, basket trailing). The bar-level
|
||||
engine produces a −40% to −50% net gap on those EAs (doc 03 §7 measured failure mode) because the BE
|
||||
update and the SL trigger fall on the same bar's opposite extreme. The fix is tick-level exit
|
||||
simulation, which needs the **M1 bars covering the same window as your signal bars**:
|
||||
|
||||
```python
|
||||
# Download BOTH timeframes. Same symbol, same window, exclusive end (match MT5 tester).
|
||||
m1_bars = load_bars(DATA / f"{SYMBOL}_M1_{start}_{end}.parquet")
|
||||
m5_bars = load_bars(DATA / f"{SYMBOL}_M5_{start}_{end}.parquet") # the signal timeframe
|
||||
|
||||
# Slice both to the exact same window (exclusive end matches MT5 tester's ToDate).
|
||||
m1_bars = m1_bars[(m1_bars["timestamp"] >= start) & (m1_bars["timestamp"] < end)]
|
||||
m5_bars = m5_bars[(m5_bars["timestamp"] >= start) & (m5_bars["timestamp"] < end)]
|
||||
|
||||
# Pass m1_bars to the engine — it switches to tick-level exit simulation automatically.
|
||||
result = engine.run(m5_bars, signals_long, signals_short, sl, tp,
|
||||
instrument, sizing, deposit, m1_bars=m1_bars)
|
||||
```
|
||||
|
||||
If your EA does **not** move its SL intra-trade (clean market entries with a fixed SL/TP), `m1_bars`
|
||||
can be omitted — the bar-level engine is exact for that class and faster.
|
||||
|
||||
---
|
||||
|
||||
## 7. Parsing the report
|
||||
@@ -223,10 +248,15 @@ For every finalist, write an `auto-verification.md` that puts the two tiers side
|
||||
| Equity DD max | … | … | …% |
|
||||
| Total trades | … | … | …% |
|
||||
|
||||
Then judge the delta **against the expected fidelity gap** (doc 03 §7): a clean-directional setup
|
||||
should show only a modest negative gap (MT5 a little below Python); a trailing/grid setup in volatile
|
||||
history can gap much wider, and that's *expected*, not a bug. The decision rule: if the **MT5** number still clears your bar after the gap,
|
||||
the finalist is real; if the edge only existed in the optimistic Python figure, discard it.
|
||||
Then judge the delta **against the expected fidelity gap** (doc 03 §7 + §8 target-gate table):
|
||||
a clean-directional setup (bar-level engine, SL not moved intra-trade) should show ≤ ~2% net gap;
|
||||
a trailing/BE setup on the **bar-level** engine is not trustworthy at all (−40% to −50% gap,
|
||||
doc 03 §7 measured failure mode) — **before** judging the gap "expected", confirm the Python run
|
||||
used M1 tick-level exit simulation (`m1_bars=` passed to the engine), which brings the gap into the
|
||||
≤ ~10% range. Only a gap inside the §8 target gate counts as "expected"; a wider gap is a
|
||||
missing-M1 / wrong-engine-mode bug, not fidelity noise. The decision rule: if the **MT5** number
|
||||
still clears your bar after the gap, the finalist is real; if the edge only existed in the
|
||||
optimistic Python figure, discard it.
|
||||
|
||||
> A useful warm-up calibration: pick one known preset and run the full Python-vs-MT5 comparison on it
|
||||
> first. It tells you *your* stack's actual gap for *your* EA, so later finalists are judged against a
|
||||
|
||||
Reference in New Issue
Block a user