Files

86 KiB
Raw Permalink Blame History

MCP Tool Specification

Full input/output schemas for MT5-Quant tools.

Documentation Status: All 90 tools are documented.


run_backtest

Run a complete backtest pipeline: compile → clean cache → backtest → extract → analyze.

When to call: Any time you need fresh backtest results. Always runs the full pipeline unless skip_* flags are set.

Input schema

{
  // Required
  expert: string;          // EA name without path or extension. e.g. "MyEA_v1.2"

  // Date range — use either preset OR from+to
  preset?: "last_month" | "last_3months" | "ytd" | "last_year";
  from?: string;           // "YYYY-MM-DD"
  to?: string;             // "YYYY-MM-DD"

  // Optional overrides
  symbol?: string;         // Default from config. e.g. "XAUUSD"
  timeframe?: "M1" | "M5" | "M15" | "M30" | "H1" | "H4" | "D1"; // Default: M5
  deposit?: number;        // Default from config. e.g. 10000
  currency?: string;       // Default: "USD"
  model?: 0 | 1 | 2;      // 0=every tick (default), 1=1min OHLC, 2=open price
  set_file?: string;       // Path to .set file. If omitted, uses EA defaults.
  leverage?: number;       // Default: 500

  // Pipeline flags
  skip_compile?: boolean;  // Skip EA compilation (use existing .ex5)
  skip_clean?: boolean;    // Skip cache clean (faster but risks stale cache)
  skip_analyze?: boolean;  // Extract only, skip deal analysis
  deep_analyze?: boolean;  // Add hourly_pnl and volume_profile to analysis.json
  strategy?: "grid" | "scalper" | "trend" | "hedge" | "generic";
                           // Analysis strategy profile (default: "grid").
                           // Controls depth tracking, exit keywords, and cycle grouping.
}

Output schema

{
  success: boolean;
  report_dir: string;       // "reports/20250619_143022_MyEA_XAUUSD_M5"
  duration_seconds: number;

  // Inline summary from metrics.json (always present on success)
  metrics: {
    net_profit: number;
    profit_factor: number;
    max_dd_pct: number;
    sharpe_ratio: number;
    total_trades: number;
    recovery_factor: number;
    expected_payoff: number;
    gross_profit: number;
    gross_loss: number;
    win_rate_pct: number;
    avg_profit: number;
    avg_loss: number;
  };

  // Deal analysis summary (present unless skip_analyze=true)
  analysis_summary: {
    green_months: number;
    total_months: number;
    worst_month: string;        // "2025-10"
    worst_month_pnl: number;
    worst_dd_event_pct: number;
    worst_dd_date: string;
    max_grid_depth: number;     // highest layer reached in any cycle
    l5_plus_count: number;      // cycles that reached L5+
  };

  // File paths for direct reading
  files: {
    metrics_json: string;
    analysis_json: string;
    // Note: deals are stored in SQLite DB, not on disk.
    // Call export_deals_csv(report_id) to generate a CSV file on demand.
  };

  error?: string;  // Present on failure
}

Example

// Input
{
  "expert": "MyEA_v1.2",
  "from": "2025-01-01",
  "to": "2025-06-30",
  "deposit": 10000,
  "model": 0
}

// Output
{
  "success": true,
  "report_dir": "reports/20250619_143022_MyEA_XAUUSD_M5",
  "duration_seconds": 287,
  "metrics": {
    "net_profit": 4832.10,
    "profit_factor": 1.54,
    "max_dd_pct": 12.3,
    "sharpe_ratio": 1.18,
    "total_trades": 891
  },
  "analysis_summary": {
    "green_months": 5,
    "total_months": 6,
    "worst_month": "2025-03",
    "worst_month_pnl": -412.80,
    "worst_dd_event_pct": 12.3,
    "worst_dd_date": "2025-03-14",
    "max_grid_depth": 6,
    "l5_plus_count": 8
  },
  "files": {
    "metrics_json": "reports/20250619_143022_MyEA_XAUUSD_M5/metrics.json",
    "analysis_json": "reports/20250619_143022_MyEA_XAUUSD_M5/analysis.json"
  }
}

run_backtest_quick

Quick backtest using pre-compiled EA: clean cache → backtest → extract → analyze.

When to call: When EA code hasn't changed and you just want to test different parameters or date ranges. Faster than run_backtest because it skips compilation.

Input schema

Same as run_backtest, but skip_compile is automatically set to true.

Output schema

Same as run_backtest.


run_backtest_only

Backtest only: clean cache → backtest → extract. No analysis phase.

When to call: When you just need raw trade data (stored in DB) and don't need analytics. Fastest option for batch processing. Use export_deals_csv afterwards if you need a CSV file.

Input schema

Same as run_backtest, but skip_compile and skip_analyze are automatically set to true.

Output schema

Same as run_backtest but without analysis_summary.


launch_backtest

Fire-and-forget mode: compile → clean → launch MT5 backtest, return immediately with job info.

When to call: When you want to launch a backtest without waiting for completion. Use get_backtest_status to poll for completion.

Input schema

{
  expert: string;          // Required. EA name without path or extension
  symbol?: string;         // Trading symbol (default: from config or first available)
  from_date?: string;      // Start date YYYY.MM.DD (default: past complete month)
  to_date?: string;        // End date YYYY.MM.DD (default: past complete month)
  timeframe?: string;      // M1, M5, M15, M30, H1, H4, D1 (default: M5)
  deposit?: number;        // Initial deposit (default: 10000)
  model?: 0 | 1 | 2;      // Tick model (default: 0)
  set_file?: string;       // Path to .set parameter file
  skip_compile?: boolean;         // Skip compilation
  skip_clean?: boolean;           // Skip cache cleaning
  timeout?: number;               // Max time in seconds (default: 900)
  gui?: boolean;                  // Enable MT5 visualization
  shutdown?: boolean;             // Shut down MT5 after test (default: true).
                                  // NOTE: on Wine/macOS terminal64.exe may not exit naturally
                                  // even with ShutdownTerminal=1. Use inactivity_kill_secs too.
  inactivity_kill_secs?: number;  // Kill MT5 if tester log hasn't grown for N seconds
                                  // (default: disabled / not set). Recommended: 120.
                                  // After silence, pipeline polls for HTML report for 30s,
                                  // then kills MT5 unconditionally. If HTML present → extracted;
                                  // otherwise falls back to journal extraction (no P&L data).
}

Output schema

{
  success: true;
  message: string;         // "Backtest launched successfully..."
  report_id: string;       // e.g., "20250122_034455_MyEA_XAUUSD_M5"
  report_dir: string;      // Full path to report directory
  expert: string;
  symbol: string;
  timeframe: string;
  launched_at: string;     // ISO8601 timestamp
  timeout_seconds: number;
  poll_hint: string;       // "Call get_backtest_status with report_dir to check progress"
}

run_rolling_backtest

Run N consecutive weekly backtests sequentially and return aggregated results. Compiles once, then each week runs with skip_compile. Kills and restarts MT5 between weeks for a clean state.

When to call: When you want to test EA stability across multiple weeks to detect performance degradation, regime change sensitivity, or parameter drift.

Input schema

{
  // Required
  expert: string;              // EA name without path or extension

  // Date range — specify both or omit for auto-calculation (N weeks back to last Sunday)
  from_date?: string;          // "YYYY.MM.DD" (default: auto-calculate N weeks back)
  to_date?: string;            // "YYYY.MM.DD" (default: auto-calculate to last Sunday)

  // Optional overrides
  symbol?: string;             // Trading symbol (default: from config or first available)
  timeframe?: "M1" | "M5" | "M15" | "M30" | "H1" | "H4" | "D1"; // Default: M5
  deposit?: number;            // Initial deposit (default: 10000)
  model?: 0 | 1 | 2;          // Tick model: 0=Every tick, 1=OHLC, 2=Open prices
  set_file?: string;           // Path to .set parameter file for EA inputs

  // Rolling options
  weeks?: number;              // Number of weekly backtests to run (default: 4, max: 52)

  // Pipeline flags
  skip_compile?: boolean;      // Skip initial compilation (default: false — compiles on first week)
  shutdown?: boolean;          // Close MT5 after backtest completes (default: true)
  kill_existing?: boolean;     // Kill any running MT5 instance first (default: true)
  timeout?: number;            // Max wait time per week in seconds (default: 900)
  gui?: boolean;               // Enable MT5 visualization window (default: false)
  startup_delay_secs?: number; // Seconds to wait for MT5 initialization (default: 10)
}

Output schema

{
  success: true;
  message: string;             // "Rolling backtest launched with N weeks. Use get_backtest_status to poll for completion."
  report_id: string;           // "ROLLING_MyEA_2026.06.24_2026.07.01"
  report_dir: string;          // Full path to report directory
  expert: string;
  weeks: Array<{
    label: string;             // "Week 1", "Week 2", etc.
    from_date: string;         // "2026.06.24"
    to_date: string;           // "2026.06.30"
  }>;
  poll_hint: string;           // "Call get_backtest_status with report_dir to check progress"
}

Status polling

After launch, poll with get_backtest_status(report_dir=<dir>) to track progress. The rolling backtest runs all weeks in a background task — each week is a full backtest pipeline (clean → launch → poll → extract → analyze).

Once complete, the report directory contains:

  • rolling_results.json — full summary with per-week metrics and totals
  • progress.log — current week being processed
  • weeks.json — the weekly schedule

Example

// Input
{
  "expert": "MyEA",
  "symbol": "XAUUSD",
  "weeks": 4,
  "deposit": 10000
}

