MagicNumber – "magischer" Identifikator der Order

Andrey Khatimlianskii | 5 November, 2015


1. Geschichte

    Es war relativ aufwendig, eröffnete Positionen im МТ3 zu kontrollieren. Traders hatten beschränkte Tools, um mit der Liste geöffneter und geschlossener Positionen zu arbeiten. Zwischen den "eigenen" und den "fremden" Positionen wurde auf eine komplizierte Weise unterschieden. Im МТ4 hat sich die Situation grundlegend verändert. Jetzt stehen dem Nutzer viele Funktionen zur Verfügung, mit denen er jegliche eröffnete Positionen und aufgegebene Orders steuern sowie Informationen über jegliche geschlossene Position erhalten kann.

    Für die Identifizierung der Orders ist ein besonderer Parameter hinzugefügt worden – die MagicNumber. Gerade darum geht es im vorliegenden Artikel.

2. Was ist die MagicNumber?

    aus dem MetaEditor:

int OrderSend( string symbol, int cmd, double volume, double price, int slippage, double stoploss, double takeprofit, string comment=NULL, int magic=0, datetime expiration=0, color arrow_color=CLR_NONE)

magic   -   magische Ordernummer. Sie kann als vom Nutzer definierter Identifikator verwendet werden.


    D.h. wenn eine Order aufgegeben wird (Positioneröffnung), kann ihr eine Nummer zugeordnet werden, nach der man sie von allen anderen Orders unterscheiden kann. Diese Möglichkeit kann man nicht im manuellen Handel verwenden, für den automatischen Handel mit einem Experten ist sie unerlässlich.


    Beispiel 1: ein Trader und ein Experte handeln gleichzeitig im Terminal.
Aufgabe: der Experte muss nach seinem Algorithmus handeln und nichts mit den von Hand eröffneten Positionen tun.
Lösung: bei der Eröffnung einer Position ordnet der Experte ihr eine einmalige MagicNumber (nicht Null) zu. Anschließend steuert er nur die Positionen, deren MagicNumber der zugeordneter Nummer gleich ist.

    Beispiel 2: zwei Experten mit unterschiedlichen Algorithmen handeln gleichzeitig im Terminal.
Aufgabe: die Experten müssen nur ihre "eigene" Orders steuern.
Lösung: jeder Experte muss bei der Positioneröffnung eine einmalige (nicht Null) MagicNumber verwenden. Danach steuern sie nur die Positionen, deren MagicNumber der bei der Einstellung angegebener Nummer gleich ist.

    Beispiel 3: mehrere Experten, ein Trader und ein helfender Experte, der einen spezifischen Trailing Stop umsetzt, handeln gleichzeitig im Terminal
Aufgabe: handelnde Experten müssen nach ihren Algorithmen arbeiten und nichts mit den von Hand eröffneten Positionen tun. Der helfende Experte, der den Trailing Stop umsetzt, muss nur die von Hand eröffneten Positionen ändern und mit den Positionen anderer Experten nichts tun.
Lösung: handelnde Experten müssen nur die einmaligen MagicNumber verwenden und nur die "eigenen" Positionen steuern. Der helfende Experte muss nur die Positionen mit der MagicNumber=0 modifizieren.

    Alle drei Beispiele sind realistisch und es kann sein, dass sich Nutzer bereits der Aufgaben gestellt haben. In allen drei Fällen wird die MagicNumber für die Lösung verwendet. Das ist nicht die einzig mögliche, aber die einfachste Lösung.
 

3. Umsetzung

    Jetzt lösen wir eine konkrete Aufgabe: erstellen wir einen Experten, der nur mit seinen "eigenen" Positionen arbeitet und die von Hand eröffneten Positionen nicht berücksichtigt.

    Zuerst schreiben wir einen einfachen Experten; als Signal zur Eröffnung einer Position wird für ihn der Moment dienen, wenn der MACD-Indikator die 0-Linie kreuzt. Er wird folgendermaßen aussehen:

int start()
{
    //---- den Indikatorwert für die weitere Analyse speichern
    //---- bitte beachten -  den ersten und den zweiten Balken verwenden. Dies ermöglicht die Verzögerung um einen Balken 
    //---- (d.h. das Signal erscheint später), schützt aber vor vielzähligen Eröffnungen und Schließungen
    //---- der Positionen innerhalb eines Balkens
    double MACD_1 = iMACD( Symbol(), 0, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 1 );
    double MACD_2 = iMACD( Symbol(), 0, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 2 );
 
    int _GetLastError = 0, _OrdersTotal = OrdersTotal();
    //---- in allen offenen Positionen suchen 
    for ( int z = _OrdersTotal - 1; z >= 0; z -- )
    {
        //---- wenn bei der Wahl der Position ein Fehler aufgetreten ist, zur nächsten übergehen
        if ( !OrderSelect( z, SELECT_BY_POS ) )
        {
            _GetLastError = GetLastError();
            Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError );
            continue;
        }
 
        //---- wenn die Position nicht für dieses Symbol eröffnet wurde, diese überspringen
        if ( OrderSymbol() != Symbol() ) continue;
 
        //---- wenn eine BUY-Position geöffnet ist,
        if ( OrderType() == OP_BUY )
        {
            //---- wenn der MACD die 0-Linie von oben nach unten kreuzt,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
            {
                //---- Position schließen
                if ( !OrderClose( OrderTicket(), OrderLots(), Bid, 5, Green ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Fehler OrderClose № ", _GetLastError );
                    return(-1);
                }
            }
            //---- wenn sich das Signal nicht geändert hat, beenden - es ist noch zu früh, eine neue Position zu eröffnen
            else return(0);
        }
        //---- wenn eine SELL-Position geöffnet ist,
        if ( OrderType() == OP_SELL )
        {
            //---- wenn der MACD die 0-Linie von unten nach oben kreuzt,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
            {
                //---- Position schließen
                if ( !OrderClose( OrderTicket(), OrderLots(), Ask, 5, Red ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Fehler OrderClose № ", _GetLastError );
                    return(-1);
                }
            }
            //---- wenn sich das Signal nicht geändert hat, beenden - es ist noch zu früh, eine neue Position zu eröffnen
            else return(0);
        }
    }
 
//+------------------------------------------------------------------
//| wenn die Ausführung diesen Punkt erreicht hat, gibt es keine offene Position
//| prüfen, ob man eine Position eröffnen kann
//+------------------------------------------------------------------
 
    //---- wenn der MACD die 0-Linie von unten nach oben kreuzt,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
    {
        //---- eine BUY-Position eröffnen
        if ( OrderSend( Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, "MACD_test", 0, 0, Green ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Fehler OrderSend № ", _GetLastError );
            return(-1);
        }
        return(0);
    }
    //---- wenn der MACD die 0-Linie von oben nach unten kreuzt,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
    {
        //---- eine SELL-Position eröffnen
        if ( OrderSend( Symbol(), OP_SELL, 0.1, Bid, 5, 0.0, 0.0, "MACD_test", 0, 0, Red ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Fehler OrderSend № ", _GetLastError );
            return(-1);
        }
        return(0);
    }

    return(0);
}

   
    Dem Chart den Experten hinzufügen und schauen, wie er funktioniert:



 

 

    Alles ist in Ordnung, jedoch gibt es ein Aber. Wenn eine Position für dasselbe Symbol während der Arbeit eines Expert Advisors eröffnet wird, betrachtet der Experte diese als seine "eigene" und steuert sie selbst. Uns passt das nicht.

    Der Experte muss so modifiziert werden, dass er nur seine "eigenen" Positionen steuert:

    Der Code wird mit Veränderungen folgendermaßen aussehen:

extern int Expert_ID = 1234;
 
int start()
{
    //---- den Indikatorwert für die weitere Analyse speichern
    //---- bitte beachten -  den ersten und den zweiten Balken verwenden. Dies ermöglicht die Verzögerung um einen Balken 
    //---- (d.h. das Signal erscheint später), schützt aber vor vielzähligen Eröffnungen und Schließungen
    //---- der Positionen innerhalb eines Balkens
    double MACD_1 = iMACD( Symbol(), 0, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 1 );
    double MACD_2 = iMACD( Symbol(), 0, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 2 );
 
    int _GetLastError = 0, _OrdersTotal = OrdersTotal();
    //---- in allen offenen Positionen suchen 
    for ( int z = _OrdersTotal - 1; z >= 0; z -- )
    {
        //---- wenn bei der Wahl der Position ein Fehler aufgetreten ist, zur nächsten übergehen
        if ( !OrderSelect( z, SELECT_BY_POS ) )
        {
            _GetLastError = GetLastError();
            Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError );
            continue;
        }
 
        //---- wenn die Position nicht für dieses Symbol eröffnet wurde, diese überspringen
        if ( OrderSymbol() != Symbol() ) continue;
 
        //---- wenn die MagicNumber der Expert_ID nicht gleich ist, die Position überspringen
        if ( OrderMagicNumber() != Expert_ID ) continue;
 
        //---- wenn eine BUY-Position geöffnet ist,
        if ( OrderType() == OP_BUY )
        {
            //---- wenn der MACD die 0-Linie von oben nach unten kreuzt,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
            {
                //---- Position schließen
                if ( !OrderClose( OrderTicket(), OrderLots(), Bid, 5, Green ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Fehler OrderClose № ", _GetLastError );
                    return(-1);
                }
            }
            //---- wenn sich das Signal nicht geändert hat, beenden - es ist noch zu früh, eine neue Position zu eröffnen
            else
            { return(0); }
        }
        //---- wenn eine SELL-Position geöffnet ist,
        if ( OrderType() == OP_SELL )
        {
            //---- wenn der MACD die 0-Linie von unten nach oben kreuzt,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
            {
                //---- Position schließen
                if ( !OrderClose( OrderTicket(), OrderLots(), Ask, 5, Red ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Fehler OrderClose № ", _GetLastError );
                    return(-1);
                }
            }
            //---- wenn sich das Signal nicht geändert hat, beenden - es ist noch zu früh, eine neue Position zu eröffnen
            else return(0);
        }
    }
 
//+------------------------------------------------------------------
//| wenn die Ausführung diesen Punkt erreicht hat, gibt es keine offene Position
//| prüfen, ob man eine Position eröffnen kann
//+------------------------------------------------------------------
 
    //---- wenn der MACD die 0-Linie von unten nach oben kreuzt,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
    {
        //---- eine BUY-Position eröffnen
        if ( OrderSend( Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, "MACD_test", 
              Expert_ID, 0, Green ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Fehler OrderSend № ", _GetLastError );
            return(-1);
        }
        return(0);
    }
    //---- wenn der MACD die 0-Linie von oben nach unten kreuzt,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
    {
        //---- eine SELL-Position eröffnen
        if ( OrderSend( Symbol(), OP_SELL, 0.1, Bid, 5, 0.0, 0.0, "MACD_test", 
              Expert_ID, 0, Red ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Fehler OrderSend № ", _GetLastError );
            return(-1);
        }
        return(0);
    }

    return(0);
}
   

    Jetzt kann man bei einem aktiven Experten Positionen von Hand eröffnen. Der Experte wird mit ihnen nichts tun .


4. Mehrere gleiche Experten auf dem Chart eines Symbols

    Es gibt Situationen, wo es nötig ist, dass einer und derselbe Experte auf dem Chart eines Symbols, z.B., in unterschiedlichen Zeitrahmen handelt. Wenn man versucht, dem Chart EURUSD, H1 unseren Experten sowie dem Chart EURUSD, M30 denselben Experten hinzuzufügen, werden sie einander stören: jeder wird eine offene Position als die "eigene" betrachten und diese nach seinem Ermessen ändern.

    Man kann dieses Problem lösen, indem man dem zweiten Experten eine andere Expert_ID zuordnet. Das ist aber nicht die praktischste Lösung. Wenn viele Experten verwendet werden, kann man sich leicht in ihren IDs verlieren.

    Für die Lösung dieses Problems muss man die Chartperiode als MagicNumber verwenden. Wie macht man das? Wenn man die Chartperiode und die Expert_ID addiert, kann passieren, dass zwei verschiedene Experten in zwei verschiedenen Charts die gleiche MagicNumber generieren.

    Aus diesem Grund wird die Expert_ID mit 10 multipliziert und die Chartperiode (genauer gesagt, ihr Code - von 1 bis 9) ans Ende gesetzt.

    Dies wird folgendermaßen aussehen:

    int Period_ID = 0;
    switch ( Period() )
    {
        case PERIOD_MN1: Period_ID = 9; break;
        case PERIOD_W1:  Period_ID = 8; break;
        case PERIOD_D1:  Period_ID = 7; break;
        case PERIOD_H4:  Period_ID = 6; break;
        case PERIOD_H1:  Period_ID = 5; break;
        case PERIOD_M30: Period_ID = 4; break;
        case PERIOD_M15: Period_ID = 3; break;
        case PERIOD_M5:  Period_ID = 2; break;
        case PERIOD_M1:  Period_ID = 1; break;
    }
    _MagicNumber = Expert_ID * 10 + Period_ID;
   
    Jetzt ist dieser Code der Expertenfunktion init() hinzuzufügen und die Expert_ID durch die _MagicNumber zu ersetzen.

    Der Experte wird schließlich folgendermaßen aussehen:

extern int Expert_ID = 1234;
int _MagicNumber = 0;
 
int init()
{
    int Period_ID = 0;
    switch ( Period() )
    {
        case PERIOD_MN1: Period_ID = 9; break;
        case PERIOD_W1:  Period_ID = 8; break;
        case PERIOD_D1:  Period_ID = 7; break;
        case PERIOD_H4:  Period_ID = 6; break;
        case PERIOD_H1:  Period_ID = 5; break;
        case PERIOD_M30: Period_ID = 4; break;
        case PERIOD_M15: Period_ID = 3; break;
        case PERIOD_M5:  Period_ID = 2; break;
        case PERIOD_M1:  Period_ID = 1; break;
    }
    _MagicNumber = Expert_ID * 10 + Period_ID;

    return(0);
}
 
int start()
{
    //---- den Indikatorwert für die weitere Analyse speichern
    //---- bitte beachten -  den ersten und den zweiten Balken verwenden. Dies ermöglicht die Verzögerung um einen Balken 
    //---- (d.h. das Signal erscheint später), schützt aber vor vielzähligen Eröffnungen und Schließungen
    //---- der Positionen innerhalb eines Balkens
    double MACD_1 = iMACD( Symbol(), 0, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 1 );
    double MACD_2 = iMACD( Symbol(), 0, 12, 26, 9, PRICE_CLOSE, MODE_MAIN, 2 );
 
    int _GetLastError = 0, _OrdersTotal = OrdersTotal();
    //---- in allen offenen Positionen suchen 
    for ( int z = _OrdersTotal - 1; z >= 0; z -- )
    {
        //---- wenn bei der Wahl der Position ein Fehler aufgetreten ist, zur nächsten übergehen
        if ( !OrderSelect( z, SELECT_BY_POS ) )
        {
            _GetLastError = GetLastError();
            Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError );
            continue;
        }
 
        //---- wenn die Position nicht für dieses Symbol eröffnet wurde, diese überspringen
        if ( OrderSymbol() != Symbol() ) continue;
 
        //---- wenn die MagicNumer und die _MagicNumber unterschiedliche Werte haben, die Position überspringen
        if ( OrderMagicNumber() != _MagicNumber ) continue;
 
        //---- wenn eine BUY-Position geöffnet ist,
        if ( OrderType() == OP_BUY )
        {
            //---- wenn der MACD die 0-Linie von oben nach unten kreuzt,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
            {
                //---- Position schließen
                if ( !OrderClose( OrderTicket(), OrderLots(), Bid, 5, Green ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Fehler OrderClose № ", _GetLastError );
                    return(-1);
                }
            }
            //---- wenn sich das Signal nicht geändert hat, beenden - es ist noch zu früh, eine neue Position zu eröffnen
            else return(0);
        }
        //---- wenn eine SELL-Position geöffnet ist,
        if ( OrderType() == OP_SELL )
        {
            //---- wenn der MACD die 0-Linie von unten nach oben kreuzt,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
            {
                //---- Position schließen
                if ( !OrderClose( OrderTicket(), OrderLots(), Ask, 5, Red ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Fehler OrderClose № ", _GetLastError );
                    return(-1);
                }
            }
            //---- wenn sich das Signal nicht geändert hat, beenden - es ist noch zu früh, eine neue Position zu eröffnen
            else return(0);
        }
    }
 
//+------------------------------------------------------------------
//| wenn die Ausführung diesen Punkt erreicht hat, gibt es keine offene Position
//| prüfen, ob man eine Position eröffnen kann
//+------------------------------------------------------------------
 
    //---- wenn der MACD die 0-Linie von unten nach oben kreuzt,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
    {
        //---- eine BUY-Position eröffnen
        if ( OrderSend( Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, "MACD_test", 
              _MagicNumber, 0, Green ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Fehler OrderSend № ", _GetLastError );
            return(-1);
        }
        return(0);
    }
    //---- wenn der MACD die 0-Linie von oben nach unten kreuzt,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
    {
        //---- eine SELL-Position eröffnen
        if ( OrderSend( Symbol(), OP_SELL, 0.1, Bid, 5, 0.0, 0.0, "MACD_test", 
              _MagicNumber, 0, Red ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Fehler OrderSend № ", _GetLastError );
            return(-1);
        }
        return(0);
    }

    return(0);
}

   
    So kann der Experte in mehreren Charts mit verschiedenen Perioden verwendet werden.

    Der Wert der Variablen Expert_ID muss nur dann geändert werden, wenn man zwei Experten in den Charts mit demselben Symbol und derselben Periode (z.B., EURUSD H1 und EURUSD H4) verwendet, das ist aber eher eine Ausnahme.


    Analog zum oben beschriebenen Code kann der Nutzer seine Experten selbst verbessern, damit sie auch zwischen den "eigenen" und den "fremden" Positionen unterscheiden könnten.