English
preview
Entwicklung des Price Action Analysis Toolkit (Teil 36): Direkter Python-Zugang zu MetaTrader 5 Market Streams freischalten

Entwicklung des Price Action Analysis Toolkit (Teil 36): Direkter Python-Zugang zu MetaTrader 5 Market Streams freischalten

MetaTrader 5Handelssysteme |
32 0
Christian Benjamin
Christian Benjamin

Inhalt


Einführung

In unserem vorigen Artikel haben wir gezeigt, wie ein einfaches MQL5-Skript historische Balken in Python übertragen, Merkmale entwickeln, ein maschinelles Lernmodell trainieren und dann Signale zur Ausführung an MetaTrader zurücksenden kann – ohne manuelle CSV-Exporte, Excel-basierte Analysen und Versionskontrollprobleme. Händler erhielten eine Ende-zu-Ende-Pipeline, die rohe Minutenbar-Daten in statistisch gesteuerte Einstiegspunkte umwandelte, komplett mit dynamisch berechneten Levels von Stop-Loss (SL) und Take-Profit (TP).

Mit diesem System wurden drei wichtige Probleme des algorithmischen Handels gelöst:

  1. Fragmentierung von Daten: Das Kopieren und Einfügen von CSV-Dateien oder der Umgang mit komplexen Tabellenkalkulationsformeln entfällt – Ihr MetaTrader 5 Chart kommuniziert direkt mit Python.
  2. Verspätete Einsicht: Die Automatisierung des Feature-Engineerings und der Modellableitung ermöglichte Echtzeitsignale und damit den Übergang vom reaktiven zum proaktiven Handel auf der Grundlage von Live-Daten.
  3. Inkonsistentes Risikomanagement: Durch die Einbeziehung von ATR-basierten SL/TP sowohl in Backtests als auch in den Live-Handel wurde sichergestellt, dass alle Handelsgeschäften volatilitätsangepassten Regeln folgten und Ihr Vorteil erhalten blieb.

Wenn man sich jedoch auf einen Expert Advisor (EA) verlässt, um Daten in Python einzuspeisen, kann dies zu Latenzzeiten und Komplexität führen. Die neue Version nutzt die Fähigkeit von Python, als Client von MetaTrader 5 zu agieren und die MetaTrader 5-Bibliothek zum direkten Abrufen und Aktualisieren von Daten zu nutzen. Dieser Ansatz macht das Warten auf einen EA-Timer überflüssig; Python kann Daten bei Bedarf einlesen, effizient in einen Parquet-Speicher schreiben und intensive Berechnungen asynchron ausführen.

Aufbauend auf dieser Grundlage bietet unser erweitertes Python-MQL5-Hybrid-Tool noch mehr Möglichkeiten:

  • Python-Seite: Echtzeit-MetaTrader 5-Dateneingabe über die systemeigene Bibliothek, fortschrittliches Feature-Engineering (z. B. Spike-Z-Scores, MACD-Differenzen, ATR-Bänder, Prophet-Trend-Deltas) und eine TimeSeries-fähige Gradient-Boosting-Pipeline, die auf rollenden Fenstern retrainiert – alles über eine leichtgewichtige Flask-API zugänglich.
  • MQL5 Seite: Ein robuster REST-Polling EA mit Wiederholungslogik, ein On-Chart-Dashboard, das Signale, Konfidenzniveaus und den Verbindungsstatus anzeigt, Pfeilmarkierungen für Ein- und Ausstiege und eine optionale automatische Auftragsausführung unter strengen Risikomanagementregeln.

Während unser erster Artikel ein Proof-of-Concept darstellte, verkürzt dieses produktionsreife Framework die Einrichtungszeit erheblich, beschleunigt Feedbackschleifen und ermöglicht es Ihnen, mit datengestütztem Vertrauen und Präzision zu handeln. Tauchen wir ein



Überblick über die Systemarchitektur

Nachfolgend finden Sie eine Übersicht über den Daten- und Signalfluss zwischen MetaTrader 5 und Ihrem Python Dienst, gefolgt von den Hauptaufgaben der einzelnen Komponenten:


MetaTrader 5-Terminal

Das MetaTrader 5-Terminal dient als primäre Marktschnittstelle und Charting-Plattform. Er enthält die Live- und historischen Kursbalken für das von Ihnen gewählte Symbol und bietet die Ausführungsumgebung für den Expert Advisor (EA). Durch seine integrierte Funktion von WebRequest() sammelt der EA regelmäßig die neuesten Balkendaten und zeigt eingehende Signale, SL/TP-Linien und Einstiegs-/Ausstiegspfeile direkt auf Ihrem Chart an. Das MetaTrader 5-Terminal ist für die Auftragserteilung (wenn aktiviert), die lokale Objektverwaltung (Panels, Pfeile, Etiketten) und die nutzerorientierte Visualisierung der Systemausgaben zuständig.

Python-Dateneinspeisung

Anstatt sich auf einen EA zu verlassen, um Balkendaten zu pushen, verwendet Python Data Feed die offizielle Python-Bibliothek von MetaTrader 5, um bei Bedarf sowohl historische als auch OHLC-Echtzeitdaten der Minutenbalken zu beziehen. Es nutzt einen komprimierten Parquet-Datenspeicher, um die Kursdaten der vergangenen Tage zu erhalten, und fügt dann neue Balken hinzu, sobald sie eintreffen. Dieses Setup eliminiert die Abhängigkeiten von Zeitintervallen in MQL5 und stellt sicher, dass der Python-Dienst immer sofortigen, zufälligen Zugriff auf die vollständige Preishistorie hat, die sowohl für Backtests als auch für Live-Inferenz benötigt wird.

Technische Merkmale

Sobald die Rohdaten im Speicher oder auf der Festplatte verfügbar sind, werden sie von der Feature-Engineering-Schicht in statistisch aussagekräftige Eingaben für das maschinelle Lernen umgewandelt. Es berechnet normalisierte Spike-Z-Scores, die MACD-Histogramm-Differenz, 14-Perioden-RSI-Werte, 14-Perioden-ATR für Volatilität und dynamische EMA-basierte Envelope-Bänder. Darüber hinaus nutzt es die Prophet-Bibliothek von Facebook, um ein Trend-Delta auf Minutenebene zu schätzen, das die Abweichung zwischen Mittelwert und Trend erfasst. Diese automatisierte Pipeline garantiert, dass Live- und historische Daten identisch verarbeitet werden und die Modelltreue erhalten bleibt.

ML-Modell

Das Herzstück des Systems ist ein Gradient-Boosting-Klassifikator, der in eine scikit-learn-Pipeline mit Standardskalierung eingebettet ist. Das Modell wird auf rollierenden Fenstern vergangener Balken trainiert, wobei TimeSeriesSplit zur Vermeidung von Vorausschauverzerrungen und RandomizedSearchCV zur Optimierung von Hyperparametern verwendet werden. Die Etiketten werden generiert, indem die Kurse zehn Minuten im Voraus betrachtet und die Bewegungen anhand eines konfigurierbaren Schwellenwerts in Kauf-, Verkaufs- oder Warteklassen eingeteilt werden. Der trainierte Schätzer wird in die Datei model.pkl serialisiert, was eine geringe Latenz beim Laden und bei der Inferenz sowohl in Backtests als auch in Live-Läufen gewährleistet.

Flask-API

Die Flask-API dient als Brücke zwischen dem datenwissenschaftlichen Ökosystem von Python und dem MQL5 EA. Es stellt einen Single/Analyze-Endpunkt zur Verfügung, der eine JSON-Nutzlast mit aktuellen Schließungen und Zeitstempeln akzeptiert, die Feature-Pipeline und das geladene Modell anwendet, um Klassenwahrscheinlichkeiten zu berechnen, und eine kurze JSON-Antwort mit Signal, sl, tp und conf (Konfidenz) zurückgibt. Diese leichtgewichtige REST-Schnittstelle kann in Containern oder auf einem beliebigen Server bereitgestellt werden, wodurch Ihre Python-Rechenressourcen von der Laufzeitumgebung des MetaTrader entkoppelt werden und die Skalierbarkeit vereinfacht wird.

MQL5 Expert Advisor

Auf der Client-Seite konzentriert sich der MQL5 EA ausschließlich auf die Nutzerinteraktion und die Handelsausführung. Es fragt regelmäßig die Flask-API ab, analysiert eingehende JSON-Daten, protokolliert jedes Signal sowohl auf der Registerkarte Experten als auch in einer lokalen CSV-Datei und aktualisiert ein Dashboard, das das aktuelle Signal, die Vertrauensstufe, den Verbindungsstatus und den Zeitstempel anzeigt. Wenn ein gültiges Kauf-, Verkaufs- oder Schließsignal eintrifft, zeichnet der EA Pfeile und SL/TP-Linien und – wenn EnableTrading wahr ist – platziert oder schließt Aufträge über die CTrade-Klasse. Durch die Verlagerung der gesamten Datenwissenschaft auf Python bleibt der EA schlank, reaktionsschnell und leicht zu pflegen.


Detaillierte Untersuchung des Python-Backends

Die Grundlage unseres Backends bildet eine robuste Datenverarbeitungspipeline, die auf das offiziellen MetaTrader 5 Python-Paket. Beim ersten Start „bootet“ der Dienst, indem er die OHLC-Minutenbalken-Daten der letzten Tage abruft und in eine komprimierte Parquet-Datei schreibt. Das spaltenorientierte Format von Parquet und die Zstandard-Komprimierung ermöglichen blitzschnelle Lesevorgänge für Zeitserien-Slices, während die Festplattennutzung minimal ist. Danach werden durch ein einfaches inkrementelles Update nur neu gebildete Balken hinzugefügt – so werden redundante Downloads vermieden und sichergestellt, dass sowohl Live-Inferenz als auch Backtests auf einer aktuellen, einzigen Quelle der Wahrheit basieren.

import datetime as dt
import pandas as pd
import MetaTrader5 as mt5
from pathlib import Path

PARQUET_FILE = "hist.parquet.zst"
DAYS_TO_PULL = 60
UTC = dt.timezone.utc

def bootstrap():
    """Fetch last DAYS_TO_PULL days of M1 bars and write to Parquet."""
    now = dt.datetime.utcnow()
    start = now - dt.timedelta(days=DAYS_TO_PULL)
    mt5.initialize()
    mt5.symbol_select("Boom 300 Index", True)
    bars = mt5.copy_rates_range("Boom 300 Index", mt5.TIMEFRAME_M1,
                                start.replace(tzinfo=UTC),
                                now.replace(tzinfo=UTC))
    df = pd.DataFrame(bars)
    df['time'] = pd.to_datetime(df['time'], unit='s')
    df.set_index('time').to_parquet(PARQUET_FILE, compression='zstd')

