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>
This commit is contained in:
co-authored by
Cursor
parent
3f75a08848
commit
605faf5310
@@ -0,0 +1,133 @@
|
||||
"""
|
||||
Sequential United EA audit (main.mq5 strategies from 123.set).
|
||||
|
||||
Usage:
|
||||
python -m cluster_audit.run_united_sequential [trials] [--only ID] [--from ID] [--continue-on-fail]
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import random
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import MetaTrader5 as mt5
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from cluster_audit.run_sequential import run_one_strategy # noqa: E402
|
||||
from cluster_audit.scoring import ( # noqa: E402
|
||||
DEFAULT_TRADES_PER_DAY,
|
||||
min_trades_for_period,
|
||||
period_days,
|
||||
score_label,
|
||||
)
|
||||
from cluster_audit.trace_log import TraceLog # noqa: E402
|
||||
from cluster_audit.united_registry import PERIODS, UNITED_STRATEGIES # noqa: E402
|
||||
|
||||
PRIMARY_PERIOD = "2021-2026"
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser(description="United EA sequential audit (main.mq5)")
|
||||
p.add_argument("trials", nargs="?", type=int, default=80)
|
||||
p.add_argument("--from", dest="from_id", default=None)
|
||||
p.add_argument("--only", default=None)
|
||||
p.add_argument("--trial-every", type=int, default=10)
|
||||
p.add_argument("--trades-per-day", type=float, default=DEFAULT_TRADES_PER_DAY)
|
||||
p.add_argument("--continue-on-fail", action="store_true")
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
log = TraceLog(enabled=True, trial_every=args.trial_every)
|
||||
|
||||
start, end = PERIODS[PRIMARY_PERIOD]
|
||||
days = period_days(datetime.fromisoformat(start), datetime.fromisoformat(end))
|
||||
strategies = UNITED_STRATEGIES
|
||||
if args.only:
|
||||
strategies = [s for s in UNITED_STRATEGIES if s["id"] == args.only]
|
||||
elif args.from_id:
|
||||
found = False
|
||||
filtered = []
|
||||
for s in UNITED_STRATEGIES:
|
||||
if s["id"] == args.from_id:
|
||||
found = True
|
||||
if found:
|
||||
filtered.append(s)
|
||||
strategies = filtered if found else UNITED_STRATEGIES
|
||||
|
||||
log.banner(
|
||||
f"UNITED EA AUDIT - {len(strategies)} strategies | "
|
||||
f">={args.trades_per_day:.1f} trade/day | {args.trials} trials"
|
||||
)
|
||||
|
||||
if not mt5.initialize():
|
||||
log.error(f"MT5 init failed: {mt5.last_error()}")
|
||||
raise SystemExit(1)
|
||||
|
||||
out_dir = Path(__file__).parent / "reports" / "united_sequential"
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
rng = random.Random(42)
|
||||
results: list[dict] = []
|
||||
passed_ids: list[str] = []
|
||||
failed_ids: list[str] = []
|
||||
|
||||
try:
|
||||
for i, spec in enumerate(strategies, 1):
|
||||
r = run_one_strategy(
|
||||
spec, PRIMARY_PERIOD, start, end, args.trials, rng, i, len(strategies),
|
||||
days, args.trades_per_day,
|
||||
)
|
||||
# relocate report to united folder
|
||||
src = Path(__file__).parent / "reports" / "sequential" / f"{spec['id']}_{PRIMARY_PERIOD}.json"
|
||||
dst = out_dir / f"{spec['id']}_{PRIMARY_PERIOD}.json"
|
||||
if src.exists():
|
||||
dst.write_text(src.read_text(encoding="utf-8"), encoding="utf-8")
|
||||
|
||||
results.append(r)
|
||||
if r.get("passed"):
|
||||
passed_ids.append(r["id"])
|
||||
elif "error" not in r:
|
||||
failed_ids.append(r["id"])
|
||||
if not args.continue_on_fail and not args.only:
|
||||
log.warn(f"STOPPED at {r['id']} — resume with --from {r['id']} --continue-on-fail")
|
||||
break
|
||||
|
||||
summary = {
|
||||
"generated": datetime.now().isoformat(),
|
||||
"period_days": days,
|
||||
"passed": passed_ids,
|
||||
"failed": failed_ids,
|
||||
"results": [{k: v for k, v in r.items() if k != "spec"} for r in results],
|
||||
}
|
||||
(out_dir / "united_summary.json").write_text(json.dumps(summary, indent=2), encoding="utf-8")
|
||||
|
||||
try:
|
||||
from cluster_audit.sync_united import main as sync_united
|
||||
sync_united()
|
||||
log.info("main.mq5 + UnitedEA_Optimized.set updated")
|
||||
except Exception as ex:
|
||||
log.warn(f"sync_united skipped: {ex}")
|
||||
|
||||
log.banner(f"PASSED {len(passed_ids)} / FAILED {len(failed_ids)}")
|
||||
for r in results:
|
||||
if not r.get("passed"):
|
||||
continue
|
||||
o = r["optimized"]
|
||||
log.info(
|
||||
f" {r['id']:28} net=${o['net_profit']:9.0f} "
|
||||
f"trades={o['total_trades']:5} pf={o['profit_factor']:.2f}"
|
||||
)
|
||||
for fid in failed_ids:
|
||||
log.warn(f" NEEDS FIX: {fid}")
|
||||
finally:
|
||||
mt5.shutdown()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user