// Output
{
  "success": true,
  "message": "Rolling backtest launched with 4 weeks. Use get_backtest_status to poll for completion.",
  "report_id": "ROLLING_MyEA_2026.06.03_2026.07.01",
  "report_dir": "reports/ROLLING_MyEA_2026.06.03_2026.07.01",
  "expert": "MyEA",
  "weeks": [
    { "label": "Jun 03 - Jun 07", "from_date": "2026.06.03", "to_date": "2026.06.07" },
    { "label": "Jun 10 - Jun 14", "from_date": "2026.06.10", "to_date": "2026.06.14" },
    { "label": "Jun 17 - Jun 21", "from_date": "2026.06.17", "to_date": "2026.06.21" },
    { "label": "Jun 24 - Jun 28", "from_date": "2026.06.24", "to_date": "2026.06.28" }
  ]
}

Rolling results format (rolling_results.json)

{
  "success": true,
  "weeks_run": 4,
  "summary": {
    "total_net_profit": 12450.50,
    "max_drawdown_pct": 8.5,
    "total_trades": 342
  },
  "weekly_results": [
    {
      "label": "Jun 03 - Jun 07",
      "from_date": "2026.06.03",
      "to_date": "2026.06.07",
      "success": true,
      "net_profit": 3200.00,
      "max_dd_pct": 3.2,
      "total_trades": 85,
      "profit_factor": 1.45,
      "report_dir": "reports/20260701_120000_MyEA_XAUUSD_M5"
    },
    {
      "label": "Jun 10 - Jun 14",
      "from_date": "2026.06.10",
      "to_date": "2026.06.14",
      "success": true,
      "net_profit": -450.00,
      "max_dd_pct": 8.5,
      "total_trades": 92,
      "profit_factor": 0.92,
      "report_dir": "reports/20260701_123000_MyEA_XAUUSD_M5"
    }
  ]
}

get_backtest_status

Check progress of a running backtest pipeline launched via launch_backtest.

When to call: Poll periodically after calling launch_backtest to check completion status.

Input schema

{
  report_dir: string;  // Report directory path from launch_backtest output
}

Output schema

{
  success: true;
  report_dir: string;
  status: "completed" | "running" | "failed" | "in_progress" | "not_started";
  stage: string;           // Current pipeline stage: COMPILE, CLEAN, BACKTEST, EXTRACT, ANALYZE, DONE
  is_complete: boolean;    // True if backtest finished successfully
  mt5_running: boolean;    // Whether MT5 process is active
  report_found: boolean;     // Whether report file exists
  metrics_extracted: boolean;
  deals_extracted: boolean;
  elapsed_seconds: number;   // Time since launch
  message: string;          // Human-readable status message
  job?: {
    report_id: string;
    expert: string;
    symbol: string;
    timeframe: string;
    launched_at: string;
    timeout_seconds: number;
  }
}

run_optimization

Launch genetic parameter optimization as a detached background process.

Important: This tool returns immediately. MT5 runs for 2-6 hours. The AI agent must NOT poll for results — the user monitors MT5 and signals when done. Call get_optimization_results only after user confirmation.

Uses Model=1 (1-min OHLC) for faster optimization. Use a separate run_backtest with model=0 to verify top optimization results — Model 1 optimization parameters may overfit grid/martingale EAs because intra-bar price movement is not simulated. The get_optimization_status tool now auto-parses results when optimization completes, returning top passes, best PF, and best profit.

Input schema

{
  expert: string;          // EA name
  set_file: string;        // Path to optimization .set file (with ||Y flags)
  from: string;            // "YYYY-MM-DD"
  to: string;              // "YYYY-MM-DD"
  symbol?: string;         // Default from config
  deposit?: number;        // Default from config
  max_passes?: number;     // Cap on genetic optimization passes (e.g. 5000 to run fewer)
  currency?: string;       // Default: "USD"
  leverage?: number;       // Default: 500
  log_file?: string;       // Where to write nohup output (default: /tmp/opt_<timestamp>.log)
}

Output schema

{
  success: boolean;
  job_id: string;          // "opt_20250619_143022"
  log_file: string;        // "/tmp/opt_20250619_143022.log"
  pid: number;             // Process ID (for user monitoring if needed)
  combinations: number;    // Estimated from set_file analysis (product of all ||Y ranges)
  message: string;         // "Optimization launched. Signal me when MT5 completes."
}

Optimization set file format

; param=current_value||start||step||stop||Y   (Y = include in sweep)
; param=value||N                               (N = fixed, not swept)

Min_Entry_Confidence=0.610||0.580||0.010||0.650||Y   ; 8 values
TP_Pips_Layer1=400||300||50||500||Y                   ; 5 values
Max_DD_Percent=15.0||N                                ; fixed

; Total combinations: 8 × 5 = 40

MT5-Quant handles automatically:

  • UTF-16LE encoding with BOM
  • chmod 444 (read-only) before launch
  • OptMode=0 reset in terminal.ini
  • LastOptimization line removal from terminal.ini
  • ExpertParameters = filename only (not full path) in launch INI

get_optimization_results

Parse completed optimization results. Handles both HTML (.htm) and SpreadsheetML XML (.htm.xml) formats transparently.

Input schema

{
  job_id?: string;         // From run_optimization response. If omitted, uses latest _opt/ dir.
  report_dir?: string;     // Explicit path to *_opt/ directory
  top_n?: number;          // How many top results to return (default: 20)
  dd_threshold?: number;   // Flag results above this DD% as high-risk (default: 20)
  sort_by?: "profit" | "profit_factor" | "sharpe"; // Default: "profit"
}

Output schema

{
  success: boolean;
  total_passes: number;
  converged: boolean;         // True if passes stopped improving in last 10%
  report_format: "html" | "xml";

  results: Array<{
    rank: number;
    net_profit: number;
    profit_factor: number;
    max_dd_pct: number;
    total_trades: number;
    sharpe_ratio: number;
    high_risk: boolean;       // DD > dd_threshold
    params: Record<string, number | boolean>;  // All swept parameter values
  }>;

  convergence_analysis: {
    top_10_agreement: Record<string, string>;  // Params same across top 10 = strong signal
    high_variance_params: string[];             // Params that vary in top 10 = uncertain
  };

  recommendation: {
    best_params: Record<string, number | boolean>;
    reasoning: string;
    next_step: "verify_model0" | "auto_promote" | "investigate";
  };
}

Convergence analysis

A parameter that appears with the same value across all top-10 results is a strong optimization signal — the genetic algorithm converged on it. A parameter that varies across top-10 means the optimizer couldn't distinguish between values — either the parameter doesn't matter much, or more passes are needed.


verify_setup

Check all required paths, Wine version, and EA/set file inventory. Run this first if run_backtest or run_optimization fails with path errors.

Input schema

{}  // No parameters required

Output schema

{
  success: boolean;
  wine_path: string;
  wine_version: string;
  mt5_dir: string;
  terminal_exe: string;
  experts_dir: string;
  display_mode: "gui" | "headless";
  ea_count: number;           // .ex5 files found in Experts/
  set_count: number;          // .set files found
  missing: string[];          // List of paths/tools that couldn't be found
  hints: string[];            // Actionable fix hints for each missing item
}

get_backtest_status

Check the current stage and elapsed time of a running backtest pipeline by reading its progress.log.

Input schema

{
  report_dir: string;    // Path to the report directory from run_backtest
}

Output schema

{
  success: boolean;
  report_dir: string;
  stage: "COMPILE" | "CLEAN" | "BACKTEST" | "EXTRACT" | "ANALYZE" | "DONE";
  elapsed_seconds: number;
  finished: boolean;
  log_lines: string[];   // Last 5 lines of progress.log
}

get_optimization_status

Check the live state of a background optimization job (started by run_optimization).

Input schema

{
  job_id: string;        // From run_optimization response
}

Output schema

{
  success: boolean;
  status: "running" | "stopped" | "completed";
  job_id: string;
  pid: number;
  expert?: string;
  symbol?: string;
  from_date?: string;
  to_date?: string;
  started_at?: string;
  // Present when status is "completed":
  total_passes?: number;
  top_10?: Array<{ pass: number; profit: number; profit_factor: number; drawdown_pct: number; }>;
  best_pf?: { /* best pass by profit factor */ };
  best_profit?: { /* best pass by profit */ };
}

prune_reports

Delete old report directories to reclaim disk space, keeping the most recent N runs. Optimization result directories (*_opt/) are always preserved.

Input schema

{
  keep_last?: number;    // How many recent reports to keep (default from config, usually 10)
  dry_run?: boolean;     // If true, list what would be deleted without deleting (default: false)
}

Output schema

{
  success: boolean;
  deleted: string[];     // Paths that were (or would be) deleted
  kept: string[];        // Paths that were kept
  freed_mb: number;      // Approximate disk space freed
}

analyze_report

Read and summarize a completed backtest report without re-running MT5. Loads deals from the SQLite database.

Input schema

{
  report_id?: string;          // Preferred: report ID from list_reports
  report_dir?: string;         // Legacy: path to report directory (looks up DB entry)
                               // Omit both to use the latest report automatically
  strategy?: "grid" | "scalper" | "trend" | "hedge" | "generic";
                               // Strategy profile that was used (default: "grid").
                               // Only affects interpretation of analysis.json fields —
                               // does not re-run analysis.
  include_deals?: boolean;     // Include top 20 deals in output (default: false)
  include_monthly?: boolean;   // Include full monthly P/L table (default: true)
  include_dd_events?: boolean; // Include DD event reconstruction (default: true)
  deep?: boolean;              // Include hourly_pnl and volume_profile (default: false)
}

Output schema

