Spaces:
No application file
No application file
| from __future__ import annotations | |
| import argparse | |
| import re | |
| import shutil | |
| import subprocess | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parents[1] | |
| DEFAULT_TERMINAL_DATA = Path( | |
| r"C:\Users\aksha\AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075" | |
| ) | |
| DEFAULT_METAEDITOR = Path(r"C:\Program Files\MetaTrader 5\MetaEditor64.exe") | |
| EA_SOURCE = ROOT / "mql5" / "Experts" / "AFML" / "AFMLLiveONNXEA.mq5" | |
| PRODUCTION_ROOT = ROOT / "production_models" / "live_mt5" | |
| def parse_args() -> argparse.Namespace: | |
| parser = argparse.ArgumentParser( | |
| description="Deploy AFML ONNX models and live EA into the local MT5 terminal." | |
| ) | |
| parser.add_argument("--terminal-data", default=str(DEFAULT_TERMINAL_DATA)) | |
| parser.add_argument("--metaeditor", default=str(DEFAULT_METAEDITOR)) | |
| parser.add_argument("--timeframe", default="M15") | |
| return parser.parse_args() | |
| def latest_model(symbol: str, timeframe: str) -> Path: | |
| model_dir = PRODUCTION_ROOT / symbol / timeframe | |
| candidates = sorted(model_dir.glob("*.onnx")) | |
| if not candidates: | |
| raise FileNotFoundError(f"No ONNX models found in {model_dir}") | |
| return candidates[-1] | |
| def compile_ea(metaeditor: Path, ea_path: Path, log_path: Path) -> tuple[bool, int, str]: | |
| proc = subprocess.run( | |
| [str(metaeditor), f"/compile:{ea_path}", f"/log:{log_path}"], | |
| capture_output=True, | |
| text=True, | |
| timeout=120, | |
| ) | |
| log_text = log_path.read_text(encoding="utf-16", errors="replace") if log_path.exists() else "" | |
| # MetaEditor may return non-zero even when compile finishes with warnings only. | |
| # Prefer the compiler summary line when available. | |
| match = re.search(r"Result:\s*(\d+)\s+errors", log_text) | |
| if match: | |
| return int(match.group(1)) == 0, proc.returncode, log_text | |
| return proc.returncode == 0, proc.returncode, log_text | |
| def write_set_file(path: Path, model_rel_path: str, timeframe: str, magic_number: int) -> None: | |
| path.write_text( | |
| "\n".join( | |
| [ | |
| f"InpOnnxModelFile={model_rel_path}", | |
| f"InpSignalTimeframe={timeframe}", | |
| "InpEnableTrading=false", | |
| "InpCloseOpposite=true", | |
| "InpFixedLot=0.01", | |
| "InpBuyThreshold=0.55", | |
| "InpSellThreshold=0.45", | |
| "InpStopAtrMultiplier=1.5", | |
| "InpTakeAtrMultiplier=2.0", | |
| "InpMaxSpreadPoints=500", | |
| "InpSlippagePoints=30", | |
| f"InpMagicNumber={magic_number}", | |
| ] | |
| ), | |
| encoding="utf-8", | |
| ) | |
| def main() -> int: | |
| args = parse_args() | |
| terminal_data = Path(args.terminal_data) | |
| metaeditor = Path(args.metaeditor) | |
| experts_dir = terminal_data / "MQL5" / "Experts" / "AFML" | |
| files_dir = terminal_data / "MQL5" / "Files" / "AFML" / "Models" | |
| presets_dir = terminal_data / "MQL5" / "Profiles" / "Tester" | |
| experts_dir.mkdir(parents=True, exist_ok=True) | |
| files_dir.mkdir(parents=True, exist_ok=True) | |
| presets_dir.mkdir(parents=True, exist_ok=True) | |
| deployed = [] | |
| for symbol, stable_name in [("BTCUSD", "btcusd_m15_live.onnx"), ("XAUUSD", "xauusd_m15_live.onnx")]: | |
| source = latest_model(symbol, args.timeframe) | |
| target = files_dir / stable_name | |
| shutil.copy2(source, target) | |
| deployed.append((symbol, source, target)) | |
| ea_target = experts_dir / EA_SOURCE.name | |
| shutil.copy2(EA_SOURCE, ea_target) | |
| write_set_file( | |
| presets_dir / "AFML_BTCUSD_M15.set", | |
| "AFML\\Models\\btcusd_m15_live.onnx", | |
| args.timeframe, | |
| 202605031, | |
| ) | |
| write_set_file( | |
| presets_dir / "AFML_XAUUSD_M15.set", | |
| "AFML\\Models\\xauusd_m15_live.onnx", | |
| args.timeframe, | |
| 202605032, | |
| ) | |
| log_path = ROOT / "diagnostics" / "metaeditor_compile.log" | |
| log_path.parent.mkdir(parents=True, exist_ok=True) | |
| compile_ok = None | |
| compile_code = None | |
| compile_log = "" | |
| if metaeditor.exists(): | |
| compile_ok, compile_code, compile_log = compile_ea(metaeditor, ea_target, log_path) | |
| print(f"EA deployed to: {ea_target}") | |
| for symbol, source, target in deployed: | |
| print(f"{symbol}: {source.name} -> {target}") | |
| print(f"Preset files: {presets_dir / 'AFML_BTCUSD_M15.set'}") | |
| print(f"Preset files: {presets_dir / 'AFML_XAUUSD_M15.set'}") | |
| if compile_code is not None: | |
| print(f"MetaEditor exit code: {compile_code}") | |
| if compile_log: | |
| print("\nCompile log tail:\n") | |
| print(compile_log[-4000:]) | |
| if compile_ok is None: | |
| return 0 | |
| return 0 if compile_ok else 1 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |