English 日本語
preview
Entwicklung eines MQTT-Clients für MetaTrader 5: ein TDD-Ansatz — Finale

Entwicklung eines MQTT-Clients für MetaTrader 5: ein TDD-Ansatz — Finale

MetaTrader 5Integration | 31 Mai 2024, 10:37
125 0
Jocimar Lopes
Jocimar Lopes
„Unser Ziel ist es immer, auf der höchsten Abstraktionsebene zu arbeiten, die angesichts eines Problems und der Beschränkungen für seine Lösung möglich ist.“ (Bjarne Stroustrup, Programming Principles and Practice Using C++)

Einführung

Da dies der letzte Artikel dieser Reihe ist, wäre vielleicht eine kurze Zusammenfassung nützlich oder zumindest praktisch.

Im ersten Artikel dieser Reihe haben wir gesehen, dass MQTT ein auf dem Publish/Subscribe-Interaktionsmodell (Pub/Sub) basierendes Protokoll für die gemeinsame Nutzung von Nachrichten ist, das in einer Handelsumgebung nützlich sein kann, indem es dem Nutzer ermöglicht, jede Art von Daten in Echtzeit zu teilen: Handelstransaktionen, Kontoinformationen, statistische Daten für die Aufnahme durch eine Pipeline für maschinelles Lernen, in Klartext, XML, JSON oder binären Daten, einschließlich Bildern. MQTT ist leichtgewichtig, unempfindlich gegenüber Netzinstabilitäten oder -unterbrechungen und inhaltsunabhängig. Darüber hinaus ist das Protokoll ausgereift, praxiserprobt und ein offener Standard, der von OASIS gepflegt wird. Die beiden meistgenutzten Versionen, die frühere 3.1.1 und die aktuelle 5.0, gehören zu den meistgenutzten Protokollen für die Verbindung einer praktisch unbegrenzten Anzahl von Geräten im sogenannten Internet der Dinge. MQTT kann in jedem Szenario eingesetzt werden, in dem ein Echtzeit-Datenaustausch zwischen entkoppelten Maschinen erforderlich ist.

Es gibt viele MQTT-Nachrichtenbroker für Entwicklungs-, Test- und Produktionsumgebungen, sowohl Open-Source als auch kommerziell, und eine Menge MQTT-Clients für praktisch jede moderne Programmiersprache. Sie können sie in dieser Liste von MQTT-Software, die Broker, Bibliotheken und Tools umfasst, überprüfen.

Im zweiten Artikel dieser Serie haben wir unsere Code-Organisation für diese Client-Entwicklung beschrieben und einige anfängliche Design-Entscheidungen, wie das objektorientierte Paradigma, kommentiert. Der größte Teil dieses Codes wurde bei unserem ersten großen Refactoring geändert, aber die Funktionalität bleibt gleich. In diesem Artikel haben wir auch einige Verbindungen beschrieben, die wir mit einem lokalen Broker auf dem Windows Subsystem für Linux (WSL) hergestellt haben, nur um festzustellen, dass unsere CONNECT-Klasse fehlerhafte Pakete erzeugt. Wir haben sie verbessert und berichten darüber im nächsten Artikel.

Im dritten Artikel dieser Reihe wurden einige Anmerkungen zum Teil „Operational Behavior“ des Protokolls und dessen Beziehung zu den CONNECT-Flags gemacht. Wir haben die Semantik dieser Flags beschrieben und wie wir sie setzen. Wir haben auch einige Notizen über die testgetriebene Entwicklung gemacht, die wir für dieses Projekt verwenden. Schließlich haben wir erklärt, wie wir die geschützten Methoden unserer Klassen testen.

Im vierten Artikel dieser Serie gehen wir auf die Bedeutung der MQTT 5.0 Properties ein. Wir haben sie und ihre jeweiligen Datentypen bzw. ihre Datendarstellung im MQTT-Jargon kommentiert. Dort haben wir uns einige Notizen darüber gemacht, wie MQTT 5.0 Properties, insbesondere die User Property(s), zur Erweiterung des Protokolls genutzt werden können.

Das Kontrollpaket PUBLISH und seine einzigartigen (nicht reservierten) festen Header-Flags waren das Topic (Thema) des fünften Artikels dieser Serie. Wir haben viel Platz darauf verwendet, zu zeigen, wie diese festen PUBLISH-Header-Flags auf Bit-Ebene funktionieren. Wir haben die Merkmale der verschiedenen MQTT-Qualitätsstufen (QoS 0, QoS 1 und QoS 2) mit einigen anschaulichen Diagrammen dargestellt, die den Paketaustausch zwischen dem Client und dem Broker in jeder dieser QoS-Stufen zeigen.

Ein Intermezzo wurde in unserem sechsten Artikel dieser Reihe beschrieben. Es war unsere erste große Code-Umgestaltung. Wir haben den Blaupause unserer Klassen der Kontrollpakete geändert und doppelte Funktionen und veralteten Testcode entfernt. Dieser Artikel ist hauptsächlich eine Dokumentation dieser Änderungen mit einigen Anmerkungen zum Kontrollpaket PUBACK. Die Semantik der PUBACK Reason Codes mit ihren jeweiligen Reason Strings wird in diesem Artikel erläutert.

In diesem siebten und letzten Teil möchten wir mit Ihnen einen Arbeitscode teilen, der ein sehr häufiges Bedürfnis von Händlern bei der Erstellung von Indikatorsignalen für Expert Advisors beheben soll: das Fehlen eines erforderlichen Symbols für den Indikator im Handelskonto.

