605faf5310
Add cluster audit pipeline, united EA updates, brochure generators, and publication hygiene (gitignore, MT5 path desensitization, pre-upload scan). Remove tracked reports, models, and binary artifacts from the repo. Co-authored-by: Cursor <cursoragent@cursor.com>
110 lines
4.0 KiB
Python
110 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Optimize SimpleEMA v4 (regime dual entry + partial TP)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import random
|
|
import sys
|
|
from dataclasses import asdict
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
import MetaTrader5 as mt5
|
|
import pandas as pd
|
|
|
|
ROOT = Path(__file__).resolve().parents[3]
|
|
LAB = Path(__file__).resolve().parent
|
|
sys.path.insert(0, str(ROOT / "backtesting" / "MT5"))
|
|
sys.path.insert(0, str(LAB))
|
|
|
|
from cluster_audit.backtest_core import CostModel, load_bars, resolve_symbol # noqa: E402
|
|
from run_backtest import pip_size # noqa: E402
|
|
from strategy_v4 import V4Params, load_v4_cache, market_from_cache, sample_v4, simulate_v4, write_v4_set # noqa: E402
|
|
|
|
|
|
def main() -> None:
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("--trials", type=int, default=3000)
|
|
ap.add_argument("--min-trades", type=int, default=400)
|
|
ap.add_argument("--max-trades", type=int, default=2500)
|
|
ap.add_argument("--seed", type=int, default=42)
|
|
args = ap.parse_args()
|
|
out = LAB
|
|
rng = random.Random(args.seed)
|
|
|
|
if not mt5.initialize():
|
|
raise SystemExit("MT5 init failed")
|
|
try:
|
|
sym = resolve_symbol("EURUSD")
|
|
df = load_bars(sym, mt5.TIMEFRAME_M15, datetime(2020, 1, 1), datetime(2026, 1, 1))
|
|
costs = CostModel.for_symbol(sym)
|
|
pip = pip_size(sym)
|
|
point = float(mt5.symbol_info(sym).point)
|
|
print(f"v4 optimize {sym} M15 trials={args.trials} trades={args.min_trades}-{args.max_trades}")
|
|
cache = load_v4_cache(df)
|
|
|
|
best_profit = None
|
|
best_balanced = None
|
|
target = None
|
|
|
|
for n in range(1, args.trials + 1):
|
|
p = sample_v4(rng)
|
|
md = market_from_cache(cache, p)
|
|
r = simulate_v4(md, sym, p, costs, pip, point)
|
|
|
|
if args.min_trades <= r.total_trades <= args.max_trades and r.net_profit > 0 and r.profit_factor >= 1.05:
|
|
if target is None or r.net_profit > target[0].net_profit:
|
|
target = (r, p)
|
|
print(f" HIT {n}: net=${r.net_profit:,.0f} t={r.total_trades} PF={r.profit_factor:.2f}")
|
|
|
|
if r.net_profit > 0:
|
|
if best_balanced is None or r.total_trades > best_balanced[0].total_trades or (
|
|
r.total_trades == best_balanced[0].total_trades and r.net_profit > best_balanced[0].net_profit
|
|
):
|
|
best_balanced = (r, p)
|
|
|
|
if best_profit is None or r.net_profit > best_profit[0].net_profit:
|
|
best_profit = (r, p)
|
|
|
|
if n % 500 == 0:
|
|
b = best_balanced or best_profit
|
|
print(f" ... {n}/{args.trials} best_bal t={b[0].total_trades} net=${b[0].net_profit:,.0f} hit={'yes' if target else 'no'}")
|
|
|
|
final_r, final_p = target or best_balanced or best_profit
|
|
assert final_r and final_p
|
|
|
|
write_v4_set(final_p, out / "SimpleEMA_optimized.set")
|
|
payload = {
|
|
"version": 4,
|
|
"target_met": target is not None,
|
|
"params": asdict(final_p),
|
|
"metrics": {k: v for k, v in asdict(final_r).items() if k != "trades"},
|
|
}
|
|
(out / "best_params.json").write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
|
|
|
(out / "best_run").mkdir(exist_ok=True)
|
|
rows = [
|
|
{
|
|
"side": t["side"],
|
|
"open_time": df.index[t["open_i"]],
|
|
"close_time": df.index[t["close_i"]],
|
|
"profit": t["profit"],
|
|
"exit_reason": t["exit_reason"],
|
|
}
|
|
for t in final_r.trades
|
|
]
|
|
pd.DataFrame(rows).to_csv(out / "best_run" / "trades.csv", index=False)
|
|
|
|
print(
|
|
f"\n{'TARGET' if target else 'BEST'}: net=${final_r.net_profit:,.2f} "
|
|
f"trades={final_r.total_trades} PF={final_r.profit_factor:.2f} WR={final_r.win_rate:.1f}% DD={final_r.max_drawdown_pct:.1f}%"
|
|
)
|
|
finally:
|
|
mt5.shutdown()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|