Variables y tipos de datos extendidos en MQL5
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:
- Constantes: identificadores de valores fijos.
- Arrays: variables de cualquier tipo con valores múltiples.
- Enumeraciones: listas enteras de constantes con valores enteros.
- Estructuras: conjunto de variables relacionadas de diferentes tipos.
- Conversión de tipos: convertir un tipo de valor en otro.
- Variables locales: variables declaradas localmente dentro de las funciones.
- Variables globales: variables declaradas globalmente fuera de las funciones.
- Variables estáticas: variables locales declaradas que conservan sus valores en la memoria.
- Variables predefinidas.
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.
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:
Como hemos mencionado antes, si intentamos actualizar la variable (val), obtendremos el siguiente error porque es una constante y no se puede actualizar.
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.
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:
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.
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.
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.
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.
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.
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.
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).
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:
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:
Si intentamos acceder a la variable local var desde fuera de la función, el compilador mostrará un error de identificador no declarado:
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.
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"):
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:
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:
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
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso