English Русский Deutsch 日本語 Português
preview
Variables y tipos de datos extendidos en MQL5

Variables y tipos de datos extendidos en MQL5

MetaTrader 5Trading | 12 septiembre 2024, 12:46
92 0
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

Introducción

MQL5 es el lenguaje de programación de la plataforma comercial más popular, MetaTrader 5, que, como todos sabemos, es la mejor equipada con herramientas para crear un sistema comercial de cualquier complejidad. Nuestro objetivo como desarrolladores es comprender cómo usar estas herramientas y conceptos para alcanzar nuestro objetivo de desarrollo.

Ya hemos mencionado los fundamentos de las variables y tipos de datos simples en MQL5 en el artículo "Aprenda por qué y cómo diseñar su sistema de trading algorítmico". Además, hemos considerado el uso de MQL5 para escribir aplicaciones comerciales, así como las variables en cuanto a su definición, tipos y formas de aplicación. También hemos aprendido los tipos de datos simples, como integer, float, double, string y bool. También hemos mencionado las variables de entrada y cómo pueden usarse para que el usuario establezca sus preferencias en el programa.

En el artículo "Trabajamos con fechas y horas en MQL5", hemos analizado con detalle el tipo de dato Datetime, esencial para cualquier aplicación comercial.

En este artículo, consideraremos nuevas variables y tipos de datos en MQL5, así como su aplicación en el desarrollo del software comercial MQL5. Asimismo, aprenderemos más sobre algunos conceptos avanzados de variables y tipos de datos y los abarcaremos en los siguientes temas:

Trataremos cada tema con detalle, incluyendo numerosos ejemplos, para profundizar en su comprensión y poder utilizar eficazmente todos los conceptos mencionados. La programación es una ciencia en la que la práctica resulta esencial, es por eso que debemos intentar aplicar lo aprendido para ver cómo podemos utilizar cada concepto como parte del código general y crear un sistema comercial eficaz.


Constantes

En esta parte profundizaremos en el concepto de constantes en programación y en MQL5 para entender por qué necesitamos usar variables de este tipo. Una variable constante también se denomina constante solo de lectura o nombrada. No se puede modificar y no podemos asignarle un valor nuevo después de la primera inicialización. Si el código intenta hacer esto, provocará un error. El concepto de constante es admitido por la mayoría de los lenguajes de programación, incluyendo MQL5. Tenemos dos métodos para definir una constante en nuestro software:

  • Constantes globales
  • El especificador const

Constantes globales:

Esta constante global puede ser definida globalmente utilizando la directiva #define del preprocesador al principio de nuestro programa. Después se especifica el identificador y se asigna el valor de la constante. El valor de una constante puede ser cualquier tipo de dato, como una cadena. En nuestro software necesitaremos codificar lo siguiente:

#define Identifier_Name constant_value

Como podemos ver en el código anterior, tenemos una directiva de preprocesador #define que especifica la declaración de una constante. El siguiente ejemplo nos permitirá comprender mejor este método:

En el ámbito global, primero utilizaremos la directiva del preprocesador #define para declarar que tenemos una constante, el identificador será PLATFORM_NAME, mientras que el valor constante del identificador será el tipo de datos de cadena "MetaTrader 5".

#define PLATFORM_NAME "MetaTrader 5"

En la función requerida, podemos decir, por ejemplo, que imprimiremos el identificador en OnStart()

void OnStart()
  {
   Print("The trading platform name is: ", PLATFORM_NAME);
  }

Al compilar y ejecutar el software, podremos encontrar el resultado en forma de mensaje en la pestaña Expertos, como se muestra en la siguiente captura de pantalla.

constant_example

Como ya hemos mencionado, el valor de una constante de MetaTrader 5 no puede cambiarse.

El especificador const:

Según este método, podemos declarar una constante usando el especificador const delante de la variable y su tipo de datos. Este especificador declara que esta será una constante y no podrá modificarse. A continuación le mostramos cómo hacerlo.

const int varName = assignedVal

Más abajo se muestra un ejemplo sencillo de declaración de una constante usando este método.