def append_new_bars():
    """Append only the newest bars since last timestamp."""
    df = pd.read_parquet(PARQUET_FILE)
    last = df.index[-1]
    now = dt.datetime.utcnow()
    new = mt5.copy_rates_range("Boom 300 Index", mt5.TIMEFRAME_M1,
                               last.replace(tzinfo=UTC) + dt.timedelta(minutes=1),
                               now.replace(tzinfo=UTC))
    if new:
        new_df = pd.DataFrame(new)
        new_df['time'] = pd.to_datetime(new_df['time'], unit='s')
        merged = pd.concat([df, new_df.set_index('time')])
        merged[~merged.index.duplicated()].to_parquet(PARQUET_FILE,
                                                      compression='zstd')

Auf der Grundlage der Rohbalken berechnet unsere Pipeline eine Reihe von Merkmalen, mit denen Momentum, Volatilität und extreme Bewegungen erfasst werden können. Wir normalisieren die erste Preisdifferenz zu einem Score von „Z-Spike“, indem wir sie durch ihre rollierende 20-Balken-Standardabweichung dividieren, um plötzliche Preissprünge zu isolieren. Die MACD-Histogramm-Differenz und der 14-Perioden-RSI quantifizieren den Trend bzw. die überkauften/überverkauften Bedingungen, während der 14-Perioden-ATR die aktuelle Volatilität misst. Ein 20-Perioden-EMA definiert die Envelope-Bänder (EMA×0,997 und EMA×1,003), die sich an wechselnde Situationen anpassen. Und schließlich nimmt die Prophet-Bibliothek von Facebook die gesamte Zeitreihe der Schlusskurse auf, um ein Trend-Delta auf Minutenebene zu prognostizieren, das eine differenziertere, zeitabhängige Saisonalität und Drift erfasst.

import numpy as np
import pandas as pd
import ta
from prophet import Prophet

def engineer(df: pd.DataFrame) -> pd.DataFrame:
    df = df.copy()
    # z-spike (20-bar rolling std)
    df['r'] = df['close'].diff()
    df['z_spike'] = df['r'] / (df['r'].rolling(20).std() + 1e-9)

    # MACD histogram diff, RSI, ATR
    df['macd'] = ta.trend.macd_diff(df['close'])
    df['rsi']  = ta.momentum.rsi(df['close'], window=14)
    df['atr']  = ta.volatility.average_true_range(df['high'],
                                                  df['low'],
                                                  df['close'],
                                                  window=14)

    # EMA envelopes
    ema = df['close'].ewm(span=20).mean()
    df['env_low'] = ema * 0.997
    df['env_up']  = ema * 1.003

    # Prophet trend delta (minute-level)
    if len(df) > 200:
        m = Prophet(daily_seasonality=False, weekly_seasonality=False)
        m.fit(pd.DataFrame({'ds': df.index, 'y': df['close']}))
        df['delta'] = m.predict(m.make_future_dataframe(periods=0,
                                                        freq='min'))['yhat'] - df['close']
    else:
        df['delta'] = 0.0

    return df.dropna()

Wir betrachten die Vorhersage als eine Drei-Klassen-Klassifizierung: „KAUFEN“, wenn der Kurs in den nächsten 10 Minuten um mehr als einen Schwellenwert steigt, „VERKAUFEN“, wenn er um mehr als diesen Schwellenwert fällt, und ansonsten „WARTEN“. Sobald die Bezeichnungen zugewiesen sind, werden die Merkmale und Bezeichnungen für das Training in rollierende Zeitfenster aufgeteilt. Eine Pipeline von scikit-learn standardisiert zunächst jedes Merkmal und passt dann einen GradientBoostingClassifier an. Hyperparameter (Lernrate, Baumanzahl, maximale Tiefe) werden mittels RandomizedSearchCV unter einer TimeSeriesSplit Kreuzvalidierungsschema optimiert, wodurch sichergestellt wird, dass keine Lecks in der Vorausschau auftreten. Das beste Modell wird in die Datei model.pkl serialisiert, die sofort und mit geringer Latenz zur Inferenz bereit ist.

import numpy as np
import joblib
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import TimeSeriesSplit, RandomizedSearchCV

LOOKAHEAD_MIN   = 10
LABEL_THRESHOLD = 0.0015
FEATS = ['z_spike','macd','rsi','atr','env_low','env_up','delta']

def label_and_train(df: pd.DataFrame):
    # Look-ahead return
    chg = (df['close'].shift(-LOOKAHEAD_MIN) - df['close']) / df['close']
    df['label'] = np.where(chg > LABEL_THRESHOLD, 1,
                   np.where(chg < -LABEL_THRESHOLD, 2, 0))

    X = df[FEATS].dropna()
    y = df.loc[X.index, 'label']

    pipe = Pipeline([
        ('scaler', StandardScaler()),
        ('gb', GradientBoostingClassifier(random_state=42))
    ])
    param_dist = {
        'gb__learning_rate': [0.01, 0.05, 0.1],
        'gb__n_estimators': [300, 500, 700],
        'gb__max_depth': [2, 3, 4]
    }
    cv = TimeSeriesSplit(n_splits=5)
    rs = RandomizedSearchCV(pipe, param_dist, n_iter=12,
                            cv=cv, scoring='roc_auc_ovr',
                            n_jobs=-1, random_state=42)
    rs.fit(X, y)
    joblib.dump(rs.best_estimator_, 'model.pkl')
