Descargar MetaTrader 5

Qué comprobaciones debe superar un robot comercial antes de ser publicado en el Mercado

28 julio 2016, 10:55
MetaQuotes Software Corp.
0
833

Por qué es necesaria la comprobación antes de publicar un producto en el Mercado

Todos los productos del Mercado deben superar una comprobación preliminar antes de su publicación, ya que un pequeño error en la lógica del asesor o indicador podría causar pérdidas en la cuenta comercial. Precisamente por ello, hemos desarrollado una serie de comprobaciones para garantizar que los productos del Mercado tengan el nivel necesario de calidad.

Si los Moderadores detectan errores durante la comprobación de su producto, será ncecesario que usted los corrija. En este artículo hablaremos de los errores más frecuentes que cometen los desarrolladores en sus robots comerciales e indicadores técnicos.   Asimismo, le recomendamos que lea los artículos siguientes:

Cómo  detectar y corregir con rapidez los errores en un robot comercial

El simulador de estrategias permite, no solo poner a prueba sistemas comerciales basándose en la historia, sino también detectar errores lógicos y algorítmicos cometidos al escribir el robot comercial. Durante la simulación, todos los mensajes sobre operaciones comerciales y errores detectados se muestran en el Registro del simulador. A la hora de analizarlos, estos mensajes se ven con facilidad en el Visor de archivos log, que se llama con el comando del menú contextual.


Abra el Visor después de la simulación del asesor y active el modo "Solo errores", como se muestra en la figura. Si en su robot comercial hay errores, los verá enseguida. Si los errores no han sido detectados a la primera, realice una serie de simulaciones cambiando los instrumentos/marcos temporales/parámetros de entrada y con diferentes valores de depósito inicial. El 99% de los errores se descubrirán con estos sencillos métodos, y será precisamente de ellos de los que hablaremos en este artículo.

Para estudiar detalladamente los errores detectados, use en el MetaEditor la Depuración en la historia, de esta forma, en el modo de simulación visual usted podrá observar no solo los gráficos de precio y los valores de los indicadores utilizados, sino también seguir los valores de cada variable del programa en cada tick. Esto le permitirá depurar su estrategia comercial sin necesidad de gastar semanas en el modo de tiempo real.

Falta de recursos para realizar una operación comercial

Antes de cada envío de una orden comercial, es necesario comprobar que los recursos en su cuenta sean suficientes. La insuficiencia de recursos para una futura posición u orden abierta se considera un error grave.

Tenga en cuenta que incluso para colocar una orden pendiente podría ser necesario proporcionar una fianza para el margen.


Recomendamos simular su robot comercial como prueba con un depósito inicial premeditadamente pequeño, por ejemplo, 1 USD o 1 Euro.

Si la prueba muestra que los recursos para ejecutar la operación comercial son insuficientes, en lugar de la llamada de la función OrderSend() se emitirá en el registro un mensaje de error. Ejemplos de comprobación:

MQL5

bool CheckMoneyForTrade(string symb,double lots,ENUM_ORDER_TYPE type)
  {
//--- obtenemos el precio de apertura
   MqlTick mqltick;
   SymbolInfoTick(symb,mqltick);
   double price=mqltick.ask;
   if(type==ORDER_TYPE_SELL)
      price=mqltick.bid;
//--- valores del margen libre imprescindible
   double margin,free_margin=AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   //--- llamamos a la función de comprobación
   if(!OrderCalcMargin(type,symb,lots,price,margin))
     {
      //--- algo no ha salido bien, informamos y retornamos false
      Print("Error in ",__FUNCTION__," code=",GetLastError());
      return(false);
     }
   //--- si los recursos para ejecutar una operación son insuficientes
   if(margin>free_margin)
     {
      //--- informamos sobre el error y retornamos false
      Print("Not enough money for ",EnumToString(type)," ",lots," ",symb," Error code=",GetLastError());
      return(false);
     }
//--- la comprobación ha tenido éxito
   return(true);
  }

MQL4

bool CheckMoneyForTrade(string symb, double lots,int type)
  {
   double free_margin=AccountFreeMarginCheck(symb,type,lots);
   //-- si no hay dinero suficiente
   if(free_margin<0)
     {
      string oper=(type==OP_BUY)? "Buy":"Sell";
      Print("Not enough money for ", oper," ",lots, " ", symb, " Error code=",GetLastError());
      return(false);
     }
   //-- la comprobación ha tenido éxito
   return(true);
  }

Volúmenes incorrectos en las operaciones comerciales

Antes de enviar órdenes comerciales también es necesario comprobar que los volúmenes indicados en la orden sean correctos. El número de lotes que tiene previsto indicar en la orden del asesor debe ser comprobado antes de llamar a la función OrderSend(). Para los instrumentos financieros en las Especificaciones se indican los volúmenes mínimos y máximos permitidos para el comercio, así como la gradación del volumen. Se pueden obtener estos valores en MQL5 de la enumeración ENUM_SYMBOL_INFO_DOUBLE con la ayuda de la función SymbolInfoDouble()

SYMBOL_VOLUME_MIN

Volumen mínimo para ejecutar una operación

SYMBOL_VOLUME_MAX

Volumen máximo para ejecutar una operación

SYMBOL_VOLUME_STEP

Salto mínimo del cambio de volumen para ejecutar una operación

Ejemplo de una función para la comprobación de volumen correcto

//+------------------------------------------------------------------+
//|  Comprobar si el volumen de una orden es correcto                          |
//+------------------------------------------------------------------+
bool CheckVolumeValue(double volume,string &description)
  {
//--- volumen mínimo permitido para las operaciones comerciales
   double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   if(volume<min_volume)
     {
      description=StringFormat("El volumen es inferior al mínimo permitido SYMBOL_VOLUME_MIN=%.2f",min_volume);
      return(false);
     }

//--- volumen máximo permitido para las operaciones comerciales
   double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
   if(volume>max_volume)
     {
      description=StringFormat("El volumen es superior al máximo permitido SYMBOL_VOLUME_MAX=%.2f",max_volume);
      return(false);
     }

//--- obtenemos la gradación mínima del volumen
   double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);

   int ratio=(int)MathRound(volume/volume_step);
   if(MathAbs(ratio*volume_step-volume)>0.0000001)
     {
      description=StringFormat("El volumen no es múltiplo de la gradación mínima SYMBOL_VOLUME_STEP=%.2f, el volumen correcto más cercano es %.2f",
                               volume_step,ratio*volume_step);
      return(false);
     }
   description="Valor correcto del volumen";
   return(true);
  }


Limitación en el número de órdenes pendientes

Asimismo, existe una limitación en la cantidad de órdenes pendientes que pueden ser colocadas en esta cuenta. Ejemplo de la función IsNewOrderAllowed(), que comprueba si se puede colocar una orden pendiente más. 