Wir schlagen eine mögliche Lösung vor, bei der nutzerdefinierte Symbole und ein Paar MQTT-Clients als Dienste auf dem Metatrader 5-Terminal laufen. Auch wenn der Democode stark vereinfacht ist und auf einer einzigen Terminalinstanz läuft, kann diese Lösung aufgrund der Haupteigenschaft des MQTT-Protokolls selbst - der Entkopplung zwischen Sender und Empfänger durch eine „Broker“-Vermittlung - auf eine beliebige Anzahl von Geräteinstanzen und Symbolen erweitert werden.

Am Ende des Artikels geben wir den aktuellen Stand der Bibliothek an, unsere Entwicklungsprioritäten mit einer möglichen Roadmap, und wo Sie das Projekt verfolgen und zu ihm beitragen können.

In den folgenden Beschreibungen werden die Begriffe MUSS und KANN so verwendet, wie sie im OASIS-Standard verwendet werden, der sie wiederum wie in IETF RFC 2119 beschrieben verwendet.

Wenn nicht anders angegeben, stammen alle Zitate aus dem OASIS Standard.


Der Bedarf eines Händlers

Nehmen wir an, Sie haben sich als Händler auf Kryptowährungen spezialisiert. Sie haben erfahren, dass es eine beständige negative Korrelation zwischen dem S&P 500 und einem obskuren, fast unbekannten Memecoin namens AncapCoin gibt. Sie haben festgestellt, dass AncapCoin sinkt, wenn der S&P 500 steigt, und andersherum. Dieses Wissen verschafft Ihnen einen Vorsprung an den Märkten, und Sie haben mit dem Handel von AncapCoin aufgrund seiner negativen Korrelation mit dem S&P 500 Geld verdient. Um Ihre Gewinne zu maximieren, möchten Sie Ihre Operationen automatisieren und Ihren Expert Advisor schließlich 24/7 auf einem VPS laufen lassen. Alles, was Sie brauchen, ist ein grundlegender S&P 500-Indikator zur Unterstützung Ihrer EA-Kauf- und Verkaufsentscheidungen.

Aber AncapBroker ist auch auf Kryptowährungen spezialisiert. Sie haben also nicht den S&P 500 unter ihren Symbolen. Und die Makler, die den S&P 500 anbieten, bieten Ihren schönen AncapCoin nicht an. Wie können Sie Ihren S&P 500-Indikator in Ihrem von AncapBroker bereitgestellten Handelskonto laufen lassen?

Auch wenn AncapBroker und AncapCoin nur erfundene Namen sind, ist dieses fiktive Szenario überhaupt nicht fiktiv. Ein Händler, der mit Indizes handelt, würde es in der Regel begrüßen, wenn er einen Indikator hätte, der aus einer Zusammensetzung ausgewählter Aktien besteht, die nicht von dem Broker angeboten werden, der die Indizes anbietet, und umgekehrt. Betrachtet man Rohstoffe, die an einer zentralen Börse gehandelt werden können, und die entsprechenden CFDs, die bei CFD-Brokern gehandelt werden können, so ergibt sich eine typische Diskrepanz, bei der der Händler zwar legitimen Zugang zu den Daten hat, diese aber von unterschiedlichen Anbietern stammen. Für den manuellen, diskretionären Handel ist das kein Problem. Mit einem separaten Monitor oder sogar mit einem separaten Fenster auf demselben Monitor können Sie den fehlenden Symbolmarkt verfolgen. Aber wenn es um die Automatisierung geht, ist es, selbst wenn beide Anbieter Metatrader 5 anbieten, für den durchschnittlichen Kleinhändler sehr schwierig, wenn nicht sogar praktisch unmöglich, die Kurse in Echtzeit an dem Ort zu haben, an dem sie für die Handelsentscheidung benötigt werden: dem Handelskonto.

Wenn Sie als Entwickler dieses Szenario als Kundenanforderung betrachten, können Sie auch das Publish/Subscribe-Messaging-Pattern als Kandidat zur Erfüllung dieser Anforderung betrachten. Unter den verfügbaren offenen und gut gewarteten Pub/Sub-Spezifikationen ist MQTT wahrscheinlich eine der einfachsten, kostengünstigsten und robustesten. Um es ganz klar zu sagen: MQTT wurde genau für die oben beschriebenen Anforderungen in unserem fiktiven Szenario entwickelt, d. h. für die Erfassung und Verteilung von Daten aus mehreren, möglicherweise geografisch verteilten Quellen in Echtzeit und mit minimalem Netzwerk-Overhead.

In den folgenden Abschnitten werden wir sehen, wie Sie diese Lösung in Metatrader 5 implementieren können, indem Sie unseren Client in seinem aktuellen Zustand verwenden, sodass Sie die Kurse von Ihrem Datenquellenkonto in Echtzeit auf Ihr Handelskonto übertragen können. Dort wird er für die Erstellung des erforderlichen Indikators zur Verfügung stehen.

Auf dem Handelskonto werden wir

  1. ein nutzerdefiniertes Symbol rstellen, um den fehlenden S&P 500 darzustellen,
  2. einen SUBSCRIBE MQTT-Dienst schreiben, um die S&P 500-Kurse vom MQTT-Broker zu erhalten,
  3. unser nutzerdefiniertes Symbol aktualisieren, das den S&P 500 repräsentiert, mit MQL5-Funktionen.

Im Datenquellenkonto werden wir

  1. einen PUBLISH MQTT-Dienst schreiben, um S&P 500-Kurse zu sammeln und sie an einen MQTT-Broker zu senden,

Bitte beachten Sie, dass wir der Einfachheit halber von einem Handelskonto und einem Datenquellenkonto sprechen, aber die Anzahl dieser Konten ist theoretisch unbegrenzt. In der Praxis ist die Anzahl der auf beiden Seiten verbundenen Konten nur durch die physischen Grenzen der Geräte (Speicher, CPU, Netzwerkbandbreite usw.) und durch die von Ihrem MQTT-Broker auferlegten Grenzen begrenzt.

Bitte bedenken Sie auch, dass es nicht unser Ziel ist, Ihnen eine fertige, produktionsreife Lösung zu liefern. Unser Ziel ist es vielmehr, Ihnen die wichtigsten Schritte für eine mögliche Implementierung aufzuzeigen und natürlich Ihr Interesse an den potenziellen Einsatzmöglichkeiten des MQTT-Pub/Sub-Musters für Ihre Handelsgeschäfte oder für die Bereitstellung maßgeschneiderter Anwendungen für Ihre Kunden zu wecken. Wir sind überzeugt, dass es weit über das Kopieren von Kursen aus einer Memecoin hinausgeht.


Erstellen eines nutzerdefinierten Symbols

Ein nutzerdefiniertes Symbol ist ein Symbol, das Sie mit Ihren gewünschten oder erforderlichen Spezifikationen erstellen und mit den von Ihnen bereitgestellten Zitaten aktualisieren. Da Sie die Kontrolle über die Spezifikationen und Kurse haben, sind nutzerdefinierte Symbole nützlich für die Erstellung zusammengesetzter Indizes und zum Testen von Expert Advisors und Indikatoren.

Sie können ein nutzerdefiniertes Symbol über die grafische Nutzeroberfläche des Metatrader 5-Editors oder programmatisch über spezielle MQL5-Funktionen erstellen. Die Dokumentation enthält detaillierte Anweisungen darüber, wie nutzerdefinierte Symbole programmtechnisch oder über die grafische Nutzeroberfläche erstellt, angepasst und verwendet werden. Für unsere Zwecke ist die grafische Nutzeroberfläche ausreichend.

Klicken Sie auf dem MetaTrader 5, auf dem sich Ihr Handelskonto befindet, auf Ansicht > Symbole > Nutzerdefiniertes Symbol erstellen.

MetaTrader 5 - Erstellen eines nutzerdefinierten Symbols mit Hilfe der grafischen Nutzeroberfläche

Abb. 01 - MetaTrader 5 - Erstellen eines nutzerdefinierten Symbols über die grafische Nutzeroberfläche

Wählen Sie im nächsten Fenster im Feld „Kopieren von:“ das S&P 500-Symbol, um ein nutzerdefiniertes Symbol auf der Grundlage des echten Index zu erstellen. Geben Sie einen Namen und eine kurze Beschreibung für Ihr nutzerdefiniertes Symbol ein.

Einstellen der Spezifikationen eines nutzerdefinierten Symbols über die grafische Nutzeroberfläche

Abb. 02 - MetaTrader 5 - Einstellen der Spezifikationen eines nutzerdefinierten Symbols über die grafische Nutzeroberfläche

WARNUNG: Der aufmerksame Leser hat vielleicht bemerkt, dass wir unser nutzerdefiniertes Symbol im Handelskonto erstellen, aber gemäß unserem Beispiel verfügt dieses Konto nicht über das S&P 500-Symbol, das als Modell für unser nutzerdefiniertes Symbol verwendet werden kann. Wir sollten auf dem Datenquellenkonto sein. Was wir hier tun, geschieht ja gerade deshalb, weil wir den S&P 500 nicht im Handelskonto zur Verfügung haben. Und Sie haben Recht!  In einer realen Situation müssen Sie diese Symbolspezifikationen entsprechend Ihren Bedürfnissen ausfüllen, wobei Sie wahrscheinlich die Spezifikationen des S&P 500 manuell kopieren. Wir vereinfachen hier zu sehr, denn die Erstellung von nutzerdefinierten Symbolen ist hier nicht unser Thema. Stattdessen sind wir daran interessiert, die Konten über MQTT zu verbinden. Wenn Sie das Symbol für Ihre konkrete Situation anpassen müssen, lesen Sie bitte die oben verlinkte Dokumentation.

Nachdem Sie auf OK geklickt haben, sollte Ihr neu erstelltes nutzerdefiniertes Symbol in der Baumliste auf der linken Seite erscheinen.

Überprüfen des Symbolbaums nach der Erstellung eines nutzerdefinierten Symbols mithilfe der grafischen Nutzeroberfläche

Abb. 03 - MetaTrader 5 - Überprüfung des Symbolbaums nach Erstellung eines nutzerdefinierten Symbols über die grafische Nutzeroberfläche

Fügen Sie ihn dann zu Ihrem Market Watch hinzu, damit er für Chart-Visualisierungen zur Verfügung steht. Dieser Schritt ist für Tick-Updates erforderlich. 

