Entradas Populares

La Elección Del Editor - 2020

MQL4: corrección de errores y advertencias durante la compilación en MetaEditor

Desarrollar expertos en comercio MQL4 no es una tarea fácil. En primer lugar, la algoritmización de cualquier sistema de comercio complejo ya es un problema, ya que es necesario tener en cuenta muchos detalles, comenzando con las características del TS y terminando con las características específicas del entorno MetaTrader 4. En segundo lugar, incluso tener un algoritmo detallado no elimina las dificultades que surgen al transferir el desarrollado algoritmo para el lenguaje de programación MQL4.

El compilador proporciona ayuda para escribir a los expertos correctos. Después de comenzar la compilación, MetaEditor informará todos los errores de sintaxis en su código. Pero, desafortunadamente, además de los errores de sintaxis, su asesor también puede contener errores lógicos que el compilador no puede detectar. Por lo tanto, tendremos que hacer esto nosotros mismos. Cómo hacer esto está en nuestro material hoy.

Los errores de compilación más comunes.

Si hay errores en el código, el programa no se puede compilar. Para un control total de todos los errores, se recomienda usar el modo de compilación estricto, que se establece en la directiva:

# propiedad estricta

Este modo simplifica enormemente la búsqueda de errores. Ahora pasemos a los errores de compilación más comunes.

El identificador coincide con la palabra reservada

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

int char; // incorrecto int char1; // correct int char () // incorrecto {return (0); }

entonces el compilador muestra mensajes de error:

Para corregir este error, debe corregir el nombre de la variable o función. Recomiendo seguir con el siguiente sistema de nombres:

Todas las funciones deben indicar una acción. Es decir, debe ser un verbo. Por ejemplo, OpenLongPosition () o ModifyStopLoss (). Después de todo, las funciones siempre hacen algo, ¿verdad?

Además, es recomendable llamar a funciones en el llamado estilo CamelCase. Y las variables están en estilo cebab_case. Esta es una práctica común.

Hablando de nombres de variables. Las variables son sustantivos. Por ejemplo, my_stop_loss, day_of_week, current_month. No es tan aterrador nombrar una variable con un nombre largo, es mucho peor nombrarla de manera incomprensible. ¿Qué es un dow, índice Dow Jones? No, resulta ser el día de la semana. Por supuesto, hoy ya entiendes qué es esta variable. Pero cuando abra el código del asesor un mes después, no todo será tan obvio. Y esta vez perdido en la decodificación de mensajes del pasado, ¿lo necesita?

Caracteres especiales en nombres de variables y funciones

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

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

entonces el compilador muestra mensajes de error:

Para corregir este error, nuevamente necesita ajustar los nombres de las variables o funciones, bien, o llamarlas inmediatamente de forma humana. Idealmente, el código debe escribirse de modo que incluso una persona que no conozca la programación lo lea y comprenda lo que está sucediendo allí.

Errores al usar la instrucción switch

La versión anterior del compilador permitía usar cualquier valor en las expresiones y constantes de la instrucción switch:

inicio nulo () {doble n = 3.14; conmutador (n) {caso 3.14: Imprimir ("Pi"); romper caso 2.7: Imprimir ("E"); romper }}

En el nuevo compilador, las expresiones y constantes de la instrucción switch deben ser enteros, de modo que cuando se usan tales construcciones, se producen errores:

Por lo tanto, cuando analiza el código clásico, como WallStreet, Ilan y otros incorruptibles (que es muy útil para el autodesarrollo), puede encontrar este error. Se trata de manera muy simple, por ejemplo, cuando se usa una línea como esta aquí:

switch (MathMod (día_48, 10))

Así es como puede resolver fácilmente el problema:

interruptor ((int) MathMod (día_48, 10))

Valores de retorno de funciones

Todas las funciones, excepto void, deben devolver un valor del tipo declarado. Por ejemplo:

función int () {}

En modo de compilación estricto (estricto), se produce un error:

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

Si el valor de retorno de la función no coincide con la declaración:

int init () {return; }

Luego, en modo de compilación estricto, se produce un error:

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

Para corregir tales errores en el código de función, solo necesita agregar una declaración de retorno return con un valor de retorno del tipo correspondiente.

Arreglos en argumentos de función

Las matrices en argumentos de función se pasan solo por referencia. Anteriormente, esto no era así, por lo que en los antiguos asesores puede encontrar este error. Aquí hay un ejemplo:

double ArrayAverage (double a) {return (0); }

El código dado en modo de compilación estricto (estricto) dará como resultado un error:

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

Para corregir tales errores, debe indicar explícitamente la transferencia de la matriz por referencia, agregando el prefijo & al nombre de la matriz:

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

Por cierto, las matrices constantes (Tiempo, Abrir, Alta, Baja, Cerrar, Volumen) no se pueden pasar por referencia. Por ejemplo, una llamada:

ArrayAverage (abierto);

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

Para eliminar tales errores, debe copiar los datos necesarios de la matriz constante:

// --- matriz para almacenar valores de precios abiertos double OpenPrices; // --- copiamos los valores del precio de apertura en la matriz OpenPrices ArrayCopy (OpenPrices, Open, 0,0, WHOLE_ARRAY); // --- llamamos a la función ArrayAverage (OpenPrices);

Uno de los errores más comunes es la pérdida de un indicador por parte de un asesor. En tales casos, generalmente los usuarios expertos en los foros escriben enojados: "¡El asesor no funciona!" o "Puse al asesor en la tabla y no pasa nada". La solución a este problema es realmente muy simple. Como siempre, solo mire la pestaña "Registro" de la terminal y encuentre allí una entrada como:

2018.07.08 09: 15: 44.957 2016.01.04 00:51 no puede abrir el archivo 'C: Users1AppDataRoamingMetaQuotesTerminal MQL4indicatorsKELTNER_F12.ex4' 2

Esto nos dice que se olvidaron de poner el indicador en la carpeta, o se llama de manera diferente. Si falta el indicador, debe agregarlo a la carpeta con indicadores. Si es así, vale la pena verificar su nombre en el código del asesor; lo más probable es que se llame de manera diferente allí.

Advertencias del compilador

Las advertencias del compilador son para fines informativos y no son mensajes de error, sin embargo, indican posibles fuentes de errores y es mejor corregirlos. El código limpio no debe contener advertencias.

Intersecciones de nombres de variables globales y locales

Si a nivel global y local hay variables con el mismo nombre:

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

entonces el compilador muestra una advertencia e indica el número de línea en la que se declara la variable global:

Para corregir tales advertencias, debe ajustar los nombres de las variables globales.

No coinciden los tipos

En el siguiente ejemplo:

#property estricto vacío OnStart () {double a = 7; flotador b = a; int c = b; cadena str = c; Impresión (c); }

en modo de compilación estricto con falta de coincidencia de tipos, el compilador muestra advertencias:

En este ejemplo, el compilador advierte de una posible pérdida de precisión al asignar varios tipos de datos y la conversión implícita de tipo int a cadena.

Para solucionarlo, debe usar la conversión de tipo explícito:

#property estricto vacío OnStart () {double a = 7; flotador b = (flotador) a; int c = (int) b; cadena str = (cadena) c; Impresión (c); }

Variables no utilizadas

La presencia de variables que no se usan en el código del programa (entidades adicionales) no es una buena forma.

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

Los mensajes sobre tales variables se muestran independientemente del modo de compilación:

Para solucionarlo, solo necesita eliminar las variables no utilizadas del código del programa.

Diagnóstico de errores de compilación

A menudo, después de escribir un programa, surgen problemas de compilación debido a errores en el código. Estos pueden ser los errores más variados, pero en cualquier caso, es necesario detectar rápidamente una sección de código donde se ha cometido un error.

A menudo, las personas toman mucho tiempo y muchos nervios en busca de algún soporte adicional. Sin embargo, hay una manera de detectar rápidamente los errores, que se basa en el uso de comentarios.

Escribir un código lo suficientemente grande sin un solo error es muy bueno. Pero, desafortunadamente, esto no sucede a menudo. No considero errores aquí que conducen a la ejecución incorrecta del código. Aquí hablaremos de errores que hacen imposible la compilación.

Los errores comunes son insertar un paréntesis adicional en una condición difícil, falta de un paréntesis, no establecer dos puntos, una coma al declarar variables, un error tipográfico en el nombre de la variable, etc. A menudo, al compilar, puede ver inmediatamente en qué línea se cometió un error similar. Pero hay momentos en que encontrar ese error no es tan simple. Ni el compilador ni el buen ojo pueden ayudarnos a encontrar inmediatamente el error. En tales casos, como regla, los programadores novatos comienzan a "recorrer" todo el código, tratando de identificar visualmente el error. Y una y otra vez, mientras los nervios resisten.

Sin embargo, MQL, como otros lenguajes de programación, ofrece una herramienta excelente: comentar. Al usarlo, puede deshabilitar algunas partes del código. Por lo general, los comentarios se utilizan para insertar algún tipo de comentario o para deshabilitar secciones de código no utilizadas. Los comentarios también se pueden aplicar con éxito al buscar errores.

La búsqueda de errores generalmente se reduce a determinar la sección del código donde se cometió el error, y luego, en esta sección, el error se ubica visualmente. Creo que es poco probable que alguien dude de que sea más fácil y rápido examinar 5-10 líneas de código más rápido que 100-500, o incluso varios miles.

Cuando se utilizan los comentarios, la tarea es extremadamente simple. Primero, necesita comentar las diferentes secciones del código (a veces casi todo el código), por lo tanto, "deshabilitarlo". Luego, a su vez, el comentario se elimina de estas secciones del código. Después del siguiente retiro de comentarios, se intenta compilar. Si la compilación es exitosa, el error no está en esta sección del código. Luego se abre la siguiente sección de código y así sucesivamente. Cuando hay una parte problemática del código, se busca visualmente un error y luego se elimina. Nuevamente, se intenta compilar. Si todo salió bien, el error se resuelve.

