English Русский Deutsch 日本語 Português
preview
Introducción a MQL5 (Parte 3): Estudiamos los elementos básicos de MQL5

Introducción a MQL5 (Parte 3): Estudiamos los elementos básicos de MQL5

MetaTrader 5Asesores Expertos | 19 julio 2024, 15:50
28 0
Israel Pelumi Abioye
Israel Pelumi Abioye

Introducción

¡Bienvenidos de nuevo, compañeros tráders y entusiastas del trading algorítmico! Hoy iniciamos el tercer capítulo de nuestro viaje a través de MQL5. Ahora nos encontraremos con un cruce entre teoría y práctica: esta vez nos ocuparemos de los arrays, las funciones definidas por el usuario, los preprocesadores y el procesamiento de eventos. El propósito de esta serie de artículos será ofrecer a cada lector, independientemente de su experiencia en programación, una comprensión profunda de los elementos fundamentales de MQL5.

Dicho principio de compromiso con una presentación clara y concisa es lo que confiere a estos artículos gran parte de su utilidad. Los artículos explican cada línea de código, garantizando que no solo se aprendan los conceptos, sino que también se vean sus aplicaciones prácticas en el mundo real. Me gustaría crear un espacio de información de este tipo en el que el lenguaje MQL5 resulte accesible a todo el mundo. Obviamente, cualquier pregunta será bienvenida. Esto es solo el principio: comenzaremos sentando las bases para un estudio más profundo de la programación MQL5. Por ello, resulta esencial estar lo más atento posible y no saltarse las descripciones de estos artículos.

Así que centrémonos y manos a la obra. Ahora empezaremos a convertir los conocimientos teóricos en conocimientos prácticos de forma accesible para todos. Todo está listo, el código está listo, así que comencemos a investigar.

En el presente artículo trataremos los temas que siguen:

  • Arrays
  • Funciones definidas por el usuario
  • Preprocesadores
  • Procesamiento de eventos

Antes de iniciar nuestro viaje práctico a las profundidades de la programación MQL5 (Parte 3), vamos a repasar el material que hemos aprendido. A continuación encontrará un breve vídeo que resume los conceptos clave y los aspectos más destacados de la segunda parte de esta serie. Primero repasaremos lo que ya hemos aprendido y luego abordaremos nuevos temas.



1. Arrays

¿Qué son los arrays?

Los arrays son conceptos de programación fundamentales que ofrecen formas eficaces de organizar y almacenar datos y permiten almacenar y organizar varios valores bajo un mismo nombre de variable. En lugar de crear variables separadas para cada dato, podemos utilizar un array para almacenar una colección de elementos del mismo tipo. El acceso y gestión de los elementos del array se realiza con la ayuda de índices.

Analogía

Un array es como una caja en la que podemos almacenar un montón de objetos similares. Imagine que tiene una fila de cajas y cada caja tiene un número diferente. Con estos números podrá encontrar y organizar fácilmente sus cosas. Supongamos que tiene una fila de casillas numeradas 0, 1, 2, 3, etc. (la numeración empieza siempre en 0). Podrá guardar algo especial en cada caja, como sus juguetes favoritos o golosinas. Si quiere jugar con un juguete concreto, solo tendrá que decir el número de la caja. El número le llevará a la caja correcta. Así, en programación, un array es como una serie de cajas numeradas. Permiten almacenar muchos artículos similares en un mismo lugar y encontrarlos fácilmente mediante números especiales. Digamos que es una forma cómoda y eficaz de sistematización.

1.1. Cómo declarar un array

En MQL5, un array se declara indicando su tipo de datos y su nombre, así como el tamaño del array (si lo hay).

Sintaxis básica:

data_type array_name[array_size];

  • data_type - tipo de datos que contendrá el array (int, double, string, etc.).
  • array_name - nombre del array.
  • array_size - tamaño o longitud del array, que determina cuántos elementos puede contener.
Ejemplo:
void OnStart()
  {

   int closePrices[5]; // An integer array with a size of 5, means the array has 5 integer elements
   double prices[10];  // A double-precision floating-point array with a size of 10

  }

1.2. Asignación de valores a un array

En MQL5, podemos asignar valores a un array durante su declaración o por separado después de su declaración. Veamos ambos métodos:

1.2.1. Asignación de valores después de la declaración

Aquí declararemos primero un array y luego le asignaremos valores a cada elemento por separado. Esto nos permite asignar valores dinámicamente durante la ejecución del programa o según determinadas condiciones. 

Ejemplo:

void OnStart()
  {

   int closePrices[5];
   closePrices[0] = 10;
   closePrices[1] = 20;
   closePrices[2] = 30;
   closePrices[3] = 40;
   closePrices[4] = 50;

  }

1.2.2. Asignación de valores durante la declaración

En este método, declararemos un array y especificaremos los valores dentro de llaves {}. El tamaño del array vendrá determinado por el número de valores especificados. En este ejemplo, el array closePrices se declarará con un tamaño de 5 y se inicializará con los valores 10, 20, 30, 40 y 50. Ambos métodos son aceptables y podremos elegir el que más nos guste o el que mejor se adapte a los requisitos de un programa concreto.

Ejemplo:

void OnStart()
  {

   int closePrices[5] = {10, 20, 30, 40, 50};

  }

1.3. Cómo acceder a los elementos de un array

En la mayoría de los lenguajes de programación, incluyendo MQL5, los índices de los arrays comienzan a partir de 0. Así, por defecto, al declarar un array y acceder a sus elementos, la cuenta empieza desde 0.

Figura 1: acceso a los elementos de un array

Ejemplo:

void OnStart()
  {
  
// Declare an integer array with a size of 5
   int closePrices[5];

// Assign values to array elements
   closePrices[0] = 10;
   closePrices[1] = 20;
   closePrices[2] = 30;
   closePrices[3] = 40;
   closePrices[4] = 50;
   
   // Access and print array elements
   Print("Element at index 0: ", closePrices[0]); // Output: 10
   Print("Element at index 1: ", closePrices[1]); // Output: 20
   Print("Element at index 2: ", closePrices[2]); // Output: 30
   Print("Element at index 3: ", closePrices[3]); // Output: 40
   Print("Element at index 4: ", closePrices[4]); // Output: 50

  }

Explicación:

Declaración de arrays:

int closePrices[5];

Esta línea declara un array de tipo entero llamado closePrices con un tamaño fijo de 5 elementos. En MQL5, los arrays podemos almacenar elementos del mismo tipo de datos, y la indexación comenzará desde 0, por lo que nuestro array tendrá índices de 0 a 4.

Asignación de valores a elementos del array:

   closePrices[0] = 10;
   closePrices[1] = 20;
   closePrices[2] = 30;
   closePrices[3] = 40;
   closePrices[4] = 50;

Aquí, los valores se asignarán a cada elemento del array utilizando índices. El array closePrices ahora contendrá valores de 10, 20, 30, 40 y 50 en las posiciones correspondientes.

Acceso y muestra de los elementos de arrays:

   Print("Element at index 0: ", closePrices[0]); // Output: 10
   Print("Element at index 1: ", closePrices[1]); // Output: 20
   Print("Element at index 2: ", closePrices[2]); // Output: 30
   Print("Element at index 3: ", closePrices[3]); // Output: 40
   Print("Element at index 4: ", closePrices[4]); // Output: 50
El ejemplo de muestra anterior representa cómo acceder a los valores almacenados en índices específicos de un array y cómo mostrar estos. Aquí se accederá a los índices de 0 a 4, mientras que los valores correspondientes se mostrarán en la consola.

Por lo tanto, hemos abarcado los fundamentos de los arrays en MQL5, utilizando la metáfora de los contenedores de datos organizados. En los ejemplos de código hemos visto cómo declarar, inicializar y acceder a los elementos de un array. Cabe señalar que MQL5 ya dispone de un conjunto de funciones para trabajar con arrays, pero hablaremos de estas funciones más adelante en otros artículos; por ahora bastará con lo básico.


2. Funciones definidas por el usuario

¿Cuáles son las funciones generales?

Las funciones personalizadas (funciones definidas por el usuario) son segmentos específicos de código que se crearán para realizar una tarea concreta o un conjunto de tareas en un lenguaje de programación. En el contexto de MQL5, que se usa para el comercio algorítmico, el programador escribe funciones personalizadas para encapsular una serie de acciones, cálculos u operaciones. Estas características permiten estructurar de forma modular nuestro código, haciéndolo más legible, reutilizable y fácil de mantener. En lugar de duplicar el mismo código en varios lugares de nuestro programa, podremos crear una función especial que encapsule esta lógica y luego llamarla cada vez que necesitemos realizar una tarea específica.

En el último artículo ya nos familiarizamos las funciones comunes Alert, Comment y Print. Ahora veremos cómo podemos ampliar las posibilidades de creación de funciones en MQL5. Del mismo modo que un profesional experimentado perfecciona sus herramientas, la creación de funciones personalizadas nos permitirá ajustar nuestro código para satisfacer los requisitos exactos de nuestras estrategias comerciales.

Analogía

Imagine que está en la cocina preparando una deliciosa comida (su código). Así, las funciones serán como recetas que ofrecen instrucciones paso a paso sobre cómo crear platos específicos. Veamos ahora algunos ejemplos de funciones personalizadas y comunes. En el ámbito de la cocina, podríamos considerar las funciones comunes como recetas conocidas publicadas en libros de cocina. Estas recetas son estándar y se usan a menudo, como la receta del arroz hervido o los huevos fritos. Desde el punto de vista de la programación, las funciones como Alert, Comment y Print son similares a estas populares recetas. Tienen objetivos concretos, están listas para funcionar y pueden utilizarse de inmediato.

Las funciones personalizadas, en cambio, serían como sus recetas familiares únicas y secretas, es decir, platos que usted ha creado teniendo en cuenta sus gustos y preferencias. Del mismo modo, en programación, las funciones definidas por el usuario serán conjuntos personalizados de instrucciones creadas por el programador para realizar tareas específicas. Las instrucciones incluirán una secuencia de pasos para conseguir un resultado concreto, como la receta secreta de la tarta de chocolate perfecta. La principal diferencia será el nivel de personalización. Las funciones comunes pueden compararse con las comidas precocinadas: son cómodas y rápidas de preparar, además de muy consumidas. Suponen una forma eficiente de realizar tareas cotidianas, como una cena preparada que se puede recalentar. En el lado opuesto están las funciones personalizadas que aportan precisión y personalización. Es como cocinar desde cero: puedes ajustar los ingredientes, los sabores y los métodos para adaptarlos a las necesidades únicas de su cocina.

2.1. Cómo crear una función

Crear una función en MQL5 incluye especificar el nombre de la función, definir sus parámetros (si los hay), indicar el tipo del valor retornado (si se retorna un valor) y proporcionar un bloque de código que componga el cuerpo de la función.

Primer paso. Definimos el objetivo de la función

Decida qué tarea o cálculo realizará la función. Esto le ayudará a seleccionar el nombre de la función, los parámetros de entrada y el tipo de valor retornado (si lo hay). Como ejemplo, crearemos una función que multiplicará 2 números.

Segundo paso. Declaramos una función

Declare la función con el nombre elegido, los parámetros de entrada y el tipo de valor retornado (si lo hay). La declaración deberá realizarse en el ámbito global, fuera de cualquier función o manejador de eventos concreto.

Nota. Los manejadores de eventos en MQL5 son funciones que reaccionan automáticamente a ciertos eventos, como los cambios en el precio del mercado (OnTick), la interacción con los gráficos (OnChartEvent) y los intervalos temporales (OnTimer). Se encargan de optimizar la ejecución del código basándose en eventos en tiempo real o programados en la plataforma MetaTrader 5. Hablaremos sobre ellos con más detalle en el futuro.

Sintaxis:

return_type FunctionName(parameter1_type parameter1, parameter2_type parameter2, ...) 
   {
    // Function body (code block)
    // Perform actions or calculations
    // Optionally, return a value using the 'return' keyword if the function has a return type
   }

Paso 3. Definimos el cuerpo de la función

Dentro de la función, añadiremos código que realice la tarea o cálculo necesario. Incluiremos todas las variables y la lógica necesarias.

Ejemplo:

double MyFunction(int param1, double param2)
     {
      // Function body: Calculate the product of the two parameters
      double result = param1 * param2;

      // Return the result
      return result;
     }

Explicación:

“double MyFunction(int param1, double param2)”:

  • Esta línea define una función llamada MyFunction. Admite dos parámetros: param1 de tipo int (entero) y param2 de tipo double (número de coma flotante). Se espera que la función retorne un valor de tipo double.

{:

  • La llave de apertura "{" marca el comienzo del cuerpo de la función, donde se encuentra el código real de la función.

“double result = param1 * param2;”:

  • Esta línea calcula el producto de param1 y param2, almacenando el resultado en una variable llamada result. Dado que tanto param1 como param2 son de tipo numérico, la operación de multiplicación (*) producirá un resultado de tipo double.

“return result;”:

  • Esta línea finalizará la función (MyFunction) y retornará el valor almacenado en la variable result al código que ha llamado a la función. Como el tipo de valor retornado de la función es double, esto significa que la persona que realiza la llamada esperará obtener un valor double.

“}”:

  • La llave de cierre "}" marca el final del cuerpo de la función.

Paso 4: Utilizamos la función

Llamaremos a la función en los lugares necesarios de nuestro código. La función puede utilizarse en manejadores de eventos, otras funciones o cualquier contexto adecuado.

Ejemplo:
void OnStart()
  {

// Calling the custom function
   double result = MyFunction(5, 3.5);

// Printing the result
   Print("The result is: ", result); // The output will be 17.5

  }

La función de nuestro ejemplo MyFunction está diseñada para multiplicar automáticamente dos números. Llamando a esta función y sustituyendo los valores numéricos necesarios como argumentos, podremos obtener rápidamente el resultado de los cálculos. Esta abstracción simplificará el proceso de multiplicación de los números, mejorando la legibilidad y reutilización del código.

Si algo no le queda claro, no tenga miedo de preguntar. Es esencial entender todo lo que estamos explicando aquí. Si es necesario, estoy dispuesto a añadir cualquier explicación adicional. Las preguntas siempre resultan útiles para aprender, así que no dude en ponerse en contacto con nosotros. Intentaré responder con prontitud.


3. Preprocesadores

¿Qué son los preprocesadores?

En el mundo dinámico de la programación MQL5, los preprocesadores actúan como herramientas importantes que determinan la forma en que se interpreta y ejecuta el código. Un preprocesador es un componente de software especializado que trabaja con el código fuente antes de que comience el proceso de compilación propiamente dicho. Su función principal consiste en procesar las instrucciones y directivas que afectan al comportamiento, la estructura y las características del programa resultante.

Los preprocesadores trabajan mediante la transformación o el preprocesamiento del código fuente. Interpretan determinadas directivas precedidas del carácter de rejilla (#) y actúan en consecuencia, modificando el contenido del código antes de compilarlo. Estas directivas se denominan directivas de preprocesador y abarcan toda una serie de funciones, como la definición de constantes, la habilitación de archivos externos, el establecimiento de propiedades de programa y la activación de compilación condicional. Cada directiva sirve a un propósito específico, y da forma al código según los requisitos del desarrollador.

Una característica única de los preprocesadores es su capacidad para ejecutarse antes de la compilación en sí. Esta fase temprana de trabajo con el código permite a los desarrolladores establecer determinadas condiciones, configuraciones o inclusiones que afectarán al programa compilado final.

Figura 2. Preprocesadores en el MetaEditor

Nota: Al escribir programas en MQL5, resulta importante recordar que los preprocesadores no van seguidos de punto y coma (;), como en los operadores normales, y se declaran en el espacio global de nuestro código. Estas instrucciones, como #define para las macros o #include para la inclusión de archivos, determinan la formación del comportamiento y la estructura del programa. Así que, una vez más, al trabajar con preprocesadores, ¡no ponga punto y coma!

Analogía

Vamos a comparar escribir un código con decirle a un robot lo que tiene que hacer. Antes de decirle nada al robot, supongamos que tenemos un amigo al que hemos llamado "Preprocesador". Este amigo nos ayudará a prepararlo todo para que el robot entienda nuestras instrucciones. El preprocesador es como un asistente que revisará nuestras instrucciones antes de que lo haga el robot. Su trabajo consiste en simplificar y organizar las cosas. A veces es necesario usar palabras especiales que signifiquen algo importante, como decir MAGIC_NUMBER en lugar del número 10. El preprocesador entiende estas palabras especiales y las sustituirá por números reales.

Ejemplo:

#define MAGIC_NUMBER 10

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

   int result = MAGIC_NUMBER + 5;
   Comment(result); // Output will be 15

  }

Explicación:

“#define MAGIC_NUMBER 10”:

  • Aquí estaremos diciendo algo como: "Preprocesador, siempre que veas la palabra MAGIC_NUMBER en mi código, sustitúyela por el número 10". Es como crear una palabra especial para denotar un significado específico.

“void OnStart()”:

  • Esta es la parte en la que comenzará la acción principal. OnStart es una función especial en MQL5, que se ejecuta al inicio de su script.

“int result = MAGIC_NUMBER + 5;”:

  • Aquí utilizaremos nuestra palabra especial MAGIC_NUMBER. El preprocesador verá esto y sustituirá MAGIC_NUMBER por 10. Así, esta línea será similar a la frase "int result = 10 + 5;"

“Comment(result); // Output will be 15”:

  • La función Comentario es como pedirle a un robot que diga algo. En este caso, estaremos pidiendo al robot que llame a un valor result igual a 15. Así, al ejecutar este script, mostrará "15" en los comentarios de su gráfico.

Lo mejor es que el preprocesador se activará incluso antes de que le digamos al robot lo que tiene que hacer. Se fijará en nuestras palabras especiales y lo preparará todo, de forma que cuando el robot empiece a funcionar, ya sabrá lo que tiene que hacer. Así pues, un preprocesador es un ayudante que se asegurará de que las instrucciones sean claras, el código esté ordenado y todo esté perfectamente preparado para que el robot lo ejecute. Se usan con frecuencia en programación.


3.1. Categorías de preprocesadores

3.1.1. Macrosustituciones (#define)

Las macros se utilizan para crear constantes. Las constantes son valores inmutables. Podemos utilizar macros para crear palabras especiales que indiquen dichos valores.

Ejemplo:

#define MAGIC_NUMBER 10

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

   int result = MAGIC_NUMBER + 5;

  }

Aquí, en lugar de usar el valor 10 cada vez, solo usaremos la palabra MAGIC_NUMBER, lo que hará que nuestro código resulte más limpio y legible.

3.1.1.1.  Macros parametrizadas

Volviendo a la receta, supongamos que de vez en cuando tenemos que cambiar un ingrediente clave. Las macros parametrizadas permitirán crear instrucciones flexibles.

Ejemplo:

#define MULTIPLY(x, y) (x * y)

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

   int result = MULTIPLY(3, 5); // Result is 15

  }

Aquí, la macro MULTIPLY tomará dos ingredientes (números) y los multiplicará. Es como una función en nuestro código.

Explicación:

“#define MULTIPLY(x, y) (x * y)”:

  • Esta línea le dice a la computadora: "Siempre que yo diga MULTIPLY, tendrás que tomar dos números (los llamaremos x e y) y multiplicarlos entre sí. Así que usando la palabra mágica creamos una máquina de multiplicar.

“int result = MULTIPLY(3, 5);”:

  • Aquí se utiliza la palabra especial MULTIPLY. La computadora lo verá y sabrá que significa "toma los números 3 y 5 y multiplícalos". Así, sustituirá MULTIPLY(3, 5) por (3 * 5).

“// Result is 15”:

  • El resultado de multiplicar 3 y 5 es 15. Así, después de que la computadora realice el cálculo, el valor de la variable resultado será 15.

Básicamente, este código simplificará el proceso de multiplicación. En lugar de escribir "3 * 5" directamente, utilizaremos la palabra MULTIPLY para que el código resulte más legible y fácil de entender. Es un asistente muy práctico y útil.

3.1.1.2. Directiva #undef

La directiva #undef más o menos dice: "Olvida lo que dije antes, comencemos de nuevo". En la programación MQL5, permite redefinir o cancelar la creación de una macro previamente definida. Es como si elimináramos lo escrito en una pizarra.

Ejemplo:

#define MAGIC_NUMBER 10

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

   int result = MAGIC_NUMBER + 5;
#undef MAGIC_NUMBER // Undefining MAGIC_NUMBER


  }

Explicación:

“#define MAGIC_NUMBER 10”:

  • Primero definiremos una macro llamada MAGIC_NUMBER y estableceremos su valor en 10. Nosotros diremos: "Cada vez que use MAGIC_NUMBER, me estaré refiriendo al número 10".

“int result = MAGIC_NUMBER + 5;”:

  • MAGIC_NUMBER se utiliza en el cálculo, el resultado será 15.

“#undef  MAGIC_NUMBER”:

  • Con "#undef MAGIC_NUMBER" estaremos diciendo: "Olvida lo que significaba MAGIC_NUMBER". Al hacerlo, borraremos la definición y haremos que MAGIC_NUMBER quede indefinido.
Si intentamos utilizar MAGIC_NUMBER después de la cadena #undef, se producirá un error porque el computadora ya no sabrá lo que significa MAGIC_NUMBER. Esto puede resultar útil si deseamos anular o dejar de utilizar una macro en una parte concreta de nuestro código. Es decir, hemos utilizado una palabra especial durante un tiempo, pero ahora ya no la necesitamos y podemos pasar a otra cosa.

