Files
2026-06-26 20:50:07 +08:00

91 lines
3.8 KiB
Python

"""Smoke test: run the scalper engine end-to-end on real XAUUSD bars.
Uses the frozen baseline params (the saved .set config). Verifies the engine
+ signals + metrics produce a sane result (trades, PnL, drawdown) before we
wire the optimizer. This is the Phase 4 validation gate — not yet the MT5
fidelity check (that's Phase 7).
"""
from __future__ import annotations
import sys
from pathlib import Path
PROJECT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(PROJECT))
import numpy as np
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 ScalperConfig, ScalperEngine
from strategies.gold_scalper_pro.search_space import FROZEN_BASELINE
from strategies.gold_scalper_pro.signals import build_signals
def main() -> int:
bars_path = PROJECT / "data" / "XAUUSD_M5_2024-06-26_2026-06-26.parquet"
print(f"loading {bars_path.name} ...")
bars = load_bars(bars_path)
print(f" {len(bars):,} bars {bars['timestamp'].iloc[0]}{bars['timestamp'].iloc[-1]}")
print("\nbuilding signals (frozen baseline params) ...")
pack = build_signals(FROZEN_BASELINE, bars, XAUUSD_REAL)
n_long = int(pack.signals_long.sum())
n_short = int(pack.signals_short.sum())
print(f" long signals : {n_long}")
print(f" short signals: {n_short}")
# Build ScalperConfig from frozen baseline (mirrors EA inputs).
cfg = ScalperConfig(
use_break_even=FROZEN_BASELINE["InpUseBreakEven"],
use_trailing=FROZEN_BASELINE["InpUseTrailing"],
use_session=FROZEN_BASELINE["InpUseSession"],
session_start_hour=FROZEN_BASELINE["InpSessionStartHour"],
session_end_hour=FROZEN_BASELINE["InpSessionEndHour"],
max_positions=FROZEN_BASELINE["InpMaxPositions"],
max_trades_per_day=FROZEN_BASELINE["InpMaxTradesPerDay"],
daily_loss_limit_pct=FROZEN_BASELINE["InpDailyLossLimit"],
daily_profit_target_pct=FROZEN_BASELINE["InpDailyProfitTarget"],
min_seconds_between=FROZEN_BASELINE["InpMinSecondsBetween"],
sizing_mode=FROZEN_BASELINE["InpSizingMode"],
fixed_lots=FROZEN_BASELINE["InpFixedLots"],
risk_percent=FROZEN_BASELINE["InpRiskPercent"],
break_even_points=FROZEN_BASELINE["InpBreakEvenPoints"],
break_even_lock=FROZEN_BASELINE["InpBreakEvenLock"],
trail_start_points=FROZEN_BASELINE["InpTrailStartPoints"],
trail_step_points=FROZEN_BASELINE["InpTrailStepPoints"],
)
sizing = SizingInputs() # unused — sizing lives in ScalperConfig for this EA
print("\nrunning engine ...")
engine = ScalperEngine()
result = engine.run(
bars, pack.signals_long, pack.signals_short,
pack.sl_prices, pack.tp_prices,
XAUUSD_REAL, sizing, initial_deposit=10000.0,
scalper_cfg=cfg,
)
metrics = compute_metrics(result, periods_per_year=252 * 24 * 12) # M5 → ~72/year
print("\n=== result (frozen baseline) ===")
print(f" trades : {metrics.total_trades}")
print(f" net profit : {metrics.net_profit:,.2f}")
print(f" profit factor : {metrics.profit_factor:.2f}")
print(f" win rate : {metrics.win_rate:.2%}")
print(f" equity DD max : {metrics.max_equity_dd:,.2f} ({metrics.max_equity_dd_pct:.2%})")
print(f" sharpe : {metrics.sharpe:.2f}")
if result.trades:
reasons = {}
for t in result.trades:
reasons[t.exit_reason] = reasons.get(t.exit_reason, 0) + 1
print(f" exit reasons : {reasons}")
print(f" final balance : {result.final_balance:,.2f}")
return 0
if __name__ == "__main__":
raise SystemExit(main())