"""Trace engine execution on 2025-01-02 to find why first trade fires at 15:50 instead of 01:55 (signal trigger bar 01:50 has buy_signal=True). Patches ScalperEngine._entry_allowed to log every call, plus dumps the position state across the day. """ 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.data.loaders import load_bars from strategies.gold_scalper_pro.instruments import XAUUSD_REAL from strategies.gold_scalper_pro.scalper_engine import ( ScalperEngine, ScalperConfig, engine_kwargs_from_params, ) from strategies.gold_scalper_pro.search_space import FROZEN_BASELINE, SEARCH_SPACE from strategies.gold_scalper_pro.signals import build_signals import optuna from shared.optimizer.selector import select_diverse_topn db = PROJECT / "studies" / "optuna" / "gold_scalper_pro_is2025.db" study = optuna.load_study( study_name="gold_scalper_pro_is2025", storage=f"sqlite:///{db}", ) finalists = select_diverse_topn(study, n=3, ranges=SEARCH_SPACE) params = {**FROZEN_BASELINE, **finalists[0].params} m5 = load_bars(PROJECT / "data" / "XAUUSD_M5_2024-06-26_2026-06-26.parquet") m1 = load_bars(PROJECT / "data" / "XAUUSD_M1_2024-06-26_2026-06-26.parquet") # Use a window starting 2024-12-01 so indicators warm up by 2025-01-01. START = pd.Timestamp("2024-12-01 00:00:00") END = pd.Timestamp("2025-01-03 00:00:00") bars = m5[(m5["timestamp"] >= START) & (m5["timestamp"] < END)].reset_index(drop=True) m1_bars = m1[(m1["timestamp"] >= START) & (m1["timestamp"] < END)].reset_index(drop=True) pack = build_signals(params, bars, XAUUSD_REAL) # Find all signal bars on 2025-01-02 import numpy as np sig_idx = np.where(pack.signals_long | pack.signals_short)[0] print(f"signal bars on 2024-12-01..2025-01-02: {len(sig_idx)}") for i in sig_idx[-10:]: t = bars["timestamp"].iloc[i] sig_dir = "LONG" if pack.signals_long[i] else "SHORT" sl = pack.sl_prices[i] if not np.isnan(pack.sl_prices[i]) else float("nan") tp = pack.tp_prices[i] if not np.isnan(pack.tp_prices[i]) else float("nan") print(f" bar {i} ts={t} sig={sig_dir} close={bars['close'].iloc[i]:.2f} " f"sl_price={sl:.2f} tp_price={tp:.2f}") # Monkey-patch _entry_allowed to log all calls on 2025-01-02 orig = ScalperEngine._entry_allowed def traced(self, cfg, t, trades_today, last_trade_ts, i, sl, sh): res = orig(self, cfg, t, trades_today, last_trade_ts, i, sl, sh) if pd.Timestamp("2025-01-02 00:00:00") <= t <= pd.Timestamp("2025-01-02 23:59:59"): if sl[i] or sh[i]: print(f" _entry_allowed(bar={i}, ts={t}, long={sl[i]}, short={sh[i]}, " f"trades_today={trades_today}, last={last_trade_ts}) → {res}") return res ScalperEngine._entry_allowed = traced print("\n--- Running engine on 2024-12-01..2025-01-02 window ---") engine = ScalperEngine() result = engine.run( bars, pack.signals_long, pack.signals_short, pack.sl_prices, pack.tp_prices, XAUUSD_REAL, SizingInputs(), 1000.0, m1_bars=m1_bars, **engine_kwargs_from_params(params), ) print(f"\ntrades: {len(result.trades)}") for tr in result.trades[:5]: d = "LONG" if tr.direction.name == "LONG" else "SHRT" print(f" {tr.entry_time} {d} entry={tr.entry_price:.2f} lots={tr.lots:.4f} " f"pnl={tr.pnl:.4f} reason={tr.exit_reason}")