//+------------------------------------------------------------------+
//| comprueba si se puede colocar una orden pendiente más                    |
//+------------------------------------------------------------------+
bool IsNewOrderAllowed()
  {
//--- obtenemos el número de órdenes pendientes permitidas en la cuenta
   int max_allowed_orders=(int)AccountInfoInteger(ACCOUNT_LIMIT_ORDERS);

//--- si no existe limitación, retornamos true, es posible enviar la orden
   if(max_allowed_orders==0) return(true);

//--- si hemos llegado hasta este lugar, significa que hay limitaciones, vemos cuántas órdenes hay ya en acción
   int orders=OrdersTotal();

//--- retornamos el resultado de la comparación
   return(orders<max_allowed_orders);
  }

La función es sencilla: en la varibale max_allowed_orders obtenemos la cantidad permitida de órdenes pendientes, y si este valor no es igual a cero, lo comparamos con el número actual de órdenes. Bien es cierto que con esta función no se tiene en cuenta una posible limitación: el volumen conjunto máximo permitido de la posición abierta y de las órdenes abiertas de un símbolo concreto.


Limitación en el número de lotes de un símbolo

Para obtener el tamaño del volumen de una posición abierta del símbolo establecido, es necesario elegir de antemano la posición con la ayuda de la función PositionSelect(). Y solo después de ello se podrá solicitar el volumen de la posición elegida con la ayuda de la función PositionGetDouble(), que retorna las diferentes propiedades de la posición elegida, que tengan el tipo double.  Para obtener el volumen de la posición, escribiremos la función PositionVolume().

//+------------------------------------------------------------------+
//| Retorna el tamaño de la posición del símbolo indicado                  |
//+------------------------------------------------------------------+
double PositionVolume(string symbol)
  {
//--- probamos a elegir la posición del símbolo
   bool selected=PositionSelect(symbol);
//--- la posición existe
   if(selected)
      //--- retornamos el volumen de la posición
      return(PositionGetDouble(POSITION_VOLUME));
   else
     {
      //--- informamos sobre el intento fallido de elegir posición
      Print(__FUNCTION__," No se ha logrado ejecutar PositionSelect() para el símbolo ",
            symbol," Error ",GetLastError());
      return(-1);
     }
  }

Para las cuentas con soporte de cobertura, hay que pasar por todas las posiciones en este instrumento.

Antes de hacer una solicitud comercial para colocar una orden pendiente de un símbolo, es necesario comprobar las limitaciones en el volumen conjunto para posiciones abiertas y órdenes pendientes de un símbolo, con SYMBOL_VOLUME_LIMIT. Si no existen limitaciones, entonces el volumen para la orden pendiente no podrá superar el volumen máximo establecido, que es posible obtener con la ayuda de la función SymbolInfoDouble().

double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT);
if(max_volume==0) volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);

Pero este enfoque no tiene en cuenta el volumen en las órdenes pendientes actuales del símbolo indicado. Vamos a mostrar un ejemplo de una función que calcula este valor:

//+------------------------------------------------------------------+
//| Retorna el tamaño de la posición del símbolo indicado                  |
//+------------------------------------------------------------------+
double PositionVolume(string symbol)
  {
//--- probamos a elegir la posición del símbolo
   bool selected=PositionSelect(symbol);
//--- la posición existe
   if(selected)
      //--- retornamos el volumen de la posición
      return(PositionGetDouble(POSITION_VOLUME));
   else
     {
      //--- retornamos cero en caso de que no haya posición
      return(0);
     }
  }

Teniendo en cuenta el volumen de la posición abierta y el volumen de las órdenes abiertas, la comprobación definitiva tendrá la forma siguiente:

//+------------------------------------------------------------------+
//|  Retorna el volumen máximo permitido para una orden del símbolo   |
//+------------------------------------------------------------------+
double NewOrderAllowedVolume(string symbol)
  {
   double allowed_volume=0;
//--- obtenemos la limitación del volumen máximo en la orden
   double symbol_max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
//--- obtenemos la limitación del símbolo en cuanto a volumen
   double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT);

//--- Obtenemos el volumen de la posición abierta del símbolo
   double opened_volume=PositionVolume(symbol);
   if(opened_volume>=0)
     {
      //--- si ya hemos agotado el volumen
      if(max_volume-opened_volume<=0)
         return(0);

      //--- el volumen de la posición abierta no supera max_volume
      double orders_volume_on_symbol=PendingsVolume(symbol);
      allowed_volume=max_volume-opened_volume-orders_volume_on_symbol;
      if(allowed_volume>symbol_max_volume) allowed_volume=symbol_max_volume;
     }
   return(allowed_volume);
  }


Establecer los niveles de TakeProfit y StopLoss dentro de los límites del nivel mínimo SYMBOL_TRADE_STOPS_LEVEL

Muchos asesores comercian usando órdenes TakeProfit y StopLoss, cuyos niveles se calculan de forma dinámica en el momento en que se realiza la compra o la venta. Una orden TakeProfit sirve para cerrar una posición cuando el precio se mueve en una dirección rentable, mientras que una orden StopLoss se usa para limitar las pérdidas cuando el precio se mueve en una dirección no rentable.

Por eso los niveles de TakeProfit y StopLoss se deben comparar con el precio actual, conforme al cual se puede ejecutar una operación en la dirección opuesta:

  • La compra se realiza al precio Ask: los niveles de TakeProfit y StopLoss se deben comparar con el precio actual de venta Bid.
  • La venta se realiza al precio Bid: los niveles de TakeProfit y StopLoss se deben comparar con el precio actual de compra Ask.
La compra tiene lugar al precio Ask
La venta tiene lugar al precio Bid
TakeProfit >= Bid
StopLoss <= Bid
TakeProfit <= Ask
StopLoss >= Ask



Para los instrumentos financieros, en los ajustes del símbolo se puede establecer el parámetro SYMBOL_TRADE_STOPS_LEVEL. Este indica en puntos la distancia mínima de los niveles de StopLoss y TakeProfit  con respecto al precio de cierre de la posición abierta. Si el valor de esta propiedad es igual a cero, significa que no se establecido distancia mínima para las órdenes SL/TP para compras y ventas.

En general, la comprobación de los niveles de TakeProfit y StopLoss teniendo en cuenta la distancia SYMBOL_TRADE_STOPS_LEVEL tiene el aspecto que sigue:

  • La compra se realiza al precio Ask: los niveles de TakeProfit y StopLoss deberán estar del precio actual de venta Bid a una distancia no inferior a SYMBOL_TRADE_STOPS_LEVEL puntos.
  • La venta se realiza al precio Bid: los niveles de TakeProfit y StopLoss deberán estar del precio actual de compra Ask a una distancia no inferior a SYMBOL_TRADE_STOPS_LEVEL puntos.
La compra tiene lugar al precio Ask
La venta tiene lugar al precio Bid
TakeProfit - Bid >= SYMBOL_TRADE_STOPS_LEVEL
Bid - StopLoss >= SYMBOL_TRADE_STOPS_LEVEL
Ask - TakeProfit >= SYMBOL_TRADE_STOPS_LEVEL
StopLoss - Ask >= SYMBOL_TRADE_STOPS_LEVEL

