English Русский 中文 Deutsch 日本語 Português 한국어 Français Italiano Türkçe
El uso de ORDER_MAGIC para el trading con distintos Expert Advisors con un solo instrumento

El uso de ORDER_MAGIC para el trading con distintos Expert Advisors con un solo instrumento

MetaTrader 5Ejemplos | 25 febrero 2014, 11:46
2 568 0
Mykola Demko
Mykola Demko


Introducción

En MQL5 tenemos la posibilidad de asignar un número mágico a cada orden pendiente, para poder utilizar esta información en la identificación de la orden. Esto abre unas posibilidades de interacción enormes entre los distintos Expert Advisors, y el desarrollo de sistemas aún más complejos. En este artículo, me gustaría presentar las posibilidades infravaloradas del Número mágico.

Pero antes de continuar con el tema de este artículo, tenemos que comprender mejor de que está compuesto el Número mágico. ¿Qué hay de mágico en un número, que define cuál es el Expert Advisor que lo establece? Los "milagros" comienzan con las oportunidades que los desarrolladores han establecido en el tipo ulong, y que está declarado mediante el Número mágico.

El tipo ulong es el más largo

Si observamos en detalle el tipo entero long, podemos observar que el valor máximo de este tipo es muy grande:

Tipo

Tamaño en bytes

Valor mínimo

Valor máximo

Análogo en el lenguaje C++

long

8

-9 223 372 036 854 775 808

9 223 372 036 854 775 807

__int64

ulong

8

0

18 446 744 073 709 551 615

unsigned __int64

Tabla 1. Propiedades de los tipos de datos long y ulong

pero el tipo ulong lo supera gracias a la combinación de la mantisa positiva y negativa.

Por lo tanto, la longitud específica es enorme, pero, ¿cómo se utilizaba antes?

En mi experiencia trabajando en mql 4, a menudo he observado un sinsentido en la codificación del número mágico por parte de muchos desarrolladores. El número mágico se estaba usando con sensatez, pero su codificación parecía bastante ridícula. Lo que se puede decir de la singularidad del Número mágico 12345, es que lo utiliza casi la mitad de la comunidad de desarrolladores. La otra mitad utiliza los Números mágicos 55555, 33333 y 77777, y esta es casi la serie entera. Quiero recordar al lector que es muy poco probable que su equipo tenga 1000 Expert Advisors, con lo cual el número 1000 será suficiente para codificar el nombre singular de todos sus Expert Advisors.

1000, corresponde a solo 3 categorías completas, ¿qué debemos hacer con las 15 categorías completas restantes, y que están presentes en el tipo ulong? La respuesta es sencilla: codificarlos.

Lo que dice Wikipedia sobre la palabra código:

El Código -regla (algoritmo), la comparación de cada mensaje individual de una combinación estrictamente concreta de símbolos (caracteres) (o señales).

Por consiguiente, vamos a establecer las reglas. Propongo especificar en el código del número mágico, no solo el ID del Expert Advisor, sino también el instrumento, en el que se está ejecutando. El hecho de que el Expert Advisor se esté ejecutando, por ejemplo, sobre EURUSD, no quiere decir que el Expert Advisor va a mostrar órdenes únicamente con este instrumento. Además, creo que sería muy útil escribir el código de interacción de los Expert Advisors, algo como "propio / ajeno", de este modo, al comprobar las posiciones, el Expert Advisor podrá entender que la orden actual está construido por un Expert Advisor amigable. Creo que esto será suficiente para crear un sistema muy complejo.

Así que, vamos a resumir lo que tenemos: Cuáles son las posibilidades que estamos sentando en el sistema:

  1. La posibilidad de que dos o más Expert Advisors trabajen con un único instrumento sin que se interfieran.
  2. La posibilidad de que dos o más Expert Advisors trabajen con distintos instrumentos y que se complementen.
  3. La capacidad de identificar la orden mediante el instrumento que trabaja con el Expert Advisor.

Y así, la tarea está definida, vamos a comenzar ahora su implementación.

Expert Advisor sencillo

Elaboramos el código de un Expert Advisor sencillo, por ejemplo, mantener la posición en la dirección del movimiento. Creo que el lector, que haya decidido analizar el Número mágico, ya ha leído el artículo Guía paso a paso para escribir un Expert Advisor en MQL5 para principiantes, y si no, es muy recomendable hacerlo, ya que no voy a entrar en los detalles de la creación del Expert Advisor. Básicamente, el Expert Advisor abrirá la posición una vez, y la corregirá todas las otras veces. Por consiguiente, vamos a necesitar una función para abrir posiciones, es decir colocar la petición de trading (orden de trading).

Creamos una clase adicional, que nos va a calcular los parámetros para rellenar los campos de la estructura de la petición de trading.

