Files
mymt5opp/scripts/diag_exit.py
T
2026-06-26 20:50:07 +08:00

64 lines
2.8 KiB
Python

"""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}")