Por eso se puede crear la función de comprobación CheckStopLoss_Takeprofit(), que exige que la distancia desde el TakeProfit y el StopLoss hasta el precio de cierre no sea inferior a  SYMBOL_TRADE_STOPS_LEVEL puntos:

bool CheckStopLoss_Takeprofit(ENUM_ORDER_TYPE type,double SL,double TP)
  {
//--- obtenemos el nivel SYMBOL_TRADE_STOPS_LEVEL
   int stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
   if(stops_level!=0)
     {
      PrintFormat("SYMBOL_TRADE_STOPS_LEVEL=%d: el StopLoss y el TakeProfit deberán estar"+
                  " no más cerca de %d puntos del precio de cierre",stops_level,stops_level);
     }
//---
   bool SL_check=false,TP_check=false;
//--- comprobamos solo dos tipos de órdenes
   switch(type)
     {
      //--- operación de compra
      case  ORDER_TYPE_BUY:
        {
         //--- comprobamos el StopLoss
         SL_check=(Bid-SL>stops_level*_Point);
         if(!SL_check)
            PrintFormat("For order %s StopLoss=%.5f must be less than %.5f"+
                        " (Bid=%.5f - SYMBOL_TRADE_STOPS_LEVEL=%d puntos)",
                        EnumToString(type),SL,Bid-stops_level*_Point,Bid,stops_level);
         //--- comprobamos el TakeProfit
         TP_check=(TP-Bid>stops_level*_Point);
         if(!TP_check)
            PrintFormat("For order %s TakeProfit=%.5f must be greater than %.5f"+
                        " (Bid=%.5f + SYMBOL_TRADE_STOPS_LEVEL=%d puntos)",
                        EnumToString(type),TP,Bid+stops_level*_Point,Bid,stops_level);
         //--- retornamos el resultado de la comprobación
         return(SL_check&&TP_check);
        }
      //--- operación de venta
      case  ORDER_TYPE_SELL:
        {
         //--- comprobamos el StopLoss
         SL_check=(SL-Ask>stops_level*_Point);
         if(!SL_check)
            PrintFormat("For order %s StopLoss=%.5f must be greater than %.5f "+
                        " (Ask=%.5f + SYMBOL_TRADE_STOPS_LEVEL=%d puntos)",
                        EnumToString(type),SL,Ask+stops_level*_Point,Ask,stops_level);
         //--- comprobamos el TakeProfit
         TP_check=(Ask-TP>stops_level*_Point);
         if(!TP_check)
            PrintFormat("For order %s TakeProfit=%.5f must be less than %.5f "+
                        " (Ask=%.5f - SYMBOL_TRADE_STOPS_LEVEL=%d puntos)",
                        EnumToString(type),TP,Ask-stops_level*_Point,Ask,stops_level);
         //--- retornamos el resultado de la comprobación
         return(TP_check&&SL_check);
        }
      break;
     }
//--- para las órdenes pendientes se necesita una función un tanto distinta
   return false;
  }

La comprobación en sí tiene este aspecto:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- obtenemos de forma aleatoria el tipo de operación
   int oper=(int)(GetTickCount()%2); // el resto de la división entre dos es o bien 0 o bien 1
   switch(oper)
     {
      //--- compramos
      case  0:
        {
         //--- obtenemos el precio de apertura y establecemos TP/SL incorrectos de antemano
         double price=Ask;
         double SL=NormalizeDouble(Bid+2*_Point,_Digits);
         double TP=NormalizeDouble(Bid-2*_Point,_Digits);
         //--- realizamos comprobación
         PrintFormat("Buy at %.5f   SL=%.5f   TP=%.5f  Bid=%.5f",price,SL,TP,Bid);
         if(!CheckStopLoss_Takeprofit(ORDER_TYPE_BUY,SL,TP))
            Print("¡El nivel de StopLoss o TakeProfit ha sido indicado de forma incorrecta!");
         //--- de todas formas intentamos comprar, para ver el resultado de la ejecución
         Buy(price,SL,TP);
        }
      break;
      //--- vendemos
      case  1:
        {
         //--- obtenemos el precio de apertura y establecemos TP/SL incorrectos de antemano
         double price=Bid;
         double SL=NormalizeDouble(Ask-2*_Point,_Digits);
         double TP=NormalizeDouble(Ask+2*_Point,_Digits);
         //--- realizamos comprobación
         PrintFormat("Sell at %.5f   SL=%.5f   TP=%.5f  Ask=%.5f",price,SL,TP,Ask);
         if(!CheckStopLoss_Takeprofit(ORDER_TYPE_SELL,SL,TP))
            Print("¡El nivel de StopLoss o TakeProfit ha sido indicado de forma incorrecta!");
         //--- de todas formas intentamos vender, para ver el resultado de la ejecución
         Sell(price,SL,TP);
        }
      break;
      //---
     }
  }

Encontrará un ejemplo de la función en los scripts adjuntos Check_TP_and_SL.mq4 y Check_TP_and_SL.mq5. Ejemplo de ejecución:

MQL5
Check_TP_and_SL (EURUSD,H1) Buy at 1.11433   SL=1.11425   TP=1.11421  Bid=1.11423
Check_TP_and_SL (EURUSD,H1) SYMBOL_TRADE_STOPS_LEVEL=30: El StopLoss y el TakeProfit deberán estar no más cerca de 30 puntos del precio de cierre
Check_TP_and_SL (EURUSD,H1) Para la orden ORDER_TYPE_BUY StopLoss=1.11425 debe ser inferior a 1.11393 (Bid=1.11423 - SYMBOL_TRADE_STOPS_LEVEL=30 puntos)
Check_TP_and_SL (EURUSD,H1) Para la orden ORDER_TYPE_BUY TakeProfit=1.11421 debe ser superior a 1.11453 (Bid=1.11423 + SYMBOL_TRADE_STOPS_LEVEL=30 puntos)
Check_TP_and_SL (EURUSD,H1) ¡El nivel de StopLoss o TakeProfit se ha indicado de forma incorrecta!
Check_TP_and_SL (EURUSD,H1) OrderSend error 4756
Check_TP_and_SL (EURUSD,H1) retcode=10016  deal=0  order=0
MQL4
Check_TP_and_SL EURUSD,H1:  Sell at 1.11430   SL=1.11445   TP=1.11449  Ask=1.11447
Check_TP_and_SL EURUSD,H1:  SYMBOL_TRADE_STOPS_LEVEL=1: El StopLoss y el TakeProfit deberán estar no más cerca de 1 punto del precio de cierre
Check_TP_and_SL EURUSD,H1:  Para la orden ORDER_TYPE_SELL StopLoss=1.11445 debe ser superior a 1.11448  (Ask=1.11447 + SYMBOL_TRADE_STOPS_LEVEL=1 puntos)
Check_TP_and_SL EURUSD,H1:  Para la orden ORDER_TYPE_SELL TakeProfit=1.11449 debe ser menor a 1.11446  (Ask=1.11447 - SYMBOL_TRADE_STOPS_LEVEL=1 puntos)
Check_TP_and_SL EURUSD,H1:  ¡El nivel de StopLoss o TakeProfit se ha indicado de forma incorrecta!
Check_TP_and_SL EURUSD,H1:  OrderSend error 130 

Para modelar la situación con valores de TakeProfit y StopLoss incorrectos se han adjuntado al artículo los asesores Test_Wrong_TakeProfit_LEVEL.mq5 y Test_Wrong_StopLoss_LEVEL.mq5. Solo se los puede iniciar en la cuenta demo. Estudie estos ejemplos para comprobar por sí mismo en qué condiciones es posible realizar compras con éxito.

Ejemplo de ejecución del asesor Test_Wrong_StopLoss_LEVEL.mq5:

Test_Wrong_StopLoss_LEVEL.mq5
Point=0.00001 Digits=5
SYMBOL_TRADE_EXECUTION=SYMBOL_TRADE_EXECUTION_INSTANT
SYMBOL_TRADE_FREEZE_LEVEL=20: está prohibido modificar la orden o posición si quedan 20 puntos hasta el precio de activación
SYMBOL_TRADE_STOPS_LEVEL=30: El StopLoss y el TakeProfit deberán estar no más cerca de 30 puntos del precio de cierre
1. Buy 1.0 EURUSD at 1.11442 SL=1.11404 Bid=1.11430 ( StopLoss-Bid=-26 points ))
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11442 sl: 1.11404 [invalid stops]
2. Buy 1.0 EURUSD at 1.11442 SL=1.11404 Bid=1.11431 ( StopLoss-Bid=-27 points ))
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11442 sl: 1.11404 [invalid stops]
3. Buy 1.0 EURUSD at 1.11442 SL=1.11402 Bid=1.11430 ( StopLoss-Bid=-28 points ))
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11442 sl: 1.11402 [invalid stops]
4. Buy 1.0 EURUSD at 1.11440 SL=1.11399 Bid=1.11428 ( StopLoss-Bid=-29 points ))
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11440 sl: 1.11399 [invalid stops]
5. Buy 1.0 EURUSD at 1.11439 SL=1.11398 Bid=1.11428 ( StopLoss-Bid=-30 points ))
Buy 1.0 EURUSD done at 1.11439 with StopLoss=41 points (spread=12 + SYMBOL_TRADE_STOPS_LEVEL=30)

Ejemplo de ejecución del asesor Test_Wrong_TakeProfit_LEVEL.mq5:

Test_Wrong_TakeProfit_LEVEL.mq5
Point=0.00001 Digits=5
SYMBOL_TRADE_EXECUTION=SYMBOL_TRADE_EXECUTION_INSTANT
SYMBOL_TRADE_FREEZE_LEVEL=20: está prohibido modificar la orden o posición si quedan 20 puntos hasta el precio de activación
SYMBOL_TRADE_STOPS_LEVEL=30: El StopLoss y el TakeProfit deberán estar no más cerca de 30 puntos del precio de cierre
1. Buy 1.0 EURUSD at 1.11461 TP=1.11478 Bid=1.11452 (TakeProfit-Bid=26 points)
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11461 tp: 1.11478 [invalid stops]
2. Buy 1.0 EURUSD at 1.11461 TP=1.11479 Bid=1.11452 (TakeProfit-Bid=27 points)
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11461 tp: 1.11479 [invalid stops]
3. Buy 1.0 EURUSD at 1.11461 TP=1.11480 Bid=1.11452 (TakeProfit-Bid=28 points)
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11461 tp: 1.11480 [invalid stops]
4. Buy 1.0 EURUSD at 1.11461 TP=1.11481 Bid=1.11452 (TakeProfit-Bid=29 points)
CTrade::OrderSend: instant buy 1.00 EURUSD at 1.11461 tp: 1.11481 [invalid stops]
5. Buy 1.0 EURUSD at 1.11462 TP=1.11482 Bid=1.11452 (TakeProfit-Bid=30 points)
Buy 1.0 EURUSD done at 1.11462 with TakeProfit=20 points (SYMBOL_TRADE_STOPS_LEVEL=30 - spread=10)

La comprobación de los niveles de StopLoss y TakeProfit en las órdenes pendientes es bastante más simple, estos niveles deberán colocarse de forma separada con respecto al precio de apertura de la orden. Es decir, la comprobación de los niveles teniendo en cuenta la distancia mínima SYMBOL_TRADE_STOPS_LEVEL tiene el aspecto siguiente: los niveles de TakeProfit y StopLoss deberán encontrarse del precio de activación de la orden a una distancia no inferior a SYMBOL_TRADE_STOPS_LEVEL puntos.

BuyLimit y BuyStop
SellLimit y SellStop
TakeProfit - Open >= SYMBOL_TRADE_STOPS_LEVEL
Open - StopLoss >= SYMBOL_TRADE_STOPS_LEVEL
Open - TakeProfit >= SYMBOL_TRADE_STOPS_LEVEL
StopLoss - Open >= SYMBOL_TRADE_STOPS_LEVEL

En el asesor Test_StopLoss_Level_in_PendingOrders.mq5 se hace una serie de intentos de colocar las órdenes BuyStop y BuyLimt  hasta que la operación no se realice con éxito. Con cada intento, el nivel de StopLoss o TakeProfit se desplaza en 1 punto en la dirección correcta. Ejemplo de ejecución de este asesor:

Test_StopLoss_Level_in_PendingOrders.mq5
SYMBOL_TRADE_EXECUTION=SYMBOL_TRADE_EXECUTION_INSTANT
SYMBOL_TRADE_FREEZE_LEVEL=20: está prohibido modificar la orden o posición si quedan 20 puntos hasta el precio de activación
SYMBOL_TRADE_STOPS_LEVEL=30: El StopLoss y el TakeProfit deberán estar no más cerca de 30 puntos del precio de cierre
1. BuyStop 1.0 EURUSD at 1.11019 SL=1.10993 (Open-StopLoss=26 points)
CTrade::OrderSend: buy stop 1.00 EURUSD at 1.11019 sl: 1.10993 [invalid stops]
2. BuyStop 1.0 EURUSD at 1.11019 SL=1.10992 (Open-StopLoss=27 points)
CTrade::OrderSend: buy stop 1.00 EURUSD at 1.11019 sl: 1.10992 [invalid stops]
3. BuyStop 1.0 EURUSD at 1.11020 SL=1.10992 (Open-StopLoss=28 points)
CTrade::OrderSend: buy stop 1.00 EURUSD at 1.11020 sl: 1.10992 [invalid stops]
4. BuyStop 1.0 EURUSD at 1.11021 SL=1.10992 (Open-StopLoss=29 points)
CTrade::OrderSend: buy stop 1.00 EURUSD at 1.11021 sl: 1.10992 [invalid stops]
5. BuyStop 1.0 EURUSD at 1.11021 SL=1.10991 (Open-StopLoss=30 points)
BuyStop 1.0 EURUSD done at 1.11021 with StopLoss=1.10991 (SYMBOL_TRADE_STOPS_LEVEL=30)
 --------- 
1. BuyLimit 1.0 EURUSD at 1.10621 TP=1.10647 (TakeProfit-Open=26 points)
CTrade::OrderSend: buy limit 1.00 EURUSD at 1.10621 tp: 1.10647 [invalid stops]
2. BuyLimit 1.0 EURUSD at 1.10621 TP=1.10648 (TakeProfit-Open=27 points)
CTrade::OrderSend: buy limit 1.00 EURUSD at 1.10621 tp: 1.10648 [invalid stops]
3. BuyLimit 1.0 EURUSD at 1.10621 TP=1.10649 (TakeProfit-Open=28 points)
CTrade::OrderSend: buy limit 1.00 EURUSD at 1.10621 tp: 1.10649 [invalid stops]
4. BuyLimit 1.0 EURUSD at 1.10619 TP=1.10648 (TakeProfit-Open=29 points)
CTrade::OrderSend: buy limit 1.00 EURUSD at 1.10619 tp: 1.10648 [invalid stops]
5. BuyLimit 1.0 EURUSD at 1.10619 TP=1.10649 (TakeProfit-Open=30 points)
BuyLimit 1.0 EURUSD done at 1.10619 with TakeProfit=1.10649 (SYMBOL_TRADE_STOPS_LEVEL=30)

Podrá encontrar ejemplos de comprobación de los niveles de TakeProfit y StopLoss en órdenes pendientes en los códigos fuente adjuntos: Check_TP_and_SL.mq4 y Check_TP_and_SL.mq5.


Intento de modificación de una orden o posición en los límites del nivel de congelamiento SYMBOL_TRADE_FREEZE_LEVEL

En los ajustes del símbolo se puede establecer el parámetro SYMBOL_TRADE_FREEZE_LEVEL, que indica en puntos la distancia de congelamiento de las operaciones comerciales para las órdenes pendientes y las posiciones abiertas. Por ejemplo, si una operación de un instrumento financiero es redirigida para el procesamiento a un sistema comercial externo, entonces la orden pendiente BuyLimit puede encontrarse en este momento demasiado cerca del precio Ask actual. Y si nosotros enviamos una orden de modificación de esta orden en el momento en el que el precio de apertura está los suficientemente cerca del precio Ask, entonces puede darse el caso de que la orden ya habrá sido ejecutada y la modificación resultará imposible.

Por eso, para las órdenes pendientes y las posisicones abiertas es posible indicar en los ajustes la distancia de congelamiento, en cuyos límites no se podrán modificar. En general, antes de intentar enviar la orden de modificación, es necesario realizar una comprobación teniendo en cuenta  SYMBOL_TRADE_FREEZE_LEVEL:

Tipo  de orden/posición
Activación al precio
Comprobación
Orden Buy Limit
 Ask
Ask-OpenPrice >= SYMBOL_TRADE_FREEZE_LEVEL
Buy Stop ордер AskOpenPrice-Ask >= SYMBOL_TRADE_FREEZE_LEVEL
Sell Limit ордер BidOpenPrice-Bid >= SYMBOL_TRADE_FREEZE_LEVEL
Sell Stop ордер BidBid-OpenPrice >= SYMBOL_TRADE_FREEZE_LEVEL
Posición Buy
 BidTakeProfit-Bid >= SYMBOL_TRADE_FREEZE_LEVEL
Bid-StopLoss >= SYMBOL_TRADE_FREEZE_LEVEL
Posición Sell
 AskAsk-TakeProfit >= SYMBOL_TRADE_FREEZE_LEVEL
StopLoss-Ask >= SYMBOL_TRADE_FREEZE_LEVEL

Podrá encontrar ejemplos completos de las funciones de comprobación de las órdenes a nivel de SYMBOL_TRADE_FREEZE_LEVEL en los scripts adjuntos Check_FreezeLevel.mq5 y Check_FreezeLevel.mq4.

//--- comprobamos el tipo de orden
   switch(type)
     {
      //--- orden pendiente BuyLimit
      case  ORDER_TYPE_BUY_LIMIT:
        {
         //--- comprobamos la distancia desde el precio de apertura hasta el precio de activación
         check=((Ask-price)>freeze_level*_Point);
         if(!check)
            PrintFormat("La orden %s #%d no puede ser modificada: Ask-Open=%d puntos < SYMBOL_TRADE_FREEZE_LEVEL=%d puntos",
                        EnumToString(type),ticket,(int)((Ask-price)/_Point),freeze_level);
         return(check);
        }
      //--- orden pendiente BuyLimit
      case  ORDER_TYPE_SELL_LIMIT:
        {
         //--- comprobamos la distancia desde el precio de apertura hasta el precio de activación
         check=((price-Bid)>freeze_level*_Point);
         if(!check)
            PrintFormat("La orden %s #%d no puede ser modificada: Open-Bid=%d puntos < SYMBOL_TRADE_FREEZE_LEVEL=%d puntos",
                        EnumToString(type),ticket,(int)((price-Bid)/_Point),freeze_level);
         return(check);
        }
      break;
      //--- orden pendiente BuyStop
      case  ORDER_TYPE_BUY_STOP:
        {
         //--- comprobamos la distancia desde el precio de apertura hasta el precio de activación
         check=((price-Ask)>freeze_level*_Point);
         if(!check)
            PrintFormat("La orden %s #%d no puede ser modificada: Ask-Open=%d puntos < SYMBOL_TRADE_FREEZE_LEVEL=%d puntos",
                        EnumToString(type),ticket,(int)((price-Ask)/_Point),freeze_level);
         return(check);
        }
      //--- orden pendiente SellStop
      case  ORDER_TYPE_SELL_STOP:
        {
         //--- comprobamos la distancia desde el precio de apertura hasta el precio de activación
         check=((Bid-price)>freeze_level*_Point);
         if(!check)
            PrintFormat("La orden %s #%d no puede ser modificada: Bid-Open=%d puntos < SYMBOL_TRADE_FREEZE_LEVEL=%d puntos",
                        EnumToString(type),ticket,(int)((Bid-price)/_Point),freeze_level);
         return(check);
        }
      break;
     }

Usted puede modelar de forma autónoma la situación en la que tiene lugar el intento de modificación de la orden pendiente dentro del nivel de congelamiento. Para ello, abra una cuenta demo en la que haya instrumentos financieros con un nivel cero de SYMBOL_TRADE_FREEZE_LEVEL, inicie en el gráfico el asesor Test_SYMBOL_TRADE_FREEZE_LEVEL.mq5 (Test_SYMBOL_TRADE_FREEZE_LEVEL.mq4) y coloque manualmente cualquier orden pendiente. El asesor acercará al máximo al mercado la orden y comenzará a realizar intentos prohibidos de modificación. Además, se reproducirá un sonido con la ayuda de la función PlaySound().


Errores que surgen al trabajar con los símbolos con una historia de cotizaciones insuficiente