const int val = 1;

Si necesitamos imprimir el valor de esta variable

Print("constant val is: ", val);

Podremos encontrar un resultado similar al siguiente:

constant_example2

Como hemos mencionado antes, si intentamos actualizar la variable (val), obtendremos el siguiente error porque es una constante y no se puede actualizar.

constant_error

Para ilustrar la diferencia entre una variable ordinaria y una constante, podemos usar el siguiente ejemplo en el que tenemos dos valores: una constante y una variable ordinaria.

   const int val = 1;
   int val2 = 2;
   
   Print("constant val is: ", val);
   Print("val 2 is: ", val2);
A continuación le mostramos el resultado al ejecutar el programa.

diffExam

Ahora intentaremos actualizar val2 con otro valor igual a 4 utilizando este código.

val2 = 4;

Si a continuación volvemos a imprimir los dos valores, el resultado será el siguiente:

diffExam2

Como podemos ver, el valor de val2 cambia de 2 a 4.

Arrays

Array es un concepto básico de cualquier lenguaje de programación. Un array es una variable en la que podemos almacenar muchos valores de cualquier tipo de datos. Podemos representar el array como una lista con índices y sus valores correspondientes. Cuando necesitamos acceder a un valor concreto, podemos hacerlo usando la indexación.

La indexación del array parte de cero, por lo que el índice máximo será igual al resultado de reducir el tamaño del array en un valor. Si tenemos un array con cinco valores, sus índices serán (0, 1, 2, 3, 4), mientras que el valor máximo como podemos ver será 4, que es el resultado de (5-1).

Si necesitamos acceder al valor de un índice concreto, nos referiremos a él especificando su índice entre corchetes []. Esto es lo que entendemos por acceso usando la indexación.

También tenemos arrays estáticos y dinámicos. La diferencia entre los dos en términos de tamaño del array es que un array estático tiene un tamaño fijo y no se puede cambiar, y uno dinámico no tiene tamaño. Podemos usarlo si necesitamos redimensionar un array en lugar de utilizar uno estático.

Declaración de un array estático

Si necesitamos declarar un nuevo array estático, podremos hacerlo utilizando el siguiente ejemplo.

int newArray[5];

newArray[0] = 1;
newArray[1] = 2;
newArray[2] = 3;
newArray[3] = 4;
newArray[4] = 5;

Print("newArray - Index 0 - Value: ", newArray[0]);
Print("newArray - Index 1 - Value: ", newArray[1]);
Print("newArray - Index 2 - Value: ", newArray[2]);
Print("newArray - Index 3 - Value: ", newArray[3]);
Print("newArray - Index 4 - Value: ", newArray[4]);

Tras ejecutar el programa, veremos el siguiente resultado.

staticArrayDeclaration

Podemos obtener el mismo resultado declarando un array usando el siguiente código abreviado para mayor eficiencia, asignando valores al array dentro de los corchetes {} y separándolos con una coma (,).

   int newArray[5] = {1, 2, 3, 4, 5};

   Print("newArray - Index 0 - Value: ", newArray[0]);
   Print("newArray - Index 1 - Value: ", newArray[1]);
   Print("newArray - Index 2 - Value: ", newArray[2]);
   Print("newArray - Index 3 - Value: ", newArray[3]);
   Print("newArray - Index 4 - Value: ", newArray[4]);

 Si ejecutamos este código, obtendremos los mensajes que ya hemos mostrado anteriormente.

Declaración de un array dinámico

Como ya hemos mencionado, un array dinámico es un array sin tamaño fijo que puede cambiar de tamaño. Este tipo de array se utiliza para almacenar datos tales como el precio y el valor del indicador, ya que estos datos son dinámicos.

A continuación le mostramos cómo se puede declarar un nuevo array dinámico.

   double myDynArray[];
   ArrayResize(myDynArray,3);

   myDynArray[0] = 1.5;
   myDynArray[1] = 2.5;
   myDynArray[2] = 3.5;
   
   Print("Dynamic Array 0: ",myDynArray[0]);
   Print("Dynamic Array 1: ",myDynArray[1]);
   Print("Dynamic Array 2: ",myDynArray[2]);