//+------------------------------------------------------------------+
//| The class provides auxiliary trading calculations                |
//+------------------------------------------------------------------+
class CProvision
  {
protected:
   MqlTradeRequest   trades;                 // pointer to the request structure of OrderSend
public:
   int               TYPE(const double &v[]);  // determines the type, in respect to the  readings of the moving
   double            pricetype(int type);     // calculates the level of the opening, in respect to the type 
   double            SLtype(int type);        // calculates the level of the stop-loss in respect to the type
   double            TPtype(int type);        // calculates the level of the take-profit, in respect to the type
   long              spread();               // returns the spread of the current instrument
   int               SendOrder(ENUM_ORDER_TYPE type,double volume);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CProvision::SendOrder(ENUM_ORDER_TYPE type,double volume)
  {
   trades.action          =TRADE_ACTION_DEAL;       // Type of the implemented actions
   trades.magic           =magic;                 // Stamp of the Expert Advisor (identifier of the magic number)
   trades.symbol          =_Symbol;                // Name of the trading instrument
   trades.volume          =volume;                // Request the volume of the trade in lots
   trades.price           =pricetype((int)type);  // Price       
   trades.sl              =SLtype((int)type);     // Level of Stop Loss order
   trades.tp              =TPtype((int)type);     // Level of Take Profit order         
   trades.deviation=(int)spread();                // Maximum acceptable deviation from the requested price
   trades.type=type;                              // Order type
   trades.type_filling=ORDER_FILLING_FOK;
   if(OrderSend(trades,res)){return(res.retcode);}
   return(-1);
  }
//+------------------------------------------------------------------+
//| Determines the type, in respect to the reading of the moving     |
//+------------------------------------------------------------------+
int CProvision::TYPE(const double &v[])
  {
   double t=v[0]-v[1];
   if(t==0.0)t=1.0;
   return((int)(0.5*t/fabs(t)+0.5));
  }
//+------------------------------------------------------------------+
//| Calculates the level of opening in respect to the type           |
//+------------------------------------------------------------------+
double CProvision::pricetype(int type)
  {
   if(SymbolInfoTick(_Symbol,tick))
     {
      if(type==0)return(tick.ask);
      if(type==1)return(tick.bid);
     }
   return(-1);
  }
//+------------------------------------------------------------------+
//| Calculates the level of stop-loss in respect to the type         |
//+------------------------------------------------------------------+
double CProvision::SLtype(int type)
  {
   if(SymbolInfoTick(_Symbol,tick))
     {
      if(type==0)return(tick.bid-SL*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
      if(type==1)return(tick.ask+SL*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
     }
   return(0);
  }
//+------------------------------------------------------------------+
//| Calculates the level of timeframe in respect to the type         |
//+------------------------------------------------------------------+
double CProvision::TPtype(int type)
  {
   if(SymbolInfoTick(_Symbol,tick))
     {
      if(type==0)return(tick.bid+TP*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
      if(type==1)return(tick.ask-TP*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
     }
   return(0);
  }
//+------------------------------------------------------------------+
//| Returns the spread                                               |
//+------------------------------------------------------------------+
long CProvision::spread() 
  {
   return(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD));
  }

Disponiendo de tal clase, podemos escribir un código para un Expert Advisor sencillo sin ningún problema: 

//+------------------------------------------------------------------+
//| Code of the Expert Advisor                                       |
//+------------------------------------------------------------------+

//--- Input parameters
input ulong              magic       =1;           // magic
input int                SL          =300;         // Stop Loss
input int                TP          =1000;        // Take Profit
input int                MA_Period   =25;         // MA period
input double             lot         =0.1;         // Volume of position
input int                MA_shift    =0;          // Shift of indicator
input ENUM_MA_METHOD     MA_smooth   =MODE_SMA;     // Smoothing type
input ENUM_APPLIED_PRICE price       =PRICE_OPEN;    // Price type 

//--- We will store the indicator's handle
int
MA_handle,     // Handle of the indicator
type_MA,       // Type that specify the direction of MA
rezult;        // The variable takes the value of the result of the OrderSend operation
double v[2];    // Buffer for receiving values of MA

MqlTradeResult   res;   // Pointer to the structure of responding by OrderSend
MqlTick         tick;  // Pointer to the structure of last market information
CProvision      prov;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Create the indicator's handle
   MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price);
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(CopyBuffer(MA_handle,0,0,2,v)<=0)
     {Print("#",magic,"Error of copying");return;}
   type_MA=prov.TYPE(v); // Determine type depending on MA indication

   if(PositionSelect(_Symbol))// If there is an open position 
     {
      if(PositionGetInteger(POSITION_TYPE)!=type_MA)// Check if its time to close
        {
         Print("#",magic,"Position by magic number has volume ",PositionGetDouble(POSITION_VOLUME),
               " reverse position of type ",PositionGetInteger(POSITION_TYPE)," by ",type_MA);
         rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,PositionGetDouble(POSITION_VOLUME)+lot);
         // reverse the position
         if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
         else{Print("#",magic,"Error",GetLastError()); return;}
        }
     }
   else // If there is no open position then open
     {
      Print("#",magic,"Position by magic number has volume ",PositionGetDouble(POSITION_VOLUME),
            " open position of type ",type_MA);
      rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);
      // open position 
      if(rezult!=-1)Print("#",magic," Code of operation result ",rezult," volume ",res.volume);
      else{Print("#",magic,"Error",GetLastError()); return;}
     }
  } 

Ejecutamos y nos aseguramos de que el Expert Advisor no presenta diferencia en términos de rentabilidad, pero que opera exactamente según el razonamiento especificado, que es precisamente lo que queremos que haga.

Figura 1. El trabajo de un Expert Advisor en un solo instrumento

Figura 1. El trabajo de un Expert Advisor en un solo instrumento

Ahora vamos a intentar ejecutar este EA, pero con períodos distintos para un instrumento (para los experimentos hemos elegido un instrumento al azar, que es EURUSD: o)

Figura 2. El conflicto de dos Expert Advisors en el mismo instrumento con distintos períodos

Figura 2. El conflicto de dos Expert Advisors en el mismo instrumento con distintos períodos

Puesto que ambos Expert Advisors se están ejecutando en un único instrumento, y el código no especifica un intercambio de posiciones, entonces ambos Expert Advisors están tratando de corregir la posición del trading, en función de las lecturas de los indicadores, y como consecuencia de esto, surge un conflicto. El Expert Advisor que se está ejecutando en M1, está tratando de corregir la posición en la celda, mientras sus competidores tratan de detenerla. Es obvio que necesitamos separar del cálculo de las posiciones, y esto es lo que haremos ahora.


¿Posición o posición virtual?

Puesto que los desarrolladores en MetaTrader 5 han pasado de las órdenes a considerar posiciones, tiene más sentido examinar con más detalle las funciones asociadas a la grabación de las posiciones.

// Returns the number of open positions.
int     PositionsTotal();

// Returns the symbol of the open position by the number in the list of positions.
string  PositionGetSymbol(int  index);

// Selects the open position for further working with it.
bool    PositionSelect(string  symbol, uint timeout=0);

// Function returns the requested property of the open position.
double  PositionGetDouble(ENUM_POSITION_PROPERTY  property_id);

// The function returns the requested property of the open position.
long    PositionGetInteger(ENUM_POSITION_PROPERTY  property_id);

// The function returns the requested property of the open position.
string  PositionGetString(ENUM_POSITION_PROPERTY  property_id);

Los identificadores de las enumeraciones para las funciones de obtención de la solicitud de las propiedades de las posiciones compatibles PositionGetDouble, PositionGetInteger, y PositionGetString, están enumerados en las tablas 2-4.