Si el asesor o indicador se inicia en el gráfico con una historia insuficiente, surgen dos casos posibles:

  1. el programa realiza una comprobación de la existencia de la historia necesaria en toda la profundidad exigida.  Si el número de barras alcanzadas es menor al necesario, entonces el programa solicita los datos que faltan y finaliza su trabajo hasta que llegue el siguiente tick. Este método es el más correcto y ayuda a evitar multitud de errores, como por ejemplo la salida fuera de los límites de la matriz o la división entre cero;
  2. el programa no realiza ninguna comprobación y comienza directamente su trabajo, como si toda la historia necesaria de todos los instrumentos y marcos temporales imprescindibles hubiera estado disponible en el primer requerimiento. Este enfoque está colmado de errores impredecibles.

Usted puede intentar modelar esa situación por su propia cuenta. Para ello, inicie el indicador o asesor testado en el gráfico, después cierre el terminal y elimine toda la historia y de nuevo inicie el terminal. Si después de reiniciar no han aparecido errores en el Registro, comience a cambiar los símbolos y marcos temporales en los gráficos en los que está iniciado su programa. Muchos indicadores dan errores al iniciarse en los marcos temporales de semanas y meses, para los que como norma general la cantidad de barras suele estar limitada. Asimismo, al cambiar bruscamente el símbolo del gráfico,  por ejemplo, de EURUSD a CADJPY, el indicador o asesor iniciado en el gráfico puede incurrir en un error que se relaciona con la ausencia de la historia necesaria para sus cálculos.


Salida de los límites de la matriz (array out of range)

Al trabajar con las matrices, el acceso a sus elementos se realiza según el número del índice, que no puede ser negativo y debe ser menor que el tamaño de la matriz. Es posible obtener el tamaño de la matriz con la ayuda de la función ArraySize().

Este error puede surgir al trabajar con una matriz dinámica, cuando su tamaño no ha sido distribuido claramente por la función  ArrayResize(), o bien por usar esta matriz en funciones que establecen por sí solas el tamaño de las matrices dinámicas que se les han transmitido. Por ejemplo, la función CopyTicks() intenta grabar en la matriz la cantidad solicitada de ticks, pero si hay menos ticks de los solicitados, el tamaño de la matriz recibida será menor del esperado.

Otro método bastante probable de obtener este error es tratar de recurrir a los datos del búfer de indicador cuando su tamaño aún no ha sido inicializado. Recordemos que los búferes de indicador son matrices dinámicas y sus dimensiones son establecidas por el sistema ejecutivo del terminal solo después de la inicialización del gráfico. Por eso,  por ejemplo, el intento de recurrir a los datos de este búfer en la función OnInit() provocará el error "array out of range".


Un ejemplo sencillo de un indicador que da este error se adjunta en el archivo Test_Out_of_range.mq5.


División entre cero (zero divide)

Otro error crítico es el intento de división entre cero. En este caso, la ejecución del programa se detiene de inmediato, el simulador muestra en el Registro el nombre de la función y el número de línea en el código fuente en la que ha sucedido el error.


Normalmente, la división entre cero tiene lugar como consecuencia de una situación imprevista por los programadores, por ejemplo, cuando se obtiene una cierta propiedad o bien por la división del valor entre datos "malos".

Podrá reproducir con facilidad la división entre cero con la ayuda del sencillo asesor TestZeroDivide.mq5, cuyo código fuente se muestra en la captura de pantalla. Otro error crítico es el uso de un puntero a objeto incorrecto. La Depuración en la historia le ayudará a aclarar los motivos de este error.


Envío de una solicitud de modificación de niveles sin cambiarlos de hecho

Si, según las normas del sistema comercial, se deben modificar las órdenes pendientes o posiciones abiertas, antes de enviar la solicitud comercial para que se realice la transacción hay que asegurarse de que la operación solicitada en realidad va a cambiar los parámetros de la orden o posición. El envío de una solicitud comercial que en realidad no introduce cambios se considera un error. Como respuesta a semejante acción, el servidor comercial retornará el código de respuesta TRADE_RETCODE_NO_CHANGES=10025 (MQL5) o el código ERR_NO_RESULT=1 (MQL4)

Un ejemplo de comprobación para MQL5 se muestra en el script Check_OrderLevels.mq5:

//--- clase para realizar operaciones comerciales
#include <Trade\Trade.mqh>
CTrade trade;
#include <Trade\Trade.mqh>
//--- clase para trabajar con órdenes
#include <Trade\OrderInfo.mqh>
COrderInfo orderinfo;
//--- clase para trabajar con posiciones
#include <Trade\PositionInfo.mqh>
CPositionInfo positioninfo;
//+------------------------------------------------------------------+
//| comprobación de los nuevos valores de los niveles antes de la modificación de la orden        |
//+------------------------------------------------------------------+
bool OrderModifyCheck(ulong ticket,double price,double sl,double tp)
  {
//--- elegimos la orden según el ticket
   if(orderinfo.Select(ticket))
     {
      //--- tamaño del punto y nombre del símbolo del que se coloca la orden pendiente
      string symbol=orderinfo.Symbol();
      double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
      int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
      //--- comprobamos si hay cambios en el precio de apertura
      bool PriceOpenChanged=(MathAbs(orderinfo.PriceOpen()-price)>point);
      //--- comprobamos si hay cambios en el nivel de StopLoss
      bool StopLossChanged=(MathAbs(orderinfo.StopLoss()-sl)>point);
      //--- comprobamos si hay cambios en el nivel de Takeprofit
      bool TakeProfitChanged=(MathAbs(orderinfo.TakeProfit()-sl)>tp);
      //--- comprobamos si hay cambios en los niveles
      if(PriceOpenChanged || StopLossChanged || TakeProfitChanged)
         return(true);  // la orden puede ser modificada      
      //--- no hay cambios en los niveles de apertura, StopLoss y Takeprofit
      else
      //--- comunicamos el error
         PrintFormat("La orden #%d ya tiene los niveles Open=%.5f SL=.5f TP=%.5f",
                     ticket,orderinfo.StopLoss(),orderinfo.TakeProfit());
     }
//--- hemos llegado al final, no hay cambios para la orden
   return(false);       // no tiene sentido modificarla 
  }
