Mt5pro-bot2/python/backtest/run.py

62 lines
3.1 KiB
Python

"""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()