„Die Funktion CustomTicksAdd arbeitet nur für benutzerdefinierte Symbole im Fenster MarketWatch (Marktübersicht). Wenn das Symbol in MarketWatch nicht ausgewählt wurde, muss man für das Einfügen von Ticks CustomTicksReplace verwenden.“ (MQL5 Referenzdokumentation)

MetaTrader 5 - Überprüfung des Fensters der Marktübersicht nach der Erstellung eines nutzerdefinierten Symbols unter Verwendung der grafischen Nutzeroberfläche

Abb. 04 - MetaTrader 5 - Überprüfung des Fensters der Marktübersicht nach Erstellung eines nutzerdefinierten Symbols über die grafische Nutzeroberfläche

Im Fenster „Marktübersicht“ (Market Watch) sehen Sie, dass es noch keine Kurse gibt. Abonnieren wir also dieses Konto bei dem MQTT-Broker, der Echtzeitkurse sendet, um unser neu erstelltes nutzerdefiniertes Symbol MySPX500 zu aktualisieren.


Einen SUBSCRIBE MQTT-Dienst schreiben

Zum gegenwärtigen Zeitpunkt kann unser Kunde mit QoS 0 und QoS 1 abonnieren, aber für die Aktualisierung von Kursen/Ticks halten wir QoS 0 für ausreichend, da ein eventuell verlorener Tick in diesem Zusammenhang nicht kritisch ist. Wir senden alle 500 ms einen Tick, d. h. wenn der eine oder andere verloren geht, wird seine Position sofort durch den nächsten ersetzt.

Wir beginnen unseren Code für den Subscribe-Dienst mit Eingabeparametern für den Broker-Host und den Port. Denken Sie daran, diese durch Ihre tatsächlichen Maklerdaten zu ersetzen. Im nächsten Abschnitt finden Sie einige Hinweise zum Einrichten Ihrer Entwicklungs-/Testumgebung mit einem lokalen Broker.

#property service
//--- input parameters
input string   host = "172.20.106.92";
input int      port = 80;

Als Nächstes deklarieren wir einige globale Variablen für unsere Objekte der Klassen Connect und Subscribe. Wir benötigen diese Variablen zum Löschen (Bereinigen), wenn wir anhalten oder von einem Fehler zurückkehren.

//--- global vars
int skt;
CConnect *conn;
CSubscribe *sub;

„Alle Objekte, die durch den Ausdruck object_pointer=new Class_name erzeugt werden, müssen anschließend durch den delete(object_pointer)-Operator gelöscht werden.“ (MQL5-Referenzdokumentation)

Direkt in OnStart() unseres Dienstes konfigurieren wir ein Verbindungsobjekt als Clean-Start-Verbindung - d. h. wir haben keine Broker-Sitzung für diese Verbindung - mit einem großzügigen Keep Alive, um zu vermeiden, dass während der Entwicklung regelmäßige Ping-Anfragen gesendet werden müssen, setzen unsere Client-Kennung und erstellen schließlich unser CONNECT-Paket. Stellen Sie sicher, dass Sie einen anderen Client-Identifikator als in Ihrem Veröffentlichungsdienst verwenden.

   uchar conn_pkt[];
   conn = new CConnect(host, port);
   conn.SetCleanStart(true);
   conn.SetKeepAlive(3600);
   conn.SetClientIdentifier("MT5_SUB");
   conn.Build(conn_pkt);

In der gleichen OnStart()-Methode konfigurieren und erstellen wir auch unser Abonnement-Paket mit seinem Themenfilter. Der Einfachheit halber verwenden wir den Namen, den wir für unser nutzerdefiniertes Symbol gewählt haben, als Themenfilter. Dies ist beliebig, vorausgesetzt natürlich, Sie verwenden denselben Themenfilter in Ihrem Veröffentlichungsdienst.

  uchar sub_pkt[];
  sub = new CSubscribe();
  sub.SetTopicFilter("MySPX500");
  sub.Build(sub_pkt);

Schließlich rufen wir die Funktionen auf, um beide Pakete nacheinander zu senden, wobei wir die Fehlerbehandlung an diese Funktionen delegieren und -1 zurückgeben, wenn bei unserer Abonnementanfrage etwas schief läuft.

if(SendConnect(host, port, conn_pkt) == 0)
     {
      Print("Client connected ", host);
     }
   if(!SendSubscribe(sub_pkt))
     {
      return -1;
     }

Die SendConnect-Funktion enthält nur wenige Zeilen MQTT-bezogenen Code. Das meiste davon hat mit Netzwerken und Anschlüssen zu tun und wird hier nicht näher erläutert. Bitte lesen Sie stattdessen die Dokumentation über Netzwerkfunktionen. Wenn Sie Ihr Verständnis der MQL5-Netzwerkfunktionen vertiefen wollen, empfehlen wir Ihnen die entsprechenden Kapitel im AlgoBook, wo Sie detaillierte Erklärungen und nützliche Beispiele sowohl für einfache als auch für sichere (TLS) Netzwerke mit MQL5 finden.

Im AlgoBook finden Sie Informationen wie den untenstehenden Auszug, der uns geholfen hat, ein intermittierendes und nicht-deterministisches Verhalten in unserer Funktion zu identifizieren und zu umgehen (siehe den kommentierten Code in den angehängten Dateien).

„Programmierer, die mit den Windows/Linux-Socket-System-APIs vertraut sind, wissen, dass ein Wert von 0 auch ein normaler Zustand sein kann, wenn keine Daten im internen Puffer des Sockets eingehen. Allerdings verhält sich diese Funktion in MQL5 anders. Bei einem leeren System-Socket-Puffer wird spekulativ 1 zurückgegeben und die tatsächliche Prüfung der Datenverfügbarkeit auf den nächsten Aufruf einer der Lesefunktionen verschoben. Insbesondere tritt diese Situation mit einem Dummy-Ergebnis von 1 Byte in der Regel beim ersten Aufruf einer Funktion auf einem Socket auf, wenn der interne Empfangspuffer noch leer ist.“ (AlgoBook)

Auf der MQTT-Seite überprüfen wir in SendConnect lediglich die CONNACK-Antwort des Brokers sowie den Wert des zugehörigen Reason Codes.

if(rsp[0] >> 4 != CONNACK)
     {
      Print("Not Connect acknowledgment");
      CleanUp();
      return -1;
     }
   if(rsp[3] != MQTT_REASON_CODE_SUCCESS)  // Connect Return code (Connection accepted)
     {
      Print("Connection Refused");
      CleanUp();
      return -1;
     }

Wie Sie sehen können, geben wir in beiden Fällen -1 als Fehler zurück, nachdem wir die dynamischen Zeiger auf unsere Klassenobjekte bereinigt haben.

Der gleiche Anteil an netzwerk- oder MQTT-bezogenem Code gilt für die SendSubscribe-Funktion. Nach der Überprüfung der SUBACK-Antwort des Brokers und des entsprechenden Reason Codes werden die dynamischen Zeiger dieser Klassenobjekte gelöscht, wenn ein Fehler auftritt.

if(((rsp[0] >> 4) & SUBACK) != SUBACK)
     {
      Print("Not Subscribe acknowledgment");
     }
   else
      Print("Subscribed");
   if(rsp[5] > 2)  // Suback Reason Code (Granted QoS 2)
     {
      Print("Subscription Refused with error code %d ", rsp[4]);
      CleanUp();
      return false;
     }

In einer Endlosschleife warten wir auf Broker-Nachrichten und lesen sie mit Hilfe einer statischen Methode der Klasse Publish.

msg += CPublish().ReadMessageRawBytes(inpkt);
               //printf("New quote arrived for MySPX500: %s", msg);
               //UpdateRates(msg);
               printf("New tick arrived for MySPX500: %s", msg);
               UpdateTicks(msg);

Sie werden feststellen, dass wir einen kommentierten Entwicklungscode für die Aktualisierung von Raten anstelle von Ticks hinterlassen haben. Sie können diese Zeilen auskommentieren, wenn Sie auf diese Weise testen wollen. Beachten Sie, dass bei der Aktualisierung von Kursen und nicht von Ticks einige Informationen im Marktbeobachtungsfenster und im Diagramm nicht angezeigt werden können. Aber es ist eine vernünftige Alternative, wenn Sie den RAM-, CPU- und Bandbreitenverbrauch reduzieren wollen und mehr Interesse an den Daten für die Handelsautomatisierung als an der Grafik haben.

Die Funktion UpdateRates arbeitet mit ihrem Gegenstück im Veröffentlichungsdienst zusammen. Wir zahlen auf beiden Seiten den Preis für die Konvertierung der Zeichenketten, während wir unsere MQTT-Nutzereigenschaft(en) entwickeln und einen zuverlässigeren binären Datenaustausch haben. Das ist die oberste Priorität in unserer Quasi-Roadmap.

void UpdateTicks(string new_ticks)
  {
   string new_ticks_arr[];

   StringSplit(new_ticks, 45, new_ticks_arr);

   MqlTick last_tick[1];

   last_tick[0].time          = StringToTime(new_ticks_arr[0]);
   last_tick[0].bid           = StringToDouble(new_ticks_arr[1]);
   last_tick[0].ask           = StringToDouble(new_ticks_arr[2]);
   last_tick[0].last          = StringToDouble(new_ticks_arr[3]);
   last_tick[0].volume        = StringToInteger(new_ticks_arr[4]);
   last_tick[0].time_msc      = StringToInteger(new_ticks_arr[5]);
   last_tick[0].flags         = (uint)StringToInteger(new_ticks_arr[6]);
   last_tick[0].volume_real   = StringToDouble(new_ticks_arr[7]);

   if(CustomTicksAdd("MySPX500", last_tick) < 1)
     {
      Print("Update ticks failed: ", _LastError);
     }
  }

Wenn Sie den Abonnementdienst starten, sollten Sie auf der Registerkarte „Log Experts“ etwas wie dieses sehen.

MetaTrader 5 - Log-Ausgabe auf der Registerkarte „Experten“ zeigt 5270 Fehler

Abb. 05 - MetaTrader 5 - Protokollausgabe auf der Registerkarte „Experten“ zeigt den Fehler 5270

Unser Abonnementdienst ruft einsam in der Wüste. Beheben wir dies, indem wir unseren MQTT-Broker ausführen.


Einrichten eines lokalen Brokers für Entwicklung und Tests