Es importante identificar correctamente las secciones de código que necesita comentar. Si esta condición (u otra construcción lógica), se debe comentar completamente. Si comenta sobre la sección de código donde se declaran las variables, es importante que no se abra la sección donde se accede a estas variables. En otras palabras, los comentarios deben aplicarse de acuerdo con la lógica de programación. El incumplimiento de este enfoque conduce a nuevos errores de compilación engañosos.

Aquí hay un gran ejemplo de un error cuando no está claro dónde buscarlo y comentar sobre el código puede ayudarnos.

Errores de tiempo de ejecución

Los errores que ocurren durante la ejecución del código del programa se denominan comúnmente errores de tiempo de ejecución. Tales errores generalmente dependen del estado del programa y están asociados con valores incorrectos de variables.

Por ejemplo, si se usa una variable como índice de elementos de la matriz, entonces sus valores negativos conducirán inevitablemente a un flujo de salida de la matriz.

Matriz fuera de rango

Este error a menudo ocurre en los indicadores al acceder a los buffers de indicadores. La función IndicatorCounted () devuelve el número de barras que no han cambiado desde la última llamada del indicador. No es necesario volver a calcular los valores de los indicadores en las barras calculadas previamente, por lo tanto, para acelerar los cálculos, es suficiente procesar solo las últimas barras.

La mayoría de los indicadores que utilizan este método de optimización de cálculos tienen la siguiente forma:

// + ----------------------------------------------- ------------------- + // | Función de iteración del indicador personalizado | // + ----------------------------------------------- ------------------- + int start () {// --- a veces se requieren al menos N barras para el cálculo (por ejemplo, 100) // si no hay tal en el gráfico el número de barras (por ejemplo, en el marco de tiempo MN) if (Bars <100) {return (-1); // detiene el cálculo y sale antes de la programación} // --- el número de barras que no han cambiado desde la última llamada al indicador int counted_bars = IndicatorCounted (); // --- en caso de error, salga if (counted_bars0 // --- durante las llamadas repetidas, aumente el límite en 1 a // --- garantizado para actualizar los valores del indicador para el último límite de barra ++;} // --- el ciclo de cálculo principal para (int i = limit; i> 0; i--) {// usando los valores de las barras que profundizan en el historial en 5 y 10 Buff1i = 0.5 * (Openi + 5 + Closei + 10)}}

A menudo hay un manejo de caso incorrecto counted_bars == 0 (la posición inicial del límite debe reducirse en un valor igual a 1 + el índice máximo relativo a la variable de bucle).

También debe recordarse que en el momento en que se ejecuta la función start (), podemos acceder a los elementos de las matrices de búfer del indicador de 0 a Bars () - 1. Si es necesario trabajar con matrices que no son buffers de indicadores, entonces su tamaño debe aumentarse utilizando la función ArrayResize () de acuerdo con el tamaño actual de los buffers de indicadores. El índice máximo de un elemento para direccionamiento también se puede obtener llamando a ArraySize () con uno de los buffers de indicador como argumento.

División por cero (división cero)

El error "División cero" se produce si el divisor es igual a cero durante la operación de división:

nulo OnStart () {int a = 0, b = 0, c; c = a / b; Imprimir ("c =", c); }

Cuando se ejecuta este script, la pestaña "Expertos" muestra un mensaje de error y el programa finaliza:

Típicamente, tal error ocurre en casos donde el valor del divisor está determinado por los valores de algunos datos externos. Por ejemplo, si se analizan los parámetros de negociación, el valor del margen involucrado será 0 si no hay órdenes abiertas. Otro ejemplo: si los datos analizados se leen de un archivo, si están ausentes, no se puede garantizar el funcionamiento correcto. Por esta razón, es aconsejable tratar de tener en cuenta estos casos y manejarlos correctamente.

La forma más fácil es verificar el divisor antes de la operación de división y mostrar un mensaje sobre el valor del parámetro incorrecto:

nulo OnStart () {int a = 0, b = 0, c; si (b! = 0) {c = a / b; Impresión (c); } else {Imprimir ("Error: b = 0"); volver }}

Como resultado, no se produce un error crítico, pero se muestra un mensaje sobre el valor incorrecto del parámetro y la operación finaliza:

Usando 0 en lugar de NULL para el personaje actual

En la versión anterior del compilador, se permitía usar 0 (cero) como argumento en funciones que requerían un instrumento financiero.

Por ejemplo, el valor del indicador técnico de Media Móvil para el símbolo actual podría solicitarse de la siguiente manera:

AlligatorJawsBufferi = iMA (0,0,13,8, MODE_SMMA, PRICE_MEDIAN, i); // mal