{
  success: boolean;
  report_dir: string;
  strategy: string;           // Active profile: "grid" | "scalper" | "trend" | "hedge" | "generic"

  metrics: { /* same as run_backtest metrics */ };

  // ── Always present (strategy-agnostic) ─────────────────────────────────────

  monthly_pnl: Array<{
    month: string;          // "2025-01"
    pnl: number;
    trades: number;
    green: boolean;
  }>;

  dd_events: Array<{
    peak_dd_pct: number;
    start_date: string;
    end_date: string;
    duration_days: number;
    recovery_date: string | null;
    recovery_days: number | null;
    cause: string;          // Profile-driven: e.g. "locking_cascade" (grid) or "whipsaw" (trend)
                            // Falls back to "unknown" when no keyword matched
  }>;

  top_losses: Array<{
    date: string;
    loss_usd: number;
    grid_depth_at_close: number;  // 0 for non-grid strategies
    volume: number;
    comment: string;
  }>;

  loss_sequences: Array<{
    length: number;
    total_loss: number;
    start: string;
    end: string;
  }>;

  position_pairs: Array<{
    time: string;
    type: "buy" | "sell";
    profit: number;
    volume: number;
    layer: number;
    hold_minutes: number | null;
    comment: string;
    magic: string;
    order: string;
  }>;

  // ── Strategy-driven (content varies by profile) ────────────────────────────

  depth_histogram: Record<string, number>;
                            // grid:    { L1: n, L2: n, …, "L8+": n }
                            // others:  {} (empty — no depth_re in profile)

  grid_depth_histogram: Record<string, number>;
                            // Backward-compat alias for depth_histogram (grid only)

  cycle_stats: {
    total_cycles: number;
    win_rate: number;       // percent
    avg_profit: number;
    win_rate_by_depth: Record<string, { total: number; win_rate: number }>;
    // win_rate_by_depth populated for grid; keys = "L?" for non-depth profiles
  };

  exit_reason_breakdown: Record<
    string,                 // Keys depend on strategy profile exit_keywords
                            // grid:    "locking" | "cutloss" | "zombie" | "timeout" | "tp" | "sl"
                            // scalper: "manual" | "trailing" | "tp" | "sl"
                            // trend:   "breakeven" | "trailing" | "partial" | "tp" | "sl"
                            // generic: "tp" | "sl"
    { count: number; total_pnl: number; avg_pnl: number }
  >;

  direction_bias: {
    buy?: { trades: number; win_rate: number; total_pnl: number; avg_pnl: number };
    sell?: { trades: number; win_rate: number; total_pnl: number; avg_pnl: number };
  };

  streak_analysis: {
    max_win_streak: number;
    max_win_start: string;
    max_win_end: string;
    max_loss_streak: number;
    max_loss_start: string;
    max_loss_end: string;
    current_streak: number;
    current_streak_type: "win" | "loss";
  };

  session_breakdown: Record<
    "asian" | "london" | "london_ny_overlap" | "new_york" | "off_hours",
    { trades: number; win_rate: number; total_pnl: number }
  >;

  weekday_pnl: Array<{
    day: string;            // "Monday" … "Sunday"
    pnl: number;
    trades: number;
    win_rate: number;
  }>;

  concurrent_peak: {
    peak_open: number;
    peak_time: string;
  };

  // ── Deep mode only (deep=true) ──────────────────────────────────────────────

  hourly_pnl?: Array<{
    hour: number;           // 023
    pnl: number;
    trades: number;
    win_rate: number;
  }>;

  volume_profile?: Array<{
    lot_tier: string;       // "0.01" | "0.02-0.04" | "0.05-0.09" | "0.10-0.49" | …
    pnl: number;
    trades: number;
    win_rate: number;
  }>;

  // ── Optional raw deals ──────────────────────────────────────────────────────

  deals?: Array<{ /* all 13 deal columns */ }>; // Only if include_deals=true
}

compare_baseline

Compare a report against a baseline and return a structured verdict.

Input schema

{
  report_dir: string;       // Report to evaluate
  baseline: {
    net_profit: number;
    max_dd_pct: number;
    total_trades?: number;
    label?: string;         // e.g. "v1.2 production"
  };
  promote_threshold?: {
    profit_gt: number;      // Auto-promote if profit > this (default: baseline profit)
    dd_lt: number;          // AND DD < this (default: 20)
  };
}

Output schema

{
  verdict: "winner" | "loser" | "marginal";
  auto_promote: boolean;

  delta: {
    profit_usd: number;     // positive = improvement
    profit_pct: number;     // relative to baseline
    dd_pp: number;          // positive = DD got worse
    trades_delta: number;
  };

  summary: string;          // Human-readable one-liner

  details: {
    candidate: { net_profit: number; max_dd_pct: number; total_trades: number; };
    baseline: { net_profit: number; max_dd_pct: number; label: string; };
  };
}

Example

// Input
{
  "report_dir": "reports/20250619_143022_MyEA_v1.3_XAUUSD_M5",
  "baseline": {
    "net_profit": 8660,
    "max_dd_pct": 15.66,
    "label": "v1.2 production"
  }
}

// Output
{
  "verdict": "winner",
  "auto_promote": true,
  "delta": {
    "profit_usd": 3186.32,
    "profit_pct": 36.8,
    "dd_pp": -7.27,
    "trades_delta": -3
  },
  "summary": "+$3,186 (+37%) profit vs v1.2. DD dropped from 15.66% to 8.39%. Auto-promoting.",
  "details": {
    "candidate": { "net_profit": 11846.32, "max_dd_pct": 8.39, "total_trades": 1963 },
    "baseline": { "net_profit": 8660.00, "max_dd_pct": 15.66, "label": "v1.2 production" }
  }
}

compile_ea

Compile an MQL5 Expert Advisor via MetaEditor (Wine/CrossOver).

Input schema

{
  expert_path: string;     // e.g. "src/MyEA_v1.2.mq5"
  include_dirs?: string[]; // Additional include search paths
}

Output schema

{
  success: boolean;
  binary_path: string;     // Path where .ex5 was written
  binary_size_bytes: number;
  warnings: number;
  errors: number;
  error_list: Array<{
    file: string;
    line: number;
    message: string;
  }>;
  compile_time_ms: number;
}

Error Handling

All tools return success: false with an error field on failure. Pipeline failures are non-fatal by default — the tool returns partial results if any stages completed.

{
  success: false,
  error: "COMPILE_FAILED",
  error_detail: "2 errors in src/MyEA_v1.2.mq5: line 847: undeclared identifier 'Max_New_Param'",
  completed_stages: ["COMPILE"],
  failed_stage: "COMPILE"
}

Error codes:

Code Stage Cause
COMPILE_FAILED COMPILE MQL5 syntax errors
WINE_NOT_FOUND Any Wine/CrossOver not installed or wrong path
MT5_TIMEOUT BACKTEST MT5 didn't exit within timeout (default: 15min)
REPORT_NOT_FOUND EXTRACT MT5 produced no report (usually parameter error)
EXTRACT_FAILED EXTRACT Report parse error (format change?)
NO_DEALS ANALYZE Report has 0 trades (check date range, symbol)
OPT_NOT_FINISHED get_opt_results Optimization still running

list_reports

List all backtest report directories with compact key metrics. Use this to survey what runs exist before deciding which to analyze — much cheaper than calling analyze_report repeatedly.

Input schema

{
  include_opt?: boolean;   // Include _opt dirs (default: false)
  limit?: number;          // Max reports, newest first (default: 30)
}

Output schema

{
  success: boolean;
  count: number;
  reports: Array<{
    name: string;           // "20250619_143022_MyEA_XAUUSD_M5"
    is_opt: boolean;
    net_profit?: number;
    max_dd_pct?: number;
    total_trades?: number;
    symbol?: string;
    timeframe?: string;
    from_date?: string;
    to_date?: string;
    metrics?: "missing";    // Present only if metrics.json is absent
  }>;
}

tail_log

Read the last N lines of a log file. Supports filter=errors to return only lines containing error/fail keywords — avoids streaming full logs into context.

Input schema

{
  // Provide one of: report_dir, job_id, or log_file
  report_dir?: string;     // Reads progress.log from this dir (omit for latest)
  job_id?: string;         // Reads the nohup log for this optimization job
  log_file?: string;       // Absolute path to any log file

  n?: number;              // Lines to return (default: 50)
  filter?: "all" | "errors" | "warnings";  // Default: "all"
}

Output schema

{
  success: boolean;
  log_file: string;        // Resolved path of the file that was read
  total_lines: number;     // Lines matched after filter applied
  lines: string[];         // Last n of the matched lines
}

cache_status

Show the MT5 tester cache directory size broken down by symbol. Use before clean_cache to see what's there.

Input schema

{}  // No parameters

Output schema

{
  success: boolean;
  cache_dir: string;
  total_size_mb: number;
  symbols: Array<{
    symbol: string;        // Subdirectory name (broker symbol)
    size_mb: number;
  }>;
}

clean_cache

Delete MT5 tester cache files. Forces MT5 to regenerate tick data on the next backtest (slower first run after clean). Supports dry-run preview and per-symbol targeting.

Input schema

{
  symbol?: string;         // Delete only this symbol's cache. Omit to delete all.
  dry_run?: boolean;       // Report what would be deleted without deleting (default: false)
}

Output schema

{
  success: boolean;
  dry_run: boolean;
  deleted_symbols: string[];
  freed_mb: number;
  hint: string;            // Reminder that next backtest will be slower
}

read_set_file

Parse an MT5 .set parameter file (UTF-16LE or UTF-8) into structured JSON. Handles BOM detection automatically. Use this instead of reading raw .set files.

Input schema

{
  path: string;            // Path to .set file
}

Output schema

{
  success: boolean;
  path: string;
  param_count: number;
  comments: string[];      // Header comment lines (stripped of semicolons)
  params: Record<string, {
    value: string;          // Current / default value
    from?: string;          // Sweep start (present for optimization params)
    to?: string;            // Sweep end
    step?: string;          // Sweep step
    optimize?: boolean;     // True if ||Y flag is set
  }>;
}

Example

// Input
{ "path": "config/MyEA_opt.set" }

// Output
{
  "success": true,
  "path": "config/MyEA_opt.set",
  "param_count": 5,
  "comments": ["MyEA optimization set — XAUUSD M5"],
  "params": {
    "Min_Entry_Confidence": { "value": "0.610", "from": "0.580", "to": "0.650", "step": "0.010", "optimize": true },
    "TP_Pips": { "value": "400", "from": "300", "to": "500", "step": "50", "optimize": true },
    "Max_DD_Percent": { "value": "15.0" }
  }
}

write_set_file

Write an MT5 .set parameter file with correct UTF-16LE encoding and chmod 444. Overwrites any existing file at the path.

Input schema

{
  path: string;            // Output path for .set file

  params: Record<string,
    | string | number      // Simple fixed value
    | {
        value: string | number;
        from?: string | number;   // Include for optimization sweep
        to?: string | number;
        step?: string | number;
        optimize?: boolean;       // true → ||Y, false → ||N (default: false)
      }
  >;
}

Output schema

{
  success: boolean;
  path: string;
  param_count: number;
  encoding: "utf-16-le";
  permissions: string;     // "444 (read-only, required by MT5)"
}

Example

// Input
{
  "path": "config/MyEA_opt.set",
  "params": {
    "Min_Entry_Confidence": { "value": 0.61, "from": 0.58, "to": 0.65, "step": 0.01, "optimize": true },
    "TP_Pips": { "value": 400, "from": 300, "to": 500, "step": 50, "optimize": true },
    "Max_DD_Percent": 15.0
  }
}

// Output
{
  "success": true,
  "path": "config/MyEA_opt.set",
  "param_count": 3,
  "encoding": "utf-16-le",
  "permissions": "444 (read-only, required by MT5)"
}

list_jobs

List all optimization jobs tracked in .mt5mcp_jobs/ with compact status. Cheaper than calling get_optimization_status per job.

Input schema

{
  include_done?: boolean;  // Include completed/failed jobs (default: true)
}

Output schema

{
  success: boolean;
  count: number;
  jobs: Array<{
    job_id: string;          // "opt_20250619_143022"
    status: "running" | "done" | "failed";
    elapsed_seconds: number | null;
    expert: string;
    started_at: string;      // ISO timestamp
    log_file: string;
  }>;
}

patch_set_file

Modify specific parameters in an existing .set file in-place. Preserves all other params, comments, and sweep config untouched. Returns a diff of what changed. Use instead of read_set_file → edit → write_set_file — saves two round-trips.

Input schema

{
  path: string;            // .set file to modify (must exist)
  patches: Record<string,
    | string | number      // scalar → only updates value, keeps existing sweep config
    | {
        value?: string | number;
        from?: string | number;
        to?: string | number;
        step?: string | number;
        optimize?: boolean;
      }
  >;
}

Output schema

{
  success: boolean;
  path: string;
  changed_count: number;
  param_count: number;
  changed: Array<{ name: string; old: string; new: string; }>;
}

Example

// Input — change two params without touching the rest of the file
{
  "path": "config/MyEA_opt.set",
  "patches": {
    "TP_Pips": 350,
    "Min_Entry_Confidence": { "value": 0.62, "from": 0.60, "to": 0.65, "optimize": true }
  }
}

// Output
{
  "success": true,
  "path": "config/MyEA_opt.set",
  "changed_count": 2,
  "param_count": 12,
  "changed": [
    { "name": "TP_Pips", "old": "400", "new": "350" },
    { "name": "Min_Entry_Confidence", "old": "0.610", "new": "0.62" }
  ]
}

clone_set_file

Copy a .set file to a new path, applying optional param overrides. One call instead of read → modify → write. Preserves header comments.

Input schema

{
  source: string;          // Source .set file
  destination: string;     // Output path (created if needed)
  overrides?: Record<string, string | number | { value; from?; to?; step?; optimize? }>;
}

Output schema

{
  success: boolean;
  source: string;
  destination: string;
  param_count: number;
  overridden_count: number;
  overridden: Array<{ name: string; old: string | null; new: string; }>;
}

set_from_optimization

Generate a clean backtest .set file directly from an optimization result's params dict. Strips all sweep flags (||Y) so the file is ready for run_backtest. Optionally fills params not in the optimization result from a template .set, and optionally re-adds sweep ranges to selected params for a narrowed follow-on optimization.

Typical call: immediately after get_optimization_results, use results[0].params as the params argument.

Input schema

{
  path: string;            // Output .set file path

  params: Record<string, string | number>;
                           // Flat param→value dict from optimization result.
                           // e.g. { "TP_Pips": 400, "Min_Confidence": 0.61 }

  template?: string;       // Path to existing .set. Params NOT in 'params' are
                           // copied from here as fixed values.

  sweep?: Record<string, { from: number; to: number; step: number; optimize?: boolean }>;
                           // Re-add sweep ranges to specific params after applying opt values.
                           // Used to create a narrowed follow-on optimization .set.
}

Output schema

{
  success: boolean;
  path: string;
  param_count: number;
  from_template: boolean;
  opt_params_applied: number;
  swept_params: number;        // > 0 if sweep was provided
  total_combinations: number;  // 0 for pure backtest .set
}

Example

// After get_optimization_results returned:
// results[0].params = { "TP_Pips": 400, "Min_Entry_Confidence": 0.62, "Max_DD_Percent": 15.0 }

{
  "path": "config/MyEA_v1.3.set",
  "params": { "TP_Pips": 400, "Min_Entry_Confidence": 0.62, "Max_DD_Percent": 15.0 },
  "template": "config/MyEA_base.set"
}

// Output
{
  "success": true,
  "path": "config/MyEA_v1.3.set",
  "param_count": 12,
  "from_template": true,
  "opt_params_applied": 3,
  "swept_params": 0,
  "total_combinations": 0
}

diff_set_files

Compare two .set files and return only the differences. Use instead of reading both files and comparing manually.

Input schema

{
  path_a: string;   // Baseline / old file
  path_b: string;   // Candidate / new file
}

Output schema

{
  success: boolean;
  path_a: string;
  path_b: string;
  identical: boolean;
  added_count: number;    // Params in b but not a
  removed_count: number;  // Params in a but not b
  changed_count: number;  // Params in both but with different value or sweep flag

  added:   Array<{ name: string; value: string; }>;
  removed: Array<{ name: string; value: string; }>;
  changed: Array<{
    name: string;
    a: string;           // value in path_a
    b: string;           // value in path_b
    sweep_a?: boolean;   // Present only if sweep flag differs
    sweep_b?: boolean;
  }>;
}

Example

{
  "path_a": "config/MyEA_v1.2.set",
  "path_b": "config/MyEA_v1.3.set"
}

// Output
{
  "success": true,
  "identical": false,
  "added_count": 1,
  "removed_count": 0,
  "changed_count": 2,
  "added":   [{ "name": "Trailing_Activation", "value": "50" }],
  "removed":  [],
  "changed": [
    { "name": "TP_Pips", "a": "400", "b": "350" },
    { "name": "Min_Entry_Confidence", "a": "0.610", "b": "0.620", "sweep_a": true, "sweep_b": false }
  ]
}

describe_sweep

Show a .set file's sweep configuration: which params are swept, their ranges, per-param value counts, and total combinations. Use before run_optimization to verify scope.

Input schema

{
  path: string;
}

Output schema

{
  success: boolean;
  path: string;
  total_params: number;
  swept_count: number;
  fixed_count: number;
  total_combinations: number;
  swept_params: Array<{
    name: string;
    from: string;
    to: string;
    step: string;
    count: number;      // Number of distinct values in this param's range
  }>;
  hint: string;         // e.g. "240 combinations. Typical range: 18h depending on EA tick speed."
}

Example

// Input
{ "path": "config/MyEA_opt.set" }

// Output
{
  "success": true,
  "total_params": 12,
  "swept_count": 3,
  "fixed_count": 9,
  "total_combinations": 240,
  "swept_params": [
    { "name": "TP_Pips",              "from": "300", "to": "500", "step": "50",   "count": 5 },
    { "name": "Min_Entry_Confidence", "from": "0.58","to": "0.65","step": "0.01", "count": 8 },
    { "name": "Max_DD_Percent",       "from": "12",  "to": "20",  "step": "2",    "count": 5 }
  ],
  "hint": "240 combinations. Typical range: 18h depending on EA tick speed."
}

list_set_files

List all .set files in the MT5 tester profiles directory with param counts, swept param counts, and total combinations per file. Use to find the right .set without reading each one.

Input schema

{
  ea?: string;    // Filter by EA name substring (case-insensitive)
}

Output schema

{
  success: boolean;
  profiles_dir: string;
  count: number;
  files: Array<{
    name: string;               // filename only
    param_count: number;
    swept_count: number;
    total_combinations: number; // 0 for backtest-only .set files
    modified: string;           // "YYYY-MM-DD HH:MM"
    error?: string;             // Present only if file is unreadable
  }>;
}

get_active_account

Get current MT5 account session information: login, server, and available symbols. This is essential for pre-flight checks to ensure symbol availability before backtesting.

Input schema

{}  // No parameters

Output schema

{
  success: boolean;
  ready_for_backtest: boolean;    // true if account exists and symbols available
  account: {
    login: string;
    server: string;
  } | null;
  server: string;                   // Active server name
  available_servers: string[];      // All servers with history data
  symbols: string[];                // Symbols available for active server
  symbol_count: number;
  hint: string;                     // "Ready for backtesting" or instructions
}

check_symbol_data_status

Validate if a symbol has sufficient historical tick data for a specified date range before running backtest. Prevents failed backtests due to missing history data.

Input schema

{
  symbol: string;        // e.g., "XAUUSDc"
  from_date: string;     // "YYYY.MM.DD"
  to_date: string;       // "YYYY.MM.DD"
}

Output schema

{
  success: boolean;
  symbol: string;
  server: string;
  has_sufficient_data: boolean;
  requested_range: { from: string; to: string };
  data_range: string;           // "YYYY.MM.DD - YYYY.MM.DD" or "unknown"
  years_available: number;      // Count of years with data
  hcc_files_count: number;      // Number of history cache files
  warnings: string[] | null;    // Data range issues
  suggestion: string;           // Action recommendation
}

check_mt5_status

Check if MT5 terminal is properly installed and configured. Returns comprehensive status of all required components.

Input schema

{}  // No parameters

Output schema

{
  success: boolean;
  terminal_ready: boolean;      // true if all components present
  checks: {
    mt5_dir_exists: boolean;
    terminal64_exe: boolean;
    metaeditor64_exe: boolean;
    metatester64_exe: boolean;
    wine_executable: boolean;
    wine_path: string | null;
  };
  mt5_version: string | null;
  current_account: {
    login: string;
    server: string;
  } | null;
  hint: string;
}

get_backtest_history

List all backtests previously run for a specific EA and/or symbol with summary metrics. Use for tracking performance over time.

Input schema

{
  expert?: string;       // Filter by EA name
  symbol?: string;       // Filter by symbol
  limit?: number;        // Max results (default: 10)
}

Output schema

{
  success: boolean;
  count: number;
  total: number;
  filters: {
    expert: string | null;
    symbol: string | null;
  };
  history: Array<{
    report_dir: string;
    date: string | null;
    expert: string | null;
    symbol: string | null;
    period: string | null;
    profit: number | null;
    profit_factor: number | null;
    expected_payoff: number | null;
    drawdown_pct: number | null;
    total_trades: number | null;
    win_rate: number | null;
  }>;
  hint: string;
}

compare_backtests

Compare two or more backtest results side-by-side with key metrics analysis. Includes profit/drawdown differences and verdict on which performed better.

Input schema

{
  report_dirs: string[];  // List of report directory paths to compare
}

Output schema

{
  success: boolean;
  count: number;
  comparisons: Array<{
    report_dir: string;
    expert: string | null;
    symbol: string | null;
    net_profit: number | null;
    profit_factor: number | null;
    drawdown_pct: number | null;
    total_trades: number | null;
    win_rate: number | null;
    expected_payoff: number | null;
    recovery_factor: number | null;
    sharpe_ratio: number | null;
  }>;
  analysis: Array<{
    compare_to: string | null;
    report: string | null;
    profit_diff: number;
    profit_pct_change: number;
    drawdown_diff: number;
    profit_factor_diff: number;
    verdict: "better" | "worse" | "mixed";
  }> | null;
  verdict: string | null;  // "Best: <report_dir>"
}

init_project

Create a new MQL5 project with standard directory structure and template files. Supports scalper, swing, grid, and basic templates.

Input schema

{
  name: string;                    // Project name (used for EA filename)
  template?: "scalper" | "swing" | "grid" | "basic";  // Default: "basic"
}

Output schema

{
  success: boolean;
  project_name: string;
  template: string;
  created_files: string[];  // Paths to created files
  hint: string;
}

validate_ea_syntax

Perform pre-compile syntax check on MQL5 source file without running full compilation. Detects common issues before expensive MetaEditor compilation.

Input schema

{
  path: string;  // Path to .mq5 source file
}

Output schema

{
  success: boolean;
  valid: boolean;
  path: string;
  checks: {
    has_on_init: boolean;
    has_on_tick: boolean;
    has_on_deinit: boolean;
    lines: number;
  };
  errors: Array<{
    line: number;
    message: string;
    severity: "error";
  }> | null;
  warnings: Array<{
    line: number;
    message: string;
    severity: "warning";
  }> | null;
  hint: string;
}

create_set_template

Generate a .set parameter file template based on an EA's input variables. Automatically parses input declarations from source code.

Input schema

{
  ea: string;              // EA name or path to .mq5/.ex5 file
  output_path?: string;    // Optional custom output path
}

Output schema

{
  success: boolean;
  ea: string;
  inputs_found: number;
  inputs: Array<{
    name: string;
    type: string;
    default: string;
    description: string | null;
  }>;
  set_file: string;  // Path to generated file
  hint: string;
}

export_report

Export backtest report to various formats (CSV, JSON, Markdown) for external analysis or sharing.

Input schema

{
  report_dir: string;        // Path to backtest report directory
  format?: "csv" | "json" | "md";  // Default: "csv"
  output_path?: string;      // Optional custom output file path
}

Output schema

{
  success: boolean;
  format: string;
  output_file: string;
  source: string;
  hint: string;
}

archive_report

Convert a backtest report directory into a compact JSON entry appended to config/backtest_history.json. Idempotent — re-archiving the same report is a no-op. Optionally deletes the source directory to reclaim disk space.

Input schema

{
  report_dir?: string;     // Directory to archive. Omit for latest.
  delete_after?: boolean;  // Delete source dir after archiving (default: false)
  verdict?: "winner" | "loser" | "marginal" | "reference";
  notes?: string;          // Free-text notes for the entry
  tags?: string[];         // Tags e.g. ["tight-sl", "new-filter"]
}

Output schema

{
  success: boolean;
  id: string;              // Report dir basename used as history entry id
  already_existed: boolean;
  deleted_source: boolean;
  history_file: string;    // Absolute path to backtest_history.json
  entry_summary: {
    ea: string;
    symbol: string;
    metrics: { net_profit: number; profit_factor: number; max_dd_pct: number; sharpe_ratio: number; total_trades: number; };
    verdict: string | null;
  };
}

archive_all_reports

Bulk-archive all backtest report directories into config/backtest_history.json. Entries already in history are skipped. Use delete_after=true to reclaim disk space while preserving all results as JSON. Optimization dirs (_opt suffix) are never deleted.

Input schema

{
  delete_after?: boolean;  // Delete source dirs after archiving (default: false)
  keep_last?: number;      // Protect newest N dirs from deletion even with delete_after=true (default: 5)
  dry_run?: boolean;       // Preview without making changes (default: false)
}

Output schema

{
  success: boolean;
  dry_run: boolean;
  archived_count: number;
  skipped_count: number;   // Already in history
  deleted_count: number;
  failed_count: number;    // Dirs with no parseable metrics
  archived: string[];
  skipped: string[];
  deleted: string[];
  failed: string[];
  history_file: string;
}

get_history

Query config/backtest_history.json with filters and sorting. Strips monthly_pnl arrays by default — set include_monthly=true when you need the full breakdown.

Input schema

{
  ea?: string;               // Substring match on EA name
  symbol?: string;           // Exact match (uppercase)
  verdict?: "winner" | "loser" | "marginal" | "reference";
  tag?: string;              // Entry must contain this tag
  min_profit?: number;       // net_profit >= this
  max_dd_pct?: number;       // max_dd_pct <= this
  sort_by?: "date" | "profit" | "dd" | "sharpe";  // Default: date, newest first
  limit?: number;            // Default: 20
  include_monthly?: boolean; // Include monthly_pnl arrays (default: false)
}

Output schema

{
  success: boolean;
  count: number;
  entries: Array<{
    id: string;                    // Report dir basename
    archived_at: string;           // ISO timestamp
    report_dir_deleted: boolean;
    ea: string;
    symbol: string;
    timeframe: string;
    from_date: string;
    to_date: string;
    metrics: {
      net_profit: number;
      profit_factor: number;
      max_dd_pct: number;
      sharpe_ratio: number;
      total_trades: number;
      recovery_factor: number;
      win_rate_pct: number;
      expected_payoff: number;
    };
    summary?: {
      green_months: number;
      total_months: number;
      worst_month: string;
      worst_month_pnl: number;
      dominant_exit?: string;
      max_win_streak?: number;
      max_loss_streak?: number;
    };
    worst_dd_event?: {
      peak_dd_pct: number;
      start_date: string;
      end_date: string;
      duration_days: number;
      cause: string;
    };
    monthly_pnl?: Array<{ month: string; pnl: number; trades: number; green: boolean; }>;
    verdict: string | null;
    notes: string;
    tags: string[];
    promoted_to_baseline: boolean;
  }>;
}

promote_to_baseline

Write a backtest result to config/baseline.json — the production reference used by compare_baseline and the Claude Code baseline hook. Also marks the source history entry as promoted_to_baseline: true.

Input schema

{
  // Provide one: history_id, report_dir, or neither (uses latest report)
  history_id?: string;     // Entry id from get_history
  report_dir?: string;     // Direct path to report directory
  notes?: string;          // Written to baseline.json notes field
}

Output schema

{
  success: boolean;
  baseline_file: string;
  baseline: {
    ea: string;
    symbol: string;
    period: string;            // "YYYY-MM-DD/YYYY-MM-DD"
    net_profit: number;
    profit_factor: number;
    max_drawdown_pct: number;
    sharpe_ratio: number;
    total_trades: number;
    recovery_factor: number;
    promoted_from: string;     // History entry id
    promoted_at: string;       // Date promoted (YYYY-MM-DD)
    notes: string;
  };
}

Example

// Input
{ "history_id": "20250619_143022_MyEA_XAUUSD_M5", "notes": "v1.3 after walk-forward validation" }

// Output
{
  "success": true,
  "baseline_file": "/path/to/config/baseline.json",
  "baseline": {
    "ea": "MyEA",
    "symbol": "XAUUSD",
    "period": "2025-01-01/2025-06-30",
    "net_profit": 4832.10,
    "profit_factor": 1.54,
    "max_drawdown_pct": 12.3,
    "sharpe_ratio": 1.18,
    "total_trades": 891,
    "recovery_from": "20250619_143022_MyEA_XAUUSD_M5",
    "promoted_at": "2025-06-20",
    "notes": "v1.3 after walk-forward validation"
  }
}

annotate_history

Update the verdict, notes, or tags on an existing history entry. Use this after compare_baseline to record the decision, or to tag runs for later retrieval.

Input schema

{
  history_id: string;      // Required — entry id to update
  verdict?: "winner" | "loser" | "marginal" | "reference";
  notes?: string;          // Replaces existing notes
  tags?: string[];         // Replaces existing tags
  add_tags?: string[];     // Appends to existing tags without overwriting
}

Output schema

{
  success: boolean;
  id: string;
  verdict: string | null;
  notes: string;
  tags: string[];
}

Token-efficient usage patterns

Surveying past runs

list_reports(limit=10)          → see what's there (live dirs)
get_history(ea="MyEA", limit=10) → see what's been archived
analyze_report(report_dir=X)    → drill into one specific run

Never call analyze_report on multiple directories to find the best run — use list_reports or get_history first.

Checking logs without noise

tail_log(job_id=X, filter=errors)   → only failures
tail_log(report_dir=X, n=20)        → last 20 lines of backtest progress

Managing disk space

archive_all_reports(dry_run=true)               → preview what would be archived
archive_all_reports(delete_after=true, keep_last=3)  → archive all, delete old, keep 3 newest
get_history(sort_by=profit, limit=5)            → find best archived runs

Labelling experiments

annotate_history(history_id=X, verdict="loser", notes="SL too tight, reversed at L3")
annotate_history(history_id=X, add_tags=["walk-forward-fail"])
get_history(verdict="winner")                   → all winners across all sessions

Promoting a new production config

run_backtest(...)
compare_baseline(...)                           → get verdict
archive_report(delete_after=true, verdict="winner")
promote_to_baseline(notes="v1.4 after WF")     → update baseline.json

Managing cache

cache_status()                                  → see symbol breakdown and total size
clean_cache(symbol=XAUUSD, dry_run=true)        → preview
clean_cache(symbol=XAUUSD)                      → execute

Pre-flight validation

get_active_account()                            → current login, server, available symbols
check_symbol_data_status(symbol=XAUUSD, from=2025.01.01, to=2025.03.31)
                                                → verify data availability before backtest
check_mt5_status()                              → verify MT5 installation and readiness
validate_ea_syntax(path=MyEA.mq5)               → pre-compile syntax check

Project management

init_project(name=MyStrategy, template=scalper)  → scaffold new EA with template
create_set_template(ea=MyEA)                     → generate .set from EA inputs
export_report(report_dir=..., format=csv)         → export to CSV/JSON/Markdown

History and comparison

get_backtest_history(expert=MyEA, limit=10)       → list past backtests with metrics
compare_backtests(report_dirs=["dir1", "dir2"])   → side-by-side comparison

Working with set files

# Inspect
list_set_files(ea="MyEA")               → all variants, swept param counts, combinations
describe_sweep(path=MyEA_opt.set)       → verify 240 combinations before launching opt
diff_set_files(a=v1.2.set, b=v1.3.set) → only changed params, not full file content

# Edit (never read+write manually)
patch_set_file(path, {TP_Pips: 350})    → change one param, keep everything else intact
clone_set_file(src, dest, overrides)    → create variant from base in one call

# Generate after optimization
set_from_optimization(                  → map results[0].params → clean backtest .set
  path=MyEA_v1.3.set,
  params=results[0].params,
  template=MyEA_base.set                → fills non-swept params from existing file
)

list_symbols

List all available symbols in the MT5 terminal.

When to call: To verify a symbol is available before running backtests, or to discover available symbols.

Input schema

{
  // No inputs required
}

Output schema

{
  success: boolean;
  symbols: Array<{
    name: string;          // e.g. "XAUUSD", "EURUSD"
    description?: string;   // Symbol description
    visible: boolean;       // Whether symbol is visible in Market Watch
  }>;
  error?: string;
}

check_update

Check if a newer version of MT5-Quant is available on GitHub.

When to call: Periodically to stay up to date with the latest features and fixes.

Input schema

{
  // No inputs required
}

Output schema

{
  success: boolean;
  current_version: string;  // Current version, e.g. "1.31.5"
  latest_version: string;   // Latest version from GitHub releases
  update_available: boolean;
  download_url?: string;    // URL to latest release if update available
  release_notes?: string;   // Release notes from latest version
  error?: string;
}

update

Update MT5-Quant to the latest version from GitHub releases.

When to call: After check_update indicates a newer version is available.

Input schema

{
  // No inputs required
}

Output schema

{
  success: boolean;
  message: string;          // Update status message
  new_version: string;      // Version installed
  error?: string;
}

healthcheck

Quick server health check to verify the MCP server is running.

When to call: To verify the server is responsive after installation or configuration changes.

Input schema

{
  // No inputs required
}

Output schema

{
  success: boolean;
  version: string;          // MT5-Quant version
  status: "healthy" | "unhealthy";
  uptime_seconds: number;
  error?: string;
}

list_experts

List all Expert Advisors (EAs) in the MQL5/Experts directory.

When to call: To discover available EAs before running backtests.

Input schema

{
  // No inputs required
}

Output schema

{
  success: boolean;
  experts: Array<{
    name: string;           // EA name without extension
    path: string;           // Full path to .mq5 file
    has_ex5: boolean;      // Whether compiled .ex5 exists
    modified: string;       // Last modified timestamp
  }>;
  error?: string;
}

list_indicators

List all indicators in the MQL5/Indicators directory.

When to call: To discover available indicators for use in projects.

Input schema

{
  // No inputs required
}

Output schema

{
  success: boolean;
  indicators: Array<{
    name: string;           // Indicator name without extension
    path: string;           // Full path to .mq5 file
    has_ex5: boolean;      // Whether compiled .ex5 exists
    modified: string;       // Last modified timestamp
  }>;
  error?: string;
}

list_scripts

List all scripts in the MQL5/Scripts directory.

When to call: To discover available scripts for one-time execution.

Input schema

{
  // No inputs required
}

Output schema

{
  success: boolean;
  scripts: Array<{
    name: string;           // Script name without extension
    path: string;           // Full path to .mq5 file
    has_ex5: boolean;      // Whether compiled .ex5 exists
    modified: string;       // Last modified timestamp
  }>;
  error?: string;
}

search_experts

Search EAs by name pattern across all directories.

When to call: To find EAs matching a specific pattern when the exact name is unknown.

Input schema

{
  pattern: string;          // Search pattern, e.g. "Grid" or "v1."
}

Output schema

{
  success: boolean;
  matches: Array<{
    name: string;           // EA name without extension
    path: string;           // Full path to .mq5 file
    has_ex5: boolean;      // Whether compiled .ex5 exists
    modified: string;       // Last modified timestamp
  }>;
  error?: string;
}

search_indicators

Search indicators by name pattern.

When to call: To find indicators matching a specific pattern.

Input schema

{
  pattern: string;          // Search pattern
}

Output schema

{
  success: boolean;
  matches: Array<{
    name: string;           // Indicator name without extension
    path: string;           // Full path to .mq5 file
    has_ex5: boolean;      // Whether compiled .ex5 exists
    modified: string;       // Last modified timestamp
  }>;
  error?: string;
}

search_scripts

Search scripts by name pattern.

When to call: To find scripts matching a specific pattern.

Input schema

{
  pattern: string;          // Search pattern
}

Output schema

{
  success: boolean;
  matches: Array<{
    name: string;           // Script name without extension
    path: string;           // Full path to .mq5 file
    has_ex5: boolean;      // Whether compiled .ex5 exists
    modified: string;       // Last modified timestamp
  }>;
  error?: string;
}

copy_indicator_to_project

Copy indicator to project directory.

When to call: To include an indicator in your EA project.

Input schema

{
  indicator_name: string;   // Name of indicator to copy
  project_dir: string;     // Target project directory path
}

Output schema

{
  success: boolean;
  source_path: string;     // Original indicator path
  target_path: string;     // Copied indicator path
  error?: string;
}

copy_script_to_project

Copy script to project directory.

When to call: To include a script in your EA project.

Input schema

{
  script_name: string;      // Name of script to copy
  project_dir: string;     // Target project directory path
}

Output schema

{
  success: boolean;
  source_path: string;     // Original script path
  target_path: string;     // Copied script path
  error?: string;
}

diagnose_wine

Check Wine installation, version, and prefix health.

When to call: When experiencing Wine-related issues or to verify Wine setup.

Input schema

{
  // No inputs required
}

Output schema

{
  success: boolean;
  wine_found: boolean;
  wine_path?: string;
  wine_version?: string;
  prefix_path?: string;
  prefix_health: "healthy" | "corrupted" | "missing";
  error?: string;
}

get_mt5_logs

Get MT5 terminal, tester, or MetaEditor logs with filtering.

When to call: To diagnose backtest failures or compile errors.

Input schema

{
  log_type: "terminal" | "tester" | "metaeditor";
  lines?: number;           // Number of lines to retrieve (default: 100)
  filter?: "all" | "errors" | "warnings"; // Default: "all"
}

Output schema

{
  success: boolean;
  log_path: string;
  lines: Array<{
    timestamp: string;
    level: "info" | "warning" | "error";
    message: string;
  }>;
  error?: string;
}

search_mt5_errors

Search logs for error patterns (crash, exception, access violation).

When to call: To quickly find crash causes in logs.

Input schema

{
  log_type: "terminal" | "tester" | "metaeditor" | "all";
  patterns?: string[];      // Custom error patterns (default: common MT5 errors)
  max_results?: number;     // Maximum error entries to return (default: 50)
}

Output schema

{
  success: boolean;
  errors: Array<{
    timestamp: string;
    log_type: string;
    message: string;
    context?: string;      // Surrounding lines for context
  }>;
  total_found: number;
  error?: string;
}

check_mt5_process

