Files
mql5-skills/AGENTS.md
T
ZhijuCen 100d62bf2a feat(parse_optimizer_report): add --sort priority for outliers subcommand
Introduce a configurable sort priority for Set A and Set B in the
`outliers` subcommand, replacing the single-criterion "Result desc".

New CLI flag:
  --sort ABBR_LIST   comma-separated metric abbreviations
                     (default: R,EP,PF,RF,SR,P,DD,C,T)

Abbreviations: R=Result, P=Profit, EP=Expected Payoff, PF=Profit Factor,
RF=Recovery Factor, SR=Sharpe Ratio, C=Custom, DD=Equity DD %, T=Trades.

Equity DD % sorts ascending (lower is better); all others descending.
Unmentioned abbreviations are appended at default order.
2026-07-07 11:37:07 +08:00

15 KiB
Raw Blame History

AGENTS.md — mql5-skills Project

Overview

MQL5 Agent Skills project. Creates and publishes Agent Skills conforming to the AgentSkills.io Specification.

  • Project version: 0.1.0 (pinned during initial phase, not incremented per change)
  • SKILL.md version: 0.1 (per-document, pinned during initial phase)
  • Language: Python 3.14, uv-managed
  • Dependencies: beautifulsoup4, requests

Directory Structure

mql5-skills/
├── AGENTS.md              # This file — project conventions
├── LICENSE                # MIT license
├── README.md              # Public readme
├── pyproject.toml         # uv project config
├── .python-version        # Pins Python 3.14
├── uv.lock                # Locked dependencies
├── sitemaps/              # Source sitemaps from mql5.com
│   ├── sitemap_book_en.xml   # 581 URLs → programming book
│   └── sitemap_docs_en.xml   # 4135 URLs → API reference docs
├── html_cache/            # Downloaded HTML (lossless, gitignored)
│   ├── book/              #   581 .html files
│   └── docs/              #   4135 .html files
├── scripts/               # Extraction scripts (Python)
│   └── extract.py         # Main extraction: XML → HTML → Markdown
├── resources/             # Static resources
│   └── random-user-agents.csv
├── skills/
│   └── mql5/              # The MQL5 development skill
│       ├── SKILL.md       # Skill definition (agentskills.io spec)
│       ├── scripts/
│       │   ├── mql5_helper.py            # Compile/deploy/status via Wine
│       │   ├── parse_tester_report.py    # Backtest report parser + analysis
│       │   ├── parse_optimizer_report.py # Optimization report parser + analysis
│       │   └── verify_sl_tp_formulas.py  # SL/TP risk formula verification
│       └── references/
│           ├── book/      # Programming book markdown (from sitemap_book_en.xml)
│           │   ├── 0000-book.md
│           │   ├── 00-intro/
│           │   │   ├── 0001-intro.md
│           │   │   └── pics/
│           │   └── ...
│           ├── docs/      # API reference markdown (from sitemap_docs_en.xml)
│           │   ├── 0000-docs.md
│           │   ├── 01-basis/
│           │   │   ├── 0001-basis.md
│           │   │   └── pics/
│           │   └── ...
│           └── symbol-spec/  # Broker symbol specifications (CSV)
│               ├── specs-XAUUSD.csv
│               └── specs-USDJPY.csv
├── jobs/                  # Backtest job folders
│   ├── 250013-job.md      # Job specification
│   └── ReportTester-250013/
│       ├── ReportTester-600xxxxx.html   # MT5 Strategy Tester HTML report
│       └── ReportTester-600xxxxx*.png   # Screenshots (equity, holding, MFE/MAE)
└── docs-dev/              # Development documentation
    ├── extraction.md      # Extraction workflow and script design
    ├── naming.md          # Folder/file naming conventions
    ├── skill-design.md    # SKILL.md content plan
    └── symbol-spec.md     # Symbol spec workflow

SKILL.md Convention (skills/mql5/)

Per agentskills.io spec:

  • Frontmatter: name (required, max 64 chars, lowercase+hyphens), description (required, max 1024 chars)
  • Body: Markdown instructions for the agent
  • Optional dirs: scripts/, references/, assets/
  • Focus areas: positions, orders, indicators, ticks, bars

Scripts

parse_tester_report.py

Parses MT5 Strategy Tester HTML reports. Supports three output modes:

# Text report (default)
python skills/mql5/scripts/parse_tester_report.py <report.html>

# JSON dump (raw parsed data)
python skills/mql5/scripts/parse_tester_report.py <report.html> --json

# JSON with trade analysis (--analyze includes idle_time, monthly breakdown, etc.)
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 — XML-tagged Excel workbook, also openable in LibreOffice Calc). Companion to parse_tester_report.py; same three output modes:

python skills/mql5/scripts/parse_optimizer_report.py <ReportOptimizer-*.xml>
python skills/mql5/scripts/parse_optimizer_report.py <report.xml> --json
python skills/mql5/scripts/parse_optimizer_report.py <report.xml> --analyze
python skills/mql5/scripts/parse_optimizer_report.py <report.xml> outliers [--sigma K] [--top-outliers N] [--top-normal M] [--sort ABBR_LIST] [--json]

Reads <DocumentProperties> for the strategy environment card (EA / Symbol / Period / Date range from Title, plus Deposit / Leverage / Server / MT5 build / run timestamp) and the single "Tester Optimizator Results" worksheet for one row per parameter pass. --analyze adds:

  • Orthogonality: actual pass count vs expected cartesian product
  • Parameter effect: which Inp* parameters actually move the result (vs. dead parameters that should be removed from optimization)
  • Dead boolean parameters: bit-for-bit identical true/false groups on key metrics — cleanest signal of a parameter not wired into the EA
  • Duplicate metric vectors: high count (>30%) usually points to a dead parameter
  • Best passes by Profit / Profit Factor / Recovery Factor / Custom
  • Trade count distribution with daily rate and correlations vs profit / drawdown (overtrading detection)

