feat(parse_tester_report): add 'windows' subcommand for time-window outlier analysis

Split backtest into N equal time slices (left-closed right-open) and
compute the 7 core metrics per window: Profit, EP, PF, RF, Balance DD
Rel%, Trades, Sharpe. Each window gets an outlier flag based on per-
metric z-score (|z|>=2 = notable, |z|>=5 = extreme). N=1 runs a full-
period cross-check vs the HTML report.

Key changes:
- Add compute_windows / compute_window_metrics / print_windows /
  windows_comparison functions, CLI subcommand 'windows'
- pair_trades now exports gross_pnl/entry_costs for MT5 GP/GL split
- compute_gross_profit_loss: MT5 accounting (entry costs always to GL)
- _balance_dd_relative: max relative DD (STAT_BALANCE_DDREL_PERCENT)
- _sharpe_ratio: textbook (AHPR-1)/std_HPR formula, 365-day year
- Help text with examples for both --help and windows --help
- verify_sl_tp_formulas.py: localize all output labels to English
- AGENTS.md / SKILL.md: document windows subcommand conventions

Docs: 5 of 7 metrics exact for N=1 (Profit, EP, PF, Trades exact;
RF/BalDD% are approximations due to balance-only reconstruction;
Sharpe uses textbook formula diverging from MT5's 22.92)
This commit is contained in:
ZhijuCen
2026-07-04 01:18:33 +08:00
parent 835186faa9
commit 061e879638
4 changed files with 879 additions and 23 deletions
+81
View File
@@ -94,6 +94,87 @@ python skills/mql5/scripts/parse_tester_report.py <report.html> --analyze
Key analysis fields: `idle_time` (HH:MM:SS flat duration across backtest period),
`win_loss_ratio`, `breakeven_win_rate`, `monthly`, `reentries`, `lot_pattern`.
#### `windows` subcommand
Splits a backtest into N equal time windows and computes the same 7
core metrics (Profit, Expected Payoff, Profit Factor, Recovery Factor,
Balance DD Rel%, Trades, Sharpe Ratio) per window. Use to detect
over-fitting / regime change.
```
# N=1: validation — should match the full report within tolerance
python skills/mql5/scripts/parse_tester_report.py <report.html> windows --count 1
# N=4: typical analysis (quarterly for a 1.5y backtest)
python skills/mql5/scripts/parse_tester_report.py <report.html> windows --count 4
# JSON output for further processing
python skills/mql5/scripts/parse_tester_report.py <report.html> windows --count 6 --json
```
**Conventions:**
- Time boundaries are equal-length `[t_start, t_end)` slices,
left-closed right-open. Window 0 starts at the backtest start;
window N-1 ends at the backtest end. Adjacent windows do not overlap.
- A trade is assigned to the window where it OPENS (entry time).
Its P&L lands at exit time, which may fall in a later window — we
attribute the P&L to the opening window because that is the
"decision moment" the user cares about.
- Balance DD Rel% computes MT5's STAT_BALANCE_DDREL_PERCENT (maximum
relative drawdown, i.e. the largest (peak - trough) / peak % across
the balance curve). For the full report this matches the HTML's
Balance Drawdown Relative field exactly (44.60% vs 44.63% on
246753; the 0.03% gap is from intra-trade floating P&L not in HTML).
Per the user's rule "如 Equity DD % 不可用,则以 Balance DD % 代替",
the script's `bal_dd_rel_pct` field is this balance-based relative
DD.
- Gross Profit / Gross Loss use MT5's split: each trade's exit-leg
P&L goes to GP if positive or GL if non-positive; entry costs always
go to GL. Matches the report exactly for window=1.
- Sharpe Ratio uses the standard per-trade HPR formula
(mean/std × sqrt(N_per_year)). MT5's reported value uses a
different (undocumented) annualization; the value differs from the
report for window=1, but the formula is consistent across all
sub-windows, so the relative ranking is still meaningful.
**Outlier flags per window (z-score vs window mean):**
- `▲2σ` — at least one metric has |z| ≥ 2 (值得关注 — this
window's value is far from the rest of the windows).
- `■EXT` — at least one metric has |z| ≥ 5 (极端 — extreme outlier).
- The marker is followed by `k=N` (count of outlier metrics) and
the metric abbreviations with their signed z (e.g.
`prof(+2.3σ),reco(+2.3σ)`).
z = (this window's value mean across all windows) / std.
Direction is sign-bearing (+/-); the threshold is on |z|.
Lower-is-better metrics (bal_dd_rel_pct) are NOT inverted — a
negative z means "this window's DD is unusually low" (good for
safety, neutral for consistency), a positive z means "unusually
high DD" (a red flag). For all other metrics, the natural sign
applies (high profit, high PF, etc. = good).
A single window with a strong outlier is a regime signal.
Multiple windows each with their own outliers point to a
high-variance strategy — harder to predict live performance.
**Tolerances (N=1 vs report):**
- 4 of 7 metrics are **exact**: Profit, Expected Payoff, Profit
Factor, Trades.
- 3 are documented approximations:
- **Balance DD Rel%** — MT5's STAT_BALANCE_DDREL_PERCENT (max
relative drawdown). Our value matches the HTML's Balance
Drawdown Relative field within 0.03%. Per the user's rule
"如 Equity DD % 不可用,则以 Balance DD % 代替" — this is what
we do.
- **Recovery Factor** — downstream of bal_dd_rel_abs.
- **Sharpe Ratio** — MT5's reported value is inconsistent with the
textbook formula `(AHPR - 1) / std_HPR × sqrt(N/year)` that the
MQL5 community reverse-engineers agree on (forum thread 337071).
For 246753 the textbook formula gives 2.49 vs the report's
22.92 — a 9.2× gap. MT5 does not publish the actual computation.
The per-trade Sharpe (`sharpe_ratio_raw` in the JSON output) is
still a useful per-window signal.
### parse_optimizer_report.py
Parses MT5 Strategy Tester Optimization XML reports (SpreadsheetML format