Descargar MetaTrader 5

MagicNumber, el identificador "mágico" de la orden

19 enero 2016, 12:33
Andrey Khatimlianskii
0
778


1. Preámbulo

En МТ3, el manejo de las posiciones abiertas requería bastante tiempo. Los traders tenían a su disposición una herramienta bastante limitada para trabajar con la lista de posiciones abiertas y cerradas. Hacía falta aplicar soluciones bastante complicadas para poder distinguir qué posiciones pertenecían a un experto y cuáles eran de otro. En MT4 esta situación ha cambiado completamente. Ahora los traders pueden utilizar una completa variedad de funciones para gestionar las posiciones abiertas, colocar órdenes y obtener acceso a la información de las posiciones cerradas.

Se ha añadido un parámetro especial llamado MagicNumber para identificar las órdenes. Este artículo explica cómo funciona este parámetro.

2. ¿Qué es el MagicNumber?

Referencia MQL4:

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 - Número mágico de la orden. Se puede utilizar como identificador definido por el usuario.

Es decir, uno puede asignar un número único a una orden en el momento de la apertura. En consecuencia, este número sirve para distinguir una orden determinada del resto de órdenes. No tiene sentido aplicar esta característica cuando se hace trading manual, pero es muy importante para los asesores expertos cuando se hace trading automático.


Ejemplo 1: Un trader humano y un experto están operando en el terminal cliente al mismo tiempo.
Tarea: El experto tiene que operar de acuerdo a su algoritmo, y no hace nada con posiciones abiertas manualmente.
Solución: El experto asigna un MagicNumber único, diferente de cero, a la posición que se va a abrir. En el futuro solamente gestionará las posiciones cuyo MagicNumber sea igual al predeterminado.

Ejemplo 2: Dos expertos que implementan diferentes algoritmos se encuentran operando en el terminal cliente al mismo tiempo.
Tarea: Cada uno de los expertos tiene que gestionar sus propias órdenes.
Solución: Cada experto utiliza su propio MagicNumber (diferente de cero) en el momento de abrir posiciones. En el futuro solamente gestionarán las posiciones cuyo MagicNumber sea igual al predeterminado.

Ejemplo 3: Varios asesores expertos, un trader humano y un experto ayudante implementan un Trailing Stop no estándar; todos operan en el terminal cliente de forma simultánea.
Tarea: Los expertos trabajan de acuerdo a sus algoritmos y no hacen nada con posiciones abiertas manualmente. El experto ayudante que implementa el Trailing Stop puede modificar solo las posiciones abiertas manualmente, pero no las colocadas por otros expertos.
Solución: Los asesores expertos utilizan sus números mágicos únicos y solo gestionan sus posiciones. El experto ayudante solo debe modificar las posiciones con un MagicNumber igual a 0.

Los tres ejemplos son bastante realistas, seguro que muchos lectores se habrán tenido que enfrentar a los problemas planteados. En todos los casos el número mágico soluciona el problema. No es la solución única, pero sí la más sencilla.


3. Implementación

Ahora vamos a resolver esta tarea específica: crear un experto que trabaje solamente con sus propias posiciones sin prestar atención a las posiciones abiertas manualmente, ni a las posiciones de otros asesores expertos.

Primero escribiremos un experto sencillo cuya señal de apertura de posiciones es cuando el indicador MACD cruza la línea cero. Este es el código del experto:

int start()
{
    //---- Recordamos los valores del indicador para futuros análisis
    //---- Nótese que utilizamos la primera y la segunda barra. Esto permite un retraso de una hora. 
    //---- es decir, la señal aparecerá más tarde, pero protege contra la repetición de apertura/cierre
    //---- de posiciones en una barra
    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();
    //---- búsqueda en todas las posiciones abiertas
    for ( int z = _OrdersTotal - 1; z >= 0; z -- )
    {
        //---- si ocurre un error en el momento de seleccionar una posición, vamos a la siguiente
        if ( !OrderSelect( z, SELECT_BY_POS ) )
        {
            _GetLastError = GetLastError();
            Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError );
            continue;
        }
 
        //---- si la posición se abrió para un símbolo diferente del actual, saltamos
        if ( OrderSymbol() != Symbol() ) continue;
 
        //---- si se ha abierto la posición BUY,
        if ( OrderType() == OP_BUY )
        {
            //---- si MACD cruza la línea cero de arriba hacia abajo,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
            {
                //---- cerrar la posición
                if ( !OrderClose( OrderTicket(), OrderLots(), Bid, 5, Green ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Error OrderClose # ", _GetLastError );
                    return(-1);
                }
            }
            //---- si la alerta no ha cambiado, salimos; es demasiado pronto para abrir una posición nueva.
            else return(0);
        }
        //---- si se ha abierto la posición SELL,
        if ( OrderType() == OP_SELL )
        {
            //---- si MACD cruza la línea cero de abajo hacia arriba,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
            {
                //---- cerrar la posición
                if ( !OrderClose( OrderTicket(), OrderLots(), Ask, 5, Red ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Error OrderClose # ", _GetLastError );
                    return(-1);
                }
            }
            //---- si la alerta no ha cambiado, salimos; es demasiado pronto para abrir una posición nueva.
            else return(0);
        }
    }
 
//+------------------------------------------------------------------+
//| si la ejecución alcanza este punto no hay posición abierta       |
//| comprobamos si todavía es posible abrir una posición             |
//+------------------------------------------------------------------+
 
    //---- si MACD cruza la línea cero de abajo hacia arriba,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
    {
        //---- abrimos una posición BUY
        if ( OrderSend( Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, "MACD_test", 0, 0, Green ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Error OrderSend # ", _GetLastError );
            return(-1);
        }
        return(0);
    }
    //---- si MACD cruza la línea cero de arriba hacia abajo,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
    {
        //---- abrimos una posición SELL
        if ( OrderSend( Symbol(), OP_SELL, 0.1, Bid, 5, 0.0, 0.0, "MACD_test", 0, 0, Red ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Error OrderSend # ", _GetLastError );
            return(-1);
        }
        return(0);
    }

    return(0);
}

Ahora vamos a adjuntar el experto al gráfico. Veamos cómo funciona:


Todo está bien pero hay un problema. Si abrimos una posición durante la operación del experto, este entenderá que tal posición es suya y actuará en consecuencia. Pero esto no es lo que queremos.

Modificaremos nuestro experto de tal forma que gestione solo sus propias posiciones:

  • Para ello añadimos la variable externa Expert_ID, para cambiar los valores del MagicNumber de las posiciones abiertas por el experto
  • Tras seleccionar la operación con la función OrderSelect() comprobamos si el número mágico de la orden seleccionada coincide con la variable Expert_ID
  • En el momento de abrir la posición escribiremos el valor Expert_ID en el campo MagicNumber, en lugar de 0

Teniendo en cuenta pues los cambios mencionados, el código es el siguiente:

extern int Expert_ID = 1234;
 
int start()
{
    //---- Recordamos los valores del indicador para futuros análisis
    //---- Nótese que utilizamos la primera y la segunda barra. Esto permite un retraso de una hora. 
    //---- es decir, la señal aparecerá más tarde, pero protege contra la repetición de apertura/cierre
    //---- posiciones dentro de una barra
    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();
    //---- búsqueda en todas las posiciones abiertas
    for ( int z = _OrdersTotal - 1; z >= 0; z -- )
    {
        //---- si ocurre un error al buscar una posición, saltamos a la siguiente
        if ( !OrderSelect( z, SELECT_BY_POS ) )
        {
            _GetLastError = GetLastError();
            Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError );
            continue;
        }
 
        //---- si una posición está cerrada para un símbolo diferente al actual, saltamos
        if ( OrderSymbol() != Symbol() ) continue;
 
        //---- si MagicNumber no es igual a Expert_ID, saltamos esta posición
        if ( OrderMagicNumber() != Expert_ID ) continue;
 
        //---- si una posición BUY está abierta,
        if ( OrderType() == OP_BUY )
        {
            //---- si MACD cruza la línea cero de arriba hacia abajo,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
            {
                //---- cerrar la posición
                if ( !OrderClose( OrderTicket(), OrderLots(), Bid, 5, Green ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Error OrderClose # ", _GetLastError );
                    return(-1);
                }
            }
            //---- si la alerta no ha cambiado, salimos; es demasiado pronto para abrir una nueva posición
            else
            { return(0); }
        }
        //---- si se ha abierto una posición SELL,
        if ( OrderType() == OP_SELL )
        {
            //---- si MACD cruza la línea cero de abajo hacia arriba,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
            {
                //---- cerrar la posición
                if ( !OrderClose( OrderTicket(), OrderLots(), Ask, 5, Red ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Error OrderClose № ", _GetLastError );
                    return(-1);
                }
            }
            //---- si la alerta no ha cambiado, salimos; es demasiado pronto para abrir una nueva posición
            else return(0);
        }
    }
 
//+------------------------------------------------------------------+
//| si la ejecución alcanza este punto no hay posición abierta       |
//| comprobamos si todavía es posible abrir una posición             |
//+------------------------------------------------------------------+
 
    //---- si MACD cruza la línea cero de abajo hacia arriba,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
    {
        //---- abrimos una posición BUY
        if ( OrderSend( Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, "MACD_test", 
              Expert_ID, 0, Green ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Error OrderSend # ", _GetLastError );
            return(-1);
        }
        return(0);
    }
    //---- si MACD cruza la línea cero de arriba hacia abajo,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
    {
        //---- abrimos una posición SELL
        if ( OrderSend( Symbol(), OP_SELL, 0.1, Bid, 5, 0.0, 0.0, "MACD_test", 
              Expert_ID, 0, Red ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Error OrderSend # ", _GetLastError );
            return(-1);
        }
        return(0);
    }

    return(0);
}

Ahora, el usuario puede abrir posiciones manualmente mientras el asesor experto trabaja. El experto no las tocará.


4. Varios expertos idénticos en gráficos diferentes de un símbolo

Hay casos donde el mismo EA debe hacer trading en gráficos del mismo símbolo, pero en diferentes marcos temporales, por ejemplo. Si intentamos adjuntar nuestro experto al gráfico EURUSD H1 y a EURUSD M30 simultáneamente, interferirán el uno con el otro. Los asesores entenderán que la posición abierta es la suya y la modificarán a su juicio.

Este problema se puede solucionar asignando otro Expert_ID al otro experto. Aunque esto no es lo más conveniente. Si se utilizan muchos expertos a la vez uno de ellos se puede hacer un lío con los IDs.

Podemos encontrarnos con este problema al utilizar el periodo del gráfico con MagicNumber. ¿Cómo vamos a hacerlo? Si añadimos el periodo del gráfico al Expert_ID es posible que dos expertos distintos, trabajando en dos gráficos diferentes, generen el mismo MagicNumber.

Así que es mejor que multipliquemos Expert_ID por 10 (para ser precisos, su código de 1 a 9) y pongamos el periodo del gráfico al final.

Tendrá el siguiente aspecto:

    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;

Hay que añadir este código a la función init() del experto y sustituir en todas partes Expert_ID por _MagicNumber.

Esta es la versión final del EA:

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()
{
    //---- Recordamos los valores del indicador para futuros análisis
    //---- Nótese que utilizamos la primera y la segunda barra. Esto permite un retraso de una hora. 
    //---- es decir, la señal aparecerá más tarde, pero protege contra la repetición de apertura/cierre
    //---- posiciones dentro de una barra
    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();
    //---- búsqueda en todas las posiciones abiertas
    for ( int z = _OrdersTotal - 1; z >= 0; z -- )
    {
        //---- si ocurre un error al buscar una posición, saltamos a la siguiente
        if ( !OrderSelect( z, SELECT_BY_POS ) )
        {
            _GetLastError = GetLastError();
            Print( "OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", _GetLastError );
            continue;
        }
 
        //---- si una posición está abierta para un símbolo diferente al actual, saltamos
        if ( OrderSymbol() != Symbol() ) continue;
 
        //---- si MagicNumber no es igual a _MagicNumber, saltamos esta posición
        if ( OrderMagicNumber() != _MagicNumber ) continue;
 
        //---- si una posición BUY está abierta,
        if ( OrderType() == OP_BUY )
        {
            //---- si MACD cruza la línea cero de arriba hacia abajo,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
            {
                //---- cerrar la posición
                if ( !OrderClose( OrderTicket(), OrderLots(), Bid, 5, Green ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Error OrderClose # ", _GetLastError );
                    return(-1);
                }
            }
            //---- si la alerta no ha cambiado, salimos; es demasiado pronto para abrir una posición nueva
            else return(0);
        }
        //---- si se ha abierto una posición SELL,
        if ( OrderType() == OP_SELL )
        {
            //---- si MACD cruza la línea cero de abajo hacia arriba,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
            {
                //---- cerrar la posición
                if ( !OrderClose( OrderTicket(), OrderLots(), Ask, 5, Red ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Error OrderClose № ", _GetLastError );
                    return(-1);
                }
            }
            //---- si la alerta no ha cambiado, salimos; es demasiado pronto para abrir una posición nueva
            else return(0);
        }
    }
 
//+------------------------------------------------------------------+
//| si la ejecución alcanza este punto no hay posición abierta       |
//| comprobamos si todavía es posible abrir una posición             |
//+------------------------------------------------------------------+
 
    //---- si MACD cruza la línea cero de abajo hacia arriba,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
    {
        //---- abrimos una posición BUY
        if ( OrderSend( Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, "MACD_test", 
              _MagicNumber, 0, Green ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Error OrderSend # ", _GetLastError );
            return(-1);
        }
        return(0);
    }
    //---- si MACD cruza la línea cero de arriba hacia abajo,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
    {
        //---- abrimos una posición SELL
        if ( OrderSend( Symbol(), OP_SELL, 0.1, Bid, 5, 0.0, 0.0, "MACD_test", 
              _MagicNumber, 0, Red ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Error OrderSend # ", _GetLastError );
            return(-1);
        }
        return(0);
    }

    return(0);
}

El experto se puede utilizar en varios gráficos con diferentes periodos.

El valor de la variable Expert_ID se cambiará solo si existe la necesidad de lanzar dos expertos en gráficos del mismo símbolo y periodo (por ejemplo, EURUSD H1 y EURUSD H4), aunque esto sucede en raras ocasiones.

Del mismo modo, el usuario puede mejorar sus EAs con el código anterior, y enseñarles a distinguir sus posiciones de aquellas que no les pertenecen.


Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/1359

Una pausa entre operaciones Una pausa entre operaciones

El presente artículo aborda el problema de la gestión de las pausas entre las operaciones de trading cuando hay varios expertos trabajando en el terminal cliente MT 4. Está pensado para los usuarios que ya cuentan con unas habilidades básicas, tanto en el manejo del terminal como en la programación MQL4.

Probando las características y los límites de MetaTrader 4 Probando las características y los límites de MetaTrader 4

Este artículo expone algunos detalles sobre las características y los límites del Probador de Estrategias de MetaTrader 4.

Cómo manejar el Error 146, "Trade context busy" Cómo manejar el Error 146, "Trade context busy"

Este artículo expone el problema que plantea el uso de varios asesores expertos que trabajan de forma simultánea en un mismo Terminal Cliente MT 4. Se dirige a todos aquellos que cuentan con experiencia básica en el manejo del terminal y tienen conocimientos básicos de programación en MQL4.

Ejemplo de Asesor Experto Ejemplo de Asesor Experto

Este artículo expone los principios de desarrollo de programas MQL4 mediante la creación de un Asesor Experto que implementa un sistema basado en el indicador estándar MACD.