Die Umgebung, die wir für unsere Entwicklung verwenden, nutzt das Windows Subsystem für Linux (WSL) auf einem Windows-Rechner. Wenn Sie nur die Beispiele ausführen wollen, können Sie sowohl den Client als auch den Broker auf einem Loopback auf demselben Rechner ausführen, vorausgesetzt, Sie verwenden unterschiedliche Client-Kennungen für die Veröffentlichungs- und Abonnementdienste. Wenn Sie jedoch nicht nur die Beispiele ausführen, sondern auch eine Entwicklungsumgebung einrichten möchten, empfehlen wir Ihnen, dafür einen separaten Rechner einzurichten. Wie Sie wahrscheinlich wissen, gilt es bei der Entwicklung von Client/Server-Anwendungen - und das Pub/Sub-Muster kann aus diesem Grund in diese Architektur einbezogen werden - als gute Praxis, jede Seite auf ihrem eigenen Host laufen zu lassen. Auf diese Weise können Sie Verbindungs-, Authentifizierungs- und andere Netzwerkprobleme früher beheben.

Diese Einrichtung mit WSL ist ziemlich einfach. In einem anderen Artikel, der vor einem Jahr veröffentlicht wurde, haben wir die Installation, Aktivierung und Konfiguration von WSL ausführlich beschrieben. Im Folgenden finden Sie einige Tipps speziell für den Einsatz des Mosquitto-Brokers an der WSL. Das sind Kleinigkeiten, die uns das Leben leichter machen, und vielleicht finden Sie sie auch nützlich.

  • Wenn Sie die WSL mit ihren Standardeinstellungen aktivieren und Mosquitto auf die einfache und empfohlene Weise installieren, werden Sie es wahrscheinlich über den Paketmanager installieren und als Ubuntu-Dienst ausführen. Das heißt, Mosquitto wird automatisch gestartet, wenn Sie die WSL-Shell starten. Für die Entwicklung empfehlen wir jedoch, den Mosquitto-Dienst zu stoppen und ihn manuell über die Befehlszeile mit dem Flag verbose (-v) neu zu starten. Dadurch wird die Verwendung des tail-Befehls zum Verfolgen der Protokolle vermieden, da Mosquitto im Vordergrund läuft und alle Protokolle nach STDOUT umleitet. Außerdem enthalten die Protokolle nicht alle Informationen, die Sie erhalten, wenn Sie das Programm mit dem Verbose-Flag starten.

Windows-Subsystem für Linux - Mosquitto-Server anhalten und mit Verbose-Flag neu starten

Abb. 06 - Windows-Subsystem für Linux - Mosquitto-Server anhalten und mit Verbose-Flag neu starten

  • Denken Sie daran, dass Sie den WSL-Hostnamen in die zulässigen URLs für die Vernetzung im Metatrader 5-Terminal aufnehmen müssen.

Windows-Subsystem für Linux - Abrufen des WSL-Hostnamens

Abb. 07 - Windows Subsystem für Linux - Ermittlung des WSL-Hostnamens

MetaTrader 5 - Erlaubte URLs in das Optionsmenü des Terminals einbeziehen

Abb. 08 - MetaTrader 5 - Erlaubte URLs in das Optionsmenü des Terminals einfügen

  • Um die Sicherheit zu erhöhen, lassen die neuesten Versionen von Mosquitto standardmäßig nur lokale Verbindungen zu, d. h. Verbindungen vom selben Rechner aus. Um sich von einem anderen Rechner aus zu verbinden - und Ihr Windows-Rechner gilt in diesem Zusammenhang als anderer Rechner - müssen Sie einen einzeiligen Listener für Port 1883 (oder einen anderen Port Ihrer Wahl) in Ihre Mosquitto.conf-Datei aufnehmen.

Windows-Subsystem für Linux - Konfiguration des Mosquitto-Servers für das Abhören von Port 1883

Abb. 09 - Windows-Subsystem für Linux - Konfigurieren des Mosquitto-Servers zum Abhören von Port 1883

  • Schließlich sollten Sie daran denken, dass Mosquitto bei Nicht-TLS-Verbindungen standardmäßig auf Port 1883 läuft. Das Metatrader 5 Terminal erlaubt nur Verbindungen zu den Ports 80 (HTTP) und 443 (HTTPS). Sie müssen also den Datenverkehr von Port 80 auf Port 1883 umleiten. Dies kann mit einem kurzen einzeiligen Befehl geschehen, wenn Sie ein Linux-Dienstprogramm namens redir installieren. Sie können es auch über den Paketmanager installieren.

Windows-Subsystem für Linux - Portumleitung mit dem Dienstprogramm Redir

Abb. 10 - Windows-Subsystem für Linux - Portumleitung mit dem Dienstprogramm Redir durchführen

  • Wenn Sie vergessen, den WSL-Hostnamen in die Liste der zulässigen URLs aufzunehmen oder die Ports umzuleiten, wird die Verbindung verweigert, und wahrscheinlich erscheint ein Fehler wie der folgende in Ihrem Log Experts Tab

MetaTrader 5 - Log-Ausgabe mit Fehler 5273

Abb. 11 - MetaTrader 5 - Protokollausgabe mit dem Fehler 5273 - Senden/Empfangen von Daten vom Socket fehlgeschlagen

Selbst wenn Sie mit Linux nicht vertraut sind, sollte die Konfiguration nicht länger als zehn bis fünfzehn Minuten dauern, wenn alles gut geht. Danach können Sie überprüfen, ob Ihr Abonnementdienst wie vorgesehen funktioniert.

MetaTrader 5 Log Output zeigt: MQTT Subscribe Service gestartet und abonniert

Abb. 12 - MetaTrader 5 Log Output zeigt, dass der MQTT Subscribe Service gestartet und abonniert wurde



