63a829cc46
主要内容: - Phase 8 PROMOTE: finalist #1 (trial #324) registry 条目,自动生成 - Optuna objective warmup bug 修复 (shared/optimizer/objective.py) - studies/ 目录按用途重组为 optuna/ + finalists/ + features/ 三层 - reports/ 加入 Optuna 中文 dashboard (5 主图 + 18 slice + 15 contour) - 新增 PROJECT_GUIDE.md 项目说明文档 - 新增 build_registry_entry.py / build_optuna_dashboard.py / build_feature_datasets.py - .gitignore: 允许提交 studies/*.db (Optuna DB) 和 reports/*.html (MT5 + dashboard)
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())
|