MQL5 und Datenverarbeitungspakete integrieren (Teil 7): Entwicklung von Multi-Agenten-Umgebungen für die symbolübergreifende Zusammenarbeit
Inhaltsverzeichnis:
- Einführung
- Überblick und Verständnis des Systems
- Die ersten Schritte
- Alles zusammen auf MQL5
- Live-Demo
- Schlussfolgerung
Einführung
In Teil 6, „Zusammenführung von Markt-Feedback und Modellanpassung“, haben wir uns darauf konzentriert, den Kreislauf zwischen Marktverhalten und Entscheidungslogik zu schließen. Anstatt uns auf statische Signale zu verlassen, haben wir Mechanismen eingeführt, die es dem Handelssystem ermöglichen, seine eigene Leistung zu beobachten, auf sich ändernde Marktbedingungen zu reagieren und seine internen Parameter entsprechend anzupassen. Dazu gehörte die Nutzung von Echtzeit-Feedback wie Handelsergebnissen, Volatilitätsveränderungen und strukturellen Marktveränderungen, um die Interpretation und Ausführung von Signalen innerhalb der MQL5–Python-Hybridarchitektur kontinuierlich zu verfeinern.
In diesem Teil erweitern wir diese Integration, um Multi-Agenten-Umgebungen zu entwickeln, die eine symbolübergreifende Zusammenarbeit ermöglichen. Das Ziel besteht darin, ein Framework zu entwickeln, in dem unabhängige Agenten verschiedene Märkte oder Symbole analysieren, Erkenntnisse austauschen und gemeinsam auf koordinierte Weise Handelsentscheidungen beeinflussen. Dieser Ansatz zielt darauf ab, die symbolübergreifenden Zusammenhänge oder Interdependenzen zwischen Symbolen (wie Währungskorrelationen oder der Risikostimmung) zu nutzen, um die Signalqualität zu verbessern, Fehlsignale zu reduzieren und ein robusteres Handelssystem zu schaffen, das sich an den breiteren Marktkontext anpasst und nicht nur an isolierte Kursbewegungen.
Überblick und Verständnis des Systems
Für den Aufbau von Multi-Agenten-Umgebungen, die eine symbolübergreifende Zusammenarbeit ermöglichen, verlagert sich das Kernparadigma von isolierten, symbolbezogenen Strategien hin zu einer Architektur vernetzter Intelligenz, in der Agenten als spezialisierte Knoten innerhalb eines kollektiven Entscheidungsgraphen agieren. Jeder Agent verkörpert Expertise in einem bestimmten Symbol oder Marktsegment – sei es EURUSD, Gold oder Indizes – und unterhält zugleich dynamische Kommunikationskanäle zu anderen Agenten. Diese Struktur ermöglicht es dem System, die Beziehungen zwischen den Märkten nicht als statische Korrelationen, sondern als dynamische, unmittelbar nutzbare Wege zu erfassen.

Die Funktionslogik der Umgebung basiert auf koordinierter Autonomie: Die Agenten verfügen über lokale Ausführungsbefugnisse, sind jedoch an eine globale Koordinierungsebene angebunden, die systemische Einschränkungen und Synergien verwaltet. Diese Ebene sorgt für die Einhaltung von Zielen auf Portfolioebene – wie beispielsweise das maximale korrelierte Engagement oder Volatilitätsziele über verschiedene Symbole hinweg –, indem sie die Handlungen einzelner Agenten durch gemeinsame Kontextpuffer, gezielte gezieltes Reward Shaping und einfache Konsensmechanismen steuert. Wenn beispielsweise mehrere Agenten widersprüchliche Signale erkennen (z. B. ein Agent, der bei GBP/USD auf steigende Kurse setzt, während ein anderer beim Dollar-Index auf fallende Kurse setzt), kann die Koordinationsschicht eine strukturierte Inferenzsitzung einleiten, bei der Evidenzhierarchien und historische Interaktionsmuster abgewogen werden, bevor eine Richtungstendenz festgelegt wird.

Die ersten Schritte
import MetaTrader5 as mt5 import pandas as pd import numpy as np import json import time import schedule from datetime import datetime, timedelta from flask import Flask, jsonify, request import threading import warnings warnings.filterwarnings('ignore') account = '123456' password = 'YourPass@MT5' server = 'YourBrokersServer-Demo' print("=" * 80) print(" MULTI-AGENT TRADING SYSTEM - LIVE DEPLOYMENT") print("=" * 80)
Zu Beginn richten wir die grundlegende Umgebung ein, die erforderlich ist, um Python mit MetaTrader 5 zu verbinden und Daten, Zeitabläufe sowie die externe Kommunikation zu verwalten. Wir importieren MetaTrader 5 für den Marktzugang, pandas und NumPy für die Datenverarbeitung sowie Flask, um Signale und den Systemstatus über eine schlanke API bereitzustellen. Zusätzliche Module übernehmen die Aufgaben der Zeitplanung, Thread-Verwaltung, Zeitstempelung und JSON-Serialisierung, sodass das Handelssystem kontinuierlich laufen und in Echtzeit sicher mit MQL5 interagieren kann. Schließlich legen die Variablen für Konto, Passwort und Server die Anmeldedaten fest, die zum Herstellen einer Live- oder Demo-Verbindung zu MetaTrader 5 erforderlich sind.
class LiveMultiAgentSystem: def __init__(self, account_number, password, server): self.account_number = account_number self.password = password self.server = server self.symbols = ["XAUUSD", "EURUSD", "GBPUSD", "USDJPY"] self.agents = {} self.positions = {} self.decision_log = [] # Initialize MT5 self.init_mt5() # Initialize agents self.init_agents() print(":/ Live Multi-Agent System Initialized") def init_mt5(self): """Initialize connection to MT5""" if not mt5.initialize(): print("X MT5 initialization failed") print(f" Error: {mt5.last_error()}") return False # Login to account authorized = mt5.login( login=self.account_number, password=self.password, server=self.server ) if authorized: print(f":/ Connected to MT5 Account: {self.account_number}") account_info = mt5.account_info() print(f" Balance: ${account_info.balance:.2f}") print(f" Equity: ${account_info.equity:.2f}") return True else: print(f"X Login failed: {mt5.last_error()}") return False
In dieser Klasse definieren wir die Kernstruktur, die für die Verwaltung einer Live-Handelsumgebung mit mehreren Agenten zuständig ist. Der Konstruktor richtet die Anmeldedaten für das Konto ein, weist eine vordefinierte Liste von Symbolen zu und bereitet Container für Agenten, offene Positionen und den Entscheidungsverlauf vor. Anschließend wird eine Verbindung zu MetaTrader 5 hergestellt, indem das Terminal initialisiert, die Anmeldung beim angegebenen Handelskonto durchgeführt, die Verbindung überprüft und grundlegende Kontoinformationen abgerufen werden. Diese Konfiguration stellt sicher, dass alle Agenten innerhalb einer gemeinsamen, authentifizierten MetaTrader 5-Sitzung arbeiten, und bildet damit die Grundlage für eine koordinierte, in Echtzeit erfolgende und symbolübergreifende Entscheidungsfindung.
def init_agents(self): """Initialize trading agents""" print("\n Initializing Live Agents...") from collections import defaultdict self.agents = { "XAUUSD": { "name": "Gold Agent", "weights": { "trend": 1.5, "momentum": 1.2, "volatility": 1.0 }, "params": { "ema_period": 20, "rsi_period": 14, "atr_period": 14 } }, "CONTEXT": { "name": "Context Agent", "weights": { "usd_strength": 1.3, "risk_sentiment": 1.1 } }, "LIQUIDITY": { "name": "Liquidity Agent", "weights": { "volatility": 1.2, "volume": 1.0 } } } print(f":/ Created {len(self.agents)} agents") def get_live_data(self, symbol, timeframe=mt5.TIMEFRAME_M5, bars=100): """Get live data from MT5""" try: rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, bars) if rates is None or len(rates) == 0: print(f"❌ No data for {symbol}") return None df = pd.DataFrame(rates) df['time'] = pd.to_datetime(df['time'], unit='s') df.set_index('time', inplace=True) # Calculate features df['returns'] = df['close'].pct_change() df['ema_20'] = df['close'].ewm(span=20).mean() df['ema_50'] = df['close'].ewm(span=50).mean() df['rsi'] = self.calculate_rsi(df['close']) df['atr'] = self.calculate_atr(df) return df except Exception as e: print(f"X Error getting data for {symbol}: {e}") return None def calculate_rsi(self, prices, period=14): """Calculate RSI""" delta = prices.diff() gain = (delta.where(delta > 0, 0)).rolling(window=period).mean() loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean() rs = gain / loss rsi = 100 - (100 / (1 + rs)) return rsi def calculate_atr(self, df, period=14): """Calculate ATR""" high = df['high'] low = df['low'] close = df['close'] tr1 = high - low tr2 = abs(high - close.shift()) tr3 = abs(low - close.shift()) tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1) atr = tr.rolling(period).mean() return atr def analyze_symbol(self, symbol): """Agent analysis for a symbol""" data = self.get_live_data(symbol) if data is None or len(data) < 50: return {"action": 0, "confidence": 0.0, "reason": "Insufficient data"} current_price = data['close'].iloc[-1] ema_20 = data['ema_20'].iloc[-1] ema_50 = data['ema_50'].iloc[-1] rsi = data['rsi'].iloc[-1] atr = data['atr'].iloc[-1] # Decision logic bullish = 0 bearish = 0 # Trend if current_price > ema_20 > ema_50: bullish += 2 elif current_price < ema_20 < ema_50: bearish += 2 # Momentum if 40 < rsi < 70: bullish += 1 elif 30 < rsi < 60: bearish += 1 # Volatility (using ATR) if atr / current_price < 0.002: # Low volatility bullish += 0.5 bearish += 0.5 # Make decision if bullish > bearish and bullish >= 2: action = 1 confidence = min(bullish / 4, 0.8) reason = f"Bullish: trend={bullish}, RSI={rsi:.1f}" elif bearish > bullish and bearish >= 2: action = -1 confidence = min(bearish / 4, 0.8) reason = f"Bearish: trend={bearish}, RSI={rsi:.1f}" else: action = 0 confidence = 0.1 reason = f"Neutral: Bull={bullish}, Bear={bearish}" return { "symbol": symbol, "action": action, "confidence": confidence, "reason": reason, "price": current_price, "timestamp": datetime.now().isoformat() }
In diesem Teil initialisieren wir die Live-Handelsagenten, aus denen das Multi-Agenten-System besteht. Jeder Agent ist mit einer bestimmten Rolle und Zuständigkeit definiert, beispielsweise der „Gold-Agent“ für die symbolspezifische Analyse, der „Context-Agent“ für die allgemeinen Marktbedingungen und der „Liquidity-Agent“ für die Erfassung von Volatilität und Handelsvolumen. Diese Agenten sind mit gewichteten Entscheidungsfaktoren und Indikatorparametern konfiguriert, sodass jeder einzelne spezifische Informationen zum Gesamtsystem beitragen kann, anstatt sich auf eine einzige, monolithische Strategie zu verlassen.
Die Methoden zur Datenverarbeitung richten dann eine Echtzeit-Pipeline für Marktdaten aus MetaTrader 5 ein. Die Kursdaten werden direkt aus MetaTrader 5 abgerufen, in einen strukturierten pandas-DataFrame konvertiert und um technische Kennzahlen wie Renditen, exponentielle gleitende Durchschnitte, RSI und ATR ergänzt. Spezielle Hilfsfunktionen berechnen RSI und ATR manuell und gewährleisten so vollständige Transparenz und Flexibilität hinsichtlich des Verhaltens der Indikatoren, während die gesamte Vorverarbeitungslogik zentralisiert und agentenübergreifend wiederverwendbar bleibt.
Die Methode „analyze_symbol“ stellt die zentrale Entscheidungskomponente für jeden symbolspezifischen Agenten dar. Das System wertet Trendstruktur, Dynamik und Volatilität aus, um bullische und bärische Marktbedingungen unabhängig voneinander zu bewerten, und wandelt so Rohmarktdaten in umsetzbare Signale um. Auf der Grundlage dieser Bewertungen gibt der Agent eine Richtungsentscheidung, einen Konfidenzwert und eine Begründung aus, die später an andere Agenten weitergegeben oder an die Ausführungsebene übermittelt werden können, um koordinierte, symbolübergreifende Handelsentscheidungen zu treffen.
def analyze_context(self): """Context agent analysis""" # Get USD strength from major pairs eur_data = self.get_live_data("EURUSD", bars=50) gbp_data = self.get_live_data("GBPUSD", bars=50) jpy_data = self.get_live_data("USDJPY", bars=50) if not all([eur_data is not None, gbp_data is not None, jpy_data is not None]): return {"action": 0, "confidence": 0.0, "reason": "Missing data"} # Calculate USD strength eur_change = (eur_data['close'].iloc[-1] / eur_data['close'].iloc[-20] - 1) gbp_change = (gbp_data['close'].iloc[-1] / gbp_data['close'].iloc[-20] - 1) jpy_change = (jpy_data['close'].iloc[-1] / jpy_data['close'].iloc[-20] - 1) # EURUSD and GBPUSD down = USD strong, USDJPY up = USD strong usd_strength = (-eur_change - gbp_change + jpy_change) / 3 if usd_strength > 0.01: action = -1 # USD strong → bearish for gold confidence = min(abs(usd_strength) * 10, 0.7) reason = f"USD Strong: {usd_strength:.2%}" elif usd_strength < -0.01: action = 1 # USD weak → bullish for gold confidence = min(abs(usd_strength) * 10, 0.7) reason = f"USD Weak: {usd_strength:.2%}" else: action = 0 confidence = 0.1 reason = "USD Neutral" return { "agent": "CONTEXT", "action": action, "confidence": confidence, "reason": reason, "usd_strength": usd_strength } def run_decision_pipeline(self, target_symbol="XAUUSD"): """Run complete decision pipeline""" print(f"\nO- {datetime.now().strftime('%H:%M:%S')} - Analyzing market...") # Collect agent decisions decisions = [] # Symbol agents for symbol in self.symbols: if symbol == target_symbol: decision = self.analyze_symbol(symbol) decisions.append(decision) print(f" {symbol}: {decision['action']} ({decision['confidence']:.1%}) - {decision['reason']}") # Context agent context_decision = self.analyze_context() decisions.append(context_decision) print(f" CONTEXT: {context_decision['action']} ({context_decision['confidence']:.1%}) - {context_decision['reason']}") # Calculate consensus weighted_sum = 0 total_weight = 0 weights = { "XAUUSD": 1.5, "EURUSD": 1.0, "GBPUSD": 1.0, "USDJPY": 1.0, "CONTEXT": 1.3 } for decision in decisions: symbol = decision.get('symbol', decision.get('agent', 'UNKNOWN')) weight = weights.get(symbol, 1.0) weighted_sum += decision['action'] * decision['confidence'] * weight total_weight += weight * decision['confidence'] # Make final decision if total_weight == 0: final_action = 0 confidence = 0.0 reason = "No consensus" else: consensus = weighted_sum / total_weight if consensus > 0.3: final_action = 1 confidence = min(abs(consensus), 0.9) reason = f"Bullish consensus: {consensus:.2f}" elif consensus < -0.3: final_action = -1 confidence = min(abs(consensus), 0.9) reason = f"Bearish consensus: {consensus:.2f}" else: final_action = 0 confidence = 0.2 reason = f"Neutral: {consensus:.2f}" final_decision = { "timestamp": datetime.now().isoformat(), "symbol": target_symbol, "action": final_action, "confidence": confidence, "reason": reason, "consensus": consensus if total_weight > 0 else 0, "agent_decisions": decisions } self.decision_log.append(final_decision) # Save decision to file for MQL5 to read self.save_decision(final_decision) # Execute trade if confidence is high enough if confidence > 0.5: self.execute_trade(final_decision) return final_decision def write_signal(signal): with open("multi_agent_signal.json", "w") as f: json.dump(signal, f, indent=2) def save_decision(self, decision): """Save decision to JSON file for MQL5""" try: with open('multi_agent_signal.json', 'w') as f: json.dump(decision, f, indent=2) print(f"💾 Signal saved to file") except Exception as e: print(f"X Error saving signal: {e}") def execute_trade(self, decision): """Execute trade via MT5""" symbol = decision['symbol'] action = decision['action'] confidence = decision['confidence'] # Check existing positions positions = mt5.positions_get(symbol=symbol) if positions: print(f"!! Existing position found for {symbol}") # Check if we should close it current_position = positions[0] if (action == 1 and current_position.type == 1) or (action == -1 and current_position.type == 0): print(f" Same direction, considering adding to position") return else: print(f" Opposite direction, closing position first") self.close_position(symbol) # Prepare trade request symbol_info = mt5.symbol_info(symbol) if symbol_info is None: print(f"X Symbol {symbol} not found") return point = symbol_info.point price = mt5.symbol_info_tick(symbol).ask if action == 1 else mt5.symbol_info_tick(symbol).bid # Calculate position size based on risk account_info = mt5.account_info() balance = account_info.balance risk_amount = balance * 0.01 * confidence # 1% risk adjusted by confidence # Calculate stop loss based on ATR data = self.get_live_data(symbol, bars=50) if data is not None and 'atr' in data.columns: atr = data['atr'].iloc[-1] stop_distance = atr * 1.5 else: stop_distance = price * 0.01 # 1% stop # Calculate volume volume = risk_amount / stop_distance volume = round(volume, 2) # Round to 2 decimal places # Validate volume min_volume = symbol_info.volume_min max_volume = symbol_info.volume_max volume = max(min_volume, min(volume, max_volume)) # Prepare order request = { "action": mt5.TRADE_ACTION_DEAL, "symbol": symbol, "volume": volume, "type": mt5.ORDER_TYPE_BUY if action == 1 else mt5.ORDER_TYPE_SELL, "price": price, "sl": price - stop_distance if action == 1 else price + stop_distance, "tp": price + stop_distance * 2 if action == 1 else price - stop_distance * 2, "deviation": 10, "magic": 234000, "comment": f"Multi-Agent: {decision['reason'][:30]}", "type_time": mt5.ORDER_TIME_GTC, "type_filling": mt5.ORDER_FILLING_IOC, } # Send order result = mt5.order_send(request) if result.retcode == mt5.TRADE_RETCODE_DONE: print(f":/ Trade executed: {'BUY' if action == 1 else 'SELL'} {volume} {symbol} at {price}") print(f" SL: {result.request.sl:.5f}, TP: {result.request.tp:.5f}") print(f" Order ID: {result.order}") else: print(f"X Trade failed: {result.comment}") def close_position(self, symbol): """Close all positions for a symbol""" positions = mt5.positions_get(symbol=symbol) if not positions: return for position in positions: tick = mt5.symbol_info_tick(symbol) request = { "action": mt5.TRADE_ACTION_DEAL, "symbol": symbol, "volume": position.volume, "type": mt5.ORDER_TYPE_BUY if position.type == 1 else mt5.ORDER_TYPE_SELL, "position": position.ticket, "price": tick.ask if position.type == 1 else tick.bid, "deviation": 10, "magic": 234000, "comment": "Close by Multi-Agent", "type_time": mt5.ORDER_TIME_GTC, "type_filling": mt5.ORDER_FILLING_IOC, } result = mt5.order_send(request) if result.retcode == mt5.TRADE_RETCODE_DONE: print(f":/ Position {position.ticket} closed") else: print(f"X Failed to close position {position.ticket}: {result.comment}") def monitor_positions(self): """Monitor and manage open positions""" positions = mt5.positions_get() for position in positions: symbol = position.symbol current_price = mt5.symbol_info_tick(symbol).bid # Check stop loss and take profit # These are managed by MT5 automatically, but we can log them unrealized_pnl = position.profit print(f" {symbol}: {position.type} {position.volume} | P&L: ${unrealized_pnl:.2f}") def run_continuously(self, interval_minutes=5): """Run the system continuously""" print(f"\n<=> Starting continuous monitoring every {interval_minutes} minutes...") def job(): try: # Run analysis decision = self.run_decision_pipeline("XAUUSD") # Monitor positions self.monitor_positions() # Log status print(f" Decision: {decision['action']} ({decision['confidence']:.1%}) - {decision['reason']}") except Exception as e: print(f"X Error in scheduled job: {e}") # Schedule the job schedule.every(interval_minutes).minutes.do(job) # Run immediately first time job() # Keep running try: while True: schedule.run_pending() time.sleep(1) except KeyboardInterrupt: print("\n!!! Stopping system...") mt5.shutdown()
In diesem Teil des Codes wird die Kontextanalyse-Ebene vorgestellt, die eher als übergeordneter Intelligenzagent denn als symbolspezifischer Händler fungiert. Die Methode „analyze_context“ bewertet die allgemeine Stärke des US-Dollars anhand der Beobachtung korrelierter Hauptwährungspaare wie EURUSD, GBPUSD und USDJPY. Durch die Messung der jüngsten prozentualen Veränderungen in diesen Märkten und deren Zusammenfassung zu einem einzigen Indikator für die Stärke des US-Dollars leitet das System eine Tendenz auf Makroebene ab, die Entscheidungen in Bezug auf Gold (XAUUSD) beeinflusst. Dadurch kann die Strategie marktübergreifende Zusammenhänge berücksichtigen, anstatt sich ausschließlich auf isolierte Kursbewegungen zu stützen.
Die Methode „run_decision_pipeline“ koordiniert anschließend den gesamten Multi-Agenten-Workflow. Sie sammelt Entscheidungen des symbolspezifischen Agenten (XAUUSD) und des Kontextagenten, protokolliert deren Ausgaben und aggregiert diese mithilfe eines gewichteten Konsensmodells. Jeder Agent trägt auf der Grundlage von Konfidenzwerten und vordefinierten Wichtigkeitsgewichten zur endgültigen Entscheidung bei, wodurch sichergestellt wird, dass stärkere oder zuverlässigere Signale einen größeren Einfluss haben. Dieser Konsensmechanismus wandelt mehrere unabhängige Meinungen in eine einzige, schlüssige Handelsentscheidung mit einem zugehörigen Konfidenzwert um.
Sobald eine endgültige Entscheidung getroffen wurde, wird diese vom System im Sinne der Transparenz und Nachvollziehbarkeit gespeichert. Die Entscheidung wird einem internen Protokoll hinzugefügt und in eine JSON-Datei geschrieben, die extern von einem MQL5-Expert Advisor genutzt werden kann. Überschreitet die Konfidenz einen vordefinierten Schwellenwert, leitet das System die Handelsausführung ein und verbindet so analytische Erkenntnisse mit dem tatsächlichen Marktgeschehen. Dieses Design trennt die Erzeugung, Speicherung und Ausführung von Entscheidungen klar voneinander, sorgt dabei aber für eine enge Synchronisation zwischen diesen Prozessen.
Die übrigen Methoden dienen der direkten Abwicklung von Live-Handelsgeschäften und der Positionsverwaltung über MetaTrader 5. Die Handelsgrößen werden dynamisch auf der Grundlage des Kontostands, des konfidenzadjustierten Risikos und der auf der ATR basierenden Stop-Abstände festgelegt, wodurch eine konsistente Risikokontrolle gewährleistet wird. Das System unterstützt zudem das Schließen gegenläufiger Positionen, die Überwachung offener Trades und den kontinuierlichen Betrieb in festgelegten Intervallen. Zusammen bilden diese Komponenten einen vollständigen adaptiven Handelszyklus, der den Marktkontext analysiert, einen Konsens zwischen mehreren Agenten herstellt, disziplinierte Handelsgeschäfte ausführt und die Systemleistung kontinuierlich in Echtzeit überwacht.
# ================================================ # FLASK API FOR EXTERNAL CONTROL # ================================================ from flask import Flask, request, jsonify import threading import time import json import MetaTrader5 as mt5 app = Flask(__name__) trading_system = None system_thread = None system_running = False system_lock = threading.Lock() @app.route('/') def home(): return jsonify({ "status": "Multi-Agent Trading System API", "version": "1.1", "running": system_running }) @app.route('/start', methods=['POST']) def start_system(): global trading_system, system_thread, system_running global account, password, serve with system_lock: if system_running: return jsonify({"error": "System already running"}), 400 data = request.json or {} account = data.get('account') password = data.get('password') server = data.get('server', 'SpaceMarkets-Live') if not all([account, password]): return jsonify({"error": "Missing account or password"}), 400 # Create system trading_system = LiveMultiAgentSystem(account, password, server) system_running = True # Background execution loop def runner(): while system_running: try: trading_system.run_once() time.sleep(5) except Exception as e: print("X System error:", e) time.sleep(5) system_thread = threading.Thread(target=runner, daemon=True) system_thread.start() return jsonify({ "status": "System started", "account": account, "server": server }) @app.route('/stop', methods=['POST']) def stop_system(): global system_running with system_lock: if not system_running: return jsonify({"error": "System not running"}), 400 system_running = False return jsonify({"status": "System stopping"}) @app.route('/status', methods=['GET']) def get_status(): if not system_running or trading_system is None: return jsonify({"status": "stopped"}) last_decision = ( trading_system.decision_log[-1] if trading_system.decision_log else None ) return jsonify({ "status": "running", "last_decision": last_decision, "total_decisions": len(trading_system.decision_log) })
Dieser Teil des Codes richtet eine sichere und kontrollierte Ausführungsumgebung für das Handelssystem ein, indem die Flask-Anwendung, gemeinsam genutzte Zustandsvariablen und Synchronisationsmechanismen initialisiert werden. Es werden globale Flags und Sperren eingeführt, um zu verfolgen, ob das System läuft, und um Race-Conditions beim Starten oder Beenden der Handels-Engine zu verhindern. Der Root-Endpunkt (/) dient als einfacher Status-/Health-Check, der die aktuelle API-Version zurückgibt und angibt, ob das Multi-Agenten-System aktiv läuft. Dies ist sowohl für die Fehlersuche als auch für die externe Überwachung nützlich.
Die übrigen Flask-Routen ermöglichen eine externe Steuerung des Lebenszyklus des Handelssystems. Der Endpunkt /start initialisiert die Multi-Agent-Engine sicher anhand der in der Anfrage angegebenen Anmeldedaten und startet sie in einem Hintergrund-Thread für den Dauerbetrieb, während /stop die Ausführung ordnungsgemäß beendet, ohne die API selbst zu beenden. Der Endpunkt /status stellt den Systemstatus in Echtzeit bereit, einschließlich der zuletzt getroffenen Entscheidung und der Gesamtzahl der getroffenen Entscheidungen, sodass MQL5- oder andere Clients auf kontrollierte, nicht blockierende Weise Abfragen durchführen und sich mit der Python-basierten Multi-Agenten-Logik synchronisieren können.
Alles zusammen auf MQL5
//+------------------------------------------------------------------+ //| Multi-Agents.mq5 | //| GIT under Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com/en/users/johnhlomohang/ | //+------------------------------------------------------------------+ #property copyright "GIT under Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/johnhlomohang/" #property version "1.00" #include <Trade/Trade.mqh> #include "Json.mqh" // Include our JSON parser //--- Input parameters input string PythonServer = "http://127.0.0.1:5000"; // Python server address input double RiskPercent = 1.0; // Risk percentage input int SignalCheckInterval = 30; // Check signal every N seconds input bool EnableTrading = true; // Enable trading input string CommentText = "Multi-Agent"; // Order comment //--- Global variables CTrade trade; datetime lastSignalCheck = 0; string currentSignal = ""; double lastSignalPrice = 0; int magicNumber = 234000; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Set magic number for order identification trade.SetExpertMagicNumber(magicNumber); // Set asynchronous mode trade.SetAsyncMode(true); // Print initialization message Print("Multi-Agent EA Initialized - Simple JSON Version"); Print("Python Server: ", PythonServer); Print("Risk: ", RiskPercent, "%"); Print("Magic Number: ", magicNumber); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Print("Multi-Agent EA Deinitialized"); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Check if it's time to check for new signals if(TimeCurrent() - lastSignalCheck >= SignalCheckInterval) { CheckSignal(); lastSignalCheck = TimeCurrent(); } // Manage existing positions ManagePositions(); } //+------------------------------------------------------------------+ //| Check signal from Python server | //+------------------------------------------------------------------+ void CheckSignal() { string url = PythonServer + "/signal"; string payload = "{}"; // empty body is fine char data[]; StringToCharArray(payload, data); char result[]; string headers = "Content-Type: application/json\r\n"; string result_headers; int res = WebRequest( "POST", url, headers, 5000, data, result, result_headers ); if(res != 200) { Print("HTTP request failed: ", res); return; } Print("LastError=", GetLastError()); if(res == 200) // HTTP OK { string jsonStr = CharArrayToString(result); Print("Received JSON: ", jsonStr); // Parse using our simple JSON parser string symbol = CJson::ParseString(jsonStr, "symbol", ""); int action = CJson::ParseInteger(jsonStr, "action", 0); double confidence = CJson::ParseNumber(jsonStr, "confidence", 0); string reason = CJson::ParseString(jsonStr, "reason", ""); double price = CJson::ParseNumber(jsonStr, "price", 0); Print("Parsed Signal: Symbol=", symbol, " Action=", action, " Confidence=", confidence, " Reason=", reason); // Only trade if confidence is high enough if(confidence >= 0.5 && EnableTrading) { // Check if we should trade this symbol if(symbol == _Symbol || symbol == "") { ExecuteSignal(action, confidence, reason, price); } } // Update current signal display currentSignal = reason; } else if(res == -1) { Print("WebRequest failed. Error: ", GetLastError()); Print("Make sure to add URL to allowed list: ", url); // To add URL to allowed list in MT5: // 1. Go to Tools -> Options -> Expert Advisors // 2. Click "Add" under "Allowed URLs" // 3. Add: http://localhost:5000 } else { Print("HTTP request failed: ", res); } }
Nun definieren wir in MQL5 einen MetaTrader 5-Expert Advisor, der als Brücke zwischen dem Handelsterminal und einem externen, auf Python basierenden Multi-Agenten-System fungiert. Wir initialisieren Handelsparameter wie Risiko, Signalabfragehäufigkeit und eine Magic Number zur Auftragsverfolgung und konfigurieren anschließend das CTrade-Objekt für die asynchrone Ausführung. Bei jedem Markt-Tick sendet der EA in regelmäßigen Abständen eine HTTP-Anfrage an den Python-Server, um ein Handelssignal abzurufen, wertet die zurückgegebene JSON-Nutzlast aus, um Felder wie Symbol, Aktion, Konfidenz und Kurs zu extrahieren, und wendet eine grundlegende Filterlogik an, um sicherzustellen, dass Trades nur dann berücksichtigt werden, wenn die Konfidenz ausreichend hoch ist und der Handel aktiviert ist.
Der verbleibende Teil der Logik konzentriert sich auf das Laufzeitverhalten und die Robustheit des Systems. Der EA verwaltet kontinuierlich bestehende Positionen, während er auf neue Signale wartet, protokolliert wichtige Ereignisse im Sinne der Transparenz und verfügt über Sicherheitsvorkehrungen für fehlgeschlagene Webanfragen oder falsch konfigurierte Berechtigungen. Indem die Signalerzeugung auf den Python-Server ausgelagert und die Ausführungslogik innerhalb von MetaTrader 5 schlank gehalten wird, trennt dieses Design die Entscheidungsfindung sauber von der Handelsausführung, sodass sich die Multi-Agenten-Intelligenz unabhängig vom Handelsterminal weiterentwickeln kann.
//+------------------------------------------------------------------+ //| Execute trading signal | //+------------------------------------------------------------------+ void ExecuteSignal(int action, double confidence, string reason, double signalPrice = 0) { // Get current price double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double price = (action == 1) ? ask : bid; // Use signal price if provided and valid if(signalPrice > 0 && MathAbs(signalPrice - price) / price < 0.05) // Within 5% { price = signalPrice; } // Check if price has changed significantly if(MathAbs(price - lastSignalPrice) / price < 0.001) // Less than 0.1% change { Print("Price hasn't changed enough, skipping trade"); return; } lastSignalPrice = price; // Close opposite positions first if(action == 1) // Buy signal { CloseSellPositions(); } else if(action == -1) // Sell signal { CloseBuyPositions(); } // Calculate position size double volume = CalculateVolume(confidence); if(volume <= 0) { Print("Volume too small, skipping trade"); return; } // Calculate stop loss and take profit based on ATR double atr = CalculateATR(14); double stopLoss = 0; double takeProfit = 0; if(action == 1) // Buy { stopLoss = price - (atr * 1.5); takeProfit = price + (atr * 3.0); } else // Sell { stopLoss = price + (atr * 1.5); takeProfit = price - (atr * 3.0); } // Normalize SL/TP to tick size stopLoss = NormalizePrice(stopLoss); takeProfit = NormalizePrice(takeProfit); // Place order if(action == 1) { if(trade.Buy(volume, _Symbol, price, stopLoss, takeProfit, reason)) { Print("BUY order placed: ", DoubleToString(volume, 2), " ", _Symbol, " at ", DoubleToString(price, 5)); Print("SL: ", DoubleToString(stopLoss, 5), " TP: ", DoubleToString(takeProfit, 5)); } else { Print("Failed to place BUY order: ", trade.ResultRetcodeDescription()); } } else if(action == -1) { if(trade.Sell(volume, _Symbol, price, stopLoss, takeProfit, reason)) { Print("SELL order placed: ", DoubleToString(volume, 2), " ", _Symbol, " at ", DoubleToString(price, 5)); Print("SL: ", DoubleToString(stopLoss, 5), " TP: ", DoubleToString(takeProfit, 5)); } else { Print("Failed to place SELL order: ", trade.ResultRetcodeDescription()); } } } //+------------------------------------------------------------------+ //| Calculate ATR | //+------------------------------------------------------------------+ double CalculateATR(int period) { double atr = 0; // Try to get ATR from indicator int atrHandle = iATR(_Symbol, PERIOD_M5, period); if(atrHandle != INVALID_HANDLE) { double atrArray[]; if(CopyBuffer(atrHandle, 0, 0, 1, atrArray) > 0) { atr = atrArray[0]; } IndicatorRelease(atrHandle); } if(atr <= 0) { // Fallback: use percentage of price atr = SymbolInfoDouble(_Symbol, SYMBOL_BID) * 0.002; // 0.2% } return atr; } //+------------------------------------------------------------------+ //| Normalize price to tick size | //+------------------------------------------------------------------+ double NormalizePrice(double price) { double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); if(tickSize > 0) { price = MathRound(price / tickSize) * tickSize; } return NormalizeDouble(price, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS)); }
Hier implementieren wir eine vollständige Pipeline zur Handelsausführung, die ein Richtungssignal aufnimmt und es in eine kontrollierte, risikobewusste Marktorder umwandelt. Die Funktion „ExecuteSignal“ ermittelt zunächst den korrekten Ausführungspreis anhand des aktuellen Geld- oder Briefkurses, überschreibt diesen optional durch einen gültigen externen Signalkurs und filtert anschließend überflüssige Trades heraus, indem sie sicherstellt, dass sich der Kurs seit dem letzten Signal deutlich bewegt hat. Das System sorgt für eine konsequente Richtungsdisziplin, indem es zunächst alle gegensätzlichen Positionen schließt, die Positionsgröße dynamisch anhand der Signalkonfidenz berechnet und adaptive Stop-Loss- und Take-Profit-Niveaus unter Verwendung des ATR-Werts ableitet, um die aktuelle Marktvolatilität widerzuspiegeln.
Vor der Auftragserteilung werden alle Preiswerte auf die Tick-Größe und die Genauigkeit des Wertpapiers normiert, um die Einhaltung der Broker-Vorgaben sicherzustellen. Anschließend werden Kauf- oder Verkaufsaufträge übermittelt und zur Transparenz vollständig protokolliert. Die unterstützenden Funktionen übernehmen die Volatilitätsmessung mithilfe des ATR-Indikators mit einem sicheren Ausweichmechanismus sowie eine präzise Kursnormalisierung, wodurch die Ausführungslogik robust, anpassungsfähig und widerstandsfähig gegenüber häufigen Handels- und Brokerfehlern wird.
//+------------------------------------------------------------------+ //| Json.mqh | //| GIT under Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com/en/users/johnhlomohang/ | //+------------------------------------------------------------------+ #property copyright "GIT under Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/johnhlomohang/" #ifndef JSON_MQH #define JSON_MQH class CJson { private: string m_json; string ExtractValue(string key) { int keyPos = StringFind(m_json, "\"" + key + "\""); if(keyPos == -1) return ""; int colonPos = StringFind(m_json, ":", keyPos); if(colonPos == -1) return ""; // Find the start of the value int valueStart = colonPos + 1; while(valueStart < StringLen(m_json) && (m_json[valueStart] == ' ' || m_json[valueStart] == '\t' || m_json[valueStart] == '\n' || m_json[valueStart] == '\r')) { valueStart++; } if(valueStart >= StringLen(m_json)) return ""; char firstChar = m_json[valueStart]; // String value if(firstChar == '\"') { int endQuote = StringFind(m_json, "\"", valueStart + 1); if(endQuote == -1) return ""; return StringSubstr(m_json, valueStart + 1, endQuote - valueStart - 1); } // Number or boolean value int valueEnd = valueStart; while(valueEnd < StringLen(m_json) && ((m_json[valueEnd] >= '0' && m_json[valueEnd] <= '9') || m_json[valueEnd] == '-' || m_json[valueEnd] == '.' || m_json[valueEnd] == 'e' || m_json[valueEnd] == 'E' || m_json[valueEnd] == 't' || m_json[valueEnd] == 'r' || m_json[valueEnd] == 'u' || m_json[valueEnd] == 'e' || m_json[valueEnd] == 'f' || m_json[valueEnd] == 'a' || m_json[valueEnd] == 'l' || m_json[valueEnd] == 's' || m_json[valueEnd] == 'n' || m_json[valueEnd] == 'u' || m_json[valueEnd] == 'l')) { valueEnd++; } return StringSubstr(m_json, valueStart, valueEnd - valueStart); } public: void SetJson(string json) { m_json = json; } string GetString(string key, string defaultValue = "") { string value = ExtractValue(key); if(value == "") return defaultValue; // Check if it's actually a string (starts with quote) if(StringGetCharacter(value, 0) == '\"') { return StringSubstr(value, 1, StringLen(value) - 2); } return value; } double GetNumber(string key, double defaultValue = 0) { string value = ExtractValue(key); if(value == "") return defaultValue; // Check for true/false if(value == "true") return 1; if(value == "false") return 0; return StringToDouble(value); } int GetInteger(string key, int defaultValue = 0) { string value = ExtractValue(key); if(value == "") return defaultValue; // Check for true/false if(value == "true") return 1; if(value == "false") return 0; return (int)StringToInteger(value); } bool GetBool(string key, bool defaultValue = false) { string value = ExtractValue(key); if(value == "") return defaultValue; if(value == "true") return true; if(value == "false") return false; if(value == "1") return true; if(value == "0") return false; return defaultValue; } // Static helper methods for quick parsing static string ParseString(string json, string key, string defaultValue = "") { CJson parser; parser.SetJson(json); return parser.GetString(key, defaultValue); } static double ParseNumber(string json, string key, double defaultValue = 0) { CJson parser; parser.SetJson(json); return parser.GetNumber(key, defaultValue); } static int ParseInteger(string json, string key, int defaultValue = 0) { CJson parser; parser.SetJson(json); return parser.GetInteger(key, defaultValue); } static bool ParseBool(string json, string key, bool defaultValue = false) { CJson parser; parser.SetJson(json); return parser.GetBool(key, defaultValue); } }; #endif
In der JSON-Datei definieren wir einen schlanken, eigenständigen JSON-Parser für MQL5, der die sichere Extraktion einfacher Schlüssel-Wert-Paare ermöglicht, ohne auf externe Bibliotheken zurückgreifen zu müssen. Die Klasse „CJson“ speichert eine JSON-Zeichenkette im Rohformat und nutzt einen kontrollierten Ansatz zum Durchsuchen der Zeichenkette, um Schlüssel zu finden, Leerzeichen zu überspringen und Zeichenketten, sowie numerische und boolesche Werte korrekt zu interpretieren. Es stellt typisierte Zugriffsmethoden (GetString, GetNumber, GetInteger und GetBool) bereit, die bei fehlenden oder fehlerhaften Schlüsseln nahtlos auf Standardwerte zurückgreifen und so die Robustheit in Live-Handelsumgebungen gewährleisten. Um die Nutzung noch weiter zu vereinfachen, ermöglichen statische Hilfsmethoden das Parsen direkt aus einer JSON-Nutzlast in nur einer Zeile. Damit eignet sich diese Klasse ideal für die Verarbeitung von API-Antworten, Konfigurationsmeldungen oder die Kommunikation zwischen Prozessen, bei denen Leistung, Sicherheit und ein Minimum an Abhängigkeiten entscheidend sind.
Live-Demo
Unten ist zu sehen, dass der Flask-Server erfolgreich initialisiert wurde und auf dem localhost am Port 5000 läuft und Signale von Jupyter Lab an MetaTrader 5 weiterleitet.


Schlussfolgerung
Zusammenfassend lässt sich sagen, dass wir eine Multi-Agenten-Handelsumgebung entwickelt haben, in der unabhängige Agenten über einen gemeinsamen Python-Dienst kommunizieren und über mehrere Symbole hinweg zusammenarbeiten, anstatt isoliert voneinander zu agieren. Jeder Agent erzeugt strukturierte Signale, die über JSON übertragen, innerhalb des MQL5 Expert Advisors sicher analysiert und anhand von Konfidenzschwellenwerten, Risikokontrollen und Symbolvalidierung ausgewertet werden. Diese Architektur ermöglicht eine symbolübergreifende Marktwahrnehmung, eine koordinierte Entscheidungsfindung und eine rückkopplungsgesteuerte Anpassung, wodurch Marktinformationen von verschiedenen Symbolen effektiv in einer einzigen, einheitlichen Ausführungsschicht innerhalb von MetaTrader 5 zusammengeführt werden.
Zusammenfassend lässt sich sagen, dass dieser Ansatz den Handel erheblich verbessern kann, indem er den „Tunnelblick“ verringert und es Strategien ermöglicht, vom breiteren Marktkontext und dem kollektiven Verhalten bei miteinander korrelierten Vermögenswerten zu profitieren. Indem sie die Entscheidungsfindung an kooperierende Agenten auslagern und gleichzeitig die Ausführung, das Risikomanagement und die Validierung streng innerhalb der EA kontrollieren, können Händler ein System schaffen, das flexibler, skalierbarer und leichter weiterzuentwickeln ist. Das Ergebnis ist eine klarere Trennung von Logik und Ausführung, schnellere Testmöglichkeiten und ein intelligenterer Handelsworkflow, der sich natürlicher an veränderte Marktbedingungen anpasst.
| Dateiname | Beschreibung der Datei |
|---|---|
| Multi Agents.mq5 | Der Haupt-Expert-Advisor, der eine Verbindung zum Python-Server herstellt, Multi-Agent-Handelssignale über JSON empfängt, das Risiko verwaltet und Trades in MetaTrader 5 ausführt. |
| Json.mq5 | Ein schlanker, maßgeschneiderter JSON-Parser, der vom EA verwendet wird, um Zeichenfolgen, Zahlen und boolesche Werte sicher aus Python-generierten Signalantworten zu extrahieren. |
| MultiAgentsLab.ipynb | Das Python-Notebook, das die Multi-Agent-Umgebung aufbaut und ausführt, symbolübergreifende Handelssignale generiert und diese über eine lokale API an MetaTrader übermittelt. |
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/21115
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
Behebung von Barrierefreiheitsproblemen bei MQL5-Handelswerkzeugen (Teil I): Hinzufügen kontextbezogener Sprachnachrichten zu MQL5-Indikatoren
Eine alternative Log-datei mit der Verwendung der HTML und CSS
MQL5-Handelswerkzeuge (Teil 17): Vektorbasierte abgerundete Rechtecke und Dreiecke
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.