Check if MT5 processes are running, get PID, CPU, memory usage.

When to call: To verify MT5 is running or to check for stuck processes.

Input schema

{
  // No inputs required
}

Output schema

{
  success: boolean;
  processes: Array<{
    pid: number;
    name: string;           // "terminal64.exe" or "metatester64.exe"
    cpu_percent: number;
    memory_mb: number;
    uptime_seconds: number;
    status: "running" | "zombie" | "stopped";
  }>;
  error?: string;
}

kill_mt5_process

Kill stuck MT5 processes.

When to call: When MT5 is stuck or hung and needs to be terminated.

Input schema

{
  pid?: number;            // Specific PID to kill (optional, kills all if omitted)
  force?: boolean;         // Force kill wineserver too (default: false)
}

Output schema

{
  success: boolean;
  killed_pids: number[];
  message: string;
  error?: string;
}

check_system_resources

Check disk space, memory, CPU availability.

When to call: Before running long optimizations to ensure sufficient resources.

Input schema

{
  // No inputs required
}

Output schema

{
  success: boolean;
  disk: {
    path: string;
    total_gb: number;
    free_gb: number;
    used_percent: number;
  };
  memory: {
    total_mb: number;
    free_mb: number;
    used_percent: number;
  };
  cpu: {
    cores: number;
    usage_percent: number;
  };
  error?: string;
}

validate_mt5_config

Validate terminal.ini and tester configuration files.

When to call: When experiencing configuration-related backtest failures.

Input schema

{
  // No inputs required (auto-detects config paths)
}

Output schema

{
  success: boolean;
  terminal_ini: {
    path: string;
    valid: boolean;
    issues?: string[];
  };
  tester_ini?: {
    path: string;
    valid: boolean;
    issues?: string[];
  };
  error?: string;
}

get_wine_prefix_info

Get Wine prefix details: Windows version, installed programs, registry.

When to call: To diagnose Wine prefix issues or verify Wine setup.

Input schema

{
  // No inputs required
}

Output schema

{
  success: boolean;
  prefix_path: string;
  windows_version: string;
  installed_programs: string[];
  registry_keys?: Array<{
    key: string;
    value: string;
  }>;
  error?: string;
}

get_backtest_crash_info

Investigate backtest failures: incomplete markers, missing metrics.json, Wine/MT5 errors.

When to call: When a backtest fails unexpectedly.

Input schema

{
  report_dir: string;       // Path to the failed backtest report directory
}

Output schema

{
  success: boolean;
  status: "complete" | "incomplete" | "crashed" | "missing";
  issues: Array<{
    type: string;
    message: string;
    severity: "error" | "warning";
  }>;
  error_log?: string;
  error?: string;
}

get_latest_report

Get most recent report with optional equity chart.

When to call: To quickly access the latest backtest results.

Input schema

{
  include_chart?: boolean;  // Include equity chart data (default: false)
}

Output schema

{
  success: boolean;
  report: {
    id: string;
    report_dir: string;
    ea: string;
    symbol: string;
    timeframe: string;
    from_date: string;
    to_date: string;
    metrics: {
      net_profit: number;
      profit_factor: number;
      max_dd_pct: number;
      total_trades: number;
    };
    equity_chart?: Array<{date: string; equity: number}>;
  };
  error?: string;
}

search_reports

Find reports by EA, symbol, date range, or profit criteria.

When to call: To find specific reports matching criteria.

Input schema

{
  ea?: string;
  symbol?: string;
  from_date?: string;      // "YYYY-MM-DD"
  to_date?: string;        // "YYYY-MM-DD"
  min_profit?: number;
  max_profit?: number;
  min_profit_factor?: number;
  max_dd_pct?: number;
  limit?: number;          // Max results (default: 50)
}

Output schema

{
  success: boolean;
  reports: Array<{
    id: string;
    report_dir: string;
    ea: string;
    symbol: string;
    timeframe: string;
    from_date: string;
    to_date: string;
    metrics: {
      net_profit: number;
      profit_factor: number;
      max_dd_pct: number;
      total_trades: number;
    };
  }>;
  total: number;
  error?: string;
}

get_report_by_id

Get specific report by ID with equity chart.

When to call: To retrieve a specific report's full details.

Input schema

{
  report_id: string;       // Report directory basename
  include_chart?: boolean;  // Include equity chart data (default: false)
}

Output schema

{
  success: boolean;
  report: {
    id: string;
    report_dir: string;
    ea: string;
    symbol: string;
    timeframe: string;
    from_date: string;
    to_date: string;
    metrics: {
      net_profit: number;
      profit_factor: number;
      max_dd_pct: number;
      total_trades: number;
      sharpe_ratio: number;
      recovery_factor: number;
      win_rate_pct: number;
    };
    equity_chart?: Array<{date: string; equity: number}>;
  };
  error?: string;
}

get_reports_summary

Aggregate stats: counts, averages, pass rates.

When to call: To get overview statistics across all reports.

Input schema

{
  ea?: string;             // Filter by EA
  symbol?: string;         // Filter by symbol
  from_date?: string;      // Filter by date range
  to_date?: string;
}

Output schema

{
  success: boolean;
  summary: {
    total_reports: number;
    avg_profit: number;
    avg_profit_factor: number;
    avg_dd_pct: number;
    profitable_count: number;
    pass_rate_pct: number;
  };
  error?: string;
}

get_best_reports

Top N reports sorted by any metric (profit factor, drawdown, etc.).

When to call: To find the best performing reports.

Input schema

{
  sort_by: "net_profit" | "profit_factor" | "sharpe_ratio" | "recovery_factor" | "win_rate_pct";
  order: "desc" | "asc";  // Default: "desc"
  limit?: number;          // Max results (default: 10)
  ea?: string;
  symbol?: string;
}

Output schema

{
  success: boolean;
  reports: Array<{
    id: string;
    report_dir: string;
    ea: string;
    symbol: string;
    metrics: {
      net_profit: number;
      profit_factor: number;
      max_dd_pct: number;
      sharpe_ratio: number;
      recovery_factor: number;
      win_rate_pct: number;
    };
  }>;
  error?: string;
}

search_reports_by_tags

Find reports by tags.

When to call: To find reports tagged with specific keywords.

Input schema

{
  tags: string[];          // Tags to search for
  match_all?: boolean;     // Require all tags (default: false)
  limit?: number;
}

Output schema

{
  success: boolean;
  reports: Array<{
    id: string;
    report_dir: string;
    tags: string[];
    metrics: {
      net_profit: number;
      profit_factor: number;
      max_dd_pct: number;
    };
  }>;
  error?: string;
}

search_reports_by_date_range

Query by backtest date range.

When to call: To find reports from a specific time period.

Input schema

{
  from_date: string;       // "YYYY-MM-DD"
  to_date: string;         // "YYYY-MM-DD"
  limit?: number;
}

Output schema

{
  success: boolean;
  reports: Array<{
    id: string;
    report_dir: string;
    timestamp: string;
    metrics: {
      net_profit: number;
      profit_factor: number;
      max_dd_pct: number;
    };
  }>;
  error?: string;
}

search_reports_by_notes

Full-text search in report notes.

When to call: To find reports with specific notes.

Input schema

{
  query: string;           // Search query
  limit?: number;
}

Output schema

{
  success: boolean;
  reports: Array<{
    id: string;
    report_dir: string;
    notes: string;
    metrics: {
      net_profit: number;
      profit_factor: number;
      max_dd_pct: number;
    };
  }>;
  error?: string;
}

get_reports_by_set_file

Find all reports using a specific .set file.

When to call: To see all backtests run with a specific parameter set.

Input schema

{
  set_file: string;        // .set file name (without path)
}

Output schema

{
  success: boolean;
  reports: Array<{
    id: string;
    report_dir: string;
    set_file: string;
    metrics: {
      net_profit: number;
      profit_factor: number;
      max_dd_pct: number;
    };
  }>;
  error?: string;
}

get_comparable_reports

Find comparable reports (same EA/symbol/timeframe).

When to call: To find reports for comparison/analysis.

Input schema

{
  ea: string;
  symbol: string;
  timeframe?: string;     // Optional, uses default if omitted
}

Output schema

{
  success: boolean;
  reports: Array<{
    id: string;
    report_dir: string;
    from_date: string;
    to_date: string;
    metrics: {
      net_profit: number;
      profit_factor: number;
      max_dd_pct: number;
      total_trades: number;
    };
  }>;
  error?: string;
}

export_deals_csv

Export deals for a report to a CSV file on demand. Deals are stored in the database — use this when you need a CSV file for external tools (Excel, pandas, etc.).

When to call: When you need a deals.csv file. CSV is not written automatically after backtests anymore.

Input schema

{
  report_id?: string;       // Report ID to export (default: latest report)
  output_path?: string;     // File path for CSV output (default: <report_dir>/deals.csv)
}

Output schema

{
  success: boolean;
  report_id: string;
  deals_count: number;
  output_path: string;      // Absolute path to the written CSV file
}

Example

// Input — export latest report to default path
{}

// Input — export specific report to custom path
{
  "report_id": "20260422_051041_DPS21_XAUUSDc_M5_1",
  "output_path": "/tmp/dps21_deals.csv"
}

// Output
{
  "success": true,
  "report_id": "20260422_051041_DPS21_XAUUSDc_M5_1",
  "deals_count": 891,
  "output_path": "/Users/…/reports/20260422_051041_DPS21_XAUUSDc_M5_1/deals.csv"
}

Granular Analytics — Common Input Pattern

All granular analytics tools below (analyze_monthly_pnl through analyze_efficiency, plus list_deals and search_deals_*) share the same report resolution logic:

{
  report_id?: string;   // Preferred: ID from list_reports
  report_dir?: string;  // Legacy: filesystem path (looks up DB entry)
  // Omit both → uses the latest report automatically
}

Deals are loaded from SQLite DB, not from CSV files. The report_dir parameter is kept for backward compatibility — if your report is in the DB, passing report_dir will resolve to its report_id automatically.


analyze_monthly_pnl

Monthly P/L breakdown only.

When to call: To analyze performance by month without full analysis.

Input schema

{
  report_id?: string;       // Preferred: from list_reports
  report_dir?: string;      // Legacy: path to report directory
}
// Omit all args to use the latest report

Output schema

{
  success: boolean;
  monthly_data: Array<{
    month: string;          // "2025-01"
    profit: number;
    trades: number;
    win_rate: number;
  }>;
  green_months: number;
  total_months: number;
  error?: string;
}

analyze_drawdown_events

Drawdown events and causes only.

When to call: To identify and analyze drawdown periods.

Input schema

{
  report_dir: string;
}

Output schema

{
  success: boolean;
  drawdowns: Array<{
    start_date: string;
    end_date: string;
    depth_pct: number;
    duration_days: number;
    recovery_days: number;
    cause?: string;
  }>;
  worst_dd_event: {
    depth_pct: number;
    date: string;
  };
  error?: string;
}

analyze_top_losses

Worst losing deals only.

When to call: To identify the biggest losing trades for analysis.

Input schema

{
  report_dir: string;
  top_n?: number;          // Number of worst deals to return (default: 10)
}

Output schema

{
  success: boolean;
  worst_deals: Array<{
    entry_time: string;
    exit_time: string;
    profit: number;
    profit_pct: number;
    volume: number;
    comment?: string;
  }>;
  error?: string;
}

analyze_loss_sequences

Consecutive loss patterns only.

When to call: To analyze losing streak patterns.

Input schema

{
  report_dir: string;
}

Output schema

{
  success: boolean;
  loss_streaks: Array<{
    start_index: number;
    end_index: number;
    length: number;
    total_loss: number;
    avg_loss: number;
  }>;
  max_loss_streak: {
    length: number;
    total_loss: number;
  };
  error?: string;
}

analyze_position_pairs

Position hold time and P/L pairs.

When to call: To analyze relationship between hold time and profit.

Input schema

{
  report_dir: string;
}

Output schema

{
  success: boolean;
  pairs: Array<{
    hold_time_minutes: number;
    profit: number;
    profit_pct: number;
    count: number;
  }>;
  correlation: number;
  error?: string;
}

analyze_direction_bias

Buy vs Sell performance.

When to call: To analyze directional bias in trading.

Input schema

{
  report_dir: string;
}

Output schema

{
  success: boolean;
  buy_trades: {
    count: number;
    profit: number;
    win_rate: number;
  };
  sell_trades: {
    count: number;
    profit: number;
    win_rate: number;
  };
  bias: "buy" | "sell" | "neutral";
  error?: string;
}

analyze_streaks

Win/loss streak analysis.

When to call: To analyze winning and losing streak patterns.

Input schema

{
  report_dir: string;
}

Output schema

{
  success: boolean;
  win_streaks: Array<{
    length: number;
    total_profit: number;
  }>;
  loss_streaks: Array<{
    length: number;
    total_loss: number;
  }>;
  max_win_streak: number;
  max_loss_streak: number;
  error?: string;
}

analyze_concurrent_peak

Peak simultaneous positions.

When to call: To analyze maximum position concurrency.

Input schema

{
  report_dir: string;
}

Output schema

{
  success: boolean;
  max_concurrent: number;
  avg_concurrent: number;
  distribution: Array<{
    concurrent_count: number;
    occurrences: number;
  }>;
  error?: string;
}

list_deals

List individual deals with filters (type, profit range, volume, dates). Loads from SQLite DB.

When to call: To query individual trades with specific criteria.

Input schema

{
  report_id?: string;       // Preferred: from list_reports
  report_dir?: string;      // Legacy: path to report directory
  // Omit all to use latest report
  deal_type?: "buy" | "sell" | "all";
  min_profit?: number;
  max_profit?: number;
  min_volume?: number;
  max_volume?: number;
  from_date?: string;      // "YYYY-MM-DD"
  to_date?: string;        // "YYYY-MM-DD"
  limit?: number;
}

Output schema

{
  success: boolean;
  deals: Array<{
    entry_time: string;
    exit_time: string;
    type: "buy" | "sell";
    profit: number;
    profit_pct: number;
    volume: number;
    comment?: string;
    magic?: number;
  }>;
  total: number;
  error?: string;
}

search_deals_by_comment

Full-text search in deal comments (e.g., "Layer #3"). Loads from SQLite DB.

When to call: To find deals with specific comment patterns.

Input schema

{
  report_id?: string;      // Preferred: from list_reports
  report_dir?: string;     // Legacy: path to report directory
  query: string;           // Search pattern in comment field (required)
  limit?: number;
}

Output schema

{
  success: boolean;
  deals: Array<{
    entry_time: string;
    exit_time: string;
    type: "buy" | "sell";
    profit: number;
    profit_pct: number;
    volume: number;
    comment: string;
    magic?: number;
  }>;
  total: number;
  error?: string;
}

search_deals_by_magic

Filter deals by EA magic number.

When to call: To find trades from a specific EA (by magic number).

Input schema

{
  report_dir: string;
  magic: number;           // EA magic number to filter by
  limit?: number;
}

Output schema

{
  success: boolean;
  deals: Array<{
    entry_time: string;
    exit_time: string;
    type: "buy" | "sell";
    profit: number;
    profit_pct: number;
    volume: number;
    comment?: string;
    magic: number;
  }>;
  total: number;
  error?: string;
}

analyze_profit_distribution

Profit histogram: small/medium/large wins and losses.

When to call: To understand profit distribution across trade sizes.

Input schema

{
  report_dir: string;
}

Output schema

{
  success: boolean;
  distribution: {
    small_wins: {count: number; avg_profit: number};
    medium_wins: {count: number; avg_profit: number};
    large_wins: {count: number; avg_profit: number};
    small_losses: {count: number; avg_loss: number};
    medium_losses: {count: number; avg_loss: number};
    large_losses: {count: number; avg_loss: number};
  };
  error?: string;
}

analyze_time_performance

Performance by hour of day and day of week.

When to call: To identify best/worst trading times.

Input schema

{
  report_dir: string;
}

Output schema

{
  success: boolean;
  hourly_performance: Array<{
    hour: number;
    profit: number;
    trades: number;
    win_rate: number;
  }>;
  daily_performance: Array<{
    day: "Mon" | "Tue" | "Wed" | "Thu" | "Fri";
    profit: number;
    trades: number;
    win_rate: number;
  }>;
  error?: string;
}

analyze_hold_time_distribution

Hold time buckets + correlation with profit.

When to call: To analyze relationship between position duration and profit.

Input schema

{
  report_dir: string;
}

Output schema

{
  success: boolean;
  buckets: Array<{
    label: string;        // e.g. "<5m", "5-15m", "15-60m", ">1h"
    min_minutes: number;
    max_minutes: number;
    count: number;
    avg_profit: number;
    win_rate: number;
  }>;
  correlation: number;
  error?: string;
}

analyze_layer_performance

Grid/martingale layer analysis from comments.

When to call: To analyze performance by grid layer (for grid/martingale strategies).

Input schema

{
  report_dir: string;
}

Output schema

{
  success: boolean;
  layers: Array<{
    layer: number;        // e.g. "L1", "L2", "L3"
    count: number;
    profit: number;
    win_rate: number;
    avg_profit: number;
  }>;
  max_layer: number;
  l5_plus_count: number;
  error?: string;
}

analyze_volume_vs_profit

Volume correlation + performance by lot size.

When to call: To analyze relationship between position size and profit.

Input schema

{
  report_dir: string;
}

Output schema

{
  success: boolean;
  by_volume: Array<{
    volume: number;
    count: number;
    avg_profit: number;
    win_rate: number;
  }>;
  correlation: number;
  optimal_volume: number;
  error?: string;
}

analyze_costs

Commission and swap impact on profitability.

When to call: To understand the impact of trading costs.

Input schema

{
  report_dir: string;
}

Output schema

{
  success: boolean;
  costs: {
    total_commission: number;
    total_swap: number;
    total_spread: number;
    avg_commission_per_trade: number;
    avg_swap_per_trade: number;
  };
  cost_impact_pct: number; // Costs as % of gross profit
  error?: string;
}

analyze_efficiency

Profit per hour/day, annualized return, trade frequency.

When to call: To measure overall trading efficiency.

Input schema

{
  report_dir: string;
}

Output schema

{
  success: boolean;
  efficiency: {
    profit_per_hour: number;
    profit_per_day: number;
    profit_per_trade: number;
    trades_per_day: number;
    annualized_return_pct: number;
  };
  error?: string;
}

Autonomous Loop Pattern

The tools are designed to support a fully autonomous experiment → evaluate → promote → optimize loop:

1.  run_backtest(new_params)
2.  compare_baseline(result, current_production)
3a. if winner:
     - archive_report(delete_after=true, verdict="winner")
     - promote_to_baseline(notes="...")
     - write_set_file(new_production.set)
     - run_optimization(new_production_set)
     - [wait for user signal]
     - get_optimization_results()
     - set_from_optimization(path=verify.set, params=results[0].params, template=prod.set)
     - verify top result: run_backtest(expert, set_file=verify.set, skip_compile=true)
     - if still beats baseline → goto step 1
3b. if loser:
     - archive_report(delete_after=true, verdict="loser", notes="root cause")
     - analyze_report(result) → find root cause
     - read_set_file() → inspect current params
     - propose parameter or code change → goto step 1

No user confirmation needed between steps 1→2→3. The AI agent drives the full loop; the user monitors and signals when optimization completes (since that runs for hours). Every run is archived before the directory is deleted, so nothing is lost.