Use alongside parse_tester_report.py for the same EA: the latter explains why a specific pass performs, the former explains which pass performs and which parameters are even worth tuning.

outliers subcommand

Per-pass z-score scan over the 8 performance metrics (Result, Profit, Expected Payoff, Profit Factor, Recovery Factor, Sharpe Ratio, Custom, Equity DD %). Two disjoint sets:

  • Set A — passes with AT LEAST ONE performance metric crossing ±σ in the favourable direction (higher-is-better metrics: z >= +σ; Equity DD % uses z <= -σ because low DD is good). Sorted by the configured priority chain (default: R↓, EP↓, PF↓, RF↓, SR↓, …), top --top-outliers (default 10) shown.
  • Set B — passes with NO performance-metric outlier. Sorted by the same priority chain, top --top-normal (default 5) shown.

Both sets EXCLUDE passes whose Trades count is itself a low-side outlier (z <= -σ) — those have too few trades to trust, and the excluded list is shown separately.

Output: header card (includes Sort priority: … line) + per-metric reference table (mean, std, ±σ threshold) + Set A records (each split into Metrics group and Params group, with the outlier σ values annotated) + Set B records + the Excluded list.

# Default (σ=2, top 10 outliers, top 5 normal)
python skills/mql5/scripts/parse_optimizer_report.py report.xml outliers

# Custom sort priority (abbreviations: R P EP PF RF SR C DD T)
python skills/mql5/scripts/parse_optimizer_report.py report.xml outliers --sort EP,RF,R,P

# Single priority metric; rest in default order
python skills/mql5/scripts/parse_optimizer_report.py report.xml outliers --sort DD

# Tighter threshold + custom counts
python skills/mql5/scripts/parse_optimizer_report.py report.xml outliers --sigma 2.5 --top-outliers 5 --top-normal 3

# JSON output for further processing
python skills/mql5/scripts/parse_optimizer_report.py report.xml outliers --json

Sort abbreviations:

Code Full metric Direction
R Result ↓ (desc)
P Profit ↓ (desc)
EP Expected Payoff ↓ (desc)
PF Profit Factor ↓ (desc)
RF Recovery Factor ↓ (desc)
SR Sharpe Ratio ↓ (desc)
C Custom ↓ (desc)
DD Equity DD % ↑ (asc)
T Trades ↓ (desc)

Default order: R↓, EP↓, PF↓, RF↓, SR↓, P↓, DD↑, C↓, T↓. DD↑ sorts ascending (lower drawdown is better); everything else descending (higher values rank first). Supply --sort ABBR_LIST as a comma-separated list to reorder; unmentioned metrics append at the end in their default positional order.

EA-agnostic by design: the script does not hardcode any input parameter name. Type inference reads <Data ss:Type="String"> from the SpreadsheetML header (boolean Inp* rendered as "true"/"false" stays a string; everything else is numeric). Input parameters are detected by column position — every column after Trades is an optimization input, regardless of whether its name starts with Inp. So an EA naming its params StopLoss / TakeProfit / UseNewsFilter parses correctly without script changes.

mql5_helper.py

MT5 development helper for compile/deploy/status via Wine:

python skills/mql5/scripts/mql5_helper.py compile FILE.mq5
python skills/mql5/scripts/mql5_helper.py check FILE.mq5     # syntax only (/s flag)
python skills/mql5/scripts/mql5_helper.py deploy FILE.mq5
python skills/mql5/scripts/mql5_helper.py status
python skills/mql5/scripts/mql5_helper.py list

MT5 paths resolved in order: $MQL5_DIR env → cwd walk-up → Program Files scan → Wine fallback.

Extraction Workflow

Two-phase pipeline (network only needed for Phase 1):

Phase 1: download  — sitemap → fetch HTML → html_cache/
Phase 2: convert   — html_cache/ → parse HTML → download images → .md files
  • Script: scripts/extract.py with download, convert, debug subcommands
  • HTML cache: html_cache/book/, html_cache/docs/ (lossless, gitignored)
  • Output: skills/mql5/references/book/, skills/mql5/references/docs/
  • Each sitemap URL → one .md file + corresponding .html in cache
  • Images → pics/ subfolder within chapter dir
  • Debug target: TesterStatistics page (mixed content: tables, images, code, console output)

Naming Convention

See docs-dev/naming.md for full specification. Key rules:

  • 4-digit sequential prefix per file within a chapter folder
  • 2-digit prefix on chapter folder names
  • Chapter folder names derived from URL path segments
  • Max one level of subfolder under book/ or docs/
  • Each chapter folder contains a pics/ subfolder

Ad-hoc Verification

No formal test suite. Scripts are verified via temporary scripts under /tmp with hermes-verify- filename prefix. Pattern:

  1. Write a focused verification script to /tmp/hermes-verify-<topic>.py
  2. Import the changed functions, exercise them with known inputs
  3. Run via uv run python /tmp/hermes-verify-<topic>.py (requires project venv for deps)
  4. Clean up the temp file after passing

Example (from parse_tester_report.py changes):

# Create /tmp/hermes-verify-parse-tester.py with test cases
# Run:
uv run python /tmp/hermes-verify-parse-tester.py
# Clean up:
rm /tmp/hermes-verify-parse-tester.py

Git Workflow

  • Conventional commits
  • Do not commit .venv, pycache
  • Tag releases per semver