Como podemos ver en el código anterior, declararemos el array con un corchete cuadrado vacío, y luego usaremos la función ArrayResize para redimensionar el array. El resultado será el siguiente.

dynamicArrayDeclaration

Tenemos tres mensajes impresos para cada índice y el valor correspondiente. Hemos definido arrays unidimensionales, pero también podemos usar arrays multidimensionales: de dos, tres o cuatro dimensiones.

Arrays multidimensionales

Las arrays multidimensionales pueden considerarse arrays anidados o arrays dentro de arrays. Por ejemplo, tenemos dos arrays, cada uno con dos elementos. Veamos cómo podemos codificar este tipo de array de la forma que sigue:

Declaración de arrays bidimensionales con dos elementos

double newMultiArray[2][2];

Asignación de valores o elementos al primer array con índice 0

newMultiArray[0][0] = 1.5;
newMultiArray[0][1] = 2.5;

 Asignación de valores o elementos del segundo array con índice 1

newMultiArray[1][0] = 3.5;
newMultiArray[1][1] = 4.5;

Imprimir los valores de dos arrays y sus elementos o valores

   Print("Array1 - Index 0 - Value: ", newMultiArray[0][0]);
   Print("Array1 - Index 1 - Value: ", newMultiArray[0][1]);
   Print("Array2 - Index 0 - Value: ", newMultiArray[1][0]);
   Print("Array2 - Index 1 - Value: ", newMultiArray[1][1]);

Tras ejecutar el programa, veremos el siguiente resultado.

multiDymArrays

Como podemos ver, tenemos el array 1 con dos valores de 1,5 y 2,5, así como el array 2 con dos valores de 3,5 y 4,5.

Al declarar arrays multidimensionales, podemos dejar vacía solo la primera dimensión, como en el siguiente ejemplo.

double newMultiArray[][2];

Luego podremos transmitirlo utilizando una variable similar a la siguiente:

int array1 = 2;
ArrayResize(newMultiArray, array1);

Después de compilar y ejecutar la aplicación, obtendremos el mismo resultado que antes.


Enumeraciones

Podemos pensar en las enumeraciones como en conjuntos de datos o listas de constantes/elementos que pueden utilizarse para describir conceptos relacionados. Las enumeraciones pueden ser incorporadas y personalizadas. Las enumeraciones incorporadas están predefinidas en MQL5, y podemos llamarlas y usarlas en nuestro programa. Las enumeraciones personalizadas se introducen según nuestras necesidades.

Las enumeraciones incorporadas se pueden encontrar en el Manual de Referencia MQL5, por ejemplo, ENUM_DAY_OF_WEEK. También podemos configurar las enumeraciones personalizadas para utilizarlas posteriormente en las aplicaciones que creemos.

En primer lugar, utilizaremos la palabra clave enum para definir la enumeración, el nombre del tipo de enumeración y una lista separada por comas con los valores de los tipos de datos relacionados.

enum name of enumerable type 
  { 
   value 1,
   value 2,
   value 3
  };

Por ejemplo, vamos a crear una enumeración con el nombre workingDays, que es un tipo. Anunciaremos las variables más adelante

   enum workingDays
     {
      Monday,
      Tuesday,
      Wednesday,
      Thursday,
      Friday,
     };

Ahora deberemos declarar una nueva variable vinculada de tipo workingDays, asignar el valor de hoy entre los definidos en la lista e imprimir esta variable.

     workingDays toDay;
     toDay = Wednesday;
     
     Print(toDay);

Podemos encontrar el mensaje (2) para el miércoles como día laborable en la lista, empezando por 0 para el lunes y llegando hasta 4 para el viernes, como mostramos en la siguiente captura de pantalla.

enums

También podemos definir otro número inicial que se asignará en lugar de (0) asignando el valor al número deseado, por ejemplo, indicando que lunes = 1.


Estructuras