//+------------------------------------------------------------------+
//| comprobación de los nuevos valores de los niveles antes de la modificación de la orden        |
//+------------------------------------------------------------------+
bool PositionModifyCheck(ulong ticket,double sl,double tp)
  {
//--- elegimos la orden según el ticket
   if(positioninfo.SelectByTicket(ticket))
     {
      //--- tamaño del punto y nombre del símbolo del que se coloca la orden pendiente
      string symbol=positioninfo.Symbol();
      double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
      //--- comprobamos si hay cambios en el nivel de StopLoss
      bool StopLossChanged=(MathAbs(positioninfo.StopLoss()-sl)>point);
      //--- comprobamos si hay cambios en el nivel de Takeprofit
      bool TakeProfitChanged=(MathAbs(positioninfo.TakeProfit()-sl)>tp);
      //--- comprobamos si hay cambios en los niveles
      if(StopLossChanged || TakeProfitChanged)
         return(true);  // la posición puede ser modificada      
      //--- no hay cambios en los niveles de StopLoss y Takeprofit
      else
      //--- comunicamos el error
         PrintFormat("La orden #%d ya tiene los niveles Open=%.5f SL=.5f TP=%.5f",
                     ticket,orderinfo.StopLoss(),orderinfo.TakeProfit());
     }
//--- hemos llegado al final, no hay cambios para la orden
   return(false);       // no tiene sentido modificarla 
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- niveles de precio para las órdenes y posiciones
   double priceopen,stoploss,takeprofit;
//--- ticket de la orden o posición actual
   ulong orderticket,positionticket;
/*
   ... obtenemos el ticket de la orden y los nuevos niveles de StopLoss/Takeprofit/PriceOpen
*/
//--- comprobamos los niveles antes de modificar la orden pendiente   
   if(OrderModifyCheck(orderticket,priceopen,stoploss,takeprofit))
     {
      //--- la comprobación ha tenido éxito
      trade.OrderModify(orderticket,priceopen,stoploss,takeprofit,
                        orderinfo.TypeTime(),orderinfo.TimeExpiration());
     }
/*
   ... obtenemos el ticket de la posición y los nuevos niveles de StopLoss/Takeprofit
*/
//--- comprobamos los niveles antes de modificar la posición
   if(PositionModifyCheck(positionticket,stoploss,takeprofit))
     {
      //--- la comprobación ha tenido éxito
      trade.PositionModify(positionticket,stoploss,takeprofit);
     }
//---
  }

Encontrará un ejemplo de comprobación en el lenguaje MQL4 en el script Check_OrderLevels.mq4:

#property strict
//+------------------------------------------------------------------+
//| comprobación de los nuevos valores de los niveles antes de la modificación de la orden        |
//+------------------------------------------------------------------+
bool OrderModifyCheck(int ticket,double price,double sl,double tp)
  {
//--- elegimos la orden según el ticket
   if(OrderSelect(ticket,SELECT_BY_TICKET))
     {
      //--- tamaño del punto y nombre del símbolo del que se coloca la orden pendiente
      string symbol=OrderSymbol();
      double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
      //--- comprobamos si hay cambios en el precio de apertura
      bool PriceOpenChanged=true;
      int type=OrderType();
      if(!(type==OP_BUY || type==OP_SELL))
        {
         PriceOpenChanged=(MathAbs(OrderOpenPrice()-price)>point);
        }
      //--- comprobamos si hay cambios en el nivel de StopLoss
      bool StopLossChanged=(MathAbs(OrderStopLoss()-sl)>point);
      //--- comprobamos si hay cambios en el nivel de Takeprofit
      bool TakeProfitChanged=(MathAbs(OrderTakeProfit()-sl)>tp);
      //--- comprobamos si hay cambios en los niveles
      if(PriceOpenChanged || StopLossChanged || TakeProfitChanged)
         return(true);  // la orden puede ser modificada      
      //--- no hay cambios en los niveles de apertura, StopLoss y Takeprofit
      else
      //--- comunicamos el error
         PrintFormat("La orden #%d ya tiene los niveles Open=%.5f SL=.5f TP=%.5f",
                     ticket,OrderOpenPrice(),OrderStopLoss(),OrderTakeProfit());
     }
//--- hemos llegado al final, no hay cambios para la orden
   return(false);       // no tiene sentido modificarla 
  }
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- niveles de precio para las órdenes y posiciones
   double priceopen,stoploss,takeprofit;
//--- ticket de la orden actual 
   int orderticket;
/*
   ... obtenemos el ticket de la orden y los nuevos niveles de StopLoss/Takeprofit/PriceOpen
*/
//--- comprobamos los niveles antes de modificar la orden   
   if(OrderModifyCheck(orderticket,priceopen,stoploss,takeprofit))
     {
      //--- la comprobación ha tenido éxito
      OrderModify(orderticket,priceopen,stoploss,takeprofit,OrderExpiration());
     }
  }

Recomendamos leer los artículos:

  1. Cómo hacer más fácil la detección y recuperación de errores en el código de un asesor experto
  2. Cómo crear un robot de trading fiable y seguro en MQL4


Intento de importación de los archivos compilados (EX4/EX5 inclusive) y DLL

Los programas difundidos por el Mercado deberán ser seguros con toda garantía para los usuarios. Por eso, cualquier intento de uso de DLL o de funciones de los archivos compilados EX4/EX5 se considera erróneo. Este tipo de productos no se publicará en el Mercado.

Si su programa necesita del uso de indicadores adicionales no incluidos en el paquete, use los Recursos.


Recurrir a los indicadores de usuario a través de iCustom()

Si para que su programa funcione es necesario recurrir a los datos de un indicador de usuario, entonces deberá ubicar todos los indicadores necesarios en los Recursos. Los productos del Mercado deberán estar listos para funcionar en cualquier entorno sin preparación, por eso deberán contener todo lo necesario en  su archivo EX4/EX5. Artículos recomendados sobre el tema:


Transmisión de un parámetro no permitido a la función (error de tiempo de ejecución)

Este tipo de errores se encuentra rara vez, para muchos de ellos hay códigos preparados, que deberían ayudar a encontrar el motivo.

Constante Valor Descripción
ERR_INTERNAL_ERROR 4001 Error interno inesperado
ERR_WRONG_INTERNAL_PARAMETER 4002 Parámetro erróneo durante la llamada built-in a la función del terminal de cliente
ERR_INVALID_PARAMETER 4003 Parámetro erróneo durante la llamada a la función de sistema
ERR_NOTIFICATION_WRONG_PARAMETER 4516 Parámetro incorrecto para el envío de notificaciones: a la función SendNotification()  le ha sido transmitida una cadena vacía o NULL
ERR_BUFFERS_WRONG_INDEX 4602 Índice erróneo de su búfer de indicadores
ERR_INDICATOR_WRONG_PARAMETERS 4808 Número erróneo de parámetros al crear un indicador
ERR_INDICATOR_PARAMETERS_MISSING 4809 No hay parámetros cuando se crea un indicador
ERR_INDICATOR_CUSTOM_NAME 4810 El primer parámetro en la matriz tiene que ser el nombre del indicador personalizado
ERR_INDICATOR_PARAMETER_TYPE 4811 Tipo erróneo del parámetro en la matriz al crear un indicador
ERR_NO_STRING_DATE 5030 No hay fecha en la cadena
ERR_WRONG_STRING_DATE 5031 Fecha errónea en la cadena
ERR_TOO_MANY_FORMATTERS 5038 Hay más especificadores de formato que parámetros
ERR_TOO_MANY_PARAMETERS 5039 Hay más parámetros que especificadores de formato

En el recuadro no se han enumerado ni mucho menos todos los errores que se pueden encontrar durante el funcionamiento del programa iniciado.

 

Access violation

Este error surge al intentar recurrir a la memoria cuyo acceso está prohibido. En cada caso semejante es necesario que se dirija a los desarrolladores en la Mesa de Ayuda a través de su Perfil o de la página Contactos. Una descripción detallada de los pasos necesarios para reproducir los errores, además de adjuntar el código fuente, acelerarán la búsqueda de este error y le ayudarán a mejorar el compilador de código fuente.