Identificador

Descripción

Tipo

POSITION_VOLUME

Volumen de la posición

double

POSITION_PRICE_OPEN

Precio de la posición

double

POSITION_SL

Nivel del Stop Loss para la posición abierta

double

POSITION_TP

Nivel del Take Profit para la posición abierta

double

POSITION_PRICE_CURRENT

Precio actual por símbolo

double

POSITION_COMMISSION

Comisión

double

POSITION_SWAP

Swap acumulado

double

POSITION_PROFIT

Beneficio actual

double

Tabla 2. Los valores de enumeración ENUM_POSITION_PROPERTY_DOUBLE

Identificador

Descripción

Tipo

POSITION_TIME

Tiempo de apertura de las posiciones

datetime

POSITION_TYPE

Tipo de posición

ENUM_POSITION_TYPE

POSITION_MAGIC

Número mágico de la posición (ver ORDER_MAGIC )

long

POSITION_IDENTIFIER

Identificación de la posición -este es un número único que se asigna a cada posición que se reabre y no cambia a lo largo de su ciclo de vida. El volumen de negocio de una posición no cambia su ID.

long

Tabla 3. Los valores de enumeración ENUM_POSITION_PROPERTY_INTEGER

Identificador

Descripción

Tipo

POSITION_SYMBOL

Símbolo para el cual se abre la posición

string

POSITION_COMMENT

Comentario de la posición

string

Tabla 4. Los valores de enumeración ENUM_POSITION_PROPERTY_STRING

A partir de las funciones podemos ver claramente que el lenguaje no contiene la división de las posiciones, basado en el principio de "quien dio la orden", pero la posibilidad de tales registros está disponible puesto que ORDER_MAGIC, POSITION_MAGIC y DEAL_MAGIC contienen exactamente el mismo número, y proceden del número mágico, indicado por el usuario. POSITION_MAGIC procede de DEAL_MAGIC, que abre la posición, y DEAL_MAGIC que a su vez, procede de ORDER_MAGIC, de la orden colocada.

Se puede identificar una orden, transacción, o posición si ningún problema, pero es imposible conducir una posición mediante un Número mágico concreto. Ahora vamos a tratar de corregir esta deficiencia. Vamos a crear funciones integradas análogas, pero identificadas por el Número mágico. Declaramos una clase para trabajar con la posición virtual del Número mágico.

Puesto que tenemos la posibilidad de trabajar con la programación orientada a objetos, vamos a declarar también nuestra propia estructura (adquiriendo más práctica al escribir de forma objetiva).

//+------------------------------------------------------------------+
//| Structure of the CPositionVirtualMagic class                     |
//+------------------------------------------------------------------+
struct SPositionVirtualMagic
  {
   double            volume; // volume of virt. position
   ENUM_POSITION_TYPE type;  // type of virt. position
  };
//+--------------------------------------------------------------------------------+
//| The class calculates the virtual position of an Expert Advisor by magic number |
//+--------------------------------------------------------------------------------+
class CPositionVirtualMagic
  {
protected:
   SPositionVirtualMagic pvm;
public:
   double               cVOLUME(){return(pvm.volume);}
   // Returns the volume of virtual position of an Expert Advisor
   ENUM_POSITION_TYPE   cTYPE(){return(pvm.type);}
   // Returns the type of virtual position of an Expert Advisor
   bool              PositionVirtualMagic(ulong Magic,
                                          string symbol,
                                          datetime CurrentTime
                                          );
   // the method of calculation virt. position returns the presence or absence of virt. position
private:
   void              prHistory_Deals(ulong &buf[],int HTD);
   // Fills the array of tickets
  };
//+-------------------------------------------------------------------------------------+
//| Method of calculation of virt. position, returns true if there is a virt. position  |
//+-------------------------------------------------------------------------------------+
bool  CPositionVirtualMagic::PositionVirtualMagic(ulong Magic,
                                                  string symbol,
                                                  datetime CurrentTime
                                                  )
  {
   int DIGITS=(int)-log10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP));
   if(DIGITS<0)DIGITS=0;
   ulong Dticket=0;
   int History_Total_Deals=-1;
   double volume=0,volume_BUY=0,volume_SELL=0;
   ulong DTicketbuf[];

   do
     {
      if(HistorySelect(0,TimeCurrent()))
        {
         History_Total_Deals=HistoryDealsTotal();
         prHistory_Deals(DTicketbuf,History_Total_Deals);
        }
      HistorySelect(0,TimeCurrent());
     }
   while(History_Total_Deals!=HistoryDealsTotal());

   for(int t=0;t<History_Total_Deals;t++)
     {
      Dticket=DTicketbuf[t];
      if(HistoryDealSelect(Dticket))
        {
         if(HistoryDealGetInteger(Dticket,DEAL_TIME)>=CurrentTime)
           {
            if(HistoryDealGetInteger(Dticket,DEAL_MAGIC)==Magic)
              {
               if(HistoryDealGetInteger(Dticket,DEAL_TYPE)==DEAL_TYPE_BUY)
                 {
                  volume_BUY+=HistoryDealGetDouble(Dticket,DEAL_VOLUME);
                 }
               else
                 {
                  if(HistoryDealGetInteger(Dticket,DEAL_TYPE)==DEAL_TYPE_SELL)
                    {
                     volume_SELL+=HistoryDealGetDouble(Dticket,DEAL_VOLUME);
                    }
                 }
              }
           }
        }
      else{HistorySelect(0,TimeCurrent());t--;}
      // if there is a fault, load history data and pass the step again
     }
   volume=NormalizeDouble(volume_BUY-volume_SELL,DIGITS);
   if(volume<0)pvm.type=POSITION_TYPE_SELL;
   else
     {
      if(volume>0)pvm.type=POSITION_TYPE_BUY;
     }
   pvm.volume=fabs(volume);
   if(pvm.volume==0)return(false);
   else return(true);
  }

En el texto anterior (referido al código de la clase CProvision), no se explicó de dónde procedía cada cosa y a dónde se enviaba, dado que el tema de este artículo no es el desarrollo de un Expert Advisor.

Pero si que vamos a ver en detalle la clase CPositionVirtualMagic.

Su estructura es la siguiente:

struct SPositionVirtualMagic

Se utiliza para aceptar los resultados de los cálculos, como una declaración global en la clase, y gracias a pvm (variable de la estructura), la estructura estará disponible en cualquier lugar y en cualquier método de la clase.

A continuación, tenemos los dos métodos de la clase:

double               cVOLUME(){return(pvm.volume);} // Returns the volume of the virtual position of the EA

ENUM_POSITION_TYPE   cTYPE()  {return(pvm.type);}   // Returns the type of the virtual position of the EA

Se declaran estos métodos como públicos, de modo que estarán disponibles mediante la llamada a las variables de la clase y en cualquier lugar del programa, son concebidas para los valores de salida de la estructura en la ubicación solicitada.

En la siguiente sección se declaran también los siguientes métodos:

bool              PositionVirtualMagic(ulong Magic,string symbol,datetime CurrentTime);

Esta es la función principal de la clase, nos centraremos más en su análisis detallado, pero voy a explicar de antemano la función en el marco del especificador de acceso privado:

void              prHistory_Deals(ulong &buf[],int HTD);

Este método genera un ticket de registro de las transacciones dentro de la matriz, que es básicamente un bucle, y se puede indicar en la función que recibe la llamada, pero quería reducir el tamaño (para incrementar la legibilidad del código) de la función PositionVirtualMagic(), así que he movido este bucle por debajo de los límites de la función, y mostrado cómo se utiliza el especificador de acceso privado.

Así que volvamos a PositionVirtualMagic(). Al comienzo, esta función dispone de una línea de cálculo de precisión, a la que tiene que redondear el valor tipo double del volumen de la posición calculada.

int DIGITS=(int)-log10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP)); if(DIGITS<0)DIGITS=0;

Esto es necesario para llevar a cabo la operación de comparación con cero, de lo contrario, algún saldo en el octavo decimal nos impedirá igualar el valor a cero y se producirá un error de ejecución.

Se redondea el volumen de la posición al incremento mínimo. Y si el incremento mínimo es superior a 1, entonces se redondea la parte entera. Después está el bucle while, pero se utiliza de un modo nuevo (distinto al de mql4), puesto que la comprobación de la veracidad de la expresión se lleva a cabo al final del bucle y no al principio.

    do
     {
      if(HistorySelect(0,TimeCurrent()))
        {
         History_Total_Deals=HistoryDealsTotal();
         prHistory_Deals(DTicketbuf,History_Total_Deals);
        }
      HistorySelect(0,TimeCurrent());  
     }
   while(History_Total_Deals!=HistoryDealsTotal());

Se utiliza este método, ya que el procesamiento de la veracidad de la expresión se lleva a cabo en el bucle, y en su versión inicial, aún no estaba preparado para llevar a cabo esta comprobación.

El bucle contiene la Carga del historial, y quiero llamar la atención del lector sobre el hecho de que es una condición necesaria para poder garantizar el funcionamiento de las funciones integrada del trabajo con el historial.

HistorySelect(0,TimeCurrent())

Creo que debería explicar mi sistema de elección de los nombres de las variables.

Un lector atento debe darse cuenta que los nombres de las clases comienzan por la letra "C", esto no es un requisito de la sintaxis y se puede asignar cualquier nombre, pero esta forma hace que la lectura sea mucho más fácil. Si la letra "C" aparece delante del nombre, sabemos que se trata del nombre de una clase, y si es la letra "S", se trata entonces de una estructura. Si las variables toman el valor de alguna función integrada, simplemente cambio los elementos del nombre de la función y obtengo el nombre de la variable, por ejemplo, así:

CurrentTime = TimeCurrent();

Sencillo y legible, y podemos ver inmediatamente las variables que contiene. Sobre todo porque MetaEditor dispone de una función que permite arrastrar un determinado trozo de código a una ubicación especifica.

Repasando el código, observamos que después de la carga del historial, viene la llamada de la función:

History_Total_Deals=HistoryDealsTotal();

con el almacenamiento del número de transacciones en la variable. Bajo las mismas condiciones, vamos a implementar la comprobación para salir del bucle. ¿Para qué necesitamos esta comprobación? ¿Y por qué no podemos simplemente cargar el historial y luego recuperar las transacciones del mismo?

El problema radica en el hecho de que durante el funcionamiento de los Expert Advisors, el historial se solicita por separado por cada EA, y por lo tanto, si los Expert Advisors se están ejecutando al mismo tiempo, la profundidad del historial será diferente. Y esto quiere decir que cuando un Expert Advisor entra en un bucle y carga el historial para sus intervalos de tiempo, antes de llegar al final del bucle, puede descubrir que este historial ya ha sido cargado mediante la petición de otro Expert Advisor, de ahí la necesidad de llevar a cabo la comprobación de autenticidad.

Por cierto, puede que no sea el mejor tipo de comprobación, pero funciona. Así que, vamos a seguir. En el bucle llamamos al método de la clase, que introduce los valores de los tickets de las transacciones en un buffer especialmente preparado. Después de llamar a la función prHistory_Deals (), se produce una vez más la carga del historial.

Es así como se lleva a cabo la comprobación durante el funcionamiento de la función prHistory_Deals (), independientemente de si hay cambios o no en el historial de las operaciones de trading. Si no hay cambios, entonces las variables de History_Total_Deals y HistoryDealsTotal () serán iguales, y se producirá una salida del bucle en una pasada. Si hay cambios, el sistema lanza un segundo bucle, y sigue repitiendo hasta que se cargue el historial de los tickets sin ningún error (no te olvides de poner ";" al final).

while(History_Total_Deals!=HistoryDealsTotal());

Más adelante en el bucle for, se llevan a cabo los cálculos de las posiciones virtuales.

Si la transacción pasa con éxito una serie de filtros (el tiempo de la transacción y el Número mágico de la transacción), entonces su volumen aumenta esta porción de la posición virtual, del tipo al cual pertenece la transacción.

Quiero señalar que guardo los cálculos de las posiciones virtuales solo desde el arranque del Expert Advisor, aunque existen otras opciones.