Einen PUBLISH MQTT-Dienst schreiben

Der Veröffentlichungsdienst folgt der gleichen Struktur wie der Abonnementdienst, sodass wir Ihnen Zeit sparen und uns hier nicht wiederholen. Ich möchte Sie nur noch einmal daran erinnern, dass Sie für die Veröffentlichungs- und Abonnementdienste eine andere Kundenkennung verwenden sollten. (Wir betonen diesen Client-Identifikator, weil wir durch das Ignorieren dieses kleinen Details in der üblichen Copy-Paste-Routine für faule Entwickler einige Zeit mit der Fehlersuche in einem „Nicht-Fehler“ verschwendet haben, der dazu führte, dass Mosquitto unsere Nachrichten nicht ordnungsgemäß zustellte).

   uchar conn_pkt[];
   conn = new CConnect(host, port);
   conn.SetCleanStart(true);
   conn.SetKeepAlive(3600);
   conn.SetClientIdentifier("MT5_PUB");
   conn.Build(conn_pkt);

Die Veröffentlichung läuft in einer Dauerschleife, bis sie gestoppt wird. Denken Sie daran, denselben Themenfilter zu setzen, den Sie in Ihrem Veröffentlichungsdienst verwenden, und wenn Sie die Kurse - statt der Ticks - aktualisieren möchten, sollte es ausreichen, die Zuweisung der Nutzlast zu GetRates zu entfernen (und die Zuweisung zu GetLastTick zu kommentieren). 

   do
     {
      uchar pub_pkt[];
      pub = new CPublish();
      pub.SetTopicName("MySPX500");
      //string payload = GetRates();
      string payload = GetLastTick();
      pub.SetPayload(payload);
      pub.Build(pub_pkt);
      delete(pub);
     //ArrayPrint(pub_pkt);
      if(!SendPublish(pub_pkt))
        {
         return -1;
         CleanUp();
        }
      ZeroMemory(pub_pkt);
      Sleep(500);
     }
   while(!IsStopped());

Die Bemerkungen über den Anteil des Netzwerkcodes und des MQTT-spezifischen Codes im Abonnementdienst gelten auch hier. Wir brauchen nicht einmal zu prüfen, ob ein PUBACK vorliegt, denn wir werden keines erhalten, da wir QoS 0 verwenden. Es geht also nur darum, die Pakete zu erstellen, eine Verbindung herzustellen und sie zu versenden.

Es ist erwähnenswert, dass wir im Veröffentlichungsdienst auch den Preis für die Zeichenkettenkonvertierung zahlen, zumindest solange, bis unsere Nutzereigenschaft(en) vollständig implementiert ist (sind) und wir mit Sicherheit binäre Daten austauschen können.

string GetLastTick()
  {
   MqlTick last_tick;
   if(SymbolInfoTick("#USSPX500", last_tick))
     {
      string format = "%G-%G-%G-%d-%I64d-%d-%G";
      string out;
      out = TimeToString(last_tick.time, TIME_SECONDS);
      out += "-" + StringFormat(format,
                                last_tick.bid, //double
                                last_tick.ask, //double
                                last_tick.last, //double
                                last_tick.volume, //ulong
                                last_tick.time_msc, //long
                                last_tick.flags, //uint
                                last_tick.volume_real);//double
      Print(last_tick.time,
            ": Bid = ", last_tick.bid,
            " Ask = ", last_tick.ask,
            " Last = ", last_tick.last,
            " Volume = ", last_tick.volume,
            " Time msc = ", last_tick.time_msc,
            " Flags = ", last_tick.flags,
            " Vol Real = ", last_tick.volume_real
           );
      Print(out);
      return out;
     }
   else
      Print("Failed to get rates for #USSPX500");
   return "";
  }

Wenn Sie den Veröffentlichungsdienst auf dem MetaTrader 5-Terminal starten, sollten Sie auf der Registerkarte „Expert“ etwas wie das Folgende sehen.

MetaTrader 5 Log Output zeigt an, dass der MQTT Publish Service gestartet und verbunden ist

Abb. 13 - MetaTrader 5 Log Output zeigt, dass der MQTT Publish Service gestartet und verbunden ist

Sie können das Thema (topic) des Mosquitto-Broker abonnieren und die ausführliche Ausgabe des Brokers überprüfen.

Windows-Subsystem für Linux zeigt Mosquitto-Server-Protokollierung mit Verbose-Flag

Abb. 14 - Windows-Subsystem für Linux zeigt Mosquitto-Server-Protokollierung mit Verbose-Flag

Wenn die Nachricht erfolgreich empfangen wurde, sollten Sie sie auf der Registerkarte das Abonnement sehen.

Windows-Subsystem für Linux zeigt die Ausgabe des Dienstprogramms mosquitto_sub

Abb. 15 - Windows-Subsystem für Linux mit der Ausgabe des Dienstprogramms mosquitto_sub


Aktualisierung des nutzerdefinierten Symbols

Nachdem beide Dienste eingerichtet und mit Ihrem Broker abgeglichen wurden, ist es an der Zeit, sie im Metatrader 5-Terminal auszuführen und die Ergebnisse Ihrer harten Arbeit zu sehen.

MetaTrader 5 Navigator mit MQTT Publish- und Subscribe-Diensten gestartet

Abb. 16 - MetaTrader 5 Navigator mit gestarteten MQTT Publish- und Abo-Services

