Files
zhutoutoutousan 605faf5310 Prepare source-only public release for develop.
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>
2026-07-02 15:03:43 +02:00

92 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""US30 lot sweep — pick lot balancing return vs equity drawdown."""
from __future__ import annotations
import json
import re
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from cluster_audit.united_mt5_manifest import ALL_ENABLE_KEYS, UNITED_MT5_STRATEGIES
from cluster_audit.united_mt5_runner import BASE_SET, deploy_united, mt5_context, patch_set, run_backtest
OUT = Path(__file__).resolve().parent / "reports" / "us30_lot_dd"
LOTS = [0.03, 0.04, 0.05, 0.06, 0.07, 0.08]
MAX_DD_PCT = 35.0 # reject lots with equity DD above this
def parse_dd_pct(dd: str | None) -> float | None:
if not dd:
return None
m = re.search(r"([\d.]+)\s*%", str(dd).replace(",", ""))
return float(m.group(1)) if m else None
def main() -> None:
sm = {s["id"]: s for s in UNITED_MT5_STRATEGIES}
spec = sm["RS_US30"]
ov_base = {
k: False for k in ALL_ENABLE_KEYS
}
ov_base[spec["enable"]] = True
ov_base.update({
"ORCH_ReferenceBalance": 3000.0,
"ORCH_ScaleLotsByBalance": True,
"GAP_Enable": False,
"OPT_GuardOptimizationMode": True,
"EnableRSIScalpingMU": False,
})
ctx = mt5_context()
deploy_united(ctx["data"], ctx["mt5_path"])
OUT.mkdir(parents=True, exist_ok=True)
trials: list[dict] = []
best_lot, best_sc, best_row = LOTS[0], -1e18, {}
for lot in LOTS:
ov = {**ov_base, spec["lot"]: lot}
tag = str(lot).replace(".", "p")
m = run_backtest(
ctx["data"], ctx["mt5_path"], ctx["login"], ctx["server"],
patch_set(BASE_SET, ov), f"us30_dd_{tag}.set", f"us30_dd_{tag}",
)
dd_pct = parse_dd_pct(m.get("max_drawdown"))
pf = float(m.get("profit_factor") or 0)
sharpe = float(m.get("sharpe") or 0)
profit = float(m.get("net_profit") or 0)
trades = int(m.get("total_trades") or 0)
if not m.get("ready") or trades < 20 or pf < 1.0:
sc = -1e10
elif dd_pct is not None and dd_pct > MAX_DD_PCT:
sc = sharpe * 500 + profit / 2000 - dd_pct * 100
else:
sc = sharpe * 2000 + profit / 500 + pf * 50 - (dd_pct or 0) * 20
row = {"lot": lot, "dd_pct": dd_pct, "score": sc, "metrics": m}
trials.append(row)
print(
f"lot={lot} PF={pf} net={profit} sharpe={sharpe} dd={m.get('max_drawdown')} sc={sc:.0f}",
flush=True,
)
if sc > best_sc:
best_sc, best_lot, best_row = sc, lot, row
# Prefer highest lot under DD cap with PF>=1.1
under_cap = [t for t in trials if t.get("dd_pct") is not None and t["dd_pct"] <= MAX_DD_PCT
and (t["metrics"].get("profit_factor") or 0) >= 1.1]
if under_cap:
best_lot = max(under_cap, key=lambda t: t["lot"])["lot"]
best_row = next(t for t in trials if t["lot"] == best_lot)
result = {"best_lot": best_lot, "max_dd_cap_pct": MAX_DD_PCT, "best": best_row, "trials": trials}
OUT.mkdir(parents=True, exist_ok=True)
(OUT / "us30_lot_dd.json").write_text(json.dumps(result, indent=2), encoding="utf-8")
print(f"BEST lot={best_lot} dd={best_row.get('dd_pct')}% PF={best_row['metrics'].get('profit_factor')}", flush=True)
if __name__ == "__main__":
main()