En el nuevo compilador, para indicar el carácter actual, debe especificar explícitamente NULL:

AlligatorJawsBufferi = iMA (NULL, 0.13.8, MODE_SMMA, PRICE_MEDIAN, i); // correcto

Además, el símbolo actual y el período del gráfico se pueden especificar utilizando las funciones Símbolo () y Período ().

AlligatorJawsBufferi = iMA (Symbol (), Period (), 13.8, MODE_SMMA, PRICE_MEDIAN, i); // correcto

Aún mejor, si usa las variables predefinidas _Symbol y _Period, se procesan más rápido:

AlligatorJawsBufferi = iMA (_Symbol, _Period, 13.8, MODE_SMMA, PRICE_MEDIAN, i); // correcto

Cadenas Unicode y su uso en DLL

Las cadenas son una secuencia de caracteres Unicode. Tenga en cuenta este hecho y use las funciones apropiadas de Windows. Por ejemplo, cuando use las funciones de biblioteca wininet.dll en lugar de InternetOpenA () e InternetOpenUrlA (), debe llamar a InternetOpenW () e InternetOpenUrlW (). Al pasar cadenas a una DLL, use la estructura MqlString:

#pragma pack (push, 1) struct MqlString {int size; // entero de 32 bits, 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; // entero de 32 bits, reservado, no usar}; #pragma pack (pop, 1)

Compartir archivos

Al abrir archivos, debe especificar explícitamente los indicadores FILE_SHARE_WRITE y FILE_SHARE_READ para compartir.

Si están ausentes, el archivo se abrirá en modo exclusivo, lo que no permitirá que nadie más lo abra hasta que el monopolista lo cierre.

Por ejemplo, cuando trabaje con gráficos sin conexión, debe especificar explícitamente los indicadores compartidos:

// 1er cambio: agregue indicadores de acciones ExtHandle = FileOpenHistory (c_symbol + i_period + ". Hst", FILE_BIN | FILE_WRITE | FILE_SHARE_WRITE | FILE_SHARE_READ);

Función de conversión de fecha y hora

Tenga en cuenta que la conversión de un tipo de fecha y hora en una cadena depende del modo de compilación:

datetime date = D'2014.03.05 15:46:58 '; string str = "mydate =" + fecha; // --- str = "mydate = 1394034418" - sin la directiva estricta #property // --- str = "mydate = 2014.03.05 15:46:58" - con la directiva estricta #property

Por ejemplo, un intento de trabajar con archivos cuyo nombre contiene dos puntos dará como resultado un error.

Manejo de errores de tiempo de ejecución

Dado que ningún experto en comercio puede prescindir de las funciones integradas definidas por el usuario, en primer lugar trataremos de simplificar nuestra vida al analizar los errores devueltos por estas funciones.

Algunas bibliotecas están disponibles en el conjunto "listo para usar" para simplificar la redacción de asesores, incluidos aquellos para trabajar con errores. Se almacenan en la carpeta MQL4 / Include:

Necesitaremos dos bibliotecas:

  • stderror.mqh: contiene constantes para el número de cada error;
  • stdlib.mqh: contiene varias funciones auxiliares, incluida la función de devolver la descripción del error como una cadena:
string ErrorDescription (int error_code)

Por lo tanto, conectaremos ambas bibliotecas a nuestro proyecto:

#include #include 

Las descripciones de error están en el archivo MQL4 / Library / stdlib.mql4 y están en inglés. Por lo tanto, si está en contra de los idiomas extranjeros, siempre puede reescribir las descripciones en su idioma nativo.

Otra función incorporada que necesitamos es GetLastError (). Es ella quien devuelve los códigos de error en forma de un entero (int), que luego procesaremos. Los códigos de error y sus descripciones en ruso se pueden encontrar en el manual mql4 de MetaQuotes. Desde allí, puede tomar información para traducir el archivo stdlib.mql4 al ruso.

Ahora que hemos conectado las bibliotecas necesarias, consideraremos los resultados de las funciones directamente relacionadas con las operaciones comerciales, ya que ignorar el mal funcionamiento de estas funciones puede tener consecuencias críticas para el bot.

Desafortunadamente, usando MQL4, no puede escribir una biblioteca generalizada para manejar todas las posibles situaciones de error. En cada caso, tendrá que manejar los errores por separado. Pero no todo es tan malo: no es necesario procesar muchos errores, es suficiente para eliminarlos en la etapa de desarrollo y prueba de un experto, aunque para esto necesita saber a tiempo sobre su presencia.

Por ejemplo, considere dos errores típicos para los expertos en MQL4:

  1. Error 130 - ERR_INVALID_STOPS
  2. Error 146 - ERR_TRADE_CONTEXT_BUSY

