Handelsereignisse in MetaTrader 5
Einleitung
Alle Befehle, Handels-Operationen auszuführen, werden vom MetaTrader 5 Client-Terminal mittels Anfrage senden an den Handels-Server weitergeleitet. Jede Anfrage sollte gemäß der angeforderten Operation korrekt befüllt sein, da sie sonst nicht die erste Prüfstufe passieren und vom Server nicht für eine weitere Bearbeitung akzeptiert wird.
Vom Handels-Server akzeptierte Anfragen werden in Form von Orders gespeichert, die entweder offen sind oder sofort durch zum Marktkurs ausgeführt werden. Orders werden solange auf dem Server gespeichert, bis sie befüllt oder storniert werden. Das Ergebnis einer Order-Ausführung ist ein Abschluss.
Ein Abschluss ändert die Handelsposition durch ein gegebenes Symbol, das die Position öffnen, schließen, erhöhen, verringern oder umkehren kann. Daher ist eine offene Position immer das Ergebnis der Ausführung eines oder mehrerer Abschlüsse. Detaillierte Informationen finden Sie im Beitrag Orders, Positions und Abschlüsse in MetaTrader 5.
Der vorliegende Beitrag beschreibt das Konzept, die Begriffe und Vorgänge, die innerhalb des Zeitraums zwischen dem Absenden einer Anfrage und ihrem Verschieben in die Handels-History, nachdem sie bearbeitet wurde, vor sich gehen.
Absenden einer Anfrage vom Client-Terminal zum Handels-Server
Um eine Handels-Operation durchzuführen, sollte man eine Order an das Handelssystem senden. Eine Anfrage wird stets an den Handels-Server durch Abschicken einer Order vom Client-Terminal gesendet. Die Struktur der Anfrage muss, ungeachtet des Handels, stets korrekt befüllt sein - entweder manuell oder via eines MQL5 Programms.
Um eine Handels-Operation manuell auszuführen, müssen Sie da Dialogfenster zum Befüllen einer Handelsanfrage öffnen oder F9 drücken. Bei automatisch via MQL5 durchgeführtem Handel, werden entsprechende Anfragen mittels der OrderSend() Funktion gesendet. Da eine große Anzahl an nicht korrekten Anfragen das unerwünschte Überladen des Handels-Servers verursachen kann, muss jede Anfrage vor dem Absenden mit Hilfe der OrderCheck() Funktion überprüft werden. Das Überprüfungsergebnis einer Anfrage wird einer Variable übertragen, die durch die MqlTradeCheckresult Struktur beschrieben ist.
Sobald eine Anfrage beim Handels-Server ankommt, durchläuft sie ihre primäre Überprüfung:
- verfügen Sie über genügend Vermögen zur Durchführung der Handels-Operation?
- ist der angegebene Kurs korrekt: Eröffnungskurse, Stop Loss, Take Profit, usw.?
- ist der angegebene Kurs im Kurs-Flow für sofortige Ausführung vorhanden?
- fehlen Stop Loss- und Take Profit-Levels im Markt-Ausführungsmodus?
- ist das Volumen korrekt: Minimum- und Maximal-Volumen, Schritt, Maximalvolumen der Position (SYMBOL_VOLUME_MIN, SYMBOL_VOLUME_MAX, SYMBOL_VOLUME_STEP und SYMBOL_VOLUME_LIMIT)?
- Status des Symbols: Quoten- oder Handelssitzung; Möglichkeit des Handels per diesem Symbol, ein festgelegter Handelsmodus (z.b. nur Schließen von Positions), usw.
- Status des Handels-Accounts: verschiedene Begrenzungen für spezifische Arten von Accounts;
- andere Prüfungen, je nach der angefragten Handels-Operation.
Sobald eine Anfrage die primäre Überprüfung auf Exaktheit besteht, wird sie zur Anfrage, die auf auf ihre Bearbeitung wartet, platziert. Als Ergebnis der Bearbeitung einer Anfrage wird eine Order (Befehl eine Handels-Operation durchzuführen) in der Basis des Handels-Servers erzeugt. Es gibt jedoch zwei Arten von Anfragen, die nicht zur Erzeugung einer Order führen:
- die Anfrage eine Position zu verändern (ihren Stop Loss und/oder Take Profit zu ändern);
- die Anfrage eine offene Order zu modifizieren (ihre Kurslevels und ihr Ablaufdatum).
Der Client-Terminal erhält die Meldung, dass die Anfrage akzeptiert und in das Handels-Subsystem der MetaTrader 5 Plattform platziert ist. Der Server platziert die akzeptierte Anfrage in die Anfrage-Warteschlange zur weiteren Bearbeitung, mit den folgenden Ergebnissen:
- Platzierung einer offenen Order;
- Ausführung einer sofortigen Order nach Marktkurs;
- Modifizierung einer Order oder Position.
Die 'Lebensdauer' einer Anfrage in der Warteschlange des Servers ist auf 180 Sekunden begrenzt. Sobald diese Zeit überschritten ist, wird die Anfrage aus der Warteschlange entfernt
Absenden von Handelsereignissen vom Handels-Server zum Client-Terminal
Das Ereignis-Modell und die Funktionen der Behandlung von Ereignissen sind in der MQL5 Sprache implementiert. Das heißt, dass bei einer Antwort auf jedes vorab festgelegte Ereignis die MQL5 Ausführungsumgebung die entsprechende Funktion aufruft - den Event-Anwender. Zur Bearbeitung von Handelsereignissen gibt es eine vorab festgelegte Funktion: OnTrade(). Der Code zur Arbeit mit Order, Positions und Abschlüssen muss in diese Funktion gesetzt werden. Diese Funktion wird nur für Expert Advisors aufgerufen und nicht in Indikatoren und Scripts verwendet, selbst wenn Sie dort eine Funktion mit demselben Namen und Typ hinzufügen.
Die Handelsereignisse werden vom Server in folgenden Fällen erzeugt:
- Veränderung aktiver Order,
- Veränderung von Positions,
- Veränderung von Abschlüssen,
- Veränderung der Handels-History.
Bitte beachten Sie, dass eine Operation durchaus das Auftreten mehrerer Ereignisse auslösen kann. So führt z.B. das Auslösen einer offenen Order zu folgenden zwei Ereignissen:
- Auftauchen eines Abschlusses, der in die Handels-History geschrieben wird;
- Verschiebung der ausstehenden Order von der Liste der aktiven in die Liste der History-Orders (die Order wird also in die History verschoben).
Ein weiteres Beispiel für mehrere Ereignisse ist die Durchführung von mehreren Abschlüssen auf Grundlage einer einzigen Order, falls das erforderliche Volumen nicht aus einem einzigen Gegenangebot erhalten werden kann. Der Handels-Server erzeugt und sendet die Meldungen über jedes Ereignis an den Client-Terminal. Deshalb kann die OnTrade() Funktion mehrere Male für ein anscheinend nur einziges Ereignis aufgerufen werden. Dies ist ein einfaches Beispiel des Bearbeitungsvorgangs einer Order im Handels-Subsystem der MetaTrader 5 Plattform.
Hier kommt das Beispiel: Während eine ausstehende Order über den Kauf von 10 Posten von EURUSD auf ihre Ausführung wartet, erscheinen Gegenangebote zum Verkauf von 1, 4 und 5 Posten. Zusammen ergeben diese drei Anfragen das erforderliche Volumen von 10 Posten, also werden sie nacheinander durchgeführt, wenn die Befüllungs-Direktive die teilweise Durchführung von Handels-Operationen erlaubt.
Als Ergebnis der Ausführung der 4 Orders, führt der Server nun 3 Abschlüsse von 1, 4 und 5 Posten auf Grundlage der bestehenden Gegen-Anfragen aus. Wie viele Handelsereignisse werden in diesem Fall erzeugt? Die erste Gegenanfrage für den Verkauf eines Postens führt zur Ausführung des Abschlusses von 1 Posten. Das ist das erste Handelsereignis (Abschluss 1 Postens). Doch die ausstehende Order des Kaufs von 10 Posten ist ebenfalls geändert, und zwar jetzt in eine Order zum Kauf von 9 Posten EURUSD. Die Änderung des Volumens der ausstehenden Order ist das zweite Handelsereignis (Volumenänderung einer ausstehenden Order).
Die die anderen zwei Handelsereignisse werden für den zweiten Abschluss von 4 Posten erzeugt, und eine Meldung über jedes Ereignis wird an den Client-Terminal geschickt, der die ursprünglich ausstehende Order für den Kauf von 10 Posten EURUSD initiiert hat.
Der letzte Abschluss von 5 Posten führt seinerseits zum Auftreten von drei Handelsereignissen:
- den Abschluss von 5 Posten,
- die Veränderung des Volumens,
- das Verschieben der Order in die Handels-History.
Als Ergebnis der Ausführung des Abschlusses erhält der Client-Terminal 7 Handelsereignisse, Trade, nacheinander (unter der Voraussetzung, dass die Verbindung zwischen Client-Terminal und Trade-Server stabil ist und keine Meldung verlorengehen). Diese Meldungen müssen in einem Expert Advisor mit Hilfe der OnTrade() Funktion verarbeitet werden.
Bearbeitung von Orders durch den Handels-Server
Alle Order, die auf ihre Ausführung warten, werden am Ende in die History verschoben - entweder wird die Bedingung für ihre Ausführung erfüllt oder sie werden storniert. Die Stornierung einer Order kann auf unterschiedliche Weise ablaufen:
- Ausführung eines Abschlusses auf Grundlage der Order;
- Ablehnung der Order durch einen Händler;
- Stornierung der Order auf Wunsch des Händlers (manuelle oder automatische Anfrage von einem MQL5 Programm);
- Ablauf der Order, der entweder vom Händler beim Sendern der Anfrage oder durch Handelsbedingungen des gegebenen Handelssystems festgelegt wird;
- mangelndes Vermögen im Handels-Account zur Ausführung des Abschlusses zum Zeitpunkt an dem die Bedingungen für eine Ausführung erfüllt sind;
- Order wird aufgrund der Befüllungs-Direktive storniert (eine teilweise befüllte Order wird storniert).
Egal aus welchem Grund eine aktive Order in die History verschoben wird, der Client-Terminal erhält eine Meldung über die Veränderung. Meldungen über das Handelsereignis gehen nicht an alle Client-Terminals, sondern nur an die mit dem entsprechenden Account verbundenen Terminals.
Und deshalb sagt die Dokumentation der OrderSend() Funktion ja auch:
Gelieferter Wert
Im Falle einer erfolgreichen Basisprüfung einer Anfrage liefert die OrderSend() Funktion true - . . Dies ist kein Anzeichen für eine erfolgreiche Ausführung einer Handels-Operation . Für eine detailliertere Beschreibung des Ausführungsergebnisses der Funktion, analysieren Sie die Felder der MqlTradeResult Struktur.
Aktualisierung von Handel und History im Client-Terminal
Meldungen über Handelsereignisse und Veränderungen in der Handels-History werden durch separate Kanäle geschickt. Beim Senden einer Kauf-Anfrage mit Hilfe der OrderSend() Funktion, bekommen Sie das Order-Ticket, das als Ergebnis der erfolgreichen Prüfung der Anfrage erstellt wird. Gleichzeitig kann die Order selbst u.U. nicht im Client-Terminal erscheinen, und ein Versuch, sie mittels OrderSelect() auszuwählen, kann scheitern.
In der obigen Abbildung erkennen Sie, wie der Handels-Server das Order-Ticket an das MQL5 Programm schickt. Doch die Meldung über das Handelsereignis Handel (Auftauchen einer neuen Order) ist noch nicht angekommen. Die Meldung über die Veränderung in der Liste der aktiven Order ist ebenfalls noch nicht angekommen.
Es kann auch durchaus die Fall eintreten, dass die Meldung Handel über das Auftauchen einer neuen Order im Programm ankommt, wenn ein Abschluss auf ihrer Basis bereits durchgeführt worden ist und die Order somit bereits nicht mehr in der Liste der aktiven Order ist, sondern sich bereits in History befindet. Dies ist eine reale Situation, da die Verarbeitungsgeschwindigkeit von Anfragen viel höher ist als die aktuelle Geschwindigkeit der Kommunikation von Meldungen durch ein Netzwerk.
Umgang mit Handelsereignissen in MQL5
Alle Operationen auf dem Handels-Server und das Versenden von Meldungen zu Handelsereignissen werden asymmetrisch ausgeführt. Es gibt nur eine Methode, um herauszufinden, welche Veränderungen tatsächlich im Handels-Account stattgefunden haben. Nämlich sich den Handels-Status und die Handels-History zu merken und sie mit dem neuen Status abzugleichen.
Mit folgendem Algorithmus können Sie Handelsereignisse im Expert Advisor nachverfolgen:
- Deklarierung der Order-, Positions- und Abschlusszähler in globalem Umfang;
- Festlegung der Intensität der Handels-History, die für den MQL5 Programm-Cache angefragt werden wird. Je mehr History wir in den Cache laden, umso mehr Ressourcen des Terminals und Computers werden aufgebraucht;
- Initialisierung der Order-, Positions- und Abschlusszähler in der OnInit Funktion;
- Festlegung der Anwender-Funktionen, in denen die Handels-History für den Cache angefordert wird;
- Nach dem Laden der Handels-History finden wir hier auch heraus, was mit dem Handels-Account geschehen ist, und zwar durch einen Vergleich des gemerkten und aktuellen Status.
Dies ist der einfachste Algorithmus, da er uns erlaubt, herauszufinden, ob die Zahl der offenen Positions (Order, Abschlüsse) verändert wurde und in welche Richtung diese Veränderung stattfand. Gibt es Veränderungen, dann können wir weitere detaillierte Informationen erhalten. Hat sich die Zahl der Order nicht verändert, wurden die Order jedoch an sich modifiziert, ist hierfür ein andere Vorgehensweise nötig. Sie wird in diesem Beitrag jedoch nicht behandelt.
Veränderungen des Zählers können in den OnTrade() und OnTick() Funktionen eines Expert Advisors nachgeprüft werden.
Schreiben wir Schritt für Schritt ein Programmbeispiel.
1. Der Order-, Abschluss- und Positionszähler im globalen Umfang.
int orders; // number of active orders int positions; // number of open positions int deals; // number of deals in the trade history cache int history_orders; // number of orders in the trade history cache bool started=false; // flag of initialization of the counters
2. Die Intensität der Handels-History, die in den Cache geladen wird, wird in der Eingabevariable days festgelegt (lädt die Handels-History für die in dieser Variable bestimmte Anzahl an Tagen).
input int days=7; // depth of the trade history in days //--- set the limit of the trade history on the global scope datetime start; // start date of the trade history in cache datetime end; // end date of the trade history in cache
3. Initialisierung der Zähler und die Begrenzungen der Handels-History. Wenn Sie die Initialisierung der Zähler in der InitCounters() Funktion ausführen, können Sie den Code besser lesen:
int OnInit() { //--- end=TimeCurrent(); start=end-days*PeriodSeconds(PERIOD_D1); PrintFormat("Limits of the trade history to be loaded: start - %s, end - %s", TimeToString(start),TimeToString(end)); InitCounters(); //--- return(0); }
Die InitCounters() Funktion versucht die Handels-History in den Cache zu laden. Wenn dies erfolgreich war, initialisiert sie alle Zähler. Wenn die History erfolgreich geladen ist, wird der Wert der globalen 'gestartet' Variable auf 'true' gesetzt, was anzeigt, dass die Zähler erfolgreich initialisiert wurden.
//+------------------------------------------------------------------+ //| initialization of the counters of positions, orders and deals | //+------------------------------------------------------------------+ void InitCounters() { ResetLastError(); //--- load history bool selected=HistorySelect(start,end); if(!selected) { PrintFormat("%s. Failed to load the history from %s to %s to the cache. Error code: %d", __FUNCTION__,TimeToString(start),TimeToString(end),GetLastError()); return; } //--- get current values orders=OrdersTotal(); positions=PositionsTotal(); deals=HistoryDealsTotal(); history_orders=HistoryOrdersTotal(); started=true; Print("The counters of orders, positions and deals are successfully initialized"); }
4. Die Suche nach Veränderungen im Status des Handels-Account wird in den OnTick() und OnTrade() Anwendern vollzogen. Als erstes wird die Variable 'gestartet' geprüft: ist ihr Wert 'true', wird die SimpleTradeProcessor() Funktion aufgerufen. Ansonsten wird die Initialisierungsfunktion der Zähler InitCounters() aufgerufen.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(started) SimpleTradeProcessor(); else InitCounters(); } //+------------------------------------------------------------------+ //| called when the Trade event occurs | //+------------------------------------------------------------------+ void OnTrade() { if(started) SimpleTradeProcessor(); else InitCounters(); }
5. Die Funktion SimpleTradeProcessor() prüft, ob sich die Anzahl der Order, Abschlüsse und Positions, verändert hat. Nachdem alle Prüfungen durchgeführt sind, rufen wir die CheckStartDateInTradeHistory() Funktion auf, die den 'Start' Wert ggf. näher an den aktuellen Augenblick heran schiebt.
//+------------------------------------------------------------------+ //| simple example of processing changes in trade and history | //+------------------------------------------------------------------+ void SimpleTradeProcessor() { end=TimeCurrent(); ResetLastError(); //--- load history bool selected=HistorySelect(start,end); if(!selected) { PrintFormat("%s. Failed to load the history from %s to %s to the cache. Error code: %d", __FUNCTION__,TimeToString(start),TimeToString(end),GetLastError()); return; } //--- get current values int curr_orders=OrdersTotal(); int curr_positions=PositionsTotal(); int curr_deals=HistoryDealsTotal(); int curr_history_orders=HistoryOrdersTotal(); //--- check whether the number of active orders has been changed if(curr_orders!=orders) { //--- number of active orders is changed PrintFormat("Number of orders has been changed. Previous number is %d, current number is %d", orders,curr_orders); /* other actions connected with changes of orders */ //--- update value orders=curr_orders; } //--- change in the number of open positions if(curr_positions!=positions) { //--- number of open positions has been changed PrintFormat("Number of positions has been changed. Previous number is %d, current number is %d", positions,curr_positions); /* other actions connected with changes of positions */ //--- update value positions=curr_positions; } //--- change in the number of deals in the trade history cache if(curr_deals!=deals) { //--- number of deals in the trade history cache has been changed PrintFormat("Number of deals has been changed. Previous number is %d, current number is %d", deals,curr_deals); /* other actions connected with change of the number of deals */ //--- update value deals=curr_deals; } //--- change in the number of history orders in the trade history cache if(curr_history_orders!=history_orders) { //--- the number of history orders in the trade history cache has been changed PrintFormat("Number of orders in the history has been changed. Previous number is %d, current number is %d", history_orders,curr_history_orders); /* other actions connected with change of the number of order in the trade history cache */ //--- update value history_orders=curr_history_orders; } //--- check whether it is necessary to change the limits of trade history to be requested in cache CheckStartDateInTradeHistory(); }
Die CheckStartDateInTradeHistory() Funktion berechnet das Startdatum der Anfrage der Handels-History für den aktuellen Moment (curr_start) und vergleicht ihn mit der 'Start' Variable. Ist die Differenz zwischen beiden > 1 Tag, wird 'Start' korrigiert, und die Zähler der Order- und Abschluss-History werden aktualisiert.
//+------------------------------------------------------------------+ //| Changing start date for the request of trade history | //+------------------------------------------------------------------+ void CheckStartDateInTradeHistory() { //--- initial interval, as if we started working right now datetime curr_start=TimeCurrent()-days*PeriodSeconds(PERIOD_D1); //--- make sure that the start limit of the trade history period has not gone //--- more than 1 day over intended date if(curr_start-start>PeriodSeconds(PERIOD_D1)) { //--- we need to correct the date of start of history loaded in the cache start=curr_start; PrintFormat("New start limit of the trade history to be loaded: start => %s", TimeToString(start)); //--- now load the trade history for the corrected interval again HistorySelect(start,end); //--- correct the counters of deals and orders in the history for further comparison history_orders=HistoryOrdersTotal(); deals=HistoryDealsTotal(); } }
Den kompletten Code des Expert Advisor DemoTradeEventProcessing.mq5 finden Sie im Anhang dieses Beitrags.
Fazit
Alle Operationen der Online Handels-Plattform MetaTrader 5 werden asynchron ausgeführt. Die Meldungen über alle Veränderungen in einem Handels-Account werden unabhängig voneinander übermittelt. Es macht daher keinen Sinn, einzelne Ereignisse nach der Regel "Eine Anfrage - Ein Handelsereignis" nach verfolgen zu wollen. Wenn Sie wirklich exakt feststellen müssen, was sich genau verändert hat, wenn ein Handel kommen sollte, sollten Sie all Ihre Abschlüsse, Positions und Order bei jedem Aufruf des OnTrade-Anwenders analysieren und ihren aktuellen Status mit dem vorigen vergleichen.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/232
- 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.