Um eine Brücke zwischen Python und MetaTrader zu schlagen, stellen wir einen einzigen Flask-Endpunkt zur Verfügung: /analyze. Clients senden einen JSON-Payload, der ein Symbol, ein Array von Schlusskursen und entsprechende UNIX-Zeitstempel enthält. Der Endpunkt spielt unsere Feature-Pipeline auf diese Nutzlast ab, lädt das vorab trainierte Modell, berechnet die Klassenwahrscheinlichkeiten, bestimmt das Signal mit der höchsten Konfidenz und leitet dynamisch Stop-Loss- und Take-Profit-Levels aus dem ATR-Merkmal ab. Die Antwort ist ein kompaktes JSON-Objekt:
from flask import Flask, request, jsonify
import joblib
import pandas as pd

app = Flask(__name__)
model = joblib.load('model.pkl')

@app.route('/analyze', methods=['POST'])
def analyze():
    payload = request.get_json(force=True)
    closes = payload['prices']
    times  = pd.to_datetime(payload['timestamps'], unit='s')
    df = pd.DataFrame({'close': closes}, index=times)
    # duplicate open/high/low for completeness
    df[['open','high','low']] = df[['close']]

    feat = engineer(df).iloc[-1:]
    probs = model.predict_proba(feat[FEATS])[0]
    p_buy, p_sell = probs[1], probs[2]
    signal = ('BUY' if p_buy > 0.55 else
              'SELL' if p_sell > 0.55 else 'WAIT')
    atr = feat['atr']
    entry = feat['close']
    sl = entry - atr if signal=='BUY' else entry + atr
    tp = entry + 2*atr if signal=='BUY' else entry - 2*atr

    return jsonify(signal=signal,
                   sl=round(sl,5),
                   tp=round(tp,5),
                   conf=round(max(p_buy,p_sell),2))

if __name__ == '__main__':
    app.run(port=5000)


Erforschung der MQL5 EA-Client-Architektur

Der Kern der EA-Schleife befindet sich entweder in einem Timer-Ereignis (OnTimer) oder in einer Prüfung auf einen neuen Balken, wo er WebRequest() aufruft, um HTTP-Nachrichten zu senden und zu empfangen. Zunächst werden die letzten N Balken über CopyRates gesammelt, das Array MqlRates in eine UTF-8-JSON-Payload konvertiert, die die Symbol- und Schlusskurssequenz enthält, und die erforderlichen HTTP-Header gesetzt. Wenn WebRequest() fehlschlägt (Rückgabe ≤0), erfasst der EA GetLastError(), erhöht einen Zähler für Wiederholungsversuche, protokolliert den Fehler und verschiebt weitere Anfragen, bis entweder die Wiederholungsversuche erschöpft sind oder der nächste Timer-Tick kommt. Erfolgreiche Antworten (Status ≥200) setzen die Anzahl der Wiederholungsversuche zurück und aktualisieren lastStatus. Dieses Muster gewährleistet eine robuste, asynchrone Signalisierung, ohne den Chart-Thread zu blockieren oder bei vorübergehenden Netzwerkproblemen abzustürzen.

// In OnTimer() or OnNewBar():
MqlRates rates[];
// Copy the last N bars into `rates`
if(CopyRates(_Symbol, _Period, 0, InpBufferBars, rates) != InpBufferBars)
    return;
ArraySetAsSeries(rates, true);

// Build payload
string payload = "{";
payload += StringFormat("\"symbol\":\"%s\",", _Symbol);
payload += "\"prices\":[";
for(int i=0; i<InpBufferBars; i++)
  {
    payload += DoubleToString(rates[i].close, _digits);
    if(i < InpBufferBars-1) payload += ",";
  }
payload += "]}";

// Send request
string headers = "Content-Type: application/json\r\nAccept: application/json\r\n\r\n";
char  req[], resp[];
int   len = StringToCharArray(payload, req, 0, WHOLE_ARRAY, CP_UTF8);
ArrayResize(req, len);
ArrayResize(resp, 8192);

int status = WebRequest("POST", InpServerURL, headers, "", InpTimeoutMs,
                        req, len, resp, headers);
if(status <= 0)
{
  int err = GetLastError();
  PrintFormat("WebRequest error %d (attempt %d/%d)", err, retryCount+1, MaxRetry);
  ResetLastError();
  retryCount = (retryCount+1) % MaxRetry;
  lastStatus = StringFormat("Err%d", err);
  return;
}
retryCount = 0;
lastStatus = StringFormat("HTTP %d", status);

Sobald eine gültige JSON-Antwort in ein Signal, sl und tp geparst wurde, aktualisiert der EA sein On-Chart-Dashboard und zeichnet alle neuen Pfeile oder Linien. Das Dashboard besteht aus einem einzigen OBJ_RECTANGLE_LABEL mit vier Textbeschriftungen, die das Symbol, das aktuelle Signal, den HTTP-Status und den Zeitstempel anzeigen. Bei Abschlüssen werden alle vorhandenen vorangestellten Objekte gelöscht, bevor ein neuer Pfeil (OBJ_ARROW) zum aktuellen Preis erstellt wird, wobei unterschiedliche Pfeilcodes und Farben für Kaufen (grün aufwärts), Verkaufen (rot abwärts) oder Schließen (orange) verwendet werden. Horizontale Linien (OBJ_HLINE) markieren Stop-Loss- und Take-Profit-Ebenen, die rot bzw. grün eingefärbt sind. Indem jedes Objekt mit einem eindeutigen Präfix versehen und bei Signaländerungen oder Deinitialisierung bereinigt wird, bleibt Ihr Chart klar und übersichtlich.