Aquí, hay que tener en cuenta la exactitud del cálculo de las posiciones. Mediante el libro de registros, que hemos utilizado desde tiempos inmemoriales hasta el presente, tenemos gastos y beneficios, y la diferencia entre estos dos valores representa el saldo, el mismo esquema se aplica al cálculo de una posición: si abre unos lotes, 0.2 para Sell y 0.3 para Buy, esto significa que tiene una posición de 0.1 para Buy. El tiempo de apertura y la diferencia de niveles son las categorías de beneficio, pero si la posición que tiene es de 0.1 lotes, el tipo es Comprar.

Es por ello que simplemente resumimos todas las transacciones hechas por el Expert Advisor sobre Buy y por separado, sobre Sell, y luego las comparamos y obtenemos la posición general, (de hecho, esto es lo que tiene que hacer el resto de la función).

Cálculo del volumen de las posiciones:

volume=NormalizeDouble(volume_BUY-volume_SELL,DIGITS);

Determinar el tipo de posición con la salida del valor en la estructura:

   if(volume<0)pvm.type=POSITION_TYPE_SELL;
   else
     {
      if(volume>0)pvm.type=POSITION_TYPE_BUY;
     }

Salida del volumen en la estructura:

pvm.volume=fabs(volume);

 La salida del valor de la función: si el volumen de la posición es 0 entonces es falso (false), de lo contrario, si la posición existe, entonces es verdadero (true):

   if(pvm.volume==0)return(false);
   else return(true);

Ahora, y disponiendo de la función de la posición virtual, podemos fácilmente elaborar el código del Expert Advisor, y que no tenga conflictos con sus "vecinos".

Para ahorrar espacio, voy a proporcionar algunas partes del código, que no estaban antes, en lugar de todo el código.

//+------------------------------------------------------------------+
//| Code of the Expert Advisor                                       |
//+------------------------------------------------------------------+

//--- input parameters
input ulong              magic       =1;           // magic
input int                SL          =300;         // Stop Loss
input int                TP          =1000;        // Take Profit
input int                MA_Period   =25;          // MA period
input double             lot         =0.1;         // Volume of position
input int                MA_shift    =0;          // Shift of indicator
input ENUM_MA_METHOD     MA_smooth   =MODE_SMA;     // Smoothing type
input ENUM_APPLIED_PRICE price       =PRICE_OPEN;    // Price type 
//--- we will store the indicator's handle
int MA_handle,type_MA,rezult;
double v[2];
datetime  CurrentTime;  // The variable stores the time of start of the Expert Advisor
MqlTradeResult    res;   // Pointer to the structure of responding by OrderSend
MqlTick          tick;  // Pointer to the structure of last market information
CPositionVirtualMagic cpvm;
CProvision prov;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   CurrentTime=TimeCurrent();// The variable stores the time of start of the Expert Advisor
//--- Create the indicator's handle
   MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price);
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(CopyBuffer(MA_handle,0,0,2,v)<=0)
     {Print("#",magic,"Error of copying");return;}
   type_MA=prov.TYPE(v); // Determine type depending on MA indication

   if(cpvm.PositionVirtualMagic(magic,_Symbol,CurrentTime))// If there is ab open position 
     {
      if((int)cpvm.cTYPE()!=type_MA)// Check if it is time to close
        {
         Print("#",magic,"Position by magic number has volume ",cpvm.cVOLUME(),
               " reverse position of type ",(int)cpvm.cTYPE()," by ",type_MA);
         //cpvm.cVOLUME() - volume of virtual position
         rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,cpvm.cVOLUME()+lot);// reverse the poistion
         if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
         else{Print("#",magic,"Error",GetLastError()); return;}
        }
     }
   else // If there is no open position then open
     {
      Print("#",magic,"Poistion by magic number has volume ",cpvm.cVOLUME()," open position of type ",type_MA);
      rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);// Open position 
      if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
      else{Print("#",magic,"Error",GetLastError()); return;}
     }
  }

Ejecutamos el Expert Advisor 3 veces con el mismo instrumento, pero con distintos períodos, además de asignar números mágicos diferentes cada vez:

Figura 3. Vamos a asignar distintos números mágicos a dos Expert Advisors idénticos (un instrumento, distintos períodos), ejecución del primer Expert Advisor

Figura 3. Vamos a asignar distintos números mágicos a dos Expert Advisors idénticos (un instrumento, distintos períodos), ejecución del primer Expert Advisor

Figura 4. Vamos a asignar distintos números mágicos a dos Expert Advisors idénticos (un instrumento, distintos períodos), ejecución del segundo Expert Advisor

Figura 4. Vamos a asignar distintos números mágicos a dos Expert Advisors idénticos (un instrumento, distintos períodos), ejecución del segundo Expert Advisor

Figura 5. El resultado es un funcionamiento sin conflictos de los Expert Advisors con un único instrumento y varios números mágicos

Figura 5. El resultado es un funcionamiento sin conflictos de los Expert Advisors con un único instrumento y varios números mágicos

La ejecución de la prueba se ha desarrollado con éxito, los Expert Advisors han funcionado muy bien el uno al lado del otro, y parece que no hay ningún conflicto.

Se ha implementado el primer punto de las especificaciones técnicas, pero todavía hay más.


Codificación del número mágico

Para la implementación de las siguientes partes, tendremos que desarrollar una clase de métodos, que van a codificar / decodificar la información, así como recuperar los valores a partir de las funciones integradas, y convertirlos a un formato determinado.

Para ello, vamos a repetir los requisitos de la codificación (por así decirlo, las especificaciones técnicas para el desarrollo):

  • Los métodos tienen que codificar el nombre del Expert Advisor (vamos a llamarlo el nombre digital),
  • El código de identificación de propio / ajeno (lo llamaremos el código de interacción)
  • El código del símbolo, sobre el cual se está ejecutando el Expert Advisor (para poder determinar mediante la transacción dónde está trabajando el EA).

Para empezar, vamos a elegir un nombre para la nueva clase, -que sea magic (un nombre genérico), asignamos nuestra enumeración, para hacer que el código sea más fácil de entender.

enum Emagic
  {
   ENUM_DIGITAL_NAME,    // digital name if the Expert Advisor
   ENUM_CODE_INTERACTION,// code of interaction
   ENUM_EXPERT_SYMBOL    // symbol, on which the EA is launched
  };

El funcionamiento de la enumeración es sencillo: describimos los nombres, separados por coma, y luego el compilador les asigna los números mediante la secuencia.

En primer lugar, si asignamos una variable de tipo distinto (esto no es válido para los números), entonces al especificar un parámetro a partir de la enumeración, recibimos un error durante la compilación, y en segundo lugar, obtenemos mayor claridad: no hay que asignar simplemente 0, sino más bien dar la orden para asignar ENUM_DIGITAL_NAME.

Como ocurre con la creación de una estructura o una clase, elegimos un nombre sencillo para la enumeración. Simplemente hemos añadido la letra E al nombre general elegido, y hemos obtenido Emagic, y respectivamente, la correspondiente estructura sería Smagic, y la clase Cmagic.

Una vez más, ten en cuenta que esto no es obligatorio, y que puede elegir Enumerator para la enumeración, Structurer para la estructura, y Classifier para la clase. Pero esto no va a proporcionar una uniformidad en los nombres, y la lectura de este tipo de código va a resultar complicada.

A continuación, vamos a crear la estructura para almacenar nuestros códigos.

struct Smagic
  {
   ulong             magicnumber;      // magic in an assembled form - how it is written in the order
   int               digital_name;     // digital name
   int               code_interaction; // code of interaction
   int               expert_symbol;    // symbol, on which the Expert Advisor is launched
  };

Después de esto, declaramos la clase Cmagic, en la cual registramos todos los métodos de codificación y decodificación del Magic, incluyendo los métodos del Expert Advisor anterior (simplemente se declaran en la clase actual y se vuelven a escribir los encabezados)

class Cmagic
  {
protected:
   Smagic            mag;
   SPositionVirtualMagic pvm;
public:
// the function returns the assembled magic, assembled from the incoming data
   ulong             SetMagic_request(int digital_name=0,int code_interaction=0);

// the function obtains the assembled magic and divides it according to the assembly logic
   ulong             SetMagic_result(ulong magicnumber);    

// the function obtains the return identification and returns the requested part of the assembled magic
   ulong             GetMagic_result(Emagic enum_); 

// the function obtains the return identification and returns the textual interpretation of the request part of the assembled magic
   string            sGetMagic_result(Emagic enum_);

// returns the voulme of the virtual position of the Expert Advisor
   double            cVOLUME(){return(pvm.volume);}
   
// returns the type of the virtual position of the Expert Advisor
   ENUM_POSITION_TYPE  cTYPE(){return(pvm.type);}
                                           
// method of calculating the virtual position, returns the presence of absence of the virtual position   
   bool              PositionVirtualMagic(Emagic enum_,
                                          string symbol,
                                          datetime CurrentTime);
private:
// function divides the magic into three parts  of three charges, and returns the part to which the category points to
   int               decodeMagic_result(int category); 

// interpretor of instrument symbols into the digital code                                                      
   int               symbolexpert();     
   
// interpretor of the digital code into the prescribed text (Expert Advisors)
   string            expertcode(int code);    
                                 
// interpretor of the digital code into the prescribed text (interaction)   
   string            codeinterdescript(int code);

// interpretor of the digital code into the instrument symbol                                         
   string            symbolexpert(int code);

// cycle of recording tickets into the buffer
   void              prHistory_Deals(ulong &buf[],int HTD);    
  };   

Ahora hemos desarrollado los métodos.

El primer método en la clase:

//+------------------------------------------------------------------+
//| Function returns the assembled magic,                            |
//| assembled from the input data                                    |
//+------------------------------------------------------------------+
ulong Cmagic::SetMagic_request(int digital_name=0,int code_interaction=0)
  {
   if(digital_name>=1000)Print("Incorrectly specified digital name of the Expert Advisor (more than 1000)");
   if(code_interaction>=1000)Print("Incorrectly specified the code of recognizing yours-foreign (more than 1000)");
   mag.digital_name     =digital_name;
   mag.code_interaction =code_interaction;
   mag.expert_symbol    =symbolexpert();
   mag.magicnumber      =mag.digital_name*(int)pow(1000,2)+
                         mag.code_interaction*(int)pow(1000,1)+
                         mag.expert_symbol;
   return(mag.magicnumber);
  }

Este método recibe dos valores: el nombre digital del Expert Advisor y el código de interacción.

ulong Cmagic::SetMagic_request(int digital_name=0,int code_interaction=0)

Y comprueba su veracidad inmediatamente:

   if(digital_name>=1000)Print("Incorrectly specified the digital name of the Expert Advisor(more than 1000)");
   if(code_interaction>=1000)Print("Incorrectly specifies the code of recognizing yours-foreign (more than 1000)");

Pero no hay ninguna respuesta ante las acciones del usuario, incluso si hay un error, sigue funcionando con toda normalidad.

A continuación es el turno de la asignación en la estructura de los datos de entrada, que especifica el usuario, sin embargo, el instrumento del símbolo no está indicado y se obtiene mediante el método privado:

int Cmagic::symbolexpert()

No voy a proporcionar el código, ya que es muy largo y está incluido en el archivo adjunto. Solo quiero decir que este método es básicamente una tabla, que asigna a cada símbolo de la ventana "vista del mercado" (market view) el número correspondiente: para EURUSD, por ejemplo, es el 1, etc.

Seguramente, podemos obtener estos datos de forma dinámica, mediante la escritura de un código para supervisar que divisas están presentes en la ventana "vista del mercado", pero la solución debe ser acorde a la complejidad del problema, y no tiene sentido estar llamando a las ventanas, ya que lo haremos de la manera más sencilla, -incluir una lista de divisas y asignar un índice a cada una de ellas.

Y, finalmente, la línea más importante de todo el método:

mag.magicnumber      =mag.digital_name*(int)pow(1000,2)+
                      mag.code_interaction*(int)pow(1000,1)+
                      mag.expert_symbol;

una combinación de las distintas partes de todo el Número mágico. Este es el Número mágico que se asignará a la orden de nuestro Expert Advisor.

El siguiente método público de nuestra clase:

//+------------------------------------------------------------------+
//| Function obtains the assembled magic                             |
//| and divides it according to the logic of the assembly            |
//+------------------------------------------------------------------+
ulong Cmagic::SetMagic_result(ulong magicnumber)
  {
   mag.magicnumber      =magicnumber;
   mag.expert_symbol    =decodeMagic_result(1);
   mag.code_interaction =decodeMagic_result(2);
   mag.digital_name     =decodeMagic_result(3);
   return(mag.magicnumber);
  }

En realidad, este método actúa como un envoltorio, distribuyendo a través de la estructura los resultados de las tres llamadas de un método privado único. La declaración con este tipo de especificador es buena por el hecho de que no se muestran los mensajes de peticiones emergentes, cuando llamamos a una variable de clase, dando la impresión de que todo el trabajo ha sido realizado mediante una función pública.

Pero volvamos a nuestras funciones privadas:

//+------------------------------------------------------------------+
//| Function divides the magic into three parts of three charges     |
//| and returns the part, which the category points to               |
//+------------------------------------------------------------------+
int Cmagic::decodeMagic_result(int category)
  {
   string string_value=(string)mag.magicnumber;
   int rem=(int)MathMod(StringLen(string_value),3);
   if(rem!=0)
     {
      rem=3-rem;
      string srem="0";
      if(rem==2)srem="00";
      string_value=srem+string_value;
     }
   int start_pos=StringLen(string_value)-3*category;
   string value=StringSubstr(string_value,start_pos,3);
   return((int)StringToInteger(value));
  }

Visualmente, este método puede ser representado como una lectura de un número de tres dígitos a partir del campo indicado, por ejemplo, si tenemos un Número mágico 123456789 , lo podemos representar como | 123 | 456 | 789 | si el campo indicado es 1 , entonces el resultado sería 789 ya que los campos están enumerados de derecha a izquierda.

De modo que después de utilizar los tres campos en el método llamado, distribuimos todos los datos adquiridos a la estructura. Esto se hace cambiando el Número mágico a un tipo de cadena (string) minúscula:

string string_value=(string)mag.magicnumber;

seguido de la ordenación de las líneas de los componentes de líneas independientes.

A continuación van dos funciones parecidas, que en realidad son interruptores (switch), y solo se diferencian en el tipo de los valores de salida:

//+------------------------------------------------------------------+
//| Function obtains the identifier of the return                    |
//| and returns the requested part of the assembled magic            |
//+------------------------------------------------------------------+
ulong Cmagic::GetMagic_result(Emagic enum_)
  {
   switch(enum_)
     {
      case ENUM_DIGITAL_NAME     : return(mag.digital_name);     break;
      case ENUM_CODE_INTERACTION : return(mag.code_interaction); break;
      case ENUM_EXPERT_SYMBOL    : return(mag.expert_symbol);    break;
      default: return(mag.magicnumber); break;
     }
  }
//+------------------------------------------------------------------------------+
//| Function obtains the identifier of the return and returns                    |
//| a textual interpretation of the requested type of the assembled magic        |
//+------------------------------------------------------------------------------+
string Cmagic::sGetMagic_result(Emagic enum_)
  {
   switch(enum_)
     {
      case ENUM_DIGITAL_NAME     : return(expertcode(mag.digital_name));            break;
      case ENUM_CODE_INTERACTION : return(codeinterdescript(mag.code_interaction)); break;
      case ENUM_EXPERT_SYMBOL    : return(symbolexpert(mag.expert_symbol));         break;
      default: return((string)mag.magicnumber); break;
     }
  }

Las funciones devuelven la parte del número mágico, que indica el parámetro de tipo Emagic, la primera proporciona un resultado del tipo ulong, que se utiliza en los cálculos, y la segunda proporciona un resultado string, que se puede utilizar para la visualización.

En la función GetMagic_result () está todo organizado de manera sencilla, distribuye los valores de la estructura por las ramas switch, mientras sGetMagic_result () es un poco más complicada. Cada case llama a una función de tablas, que pasa los valores de la estructura a una forma visual. Por lo tanto, si el valor mag.expert_symbol = 1, entonces la primera función va a devolver 1, y la segunda EURUSD .

Ya he explicado las ventajas de las funciones de tablas en la codificación / decodificación de la información, así que solo voy a mencionar que hay que tratar cada caso por separado, basándose en la complejidad de la implementación de un método sin tablas, y sus ventajas en lo que se refiere al tiempo necesario para escribir las tablas. Si es más fácil escribir tablas de estados, entonces no hay ninguna necesidad de complicar las cosas. Pero si la escritura de la tabla requiere mucho tiempo, obviamente, la mejor opción sería utilizar los métodos de procedimiento. Para ahorrar espacio, no estoy proporcionando las tablas (se pueden encontrar en los archivos adjuntos).

Básicamente, esto es todo, hemos desarrollado nuestra clase, sin embargo, aún están las cuatro funciones restantes que hemos utilizado en el desarrollo de Expert Advisor anterior. 

Simplemente vuelvo a declararlas en una nueva clase, teniendo en cuenta sobre todo, que tienen que ser ligeramente modificadas.

Ahora el método principal:

bool  Cmagic::PositionVirtualMagic(Emagic enum_,
                                   string symbol,
                                   datetime CurrentTime)

no solo se declara como un método de la clase Cmagic sino que también tiene una serie diferente de parámetros.

En lugar del Número mágico, se identifica ahora mediante el campo del Número mágico, cuya posición ha sido calculada. Además, a pesar de la presencia del símbolo en la última opción, solo se ha utilizado para obtener la información sobre el paso o incremento mínimo del lote mediante el símbolo. Y ahora está incluido en el filtro, y puede participar, en las mismas condiciones con los otros, en filtrar el conteo de las posiciones.

¿Qué nos aporta esto? Ahora podemos filtrar al mismo tiempo las transacciones que estaban abiertas en distintos instrumentos, pero mediante el mismo Expert Advisor. Haciéndolo así, no hay que confundirse con otros Expert Advisors similares, ejecutándose en un instrumento distinto. Con toda sinceridad, es muy difícil describir todas las formas posibles de utilizar este nuevo sistema de cálculo. Y el lector puede decidir personalmente lo que necesita para un sistema tan complicado. Recomiendo encarecidamente no complicar los casos en los que la escritura del código puede ser sencilla, y tampoco hay que tener miedo a tales complicaciones cuando de verdad hacen falta.

