89 lines
4.0 KiB
Python
89 lines
4.0 KiB
Python
"""Diagnostic: print the ATR value Python computes at the first signal bar
|
||||
|
|
(2025-01-02 01:50) and derive what MT5's ATR must have been (from the observed
|
|||
|
|
0.16 lots). If they differ, the gap is in the bar data, not in the ATR math.
|
|||
|
|
|
|||
|
|
Also dump the OHLC of the M5 bars around 2025-01-02 01:50 so we can compare
|
|||
|
|
against what MT5 sees.
|
|||
|
|
"""
|
|||
|
|
from __future__ import annotations
|
|||
|
|
import sys
|
|||
|
|
from pathlib import Path
|
|||
|
|
PROJECT = Path(__file__).resolve().parent.parent
|
|||
|
|
sys.path.insert(0, str(PROJECT))
|
|||
|
|
|
|||
|
|
import optuna
|
|||
|
|
import pandas as pd
|
|||
|
|
|
|||
|
|
from shared.data.loaders import load_bars
|
|||
|
|
from shared.indicators.base import atr
|
|||
|
|
from shared.optimizer.selector import select_diverse_topn
|
|||
|
|
from strategies.gold_scalper_pro.instruments import XAUUSD_REAL
|
|||
|
|
from strategies.gold_scalper_pro.search_space import FROZEN_BASELINE, SEARCH_SPACE
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main() -> int:
|
|||
|
|
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)
|
|||
|
|
f1 = finalists[0]
|
|||
|
|
merged = {**FROZEN_BASELINE, **f1.params}
|
|||
|
|
atr_period = int(merged["InpAtrPeriod"])
|
|||
|
|
sl_mult = float(merged["InpAtrSLMult"])
|
|||
|
|
risk_pct = float(merged["InpRiskPercent"])
|
|||
|
|
init_deposit = 1000.0
|
|||
|
|
|
|||
|
|
print(f"finalist #1 atr_period={atr_period} sl_mult={sl_mult} risk={risk_pct}%")
|
|||
|
|
|
|||
|
|
full_m5 = load_bars(PROJECT / "data" / "XAUUSD_M5_2024-06-26_2026-06-26.parquet")
|
|||
|
|
close = full_m5["close"].to_numpy(dtype=float)
|
|||
|
|
high = full_m5["high"].to_numpy(dtype=float)
|
|||
|
|
low = full_m5["low"].to_numpy(dtype=float)
|
|||
|
|
atr_arr = atr(high, low, close, atr_period)
|
|||
|
|
|
|||
|
|
# Find the 2025-01-02 01:50 bar (signal bar for first trade).
|
|||
|
|
target = pd.Timestamp("2025-01-02 01:50:00")
|
|||
|
|
ts = pd.to_datetime(full_m5["timestamp"].to_numpy())
|
|||
|
|
idx = int(ts.searchsorted(target, side="left"))
|
|||
|
|
print(f"\n first-signal bar @ {ts[idx]} (idx {idx})")
|
|||
|
|
print(f" OHLC = O={full_m5['open'].iloc[idx]:.2f} H={full_m5['high'].iloc[idx]:.2f} "
|
|||
|
|
f"L={full_m5['low'].iloc[idx]:.2f} C={full_m5['close'].iloc[idx]:.2f} "
|
|||
|
|
f"spread={full_m5['spread'].iloc[idx]}")
|
|||
|
|
print(f" ATR({atr_period}) at this bar = {atr_arr[idx]:.6f}")
|
|||
|
|
print(f" sl_dist = {sl_mult} × ATR = {sl_mult * atr_arr[idx]:.6f}")
|
|||
|
|
print(f" loss_per_lot = sl_dist / tick_size × tick_value = "
|
|||
|
|
f"{sl_mult * atr_arr[idx] / XAUUSD_REAL.tick_size * XAUUSD_REAL.tick_value:.4f}")
|
|||
|
|
risk_money = init_deposit * risk_pct / 100.0
|
|||
|
|
sl_dist = sl_mult * atr_arr[idx]
|
|||
|
|
loss_per_lot = sl_dist / XAUUSD_REAL.tick_size * XAUUSD_REAL.tick_value
|
|||
|
|
lots = risk_money / loss_per_lot
|
|||
|
|
print(f" risk_money = ${risk_money:.4f}")
|
|||
|
|
print(f" Python computed lots = {lots:.6f} → rounded to {XAUUSD_REAL.round_volume(lots):.4f}")
|
|||
|
|
|
|||
|
|
# What would MT5's ATR have to be to produce 0.16 lots?
|
|||
|
|
mt5_lots = 0.16
|
|||
|
|
mt5_loss_per_lot = risk_money / mt5_lots
|
|||
|
|
mt5_sl_dist = mt5_loss_per_lot * XAUUSD_REAL.tick_size / XAUUSD_REAL.tick_value
|
|||
|
|
mt5_atr = mt5_sl_dist / sl_mult
|
|||
|
|
print(f"\n MT5 first trade: {mt5_lots} lots → sl_dist={mt5_sl_dist:.6f} → ATR={mt5_atr:.6f}")
|
|||
|
|
print(f" ratio Python/MT5 ATR = {atr_arr[idx] / mt5_atr:.4f} ({(atr_arr[idx]/mt5_atr - 1)*100:+.1f}%)")
|
|||
|
|
|
|||
|
|
# Dump 30 bars around the signal to inspect the recent volatility.
|
|||
|
|
print(f"\n last {atr_period + 5} bars before signal (for ATR warmup):")
|
|||
|
|
print(f" {'ts':<22} {'open':>9} {'high':>9} {'low':>9} {'close':>9} {'TR':>9}")
|
|||
|
|
for j in range(max(0, idx - atr_period - 5), idx + 1):
|
|||
|
|
tr = max(
|
|||
|
|
high[j] - low[j],
|
|||
|
|
abs(high[j] - close[j-1]) if j > 0 else high[j] - low[j],
|
|||
|
|
abs(low[j] - close[j-1]) if j > 0 else high[j] - low[j],
|
|||
|
|
)
|
|||
|
|
print(f" {str(ts[j]):<22} {full_m5['open'].iloc[j]:>9.2f} {full_m5['high'].iloc[j]:>9.2f} "
|
|||
|
|
f"{full_m5['low'].iloc[j]:>9.2f} {full_m5['close'].iloc[j]:>9.2f} {tr:>9.4f}")
|
|||
|
|
return 0
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
raise SystemExit(main())
|