Wenn alles gut geht, sollten Sie auf der Registerkarte „Ticks“ Ihres Marktbeobachtungsfensters etwas wie das Folgende sehen.

 MetaTrader 5 Marktübersicht, Registerkarte Ticks mit nutzerdefinierten Symbol Tick Updates

Abb. 17 - MetaTrader 5 Marktübersicht, Registerkarte Ticks mit nutzerdefinierten Symbol Tick Updates

In Ihrem nutzerdefinierten Symboldiagramm sollten die Tick-Updates ebenfalls angezeigt werden.

MetaTrader 5 Chart mit nutzerdefinierten Symbol-Tick-Updates

Abb. 18 - MetaTrader 5 Chart mit nutzerdefinierten Symbol-Tick-Updates

Auf der Registerkarte Log Experts sollten Sie eine etwas ausführliche Ausgabe finden. Das liegt daran, dass wir einige Debug-Informationen vorerst zurückgelassen haben. Dort können Sie unter anderem die Ausgabe der PrintArray-Funktion für die ausgetauschten Pakete sehen. Mit dieser Ausgabe kann die Notwendigkeit vermieden werden, einen Paketanalysator wie Wireshark einzusetzen, um den Inhalt der Pakete zu überprüfen.

MetaTrader 5 Log Output in der Registerkarte Expert mit MQTT Services Logging für Entwicklung und Debugging

Abb. 19 - MetaTrader 5 Log Output in der Registerkarte Expert mit MQTT Services Logging für Entwicklung und Debug



Schlussfolgerung

Dieser Artikel stellt einen funktionalen Code für den Austausch von Echtzeitkursen zwischen Brokern und Konten über MQTT vor. Obwohl die Demo auf derselben Metatrader 5-Instanz und demselben Rechner läuft, zeigt sie die Flexibilität und Robustheit von MQTT. Unser nativer MQTT-Client befindet sich in unserer begrenzten Freizeit noch in der Entwicklung. Unser Ziel ist eine vollständig konforme Version bis Juni, die Herausforderungen wie QoS_2-Implementierung und Nutzereigenschaften bewältigt. Wir planen, den Code zu verfeinern und ihn bis Ende April auf GitHub zu veröffentlichen.

Wir freuen uns über Mitwirkende an unserem Open-Source-Projekt, unabhängig von ihren MQL5-Kenntnissen. Unser testgetriebener Entwicklungsansatz gewährleistet einen fehlerfreien Fortschritt, auch für Nicht-Experten. Ausgehend von einfachen Tests haben wir uns kontinuierlich durch die MQL5-Dokumentation gearbeitet. Wir sind bestrebt, unseren Client so lange weiterzuentwickeln, bis er alle MQTT-Standards erfüllt.

Kommen Sie zu uns, auch mit Grundkenntnissen. Ihr Beitrag ist wertvoll. Willkommen an Bord!

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

Beigefügte Dateien |
mqtt-headers.zip (23.78 KB)
mqtt-services.zip (3.51 KB)
mqtt-tests.zip (19.52 KB)
Aufbau eines Modells von Kerzen, Trend und Nebenbedingungen (Teil 1): Für EAs und technische Indikatoren Aufbau eines Modells von Kerzen, Trend und Nebenbedingungen (Teil 1): Für EAs und technische Indikatoren
Dieser Artikel richtet sich an Anfänger und Profi-MQL5-Entwickler. Es stellt einen Code zur Verfügung, um signalgenerierende Indikatoren zu definieren und auf Trends in höheren Zeitrahmen zu beschränken. Auf diese Weise können Händler ihre Strategien verbessern, indem sie eine breitere Marktperspektive einbeziehen, was zu potenziell robusteren und zuverlässigeren Handelssignalen führt.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 15): Support-Vektor-Maschinen mit dem Newtonschen Polynom MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 15): Support-Vektor-Maschinen mit dem Newtonschen Polynom
Support-Vektor-Maschinen klassifizieren Daten auf der Grundlage vordefinierter Klassen, indem sie die Auswirkungen einer Erhöhung der Dimensionalität untersuchen. Es handelt sich um eine überwachte Lernmethode, die angesichts ihres Potenzials, mit mehrdimensionalen Daten umzugehen, ziemlich komplex ist. In diesem Artikel wird untersucht, wie die sehr einfache Implementierung von 2-dimensionalen Daten mit dem Newton'schen Polynom bei der Klassifizierung von Preis-Aktionen effizienter durchgeführt werden kann.
Lernen Sie Schritt für Schritt, wie Sie die Fair Value Gap (FVG)/Ungleichgewichte handeln können: Ein Ansatz des Smart Money-Konzepts Lernen Sie Schritt für Schritt, wie Sie die Fair Value Gap (FVG)/Ungleichgewichte handeln können: Ein Ansatz des Smart Money-Konzepts
Eine Schritt-für-Schritt-Anleitung zur Erstellung und Implementierung eines automatisierten Handelsalgorithmus in MQL5 auf der Grundlage der Handelsstrategie Fair Value Gap (FVG). Eine detaillierte Anleitung zur Erstellung eines Expertenberaters, die sowohl für Anfänger als auch für erfahrene Händler nützlich sein kann.
Selbstoptimierende Expert Advisors in MQL5 erstellen Selbstoptimierende Expert Advisors in MQL5 erstellen
Bauen wir Expert Advisor, die in die Zukunft blicken und sich an jeden Markt anpassen können.