// Panel (rectangle + labels)
void DrawPanel()
{
  const string pid = "SigPanel";
  if(ObjectFind(0, pid) < 0)
    ObjectCreate(0, pid, OBJ_RECTANGLE_LABEL, 0, 0, 0);

  ObjectSetInteger(0, pid, OBJPROP_CORNER, CORNER_LEFT_UPPER);
  ObjectSetInteger(0, pid, OBJPROP_XDISTANCE, PanelX);
  ObjectSetInteger(0, pid, OBJPROP_YDISTANCE, PanelY);
  ObjectSetInteger(0, pid, OBJPROP_XSIZE, PanelW);
  ObjectSetInteger(0, pid, OBJPROP_YSIZE, PanelH);
  ObjectSetInteger(0, pid, OBJPROP_BACK, true);
  ObjectSetInteger(0, pid, OBJPROP_BGCOLOR, PanelBG);
  ObjectSetInteger(0, pid, OBJPROP_COLOR, PanelBorder);

  string lines[4] = {
    StringFormat("Symbol : %s", _Symbol),
    StringFormat("Signal : %s", lastSignal),
    StringFormat("Status : %s", lastStatus),
    StringFormat("Time   : %s", TimeToString(TimeLocal(), TIME_MINUTES))
  };

  for(int i=0; i<4; i++)
  {
    string lbl = pid + "_L" + IntegerToString(i);
    if(ObjectFind(0, lbl) < 0)
      ObjectCreate(0, lbl, OBJ_LABEL, 0, 0, 0);
    ObjectSetInteger(0, lbl, OBJPROP_CORNER, CORNER_LEFT_UPPER);
    ObjectSetInteger(0, lbl, OBJPROP_XDISTANCE, PanelX + 6);
    ObjectSetInteger(0, lbl, OBJPROP_YDISTANCE, PanelY + 4 + i*(TxtSize+2));
    ObjectSetString(0, lbl, OBJPROP_TEXT, lines[i]);
    ObjectSetInteger(0, lbl, OBJPROP_FONTSIZE, TxtSize);
    ObjectSetInteger(0, lbl, OBJPROP_COLOR, TxtColor);
  }
}

// Arrows & SL/TP lines
void ActOnSignal(ESignal code, double sl, double tp)
{
  // remove old arrows/lines
  for(int i=ObjectsTotal(0)-1; i>=0; i--)
    if(StringFind(ObjectName(0,i), objPrefix) == 0)
      ObjectDelete(0, ObjectName(0,i));

  // arrow
  int    arrCode = (code==SIG_BUY ? 233 : code==SIG_SELL ? 234 : 158);
  color  clr     = (code==SIG_BUY ? clrLime : code==SIG_SELL ? clrRed : clrOrange);
  string name    = objPrefix + "Arr_" + TimeToString(TimeCurrent(), TIME_SECONDS);
  ObjectCreate(0, name, OBJ_ARROW, 0, TimeCurrent(), SymbolInfoDouble(_Symbol, SYMBOL_BID));
  ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrCode);
  ObjectSetInteger(0, name, OBJPROP_COLOR, clr);

  // SL line
  if(sl > 0)
  {
    string sln = objPrefix + "SL_" + name;
    ObjectCreate(0, sln, OBJ_HLINE, 0, 0, sl);
    ObjectSetInteger(0, sln, OBJPROP_COLOR, clrRed);
  }

  // TP line
  if(tp > 0)
  {
    string tpn = objPrefix + "TP_" + name;
    ObjectCreate(0, tpn, OBJ_HLINE, 0, 0, tp);
    ObjectSetInteger(0, tpn, OBJPROP_COLOR, clrLime);
  }
}

Die tatsächliche Auftragserteilung ist durch das Flag EnableTrading geschützt, sodass Sie mühelos zwischen dem reinen Anzeige- und dem Live-Ausführungsmodus wechseln können. Vor jeder Market-Order prüft der EA PositionSelect(_Symbol), um doppelte Positionen zu vermeiden. Für ein Kaufsignal wird CTrade.Buy() mit FixedLots, SL und TP aufgerufen, für einen Verkauf CTrade.Sell() und für ein CLOSE-Signal CTrade.PositionClose(). Die Schlupftoleranz (SlippagePoints) wird beim Schließen angewendet. Diese minimale, zustandsabhängige Logik stellt sicher, dass Sie nie versehentlich dieselbe Seite zweimal eingeben und dass alle Aufträge Ihre vordefinierten Risikoparameter einhalten.

void ExecuteTrade(ESignal code, double sl, double tp)
{
  if(!EnableTrading) return;

  bool hasPosition = PositionSelect(_Symbol);
  if(code == SIG_BUY && !hasPosition)
    trade.Buy(FixedLots, _Symbol, 0, sl, tp);
  else if(code == SIG_SELL && !hasPosition)
    trade.Sell(FixedLots, _Symbol, 0, sl, tp);
  else if(code == SIG_CLOSE && hasPosition)
    trade.PositionClose(_Symbol, SlippagePoints);
}

Installation und Konfiguration

