English Русский 中文 Deutsch 日本語 Português
LifeHack para tráders: cocinamos ForEach usando #define

LifeHack para tráders: cocinamos ForEach usando #define

MetaTrader 5Ejemplos | 23 febrero 2018, 09:47
1 978 1
Vladimir Karputov
Vladimir Karputov

 — ¿En qué reside la fuerza, hermano?                                
  —La fuerza, hermano, está en las directivas define                    

¿Aún sigue escribiendo en MQL4 y quiere dar el salto a MQL5, pero no sabe por dónde comenzar? ¡Deje que le ayudemos! Ahora ha aparecido la posibilidad de trabajar confortablemente en el editor MetaEditor MQL5 y seguir usando la anotación MQL4 (en honor a la verdad, debemos observar que ya apareció hace un tiempo: en este artículo queremos ofrecer una descripción más amplia y detallada del traslado de las funciones MQL4 a MQL5).

Un buen programador es un programador perezoso

La creación de asesores o robots comerciales requiere casi siempre de mucho trabajo con los ciclos. Los ciclos nos rodean por todas partes: la iteración de órdenes, de transacciones en la historia, de objetos en el gráfico, de símbolos en la Observación del mercado, de barras en el búfer de indicador. Y para facilitar un poco la vida del programador, al MetaEditor se han añadido los snippets : una característica que permite al usuario introducir los símbolos iniciales y desplegar estos en un pequeño fragmento de código pulsando la tecla Tab. Aquí vemos cómo funciona un snippet para el ciclo for:


No está mal, pero no satisface todas nuestras necesidades. Un ejemplo más sencillo: tenemos que iterar todos los símbolos en la Observación de mercado.

   int total=SymbolsTotal(true);
   for(int i=0;i<total;i++)
     {
      string symbol=SymbolName(i,true);
      PrintFormat("%d. %s",i+1,symbol);
     }

Estaría muy bien crear en el MetaEditor nuestro propio snippet, que comenzase por fes (for_each_symbol) y se desplegase en un bloque así:


Pero en el MetaEditor no tenemos snippets personalizados, por eso haremos otra cosa: recurriremos a las directivas define. La macrosustitución #define se diseñó para programadores perezosos e inteligentes, que perseguían varios objetivos. Entre ellos, lograr una mayor sencillez de lectura y una mayor comodidad de escritura en un código que se repite múltiples veces.

En muchos lenguajes de programación, aparte del ciclo clásico for , existe también una variedad del tipo for(<typename> element:Collection) o for each (type identifier in expression). Si fuese posible escribir un código del tipo

for(ulong order_id in History)
  {
   trabajo con order_id  
  }

, la vida del programador sería mucho más sencilla. En internet podrá encontrar tanto a seguidores, como a detractores de este enfoque. Aquí les vamos a mostrar cómo hacer algo semejante con la ayuda de los macros #define.

Vamos a comenzar con una sencilla tarea, obtener los nombres de todos los símbolos en la Observación del mercado. Vamos a escribir directamente un macro así:

#define ForEachSymbol(s,i)  string s; int total=SymbolsTotal(true); for(int i=0;i<total;i++,s=SymbolName(i,true))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   ForEachSymbol(symbol,index)
     {
      PrintFormat("%d. %s",index,symbol);
     }
  }

El compilador comprende perfectamente esta entrada y no da error. Iniciamos la depuración con F5 y vemos que algo no ha salido como debería:

1. (null)
2. GBPUSD
3. USDCHF
4. USDJPY
...

El problema es que la expresión s=SymbolName(i,true) en el ciclo for se calcula después de ejecutar la iteración, y nuestra variable S no ha sido iniciada en la primera iteración, cuando i=0. Operador del ciclo for:

El operador for consta de tres expresiones y un operador ejecutable:

for(expresión1; expresión2; expresión3)
   operador;

La expresión1 describe la inicialización del ciclo. La expresión2 comprueba la condición de finalización del ciclo. Si es verdadera, se ejecuta el operador del cuerpo del ciclo for. Todo se repite, hasta que la expresión2 sea falsa. Si es falsa, el ciclo finaliza y el control se transmite al siguiente operador. La expresiónЗ se calcula después de cada iteración.

Esto se soluciona fácilmente. Hacemos un par de correcciones:

#define ForEachSymbol(s,i)  string s=SymbolName(0,true); int total=SymbolsTotal(true); for(int i=1;i<total;i++,s=SymbolName(i,true))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   ForEachSymbol(symbol,index)
     {
      PrintFormat("%d. %s",index,symbol); // en lugar de index+1, ahora tenemos simplemente index
     }
  }

y obtenemos el resultado necesario. Hemos escrito el macro ForEachSymbol con los parámetros symbol y index, como si se tratara de un ciclo for normal, y en el cuerpo del pseudociclo hemos trabajado con estas variables como si ya hubieran sido declaradas e inicializadas con los valores necesarios. De esta forma, podemos obtener las propiedades necesarias de los símbolos de la Observación del mercado con la ayuda de funciones del tipo SymbolInfoXXX(). Por ejemplo, así:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   ForEachSymbol(symbol,index)
     {
      //--- preparamos los datos
      double spread=SymbolInfoDouble(symbol,SYMBOL_ASK)-SymbolInfoDouble(symbol,SYMBOL_BID);
      double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
      long digits=SymbolInfoInteger(symbol,SYMBOL_DIGITS);
      string str_spread=DoubleToString(spread/point,0);
      string str_point=DoubleToString(point,digits);
      //--- mostramos la información
      Print(index,". ",symbol," spread=",str_spread," points (",
            digits," digits",", point=",str_point,")");
     }
/* Ejemplo mostrado
        1  EURUSD spread=3 points (5 digits, point=0.00001)
        2. USDCHF spread=8 points (5 digits, point=0.00001)
        3. USDJPY spread=5 points (3 digits, point=0.001)
        4. USDCAD spread=9 points (5 digits, point=0.00001)
        5. AUDUSD spread=5 points (5 digits, point=0.00001)
        6. NZDUSD spread=10 points (5 digits, point=0.00001)
        7. USDSEK spread=150 points (5 digits, point=0.00001)
*/
  }

Ahora también podemos escribir un macro semejante para iterar los objetos gráficos en el gráfico:

#define ForEachObject(name,i)   string name=ObjectName(0,0); int total=ObjectsTotal(0); for(int i=1;i<=total;i++,name=ObjectName(0,i-1))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   ForEachObject(objectname,index)
     {
      Print(index,": objectname=\"",objectname,"\", objecttype=",
            EnumToString((ENUM_OBJECT)ObjectGetInteger(0,objectname,OBJPROP_TYPE)));
     }
/* Ejemplo mostrado
        1: objectname="H1 Arrow 61067", objecttype=OBJ_ARROW_UP
        2: objectname="H1 Rectangle 31152", objecttype=OBJ_RECTANGLE
        3: objectname="H1 StdDev Channel 56931", objecttype=OBJ_STDDEVCHANNEL
        4: objectname="H1 Trendline 6605", objecttype=OBJ_TREND
*/     
  }

La verdad es que la línea de definición del macro ForEachObject ahora es un poco más larga, además, entender un código de sustitución escrito en una sola línea no es tan sencillo. Pero resulta que este problema también está resuelto: la definición del macro se puede dividir en líneas con la ayuda de la barra inversa '\'. Así:

#define ForEachObject(name,i)   string name=ObjectName(0,0);   \
   int ob_total=ObjectsTotal(0);                               \
   for(int i=1;i<=ob_total;i++,name=ObjectName(0,i-1))

Para el compilador, estas tres líneas tendrán el aspecto de una línea larga, pero para el programador resulta bastante más sencillo leer un código así. Ya solo nos queda crear macros semejantes para trabajar con entidades comerciales: órdenes, posiciones y transacciones.

Vamos a comenzar con la iteración de órdenes. Aquí se añade solo la función de elección de la historia HistorySelect():

#define ForEachOrder(ticket,i)    HistorySelect(0,TimeCurrent());    \
  ulong ticket=OrderGetTicket(0);                                    \ 
  int or_total=OrdersTotal();                                        \
  for(int i=1;i<or_total;i++,ticket=OrderGetTicket(i))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   ForEachOrder(orderticket,index)
     {
      Print(index,": #",orderticket," ",OrderGetString(ORDER_SYMBOL)," ",
            EnumToString((ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE)));
     }
/* Ejemplo mostrado     
   1: #13965457 CADJPY ORDER_TYPE_SELL_LIMIT
   2: #14246567 AUDNZD ORDER_TYPE_SELL_LIMIT
*/ 
  }

Podemos notar que en este caso, además, en este macro (y los dos anteriores) no existe el procesamiento de errores. Por ejmplo, ¿qué sucede si HistorySelect() retorna false? Podemos responder solo que en este caso no podremos iterar en el ciclo por todas las órdenes. Además, ¿quién analiza el resultado de la ejecución de HistorySelect()? Resulta que en este macro tampoco hay nada prohibido para el método habitual de escritura de programas.

Pero la crítica más intensa que se puede hacer al uso de la directiva #define es que el uso de macrosustituciones no permite realizar la depuración del código. Estamos de acuerdo con ello, aunque, como dice fxsaber, "Un paciente bien sujeto no necesita anestesia, y un macro bien depurado no necesita depuración".

A continuación, hacemos un macro por analogía para la iteración de posiciones:

#define ForEachPosition(ticket,i) HistorySelect(0,TimeCurrent());    \
   ulong ticket=PositionGetTicket(0);                                \
   int po_total=PositionsTotal();                                    \
   for(int i=1;i<=po_total;i++,ticket=PositionGetTicket(i-1))        
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   ForEachPosition(positionid,index)
     {
      Print(index,": ",PositionGetString(POSITION_SYMBOL)," postionID #",positionid);
     }
/* Ejemplo mostrado 
   1: AUDCAD postionID #13234934
   2: EURNZD postionID #13443909
   3: AUDUSD postionID #14956799
   4: EURUSD postionID #14878673
*/ 

Iteración de las transacciones en la historia:

#define ForEachDeal(ticket,i) HistorySelect(0,TimeCurrent());        \
   ulong ticket=HistoryDealGetTicket(0);                             \
   int total=HistoryDealsTotal();                                    \
   for(int i=1;i<=total;i++,ticket=HistoryDealGetTicket(i-1))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   //---
   ForEachDeal(dealticket,index)
     {
      Print(index,": deal #",dealticket,",  order ticket=",
            HistoryDealGetInteger(dealticket,DEAL_ORDER));
     }
  }

Iteración de órdenes en la historia:

#define ForEachHistoryOrder(ticket,i) HistorySelect(0,TimeCurrent());\
   ulong ticket=HistoryOrderGetTicket(0);                            \
   int total=HistoryOrdersTotal();                                   \
   for(int i=1;i<=total;i++,ticket=HistoryOrderGetTicket(i-1))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   ForEachHistoryOrder(historyorderticket,index)
     {
      Print(index,": #",historyorderticket);
     }
  }

Reunimos todas las macrosustituciones en el archivo ForEach.mqh4:

//+------------------------------------------------------------------+
//|                                                      ForEach.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
//+------------------------------------------------------------------+
//| Ciclo de iteración de símbolos en la Observación de mercado      |
//+------------------------------------------------------------------+
#define ForEachSymbol(symbol,i)  string symbol=SymbolName(0,true);   \
   int os_total=SymbolsTotal(true);                                  \
   for(int i=1;i<os_total;i++,symbol=SymbolName(i,true))
//+------------------------------------------------------------------+
//| Ciclo de iteración de objetos en la ventana principal del gráfico|
//+------------------------------------------------------------------+   
#define ForEachObject(name,i)   string name=ObjectName(0,0);         \
   int ob_total=ObjectsTotal(0);                                     \
   for(int i=1;i<=ob_total;i++,name=ObjectName(0,i-1))      
//+------------------------------------------------------------------+
//| Ciclo de iteración de órdenes activas                            |
//+------------------------------------------------------------------+
#define ForEachOrder(ticket,i)    HistorySelect(0,TimeCurrent());    \
   ulong ticket=OrderGetTicket(0);                                   \
   int or_total=OrdersTotal();                                       \
   for(int i=1;i<or_total;i++,ticket=OrderGetTicket(i))   
//+------------------------------------------------------------------+
//| Ciclo de iteración de posiciones abiertas                        |
//+------------------------------------------------------------------+
#define ForEachPosition(ticket,i) HistorySelect(0,TimeCurrent());    \
   ulong ticket=PositionGetTicket(0);                                \
   int po_total=PositionsTotal();                                    \
   for(int i=1;i<=po_total;i++,ticket=PositionGetTicket(i-1))      
//+------------------------------------------------------------------+
//| Ciclo de iteración de transacciones en la historia               |
//+------------------------------------------------------------------+
#define ForEachDeal(ticket,i) HistorySelect(0,TimeCurrent());        \
   ulong ticket=HistoryDealGetTicket(0);                             \
   int dh_total=HistoryDealsTotal();                                 \
   for(int i=1;i<=dh_total;i++,ticket=HistoryDealGetTicket(i-1))        
//+------------------------------------------------------------------+
//| Ciclo de iteración de órdenes en la historia                     |
//+------------------------------------------------------------------+
#define ForEachHistoryOrder(ticket,i) HistorySelect(0,TimeCurrent());\
   ulong ticket=HistoryOrderGetTicket(0);                            \
   int oh_total=HistoryOrdersTotal();                                \
   for(int i=1;i<=oh_total;i++,ticket=HistoryOrderGetTicket(i-1))   
//+------------------------------------------------------------------+

Preste atención: hemos tenido que añadir un prefijo para cada macro para la variable "total", para que no haya conflictos si decidimos utilizar más de un macro en nuestro código. Esta es la principal desventaja de este macro: escondemos tras este la declaración de una variable que se verá desde el exterior. Esto puede provocar errores de compilación difíciles de detectar.

Además, al usar un macro paramétrico, el compilador no da ninguna pista sobre cómo se hace esto para las funciones. Tendrá que estudiar estos 6 macros, si desea utilizarlos. Aunque no resulta complicado: el primer parámetro siempre es la entidad que se itera en el ciclo, y el segundo parámetro es siempre el índice del ciclo que comienza por 1 (unidad).

Al fin, vamos a añadir otro macro para el bypass en orden inverso. En este caso, es necesario moverse desde el final de la lista hacia el principio. Añadimos al nombre del macro el sufijo Back y hacemos pequeñas correcciones. Este será el aspecto del macro para la iteración de los símbolos en la observación del mercado.

#define ForEachSymbolBack(symbol,i) int s_start=SymbolsTotal(true)-1;\
   string symbol=SymbolName(s_start,true);                           \
   for(int i=s_start;i>=0;i--,symbol=SymbolName(i,true))
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   ForEachSymbolBack(symbol,index)
     {
      //--- preparamos los datos
      double spread=SymbolInfoDouble(symbol,SYMBOL_ASK)-SymbolInfoDouble(symbol,SYMBOL_BID);
      double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
      long digits=SymbolInfoInteger(symbol,SYMBOL_DIGITS);
      string str_spread=DoubleToString(spread/point,0);
      string str_point=DoubleToString(point,digits);
      //--- mostramos la información
      Print(index,". ",symbol," spread=",str_spread," points (",
            digits," digits",", point=",str_point,")");
     }
/* Ejemplo mostrado
   3. USDJPY spread=5 points (3 digits, point=0.001)
   2. USDCHF spread=8 points (5 digits, point=0.00001)
   1  GBPUSD spread=9 points (5 digits, point=0.00001)
   0. EURUSD spread=2 points (5 digits, point=0.00001)
*/
  }

Como puede ver, en este caso, el valor de la variable index varía entre size-1 y 0. Con esto, finalizamos la introducción a #define y pasamos a la escritura de las funciones para el acceso simple al estilo MQL4.

1 Qué grupos de funiones MQL4 se describen en el artículo

Atención: Deberá cambiar por sí mismo variables tales como Point, Digits y Bar a Point(), Digits() y Bar(Symbol(),Period())

Se pasarán a MQL5 los grupos MQL4 AccountXXXX, MQL4 MarketInfo, MQL4 Comprobación del estado, MQL4 Variables predefinidas. De esta forma, a la carpeta [date folder]\MQL5\Include\SimpleCall\ se añadirán cuatro archivos: AccountInfo.mqhMarketInfo.mqhCheck.mqhPredefined.mqh. En total, en la carpeta habrá siete archivos para pasar las funciones MQL4 a MQL5: AccountInfo.mqh, Check.mqh, IndicatorsMQL4.mqh, IndicatorsMQL5.mqh, MarketInfo.mqh, Predefined.mqh y Series.mqh.

Para trabajar, usted deberá incluir estos archivos por sí mismo. En este caso, además, existe una limitación: los archivos IndicatorsMQL4.mqh y IndicatorsMQL5.mqh no se pueden incluir a la vez, hay que elegir uno de ellos. Por eso tenemos dos archivos: SimpleCallMQL4.mqh — incluye todos los archivos más IndicatorsMQL4.mqh y SimpleCallMQL5.mqh — incluye todos los archivos más IndicatorsMQL5.mqh.

Ejemplo de inclusión basado en MACD Sample.mq4

Copiamos el archivo MACD Sample.mq4 a una carpeta MQL5 con los expertos, por ejemplo, a [data folder]\MQL5\Experts\, y cambiamos la extensión del archivo a mq5. De esta forma, obtenemos el archivo MACD Sample.mq5. Lo compilamos y obtenemos directamente 59 errores y una advertencia.

Ahora incluimos el archivo SimpleCallMQL4.mqh:

//+------------------------------------------------------------------+
//|                                                  MACD Sample.mq4 |
//|                   Copyright 2005-2014, MetaQuotes Software Corp. |
//|                                              http://www.mql4.com |
//+------------------------------------------------------------------+
#property copyright   "2005-2014, MetaQuotes Software Corp."
#property link        "http://www.mql4.com"
//---
#include <SimpleCall\SimpleCallMQL4.mqh>
//---
input double TakeProfit    =50;

Compilamos de nuevo y esta vez obtenemos 39 errores y una advertencia. Ahora necesitamos sustituir manualmente (con la ayuda de Ctrl+H) Bars por Bars(Symbol(),Period()) y Point por Point(). Copiamos. Quedan 35 errores, todos ellos relacionados con las funciones comerciales. Pero no vamos a hablar sobre las funciones comerciales en este artículo.

1.1. MQL4 AccountXXXX

La conversión de las funciones MQL4 AccountXXXX se ha ejeuctado en el archivo [date folder]\MQL5\Include\SimpleCall\AccountInfo.mqh

El recuadro de correspondencias de las funciones de MQL4 con las funciones MQL5 tiene el aspecto siguiente:

MQL4 MQL5 AccountInfoXXXX MQL5 CAccountInfo  Observaciones 
 AccountInfoDouble  AccountInfoDouble  CAccountInfo::InfoDouble o los métodos CAccountInfo::double  
 AccountInfoInteger  AccountInfoInteger  CAccountInfo::InfoInteger o los métodos de número entero CAccountInfo::  
 AccountInfoString  AccountInfoString  CAccountInfo::InfoString o los métodos de texto CAccountInfo::  
 AccountBalance  AccountInfoDouble(ACCOUNT_BALANCE) o   CAccountInfo::Balance  
 AccountCredit  AccountInfoDouble(ACCOUNT_CREDIT) o   CAccountInfo::Credit  
 AccountCompany  AccountInfoString(ACCOUNT_COMPANY) o   CAccountInfo::Company  
 AccountCurrency  AccountInfoString(ACCOUNT_CURRENCY) o   CAccountInfo::Currency  
 AccountEquity  AccountInfoDouble(ACCOUNT_EQUITY) o   CAccountInfo::Equity  
 AccountFreeMargin  AccountInfoDouble(ACCOUNT_FREEMARGIN) o   CAccountInfo::FreeMargin  
 AccountFreeMarginCheck  --- /---  CAccountInfo::FreeMarginCheck   
 AccountFreeMarginMode  No hay análogos  No hay análogos  
 AccountLeverage  AccountInfoInteger(ACCOUNT_LEVERAGE)   CAccountInfo::Leverage  En MQL4 tiene el tipo int, en MQL5: long
 AccountMargin  AccountInfoDouble(ACCOUNT_MARGIN)  CAccountInfo::Margin  
 AccountName  AccountInfoString(ACCOUNT_NAME)  CAccountInfo::Name  
 AccountNumber  AccountInfoInteger(ACCOUNT_LOGIN)  CAccountInfo::Login  En MQL4 tiene el tipo int, en MQL5: long
 AccountProfit  AccountInfoDouble(ACCOUNT_PROFIT)  CAccountInfo::Profit   
 AccountServer  AccountInfoString(ACCOUNT_SERVER)  CAccountInfo::Server  
 AccountStopoutLevel  AccountInfoDouble(ACCOUNT_MARGIN_SO_SO)  CAccountInfo::MarginStopOut  En MQL4 tiene el tipo int, en MQL5: double
 AccountStopoutMode  AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE)  CAccountInfo::StopoutMode  

Preste atención: para MQL4 AccountFreeMarginMode no hay análogo en MQL5. En lo sucesivo, deberá usar MQL4 AccountFreeMarginMode por su cuenta y riesgo. Si se da MQL4 AccountFreeMarginMode, en el log se imprimirá una advertencia y se retornará un NaN — "no es un número".

Para el resto de las funciones MQL4 AccountXXXX hay análogos, además en dos variantes: a través de AccountInfoXXXX o a través de la clase comercial CAccountInfo. Para AccountInfoDouble, AccountInfoInteger y AccountInfoString no hay diferencias entre MQL4 y MQL5.

En el encabezado del archivo se ha introducido este código. ¿Cómo funciona?

//---
#define  OP_BUY                     ORDER_TYPE_BUY
#define  OP_SELL                    ORDER_TYPE_SELL
//--- returns balance value of the current account

Para comenzar, echemos un vistazo al manual de referencia de #define:

La directiva #define se puede utilizar para asignar a las expresiones nombres mnemotécnicos. Hay dos formas:

#define identifier expression                   // forma no paramétrica 
#define identifier(par1,... par8) expression    // forma paramétrica

La directiva #define sustituye con "expression" todas las entradas sucesivas "identifier" encontradas en el texto original.

En el caso de nuestro código (hemos utilizado la forma no paramétrica): la directiva #define sustituye con ORDER_TYPE_BUY todas las entradas OP_BUY encontradas en el texto fuente. La directiva #define sustituye con ORDER_TYPE_SELL todas las entradas sucesivas OP_SELL encontradas en el texto fuente. Es decir, después de incluir el archivo [date folder]\MQL5\Include\SimpleCall\AccountInfo.mqh en su asesor MQL5, podrá utilizar en él los tipos MQL4 OP_BUY y OP_SELL de la forma acostumbrada. El compilador no dará error.

Bien, el primer grupo de funciones que convertiremos a la forma MQL5 comprende: AccountBalance(), AccountCredit(), AccountCompany(), AccountCurrency(), AccountEquity() y AccountFreeMargin():

//--- returns balance value of the current account
#define  AccountBalance(void)       AccountInfoDouble(ACCOUNT_BALANCE)
//--- returns credit value of the current account
#define  AccountCredit(void)        AccountInfoDouble(ACCOUNT_CREDIT)
//--- returns the brokerage company name where the current account was registered
#define  AccountCompany(void)       AccountInfoString(ACCOUNT_COMPANY)
//--- returns currency name of the current account 
#define  AccountCurrency(void)      AccountInfoString(ACCOUNT_CURRENCY)
//--- returns equity value of the current account
#define  AccountEquity(void)        AccountInfoDouble(ACCOUNT_EQUITY)
//--- returns free margin value of the current account
#define  AccountFreeMargin(void)    AccountInfoDouble(ACCOUNT_MARGIN_FREE)
Aquí en las funciones MQL4 XXXX(void) ya se usa la forma paramétrica #define, donde "void" actúa como parámetro. Esto se puede comprobar con facilidad: si quitamos "void", al compilar obtendremos el error "unexpected in macro format parameter list":


unexpected in macro format parameter list

En el caso con MQL4 AccountFreeMarginCheck, actuaremos de otra forma: implementaremos AccountFreeMarginCheck como una función dentro de la cual se usa solo código MQL5:

//--- returns free margin that remains after the specified order has been opened 
//---    at the current price on the current account
double   AccountFreeMarginCheck(string symbol,int cmd,double volume)
  {
   double price=0.0;
   ENUM_ORDER_TYPE trade_operation=(ENUM_ORDER_TYPE)cmd;
   if(trade_operation==ORDER_TYPE_BUY)
      price=SymbolInfoDouble(symbol,SYMBOL_ASK);
   if(trade_operation==ORDER_TYPE_SELL)
      price=SymbolInfoDouble(symbol,SYMBOL_BID);
//---
   double margin_check=EMPTY_VALUE;
   double margin=EMPTY_VALUE;
   margin_check=(!OrderCalcMargin(trade_operation,symbol,volume,price,margin))?EMPTY_VALUE:margin;
//---
   return(AccountInfoDouble(ACCOUNT_FREEMARGIN)-margin_check);
  }

Como se ha dicho anteriormente, AccountFreeMarginMode no tiene análogo en MQL5, por eso, al encontrar en el código AccountFreeMarginMode, se mostrará una advertencia y se retornará "no es un número":

//--- returns the calculation mode of free margin allowed to open orders on the current account
double AccountFreeMarginMode(void)
  {
   string text="MQL4 functions \"AccountFreeMarginMode()\" has no analogs in MQL5. Returned \"NAN - not a number\"";
   Alert(text);
   Print(text);
   return(double("nan"));
  }

Para el resto de las funciones MQL4, actuamos por analogía con las anteriores:

//--- returns leverage of the current account
#define  AccountLeverage(void)      (int)AccountInfoInteger(ACCOUNT_LEVERAGE)
//--- returns margin value of the current account
#define  AccountMargin(void)        AccountInfoDouble(ACCOUNT_MARGIN)
//--- returns the current account name
#define  AccountName(void)          AccountInfoString(ACCOUNT_NAME)
//--- returns the current account number
#define  AccountNumber(void)        (int)AccountInfoInteger(ACCOUNT_LOGIN)
//--- returns profit value of the current account
#define  AccountProfit(void)        AccountInfoDouble(ACCOUNT_PROFIT)
//--- returns the connected server name
#define  AccountServer(void)        AccountInfoString(ACCOUNT_SERVER)
//--- returns the value of the Stop Out level
#define  AccountStopoutLevel(void)  (int)AccountInfoDouble(ACCOUNT_MARGIN_SO_SO)
//--- returns the calculation mode for the Stop Out level
int      AccountStopoutMode(void)
  {
   ENUM_ACCOUNT_STOPOUT_MODE stopout_mode=(ENUM_ACCOUNT_STOPOUT_MODE)AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE);
   if(stopout_mode==ACCOUNT_STOPOUT_MODE_PERCENT)
      return(0);
   return(1);
  }

1.2. MQL4 MarketInfo

La conversión de funciones MQL4 MarketInfotXXXX se ha ejecutado en el archivo [date folder]\MQL5\Include\SimpleCall\MarketInfo.mqh

MQL4 MarketInfo tiene el tipo double, pero en MQL5 hay análogos de MarketInfo tanto en SymbolInfoInteger() (retorna el tipo long), como en SymbolInfoDouble() (retorna el tipo double). Aquí salta a la vista la torpeza en el uso de la función anticuada MarketInfo: conversión de tipos. Tomamos como ejemplo MQL5 SYMBOL_DIGITS:

MarketInfo(Symbol(),MODE_DIGITS) <= (double)SymbolInfoInteger(symbol,SYMBOL_DIGITS)

1.2.1 La ambigüedad con MQL4 MODE_TRADEALLOWED 

En MQL4 se trata de una bandera sencilla bool, mientras que en MQL5 para el símbolo pueden existir varios tipos de permisos/prohibiciones:

ENUM_SYMBOL_TRADE_MODE

Identificador Descripción
 SYMBOL_TRADE_MODE_DISABLED  El comercio con el símbolo está prohibido
 SYMBOL_TRADE_MODE_LONGONLY  Solo se permite la compra
 SYMBOL_TRADE_MODE_SHORTONLY  Solo se permite la venta
 SYMBOL_TRADE_MODE_CLOSEONLY  Solo se permiten las operaciones de cierre de posiciones
 YMBOL_TRADE_MODE_FULL  No hay limitaciones en las operaciones comerciales

Proponemos mostrar false solo en el caso SYMBOL_TRADE_MODE_DISABLED, y true para las limitaciones parciales o para el acceso completo (además, en este caso, mostramos Alert y Print con una advertencia sobre la limitación parcial).

1.2.2. Ambigüedad con MQL4 MODE_SWAPTYPE

En MQL4, MODE_SWAPTYPE retorna solo cuatro valores (método de cálculo de swaps. 0 — en puntos; 1 — en la divisa básica del instrumento; 2 — en tanto por ciento; 3 — en la divisa del margen), mientras que en MQL5, la enumeración ENUM_SYMBOL_SWAP_MODE contiene nueve valores que se cruzan con los valores en MQL4:

MQL4 MODE_SWAPTYPE MQL5 ENUM_SYMBOL_SWAP_MODE
 No hay correspondencia  SYMBOL_SWAP_MODE_DISABLED
 0 - en puntos  SYMBOL_SWAP_MODE_POINTS
 1 - en la divisa básica del instrumento  SYMBOL_SWAP_MODE_CURRENCY_SYMBOL
 3 - en la divisa del margen  SYMBOL_SWAP_MODE_CURRENCY_MARGIN
 No hay correspondencia  SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT
 2 - en tanto por ciento  SYMBOL_SWAP_MODE_INTEREST_CURRENT
 2 - en tanto por ciento  SYMBOL_SWAP_MODE_INTEREST_OPEN
 No hay correspondencia  SYMBOL_SWAP_MODE_REOPEN_CURRENT
 No hay correspondencia  SYMBOL_SWAP_MODE_REOPEN_BID

En MQL5, para el cálculo de los swaps en tanto por ciento, puede haber dos variantes:

/*
SYMBOL_SWAP_MODE_INTEREST_CURRENT
Los swaps se pagan directamente en tanto por ciento anual del precio del instrumento en el momento del cálculo (modo bancario – 360 días al año)
SYMBOL_SWAP_MODE_INTEREST_OPEN
Los swaps se pagan directamente en tanto por ciento anual del precio de apertura de la posición del símbolo (modo bancario – 360 días al año)
*/

Vamos a considerarlos como el mismo tipo. Para el resto de variantes MQL5 que carecen de análogos en MQL4, se mostrará una advertencia y se retornará "no es un número".

1.2.3. La ambigüedad con MQL4 MODE_PROFITCALCMODE y MODE_MARGINCALCMODE

En MQL4 MODE_PROFITCALCMODE (método de cálculo del beneficio) retorna solo tres valores: 0 — Forex; 1 — CFD; 2 — Futures, y MODE_MARGINCALCMODE (método de cálculo del margen) — cuatro: 0 — Forex; 1 — CFD; 2 — Futures; 3 — CFD de índices. En MQL5, para el instrumento se puede definir el método de cálculo del margen (enumeración ENUM_SYMBOL_CALC_MODE), pero no tiene método de cálculo del beneficio. Suponemos que en MQL5 el método de cálculo del margen es igual al método de cálculo del beneficio, por eso para MQL4 MODE_PROFITCALCMODE y MODE_MARGINCALCMODE se retornará un valor idéntico con MQL5 ENUM_SYMBOL_CALC_MODE.

La enumeración MQL5 ENUM_SYMBOL_CALC_MODE contiene diez métodos. Comparamos MQL4 MODE_PROFITCALCMODE con MQL5 ENUM_SYMBOL_CALC_MODE:

MQL4 MODE_PROFITCALCMODE "Forex" <==> MQL5 SYMBOL_CALC_MODE_FOREX

MQL4 MODE_PROFITCALCMODE "CFD" <==> MQL5 SYMBOL_CALC_MODE_CFD

MQL4 MODE_PROFITCALCMODE "Futures" <==> MQL5 SYMBOL_CALC_MODE_FUTURES

Para el resto de variantes MQL5 que carecen de análogos en MQL4, se mostrará una advertencia y se retornará "no es un número".

...
#define MODE_PROFITCALCMODE   1000//SYMBOL_TRADE_CALC_MODE
#define MODE_MARGINCALCMODE   1001//SYMBOL_TRADE_CALC_MODE
...
      case MODE_PROFITCALCMODE:
        {
         ENUM_SYMBOL_CALC_MODE profit_calc_mode=(ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(symbol,SYMBOL_TRADE_CALC_MODE);
         switch(profit_calc_mode)
           {
            case  SYMBOL_CALC_MODE_FOREX:
               return((double)0);
            case  SYMBOL_CALC_MODE_FUTURES:
               return((double)2);
            case  SYMBOL_CALC_MODE_CFD:
               return((double)1);
            default :
              {
               string text="MQL4 MODE_PROFITCALCMODE returned MQL5 "+EnumToString(profit_calc_mode);
               Alert(text);
               Print(text);
               return(double("nan"));
              }
           }
        }
      case MODE_MARGINCALCMODE:
        {
         ENUM_SYMBOL_CALC_MODE profit_calc_mode=(ENUM_SYMBOL_CALC_MODE)SymbolInfoInteger(symbol,SYMBOL_TRADE_CALC_MODE);
         switch(profit_calc_mode)
           {
            case  SYMBOL_CALC_MODE_FOREX:
               return((double)0);
            case  SYMBOL_CALC_MODE_FUTURES:
               return((double)2);
            case  SYMBOL_CALC_MODE_CFD:
               return((double)1);
            default :
              {
               string text="MQL4 MODE_MARGINCALCMODE returned MQL5 "+EnumToString(profit_calc_mode);
               Alert(text);
               Print(text);
               return(double("nan"));
              }
           }
        }

1.3. MQL4 Comprobación del estado

La conversión de funciones MQL4 de comprobación de estado se ha ejecutado en el archivo [date folder]\MQL5\Include\SimpleCall\Check.mqh

Aquí se incluyen elementos como:

  • Digits
  • Point
  • IsConnected
  • IsDemo
  • IsDllsAllowed
  • IsExpertEnabled
  • IsLibrariesAllowed
  • IsOptimization
  • IsTesting
  • IsTradeAllowed
  • IsTradeContextBusy
  • IsVisualMode
  • TerminalCompany
  • TerminalName
  • TerminalPath
MQL4 MQL5 MQL5 classes Nota
 Digits

 MQL4 permite usar simultáneamente Digits y Digits()
 Point

 MQL4 permite usar simultáneamente Point y Point()
 IsConnected  TerminalInfoInteger(TERMINAL_CONNECTED)  CTerminalInfo::IsConnected 
 IsDemo  AccountInfoInteger(ACCOUNT_TRADE_MODE)  CAccountInfo::TradeMode  Se retorna uno de los valores de la enumeración ENUM_ACCOUNT_TRADE_MODE
 IsDllsAllowed  TerminalInfoInteger(TERMINAL_DLLS_ALLOWED) и MQLInfoInteger(MQL_DLLS_ALLOWED)  CTerminalInfo::IsDLLsAllowed   TERMINAL_DLLS_ALLOWED tiene posee el estado superior, mientras que MQL_DLLS_ALLOWED se puede ignorar
 IsExpertEnabled  TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)   CTerminalInfo::IsTradeAllowed  Estado del botón "Autotrading" en el terminal
 IsLibrariesAllowed  MQLInfoInteger(MQL_DLLS_ALLOWED)  -/-  La comprobación no tiene sentido: si el programa usa DLL y usted no le permite utilizarlas (pestaña "Dependencias" del programa), usted sencillamente no podrá iniciar el programa.
 IsOptimization  MQLInfoInteger(MQL_OPTIMIZATION)  -/-  Un programa MQL5 tiene cuatro modos: depuración, perfilado de código, simulación y optimización
 IsTesting  MQLInfoInteger(MQL_TESTER)  -/-  Un programa MQL5 tiene cuatro modos: depuración, perfilado de código, simulación y optimización
 IsTradeAllowed  MQLInfoInteger(MQL_TRADE_ALLOWED)  -/-  Estado de la casilla de verificación "Permitir el comercio automático" en las propiedades del programa
 IsTradeContextBusy  -/-  -/-  Se retornará "false"
 IsVisualMode  MQLInfoInteger(MQL_VISUAL_MODE)
 Un programa MQL5 tiene cuatro modos: depuración, perfilado de código, simulación y optimización
 TerminalCompany  TerminalInfoString(TERMINAL_COMPANY)  CTerminalInfo::Company  
 TerminalName  TerminalInfoString(TERMINAL_NAME)  CTerminalInfo::Name  
 TerminalPath  TerminalInfoString(TERMINAL_PATH)  CTerminalInfo::Path  

Los dos primeros puntos (Digits y Point) no se pueden implementar porque aquí MQL4 y MQL5 están totalmente enredados. Para ser más concretos: en MQL4 se pueden encontrar simultáneamente Digits y Digits() y Point y Point(). Por ejemplo, nosotros personalmente desconocemos cómo podemos unificar Digits y Digits() en Digits() a través de la directiva define. El resto de los puntos se encuentran unívocamente como XXXX() y pueden ser sustituidos por análogos de MQL5.

Aquí tenemos la implementación propiamente dicha:

//+------------------------------------------------------------------+
//|                                                        Check.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "http://wmua.ru/slesar/"
#property version   "1.003" 
//--- checks connection between client terminal and server
#define IsConnected        (bool)TerminalInfoInteger(TERMINAL_CONNECTED)
//--- checks if the Expert Advisor runs on a demo account
#define IsDemo             (bool)(AccountInfoInteger(ACCOUNT_TRADE_MODE)==(ENUM_ACCOUNT_TRADE_MODE)ACCOUNT_TRADE_MODE_DEMO)
//--- checks if the DLL function call is allowed for the Expert Advisor
#define IsDllsAllowed      (bool)TerminalInfoInteger(TERMINAL_DLLS_ALLOWED) 
//--- checks if Expert Advisors are enabled for running
#define IsExpertEnabled    (bool)TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) 
//--- checks if the Expert Advisor can call library function
#define IsLibrariesAllowed (bool)MQLInfoInteger(MQL_DLLS_ALLOWED)
//--- checks if Expert Advisor runs in the Strategy Tester optimization mode
#define IsOptimization     (bool)MQLInfoInteger(MQL_OPTIMIZATION)
//--- checks if the Expert Advisor runs in the testing mode
#define IsTesting                (bool)MQLInfoInteger(MQL_TESTER)
//--- checks if the Expert Advisor is allowed to trade and trading context is not busy 
#define IsTradeAllowed     (bool)MQLInfoInteger(MQL_TRADE_ALLOWED)
//--- returns the information about trade context 
#define IsTradeContextBusy  false
//--- checks if the Expert Advisor is tested in visual mode 
#define IsVisualMode          (bool)MQLInfoInteger(MQL_VISUAL_MODE)
//--- returns the name of company owning the client terminal 
#define TerminalCompany    TerminalInfoString(TERMINAL_COMPANY)
//--- returns client terminal name
#define TerminalName          TerminalInfoString(TERMINAL_NAME)
//--- returns the directory, from which the client terminal was launched
#define TerminalPath          TerminalInfoString(TERMINAL_PATH)
//+------------------------------------------------------------------+

1.4. MQL4 Variables predefinidas

La implementación se encuentra en el archivo [data folder]\MQL5\Include\SimpleCall\Predefined.mqh

MQL4 variables predefinidas:

  • _Digits
  • _Point
  • _LastError
  • _Period
  • _RandomSeed
  • _StopFlag
  • _Symbol
  • _UninitReason
  • Ask
  • Bars
  • Bid
  • Close
  • Digits
  • High
  • Low
  • Open
  • Point
  • Time
  • Volume

Las variables predefinidas _XXXX se convierten en funciones MQL5 con la ayuda de la forma no paramétrica #define:

//--- the _Digits variable stores number of digits after a decimal point,
#define _Digits         Digits()
//--- the _Point variable contains the point size of the current symbol in the quote currency
#define _Point          Point()
//--- the _LastError variable contains code of the last error
#define _LastError      GetLastError()
//--- the _Period variable contains the value of the timeframe of the current chart
#define _Period         Period()
//#define _RandomSeed
//--- the _StopFlag variable contains the flag of the program stop
#define _StopFlag       IsStopped()
//--- the _Symbol variable contains the symbol name of the current chart
#define _Symbol         Symbol()
//--- the _UninitReason variable contains the code of the program uninitialization reason
#define _UninitReason   UninitializeReason()
//#define Bars            Bars(Symbol(),Period());
//#define Digits
//#define Point

La única excepción es para "_RandomSeed", en esta variable se guarda el estado actual del generador de números enteros pseudoaleatorios. Tampoco hay conversión a MQL5 para Bars (o mejor dicho, Bars se deja para la sustitución manual). Para Digits y Point no hay solución. Como ya hemos mencionado anteriormente, en el texto pueden encontrarse simultáneamente tanto Digits y Digits(), como Point y Point().

MQL4 Ask y Bid son sustituidas por las funciones personalizadas (propias) GetAsk() y GetBid():

//--- the latest known seller's price (ask price) for the current symbol
#define Ask             GetAsk()
//--- the latest known buyer's price (offer price, bid price) of the current symbol
#define Bid             GetBid()
...
//--- the latest known seller's price (ask price) for the current symbol                  
double GetAsk()
  {
   MqlTick tick;
   SymbolInfoTick(Symbol(),tick);
   return(tick.ask);
  }
//--- the latest known buyer's price (offer price, bid price) of the current symbol             
double GetBid()
  {
   MqlTick tick;
   SymbolInfoTick(Symbol(),tick);
   return(tick.bid);
  }

Nosotros, por supuesto, podríamos escribir el macro para Ask de forma más sencilla:

#define Ask SymbolInfoDouble(__Symbol,SYMBOL_ASK)

Pero en las observaciones a SymbolInfoDouble se dice:

Si la función se usa para obtener información sobre el último tick, mejor usar SymbolInfoTick(). Es muy posible que para este símbolo aún no haya habido ninguna cotización desde el momento de conexión del terminal a la cuenta de trading. En este caso, el valor que se solicita va a ser indeterminado.

En la mayoría de los casos, será suficiente utilizar la función SymbolInfoTick() que en una llamada permite conseguir los valores Ask, Bid, Last, Volume y el tiempo de llegada del último tick.

Y ahora, algo nuevo: vamos a usar "operator"

La palabra clave operator se usará para la sobrecarga (redesignación) del operador de indexación []. Esto se hará necesario para la conversión de las matrices-series temporales MQL4 (Open[], High[], Low[], Close[], Time[], Volume[]) a la forma MQL5.

Qué es lo que nos dice el manual de referencia sobre "operator":

La sobrecarga de operaciones permite usar una anotación operacional (anotación en forma de expresiones simples) aplicada a los objetos complejos: estructuras y clases.

Partiendo del manual de referencia, podemos determinar que para la sobrecarga del operador de indexación [], necesitaremos crear una clase.

Recordando #define:

La directiva #define se puede utilizar para asignar a las expresiones nombres mnemotécnicos. Hay dos formas:

#define identifier expression                   // forma no paramétrica 
#define identifier(par1,... par8) expression    // forma paramétrica

La directiva #define sustituye con "expression" todas las entradas sucesivas "identifier" encontradas en el texto original.

el código siguiente se puede leer así: la directiva #define sustituye con 159 todas las entradas sucesivas encontradas en el texto fuente.

#define SeriesVolume(Volume,T) 159
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   long a=SeriesVolume(Volume,long);
  }

Es decir, el código en OnStart se transforma en:

   long a=159;

Paso 2

Aquí, dentro de #define hemos ubicado una clase completa

//+------------------------------------------------------------------+
//|                                                      Test_en.mq5 |
//|                                      Copyright 2012, CompanyName |
//|                                       http://www.companyname.net |
//+------------------------------------------------------------------+
#define SeriesVolume(Volume,T) class CVolume       \
  {                                                \
  public:                                          \
    T operator[](const int i) const                \
    {                                              \
    long val[1];                                   \
    if(CopyTickVolume(Symbol(),Period(),i,1,val)==1)\
      return(val[0]);                              \
    else                                           \
      return(-1);                                  \
    }                                              \
  };                                               \
CVolume Volume;
//---
SeriesVolume(Volume,long)
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   Print(Volume[4]);
  }
//+------------------------------------------------------------------+

Esta sustitución se puede imaginar así:

//+------------------------------------------------------------------+
//|                                                      Test_en.mq5 |
//|                                      Copyright 2012, CompanyName |
//|                                       http://www.companyname.net |
//+------------------------------------------------------------------+
class CVolume      
  {                                                
  public:                                          
    long operator[](const int i) const                
    {                                              
    long val[1];                                   
    if(CopyTickVolume(Symbol(),Period(),i,1,val)==1)
      return(val[0]);                              
    else                                           
      return(-1);                                  
    }                                              
  };                                               
CVolume Volume;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   Print(Volume[4]);
  }

Es decir, en OnStart, en esencia, recurrimos al objeto Volume de la clase CVolume, al método [], en el que transmitimos el índice i. Según ese mismo principio, escribimos para las series MQL4 Open, High, Low, Close y Time.

Y al final nos quedan las funciones MQL4 iXXX: iOpen, iHigh, iLow, iClose, iTime e iVolume. Para ellas aplicaremos el método de declaración de la función personalizada.

Ejemplo para iClose:

//--- returns Close price value for the bar of specified symbol with timeframe and shift
double   iClose(
                string                    symbol,              // symbol
                ENUM_TIMEFRAMES           timeframe,           // timeframe
                int                       shift                // shift
                )
  {
   double result=0.0;
//---
   double val[1];
   ResetLastError();
   int copied=CopyClose(symbol,timeframe,shift,1,val);
   if(copied>0)
      result=val[0];
   else
      Print(__FUNCTION__,": CopyClose error=",GetLastError());
//---
   return(result);
  }

2. Cambios en otros archivos

En [data folder]\MQL5\Include\SimpleCall\IndicatorsMQL4.mqh, todos los nombres MQL4 de las líneas ahora se escriben en el encabezado:

double NaN=double("nan");
#define MODE_MAIN          0   
#define MODE_SIGNAL        1
#define MODE_PLUSDI        1
#define MODE_MINUSDI       2  
#define MODE_GATORJAW      1
#define MODE_GATORTEETH    2
#define MODE_GATORLIPS     3 
#define MODE_UPPER         1
#define MODE_LOWER         2  
#define MODE_TENKANSEN     1     
#define MODE_KIJUNSEN      2                                   
#define MODE_SENKOUSPANA   3                                 
#define MODE_SENKOUSPANB   4                                 
#define MODE_CHIKOUSPAN    5   

В [data folder]\MQL5\Include\SimpleCall\Series.mqh убраны 

#define MODE_OPEN    0
//#define MODE_LOW     1
//#define MODE_HIGH    2
#define MODE_CLOSE   3
#define MODE_VOLUME  4
//#define MODE_TIME    5

Ahora están escritos en [data folder]\MQL5\Include\SimpleCall\Header.mqh :

//+------------------------------------------------------------------+
//|                                                       Header.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "http://wmua.ru/slesar/"
//---
#define MODE_LOW     10001
#define MODE_HIGH    10002
#define MODE_TIME    10005

tanto iClose, iHigh, iLow, iOpen, iTime como iVolume ahora están escritos en [data folder]\MQL5\Include\SimpleCall\Predefined.mqh


Conclusión

En el artículo anterior ya vimos cómo escribir las llamadas de los indicadores en el estilo MQL4, y lo que esto provoca. Resulta que, debido a su sencillez de escritura, tenemos que pagar con la ralentización del funcionamiento de los asesores, en los que no existe control de los indicadores creados. En este artículo hemos continuado buscando un modo de simplificar la escritura del código y hemos analizado la macrosustitución #define.

Como resultado, vemos que con la ayuda de los códigos adjuntos al artículo, podemos obligar a funcionar a casi cualquier asesor MQL4 en MetaTrader 5. Solo necesitamos incluir los archivos necesarios, que sobrecargan o añaden nuevas funciones y variables predeterminadas.

Para lograr la compatibilidad completa solo nos faltan las funciones simplificadas de MQL4. Sin embargo, este problema también se puede resolver, si se desea. En resumen, vamos a repetir las ventajas y desventajas de este enfoque:

Desventajas:

  • limitaciones en el procesamiento del error retornado al acceder a los indicadores;
  • reducción de la velocidad de simulación al acceder a más de un indicador simultáneamente;
  • necesidad de indicar correctamente las líneas de los indicadores, dependiendo de la inclusión de "IndicatorsMQL5.mqh" o "IndicatorsMQL4.mqh";
  • imposibilidad de depurar la macrosustitución #define;
  • no hay pista emergente sobre los argumentos de la #define paramétrica;
  • potenciales colisiones de las variables escondidas tras los macros.
Ventajas
  • sencillez de escritura del código, una línea en lugar de varias;
  • claridad y brevedad: cuanto menos código hay, más sencillo resulta comprenderlo;
  • las macrosustituciones se iluminan en el editor en color rojo, por lo que resulta más sencillo ver los identificadores y funciones personalizados;
  • podemos crear nuestros propios sucedáneos de snippets.

Nosotros analizamos los métodos mostrados en el artículo como consejos prácticos, pero nos reafirmamos como seguidores del enfoque MQL5. Es posible que el artículo ayude a los lectores acostrumbrados a escribir en el estilo MQL4 a superar la barrera psicológica que supone dar el salto a MetaTrader 5, que resulta más cómodo en todos los parámetros.

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

Archivos adjuntos |
ForEachSymbol.mq5 (3.74 KB)
ForEachObject.mq5 (2.82 KB)
ForEachOrder.mq5 (2.59 KB)
ForEachDeal.mq5 (1.24 KB)
SimpleCall.zip (25.38 KB)
William Oswaldo Mayorga Urduy
William Oswaldo Mayorga Urduy | 17 ago. 2022 en 04:04

muy buen articulo, ayuda mucho.

Neuroredes profundas (Parte V). Optimización bayesiana de los hiperparámetros de las DNN Neuroredes profundas (Parte V). Optimización bayesiana de los hiperparámetros de las DNN
En el artículo se analizan las posibilidades de la optimización bayesiana de los hiperparámetros de las neuroredes profundas obtenidas con diferentes formas de entrenamiento. Se compara la calidad de la clasificación de las DNN con los hiperparámetros óptimos en diferentes variedades de entrenamiento. Se ha comprobado mediante forward tests la profundidad de la efectividad de los hiperparámetros óptimos de la DNN. Se han definido los posibles campos de mejora de la calidad de la clasificación.
Construcción automática de las líneas de apoyo y resistencia Construcción automática de las líneas de apoyo y resistencia
En el artículo se analiza la construcción automática de las líneas de apoyo y resistencia a través de los máximos y mínimos locales de los gráficos de precio. Para definir estos extremos, usaremos el indicador ZigZag, conocido por todos.
ZUP - zigzag universal con patrones Pesavento. Búsqueda de patrones ZUP - zigzag universal con patrones Pesavento. Búsqueda de patrones
La plataforma de indicador ZUP permite buscar multitud de patrones conocidos, cuyos parámetros ya se han indicado. Pero también podemos ajustar estos parámetros de acuerdo con nuestras exigencias. Asimismo, existe la posibilidad de crear nuevos patrones con la ayuda de la interfaz gráfica ZUP y guardar sus parámetros en un archivo. Después de ello, podremos comprobar rápidamente si se encuentran nuevos patrones en los gráficos.
Patrón de ruptura del canal Patrón de ruptura del canal
Como se sabe, los canales de precios se forman por las tendencias de precios. Una de las señales más fuertes del cambio de la tendencia es la ruptura del canal actual. En este artículo, yo propongo intentar automatizar el proceso de la búsqueda de las señales de este tipo, y ver si es posible formar su propia estrategia a base de eso.