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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user