3.1.2. Propiedades del programa (#property)

En el mundo de la programación, cada programa posee sus propias características, como el personaje de una historia. Con #property podremos dotar a nuestro programa de características y cualidades especiales. Es la forma de decir a los computadoras que existen ciertas cosas que hacen que un programa sea especial. Imagine que ha escrito un libro o una canción y quiere decirle a la gente quién y cuándo lo ha creado. Esto es más o menos lo que se hace en los programas usando #property. Así es como añadiremos una pequeña nota al principio del código: "Este programa es la versión 1.0, y lo escribí en 2022".

Ejemplo:

#property copyright "crownsoyin"
#property version   "1.00"

Resultado tras la ejecución del código

Figura 3: Resultado tras ejecutar el código en MetaTrader 5

Aquí, la directiva #property crea algo así como la portada de un libro o los créditos iniciales de una película. Establece el tono, ofrece información sobre el programa y ayuda a todos, incluido el propio programador y otros usuarios, a que puedan leer el código más adelante y entender lo que está pasando.

3.1.3 Directiva de inclusión (#include)

¿Qué es un archivo de inclusión?

Volvamos a nuestras recetas. Imaginemos que tienes un recetario, pero hay algunas recetas guardadas en otro libro. La directiva #include especificará "tomar recetas adicionales de otro libro y combinarlas en una gran colección de recetas". Al escribir código, esto le permitirá fusionar archivos externos con el código principal, haciendo que todo esté disponible en un solo lugar. En este caso, programar será como construir una casa con diferentes habitaciones. Cada habitación tiene una función específica, como la cocina o el dormitorio. La directiva #include nos permitirá reutilizar estas salas (funciones y estructuras) en otras casas (programas). En la construcción, sonaría así: "He construido una cocina genial y ahora puedo usarla en todas mis casas en lugar de construirla desde cero cada vez".

Ejemplo:

#include "extraRecipes.mqh" // Include external file with extra recipes

Explicación:

“#include”:

  • El comando #include dice: "Trae algo de otro sitio y añádelo aquí".

‘"extraRecipes.mqh"’:

  • El texto dentro de las comillas dobles será el nombre del archivo externo a incluir. En este caso, será "extraRecipes.mqh". Este archivo contiene recetas adicionales (código) que se utilizarán en el programa principal.

Así, escribiendo "#include 'extraRecipes.mqh'", será como si abriéramos un libro de cocina y dijéramos: "Vamos a añadir estas recetas adicionales a nuestras instrucciones básicas de cocina". Esto ayudará a mantener el código principal limpio y organizado, a la vez que se mantiene el acceso a funciones adicionales desde "extraRecipes.mqh". Utilizando #include para incluir un archivo externo en el código, cualquier función o estructura definida en ese archivo externo pasará a estar disponible y podrá utilizarse en el código principal.

Ejemplos:

Archivo externo: "extraRecipes.mqh"

// extraRecipes.mqh
int MultiplyByTwo(int number) // function from an external file  
{
    return number * 2;
}

Código base:

// Your main code
#include "extraRecipes.mqh" // Include external file with extra recipes
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

   int result = MultiplyByTwo(5);  // Using the function from the included file
   Print("Result: ", result);      // Output: Result: 10

  }


En este ejemplo, la función MultiplyByTwo se ha definido en el archivo externo extraRecipes.mqh. Usando #include "extraRecipes.mqh" en el código principal, podremos llamar y usar la función MultiplyByTwo como si estuviera definida directamente en el código principal.

Comprender los entresijos de la inclusión de código y el uso de funciones puede parecer un poco desalentador a primera vista, ¡pero no pasa nada! Siempre nos encontramos con retos al explorar nuevos ámbitos de conocimiento. Y si hay algo que no entiende, no dude en preguntar. Preguntar y buscar explicaciones adicionales enriquecerá el proceso de aprendizaje. Recuerde que estamos hablando de los fundamentos del lenguaje y que sin entender estos, no podremos avanzar. No dude en preguntar cualquier duda que tenga.

3.1.4. Compilación condicional (#ifdef, #ifndef, #else, #endif)

Las directivas de compilación condicional permiten incluir o excluir partes de código en la compilación. Se trata de instrucciones especiales que indican al compilador qué debe incluir según determinadas condiciones.

3.1.4.1. Directiva #ifdef

En MQL5, #ifdef es una directiva del preprocesador que comprobará si un símbolo en particular está definido. Si el símbolo está definido, el bloque de código que sigue a #ifdef se incluirá durante la compilación. En caso contrario, se incluirá el bloque de código que siga a #else o #endif.

Ejemplo:

#define MAGIC_NUMBER 10
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

#ifdef MAGIC_NUMBER
   Print(MAGIC_NUMBER);
#else
   Print(MAGIC_NUMBER);
#endif


  }

Explicación:

  • En este ejemplo, MAGIC_NUMBER se define usando la directiva #define.
  • La directiva "#ifdef MAGIC_NUMBER" comprobará si MAGIC_NUMBER está definido.
  • Como ya está definido, se incluirá el bloque de código que sigue a #ifdef, lo cual hace que se compile el primer operador Print.
  • Si no hubiéramos definido MAGIC_NUMBER, se habría incluido el bloque de código que sigue a #else y se habría compilado el segundo operador Print.

#ifdef se usa para la compilación condicional, permitiendo a los desarrolladores incluir o excluir ciertas secciones de código basándose en símbolos predefinidos. Es una valiosa herramienta para crear código personalizable, que permita a los desarrolladores adaptar sus aplicaciones en función de determinados símbolos o condiciones durante la compilación.

3.1.4.2. Directiva #ifndef

En MQL5, #ifndef es una directiva de preprocesador que comprueba si un símbolo en particular no está definido. Si no se define ninguna macro, el bloque de código que sigue a #ifndef se incluirá durante la compilación. En caso contrario, se incluirá el bloque de código que sigue a #else o #endif.

Ejemplo:

void OnStart()
  {

#ifndef MAGIC_NUMBER
   Print(MAGIC_NUMBER);
#else
   Print(MAGIC_NUMBER);
#endif


  }

Explicación:

  • #ifndef MAGIC_NUMBER comprueba si falta la definición de "MAGIC_NUMBER".
  • Si la macro MAGIC_NUMBER no está definida, se incluirá el bloque de código que sigue a #ifndef y se imprimirá un mensaje indicando que la macro MAGIC_NUMBER no está definida.
  • Si MAGIC_NUMBER está definido, el bloque de código que sigue a #else se activará y mostrará el valor de MAGIC_NUMBER.

Este código muestra el uso de la compilación condicional basada en si una macro en particular (en este caso MAGIC_NUMBER) está definida o no. Dependiendo de la presencia o ausencia de una macro, se incluirán diferentes bloques de código durante la compilación.

Nota. MAGIC_NUMBER no se ha definido en este ejemplo.

3.1.4.3. Directiva "#endif"

La directiva #endif en MQL5 marca el final del bloque de código condicional comenzado por las directivas #ifdef o #ifndef. Sirve como señal al preprocesador de que el sector de compilación condicional ha finalizado y debe procesarse el código de compilación posterior. No tiene condiciones ni parámetros: su propósito es marcar el final del bloque de compilación condicional.

Ejemplo:

#define MAGIC_NUMBER 10
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {

#ifndef MAGIC_NUMBER
   Print(MAGIC_NUMBER);
#else
   Print(MAGIC_NUMBER);
#endif

  }

Explicación:

  • #endif marca el final del bloque condicional y el código siguiente se procesará normalmente.

Nota. Para conservar la sintaxis correcta y evitar errores de compilación, combine siempre #endif con una directiva condicional de apertura (#ifdef o #ifndef).

En la breve revisión de los preprocesadores MQL5, hemos analizado las macros para crear constantes, las propiedades del programa (#properties) para definir características, la inclusión de archivos para la modularidad y la compilación condicional para lograr una mayor flexibilidad del código. Esto es solo una introducción, aún queda mucho más por venir. No obstante, estas herramientas constituyen la base de una programación MQL5 eficaz, pues aportan versatilidad y adaptabilidad. Y todo esto resulta muy importante en nuestro camino hacia la creación de potentes algoritmos comerciales. Una vez más, si algo no queda claro, pregúntenos en los comentarios.


4. Procesamiento de eventos

¿Qué son los eventos?

En programación, un evento se refiere a un suceso específico ocurrido durante la ejecución de un programa. Los eventos pueden activarse por diversas acciones, como las interacciones de los usuarios y los cambios de estado del sistema. En el contexto de MQL5, los eventos resultan cruciales a la hora de desarrollar programas para el comercio algorítmico que reaccionen dinámicamente a las condiciones del mercado.

¿Qué son los manejadores de eventos?

Un manejador de eventos es un apartado concreto de código responsable de reaccionar a un evento de un tipo específico. En MQL5, los manejadores de eventos son funciones destinadas a ejecutarse cuando ocurre un determinado evento. Estas funciones están predefinidas y han sido diseñadas para responder a diversos eventos. A cada tipo de evento le corresponde una función manejadora de eventos.

Analogía

Imagínese que está en un espectáculo de marionetas y que éstas se mueven y hablan cuando se producen determinados eventos, como cuando el público aplaude o cuando se pulsa un botón para tal efecto. En esta analogía, las marionetas serían las distintas partes de un programa informático, y el botón sería el evento. El manejador de eventos es como un titiritero entre bastidores, a la espera de que se produzcan determinados momentos (eventos). Cuando se produce un evento, el manejador de eventos se activa, haciendo que el programa haga algo especial. Por ejemplo, cuando el público aplaude o pulsa un botón, el titiritero hace que las marionetas bailen o hablen en consecuencia. Así, el manejador de eventos da vida a las acciones correctas cuando ocurren ciertos eventos.

4.1. Tipos de manejadores de eventos

Figura 4: tipos de manejadores de eventos


4.1.1. OnInit

En MQL5, OnInit es una función especial usada en los asesores para la inicialización en la primera carga en el gráfico.

Analogía:

Vamos a imaginar que tenemos un ayudante robótico. Antes de que el robot pueda hacer nada interesante, como moverse o emitir sonidos divertidos, debemos prepararlo. La parte de la "preparación" es similar al momento OnInit del robot. Así, cuando el robot se enciende por primera vez, entra en una sala especial (función OnInit) donde podemos prepararlo. Aquí es donde establece sus colores favoritos, decide a qué velocidad quiere moverse y se asegura de que todo esté en su sitio. Una vez que todo está preparado, el robot estará listo para salir y empezar a hacer trucos, como bailar o contar chistes.

En los programas informáticos, la función OnInit funciona de manera similar. Se trata de una sala especial donde se prepara el programa antes de comenzar con sus tareas. Puede compararse con la ceremonia de apertura de un programa: ¡asegurarse de que todo está listo para comenzar!

Ejemplos:

// Declare global variables
double LotSize;
int TakeProfit;
int StopLoss;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
// OnInit function
int OnInit()
  {
// Set EA parameters
   LotSize = 0.1;
   TakeProfit = 50;
   StopLoss = 30;

// Display initialization message
   Print("EA is getting ready. Lot Size: ", LotSize, ", Take Profit: ", TakeProfit, ", Stop Loss: ", StopLoss);

   return(INIT_SUCCEEDED);
  }

Explicación:

  • Vamos a declarar algunas variables globales que el asesor puede utilizar.
  • La función OnInit es donde inicializamos estas variables y donde realizaremos todos los ajustes necesarios. En nuestro ejemplo, estableceremos los valores LotSize, TakeProfit y StopLoss.
  • El operador Print es un mensaje que el asesor puede enviar a la consola para informar de su inicialización. El programa dice: "Me estoy preparando, y aquí están mis ajustes".

Al ejecutar el asesor en un gráfico, la función OnInit se ejecutará una vez y la consola mostrará un mensaje de inicialización. Esto garantiza que el asesor haya completado los ajustes antes de empezar a funcionar o realizar otras acciones.

4.1.2. OnStart

En MQL5, la función OnStart supone una parte importante de los scripts y asesores. Su función principal consiste en ejecutar comandos una sola vez al activar o ejecutar un script. Este es el punto de entrada inicial para ejecutar el script. En el caso de un script, la función OnStart ejecuta alguna lógica que puede incluir la colocación de transacciones, la realización de cálculos u otras acciones. Sin embargo, a diferencia de un asesor que se ejecuta constantemente y reevalúa las condiciones en la función OnStart, el script ejecutará su lógica solo una vez y luego finalizará.

Así, si tenemos acciones relacionadas con el trading, en la función OnStart del script MQL5, estas acciones se realizarán cuando el script se active, pero el script no monitorizará constantemente el mercado ni realizará transacciones adicionales.

Ejemplo:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
// This script prints a message to the console when activated

// Entry point when the script is started
void OnStart()
  {
// Print a message to the console
   Print("Script Activated! Hello, Traders!");
  }

Explicación:

  • La función OnStart es el punto de entrada del script..
  • La función Print sirve para mostrar el mensaje en la pestaña "Expertos" de la ventana del terminal en MetaTrader.
  • Al ejecutar el script en un gráfico o en el MetaEditor, veremos el mensaje especificado en la consola. Podemos modificar el operador Print y añadir una lógica más compleja para adaptarla a los requisitos de nuestro script.

Al ejecutar el script en el gráfico, la función OnStart se ejecutará una vez. El propósito de la función OnStart en un script suele ser realizar tareas de inicialización o ejecutar determinadas acciones cuando el script se adjunte al gráfico. Una vez que el script haya resuelto su lógica, finalizará.

 

4.1.3. OnTick

En MQL5, la función OnTick es una parte importante de los asesores. Contiene la lógica comercial básica y las acciones a realizar en cada tick. Los asesores usan OnTick para monitorear la evolución de los precios, analizar las condiciones del mercado y tomar decisiones basadas en estrategias comerciales predefinidas. Las acciones relacionadas con las órdenes, como la apertura, la modificación o el cierre de posiciones, suelen colocarse en la función OnTick.

Analogía

OnTick funciona como el asistente TickTrader que vigila un mercado en constante movimiento. Cada tic supone una nueva oportunidad o cambio. TickTrader siempre está buscando buenas transacciones, igual que nosotros buscamos las mejores ofertas en un mercado ajetreado.

Cuando el mercado está en calma, TickTrader puede actuar lentamente, limitándose a mirar a su alrededor sin comprar ni vender con rapidez. De la misma manera, si la función OnTick ve que las cosas permanecen tranquilas con cada tick, puede sugerir ser cauteloso; por ejemplo, puede proponer vigilar de cerca el mercado. Si los precios suben de repente, TickTrader puede ver una transacción rentable y optar por la compra. Del mismo modo, cuando la función OnTick observa una gran variación del precio con cada tick, puede ofrecernos aprovechar buenas oportunidades, como comprar o vender un activo.

En este mercado tan ajetreado, TickTrader estará siempre listo para la siguiente oportunidad, tomando decisiones a medida que llegan los ticks. De la misma forma, OnTick funcionará en tiempo real, ajustándose a cada tick y gestionando las acciones comerciales en un mercado dinámico.

Ejemplo:
// Declare a variable to store the last tick's close price
double lastClose;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
// OnInit function
int OnInit()
  {

// Initialize the variable with the current close price
   lastClose = iClose(_Symbol, PERIOD_CURRENT, 0);

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

// Get the current close price
   double currentClose = iClose(_Symbol, PERIOD_CURRENT, 0);

// Check if the close price has changed
   if(currentClose != lastClose)
     {
      // Print a message when the close price changes
      Print("Close price changed! New close price: ", currentClose);

      // Update the last close price
      lastClose = currentClose;
     }


  }

Explicación:

Declaración de variables:

// Declare a variable to store the last tick's close price
double lastClose;

Hemos declarado la variable lastClose de tipo double para guardar el precio de cierre del último tick.

Inicialización (función OnInit):

int OnInit()
  {

// Initialize the variable with the current close price
   lastClose = iClose(_Symbol, PERIOD_CURRENT, 0);

   return(INIT_SUCCEEDED);
  }

En la función OnInit, inicializaremos lastClose con el precio de cierre de la vela más reciente usando la función iClose. Los parámetros _Symbol, PERIOD_CURRENT y 0 especificarán el símbolo actual, el marco temporal actual y la vela más reciente, respectivamente.

Función OnTick:

void OnTick()
  {

// Get the current close price 
   double currentClose = iClose(_Symbol, PERIOD_CURRENT, 0);

// Check if the close price has changed
   if(currentClose != lastClose)
     {
      // Print a message when the close price changes
      Print("Close price changed! New close price: ", currentClose);

      // Update the last close price
      lastClose = currentClose;
     }


  }

  • En la función OnTick, obtendremos el precio de cierre actual utilizando iClose y lo almacenaremos en la variable currentClose.
  • A continuación, comprobaremos si el precio de cierre actual difiere del último precio de cierre registrado (currentClose != LastClose).
  • Si se ha dado un cambio, mostraremos un mensaje indicando el cambio y actualizaremos la variable lastClose.

Este código monitoreará y mostrará un mensaje cada vez que cambie el precio de cierre de un tick. Muestra cómo puede utilizarse la función OnTick para responder a la dinámica del mercado en tiempo real.


Conclusión

En este artículo, hemos analizado con detalle varios aspectos fundamentales de MQL5: arrays, funciones personalizadas, preprocesadores y manejo de eventos. Espero que las descripciones ofrecidas le sean claras y útiles. La comprensión de estos elementos fundamentales resulta simplemente esencial para proceder a crear robots comerciales. El conocimiento aumenta con la curiosidad, así que explore, pruebe y no deje de hacer preguntas. Permanezca atento a la publicación de las próximas entregas, en las que avanzaremos paso a paso hacia la construcción de robots comerciales. Mientras tanto, tómese su tiempo para comprender con confianza los conceptos básicos. ¡Le deseo la mejor de las suertes en su camino como programador y tráder!

Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/14099

Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
El tipo de dibujado DRAW_ARROW en indicadores de símbolo y periodo múltiple El tipo de dibujado DRAW_ARROW en indicadores de símbolo y periodo múltiple
En este artículo nos ocuparemos de dibujar los indicadores de símbolo y periodo múltiple. Asimismo, mejoraremos los métodos de la clase para representar correctamente las flechas que muestran los datos de los indicadores de flecha calculados sobre un símbolo/periodo que no se corresponde con el símbolo/periodo del gráfico actual.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Desarrollo de un sistema de repetición (Parte 50): Esto complica las cosas (II) Desarrollo de un sistema de repetición (Parte 50): Esto complica las cosas (II)
Vamos resolver la cuestión del ID del gráfico, pero al mismo tiempo, vamos empezar a garantizar que el usuario pueda hacer uso de una plantilla personal, enfocada en analizar el activo que desea estudiar y simular. El contenido expuesto aquí tiene como objetivo, pura y simplemente, ser didáctico. En ningún caso debe considerarse como una aplicación cuya finalidad no sea el aprendizaje y el estudio de los conceptos mostrados.