Bevor Sie mit der Erzeugung von Live-Signalen beginnen können, müssen Sie sowohl Ihre Python-Umgebung als auch Ihre MetaTrader 5-Plattform vorbereiten. Beginnen Sie mit der Installation von Python 3.8 oder neuer auf demselben Rechner, auf dem auch Ihr MetaTrader 5 Terminal läuft. Erstellen und aktivieren Sie eine virtuelle Umgebung (Python-m venv venv dann unter Windows venv\Scripts\activate, oder unter macOS/Linux venv/bin/activate) und installieren Sie alle Abhängigkeiten mit:
pip install MetaTrader 5 pandas numpy ta prophet scikit-learn flask loguru joblib
Konfigurieren Sie als Nächstes Ihre Windows-Firewall so, dass sie ausgehende HTTP-Anfragen sowohl für python.exe als auch für terminal64.exe zulässt. Wenn Sie planen, HTTPS im Handel einzusetzen, installieren Sie Ihr SSL-Zertifikat im Trusted Root-Zertifikatspeicher, damit MetaTrader 5 sichere Verbindungen akzeptiert.

Öffnen Sie auf der MetaTrader 5-Seite

Tools → Optionen → Expert Advisors, aktivieren Sie DLL-Importe und fügen Sie Ihren lokalen API-Host hinzu.

Zum Beispiel (http://127.0.0.1:5000) in das Feld „WebRequest für folgende URLS erlauben“. Dieser Schritt der Whitelist ist von entscheidender Bedeutung – ohne ihn verwirft MetaTrader 5 alle POST-Payloads stillschweigend.

Kopieren Sie in Ihrem Projektordner das Python-Service-Skript (z.B. market_ai_engine.py) in ein Arbeitsverzeichnis Ihrer Wahl. Bearbeiten Sie den oberen Teil des Skripts, um Ihr Handelssymbol (MAIN_SYMBOL), Ihre MetaTrader 5-Anmeldedaten (LOGIN_ID, PASSWORD, SERVER) und Dateipfade (PARQUET_FILE, MODEL_FILE) festzulegen. Wenn Sie einen anderen als den Standard-Port für den Flask-Server bevorzugen, können Sie diesen beim Start des Dienstes mit --port übergeben.
Um den Expert Advisor einzusetzen, platzieren Sie die kompilierte EA.ex5 (oder die .mq5-Quelldatei) in Ihrer MetaTrader 5-Installation unter MQL5 → Experts.

Starten Sie MetaTrader 5 neu oder aktualisieren Sie den Navigator, damit der EA in Ihrer Liste erscheint. Ziehen Sie es auf ein M1-Chart des gleichen Symbols, das Sie in Python konfiguriert haben. In den Eingaben des EA, Punkt InpServerURL auf http://127.0.0.1:5000/analyze setzen Sie InpBufferBars (z.B. 60),InpPollInterval (z. B. 60 Sekunden) und InpTimeoutMs (z. B. 5000 ms). Lssen Sie EnableTrading zunächst ausgeschaltet, damit Sie die Signale überprüfen können, ohne dass es zu echten Aufträgen kommt.

Schließlich starten Sie Ihr Python-Backend in der folgenden Reihenfolge:

1. python market_ai_engine.py bootstrap

2. python market_ai_engine.py collect 

3. python market_ai_engine.py train

4. python market_ai_engine.py serve --port 5000

Wenn der Flask-Server läuft und AutoTrading in MetaTrader aktiviert ist, beginnt der EA mit der Abfrage von Live-Signalen, dem Zeichnen von Pfeilen und SL/TP-Linien auf Ihrem Chart und – sobald Sie sich sicher sind – dem Platzieren von Handelsgeschäften nach Ihren vordefinierten Risikoregeln.

Fehlersuche

Wenn der EA keine Daten anzeigt oder immer „WAIT“ zurückgibt, vergewissern Sie sich, dass Ihre API-URL in den Einstellungen des MetaTrader 5 Expert Advisor auf der Whitelist steht. Bei gemischten HTTP/HTTPS-Umgebungen sollten Sie für lokale Tests ein einfaches HTTP (127.0.0.1) verwenden und für den Handel auf HTTPS mit einem vertrauenswürdigen Zertifikat umstellen. Vergewissern Sie sich, dass die Uhren Ihres Servers und Ihres MetaTrader 5-Terminals synchronisiert sind (entweder in UTC oder in der gleichen lokalen Zeitzone), um falsch ausgerichtete Balken-Anforderungen zu vermeiden. Vergewissern Sie sich schließlich, dass AutoTrading aktiviert ist und dass keine globalen Berechtigungen oder andere EAs Ihren Experten blockieren.


Bewertung und Leistungsergebnisse

Um die Arbeit mit dem hybriden Python-MQL5-Maschinenlernsystem zu beginnen, muss man zunächst historische Daten mit dem Befehl

python market_ai_engine.py bootstrap
Damit wird ein lokaler Datensatz initialisiert, indem die letzten 60 Tage der M1-Balken (1-Minuten-Balken) direkt von MetaTrader 5 heruntergeladen werden, wobei die native Python-Bibliothek von MetaTrader 5 verwendet wird. Die Daten werden in einer komprimierten Parquet-Datei (hist.parquet.zst) gespeichert, die einen schnellen Plattenzugriff und eine effiziente Speicherung ermöglicht. Dieser Schritt muss nur einmal durchgeführt werden, es sei denn, Sie möchten die historischen Daten vollständig zurücksetzen.
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py bootstrap
2025-08-04 23:39:23 | INFO | Bootstrapped historical data: 86394 rows

Nach dem Bootstrapping können kontinuierlich aktualisieren durch das Ausführen von

python market_ai_engine.py collect
Dadurch wird sichergestellt, dass der Datensatz in Echtzeit aktuell bleibt. Dieser Collector kann zwar im Hintergrund laufen, ist aber nur dann erforderlich, wenn Sie einen kontinuierlich aktualisierten Stream wünschen, ohne vor jedem Training einen Neustart durchführen zu müssen.
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py collect
2025-08-04 23:41:01 | INFO | Appended 2 new bars
2025-08-04 23:42:01 | INFO | Appended 1 new bars
2025-08-04 23:43:01 | INFO | Appended 1 new bars

Wenn ein aktueller Datensatz vorliegt, ist der nächste Schritt das Modelltraining. 

python market_ai_engine.py train
Das löst die gesamte Pipeline des maschinellen Lernens aus. Das Skript liest die Parquet-Datei, wendet Feature-Engineering-Techniken an (z. B. Spike-Erkennung, MACD-Differenzen, RSI, ATR-Bänder, EMA-Hüllkurven und Prophet-basierte Trends) und verwendet dann einen GradientBoostingClassifier, der mit RandomizedSearchCV verpackt ist, um ein Vorhersagemodell auf einem rollierenden Fenster der jüngsten Daten zu trainieren. Das Ergebnis ist ein serialisiertes Modell, das als model.pkl gespeichert wird und zur Inferenz bereit ist.
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py train
23:48:44 - cmdstanpy - INFO - Chain [1] start processing
23:51:24 - cmdstanpy - INFO - Chain [1] done processing
2025-08-05 02:59:08 | INFO | Model training complete

Um herauszufinden, wie gut das Modell im realen Handel funktioniert, können Sie einen Backtest mit folgenden Befehl durchführen:

python market_ai_engine.py backtest --days 30
Dabei werden die Signalerzeugung und die Handelsergebnisse der letzten 30 Tage simuliert und Kennzahlen wie Signalgenauigkeit, Gewinn/Verlust-Verhältnis und Gesamtrentabilität berechnet. Die Ergebnisse werden zur einfachen Analyse in einer CSV-Datei gespeichert und geben Aufschluss darüber, ob die Strategie des Systems mit Ihren Erwartungen übereinstimmt.
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py backtest --days 30
06:57:33 - cmdstanpy - INFO - Chain [1] start processing
06:58:30 - cmdstanpy - INFO - Chain [1] done processing
2025-08-05 06:59:20 | INFO | Backtest results saved to backtest_results_30d.csv

Backtest-Ergebnisse

Nachfolgend finden Sie ausgewählte Kennzahlen und Handelsdetails, die aus den 30-Tage-Backtest-Ergebnissen extrahiert wurden:

Kumulative Kapitalkurve im Zeitverlauf

Hier finden Sie eine Zusammenfassung der wichtigsten Kennzahlen:

*   **Average Entry Price:** 3099.85
*   **Average Exit Price:** 3096.53
*   **Average PNL (Profit and Loss):** 3.32
*   **Total PNL:** 195.69
*   **Average Cumulative Equity:** 96.34
*   **First Trade Time:** 2025-07-11 14:18:00
*   **Last Trade Time:** 2025-07-27 02:00:00

Gewinnrate:

win_rate
72.88135528564453
The win rate is 72.88%. This means that approximately 73% of the trades resulted in a profit.

Nachdem das Modell trainiert und validiert wurde, starten Sie den Live-Inferenzserver mit

python market_ai_engine.py serve --port 5000
Dieser Befehl startet eine leichtgewichtige Flask-API, die auf eingehende Anfragen von Ihrem MQL5 EA wartet. Wenn der EA den Endpunkt /analyze abruft, holt der Server sofort den neuesten Balken, berechnet seine Merkmale, führt das Modell aus und liefert eine Vorhersage mit den entsprechenden SL-, TP- und Konfidenzwerten im JSON-Format. Dieser Server fungiert als Live-Brücke zwischen Ihrer Charts und der KI-Engine.
2025-08-05 12:41:53 | INFO | analyze: signal=%s, sl=%.5f, tp=%.5f
127.0.0.1 - - [05/Aug/2025 12:41:53] "POST /analyze HTTP/1.1" 200 -

Auf der MetaTrader-Seite muss der EA richtig konfiguriert sein, um den Python-Server abzufragen. In den Eingaben des EA sollte die Server-URL (z. B. http://127.0.0.1:5000/analyze) definiert werden, und der EA muss mit demselben Symbol und Zeitrahmen verknüpft werden, mit dem das Modell trainiert wurde – in der Regel M1. Sobald der EA läuft, holt er in regelmäßigen Abständen Signale ab, stellt sie als Pfeile auf dem Chart dar und handelt optional auf der Grundlage strenger Risikoregeln aus.

2025.08.05 12:41:53.532 trained model (1) (Boom 300 Index,M1)   >>> JSON: {"symbol":"Boom 300 Index","prices":[2701.855,2703.124,
2704.408,2705.493,2705.963,2696.806,2698.278,2699.877,2701.464,2702.788,2691.762,2693.046,2694.263,2695.587,2696.863,2698.
179,2699.775,2701.328,2702.888,2698.471,2699.887,2695.534,2696.952,2698.426,2699.756,2699.552,2700.954,2702.131,2703.571,
2699.549,2700.868,2702.567,2703.798,2705.067,2706.874,2698.084,2699.538,2700.856,2702.227,2703.692,2705.102,2706.188,2707.609,2709.001,
2710.335,2711.716,2712.919,2712.028,2713.529,2715.052,2716.578,2717.
2025.08.05 12:41:53.943 trained model (1) (Boom 300 Index,M1)   <<< HTTP 200 hdr:
2025.08.05 12:41:53.943 trained model (1) (Boom 300 Index,M1)   {"conf":0.43,"signal":"WAIT","sl":2725.04317,"tp":2720.18266}
2025.08.05 12:41:53.943 trained model (1) (Boom 300 Index,M1)   [2025.08.05 12:41:53] Signal → WAIT | SL=2725.04317 | TP=2720.18266 | Conf=0.43


Schlussfolgerung

In diesem Artikel haben wir einen MQL5 Expert Advisor entwickelt, der sich die Stärken von Python im Bereich der Datenwissenschaft zunutze macht. Hier ist, was Sie jetzt haben:
  • Datenabruf: Ihr EA zieht die Minutenbalken aus dem MetaTrader 5 und schreibt sie ins Parquet.
  • Merkmalsanalyse: Es wird Spike-Z, MACD, RSI, ATR, Envelope-Bänder und sogar Prophet-basierte Trenddeltas berechnet.
  • Modellierung und Bereitstellung: Sie trainieren ein zeitbewusstes Gradient-Boosting-Modell und stellen Vorhersagen über Flask bereit.
  • On-Chart-Aktion: MQL5 nutzt diese Signale, um Pfeile und SL/TP-Linien zu zeichnen – und kann automatisch Handelsgeschäfte platzieren.
Auf dem Weg dorthin haben Sie sich mit realen Problemen auseinandergesetzt, vom JSON-Parsing mit zusätzlichen Daten in Flask bis zur Drosselung von HTTP-Aufrufen in Ihrem EA, und Sie haben gesehen, wie die Aufteilung von Anliegen alles wartbar und erweiterbar macht.
Sie können versuchen, einen anderen Algorithmus (XGBoost, LSTM, usw.) einzusetzen, Ihre Regeln für das Risikomanagement zu verbessern oder den Python-Dienst mit Docker zu containerisieren, um eine sauberere Bereitstellung zu erreichen. Mit dieser Grundlage können Sie Ihre Backtests verfeinern und Ihre automatisierten Strategien noch weiter ausbauen.






Chart Projector
Analytical Comment
Analytics Master
Analytics Forecaster 
Volatility Navigator
Der Mean Reversion Signal Reaper
Signal Pulse 
Metrics Board 
External Flow
VWAP
Heikin Ashi   FibVWAP  
RSI DIVERGENCE
Parabolic Stop and Reverse (PSAR) 
Quarters Drawer Script
Intrusion Detector
TrendLoom Tool  Quarters Board 
ZigZag Analyzer  Correlation Pathfinder  Market Structure Flip Detector Tool
Correlation Dashboard   Currency Strength Meter
PAQ-Analyse-Tool 
Dual EMA Fractal Breaker
Pin-Bar, Engulfing und RSI-Divergenz
Liquidity Sweep Werkzeug zum Ausbrechen aus dem Eröffnungsbereich Boom and Crash Interceptor CCI Zero-Line EA
Erkennen von Kerzenmuster Kerzenerkennung mit Ta-Lib Candle Range Tool MetaTrader 5 Datenabruf Modelltraining und -einführung Verwendung der Python Lib

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/19065

Beigefügte Dateien |
MetaTrader Tick-Info-Zugang von MQL5-Diensten zur Python-Anwendung über Sockets MetaTrader Tick-Info-Zugang von MQL5-Diensten zur Python-Anwendung über Sockets
Manchmal ist nicht alles in der MQL5-Sprache programmierbar. Und selbst wenn es möglich wäre, bestehende fortgeschrittene Bibliotheken in MQL5 zu konvertieren, wäre dies sehr zeitaufwändig. Dieser Artikel versucht zu zeigen, dass wir die Abhängigkeit vom Windows-Betriebssystem umgehen können, indem wir Tick-Informationen wie Bid, Ask und Time mit MetaTrader-Diensten über Sockets an eine Python-Anwendung übertragen.
Automatisieren von Handelsstrategien in MQL5 (Teil 25): Trendlinien-Händler mit der Anpassung der kleinsten Quadrate und dynamischer Signalgenerierung Automatisieren von Handelsstrategien in MQL5 (Teil 25): Trendlinien-Händler mit der Anpassung der kleinsten Quadrate und dynamischer Signalgenerierung
In diesem Artikel entwickeln wir ein Trendlinien-Handelsprogramm, das die kleinsten Quadrate verwendet, um Unterstützungs- und Widerstandstrendlinien zu erkennen, dynamische Kauf- und Verkaufssignale auf der Grundlage von Preisberührungen zu erzeugen und Positionen auf der Grundlage der erzeugten Signale zu eröffnen.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
CRUD-Operationen in Firebase mit MQL CRUD-Operationen in Firebase mit MQL
Dieser Artikel bietet eine Schritt-für-Schritt-Anleitung zur Beherrschung von CRUD-Operationen (Create, Read, Update, Delete) in Firebase, wobei der Schwerpunkt auf der Echtzeitdatenbank und dem Firestore liegt. Entdecken Sie, wie Sie die SDK-Methoden von Firebase nutzen können, um Daten in Web- und Mobilanwendungen effizient zu verwalten, vom Hinzufügen neuer Datensätze bis zum Abfragen, Ändern und Löschen von Einträgen. Lernen Sie praktische Code-Beispiele und Best Practices für die Strukturierung und Verarbeitung von Daten in Echtzeit kennen, die es Entwicklern ermöglichen, dynamische, skalierbare Anwendungen mit der flexiblen NoSQL-Architektur von Firebase zu erstellen.