Tenemos que declarar varios tipos de datos diferentes para las variables vinculadas, y aquí es donde las estructuras podrían ayudarnos. Los miembros de una estructura pueden ser de cualquier tipo de datos, a diferencia de la enumeración, cuyos miembros solo pueden ser de un tipo. Similar a las enumeraciones, MQL5 tiene estructuras predefinidas, como MqlTick, pero podemos crear las nuestras propias.

Digamos que necesitamos crear una estructura para tradeInfo. Como miembros tenemos el símbolo, el precio, el stop loss, el take profit y el tiempo de negociación. Tenemos diferentes tipos de datos: un símbolo representa una cadena, mientras que el precio, el stop loss y el take profit representan un valor de tipo double. En este caso, necesitaremos utilizar estructuras para llevar a cabo nuestra tarea:

Utilizaremos la palabra clave struct para declarar nuestra propia estructura.

   struct tradeInfo
     {
      string         symbol;
      double         price;
      double         stopLoss;
      double         takeProfit;
     };

A continuación, podremos declarar un nuevo objeto comercial de tipo tradeInfo y asignar valores a los miembros de esta estructura refiriéndonos a ellos con (.) después del nombre del objeto, del siguiente modo

   tradeInfo trade;

   trade.symbol = "EURUSD";
   trade.price = 1.07550;
   trade.stopLoss = 1.07500;
   trade.takeProfit = 1.07700;

Luego podremos imprimir los miembros de la estructura con sus valores como se muestra a continuación

   Print("Symbol Trade Info: ", trade.symbol);
   Print("Price Trade Info: ", trade.price);
   Print("SL Trade Info: ", trade.stopLoss);
   Print("TP Trade Info: ", trade.takeProfit);

Y visualizaremos el mensaje como se muestra en la siguiente captura de pantalla.

struct

Como podemos ver en la captura de pantalla anterior, estos son los valores asignados que queremos, por lo que podemos experimentar con ellos para lograr nuestros objetivos.


Conversión de tipos

La conversión de tipos se refiere a la conversión del valor de una variable de un tipo de datos a otro. Este proceso puede generar resultados inesperados.

Al convertir o copiar un valor numérico de una variable a otra, podemos encontrarnos con pérdidas de datos cuando este proceso se produce entre tipos diferentes.

A continuación, veremos algunos ejemplos en los que no se pierden datos o se pierden algunos:

Supongamos que estamos copiando un valor entero en un valor long. En este caso, no se producirá ninguna pérdida de datos porque la conversión se realizará de variables más pequeñas a variables más grandes. Al copiar un entero a double, también se copiará un entero, mientras que el valor después del punto decimal será cero, por lo que no habrá datos que perder. Si, por el contrario, copiamos el valor de double en una variable entera, el valor después del decimal se perderá (truncado).

Veamos algunos ejemplos de código. En primer lugar, veremos una conversión de datos sin pérdidas:

   int price = 3 - 1;
   double newPrice = price + 1;
   
   Print ("The price is ", newPrice);

Como podemos ver, el tipo de la variable (precio) es int, entonces la convertiremos en una variable double y la imprimiremos. Supuestamente, no se producirá ninguna pérdida de datos, ya que estamos realizando una conversión de un tipo más pequeño a un tipo más grande. El mensaje que aparecerá es el siguiente.

typeCasting1

Pero al realizar la operación inversa, vemos un panorama muy diferente.

   double price = 3.50 - 1;
   int newPrice = price + 1;
   
   Print ("The price is ", newPrice);

El tipo de valor es double y el valor del precio es 3,50. Vamos a pasarlo a una variable int (newPrice) e imprimirlo. Así perderemos el dato después del punto decimal, que es el valor (.50), y el resultado será (3).

typeCasting2

El compilador nos avisará si convertimos un valor de un tipo mayor a una variable de un tipo menor. Ya dependerá de nosotros decidir si ignoramos o no la pérdida de datos, en función de nuestros objetivos y de la importancia de los datos perdidos. A continuación le mostramos un ejemplo de esta advertencia:

typeCasting2

Podemos redondear el valor añadiendo (int) delante de la variable double para mostrar una advertencia del compilador.


