
Ereignisse im МetaТrader 4
Einleitung
In diesem Artikel geht es darum, wie man solche Ereignisse wie Eröffnung, Schließung und Modifizierung von Orders im Terminal МetaТrader 4 verfolgen kann. Der Artikel setzt Grundkenntnisse über das Terminal und Programmieren in MQL4 voraus.
1. Was sind Ereignisse und warum muss man sie verfolgen?
Für die Umsetzung einiger Strategien genügt nicht zu wissen, ob es eine durch den Experten eröffnete Position gibt. Manchmal muss man gerade den Moment der Eröffnung, Schließung oder Modifizierung einer Position oder der Auslösung einer Pending Order erwischen.
In der MQL4 gibt es keine eingebauten Funktionen, die diese Aufgabe lösen können, es gibt aber alles Notwendige, um solches Instrument zu erstellen. Damit beschäftigen wir uns jetzt.
2. Das Definitionsprinzip des Ereignisses
Wie versteht man, dass ein Ereignis geschehen ist? Was heißt überhaupt ein Ereignis? Wenn man diese Fragen beantwortet, kommt man zum folgenden Ergebnis: ein Ereignis ist eine Statusveränderung einer Order oder einer Position. Im Zusammenhang mit unserer Aufgabe ist das z.B. eine Veränderung der Zahl eröffneter Positionen oder des Stop-Loss-Levels der Position.
Wie kann man feststellen, dass ein Ereignis gerade ebene geschehen
ist? Das ist sehr einfach. Dafür muss man den verfolgten Wert speichern
(in unserem Beispiel - die Zahl der Positionen), und in den nächsten
Moment, z.B. beim nächsten Tick, ihn mit dem neu erhobenen Wert
vergleichen. Erstellen wir einen einfachen Experten, der uns über die
Veränderung der Zahl der Positionen informiert.
int start() { static bool first = true; static int pre_OrdersTotal = 0; int _OrdersTotal = OrdersTotal(); // Wenn der Experte zum ersten Mal gestartet wird, ist die Zahl der Orders beim vorherigen Tick nicht bekannt. // Deswegen ihn einfach speichern, den ersten Start anmerken und beenden. if ( first ) { pre_OrdersTotal = _OrdersTotal; first = false; return(0); } // Die Zahl der Positionen beim vorherigen Tick mit der laufenden Zahl vergleichen // Wenn sich die Zahl verändert hat, die Meldung anzeigen if ( _OrdersTotal > pre_OrdersTotal ) Alert( "Die Zahl der Positionen ist gestiegen! früher - ", pre_OrdersTotal, ", jetzt - ", _OrdersTotal ); if ( _OrdersTotal < pre_OrdersTotal ) Alert( "Die Zahl der Positionen ist gesunken! früher - ", pre_OrdersTotal, ", jetzt - ", _OrdersTotal ); // Die Zahl der Positionen speichern pre_OrdersTotal = _OrdersTotal; return(0); }
Es sind einige Besonderheiten anzumerken:
- Die Variablen first und pre_OrdersTotal sind als static deklariert. Dadurch werden ihre Werte beim Beenden der start()Funktion nicht auf Null gesetzt. Globale Variablen (außerhalb der Funktion) können Alternative zu statischen Variablen werden, ihre große Anzahl kann aber zu Verwirrungen mit den Namen führen (aus Versehen kann man eine gleichnamige Variable innerhalb der Funktion deklarieren, dann können Konflikte entstehen). Deswegen werden alle Variablen im Funktionskörper deklariert.
- Der Experte informiert über die Veränderung der Zahl eröffneter Positionen und Pending Orders (die Funktion OrdersTotal() liefert ihre Gesamtzahl).
- Der Experte informiert nicht über die Auslösung einer Pending Order, weil sich der Wert der OrdersTotal() in diesem Fall nicht ändert.
- Beim ersten Start kann der Experte die Veränderung der Orderzahl nicht erkennen, weil ihm die Zahl beim vorherigen Tick unbekannt ist.
- Die Meldung erscheint nur beim nächsten Tick des Symbols, im dessen
Chart der Experte arbeitet. Andere Startereignisse hat der Experte
nicht.
Das letzte Problem kann man lösen, indem man den Funktionskörper
Start im Zyklus platziert. So wird die Überprüfung nicht jeden Tick,
sondern in einem angegebenen Zeitintervall durchgeführt:
int start() { static bool first = true; static int pre_OrdersTotal = 0; int _OrdersTotal = OrdersTotal(); // Wenn der Experte zum ersten Mal gestartet wird, ist die Zahl der Orders beim vorherigen Tick nicht bekannt. // Deswegen ihn einfach speichern, den ersten Start anmerken und beenden. if ( first ) { pre_OrdersTotal = _OrdersTotal; first = false; return(0); } while ( !IsStopped() ) { _OrdersTotal = OrdersTotal(); // Die Zahl der Positionen beim vorherigen Tick mit der laufenden Zahl vergleichen // Wenn sich die Zahl verändert hat, die Meldung anzeigen if ( _OrdersTotal > pre_OrdersTotal ) Alert( "Die Zahl der Positionen ist gestiegen! früher - ", pre_OrdersTotal, ", jetzt - ", _OrdersTotal ); if ( _OrdersTotal < pre_OrdersTotal ) Alert( "Die Zahl der Positionen ist gesunken! früher - ", pre_OrdersTotal, ", jetzt - ", _OrdersTotal ); // Die Zahl der Positionen speichern pre_OrdersTotal = _OrdersTotal; Sleep(100); } return(0); }
In dieser Version erscheint die Meldung über die Veränderung der Zahl der Positionen umgehend - Sie können das selbst prüfen.
3. Ereignisse filtern: Kriterien
Bei der laufenden Umsetzung informiert uns der Experte über die
Erscheinung der Positionen auf allen Symbolen. Häufig wird aber nur die
Information über die Orders auf dem laufenden Symbol benötigt. Darüber
hinaus werden die Orders, die einem Experten gehören, mit einer
MagicNumber markiert. Lassen Sie uns die Ereignisse nach diesen zwei
Kriterien "filtern", d.h. über die Veränderungen der Zahl der Orders und
Positionen nur mit der angegebenen MagicNumber und nur auf dem
laufenden Symbol informieren.
extern int MagicNumber = 0; int start() { static bool first = true; static int pre_OrdersTotal = 0; int _OrdersTotal = 0, now_OrdersTotal = 0, _GetLastError = 0; while ( !IsStopped() ) { _OrdersTotal = OrdersTotal(); now_OrdersTotal = 0; for ( int z = _OrdersTotal - 1; z >= 0; z -- ) { if ( !OrderSelect( z, SELECT_BY_POS ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError ); continue; } // die Zahl der Orders auf dem laufenden Symbol und der Orders mit der angegebenen MagicNumber zählen if ( OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol() ) now_OrdersTotal ++; } // die Information nur in dem Fall anzeigen, wenn dies nicht der erste Start des Experten ist if ( !first ) { // Die Zahl der Positionen beim vorherigen Tick mit der laufenden Zahl vergleichen // Wenn sich die Zahl verändert hat, die Meldung anzeigen if ( now_OrdersTotal > pre_OrdersTotal ) Alert( Symbol(), ": die Zahl der Positionen mit der MagicNumber ", MagicNumber, " ist gestiegen! Früher - ", pre_OrdersTotal, ", jetzt - ", now_OrdersTotal ); if ( now_OrdersTotal < pre_OrdersTotal ) Alert( Symbol(), ": die Zahl der Positionen mit der MagicNumber ", MagicNumber, " ist gesunken! Früher - ", pre_OrdersTotal, ", jetzt - ", now_OrdersTotal ); } else { first = false; } //---- die Zahl der Positionen speichern pre_OrdersTotal = now_OrdersTotal; Sleep(100); } return(0); }
4. Konkretisierung
Die Gesamtzahl der Orders zu wissen ist gut, am häufigsten braucht man aber eine konkretere Information – z.B. "wurde eine Buy- oder Sell-Position eröffnet?", "wurde eine Pending Order ausgelöst?", "wurde eine Position beim Stop-Loss, Take Profit oder manuell geschlossen?". Erstellen wir eine ausführliche Liste der Ereignisse, die verfolgt werden müssen und teilen sie in Gruppen auf.
- Eröffnung der Position
- "Marktposition"
- Buy
- Sell
- Pending Order
- Buy-Limit
- Sell-Limit
- Buy-Stop
- Sell-Stop
- "Marktposition"
- Auslösung der Order
- Buy-Limit
- Sell-Limit
- Buy-Stop
- Sell-Stop
- Schließung der Position
- "Marktposition"
- Buy
- Stop-Loss
- Take Profit
- manuell (weder Stop-Loss noch Take Profit)
- Sell
- Stop-Loss
- Take Profit
- manuell
- Buy
- Pending Order (Löschung)
- Buy-Limit
- Verfall
- manuell
- Sell-Limit
- Verfall
- manuell
- Buy-Stop
- Verfall
- manuell
- Sell-Stop
- Verfall
- manuell
- Buy-Limit
- "Marktposition"
- Modifizierung der Position
- "Marktposition"
- Buy
- Stop-Loss
- Take Profit
- Sell
- Stop-Loss
- Take Profit
- Buy
- Pending Order
- Buy-Limit
- Eröffnungspreis
- Stop-Loss
- Take Profit
- Verfall
- Sell-Limit
- Eröffnungspreis
- Stop-Loss
- Take Profit
- Verfall
- Buy-Stop
- Eröffnungspreis
- Stop-Loss
- Take Profit
- Verfall
- Sell-Stop
- Eröffnungspreis
- Stop-Loss
- Take Profit
- Verfall
- Buy-Limit
- "Marktposition"
Vor der Umsetzung des Algorithmus muss festgestellt werden, ob alle aufgelisteten Ereignisse benötigt werden. Wenn man sich vornimmt, einen Experten zu schreiben, der über alle Veränderungen auf allen Positionen informiert (oder Berichte schreibt), dann müssen alle diese Ereignisse berücksichtigt werden. Unser Ziel ist aber dem handelnden Experten helfen zu verstehen,was mit seinen Positionen geschieht. In diesem Fall kann man die Liste deutlich kürzen: Eröffnung von Positionen, Platzierung von Pending Orders, alle Elemente der Modifizierung und alle "manuellen" Schließungen können entfernt werden – diese Ereignisse generiert der Expert Advisor selbst (ohne ihn können sie nicht geschehen). Was haben wir jetzt:
- Auslösung der Order
- Buy-Limit
- Sell-Limit
- Buy-Stop
- Sell-Stop
- Schließung der Position
- "Marktposition"
- Buy
- Stop-Loss
- Take Profit
- Sell
- Stop-Loss
- Take Profit
- Buy
- Pending Order (Verfall)
- Buy-Limit
- Sell-Limit
- Buy-Stop
- Sell-Stop
- "Marktposition"
So sieht die Liste viel besser aus, und man kann anfangen, den Code zu schreiben. Nur noch eine kurze Anmerkung, es gibt mehrere Varianten, die Art und Weise der Positionschließung (SL, TP) festzustellen:
- Wenn die Gesamtzahl der Positionen sinkt, nach einer Position mit der maximalen Schließungszeit in der Historie suchen und nach ihrer Parametern feststellen, wie diese geschlossen wurde;
- Die Ticketnummer aller eröffneten Positionen speichern, dann die "verschwundene" Position nach der Ticketnummer in der Historie suchen.
Die erste Variante ist einfacher umzusetzen, sie kann aber falsche Informationen vermitteln. Wenn zwei Positionen bei einem Tick geschlossen werden - eine von Hand und die andere beim Stop-Loss, findet der Experte die Position mit der maximalen Schließungszeit, generiert aber zwei gleiche Ereignisse (wenn die letzte Position "manuell" geschlossen wurde, werden beide Ereignisse als "manuelle Schließung" betrachtet). Dann kann der Experte nicht erkennen, dass nur eine seiner Positionen beim Stop-Loss geschlossen wurde.
Unser Ziel ist deswegen, einen möglichst korrekten Code zu schreiben, um diese Probleme zu vermeiden.
extern int MagicNumber = 0; // das Array eröffneter Positionen beim vorherigen Tick int pre_OrdersArray[][2]; // [Anzahl der Positionen][№ des Tickets, Positionstyp] int start() { // die Flagge des ersten Starts static bool first = true; // der Code des letzten Fehlers int _GetLastError = 0; // die Gesamtzahl der Positionen int _OrdersTotal = 0; // die Anzahl der Positionen, die den Kriterien entsprechen (das laufende Symbol und die angegebene MagicNumber), // beim laufenden Tick int now_OrdersTotal = 0; // die Anzahl der Positionen, die den Kriterien entsprechen (das laufende Symbol und die angegebene MagicNumber), // beim vorherigen Tick static int pre_OrdersTotal = 0; // das Array eröffneter Positionen beim vorherigen Tick int now_OrdersArray[][2]; // [№ in der Liste][Ticketnummer, Positionstyp] // die laufende Nummer der Position im Array now_OrdersArray (für Suche) int now_CurOrder = 0; // die laufende Nummer der Position im Array pre_OrdersArray (für Suche) int pre_CurOrder = 0; // das Array, wo geschlossene Positionen jeden Typs gespeichert werden int now_ClosedOrdersArray[6][3]; // [Ordertyp][Schliesungstyp] // Array, wo ausgelöste Pending Orders gespeichert werden int now_OpenedPendingOrders[4]; // [Ordertyp] (es gibt insgesamt vier Typen von Pending Orders) // vorläufige Flaggen bool OrderClosed = true, PendingOrderOpened = false; // temporäre Variablen int ticket = 0, type = -1, close_type = -1; //+------------------------------------------------------------------ //| Endlosschleife //+------------------------------------------------------------------ while ( !IsStopped() ) { // die Gesamtzahl der Positionen speichern _OrdersTotal = OrdersTotal(); // die Größe des Arrays eröffneter Positionen entsprechend der aktuellen Anzahl ändern ArrayResize( now_OrdersArray, _OrdersTotal ); // das Array auf Null setzen ArrayInitialize( now_OrdersArray, 0.0 ); // die Zahl der Positionen, die den Kriterien entsprechen, auf Null setzen. now_OrdersTotal = 0; // die Arrays geschlossener Positionen und Pending Orders auf Null setzen ArrayInitialize( now_ClosedOrdersArray, 0.0 ); ArrayInitialize( now_OpenedPendingOrders, 0.0 ); //+------------------------------------------------------------------ //| In allen Positionen suchen und nur diejenigen ins Array schreiben, die //| den Kriterien entsprechen //+------------------------------------------------------------------ for ( int z = _OrdersTotal - 1; z >= 0; z -- ) { if ( !OrderSelect( z, SELECT_BY_POS ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError ); continue; } // die Zahl der Orders auf dem laufenden Symbol und der Orders mit der angegebenen MagicNumber zählen if ( OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol() ) { now_OrdersArray[now_OrdersTotal][0] = OrderTicket(); now_OrdersArray[now_OrdersTotal][1] = OrderType(); now_OrdersTotal ++; } } // die Größe des Arrays eröffneter Positionen hinsichtlich der Zahl der Positionen, die den Kriterien entsprechen. ArrayResize( now_OrdersArray, now_OrdersTotal ); //+------------------------------------------------------------------ //| in der der Liste der Positionen beim vorherigen Tick suchen und geschlossene Positionen sowie ausgelöste //| Pending Orders zählen //+------------------------------------------------------------------ for ( pre_CurOrder = 0; pre_CurOrder < pre_OrdersTotal; pre_CurOrder ++ ) { // das Ticket und den Ordertyp speichern ticket = pre_OrdersArray[pre_CurOrder][0]; type = pre_OrdersArray[pre_CurOrder][1]; // annehmen, wenn dies eine Position war, wurde sie geschlossen, OrderClosed = true; // annehmen, wenn dies eine Pending Order war, wurde sie nicht ausgelöst PendingOrderOpened = false; // in allen Positionen der laufenden Liste eröffneter Positionen suchen for ( now_CurOrder = 0; now_CurOrder < now_OrdersTotal; now_CurOrder ++ ) { // wenn es eine Position mit dem Ticket in der Liste gibt, if ( ticket == now_OrdersArray[now_CurOrder][0] ) { // bedeutet das, dass die Position nicht geschlossen wurde (die Order wurde nicht gelöscht) OrderClosed = false; // wenn sich ihr Typ geändert hat, if ( type != now_OrdersArray[now_CurOrder][1] ) { // war das eine Pending Order, und sie wurde ausgelöst PendingOrderOpened = true; } break; } } // wenn eine Position geschlossen wurde (die Order wurde gelöscht), if ( OrderClosed ) { // diese wählen if ( !OrderSelect( ticket, SELECT_BY_TICKET ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", ticket, ", SELECT_BY_TICKET ) - Error #", _GetLastError ); continue; } // und feststellen, WIE die Position geschlossen (die Order gelöscht) wurde: if ( type < 2 ) { // Buy und Sell: 0 - manuell, 1 - SL, 2 - TP close_type = 0; if ( StringFind( OrderComment(), "[sl]" ) >= 0 ) close_type = 1; if ( StringFind( OrderComment(), "[tp]" ) >= 0 ) close_type = 2; } else { // Pending Orders: 0 - manuell, 1 - Verfall close_type = 0; if ( StringFind( OrderComment(), "expiration" ) >= 0 ) close_type = 1; } // ins Array geschlossener Orders schreiben, die Order vom Typ type // wurde unter close_type Bedingungen geschlossen now_ClosedOrdersArray[type][close_type] ++; continue; } // wenn eine Pending Order ausgelöst wurde, if ( PendingOrderOpened ) { // ins Array ausgelöster Orders schreiben, die Order vom Typ type wurde ausgelöst now_OpenedPendingOrders[type-2] ++; continue; } } //+------------------------------------------------------------------ //| Alle notwendigen Informationen wurden gesammelt - Information anzeigen //+------------------------------------------------------------------ // wenn dies nicht der erste Start des Experten ist, if ( !first ) { // in allen Elementen des Arrays ausgelöster Pending Orders suchen for ( type = 2; type < 6; type ++ ) { //wenn das Element nicht leer ist (eine Order von diesem Typ wurde ausgelöst), Information anzeigen if ( now_OpenedPendingOrders[type-2] > 0 ) Alert( Symbol(), ": ausgelöst wurde die ", _OrderType_str( type ), "-Order!" ); } // in allen Elementen des Arrays geschlossener Positionen suchen for ( type = 0; type < 6; type ++ ) { for ( close_type = 0; close_type < 3; close_type ++ ) { // wenn das Element nicht leer ist (die Position wurde geschlossen, Information anzeigen if ( now_ClosedOrdersArray[type][close_type] > 0 ) CloseAlert( type, close_type ); } } } else { first = false; } //---- das Array laufender Positionen in das Array vorheriger Positionen speichern ArrayResize( pre_OrdersArray, now_OrdersTotal ); for ( now_CurOrder = 0; now_CurOrder < now_OrdersTotal; now_CurOrder ++ ) { pre_OrdersArray[now_CurOrder][0] = now_OrdersArray[now_CurOrder][0]; pre_OrdersArray[now_CurOrder][1] = now_OrdersArray[now_CurOrder][1]; } pre_OrdersTotal = now_OrdersTotal; Sleep(100); } return(0); } void CloseAlert( int alert_type, int alert_close_type ) { string action = ""; if ( alert_type < 2 ) { switch ( alert_close_type ) { case 1: action = " beim Stop-Loss!"; break; case 2: action = " beim Take Profit!"; break; default: action = " manuell!"; break; } Alert( Symbol(), ": ", _OrderType_str( alert_type ), "-Position geschlossen ", action ); } else { switch ( alert_close_type ) { case 1: action = " durch den Verfall!"; break; default: action = " manuell!"; break; } Alert( Symbol(), ": ", _OrderType_str( alert_type ), "-Order gelöscht", action ); } } // liefert OrderType als Text string _OrderType_str( int _OrderType ) { switch ( _OrderType ) { case OP_BUY: return("Buy"); case OP_SELL: return("Sell"); case OP_BUYLIMIT: return("BuyLimit"); case OP_BUYSTOP: return("BuyStop"); case OP_SELLLIMIT: return("SellLimit"); case OP_SELLSTOP: return("SellStop"); default: return("UnknownOrderType"); } }
5. Eingliederung in die Experte und Anwendung
Damit man diese "Ereignisfalle" von jedem Experten aus bequem nutzen könnte, muss der Code in der Datei Events.mq4 platziert werden, damit man ihn später mit der Direktive #include in jeden beliebigen Experten eingliedern könnte. Hierfür:
- den Code als eine Funktion gestalten, die aus Experten aufgerufen wird;
- die externe Variable MagicNumber entfernen und die Parameter der Funktion magic hinzufügen (sie spielen die gleiche Rolle; dies wird getan, um die Liste der externen Variablen nicht zu "verstopfen");
- jedem Ereignis eine globale Variable hinzufügen; dies erleichtert die Anwendung (nicht vergessen, diese Variablen am Anfang der Funktion auf Null zu setzten);
- die Endlosschleife entfernen – jetzt wird zwischen Funktionsaufrufen "gemessen"(d.h. wenn man eine Funktion aufruft, bekommt man eine Liste mit den Veränderungen im Vergleich zum vorherigen Funktionsaufruf);
- Alerts entfernen; wenn nötig, kann man sie dem Experten hinzufügen;
- den Code unter Berücksichtigung des Obengenannten verbessern.
Man bekommt folgendes Ergebnis:
// das Array eröffneter Positionen beim vorherigen Tick int pre_OrdersArray[][2]; // [Anzahl der Positionen][№ des Tickets, Positionstyp] // Ereignisvariable int eventBuyClosed_SL = 0, eventBuyClosed_TP = 0; int eventSellClosed_SL = 0, eventSellClosed_TP = 0; int eventBuyLimitDeleted_Exp = 0, eventBuyStopDeleted_Exp = 0; int eventSellLimitDeleted_Exp = 0, eventSellStopDeleted_Exp = 0; int eventBuyLimitOpened = 0, eventBuyStopOpened = 0; int eventSellLimitOpened = 0, eventSellStopOpened = 0; void CheckEvents( int magic = 0 ) { // die Flagge des ersten Starts static bool first = true; // der Code des letzten Fehlers int _GetLastError = 0; // die Gesamtzahl der Positionen int _OrdersTotal = OrdersTotal(); // die Anzahl der Positionen, die den Kriterien entsprechen (das laufende Symbol und die angegebene MagicNumber), // beim laufenden Tick int now_OrdersTotal = 0; // Anzahl der Positionen, die den Kriterien entsprechen, beim vorherigen Tick static int pre_OrdersTotal = 0; // das Array eröffneter Positionen beim vorherigen Tick int now_OrdersArray[][2]; // [№ in der Liste][Ticketnummer, Positionstyp] // die laufende Nummer der Position im Array now_OrdersArray (für Suche) int now_CurOrder = 0; // die laufende Nummer der Position im Array pre_OrdersArray (für Suche) int pre_CurOrder = 0; // das Array, wo geschlossene Positionen jeden Typs gespeichert werden int now_ClosedOrdersArray[6][3]; // [Ordertyp][Schliesungstyp] // Array, wo ausgelöste Pending Orders gespeichert werden int now_OpenedPendingOrders[4]; // [Ordertyp] // vorläufige Flaggen bool OrderClosed = true, PendingOrderOpened = false; // temporäre Variablen int ticket = 0, type = -1, close_type = -1; //Ereignisvariablen auf Null setzen eventBuyClosed_SL = 0; eventBuyClosed_TP = 0; eventSellClosed_SL = 0; eventSellClosed_TP = 0; eventBuyLimitDeleted_Exp = 0; eventBuyStopDeleted_Exp = 0; eventSellLimitDeleted_Exp = 0; eventSellStopDeleted_Exp = 0; eventBuyLimitOpened = 0; eventBuyStopOpened = 0; eventSellLimitOpened = 0; eventSellStopOpened = 0; // die Größe des Arrays eröffneter Positionen entsprechend der aktuellen Anzahl ändern ArrayResize( now_OrdersArray, MathMax( _OrdersTotal, 1 ) ); // das Array auf Null setzen ArrayInitialize( now_OrdersArray, 0.0 ); // die Arrays geschlossener Positionen und Pending Orders auf Null setzen ArrayInitialize( now_ClosedOrdersArray, 0.0 ); ArrayInitialize( now_OpenedPendingOrders, 0.0 ); //+------------------------------------------------------------------ //| In allen Positionen suchen und nur diejenigen ins Array schreiben, die //| den Kriterien entsprechen //+------------------------------------------------------------------ for ( int z = _OrdersTotal - 1; z >= 0; z -- ) { if ( !OrderSelect( z, SELECT_BY_POS ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError ); continue; } // die Zahl der Orders auf dem laufenden Symbol und der Orders mit der angegebenen MagicNumber zählen if ( OrderMagicNumber() == magic && OrderSymbol() == Symbol() ) { now_OrdersArray[now_OrdersTotal][0] = OrderTicket(); now_OrdersArray[now_OrdersTotal][1] = OrderType(); now_OrdersTotal ++; } } // die Größe des Arrays eröffneter Positionen hinsichtlich der Zahl der Positionen, die den Kriterien entsprechen. ArrayResize( now_OrdersArray, MathMax( now_OrdersTotal, 1 ) ); //+------------------------------------------------------------------ //| in der der Liste der Positionen beim vorherigen Tick suchen und geschlossene Positionen sowie ausgelöste //| Pending Orders zählen //+------------------------------------------------------------------ for ( pre_CurOrder = 0; pre_CurOrder < pre_OrdersTotal; pre_CurOrder ++ ) { // das Ticket und den Ordertyp speichern ticket = pre_OrdersArray[pre_CurOrder][0]; type = pre_OrdersArray[pre_CurOrder][1]; // annehmen, wenn dies eine Position war, wurde sie geschlossen, OrderClosed = true; // annehmen, wenn dies eine Pending Order war, wurde sie nicht ausgelöst PendingOrderOpened = false; // in allen Positionen der laufenden Liste eröffneter Positionen suchen for ( now_CurOrder = 0; now_CurOrder < now_OrdersTotal; now_CurOrder ++ ) { // wenn es eine Position mit dem Ticket in der Liste gibt, if ( ticket == now_OrdersArray[now_CurOrder][0] ) { // bedeutet das, dass die Position nicht geschlossen wurde (die Order wurde nicht gelöscht) OrderClosed = false; // wenn sich ihr Typ geändert hat, if ( type != now_OrdersArray[now_CurOrder][1] ) { // war das eine Pending Order, und sie wurde ausgelöst PendingOrderOpened = true; } break; } } // wenn eine Position geschlossen wurde (die Order wurde gelöscht), if ( OrderClosed ) { // diese wählen if ( !OrderSelect( ticket, SELECT_BY_TICKET ) ) { _GetLastError = GetLastError(); Print( "OrderSelect( ", ticket, ", SELECT_BY_TICKET ) - Error #", _GetLastError ); continue; } // und feststellen, WIE die Position geschlossen (die Order gelöscht) wurde: if ( type < 2 ) { // Buy und Sell: 0 - manuell, 1 - SL, 2 - TP close_type = 0; if ( StringFind( OrderComment(), "[sl]" ) >= 0 ) close_type = 1; if ( StringFind( OrderComment(), "[tp]" ) >= 0 ) close_type = 2; } else { // Pending Orders: 0 - manuell, 1 - Verfall close_type = 0; if ( StringFind( OrderComment(), "expiration" ) >= 0 ) close_type = 1; } // ins Array geschlossener Orders schreiben, die Order vom Typ type // wurde unter close_type Bedingungen geschlossen now_ClosedOrdersArray[type][close_type] ++; continue; } // wenn eine Pending Order ausgelöst wurde, if ( PendingOrderOpened ) { // ins Array ausgelöster Orders schreiben, die Order vom Typ type wurde ausgelöst now_OpenedPendingOrders[type-2] ++; continue; } } //+------------------------------------------------------------------ //| Alle notwendigen Informationen wurden gesammelt - den Variablen der Ereignisse entsprechende Werte zuordnen //+------------------------------------------------------------------ // wenn dies nicht der erste Start des Experten ist, if ( !first ) { // in allen Elementen des Arrays ausgelöster Pending Orders suchen for ( type = 2; type < 6; type ++ ) { // wenn dieses Element nicht leer ist (eine Order diesen Typs wurde ausgelöst), den Variablenwert ändern if ( now_OpenedPendingOrders[type-2] > 0 ) SetOpenEvent( type ); } // in allen Elementen des Arrays geschlossener Positionen suchen for ( type = 0; type < 6; type ++ ) { for ( close_type = 0; close_type < 3; close_type ++ ) { // wenn das Element nicht leer ist (die Position wurde geschlossen), den Variablenwert ändern if ( now_ClosedOrdersArray[type][close_type] > 0 ) SetCloseEvent( type, close_type ); } } } else { first = false; } //---- das Array laufender Positionen in das Array vorheriger Positionen speichern ArrayResize( pre_OrdersArray, MathMax( now_OrdersTotal, 1 ) ); for ( now_CurOrder = 0; now_CurOrder < now_OrdersTotal; now_CurOrder ++ ) { pre_OrdersArray[now_CurOrder][0] = now_OrdersArray[now_CurOrder][0]; pre_OrdersArray[now_CurOrder][1] = now_OrdersArray[now_CurOrder][1]; } pre_OrdersTotal = now_OrdersTotal; } void SetOpenEvent( int SetOpenEvent_type ) { switch ( SetOpenEvent_type ) { case OP_BUYLIMIT: eventBuyLimitOpened ++; return(0); case OP_BUYSTOP: eventBuyStopOpened ++; return(0); case OP_SELLLIMIT: eventSellLimitOpened ++; return(0); case OP_SELLSTOP: eventSellStopOpened ++; return(0); } } void SetCloseEvent( int SetCloseEvent_type, int SetCloseEvent_close_type ) { switch ( SetCloseEvent_type ) { case OP_BUY: { if ( SetCloseEvent_close_type == 1 ) eventBuyClosed_SL ++; if ( SetCloseEvent_close_type == 2 ) eventBuyClosed_TP ++; return(0); } case OP_SELL: { if ( SetCloseEvent_close_type == 1 ) eventSellClosed_SL ++; if ( SetCloseEvent_close_type == 2 ) eventSellClosed_TP ++; return(0); } case OP_BUYLIMIT: { if ( SetCloseEvent_close_type == 1 ) eventBuyLimitDeleted_Exp ++; return(0); } case OP_BUYSTOP: { if ( SetCloseEvent_close_type == 1 ) eventBuyStopDeleted_Exp ++; return(0); } case OP_SELLLIMIT: { if ( SetCloseEvent_close_type == 1 ) eventSellLimitDeleted_Exp ++; return(0); } case OP_SELLSTOP: { if ( SetCloseEvent_close_type == 1 ) eventSellStopDeleted_Exp ++; return(0); } } }
Jetzt kann man Ereignisse von jedem Experten aus verfolgen, indem man
die Bibliothek einschaltet. Unten ist solcher Experte (EventsExpert.mq4)
als Beispiel angeführt:
extern int MagicNumber = 0; #include <Events.mq4> int start() { CheckEvents( MagicNumber ); if ( eventBuyClosed_SL > 0 ) Alert( Symbol(), ": Buy-Position wurde beim Stop-Loss geschlossen!" ); if ( eventBuyClosed_TP > 0 ) Alert( Symbol(), ": Buy-Position wurde beim Take Profit geschlossen!" ); if ( eventBuyLimitOpened > 0 || eventBuyStopOpened > 0 || eventSellLimitOpened > 0 || eventSellStopOpened > 0 ) Alert( Symbol(), ": Pending Order ausgelöst!" ); return(0); }
6. Fazit
Der vorliegende Artikel handelte sich darum, wie man Ereignisse im МetaТrader 4 mit der Sprache MQL4 verfolgen kann. Die Typen der Verfolgung wurden in Gruppen aufgeteilt und nach angegebenen Kriterien ausgefiltert. Darüber hinaus wurde eine Bibliothek erstellt, in der man einige Ereignisse aus jedem Experten verfolgen kann.
Die Funktion CheckEvents() kann ergänzt werden (oder als Vorlage dienen), um andere Ereignisse zu verfolgen, die in diesem Artikel nicht betrachtet wurden.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/1399




- 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.