"""Usage: python -m backtest.run --strategy regime_rsi --symbol XAUUSD --csv data/XAUUSD_M15.csv --walkforward python -m backtest.run --strategy ema_atr --symbol XAUUSD --mt5 --bars 40000 python -m backtest.run --strategy ema_atr --symbol XAUUSD --demo (données fictives, test du moteur) """ import argparse, os, time, yaml from backtest import data from backtest.engine import run_backtest from backtest.metrics import compute_metrics, fmt from backtest.montecarlo import monte_carlo from backtest.walkforward import walk_forward from backtest.report import render_report def main(): ap = argparse.ArgumentParser() ap.add_argument("--strategy", required=True) ap.add_argument("--symbol", default="XAUUSD") ap.add_argument("--tf", default=None) ap.add_argument("--csv"); ap.add_argument("--mt5", action="store_true"); ap.add_argument("--demo", action="store_true") ap.add_argument("--bars", type=int, default=30000) ap.add_argument("--walkforward", action="store_true") ap.add_argument("--out", default=None) ap.add_argument("--config", default="config.yaml") a = ap.parse_args() cfg = yaml.safe_load(open(a.config, encoding="utf-8")) bt = dict(cfg["backtest"]); bt["_filters"] = cfg["filters"] spec = bt["symbols"][a.symbol] entry = next(s for s in cfg["strategies"] if s["name"] == a.strategy) tf, params = a.tf or entry["timeframe"], entry["params"] if a.csv: df = data.load_csv(a.csv) elif a.mt5: df = data.from_mt5(a.symbol, tf, a.bars, cfg["account"]) elif a.demo: df = data.synthetic(a.bars, start_price=2000 if a.symbol == "XAUUSD" else 1.1) else: raise SystemExit("Choisis une source: --csv, --mt5 ou --demo") print(f"{len(df)} barres {tf} | {df.time.iloc[0]} → {df.time.iloc[-1]}") t0 = time.time() r = run_backtest(df, a.strategy, params, tf, a.symbol, spec, cfg["risk"], bt) m = compute_metrics(r["trades"], r["equity"], r["initial"]) print("BACKTEST |", fmt(m), f"| {time.time()-t0:.0f}s") if r["halted"]: print("⚠️ Arrêt par limite de drawdown pendant le test") mcc = cfg["backtest"]["montecarlo"] mc = monte_carlo(r["trades"], r["initial"], mcc["runs"], mcc["ruin_dd_pct"]) if mc: print(f"MONTE CARLO | DD médian {mc['dd_p50']:.1f}% | DD 95e pct {mc['dd_p95']:.1f}% | proba perte {mc['prob_loss']:.0f}% | proba DD>=10% {mc['prob_dd10']:.0f}%") wf = None if a.walkforward: grid = cfg["backtest"]["optimize"].get(a.strategy) if not grid: raise SystemExit("Pas de grille 'optimize' pour cette stratégie") wf = walk_forward(df, a.strategy, params, grid, tf, a.symbol, spec, cfg["risk"], bt, cfg["backtest"]["walkforward"]) if wf["metrics"]: print("WALK-FORWARD (OOS) |", fmt(wf["metrics"]), "| WFE", f"{wf['wfe']:.0f}%" if wf["wfe"] is not None else "n/a") os.makedirs("reports", exist_ok=True) out = a.out or f"reports/{a.strategy}_{a.symbol}.html" render_report(out, f"{a.strategy} — {a.symbol} {tf}", m, r["equity"], r["trades"], wf, mc, synthetic=a.demo) print("Rapport:", out) if __name__ == "__main__": main()