Files
mymt5opp/run.py
T
2026-06-26 20:50:07 +08:00

154 lines
6.3 KiB
Python

"""Phase 5+6 entry point: optimize GoldScalperPro on real XAUUSD bars.
Assembles ObjectiveConfig (engine + signals + search space + constraints),
runs Optuna, applies the diverse top-N selector, then runs the robustness
layers on the finalists and prints a report. This is the cycle that Phase 7
will feed into MT5 for verification.
Usage:
python run.py [--trials 200] [--top-n 3] [--deposit 10000]
"""
from __future__ import annotations
import argparse
import sys
import time
from pathlib import Path
PROJECT = Path(__file__).resolve().parent
sys.path.insert(0, str(PROJECT))
import optuna
from shared.core.engine import SizingInputs
from shared.data.loaders import load_bars
from shared.optimizer.objective import (
Constraints,
ObjectiveConfig,
build_objective,
)
from shared.optimizer.selector import select_diverse_topn
from shared.robustness.layers import stability_region
from strategies.gold_scalper_pro.instruments import XAUUSD_REAL
from strategies.gold_scalper_pro.scalper_engine import (
ScalperConfig,
ScalperEngine,
config_from_params,
engine_kwargs_from_params,
)
from strategies.gold_scalper_pro.search_space import (
FROZEN_BASELINE,
INT_PARAMS,
SEARCH_SPACE,
)
from strategies.gold_scalper_pro.signals import build_signals
def find_bars_file() -> Path:
"""Auto-find the latest XAUUSD M5 parquet in data/."""
data_dir = PROJECT / "data"
candidates = sorted(data_dir.glob("XAUUSD_M5_*.parquet"))
if not candidates:
raise FileNotFoundError(f"no XAUUSD M5 parquet in {data_dir}")
return candidates[-1]
def main() -> int:
ap = argparse.ArgumentParser(description="Optimize GoldScalperPro.")
ap.add_argument("--trials", type=int, default=100,
help="Optuna trials (default 100)")
ap.add_argument("--top-n", type=int, default=3,
help="diverse finalists to select (default 3)")
ap.add_argument("--deposit", type=float, default=10000.0,
help="initial deposit (default 10000)")
ap.add_argument("--bars", type=str, default="",
help="path to parquet bars (blank = auto-find)")
args = ap.parse_args()
bars_path = Path(args.bars) if args.bars else find_bars_file()
print(f"=== GoldScalperPro optimization ===")
print(f"bars : {bars_path.name}")
print(f"trials : {args.trials}")
print(f"top-n : {args.top_n}")
print(f"deposit : {args.deposit:,.0f} USD")
print()
bars = load_bars(bars_path)
print(f"loaded {len(bars):,} bars {bars['timestamp'].iloc[0]}{bars['timestamp'].iloc[-1]}")
# ── Assemble the objective ────────────────────────────────────────────
constraints = Constraints(
min_trades=25,
min_profit_factor=1.2, # relaxed for first pass; tightened later
max_equity_dd_pct=0.40, # 40% hard cap
)
obj_cfg = ObjectiveConfig(
engine=ScalperEngine(),
bars=bars,
instrument=XAUUSD_REAL,
sizing=SizingInputs(),
initial_deposit=args.deposit,
search_space=SEARCH_SPACE,
int_params=INT_PARAMS,
frozen_baseline=FROZEN_BASELINE,
constraints=constraints,
dd_weight=1.0,
build_signals=build_signals,
build_engine_kwargs=engine_kwargs_from_params,
)
objective = build_objective(obj_cfg)
# ── Run Optuna ─────────────────────────────────────────────────────────
optuna.logging.set_verbosity(optuna.logging.WARNING)
study = optuna.create_study(direction="maximize",
sampler=optuna.samplers.TPESampler(seed=42))
print(f"\nrunning {args.trials} trials ...")
t0 = time.time()
study.optimize(objective, n_trials=args.trials, show_progress_bar=False)
elapsed = time.time() - t0
print(f"done in {elapsed:.1f}s ({elapsed/args.trials:.2f}s/trial)")
# ── Report ────────────────────────────────────────────────────────────
best = study.best_trial
print(f"\n=== best trial #{best.number} ===")
print(f" score : {best.value:+,.2f}")
print(f" net profit : {best.user_attrs['net_profit']:+,.2f}")
print(f" profit factor : {best.user_attrs['profit_factor']:.2f}")
print(f" trades : {best.user_attrs['total_trades']}")
print(f" win rate : {best.user_attrs['win_rate']:.2%}")
print(f" equity DD : {best.user_attrs['max_equity_dd']:,.2f} "
f"({best.user_attrs['max_equity_dd_pct']:.2%})")
print(f" sharpe : {best.user_attrs['sharpe']:.2f}")
if best.user_attrs.get("violations"):
print(f" violations : {best.user_attrs['violations']}")
print(" params:")
for k, v in best.user_attrs["params"].items():
if k in SEARCH_SPACE:
print(f" {k:24s} = {v}")
# ── Diverse top-N ─────────────────────────────────────────────────────
print(f"\n=== diverse top-{args.top_n} finalists ===")
finalists = select_diverse_topn(study, args.top_n, SEARCH_SPACE)
for i, t in enumerate(finalists, 1):
print(f" #{i} trial {t.number}: score={t.value:+,.2f} "
f"net={t.user_attrs['net_profit']:+,.2f} "
f"PF={t.user_attrs['profit_factor']:.2f} "
f"trades={t.user_attrs['total_trades']}")
# ── Stability region (is the best on a plateau?) ─────────────────────
print(f"\n=== stability region ===")
sr = stability_region(study, SEARCH_SPACE)
print(f" passed : {sr.get('passed')}")
print(f" cluster_size : {sr.get('cluster_size')}")
print(f" best_in_cluster : {sr.get('best_in_cluster')}")
if sr.get("reason"):
print(f" reason : {sr['reason']}")
print("\n=== done ===")
print("Next: Phase 7 — generate .set/.ini for each finalist, run MT5, compare.")
return 0
if __name__ == "__main__":
raise SystemExit(main())