- Diseño de programas MQL de varios tipos
- Hilos
- Visión general de las funciones de gestión de eventos
- Funciones de inicio y parada de programas de varios tipos
- Eventos de referencia de indicadores y Asesores Expertos: OnInit y OnDeinit
- Función principal de scripts y servicios: OnStart
- Eliminación programática de Asesores Expertos y scripts: ExpertRemove
Diseño de programas MQL de varios tipos
El tipo de programa es una propiedad fundamental en MQL5. A diferencia de C++ u otros lenguajes de programación de propósito general, en los que cualquier programa puede desarrollarse en direcciones arbitrarias, por ejemplo añadiendo una interfaz gráfica o cargando datos desde un servidor a través de la red, los programas MQL se dividen en determinados grupos en función de su finalidad. Por ejemplo, el análisis técnico de series temporales con visualización se realiza mediante indicadores, pero éstos no permiten realizar operaciones de trading. A su vez, las funciones de la API de trading están disponibles para los Asesores Expertos, pero carecen de búferes de indicadores (arrays para dibujar líneas).
Por lo tanto, al resolver un problema específico aplicado, el desarrollador debe descomponerlo en partes, y la funcionalidad de cada parte debe encajar en la especialización de un tipo separado. Por supuesto, en casos sencillos basta con un único programa MQL, pero a veces la solución técnica óptima no es evidente. Por ejemplo, ¿cómo implementaría el trazado de un gráfico Renko: como un indicador, como un símbolo personalizado generado por el servicio, o puede que como cálculos específicos directamente en el Asesor Experto de trading? Todas las opciones son posibles.
El tipo de programa MQL se caracteriza por varios factores.
En primer lugar, cada tipo de programa tiene una carpeta separada en el directorio de trabajo MQL5. Ya hemos mencionado este hecho en la introducción a la Parte 1 y enumerado las carpetas. Así, para los indicadores, Asesores Expertos, scripts y servicios, las carpetas designadas son Indicators, Experts, Scripts y Services, respectivamente. La subcarpeta Libraries está reservada para las bibliotecas de la carpeta MQL5. En cada una de ellas, puede organizar un árbol de carpetas anidadas de configuración arbitraria.
El archivo binario (el programa terminado con la extensión ex5), que es el resultado de compilar el archivo mq5, se genera en el mismo directorio que el archivo fuente mq5. No obstante, debemos mencionar también los proyectos en MetaEditor (archivos con la extensión mqproj), que analizaremos en el capítulo Proyectos. Cuando se desarrolla un proyecto, se crea un producto acabado en un directorio junto al proyecto. Al crear un programa desde el Asistente MQL5 en MetaEditor (comando Archivo -> Nuevo), el archivo fuente se coloca por defecto en la carpeta correspondiente al tipo de programa. Si copia accidentalmente un programa en el directorio equivocado, no ocurrirá nada terrible: no se convertirá, por ejemplo, de un Asesor Experto en un indicador, o viceversa. Se puede mover a la ubicación deseada directamente en el editor, dentro de la ventana Navigator o en un administrador de archivos externo. En Navigator, cada tipo de programa se muestra con un icono especial.
La ubicación de un programa dentro del directorio MQL5 en una subcarpeta dedicada a un tipo concreto no determina el tipo de este programa MQL en particular. El tipo se determina en función del contenido del archivo ejecutable, que, a su vez, es formado por el compilador a partir de directivas de propiedades y sentencias del código fuente.
La jerarquía de carpetas por tipos de programa se utiliza por comodidad. Se recomienda ceñirse a ella, salvo cuando se trate de un grupo de proyectos relacionados (con programas de distinto tipo), que es más lógico almacenar en un directorio distinto.
En segundo lugar, cada tipo de programa se caracteriza por ser compatible con un conjunto limitado y específico de eventos del sistema que activan el programa. Obtendremos una visión general de las funciones de gestión de eventos en una sección aparte. Para recibir eventos de un tipo específico en un programa es necesario describir una función manejadora con un prototipo predefinido (nombre, lista de parámetros, valor de retorno).
Por ejemplo, ya hemos visto que, en los scripts y servicios, el trabajo se inicia en la función OnStart, y puesto que es la única que existe, puede denominarse el «punto de entrada» principal a través del cual el terminal transfiere el control al código de la aplicación. En otros tipos de programas, la situación es algo más complicada. En general, observamos que un tipo de programa se caracteriza por un determinado conjunto de manejadores, algunos de los cuales pueden ser obligatorios y otros opcionales (pero, al mismo tiempo, inaceptables para otros tipos de programas). En concreto, un indicador requiere la función OnCalculate (sin ella, un indicador no compilará y el compilador generará un error). Sin embargo, esta función no se utiliza en los Asesores Expertos.
En tercer lugar, algunos tipos de programas requieren directivas especiales de #property. En el capítulo Propiedades generales de los programas vimos ya directivas que pueden utilizarse en todo tipo de programas. Sin embargo, existen otras directivas especializadas. Por ejemplo, en las tareas con servicios, que hemos mencionado, nos encontramos con la directiva #property service, que convierte el programa en un servicio. Sin ella, ni siquiera colocando el programa en la carpeta Services podrá ejecutarse en segundo plano.
Del mismo modo, la directiva #property library desempeña un papel definitorio en la creación de bibliotecas. Todas estas propiedades de las directivas se abordarán en las secciones correspondientes a cada tipo de programa.
La combinación de directivas y manejadores de eventos se tiene en cuenta a la hora de establecer un tipo de programa MQL en el siguiente orden (de arriba a abajo hasta la primera coincidencia):
- indicador: la presencia del manejador OnCalculate
- biblioteca: #property library
- script: la presencia del manejador OnStart y la ausencia de #property service
- servicio: la presencia del manejador OnStart y #property service
- Asesor Experto: la presencia de cualquier otro manejador
Un ejemplo del efecto que estas propiedades tienen sobre el compilador se ofrecerá en la sección Visión general de las funciones de gestión de eventos.
Por todo lo anterior, hay que tener en cuenta un punto más. El tipo de programa viene determinado por el módulo principal compilado: un archivo con la extensión mq5, en el que se pueden incluir otras fuentes de otros directorios mediante la directiva #include. Todas las funciones incluidas de esta forma se tienen en cuenta al mismo nivel que las que están presentes directamente en el archivo mq5 principal.
Por otra parte, las directivas #property sólo tienen efecto cuando se colocan en el archivo mq5 principal compilado. Si las directivas aparecen en archivos incluidos en el programa utilizando #include, serán ignoradas.
El archivo principal mq5 no tiene que contener literalmente funciones manejadoras de eventos. Es perfectamente aceptable colocar parte o todo el algoritmo en archivos de encabezado mqh y luego incluirlos en uno o más programas. Por ejemplo, podemos implementar el manejador OnStart con un conjunto de acciones útiles en un archivo mqh y utilizarlo a través de #include dentro de dos programas separados: un script y un servicio.
Mientras tanto, fijémonos en que la presencia de manejadores de eventos comunes no es el único motivo para separar fragmentos de algoritmos comunes en un archivo de encabezado. Puede utilizar la misma fórmula de cálculo, por ejemplo, en un indicador y en un Asesor Experto, dejando sus manejadores de eventos en los módulos principales del programa.
Aunque es habitual referirse a los archivos de inclusión como archivos de encabezado y darles la extensión mqh, esto no es técnicamente necesario. Es bastante aceptable (aunque no recomendable) incluir otro archivo mq5 o, por ejemplo, un archivo txt en un archivo mq5. Pueden contener algún código heredado o, digamos, la inicialización de ciertos arrays con constantes. La inclusión de otro archivo mq5 no lo convierte en el principal.
Debe asegurarse de que sólo se introduzcan en el programa las funciones de gestión de eventos características del tipo de programa específico, y de que no haya duplicados entre ellas (como sabe, las funciones se identifican mediante una combinación de nombres y una lista de parámetros: la sobrecarga de funciones sólo se permite con un conjunto diferente de parámetros). Esto suele conseguirse utilizando varias directivas del preprocesador. Por ejemplo, si definimos la macro #define OnStart OnStartPrevious antes de incluir un archivo de script mq5 de terceros en algunos de nuestros programas, convertiremos realmente la función OnStart descrita en él en OnStartPrevious, y podremos llamarla como de costumbre desde nuestros propios manejadores de eventos.
Sin embargo, este enfoque sólo tiene sentido en casos excepcionales, cuando el código fuente del archivo mq5 incluido no puede modificarse por algún motivo; en concreto, cuando no puede estructurarse con la selección de algoritmos de interés en funciones o clases en archivos de encabezado independientes.
Según el principio de interacción con el usuario, los programas MQL pueden dividirse en interactivos y funcionales.
Los programas interactivos (indicadores y Asesores Expertos) pueden procesar eventos, los cuales se producen en el entorno de software en respuesta a acciones del usuario, tales como pulsar botones en el teclado, mover el ratón, cambiar el tamaño de la ventana, así como muchos otros eventos, por ejemplo, relacionados con la recepción de datos de cotización o con acciones del temporizador.
Los programas de utilidades (servicios y scripts) se guían únicamente por las variables de entrada establecidas en el momento del lanzamiento, y no responden a eventos del sistema.
Aparte de todos los tipos de programas están las bibliotecas. Siempre se ejecutan como parte de otro tipo de programa MQL (uno de los cuatro principales), por lo que no tienen ninguna característica o comportamiento distintivo. En concreto, no pueden recibir directamente eventos del terminal y no disponen de hilos propios (véase la sección siguiente). La misma biblioteca puede estar conectada a muchos programas, y esto ocurre de forma dinámica en el momento de lanzar cada programa padre. En la sección sobre bibliotecas aprenderemos a describir la API exportada de una biblioteca y a importarla a un programa padre.