"""Diagnose the exit-side PnL gap between Python and MT5. MT5: gross profit 185.01, gross loss -123.78, 92 trades, net 61.23. Python: 92 trades, net 118.79. So Python's gross profit is much higher OR its gross loss is much smaller. Find out which by dumping Python's gross profit / gross loss + per-reason breakdown. """ import sys from pathlib import Path PROJECT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(PROJECT)) import pandas as pd from shared.core.engine import SizingInputs from shared.core.metrics import compute_metrics from shared.data.loaders import load_bars from strategies.gold_scalper_pro.instruments import XAUUSD_REAL from strategies.gold_scalper_pro.scalper_engine import ScalperEngine, engine_kwargs_from_params from strategies.gold_scalper_pro.search_space import FROZEN_BASELINE from strategies.gold_scalper_pro.signals import build_signals import collections bars = load_bars(PROJECT / "data" / "XAUUSD_M5_2024-06-26_2026-06-26.parquet") start = pd.Timestamp("2026-04-16 00:00:00") end = pd.Timestamp("2026-05-08 00:00:00") window = bars[(bars["timestamp"] >= start) & (bars["timestamp"] < end)].reset_index(drop=True) params = dict(FROZEN_BASELINE) params["InpAtrPeriod"] = 15 pack = build_signals(params, window, XAUUSD_REAL) engine = ScalperEngine() result = engine.run(window, pack.signals_long, pack.signals_short, pack.sl_prices, pack.tp_prices, XAUUSD_REAL, SizingInputs(), 1000.0, **engine_kwargs_from_params(params)) wins = [t.pnl for t in result.trades if t.pnl > 0] losses = [t.pnl for t in result.trades if t.pnl < 0] print(f"=== Python exit-side breakdown ({len(result.trades)} trades) ===") print(f" gross profit : {sum(wins):+.2f} ({len(wins)} trades)") print(f" gross loss : {sum(losses):+.2f} ({len(losses)} trades)") print(f" net : {sum(t.pnl for t in result.trades):+.2f}") print() print(f"=== MT5 (from report) ===") print(f" gross profit : +185.01") print(f" gross loss : -123.78") print(f" net : +61.23") print() by_reason = collections.defaultdict(list) for t in result.trades: by_reason[t.exit_reason].append(t.pnl) print("=== Python PnL by exit reason ===") for reason, pnls in sorted(by_reason.items()): arr = __import__("numpy").array(pnls) print(f" {reason:14s} n={len(arr):3d} sum={arr.sum():+8.2f} " f"mean={arr.mean():+6.2f} min={arr.min():+7.2f} max={arr.max():+7.2f}") # Distribution of win sizes — MT5's avg win = 185.01 / n_wins; need n_wins from MT5. # MT5 win rate unknown, but PF = GP/|GL| = 185.01/123.78 = 1.494 → matches. print(f"\n=== Python win/loss sizes ===") print(f" avg win : {sum(wins)/len(wins):+.2f} (n={len(wins)})") print(f" avg loss : {sum(losses)/len(losses):+.2f} (n={len(losses)})") print(f" Python PF: {sum(wins)/abs(sum(losses)):.2f}")