Variables locales

Imaginemos que nuestro software tiene tanto el ámbito global como el local. El ámbito global es aquel al que podemos acceder en cualquier parte de nuestra aplicación (exploraremos el concepto de ámbito global en términos de variables en la siguiente sección), pero al ámbito local solo podrá accederse en el ámbito en el que está declarado.

Si tenemos una función y dentro de esa función hay variables que se declaran solo a nivel de función, dichas variables se considerarán locales. Por lo tanto, pueden estar disponibles mientras se ejecuta la función, pero no lo estarán después de salir de la misma.

Para ilustrarlo, consideraremos el siguiente ejemplo:

void myFunc()
{
   int var = 5;
   Print(var);
}

Como podemos ver, tenemos una función myFunc. Esta función puede invocarse en cualquier lugar. Al llamar a esta función, veremos la variable local var. Esta variable solo puede operar dentro de la función, pero no estará disponible después de salir de esta.

Así, al llamar a la función, veremos que el resultado es 5:

localVar

Si intentamos acceder a la variable local var desde fuera de la función, el compilador mostrará un error de identificador no declarado:

localVarError

Lo mismo ocurrirá si hay niveles o ámbitos anidados dentro de una función: cada variable declarada solo estará disponible en su ámbito.

Veámoslo con el siguiente ejemplo:

void myFunc()
  {
   int var = 5;
   if(var == 5)
     {
      int newVar = 6 ;
      Print(newVar);
     }

  }

Como podemos ver, hemos adjuntado un if para comparar el valor de var con 5. Si es igual a true, declararemos newVar con un valor de 6 y lo imprimiremos. Como resultado, obtendremos 6, ya que la condición es verdadera.

localVar2

newVar representa una nueva variable local dentro del ámbito de la sentencia if. Como cualquier otro ámbito local, resulta inaccesible fuera de él. Cualquier declaración de una variable con el mismo nombre que una variable local redefinirá la anterior y será llevada a un ámbito superior.

Así pues, el concepto es muy sencillo: cualquier variable local estará disponible en su propio nivel o ámbito dentro de una función. Pero, ¿qué haremos si necesitamos acceder a una variable en cualquier parte de nuestra aplicación? Aquí es donde entrará en juego la variable global.


Variables globales

A diferencia de las variables locales, podremos acceder a las variables globales desde cualquier parte de nuestra aplicación. Cuando necesitemos declarar variables globales, deberemos hacerlo en el ámbito global del software fuera de cualquier función para que podamos usarlas o llamarlas en cualquier parte de la aplicación. Este tipo de variables se pueden usar cuando necesitemos declarar una variable reutilizable por muchas funciones.

Las variables globales se definirán en la parte inicial de la aplicación, después de las variables de entrada. A diferencia de las variables locales, si intentamos declarar variables globales dentro de un bloque, se producirá un error de compilación "Declaration of variable hides global declaration" ("la declaración de la variable oculta la declaración global"):

globalVar

Podemos actualizar las variables globales en cualquier punto de la aplicación, como en el ejemplo siguiente:

int stopLoss = 100;

void OnStart()
  {
   Print("1st Value: ", stopLoss);
   addToSL();
  }

void addToSL()
  {
   stopLoss = stopLoss + 50;
   Print("Updated Value: ", stopLoss);
  }

Como podemos ver, declararemos la variable stopLoss de forma global en la parte superior del programa, imprimiremos el valor asignado, llamaremos addToSL() que ha añadido 50 al primer valor asignado, y luego imprimiremos el valor actualizado. Después de compilar y ejecutar la aplicación, veremos los siguientes mensajes:

globalVar2

Como podemos ver, el valor original de nuestra variable es 100, y cuando lo actualizamos añadiendo 50 en addToSL(), se convierte en 150.


Variables estáticas

Las variables estáticas son variables locales, pero conservan sus valores en memoria aunque la aplicación haya salido de su ámbito. Pueden declararse tanto en un bloque de funciones como en variables locales.

Podemos hacerlo usando la palabra clave static delante del nombre de la variable y asignando su valor.

void staticFunc()
{
   static int statVar = 5;
   statVar ++;
   Print(statVar);
}

Primero llamaremos a la función staticFunc()

staticFunc();

Aquí la variable almacenará su valor (5) en la memoria y cada vez que llamemos a la función, la salida será 6 (5+1). A continuación le mostramos una captura de pantalla de la salida:

staticVar

Podemos ver un valor de 6 en la pestaña "Expertos".


Variables predefinidas

Al crear código, puede que necesitemos escribir constantemente muchas líneas para hacer algo usado con frecuencia. Muchos lenguajes de programación tienen variables e incluso funciones predefinidas. Podemos usarlos fácilmente sin tener que reescribir todo el código de nuevo. Solo necesitaremos saber qué palabra clave utilizar.

Existen muchas variables predefinidas en MQL5, y podemos acceder a sus valores, como en los ejemplos siguientes:

  • _Symbol - símbolo actual del gráfico.
  • _Point - valor en puntos del símbolo actual: 0,00001 para cinco dígitos del símbolo actual y 0,001 para tres dígitos del símbolo actual.
  • _Period - periodo o marco temporal actual del símbolo.
  • Digits - número de decimales (cinco o tres).
  • _LastError - valor del último error.
  • _RandomSeed - estado actual del generador de números pseudoaleatorios.
  • _AppliedTo - tipo de datos para calcular el indicador.

Estas y otras variables predefinidas pueden consultarse en la sección homónima de la documentación.


Conclusión

Hoy hemos abarcado ciertas complejidades de MQL5, centrándonos en conceptos básicos de programación como los tipos de datos, las variables y otros elementos clave cruciales para el desarrollo de aplicaciones modernas. El dominio de MQL5 requiere comprender tanto los aspectos básicos como los avanzados, mientras que la práctica resulta fundamental para aumentar el nivel de profesionalidad. Si desea aprender más sobre la programación o el desarrollo de sistemas comerciales basados en indicadores técnicos populares como medias móviles, RSI y otros, podrá leer mis otros artículos en la sección correspondiente.


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

Indicadores personalizados (Parte 1): Guía introductoria paso a paso para desarrollar indicadores personalizados simples en MQL5 Indicadores personalizados (Parte 1): Guía introductoria paso a paso para desarrollar indicadores personalizados simples en MQL5
Aprenda a crear indicadores personalizados utilizando MQL5. Este artículo introductorio le guiará a través de los fundamentos de la construcción de indicadores personalizados simples y demostrar un enfoque práctico para la codificación de diferentes indicadores personalizados para cualquier programador MQL5 nuevo en este interesante tema.
Características del Wizard MQL5 que debe conocer (Parte 17): Negociación con multidivisas Características del Wizard MQL5 que debe conocer (Parte 17): Negociación con multidivisas
La negociación con varias divisas no está disponible por defecto cuando se crea un asesor experto mediante el asistente. Examinamos dos posibles trucos que los operadores pueden utilizar para poner a prueba sus ideas con más de un símbolo a la vez.
Creamos un asesor multidivisa sencillo utilizando MQL5 (Parte 7): Señales de los indicadores ZigZag y Awesome Oscillator Creamos un asesor multidivisa sencillo utilizando MQL5 (Parte 7): Señales de los indicadores ZigZag y Awesome Oscillator
En este artículo, entenderemos por EA multidivisa un EA o robot comercial que utiliza indicadores ZigZag y Awesome Oscillator que filtran mutuamente sus señales.
Desarrollando un cliente MQTT para MetaTrader 5: metodología TDD (Parte 6) Desarrollando un cliente MQTT para MetaTrader 5: metodología TDD (Parte 6)
Este artículo supone la sexta parte de la serie que describe las etapas de desarrollo de un cliente MQL5 nativo para el protocolo MQTT 5.0. En esta parte, describiremos los principales cambios en nuestra primera refactorización, obteniendo un borrador de trabajo de nuestras clases de construcción de paquetes, creando los paquetes PUBLISH y PUBACK y la semántica de los códigos de motivo PUBACK.