Errores comunes en los programas MQL4 y cómo evitarlos

MetaQuotes | 7 octubre, 2015

Introducción

Algunos programas más antiguos pueden devolver errores en la nueva versión del compilador MQL4.

Para evitar la conclusión crítica de programas, la versión anterior del compilador maneja muchos errores en el entorno de ejecución. Por ejemplo, división por cero o array fuera de rango son errores críticos y generalmente conducen al desplome de la aplicación. Tales errores ocurren solamente en algunos sstados para determinados valores de las variables. Lee este artículo para saber cómo manejar estos casos.

El nuevo compilador puede detectar fuentes reales o potenciales de errores y mejorar la calidad del código.

En este artículo, discutimos los posibles errores que pueden ser detectados durante la compilación de antiguos programas y formas de solucionarlos.

  1. Errores de compilación
  2. Errores de tiempo de ejecución
  3. Advertencias del compilador

1. Errores de compilación

Si un código de programa contiene errores, no puede ser compilado.

Para controlar todos los errores, se recomienda utilizar el modo de compilación estricta, que se determina por la siguiente directiva:

#property strict

Este modo simplifica considerablemente la resolución de problemas.


1.1. El identificador coincide con una palabra reservada

Si el nombre de una variable o función coincide con una de las palabras reservadas

int char[];  // incorrecto
int char1[]; // correcto
int char()   // incorrecto
{
 return(0);
}

el compilador devuelve un mensaje de error:

Figura 1. Errores de "símbolo inesperado" y de "nombre esperado"

Figura 1. Errores de "símbolo inesperado" y de "nombre esperado"

To fix this error, you need to use the correct name of the variable or function.


1.2. Caracteres especiales en los nombres de variables y funciones

Si los nombres de variables o funciones contienen caracteres especiales ($, @, punto):

int $var1; // incorrecto
int @var2; // incorrecto 
int var.3; // incorrecto
void f@()  // incorrecto
{
 return;
}

el compilador devuelve un mensaje de error:

Figura 2. Errores de "símbolo desconocido" y "punto y coma esperado"

Figura 2. Errores de "símbolo desconocido" y "punto y coma esperado"

Para corregir este error, necesitará usar la función correcta o nombres de variables.


1.3. Errores utilizando el operador del conmutador

En la versión antigua del compilador puede utilizar cualquiera de los valores en las expresiones y constantes del operador del interruptor :

void start()
  {
   double n=3.14;
   switch(n)
     {
      case 3.14: Print("Pi");break;
      case 2.7: Print("E");break;
     }
  }

El nuevo compilador, constantes y expresiones del operador del interruptor deberán estar enteros, por lo que se producirán errores al intentar utilizar estas construcciones:

Figura 3. Errores de "tipo de expresión del interruptor ilegal" y de "expresión constante no integral"

Figura 3. Errores de "tipo de expresión del interruptor ilegal" y de "expresión constante no integral"

In such cases, you can use explicit comparison of numerical values, for example:

void start()
  {
   double n=3.14;
   if(n==3.14) Print("Pi");
   else
      if(n==2.7) Print("E");
  }

1.4. Valores devueltos de las funciones

Todas las funciones excepto el vacío deben devolver el valor del tipo declarado. Por ejemplo:

int function()
{
}

En el modo de compilación estricta el error se produce:

Figura 4. Error "no todas las rutas de control devuelven un valor"

Figura 4. Error "no todas las rutas de control devuelven un valor"

En el modo de compilación por defecto, el compilador devuelve una advertencia:

Figura 5. Advertencia "no todas las rutas de control devuelven un valor"

Figura 5. Advertencia "no todas las rutas de control devuelven un valor"

Si el valor devuelto de la función coincide con el declarado:

int init()                         
  {
   return;                          
  }

En el modo de compilación estricta el error se produce:

Figura 6. Error "la función debe devolver un valor"

Figura 6. Error "la función debe devolver un valor"

En el modo de compilación por defecto, el compilador devuelve una advertencia:

Figura 7. Advertencia ' Retorno - la función debe devolver un valor "

Figura 7. Advertencia ' Retorno - la función debe devolver un valor "

Para corregir estos errores, agregar el operador de retorno con el valor devuelto de tipo correspondiente al código de función.



1.5. Arrays en argumentos de la función

En los argumentos de la función, arrays de discos ahora son pasados por referencia solamente.

double ArrayAverage(double a[])
{
 return(0);
}
En el modo de compilación estricta, este código producirá un error:

Figura 8. Error del compilador "matrices pasadas por solo referencia"

Figure 8. Error del compilador "matrices pasadas por referencia"

En el modo de compilación por defecto, el compilador devuelve una advertencia:

Figura 9. Advertencia del compilador "matrices pasadas por referencia solamente"

Figura 9. Advertencia del compilador "matrices pasadas por referencia solamente"

Para corregir este error, debe especificar explícitamente que la matriz se pasa por referencia, añadiendo el prefijo & antes del nombre de la matriz:

double ArrayAverage(double &a[])
{
 return(0);
}

Cabe señalar que ahora las matrices de las constantes (Time[], Open[] High[], Low[], Close[], Volume[]) no se puede pasar por referencia. Por ejemplo, la siguiente llamada:

ArrayAverage(Open);

independientemente del modo de compilación conduce a un error:

Figura 10. Error 'Open' - variable constante no se puede pasar como referencia

Figura 10. Error 'Open' - variable constante no se puede pasar como referencia

Para evitar estos errores, copiar los datos necesarios de la matriz de constantes:

   //---un array que almacena los valores de precio de apertura
   double OpenPrices[];
   //---copiar los valores de precios de apertura en la matriz OpenPrices[]
   ArrayCopy(OpenPrices,Open,0,0,WHOLE_ARRAY);
   //---función de llamada
   ArrayAverage(OpenPrices);


2. Errores de tiempo de ejecución

Los errores que se producen durante la ejecución del código de programa se llaman errores de tiempo de ejecución. Este tipo de errores generalmente dependen del estado de un programa y se asocian con valores incorrectos de las variables.

Por ejemplo, si una variable se utiliza como un índice de elementos de un array, sus valores negativos conducirán inevitablemente a la matriz de error de Rango.


2.1. Array fuera de Rango

Este error se produce a menudo en los indicadores cundo acceden a buffers del indicador. La función IndicatorCounted() devuelve el número de barras sin cambios desde la última llamada al indicador. Los valores del indicador en las barras previamente calculadas no son necesarios recalcular, para cálculos más rápidos sólo se necesitan procesar algunas barras anteriores.

La mayoría de los indicadores que utilizan este método de optimización de cálculo este aspecto:

//+------------------------------------------------------------------+
//| Función de iteración de indicador personalizado                  |
//+------------------------------------------------------------------+
int start()
  {
   //--- algunos cálculos no requieren menos de N barras (p. ej. 100)      
   if (Bars<100) // si se disponen de menos barras en un gráfico (por ejemplo con timeframe MN)    
     return(-1); // detener el cálculo y salir

   //--- el número de barrass que no han cambiado desde la última llamada al indicador
   int counted_bars=IndicatorCounted();
   //--- salir si se produce algún error
   if(counted_bars<0) return(-1);
      
   //--- posición de la barra calculado en el comienzo del bucle
   int limit=Bars-counted_bars;

   //--- Si counted_bars = 0, reducir la posición de salida en el bucle por 1,,   
   if(counted_bars==0) 
     {
      limit--;  // para evitar que la matriz tenga problema de fuera de rango cuando counted_bars == 0
      // ---Utilizamos un cambio de 10 barras atrás en el historial, así que habrá añadir este cambio durante el primer cálculo
      limit-=10;
     }
   else //--- el indicator ya ha sido calculado, counted_bars>0
     {     
      //--- para llamadas repetidas aumentar el límite en 1 para actualizar los valores del indicador para la última barra
      limit++;
     } 
   //--- cáculo principal del bucle
   for (int i=limit; i>0; i--)
   {
     Buff1[i]=0.5*(Open[i+5]+Close[i+10]) // se utilizan los valores de barras más profundas 5 y 10 del historial
   }
}

A menudo el caso de counted_bars == 0 se maneja incorrectamente (la posición inicial límite debe reducirse por el valor igual a 1 + máximo índice en relación con la variable de bucle).

Además, recuerde que en el momento de la ejecución de la función start() podemos acceder a elementos del array de búferes de indicador de 0 a Bars ()-1. Si usted necesita trabajar con los arrays de discos que no son bufers del indicador, aumentar su tamaño mediante la función ArrayResize() de acuerdo con el actual tamaño de búferes de indicador. El índice máximo del elemento de la dirección también puede obtenerse llamando a ArraySize() con uno de los buffers del indicador utilizados como argumento.


2.2. División por Cero

El error de dividir por Cero ocurre cuando un divisor en una operación de división es igual a cero:

void OnStart()
  {
//---
   int a=0, b=0,c;
   c=a/b;
   Print("c=",c);
  }

Cuando se ejecuta este script, aparece un mensaje de error en la ficha de expertos y se cierra el programa:

Figura 11. Mensaje de error "división por cero"

Figura 11. Mensaje de error "división por cero"

Generalmente este error se produce cuando el valor del divisor es determinado por los valores de los datos externos. Por ejemplo, si se analizan los parámetros de operaciones, el valor del margen usado es igual a 0 si no hay órdenes abiertas. Otro ejemplo: si los datos se leen desde un archivo, no podemos garantizar el correcto funcionamiento si el archivo no está disponible. Por lo que se debe tener en cuenta estos casos y procesarlos correctamente.

La forma más fácil es comprobar el divisor antes de la operación de división y declarar un valor de parámetro incorrecto:

void OnStart()
  {
//---
   int a=0, b=0,c;
   if(b!=0) {c=a/b; Print(c);}
   else {Print("Error: b=0"); return; };
  }

Esto no causa un error crítico, pero aparece un mensaje acerca de un valor de parámetro incorrecto y cierra el programa:

Figura 12. Mensaje incorrecto del divisor

Figura 12. Mensaje incorrecto del divisor


2.3. Uso de 0 en lugar de NULL para el carácter actual

En la vieja versión del compilador 0 (cero) podría utilizarse como argumento en las funciones que requieren la especificación de un instrumento financiero.

Por ejemplo, el valor del indicador técnico Media Móvil (MA) para el símbolo actual podría ser solicitado como sigue:

AlligatorJawsBuffer[i]=iMA(0,0,13,8,MODE_SMMA,PRICE_MEDIAN,i);    // incorrect

En el nuevo compilador se debe especificar explícitamente NULL para especificar el símbolo actual:

AlligatorJawsBuffer[i]=iMA(NULL,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); // correct

Además, el actual símbolo y el periodo de la gráfica puede especificarse usando las funciones Symbol() y Period() .

AlligatorJawsBuffer[i]=iMA(Symbol(),Period(),13,8,MODE_SMMA,PRICE_MEDIAN,i); // correct


2.4. Cadenas Unicode y su uso en un archivo DLL

Las cadenas están representadas ahora como una secuencia de caracteres Unicode.

Recordar esto y utilizar las funciones de Windows apropiadas. Por ejemplo, cuando se utiliza la libreria wininet.dll, en cambio InternetOpenA() y InternetOpenUrlA(), debe llamar a InternetOpenW() y InternetOpenUrlW().

La estructura interna de cadenas ha cambiado en MQL4 (ahora tiene 12 bytes), y la estructura de MqlString se debe utilizar cuando se pasan cadenas a DLL:

#pragma pack(push,1)
struct MqlString
  {
   int      size;       // 32 bits entero, contiene el tamaño del búfer asignado para la cadena
   LPWSTR   buffer;     // dirección de 32 bits del búfer que contiene la cadena
   int      reserved;   // 32 bit entero, reservado, no usar
  };
