Ereignisse im МetaТrader 4

Andrey Khatimlianskii | 5 November, 2015

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:

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.

  1. Eröffnung der Position
    • "Marktposition"
      • Buy
      • Sell
    • Pending Order
      • Buy-Limit
      • Sell-Limit
      • Buy-Stop
      • Sell-Stop
  2. Auslösung der Order
    • Buy-Limit
    • Sell-Limit
    • Buy-Stop
    • Sell-Stop
  3. Schließung der Position
    • "Marktposition"
      • Buy
        • Stop-Loss
        • Take Profit
        • manuell (weder Stop-Loss noch Take Profit)
      • Sell
        • Stop-Loss
        • Take Profit
        • manuell
    • Pending Order (Löschung)
      • Buy-Limit
        • Verfall
        • manuell
      • Sell-Limit
        • Verfall
        • manuell
      • Buy-Stop
        • Verfall
        • manuell
      • Sell-Stop
        • Verfall
        • manuell
  4. Modifizierung der Position
    • "Marktposition"
      • Buy
        • Stop-Loss
        • Take Profit
      • Sell
        • Stop-Loss
        • Take Profit
    • 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

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:

  1. Auslösung der Order
    • Buy-Limit
    • Sell-Limit
    • Buy-Stop
    • Sell-Stop
  2. Schließung der Position
    • "Marktposition"
      • Buy
        • Stop-Loss
        • Take Profit
      • Sell
        • Stop-Loss
        • Take Profit
    • Pending Order (Verfall)
      • Buy-Limit
      • Sell-Limit
      • Buy-Stop
      • Sell-Stop

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:

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:

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.