115 lines
5.0 KiB
Python
115 lines
5.0 KiB
Python
"""Compare the dry-run MT5 report against Python on the SAME window + deposit.
|
|
|
|
The dry-run MT5 report shows the tester was run on 2026.04.16 - 2026.05.08
|
|
with initial deposit 1000 USD. To make a fair Python-vs-MT5 comparison we
|
|
re-slice the bars to that exact window and re-run the engine with the same
|
|
deposit. Then we print the side-by-side table.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
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 shared.data.mt5_report import parse_mt5_report
|
|
from shared.mt5_pipeline.compare import build_comparison_table
|
|
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
|
|
|
|
|
|
def main() -> int:
|
|
# ── Parse the MT5 report ──────────────────────────────────────────────
|
|
report = PROJECT / "reports" / "ReportTester-52845377.html"
|
|
mt5 = parse_mt5_report(report)
|
|
print("=== MT5 report (dry run) ===")
|
|
for k, v in mt5.items():
|
|
if k.startswith("_"):
|
|
continue
|
|
print(f" {k:24s}: {v}")
|
|
# The MT5 window + deposit (parsed from the report's _all dict).
|
|
all_fields = mt5["_all"]
|
|
window_label = all_fields.get("期间:", "")
|
|
print(f" window (raw) : {window_label}")
|
|
deposit = mt5.get("Initial Deposit") or 1000
|
|
print(f" initial deposit: {deposit}")
|
|
|
|
# ── Slice Python bars to the same window ─────────────────────────────
|
|
bars = load_bars(PROJECT / "data" / "XAUUSD_M5_2024-06-26_2026-06-26.parquet")
|
|
# MT5 tester's ToDate is exclusive of the day (uses 00:00 of that day),
|
|
# so the actual data ends at 2026.05.08 00:00 (not 23:59). Match it exactly.
|
|
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)
|
|
print(f"\n=== Python (matched window) ===")
|
|
print(f" bars : {len(window):,}")
|
|
print(f" window : {window['timestamp'].iloc[0]} → {window['timestamp'].iloc[-1]}")
|
|
print(f" deposit : {deposit}")
|
|
|
|
# ── Run the engine on the matched window ─────────────────────────────
|
|
# Override InpAtrPeriod to 15 to match the MT5 manual run (user changed
|
|
# it from the .set's 14 to 15 in the tester UI). RSI stays at 14.
|
|
params = dict(FROZEN_BASELINE)
|
|
params["InpAtrPeriod"] = 15
|
|
pack = build_signals(params, window, XAUUSD_REAL)
|
|
# Load M1 data for tick-level exit simulation (closes the bar-level
|
|
# optimism gap on BE/trailing — doc 03 §7).
|
|
m1_path = PROJECT / "data" / "XAUUSD_M1_2024-06-26_2026-06-26.parquet"
|
|
m1_bars = load_bars(m1_path) if m1_path.exists() else None
|
|
if m1_bars is not None:
|
|
# Slice M1 to the same window as M5 (exclusive end, matching MT5).
|
|
m1_bars = m1_bars[
|
|
(m1_bars["timestamp"] >= start) & (m1_bars["timestamp"] < end)
|
|
].reset_index(drop=True)
|
|
print(f" m1 bars : {len(m1_bars):,} (tick-level exit simulation ON)")
|
|
engine = ScalperEngine()
|
|
result = engine.run(
|
|
window, pack.signals_long, pack.signals_short,
|
|
pack.sl_prices, pack.tp_prices,
|
|
XAUUSD_REAL, SizingInputs(), float(deposit),
|
|
m1_bars=m1_bars,
|
|
**engine_kwargs_from_params(params),
|
|
)
|
|
py = compute_metrics(result)
|
|
print(f" trades : {py.total_trades}")
|
|
print(f" net : {py.net_profit:+.2f}")
|
|
print(f" PF : {py.profit_factor:.2f}")
|
|
print(f" DD : {py.max_equity_dd:.2f} ({py.max_equity_dd_pct:.2%})")
|
|
|
|
# ── Build the comparison table ───────────────────────────────────────
|
|
# Map Python Metrics → keys the compare table expects.
|
|
py_mapped = {
|
|
"net_profit": py.net_profit,
|
|
"profit_factor": py.profit_factor,
|
|
"total_trades": py.total_trades,
|
|
"max_equity_dd": py.max_equity_dd,
|
|
"win_rate": py.win_rate,
|
|
"sharpe": py.sharpe,
|
|
}
|
|
mt5_mapped = {
|
|
"net_profit": mt5.get("Total Net Profit"),
|
|
"profit_factor": mt5.get("Profit Factor"),
|
|
"total_trades": mt5.get("Total Trades"),
|
|
"max_equity_dd": mt5.get("Equity Drawdown Maximal"),
|
|
"win_rate": None,
|
|
"sharpe": mt5.get("Sharpe Ratio"),
|
|
}
|
|
print(f"\n=== Python vs MT5 (matched window {start.date()} → {end.date()}) ===")
|
|
print(build_comparison_table(py_mapped, mt5_mapped))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|