#pragma pack(pop,1)


2.5. Uso compartido de archivos

En el MQL4 nuevo, las banderas FILE_SHARE_WRITE y FILE_SHARE_READ deben ser especificadas explícitamente para el uso compartido al abrir los archivos.

Si las banderas están ausentes, el archivo se abre en modo exclusivo y no se puede abrir por ninguna persona hasta que se cierra por el usuario que la abrió.

Por ejemplo, cuando se trabaja graficos offline las banderas compartidas deben ser especificadas explícitamente:

   // 1º cambiar - agregar flags a compartir
   ExtHandle=FileOpenHistory(c_symbol+i_period+".hst",FILE_BIN|FILE_WRITE|FILE_SHARE_WRITE|FILE_SHARE_READ);

Para más detalles lea Gráfica Offline en el nuevo MQL4.


2.6. Conversión de DateTime

La conversión de datetime en una cadena depende ahora del modo de compilación:

  datetime date=D'2014.03.05 15:46:58';
  string str="mydate="+date;
//--- str="mydate=1394034418" - viejo compilador, ninguna directiva de #property strict en el nuevo compilador
//--- str="mydate=2014.03.05 15:46:58" - nuevo vompilador con directiva #property strict

Por ejemplo, tratando de trabajar con los archivos cuyo nombre contiene dos puntos causa un error.


3. Advertencias del compilador

Las advertencias del compilador son informativas y no mensajes de error, pero se indican las fuentes posibles de error.

Un código claro no debe contener advertencias.


3.1. Nombres de variables globales y locales que coinciden

Si las variables en los niveles locales y globales tienen nombres similares:

int i; // a global variable
void OnStart()
  {
//---
   int i=0,j=0; // variables locales
   for (i=0; i<5; i++) {j+=i;}
   PrintFormat("i=%d, j=%d",i,j);
  }

el compilador muestra un aviso que muestra el número de línea en la que se declara la variable global:

Figura 13. ADVERTENCIA «declaración de '%' oculta declaración global en línea %»

Figura 13. Advertencia «declaración de '%' oculta declaración global en línea %»

Para fijar estas advertencias deben haber correctos nombres de variables globales.


3.2. Falta de coincidencia de tipos

El nuevo compilador tiene una nueva operación de esterotipos .

#property strict
void OnStart()
  {
   double a=7;
   float b=a;
   int c=b;
   string str=c;
   Print(c);
  }

En modo de compilación estricta el compilador muestra advertencias si falta de coincidencia de tipos:

Figura 14. Advertencias "posible pérdida de datos debido a la conversión de tipo" y "conversión implícita de 'número' a 'cadena'

Figura 14. Advertencias "posible pérdida de datos debido a la conversión de tipo" y "conversión implícita de 'número' a 'cadena'

En este ejemplo, el compilador advierte sobre la posible pérdida de precisión para diferentes tipos de datos asignados y conversión implícita de int a string.

Para fijar la advertencia utilice esterotipos explícitos:

#property strict
void OnStart()
  {
   double a=7;
   float b=(float)a;
   int c=(int)b;
   string str=(string)c;
   Print(c);
  }

3.3. Variables no utilizadas

La presencia de variables que no se utilizan en el código del programa (entidades superfluas) no es un buen hábito.

void OnStart()
  {
   int i,j=10,k,l,m,n2=1;
   for(i=0; i<5; i++) {j+=i;}
  }

Los informes de dichas variables se muestran independientemente del modo de compilación:

Figura 15. ADVERTENCIA "variable '%' no usada'

Figura 15. Advertencia "variable '%' not used"

Para solucionarlo, quite variables no utilizadas desde el código.


Conclusiones

El artículo describe problemas comunes que pueden ocurrir durante la compilación de programas viejos que contienen errores.

En todos los casos se recomienda utilizar el modo de compilación estricta para la depuración de programas.