Consumo de recursos del procesador y la memoria

A la hora de escribir programas, es importante usar los algorítmos óptimos en cuanto al tiempo de ejecución, pues en el caso contrario, el trabajo con otros programas iniciados en el terminal puede dificultarse o volverse imposible.

Es importante recordar que cada símbolo en la Observación del mercado reserva un hilo común para el funcionamiento del terminal. En dicho hilo trabajan todos los indicadores y gráficos abiertos de él.

Esto significa que si usted tiene abiertos 5 gráficos de EURUSD en diferentes marcos temporales  y en estos gráficos se han iniciado 15 indicadores, entonces todos estos gráficos e indicadores tienen a su disposición un hilo único para calcular y representar información en los gráficos. Por eso, si un indicador que es ineficaz y gasta muchos recursos está iniciado en el gráfico, puede ralentizar el funcionamiento del resto de los indicadores e incluso frenar el dibujado de los precios en el resto de los gráficos de este símbolo.

Usted podrá comprobar con facilidad el tiempo invertido por su algoritmo con la ayuda de la función GetMicrosecondCount(). Midiendo el tiempo entre dos líneas de código es fácil obtener el tiempo de ejecución en microsegundos.  Para pasarlo a milisegundos (ms), se debe dividir el tiempo entre 1000 (en 1 milisegundo hay 1000 microsegundos). Para los indicadores, un lugar crítico en lo que respecta al tiempo de ejecución suele ser el procesador OnCalculate(). Normalmente, el primer cálculo del indicador depende mucho del parámetro Máx. de barras en la ventana. Indique en este el valor "Unlimited" e inicie su indicador en un símbolo con una historia superior a 10 años en el marco temporal M1. Si el primer inicio ocupa mucho tiempo (por ejemplo, más de 100 ms), entonces es necesario optimizar el código.

Aquí tenemos un ejemplo de cómo medir el tiempo de ejecución del procesador OnCalculate() en el indicador ROC, incluido en el paquete del terminal, en el código fuente. Las inserciones se destacan con fondo amarillo:

//+------------------------------------------------------------------+
//| Rate of Change                                                   |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,const int prev_calculated,const int begin,const double &price[])
  {
//--- check for rates count
   if(rates_total<ExtRocPeriod)
      return(0);
//--- tiempo de comienzo de los cálculos
   ulong start=GetMicrosecondCount();  
//--- preliminary calculations
   int pos=prev_calculated-1; // set calc position
   if(pos<ExtRocPeriod)
      pos=ExtRocPeriod;
//--- the main loop of calculations
   for(int i=pos;i<rates_total && !IsStopped();i++)
     {
      if(price[i]==0.0)
         ExtRocBuffer[i]=0.0;
      else
         ExtRocBuffer[i]=(price[i]-price[i-ExtRocPeriod])/price[i]*100;
     }
//--- tiempo de finalización de los cálculos     
   ulong finish=GetMicrosecondCount();  
   PrintFormat("La función %s en %s ha invertido %.1f ms",__FUNCTION__,__FILE__,(finish-start)/1000.);
//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
  }

La memoria utilizada se puede medir con la ayuda de la función  MQLInfoInteger(MQL_MEMORY_USED). Y, por supuesto, use el Perfilador de código para buscar las funciones que más memoria gastan en su programa. Asimismo, le recomendamos leer el artículo Los principios del cálculo económico de los indicadores y Depuración de programas en MQL5.

Los expertos funcionan en sus propios hilos, pero todo lo dicho más arriba se relaciona igualmente con ellos. Es imprescindible que escriba el código óptimo en cualquier tipo de programa: asesores, indicadores, bibliotecas y scripts.


Las comprobaciones nunca están de más

Todos los consejos expuestos en lo que respecta a la comrpobación de indicadores y asesores se recomiendan no solo para la publicación de productos en el Mercado, sino también como práctica habitual  cuando usted escriba para sí mismo. En este artículo no hemos analizado ni mucho menos todos los errores que se pueden encontrar al comerciar en cuentas reales. Aquí no se han estudiado las normas de procesamiento de los errores comerciales surgidos al vender la conexión a un servidor comercial, las recotizaciones, las negativas a la ejecución de operaciones y muchos más factores que pueden romper las reglas ideales de un sistema comercial. Para estos casos, cada procesador de robots tiene sus recetas preparadas y elaboradas por la experiencia.

A los principiantes les recomendamos leer todos los artículos dedicados al procesamiento de errores, y también les recomendamos hacer preguntas en el foro y en los comentarios a este artículo. Otros miembros con más experiencia de la comunidad MQL5.community le ayudarán con las cuestiones más complicadas.  Esperamos que la información reunida en el artículo le sirva de ayuda para crear robots comerciales más fiables y en un plazo más corto.


Artículos recomendados sobre el tema:

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

Archivos adjuntos |
2555_en.zip (26.47 KB)
LifeHack para tráders: indicadores de balance, reducción, carga y ticks durante la simulación LifeHack para tráders: indicadores de balance, reducción, carga y ticks durante la simulación

¿Cómo convertir la simulación en algo más visual? La respuesta es sencilla: hay que usar en el simulador uno o varios indicadores, un indicador de ticks, un indicador de balance y equidad, un indicador de reducción y carga del depósito. Esto permitirá realizar un seguimiento visual de la naturaleza de los ticks, o de los cambios de balance y equidad, o de la reducción y la carga del depósito.

Experto comercial universal: integración con los módulos estándar de señales de MetaTrader (parte 7) Experto comercial universal: integración con los módulos estándar de señales de MetaTrader (parte 7)

Esta parte está dedicada a la integración del motor comercial CStrategy con los módulos de señales incluidos en la biblioteca estándar de MetaTrader. El material describe los métodos de trabajo con las señales y la creación de estrategias de usuario basadas ellas.

Interfaces gráficas VIII: Control "Lista jerárquica" (Capítulo 2) Interfaces gráficas VIII: Control "Lista jerárquica" (Capítulo 2)

En el capítulo anterior de la octava parte de la serie sobre las interfaces gráficas hemos analizado los controles “Calendario estático” y “Calendario desplegable”. El segundo capítulo va a dedicarse a un control compuesto no menos complejo, “Lista jerárquica”, sin la que no se arregla ninguna librería multifuncional para la creación de interfaces gráficas. La implementación de la lista jerárquica presentada en este artículo contiene múltiples ajustes y modos flexibles, lo que permitirá configurar este control a sus necesidades con la máxima precisión.

Interfaces gráficas VIII: Control "Explorador de archivos" (Capítulo 3) Interfaces gráficas VIII: Control "Explorador de archivos" (Capítulo 3)

En los capítulos anteriores de la octava parte de la serie, nuestra librería se ha completado con las clases para la creación de los punteros para el cursor del ratón, calendarios y listas jerárquicas. En este artículo vamos a analizar el control “Explorador de archivos” que también puede utilizarse como parte de la interfaz gráfica de la aplicación MQL.