Bueno, ya que está las clase diseñada, es el momento de probarla con un nuevo Expert Advisor:

//+------------------------------------------------------------------+
//| Code of the Expert Advisor                                       |
//+------------------------------------------------------------------+
//--- input parameters
input ulong              digital_name_       =4;           // Digital name of Expert Advisor
input ulong              code_interaction_   =1;           // Code of interaction
input Emagic             _enum               =0;           // Model of magic number  
input int                SL                  =300;         // Stop Loss
input int                TP                  =1000;        // Take Profit
input int                MA_Period           =25;          // MA period
input double             lot                 =0.4;         // Volume of position
input int                MA_shift            =0;           // Shift of indicator
input ENUM_MA_METHOD     MA_smooth           =MODE_SMA;      // Smoothing type
input ENUM_APPLIED_PRICE price               =PRICE_OPEN;    // Price type 

//--- we will store the indicator's handle
int MA_handle,type_MA,rezult;
static ulong magic;
double v[2];
datetime  CurrentTime;// The variable stores the time of start of the Expert Advisor

MqlTradeResult  res;   // Pointer to the structure of responding by OrderSend
MqlTick         tick;  // Pointer to the structure of last market information
CProvision prov;
Cmagic mg;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   magic=mg.SetMagic_request(digital_name_,code_interaction_);
// Stamp of Expert Advisor (the magic number identifier) the magic variable is declared at the global scope
// used in int CProvision::SendOrder(ENUM_ORDER_TYPE type,double volume)
   CurrentTime=TimeCurrent();// The variable stores the time of start of the Expert Advisor
//--- Create the indicator's handle
   MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price);
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(CopyBuffer(MA_handle,0,0,2,v)<=0)
     {Print("#",magic,"Error of copying");return;}
   type_MA=prov.TYPE(v); // Determine type depending on MA indication
   mg.SetMagic_result(magic);// put the information into the structure
   if(mg.PositionVirtualMagic(_enum,_Symbol,CurrentTime))// If three is an open position 
     {
      if((int)mg.cTYPE()!=type_MA)// Check if it is time to close
        {
         mg.SetMagic_result(magic);// put the information into the structure
         Print("#",mg.GetMagic_result(_enum),"Position by magic number has volume ",mg.cVOLUME(),
               " reverse position of type ",(int)mg.cTYPE()," by ",type_MA);
         //cpvm.cVOLUME() - volume of virtual position
         rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,mg.cVOLUME()+lot);// reverse position
         if(rezult!=-1)Print("№",magic," Code of the operation result ",rezult," volume ",res.volume);
         else{Print("№",magic,"Error",GetLastError()); return;}
        }
     }
   else // If there is no open position then open
     {
      Print("#",magic,"Position by magic number has volume  ",mg.cVOLUME()," open position of type",type_MA);
      rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);// Open position 
      if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
      else{Print("#",magic,"Error",GetLastError()); return;}
     }
  }

Como se mencionó antes, este Expert Advisor es extremadamente sencillo y se creó solo para demostrar las distintas capacidades, ejecútelo tres veces en un solo instrumento:

Figura 6. Implementación de tres Expert Advisors, con distintos números mágicos y distintos gráficos

Figura 6. Implementación de tres Expert Advisors, con distintos números mágicos y distintos gráficos

Figura 7. El resultado es un trading sin conflictos de tres Expert Advisors con distintos números mágicos

Figura 7. El resultado es un trading sin conflictos de tres Expert Advisors con distintos números mágicos

Como se puede observar en los mensajes de los Expert Advisors, los tres programas se han lanzado con éxito y no muestran ningún conflicto.


Conclusión

Al proporcionar la posibilidad de asignar órdenes mágicas a las operaciones de trading, los creadores de MQL5 han facilitado en gran medida la vida de los que escriben Expert Advisors. Pero los desarrolladores solo pueden proporcionarle las herramientas que en realidad necesita para conseguir los diamantes.

Buena suerte y hasta la próxima.


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

Archivos adjuntos |
magic_exp0_en.mq5 (8.34 KB)
magic_exp1_en.mq5 (11.84 KB)
magic_exp2_en.mq5 (25.39 KB)
Uso de MetaTrader 5 como proveedor de señales comerciales para MetaTrader 4 Uso de MetaTrader 5 como proveedor de señales comerciales para MetaTrader 4
En este artículo se discuten las particularidades del uso de MetaTrader 5 como proveedor de señales comerciales para MetaTrader 4. Ustedes conocerán cómo crear un sencillo proveedor de señales desde MetaTrader 5 y cómo conectarlo a varios terminales MetaTrader 4. Además, conocerán cómo copiar en tiempo real las transacciones de los participantes de Automated Trading Championship a su cuenta real en MetaTrader 4.
Los principios del cálculo económico de los indicadores Los principios del cálculo económico de los indicadores
Las llamadas a los usuarios y a los indicadores técnicos requieren muy poco espacio en el código del programa de los sistemas de trading automatizados. Se trata a menudo de pocas líneas de código. Pero con frecuencia, son estas pocas líneas de código las que requieren la mayor parte del tiempo, destinada a probar el Expert Advisor. Por lo tanto, hay que tener en cuenta mucho más de lo parecía al principio todo lo que está relacionado con los cálculos de datos en un indicador. Este artículo trata justamente esta cuestión.
Carry Trading Estadístico Carry Trading Estadístico
Algoritmo de protección estadística de posiciones abiertas con swap (permutaciones) positivas contra movimientos no deseados de las cotizaciones. Para compensar el riesgo potencial que supone el movimiento de las cotizaciones en dirección opuesta a la posición abierta, en este artículo se presenta la variante Carry Trading de estrategia protegida.
Pruebas de rendimiento computacional de los promedios móviles en MQL5 Pruebas de rendimiento computacional de los promedios móviles en MQL5
Desde la creación del primer indicador de Promedio móvil, surgieron muchos indicadores. Muchos de ellos utilizan los mismos métodos de suavizado, pero no se han estudiado los rendimientos de los distintos algoritmos de los promedios móviles. En este artículo, vamos a examinar distintas maneras de utilizar los Promedios móviles en MQL5 y comparar su rendimiento.