Uno de los casos en que se produce el primer error es el intento del experto de colocar una orden pendiente demasiado cerca del mercado. Su presencia puede afectar gravemente el rendimiento de los expertos en algunos casos. Por ejemplo, supongamos que un experto, después de haber abierto una posición rentable, obtiene ganancias cada 150 puntos. Si el siguiente intento provoca un error de 130, y el precio vuelve irremediablemente al nivel de detención anterior, el experto puede privarlo de una ganancia legítima. A pesar de la posibilidad de tales consecuencias, este error puede eliminarse fundamentalmente al finalizar el código experto para que tenga en cuenta la distancia mínima permitida entre el precio y las paradas.

El segundo error relacionado con la ocupación del contexto comercial del terminal no puede eliminarse por completo. Cuando varios expertos trabajan en el mismo terminal, siempre es posible que uno de ellos intente abrir una posición mientras el otro sigue haciendo lo mismo. Por lo tanto, dicho error siempre debe manejarse.

Por lo tanto, siempre debemos saber si alguna de las funciones incorporadas utilizadas devuelve un error mientras el EA está funcionando. Esto se puede lograr utilizando la siguiente función auxiliar simple:

vacío logError (string functionName, string msg, int errorCode = -1) {Print ("ERROR: in" + functionName + "()"); Imprimir ("ERROR:" + msg); int err = GetLastError (); if (errorCode! = -1) {err = errorCode; } if (err! = ERR_NO_ERROR) {Print ("ERROR: code =" + err + "-" + ErrorDescription (err)); }}

Lo usaremos de la siguiente manera:

void openLongTrade () {int ticket = OrderSend (Symbol (), OP_BUY, 1.0, Ask + 5, 5, 0, 0); if (ticket == -1) {logError ("openLongTrade", "no se pudo abrir el pedido"); }}

Por supuesto, este es un ejemplo simplificado. Para escribir funciones más competentes de abrir, cerrar y modificar órdenes, vea esta lección.

El primer parámetro para la función logError () es el nombre de la función en la que se detectó el error, en nuestro ejemplo, en la función openLongTrade (). Si nuestro experto llama a la función OrderSend () en varios lugares, esto nos permitirá determinar exactamente en cuál de ellos ocurrió el error. El segundo parámetro pasa la descripción del error para que pueda comprender exactamente dónde se detectó el error dentro de la función openLongTrade (). Esto puede ser una breve descripción del error o una descripción más detallada de los valores de todos los parámetros pasados ​​a la función incorporada.

Prefiero la última opción, ya que si se produce un error, puede obtener de inmediato toda la información necesaria para el análisis. Por ejemplo, supongamos que antes de llamar a OrderSend (), el precio actual tenía que desviarse mucho del último precio conocido. Como resultado, se producirá un error durante la ejecución de este ejemplo, y las siguientes líneas aparecerán en el registro experto:

ERROR: en openLongTrade () ERROR: no se pudo abrir el pedido ERROR: código = 138 - requote

Es decir, verá de inmediato:

  • en qué función se produjo el error;
  • a qué se refiere (en este caso, un intento de abrir una posición);
  • qué error ocurrió (código de error y su descripción).

Ahora considere el tercer parámetro opcional para la función logError (). Es necesario en aquellos casos en que deseamos procesar un tipo específico de error, e informaremos sobre el resto en el protocolo del trabajo del experto, como antes:

void updateStopLoss (double newStopLoss) {bool modified = OrderModify (OrderTicket (), OrderOpenPrice (), newStopLoss, OrderTakeProfit (), OrderExpiration ()); if (! modificado) {int errorCode = GetLastError (); if (errorCode! = ERR_NO_RESULT) {logError ("updateStopLoss", "no se pudo modificar el orden", errorCode); }}}

Aquí es donde se llama a la función incorporada OrderModify () en la función updateStopLoss (). Esta función es ligeramente diferente en términos de manejo de errores de OrderSend (). Si ninguno de los parámetros del orden a cambiar difiere de sus parámetros actuales, entonces la función devolverá un error ERR_NO_RESULT. Si en nuestro experto tal situación es permisible, entonces deberíamos ignorar específicamente este error. Para hacer esto, analizamos el valor devuelto por GetLastError (). Si ocurre un error con el código ERR_NO_RESULT, entonces no enviamos nada al protocolo.

Sin embargo, si ocurrió otro error, es necesario informarlo por completo, como lo hicimos antes. Es por eso que guardamos el resultado de la función GetLastError () en una variable intermedia y le pasamos el tercer parámetro a la función logError (). El hecho es que la función incorporada GetLastError () restablece automáticamente el código del último error después de su llamada. Si no pasamos el código de error explícitamente a logError (), entonces un error con el código 0 y una descripción de "sin error" se reflejaría en el protocolo.

Se deben realizar acciones similares al procesar otros errores, por ejemplo, cotizaciones. La idea principal es manejar solo los errores que necesitan ser procesados, y pasar el resto a la función logError (). Entonces siempre estaremos al tanto de si ocurrió un error inesperado durante el trabajo del experto. Después de analizar los registros, podemos decidir si este error requiere un procesamiento por separado o si se puede eliminar al finalizar el código experto. Este enfoque a menudo simplifica significativamente la vida y reduce el tiempo que lleva lidiar con los errores.

Diagnóstico de errores lógicos.

Los errores lógicos en el código experto pueden causar muchos problemas. La falta de la posibilidad de depuración paso a paso de expertos hace que la lucha contra tales errores no sea una tarea muy agradable. La herramienta principal para diagnosticar esto en este momento es la función integrada Print (). Al usarlo, puede imprimir los valores actuales de variables importantes, así como registrar el trabajo del experto directamente en el terminal durante la prueba. Al depurar a un experto durante las pruebas con visualización, la función Comentario () también puede ayudar, que muestra mensajes en un gráfico. Como regla general, asegurándose de que el experto no funcione según lo previsto, debe agregar llamadas temporales a la función Print () y registrar el estado interno del experto en los supuestos lugares donde ocurrió el error.

Sin embargo, para detectar situaciones de error complejas, a veces tiene que agregar docenas de tales llamadas a la función Print (), y después de encontrar y solucionar el problema, debe eliminarlas o comentarlas para que el código experto no esté abarrotado y su prueba no se ralentice. La situación empeora si la función Print () ya se usa en el código experto para registrar periódicamente varios estados. Entonces, la eliminación de llamadas temporales a Print () no se puede realizar simplemente buscando la frase 'Print' en el código experto. Debe pensarlo para no eliminar llamadas útiles a esta función.

Por ejemplo, al registrar los errores de las funciones OrderSend (), OrderModify () y OrderClose (), es útil imprimir el valor actual de las variables Bid y Ask en el protocolo. Esto facilita el reconocimiento de las causas de errores como ERR_INVALID_STOPS y ERR_OFF_QUOTES.

Para resaltar tales hallazgos de diagnóstico en el protocolo, recomiendo usar la siguiente función auxiliar:

void logInfo (string msg) {Print ("INFO:" + msg); }

Esto es deseable por varias razones. En primer lugar, ahora esas llamadas no se verán al buscar 'Imprimir' en el código experto, porque buscaremos logInfo. En segundo lugar, esta función tiene otra característica útil, de la que hablaremos más adelante.

Agregar y eliminar llamadas de diagnóstico temporales a la función Print () nos lleva un tiempo valioso. Por lo tanto, propongo considerar otro enfoque que sea efectivo para detectar errores lógicos en el código y nos permita ahorrar un poco de tiempo. Considere la siguiente función simple:

void openLongTrade (double stopLoss) {int ticket = OrderSend (Symbol (), OP_BUY, 1.0, Ask, 5, stopLoss, 0); if (ticket == -1) {logError ("openLongTrade", "no se pudo abrir el pedido"); }}

En este caso, dado que estamos abriendo una posición larga, es bastante obvio que durante el funcionamiento normal del experto, el valor del parámetro stopLoss nunca será mayor o igual que el precio de oferta actual. Es decir, cuando se llama a la función openLongTrade (), la condición stopLoss <Bid siempre se cumple. Como sabemos sobre esto incluso en la etapa de escribir la función en cuestión, podemos usarla de la siguiente manera:

vacío openLongTrade (doble stopLoss) {afirmar ("openLongTrade", stopLoss <Oferta, "stopLoss <Oferta"); int ticket = OrderSend (Symbol (), OP_BUY, 1.0, Ask, 5, stopLoss, 0); if (ticket == -1) {logError ("openLongTrade", "no se pudo abrir el pedido"); }}

Es decir, registramos nuestra declaración en el código usando la nueva función auxiliar afirmar (). La función en sí parece bastante simple:

declaración nula (stringName de función, aserción bool, descripción de cadena = "") {if (! aserción) {Print ("ASSERT: en" + functionName + "() -" + descripción); }}

El primer parámetro de la función es su nombre, en el que se verifica nuestra condición (por analogía con la función logError ()). El segundo parámetro pasa el resultado de verificar esta condición. Y el tercer parámetro es su descripción. Como resultado, si no se cumple la condición esperada, se mostrará la siguiente información en el protocolo:

  • el nombre de la función en la que se violó la condición;
  • Descripción de la condición violada.

Como descripción, puede pasar, por ejemplo, la condición en sí, o puede mostrar una descripción más detallada que contenga los valores de las variables controladas en el momento de verificar la condición, si esto ayuda a comprender las causas del error.

Por supuesto, el ejemplo considerado se simplifica al máximo. Pero, espero, la idea principal se refleja bastante bien. En el proceso de creación de la funcionalidad del experto, somos conscientes de cómo debería funcionar y qué estados y parámetros de entrada de las funciones son válidos y cuáles no. Arreglando esto en el código experto utilizando la función afirmar (), obtenemos información valiosa sobre el lugar en el que se viola la lógica del trabajo del experto. Además, nos liberamos parcialmente de la necesidad de agregar y eliminar llamadas temporales a la función Print (), ya que la función afirmar () emite mensajes de diagnóstico al protocolo solo cuando se detectan inconsistencias en las condiciones esperadas.

Otro truco útil es usar esta función antes de cada operación de división. El hecho es que a veces como resultado de uno u otro error lógico, a veces se produce la división por cero. El trabajo del experto en este caso finaliza y solo aparece una línea en el protocolo con un diagnóstico triste: "división cero". Averiguar exactamente dónde ocurrió este error si la operación de división se usa repetidamente en el código es bastante difícil. La función afirmar () ayudará aquí. Insertamos los controles correspondientes antes de cada operación de división:

afirmar ("buildChannel", distancia> 0, "distancia> 0"); doble pendiente = delta / distancia;

Y ahora, en el caso de la división por cero, será suficiente solo mirar en los registros para averiguar en qué lugar exacto ocurrió el error.

Manejo del estado

Mientras el experto trabaja en su cuenta, pueden surgir algunas situaciones que no son errores, los llamados estados expertos. Dichos estados no son errores, pero aún así deben registrarse. Las funciones especiales del lenguaje mql4 ayudan en esto.

La función IsExpertEnabled () devuelve información sobre la capacidad de ejecutar expertos. La función devolverá verdadero si los expertos pueden ejecutarse en la terminal del cliente, de lo contrario, devuelve falso. Si se devuelve falso, será útil notificar al usuario con una solicitud para habilitar la configuración adecuada. Un ejemplo:

nulo OnStart () {if (! IsExpertEnabled () {// los asesores no pueden intercambiar Alert ("¡Atención! Presione el botón" Expert Advisors "en MT4");} // algoritmo de trabajo del asesor return;}

Si el experto utiliza bibliotecas externas, la función IsLibrariesAllowed () es útil. Devuelve verdadero si el experto puede llamar a la función de biblioteca; de lo contrario, devuelve falso.

Si la biblioteca tiene la forma de un archivo dll, la función IsDllsAllowed () es útil. También será útil verificar si generalmente es posible comerciar con la ayuda de expertos que usan la función IsTradeAllowed ().

Si desea averiguar si la cuenta es demo o real, puede usar la función IsDemo ().

Todas las comprobaciones anteriores deben realizarse en la función OnInit ().

Por supuesto, vale la pena verificar periódicamente la conexión con el servidor. La función IsConnected () ayudará.

Las siguientes tres funciones ayudarán a determinar en qué modo está el EA. Si IsOptimisation () devuelve verdadero, se realiza la optimización, si IsTesting (), luego prueba, IsVisualMode () - prueba en modo de visualización. Para cada una de estas opciones, el asesor puede tener su propia lógica. Por ejemplo, para el modo de visualización, puede mostrar algo en el gráfico (y no mostrarlo en otros modos para ahorrar recursos). En el modo de prueba, puede mostrar información de depuración, en el modo de optimización, aligerar el código tanto como sea posible para ahorrar tiempo.

Y la última función es IsTradeContextBusy (). Volverá verdadero si el hilo para realizar operaciones comerciales está ocupado. Esto puede ser útil cuando un experto realiza operaciones comerciales. Puede usar la función Suspender para esperar un momento e intentar nuevamente.

Otra característica útil es UninitializeReason ()

int deinit () {switch (UninitializeReason ()) {case REASON_CHARTCLOSE: case REASON_REMOVE: CleanUp (); romper // recursos limpios y gratuitos. case REASON_RECOMPILE: case REASON_CHARTCHANGE: case REASON_PARAMETERS: case REASON_ACCOUNT: StoreData (); romper // preparación para reiniciar. } // ...}

También puede registrar el motivo para que el asesor salga.

Códigos de los errores más comunes y su probable solución.

Número de errorValorEl problemaSolución
4, 146Servidor comercial ocupadoEl asesor dio demasiadas órdenes al mismo tiempo o sin esperar una respuesta del servidor, durante la operación: el asesor intenta enviar una nueva ordenReiniciar el terminal u optimizar el código del asesor utilizando funciones de manejo de errores
8, 141Solicitudes demasiado frecuentesRazones de error anteriores en una solicitud muy frecuenteSolución similar
129Precio incorrectoEl precio al que está intentando abrir una posición (COMPRAR o VENDER) es incorrectoCOMPRAR debe ser abierto por Ask y cerrado por BID;
VENTA necesita ser abierta por BID y cerrada por ASK
130, 145Pies equivocadosStop loss, take profit o el nivel de apertura de un pendiente o limitador son incorrectos.
Las paradas están demasiado cerca del precio.
Su cuenta se abre en el grupo ECN (ETSN) o NDD (NDD), lo que le impide colocar paradas inmediatamente
Verifique los valores de sus stop loss, obtenga ganancias, verifique el nivel de stop mínimo para su instrumento con un corredor, cuando establezca stop, observe el nivel de distancia mínima. Un asesor bien escrito debe tener funciones para trabajar en cuentas ECN y NDD; esto sucede modificando el pedido una vez abierto
131Volumen equivocadoLote incorrecto al abrir un acuerdo, o menos del mínimo (más del máximo). La profundidad de bits de un lote también puede diferir de la de un brokerVerifique la apertura correcta del lote, estudie las especificaciones del contrato y lea los términos de intercambio en su DC, verifique el lote mínimo y máximo en su DC y en su cuenta
132El mercado esta cerradoEl mercado está cerrado los fines de semana.Intenta contactar con el mercado después del fin de semana
133Sin comercioSin comercio en este momentoEstá prohibido operar con este par de divisas, en un momento determinado o en general. A menudo, los corredores tienen un descanso de unos minutos a la medianoche.
134No hay suficiente dinero para completar la operación.El lote que está intentando abrir es demasiado grande, no tiene suficiente margenVerifique el nivel de fondos disponibles y calcule los fondos que necesita para abrir un lote, controle el nivel de sus fondos disponibles
135-138El precio ha cambiadoRequote, mercado demasiado rápido (noticias), Broker o DC no le permite colocar una posición al precio declaradoNo opere en esos momentos, aumente el nivel de deslizamiento, pero recuerde que esto implica la apertura de posiciones que no están al precio que usted indicó. Proporcione una función de manejo de errores y el número de intentos de abrir posiciones en el EA
147El broker prohíbe el uso de la fecha de vencimiento de la ordenSu asesor o usted está tratando de establecer la fecha de vencimiento de una orden pendienteEn el Asesor experto, en la función OrderSend, establezca el parámetro de caducidad en 0 (cero). No establezca una fecha de vencimiento
148El número de órdenes abiertas y pendientes ha alcanzado el límite establecido por el corredorEl número máximo de órdenes y posiciones abiertas ha alcanzado el límite establecido por el corredorEliminar o cerrar parte de las posiciones. Detener el proceso de apertura de nuevos puestos.
4012, 4013Resto de división por ceroEstás intentando dividir el número por 0 (cero)Verifique el código del asesor en busca de un error, o verifique todos los valores de las funciones de MarketInfo al momento de devolver 0, a veces cuando MarketInfo (Symbol (), MODE_SPREAD), no se devuelve el spread, sino 0 (para corredores con un spread flotante)
4017Llamadas DLL no permitidasLlamadas DLL prohibidas en su terminalPermitir llamada DLL a través del menú - Servicio - Configuración - Asesor - Permitir llamada DLL
4018, 4019No se puede cargar la bibliotecaLa biblioteca está dañada o su llamada falla, tal vez no se encuentreComprobar DLL
4020No se permiten llamadas a funciones de biblioteca externa.Está prohibido llamar a funciones de expertos externos en su terminalPermitir funciones de llamada a través de Menú - Servicio - Configuración - Asesor - Permitir llamar a expertos externos
4103No se puede abrir el archivoEste archivo no existe o está bloqueado por otro proceso.Verifica el archivo especificado. Compruebe si el archivo está bloqueado por el sistema antivirus, si el modo de escritura-lectura del archivo está permitido
4106Personaje desconocidoNingún símbolo en el resumen del mercadoEn la descripción general del mercado, haga clic con el botón derecho, muestre todos los símbolos. Verifique el nombre del símbolo en el asesor y su presencia en el resumen del mercado. Algunos asesores usan nombres claros sin sufijos, y los corredores establecen intencionalmente sufijos, por ejemplo EURUSDx donde x es el sufijo
4108Número de boleto inválidoEl ticket de pedido que selecciona el experto no existe. Un experto está tratando de elegir un boleto, pero esta orden fue cerrada por otro asesor o por manos. Al intentar ejecutar una orden sobre una orden, el boleto fue ejecutado y cerrado por un corredorSi este error aparece con mucha frecuencia, 100-1000 veces por minuto, verifique las funciones de su asesor. Desactive a otros asesores o configúrelos para que no entren en conflicto, no cierre el pedido con las manos cuando el experto realice la operación. A veces esto sucede cuando varios asesores usan el mismo MagicNumber.
4109Comercio no permitidoEl asesor tiene prohibido comerciar, en el gráfico una triste sonrisa o una cruzActive la casilla de verificación "Permitir que el asesor negocie" en el depósito al instalar el asesor, o en el menú - servicio - configuración - asesores
4110, 4111Posiciones largas / cortas no permitidasEn la configuración del asesor, en la pestaña General, el tipo de puestos no está permitidoEn la pestaña General, al instalar el asesor, hay una variedad de posiciones que se pueden abrir

Conclusión

Las funciones auxiliares consideradas y los trucos simples pueden simplificar y acelerar significativamente el proceso de detección y corrección de errores en el código de expertos comerciales escrito en el lenguaje de programación MQL4. La escritura adecuada de código y funciones para registrar y rastrear el trabajo del asesor acelera significativamente el proceso de su desarrollo.

Deja Tu Comentario