English Русский 中文 Deutsch 日本語 Português
preview
Aprendiendo MQL5 de principiante a profesional (Parte II): Tipos de datos básicos y uso de variables

Aprendiendo MQL5 de principiante a profesional (Parte II): Tipos de datos básicos y uso de variables

MetaTrader 5Ejemplos | 5 julio 2024, 16:25
432 0
Oleh Fedorov
Oleh Fedorov

Introducción

En mi artículo anterior, revisamos los principales programas usados por los programadores de MQL5 (y llegamos a la conclusión de que el MetaEditor IDE es suficiente para los principiantes). También echamos un vistazo rápido al concepto de función y creamos un sencillo script que envía un mensaje para el usuario al registro del sistema. Estos registros pueden consultarse en la parte inferior de la ventana del terminal, en la pestaña "Expertos".

Recordatorio: una función es una descripción de una acción.

Solo utilizaremos funciones predefinidas: OnStart y Print, además, la primera la "rellenaremos" nosotros, mientras que la segunda, que emite la información que necesitamos, la tomaremos ya hecha y solo le pasaremos los parámetros. Y, en general, un programador podrá crear sus propias funciones personalizadas que necesite para resolver sus tareas.

Cada función consta de pasos-acciones muy elementales denominados operadores. Estas acciones son bastante sencillas: comparar dos números, repetir varias veces un fragmento de código, pegar dos trozos de texto, llamar a otra función... Y así sucesivamente. Hay bastantes, y algunas las repasaremos en este artículo.

Los algoritmos están formados por una secuencia de operadores.

Un algoritmo supone instrucciones claras y comprensibles al ejecutor (en este caso, la computadora) para realizar ciertas acciones que conducen a la solución de una tarea específica, más global. Y por eso hay un número enorme de algoritmos, dado que un mismo problema suele poder resolverse de muchas formas distintas.

Por ejemplo, el propio trading. Entrar en una operación, salir de ella, generar los registros correspondientes... todo esto puede hacerse de distintas maneras. Y la computadora debe tener absolutamente claro lo que usted (o su cliente) quiere en este caso concreto.

Podemos decir que los algoritmos más complejos se componen de otros más simples, y que cada algoritmo se realiza mediante alguna función que ejecuta algunas acciones. Y estas acciones se realizan sobre datos. Los datos pueden ser precios de compra y venta o volúmenes de transacciones que llegan varias veces por segundo. O puede haber puntos en la pantalla entre los que necesitemos dibujar una cadena o una curva. O un sonido que se reproduzca en el momento de la transacción. O un texto, como una lista de cotizaciones de un periodo determinado. O... Espero que la idea esté clara.

Lo importante es que estos datos deben almacenarse en algún sitio.

Hoy hablaremos de cómo se almacenan los datos en la memoria RAM. Los datos se almacenan en la memoria en variables o constantes.

Las diferencias son evidentes:

  • Las variables se modifican, el programa tiene derecho a sobrescribirlas;
  • Las constantes permanecen invariables durante toda la vida del programa, y si el programador intenta sobrescribir sus valores, recibirá un mensaje de error de compilación.

Por lo demás, su significado es exactamente el mismo: se trata de una zona de la RAM que almacena datos, no instrucciones del procesador. Normalmente, la gente nombra estas áreas de memoria con algunas palabras significativas para que ellos mismos puedan entender por qué han escrito "esto" aquí.

Obviamente, el compilador eliminará estos nombres más adelante, pero si tenemos acceso al código fuente (nuestro archivo de texto), siempre podremos entender en el futuro para qué sirve una determinada variable según su nombre. Si, por supuesto, las describimos correctamente.

En algunos casos, las constantes no tienen nombre. El programador simplemente escribe con exactitud lo que quiere procesar (como las cadenas que pasamos a la función Print). Estas constantes sin nombre se denominan literales.

En este artículo, veremos con detalle los tipos de datos básicos, cómo se describen las variables y las constantes, y los operadores básicos que un programador puede utilizar para crear sus algoritmos. Esto, a su vez, nos permitirá crear programas más útiles que un simple "Hola, mundo".


Código básico para comprobar todas las expresiones del artículo

En el artículo anterior creamos un programa sencillo: un script que envía datos a la pestaña "Expertos" del panel inferior del terminal. Aquí lo tenemos:

//+------------------------------------------------------------------+
//|                                                   HelloWorld.mq5 |
//|                                       Oleg Fedorov (aka certain) |
//|                                   mailto:coder.fedorov@gmail.com |
//+------------------------------------------------------------------+
#property copyright "Oleg Fedorov (aka certain)"
#property link      "mailto:coder.fedorov@gmail.com"
#property version   "1.00"
//#property script_show_inputs

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
  Print("Hello, MQL5 world!");
  }

//+------------------------------------------------------------------+

Ejemplo 1. Texto completo de un script sencillo

Precisamente este código modificaremos hoy, sustituyendo todas las cadenas de los ejemplos dentro de llaves (a menos que las explicaciones de los ejemplos especifiquen explícitamente algún otro lugar para la inserción).


Literales

Veamos exactamente cómo podemos imprimir los datos.

Empezaremos por las cadenas.

Un literal de cadena va entre comillas dobles <">. Sin embargo, no todos los caracteres pueden mostrarse sin más. Algunos de ellos tienen un significado especial en el lenguaje (las mismas comillas), otros no se muestran en absoluto, como el carácter de nueva línea.

Existe una convención especial para estos caracteres: se escriben con una barra invertida:

Print(
    "Symbol <\"> can't be placed"
    " without (\\).\n  And we can carrying over the string now."
  );

Ejemplo 2. Uso de barras invertidas para mostrar caracteres especiales

En el ejemplo 2, las comillas que describen las cadenas a mostrar aparecen en verde, y los caracteres especiales en amarillo. Así, el carácter "\n" indica un salto de línea.

Ejemplo de muestra de literales de cadena

Figura 1. Ejemplo de muestra de literales de cadena

Fíjese también en otro matiz. En MQL5, las cadenas muy largas se pueden romper en cualquier lugar (si tomamos cada parte entre comillas). No es necesario insertar ninguna otra acción para pegar estas cadenas. Si el compilador detecta dos literales de cadena seguidos (como en el ejemplo), concatenará automáticamente las cadenas en una cadena mayor y buscará caracteres especiales en ella.

En la guía de ayuda oficial encontrará una tabla completa de caracteres especiales, así como una descripción más detallada de las constantes de caracteres.

Los segundos más usados son los números.

Los números enteros parecen normales:

Print(5);

Ejemplo 3. Muestra de un número entero

Los fraccionarios también son fáciles de mostrar. Quienes han trabajado con una calculadora saben que los puntos se utilizan para separar las partes enteras de las fraccionarias:

Print(5.5);

Ejemplo 4. Muestra de fracciones decimales

Y para números muy grandes o muy pequeños, podemos utilizar la notación de coma flotante (a veces llamada "forma exponencial"):

Print(5.5e3);
Print(5.5e-3);

Ejemplo 5. Uso de literales de coma flotante

El resultado de un script que usa todas estas formas de números se muestra en la figura 2.

Mostramos números con la función Print

Figura 2. Resultado de los números mostrados por la función Print

Observe cómo la función ha transformado los datos que le han transmitido en las dos últimas cadenas. Esto sugiere que los datos se han tomado exactamente como números y se han procesado de la forma correspondiente.

A veces, los programadores tienen que tratar con números hexadecimales. Los principiantes rara vez los necesitan, pero a veces pueden resultar útiles, por ejemplo para describir un color. Para este caso, sepa que los números hexadecimales constan de cifras (0..9) y/o letras (a..f).

Para que el programa entienda exactamente que se trata de un literal hexadecimal, deberemos añadir los caracteres "0x" (cifra cero y letra equis).

Las mayúsculas y minúsculas no son importantes.

Print(0xAF35);

Ejemplo 6. Utilizamos un número hexadecimal

Muestra de un número hexadecimal

Figura 3. Resultado de la muestra de un número hexadecimal

El resultado del script se muestra en la figura 3. El número se ha convertido de nuevo (a un número entero normal), por lo que de nuevo la computadora nos ha entendido a la perfección.

Los programadores de MQL5 con frecuencia tienen que utilizar fechas.

Para registrar la fecha y la hora al completo, comenzaremos con una letra "D" mayúscula, luego colocaremos un apóstrofe < ' >, escribiremos la fecha deseada con puntos o barras si es necesario, espaciaremos la hora, separando minutos y segundos con dos puntos, y colocaremos otro apóstrofe:

  Print(D'24/12/23');
  Print(D'24.12');
  Print(D'24.12.2023 7:55:34');

Ejemplo 7. Utilizamos la fecha y la hora

Sin embargo, obtendremos una advertencia del compilador sobre la segunda cadena, diciendo que la fecha está incompleta... No obstante, el archivo acabará compilándose y las tres cadenas funcionarán correctamente:

Advertencia sobre literal incompleto

Figura 4. Advertencia (MetaEditor) sobre literal de fecha incompleto

Resultados del script (muestra de la fecha)

Figura 5. Resultados del script de muestra de fecha (terminal)

Observe que al compilar la primera cadena se ha sustituido la hora de inicio del día, y al compilar la segunda cadena se ha sustituido la hora de compilación. Bueno, la conversión de formato demuestra una vez más que el software nos ha comprendido correctamente....

Y podemos realizar cualquier acción con cualquier literal que esté permitida para este tipo de datos.

Por ejemplo, podemos comparar números, realizar operaciones aritméticas con ellos y transmitirlos a funciones como parámetros.

Las filas también se pueden sumar (se pegan entre sí), pero no se pueden restar unas a otras.

Veamos el siguiente ejemplo:

Print( "This will be calculated: "+4+9 );
Print( "This will be calculated: "+(4+9) );

Ejemplo 8. Uso de paréntesis al escribir expresiones.

Advertencias del compilador sobre el uso de números en una expresión de cadena

Figura 6. Advertencia al compilador sobre el uso de números en una expresión de cadena

Resultados de los cálculos

Figura 7. La función ha mostrado el resultado de los cálculos

Observe que donde no había paréntesis, los números estaban simplemente "pegados" al texto, y donde los había, todo estaba calculado correctamente. Es un error bastante difícil de ver, por eso el compilador nos lo advierte enseguida. Existen funciones especiales para convertir números en cadenas explícitamente. Pero de momento, recuerde: los paréntesis son importantes.


Descripción de constantes usando la directiva de preprocesador #define

Si no quiere confundirse en su propio código, sino que quiere entender "para qué sirve este 5" y "qué significa este 72", será mejor que dé nombres significativos a todas sus constantes. La directiva #define del preprocesador es la más usada para este fin:
#define name value

Ejemplo 9. Directiva #define

Recordemos que un lenguaje de preprocesador es una especie de "lenguaje dentro del lenguaje", y que este lenguaje describe las acciones antes de que la compilación comience.

Normalmente, el funcionamiento del preprocesador consiste en cambiar unos fragmentos de código por otros. Así, la directiva #define indica al compilador que sustituya en todo nuestro código "nombre" por "valor" en la primera pasada, y que solo entonces inicie todo tipo de comprobaciones sintácticas, etc.

Por ejemplo, podríamos escribir:

  #define MY_SUPER_CONSTANT 354
  Print(MY_SUPER_CONSTANT);

Ejemplo 10. Directiva #define

El programa muestra un valor pero no el nombre

Figura 8. El programa muestra el valor de la constante, pero no el nombre

El programa mostrará el número 354, no su nombre.

Obsérvese que después del literal que describe el número, no hay punto y coma.

Al describir constantes utilizando una directiva de preprocesador, el punto y coma no es necesario.

Si lo hubiéramos escrito, el preprocesador habría sustituido este signo junto con el número -dentro de los paréntesis de Print- y habríamos recibido un mensaje de error de compilación.

Así que, recuerde: nombraremos cualquier constante, y en las expresiones usaremos el nombre, no el valor.

Por cierto, los nombres resultan muy útiles cuando la misma constante se utiliza varias veces en distintos lugares de un programa o cuando varias constantes tienen el mismo valor. Si de repente resulta que el valor de una constante ha cambiado, será mucho más fácil cambiarlo en un solo lugar, normalmente al principio del documento o en un archivo aparte, en lugar de tener que buscar en todos los demás lugares y pensar cada vez qué se quería decir con tal o cual número.


<Descripción de variables

Recuerde: si debemos cambiar los datos en la memoria durante el funcionamiento, utilizaremos variables.

La descripción de las variables tampoco resulta complicada. Solo tenemos que escribir exactamente lo que queremos almacenar:

type variable_name;

Ejemplo 11. Plantilla de descripción de variables

Esta entrada significa que el compilador debe asignar una cierta cantidad de memoria a nuestros datos. A continuación ofrecemos más detalles sobre los tipos y sus tamaños.

Ahora podemos acceder a estos datos por su nombre (nombre_variable).


Convenciones sobre identificadores

El nombre (a menudo dicho "identificador") de una variable, y en general cualquiera que se nos ocurra, debe

  • ser informativo para nosotros (mejor "chartNumber" que "sss");
  • constar de letras del alfabeto latino, dígitos y un guión bajo (_), 

El nombre NO debe:

  • empezar con un número;
  • coincidir con las palabras reservadas;
  • superar los 63 caracteres de longitud.

Las mayúsculas y minúsculas del nombre son importantes. Por lo tanto, myVariable y MyVariable son nombres diferentes. (Y, por supuesto, le desaconsejamos encarecidamente utilizar ambos nombres en el mismo archivo).

Si accidentalmente confunde un registro en algún lugar del texto del programa, el compilador generará un mensaje de error: "Variable no descrita", así que podrá arreglarlo fácilmente. Sin embargo, si describe ambas variables según todas las reglas, pero solo se diferencian en una letra, será demasiado fácil confundirse.

Por lo demás, no existen restricciones. Incluso podrá nombrar su variable con el nombre de alguna función incorporada, si así lo quiere (esperemos que no).


Operación de asignación

La operación de asignación se usa para escribir datos en una variable. A veces esto se hace justo en la descripción, entonces hablaremos de inicialización:

// Initialization (at creation)
int counter = 0;
// Normal assignment
counter = 10;

Ejemplo 12. Asignación simple

La palabra int indica que la variable solo puede contener datos de tipo entero.

En este ejemplo, "=" significa operador de asignación. En este caso, se escribe un número entero en la celda llamada counter.

Todos los datos que había en la celda antes de la asignación se perderán.

Los datos de esta variable se pueden utilizar en cualquier parte del programa, como de costumbre, según su nombre. Por ejemplo, podemos transmitirla como parámetro a cualquier función o utilizarla en una expresión (encontrará ejemplos más adelante en el artículo).


Operación de asignación - matices

La asignación puede ser muy sencilla, como hemos descrito en la sección anterior. Pero si utilizamos esta operación en expresiones, es de esperar que las siguientes observaciones le ayuden a utilizarla con mayor eficacia.

  • La prioridad de la operación de asignación es la más baja, por lo que se efectúa de derecha a izquierda. Es decir, el lado derecho es una expresión, mientras que el lado izquierdo es una variable donde se escriben los datos y se evalúa primero la expresión:
a = b + c;

Ejemplo 13. La asignación tiene la prioridad más baja.

La expresión anterior realizará primero la suma de b y c, y luego el resultado de dicha expresión se escribirá en la variable a.

  • como consecuencia de la observación anterior, el valor de una variable se podrá utilizar en una expresión y luego el resultado obtenido lo podremos escribir en la misma variable:
a = a — c;

    Ejemplo 14. En la expresión, podemos utilizar el valor anterior de la variable

    • En MQL5, las expresiones en las que la misma variable aparece una vez a la derecha y otra a la izquierda (como en el ejemplo anterior) se pueden acortar desplazando el signo de la expresión a la parte izquierda de la operación:
    a -= c;

    Ejemplo 15. Uso abreviado de la asignación

    Esta abreviatura es válida para cualquier operador binario (que utiliza dos valores como suma o multiplicación): multiplicación, división, desplazamiento... Lo principal es que la variable de esta expresión pueda aislarse fácilmente.

    Por ejemplo, para la expresión a = a*(1+1/a) este truco no funcionará si no abrimos los paréntesis, pero para a = a*(b+c) - será muy sencillo: a *= b+c.

    • Si queremos aumentar o disminuir un entero en 1, no necesitaremos utilizar la asignación. En su lugar podremos utilizar operaciones de incremento y decremento:
    a++; // Increment. Increase a by 1
    b--; // Decrement. Decreases b by 1
    

    Ejemplo 16. Incremento y decremento

    Estas operaciones son unarias, es decir, solo requieren una variable para operar. La peculiaridad de estas operaciones es que tienen dos formas de registro: prefijo y postfijo.

    Si los utilizamos en forma de prefijo, primero se realizará la acción y después se utilizará el resultado en la expresión.

    Pero si utilizamos postfijos, primero se utilizará el valor antiguo de la variable y luego se cambiará a 1:

    int a = 1;
    Print (++a); // 2, and a == 2
    Print (a++); // 2, but a == 3
    

      Ejemplo 17. Forma prefija y postfija de incremento (el decremento se usa exactamente igual)

      • Podemos utilizar varios operadores de asignación seguidos, en "cascada". La secuencia se mantendrá de derecha a izquierda.
      int a=1, c=3;
      a = c = a+c; // first a+c (4), then c = 4, then a = c (i.e. a = 4)
      

      Ejemplo 18. Atribución en "cascada"


      Tipos de datos básicos

      Existen relativamente muchos tipos de datos.

      Existen tipos "simples" (o "básicos"), como cadenas, números, fechas, colores, etc., y tipos "complejos", que inventa el propio desarrollador de programas MQL5.. Normalmente, los tipos de datos "complejos" son una combinación de los simples, en los que varias variables se combinan en un solo bloque para mayor comodidad.

      En este artículo solo trataremos los tipos básicos. Hablaremos de los más complejos en la próxima parte.

      Tipos enteros

      En primer lugar, deberemos entender que los números enteros son la principal forma de "pensar" del computadora.

      Las operaciones con números enteros son sencillas y rápidas para la misma, pero si el resultado de la operación supera un determinado rango de valores, puede producirse una pérdida de datos.

      El segundo punto importante es que los números enteros existen con y sin signo.

      Si el número es "sin signo", puede usar números de 0 al máximo. Digamos que los números que ocupan 1 byte pueden crecer de 0 hasta 28-1 = 255, un total de 256 valores.

      Si alguien intenta escribir un número "256" o "-1" en una variable de este tipo, se producirá un "desbordamiento", el resultado seguirá estando dentro de los mismos límites [0..255] , y el resto de los datos se perderán. En principio, a veces puede resultar útil, pero en la mayoría absoluta de los casos es mejor utilizar otros métodos para esas transformaciones. Por ejemplo, podemos utilizar la operación de resto de la división (más adelante en este artículo). Y será mejor utilizar variables de los tipos que están garantizados para admitir todos nuestros datos sin pérdida.

      La letra "u" (del inglés unsigned, sin signo) se antepone a los nombres de los tipos que empiezan por 0 (y, de hecho, describen números naturales).

      Las cifras "con signo" utilizan el mismo rango de valores, dividido por la mitad. Así, los mismos números de un byte serán correctos en el intervalo [-128..127].

      Tabla 1. Tipos de datos enteros.

      Nombre
      Tamaño, bytes
       Valor mínimo
      Valor máximo
      char
      1 (8 bits)
      -128
      127
      uchar
      1 (8 bits)
      0
      255
      short
      2 (16 bits)
      -32 768
      32 767
      ushort
      2 (16 bits)
      0
      65 535
      int
      4 (32 bits)
      -2 147 483 648
      2 147 483 647
      uint
      4 (32 bits)
      0
      4 294 967 295
      long
      8 (64 bits)
      -9 223 372 036 854 775 808
      9 223 372 036 854 775 807
      ulong
      8 (64 bits)
       0  18 446 744 073 709 551 615

      En la práctica, los tipos más utilizados son int (porque esta palabra es rápida de escribir y los datos de este tipo tienen un tamaño suficientemente grande) y long (el tamaño es suficiente para la absoluta mayoría de las tareas, y el compilador puede optimizar el bytecode para obtener el mejor rendimiento en los computadoras modernas).

      Sin embargo, también pueden resultar útiles otros tipos de enteros.

      Tipo booleano (lógico)

      Se denota con la palabra clave bool, ocupa 1 byte en memoria y solo puede tomar dos valores: true o false ("verdadero" o "falso").

      Si es absolutamente necesario, se puede usar cualquier número como dato lógico. Si un número es igual a 0 es "false", si no es igual es "true". Pero debemos aplicar esta posibilidad con mucha precaución...

      Números reales (también conocidos como números de coma flotante)

      Tabla 2. Tipos de datos reales

      Nombre
       Tamaño, bytes Valor positivo mínimo  Valor máximo  
      float
      4 (32 bits)
      1.175494351e-38
      3.402823466e+38
      double
      8 (64 bits)
      2.2250738585072014e-308
      1.7976931348623158e+308

      En la práctica moderna, se usa sobre todo el tipo double. Hace tiempo que no me encuentro con el tipo float en los códigos de los demás. Quizá se haya dejado por compatibilidad con versiones anteriores. Aunque en conjuntos de datos muy grandes, probablemente siga siendo útil, para ahorrar espacio de memoria.

      Los números reales representan perfectamente los precios, la cantidad de la divisa necesaria y otros conceptos útiles.

      Abarcan una gama de valores mucho mayor que los números enteros.

      Sin embargo, para una computadora no resulta muy "cómodo" trabajar con estos tipos. En primer lugar, las operaciones con números reales son un poco más lentas que las operaciones con enteros y, en segundo lugar, debido a las peculiaridades del formato, casi siempre se obtienen cálculos con errores en los últimos dígitos. Así, en lugar de 1,0 en alguna operación podríamos obtener 1,00000001 y en otro caso 0,99999999.

      Por lo tanto, si es necesario comparar dos números reales, se suele tomar su diferencia y compararla con algún valor pequeño, inequívocamente mayor que el error. Es más seguro.

      Fecha y hora

      Se denomina con la palabra datetime, y ocupa 8 bytes en memoria.

      Cada variable de este tipo contiene el número de segundos transcurridos desde el 1 de enero de 1970 hasta la fecha deseada, es decir, un número entero ordinario.

      La última fecha posible es el 31 de diciembre de 3000. Quizá hayamos tenido suficiente por nuestra época, y el "error 2000" (quién se acuerda :-) ) no supone por ahora una amenaza....

      Existen constantes predefinidas especiales:

      • __DATE__ — fecha de compilación;
      • __DATETIME__ - fecha y hora de compilación;
      • podemos utilizar la expresión __DATETIME__ — __DATE__, esta describe solo el tiempo de compilación, sin fecha.

      Al escribir un literal, podemos omitir todo y escribir el valor como D'' (D y dos apóstrofes). Una entrada de este tipo equivale a __DATETIME__. No obstante, la legibilidad del código disminuye mucho en este caso.

      Colores

      El color en MQL5 se define por un tipo separado, que se llama, por supuesto, color. Para describir un color, podemos utilizar un literal:

        color myColor1=C'100,200,30';
        color myColor2=C'0xFF,0x00,0x5A';
      

      Ejemplo 19. Describimos un color mediante números decimales o hexadecimales

      También podemos utilizar constantes predefinidas para los colores web. Los nombres de las constantes de color empiezan por las letras clr (por ejemplo, clrBlue - azul). Podemos ver la lista completa de constantes escribiendo clr en el MetaEditor, o leyendo la guía de ayuda oficial.

      En el ejemplo 12, podemos ver que cada descripción de color consta de tres fragmentos. Cada uno de estos fragmentos describe la intensidad luminosa de un punto rojo, verde o azul de la pantalla (Red, Green, Blue = RGB). Juntos dan toda la variedad de colores. Mezclando rojo y verde, obtenemos todos los matices de la gama amarillo-marrón-naranja. El rojo y el azul nos dan los tonos violeta-rosados. El verde y el azul ofrecen diferentes variaciones de turquesa, azul, etc.

      Mezclando los tres tonos en proporciones iguales se obtiene la gama de tonos grises: desde el negro, cuando todos los componentes están "apagados", es decir, tienen una intensidad igual a 0, hasta el blanco, cuando todos tienen una intensidad máxima de 255 (0xFF)-. Si las proporciones son desiguales, es decir, la intensidad luminosa de cada componente es diferente a la de los demás, se obtendrán todas las demás tonalidades en el monitor. Por regla general, el verde aclara el color global, el azul lo oscurece, pero, por supuesto, en general, cuanto más brillante sea el componente, más claro será (y más claro será el color global).

      Todas estas reglas se ilustran en la tabla 3, donde nos hemos limitado a colorear las celdas con los colores disponibles en el editor.

      En la práctica, a menudo no es necesario conocer los valores numéricos de cada color, bastará con elegir un color de una paleta especial o pasar a la función una constante predefinida en el lenguaje. Pero, en mi opinión, entender cómo funcionan las cosas "desde dentro" sigue resultando útil.

      Los datos de color ocupan 4 bytes en la memoria, aunque solo se utilizan 3 de ellos. Este ha sido el caso históricamente, y es la convención general para cualquier software que pueda trabajar con este modelo de descripción del color.

      Tabla 3. Ejemplos de uso de colores

      0, 0, 0 156, 15, 15 106, 0, 86 0, 49, 110 0, 110, 41 56, 37, 9 56, 37, 9
      51, 51, 51 191, 3, 3 133, 2, 108 0, 67, 138 0, 137, 44 243, 195, 0 87, 64, 30
      102, 102, 102 226, 8, 0 160, 39, 134 0, 87, 174 55, 164, 44 255, 221, 0 117, 81, 26
      153, 153, 153 232, 87, 82 177, 79, 154 44, 114, 199 119, 183, 83 255, 235, 85 143, 107, 50
      204, 204, 204 240, 134, 130 193, 115, 176 97, 147, 207 177, 210, 143 255, 242, 153 179, 146, 93
      255, 255, 255 249, 204, 202 232, 183, 215 164, 192, 228 216, 232, 194 255, 246, 200 222, 188, 133

      Si así lo deseamos, podremos trabajar con el color del mismo modo que con los números enteros normales.

      Por ejemplo, aquí tenemos un código:

        color a = C'255,0,0';
        color b = C'0,255,0';
        color d = a+b;
        Print(a," ",b," ",d);
      

      Ejemplo 20. Utilización de colores en expresiones aritméticas

      Este es el resultado:

      Resultado del uso de colores en expresiones aritméticas

      Figura 9. Resultado del uso de colores en expresiones aritméticas

      Enumeraciones

      El último tipo básico que veremos son las enumeraciones.

      Hay situaciones en las que el significado de la tarea requiere que una variable adopte solo determinados valores. Por ejemplo, una tendencia puede ser bajista, alcista o lateral (plana). O las órdenes de compra: comprar en el mercado (Buy), posponer hasta que el precio alcance un determinado nivel en una ruptura (Buy Stop) o en un rebote (Buy Limit). O los días de la semana. En cualquier caso, creo que el principio queda claro.

      Para eso se usan las enumeraciones.

      La descripción de las enumeraciones consta de tres pasos.

      1. El primero consiste en crear una lista propia y llamarla de alguna manera. El nombre resultante será el nombre del tipo para cualquier variable o función. La única diferencia entre este nombre y los tipos predefinidos es que se nos ha ocurrido a nosotros.
      2. El segundo paso es crear una variable de dicho tipo.
      3. Por último, en el tercer paso, podremos utilizar esta variable.
      El ejemplo 15 muestra cómo describir y usar una enumeración. Para ilustrar esto, hemos tomado la descripción de una dirección (DIRECTION), que solo puede utilizar tres valores: "Ascenso", "Descenso", "Lateral".

      //--- First step: creating a new list (new data type)
        enum ENUM_DIRECTION
         {
          Upward,
          Downward,
          Aside
         };
      
      //--- Second step: description (and, if necessary, initialization) of a variable of this type
        ENUM_DIRECTION next=Upward;
      
      //--- Third step: using the variable
        Print(next);
      

      Ejemplo 21. Ejemplo de descripción y uso de una enumeración

      Normalmente, la lista de enumeración se crea al principio del archivo, justo después de las directivas del preprocesador. En este caso, estará disponible para todas las funciones, de nuestra aplicación, de manera global,

      aunque podemos describirlo localmente, dentro de alguna función. Entonces esta enumeración no resultará visible desde otras funciones. La mayoría de las veces no tiene mucho sentido hacerlo, pero no sabemos a qué retos nos enfrentaremos....

      Los nombres de los elementos de la lista aparecen entre llaves, separados por comas.

      Después de la llave de cierre de cualquier descripción de tipo (incluidas las enumeraciones) necesitaremos obligatoriamente punto y coma. Este podría no ser el caso de otros bloques.

      Las enumeraciones predefinidas en el lenguaje tienen los nombres escritos en mayúsculas, y estos nombres comienzan con el prefijo ENUM_. Podemos llamar a sus listados como queramos (dentro de los límites), pero será buena idea ceñirse a las mismas normas.

      La representación interna de las enumeraciones es un entero con signo que ocupa 4 bytes en memoria.

      Si intentamos ejecutar el código del ejemplo 21, veremos el número 0. Por lo tanto, cuando dejamos que MQL5 seleccione números para los elementos de enumeración por sí mismo, empezaremos desde cero.

      No obstante, podemos especificar otro número, solo tendremos que especificarlo explícitamente:

      //--- First step: creating a new list (new data type)
        enum DIRECTION
         {
          Upward = 1,
          Downward = -1,
          Aside = 0
         };
      

      Ejemplo 22. Especificación explícita de los valores de enumeración

      No tendremos que especificar todos los valores.

      Si especificamos algunos valores y otros no, MQL5 recogerá los números por sí mismo, basándose en el orden de los elementos y en el último número más alto. Esto significa que si en el ejemplo 15 establecemos Upward = 1 y eliminamos todas las demás inicializaciones, Downward será 2, y Upward será 3. Le recomiendo que lo compruebe usted mismo.


      Expresiones y operadores simples

      Al trabajar con datos, es importante poder compararlos, realizar operaciones matemáticas, etc. Distintas expresiones tienen sentido para distintos tipos de datos.

      Operadores de comparación

      Estos operadores tienen sentido para todos los tipos de datos.

      El resultado de la comparación será, por supuesto, lógico.

      Existen los siguientes operadores de comparación:

      • más que ( > ), 
      • menos que ( < ), 
      • mayor o igual que ( >= ), 
      • menor o igual que ( <= ), 
      • igual a ( == ), 
      • no igual a ( != )

      La prioridad de todas estas operaciones es la misma.

      Al comparar cadenas, la computadora seguirá la disposición de los caracteres en la codificación. Por ejemplo, la letra "A" mayúscula será anterior a la "a" minúscula, por lo que será más pequeña y, por tanto,

      "Assol" < "a salt?" //true

      Ejemplo 23. Comparación de cadenas. Las mayúsculas son más pequeñas que las minúsculas

      Si tenemos varios caracteres idénticos en una cadena, se seleccionará el primer carácter desigual para la comparación.

      "Assol"> "a salt?" //true

      Ejemplo 24. Las primeras letras coinciden

      La expresión del ejemplo 24 es correcta porque el espacio en la codificación va antes de los caracteres alfabéticos y las primeras letras son iguales.

      Si la cadena ya ha terminado y la segunda todavía está en curso, y los caracteres han coincidido hasta ahora, la cadena más pequeña será la que ha terminado. Por ejemplo:

      "Song" < "Bunny Song" //true

      Ejemplo 25. Diferentes longitudes de cadena

      Operaciones aritméticas

      El resultado vendrá determinado por el tipo de datos que intervienen en la expresión.

      Podemos realizar operaciones aritméticas con números:

      • signo antes del número ( -3) (a veces se dice menos "unario");
      • multiplicación ( * ), división ( / ) (redondea hacia abajo para números enteros), resto de la división ( % ) (solo para números enteros, 5%2 == 1);
      • suma ( + ), resta ( - );
      • incremento ( ++ ), decremento ( -- )

      La lista se muestra por orden de prioridad.

      Sin embargo, será mejor no mezclar el incremento y el decremento en expresiones ordinarias con el resto de operadores aritméticos, ya que puede haber situaciones en las que el resultado sea indefinido.

      La operación ( + ) también se define para cadenas, pero aquí denota el pegado (convertir dos cadenas cortas en una larga).

      Operaciones bit a bit

      El resultado es un número entero.

      Para los números enteros, también existen las operaciones bit a bit:

      • negación bit a bit ( ~ );
      • desplazamiento a la derecha ( >> );
      • desplazamiento a la izquierda ( << );
      • "and" bit a bit ( & );
      • "or" bit a bit ( | );
      • "o" excluyente ( ^ ).

      Si llega a necesitarlas, entonces definitivamente ya no será un principiante y podrá echar un vistazo a la guía de ayuda. :-)

      La lista se muestra por orden de prioridad.

      Operadores lógicos

      El resultado lógico.

      • de la negación lógica ( ! );
      • de la multiplicación lógica, la "y" lógica ( && );
      • de la suma lógica, la "o" lógica ( || ).

      La lista se muestra por orden de prioridad.

      También existen otras operaciones que trataremos en otros artículos. Además, en otros artículos hablaremos con más detalle de ejemplos de uso de todos los operadores anteriores. De momento, utilizaremos lo que nos parezca lógico o consultaremos la ayuda. No debería ser tan difícil....


      Conversión de tipos

      A veces, en una expresión aritmética interviene más de un tipo de dato. Por ejemplo, con la función Print, mostramos cadenas junto con números todo el tiempo, y en este artículo, incluso colores.

      ¿De qué tipo será el resultado? Aquí tenemos dos opciones: o lo definimos nosotros mismos, o confiamos en el compilador.

      El compilador es, por supuesto, inteligente, lo averiguará..... Pero no siempre.

      Así que vamos a averiguar ahora qué hace el compilador y qué podemos hacer "manualmente" para no perder datos importantes, no recibir advertencias al compilar y estar seguros del resultado.

      En primer lugar, ¿qué hace el compilador?

      En primer lugar, si la expresión usa datos del mismo tipo, el resultado será del mismo tipo. Sin duda, este es el caso más fácil.

      Si intervienen distintos tipos, el compilador intentará ampliar el resultado al más preciso. Por ejemplo, si intentamos sumar un entero de 4 bytes ( int ) con una fecha ( datetime ), obtendremos una fecha (porque su rango resulta más amplio).

      Un literal entero pertenecerá al tipo int, un literal de coma flotante suele pertenecer al tipo double, a menos que coloquemos una "f" minúscula al final:

      5 + 3.4f + 4.25 // The result is double, since 5 is first converted to float, and then 4.25 sets double precision

      Ejemplo 26. Conversión de tipos al usar literales

      La ayuda ofrece un esquema de priorización de las conversiones:

      Prioridades de la conversión de datos

      Figura 10. Prioridades de las conversiones de datos

      Debemos tener en cuenta que las conversiones de los tipos con signo y sin signo entre sí pueden conducir potencialmente a la pérdida de datos, y las conversiones al tipo float, a la pérdida de precisión.

      Por lo tanto, si no estamos seguros de cómo convierte los datos el compilador, podríamos considerar especificar manualmente qué convertir en qué.

      Si necesitamos convertir el resultado (o un valor concreto) a un tipo específico, podemos hacerlo:

      • Bastará con escribir el resultado en una variable de un tipo determinado. Este método es esencialmente una variante de la conversión automática, por lo que deberemos utilizarlo con precaución;
      • Utilizar funciones especiales de conversión de datos;
      • Utilizar la forma abreviada de conversión de tipos.
      (int)a+b // converts a to an integer. b remains unchanged
      double (c+d) // absolutely similar to the previous one. In this case, the result of the summation is converted
      
      // and so on - you can use any suitable type
      
      

      Ejemplo 27. Forma abreviada de conversión de tipos

      Naturalmente, debemos recordar que los paréntesis permiten cambiar el orden de las operaciones porque tienen la máxima prioridad. Si no está seguro, ponga paréntesis y será feliz....


      Conclusión

      Hoy hemos abarcado una gran parte de la teoría sobre los tipos de datos básicos, las variables y las expresiones. Si entiende el material de este artículo y del siguiente, casi dejará de ser un principiante y pasará a un nivel superior :-) Si entiende cómo funcionan las variables (el material de este artículo) y las funciones (eso lo veremos en el siguiente), entonces no deberá tener miedo de la programación orientada a objetos....

      La programación orientada a objetos (POO ) se considera una materia compleja, pero en realidad, si se entienden las bases, las dificultades son más ideológicas que técnicas.

      Para los que no entiendan algunos puntos, les recomiendo que vuelvan a leer el artículo (o más de una vez), solo que muy despacio, un concepto cada vez, comprobando todo lo que se dice en el código.

      Para aquellos que lo hayan entendido todo y no quieran esperar a que yo presente el próximo artículo, les recomiendo que amenicen la espera escribiendo un script propio para mostrar información útil sobre sus instrumentos de trabajo y su balance. La mayor parte de esta información puede obtenerse de la familia de funciones AccountInfo... y SymbolInfo...

      Intente encontrar los nombres completos de cada función de estas familias usando el MetaEditor, y luego busque sus descripciones en la ayuda. Y cuando encuentre esa descripción, sí, con el material que ya conoce... Es posible que la creación de este script ya no requiera un esfuerzo excesivo por su parte.

      P.D. Existe un ejemplo de este tipo de script en la biblioteca estándar. Si le da pereza escribir el suyo propio, pruebe con uno ya hecho. Para los que escriban algo propio, les recomiendo comparar.....

      Traducción del ruso hecha por MetaQuotes Ltd.
      Artículo original: https://www.mql5.com/ru/articles/13749

      Algoritmos de optimización de la población: microsistema inmune artificial (Micro Artificial immune system, Micro-AIS) Algoritmos de optimización de la población: microsistema inmune artificial (Micro Artificial immune system, Micro-AIS)
      El artículo habla de un método de optimización basado en los principios del sistema inmune del organismo -Micro Artificial immune system, (Micro-AIS)-, una modificación del AIS. El Micro-AIS usa un modelo más simple del sistema inmunitario y operaciones sencillas de procesamiento de la información inmunitaria. El artículo también analizará las ventajas e inconvenientes del Micro-AIS en comparación con el AIS convencional.
      Algoritmos de optimización de la población: Algoritmo híbrido de optimización de forrajeo bacteriano con algoritmo genético (Bacterial Foraging Optimization - Genetic Algorithm, BFO-GA) Algoritmos de optimización de la población: Algoritmo híbrido de optimización de forrajeo bacteriano con algoritmo genético (Bacterial Foraging Optimization - Genetic Algorithm, BFO-GA)
      Este artículo presenta un nuevo enfoque para resolver problemas de optimización combinando las ideas de los algoritmos de optimización de forrajeo bacteriano (BFO) y las técnicas utilizadas en el algoritmo genético (GA) en un algoritmo híbrido BFO-GA. Dicha técnica utiliza enjambres bacterianos para buscar una solución óptima de manera global y operadores genéticos para refinar los óptimos locales. A diferencia del BFO original, ahora las bacterias pueden mutar y heredar genes.
      Introducción a MQL5 (Parte 2): Variables predefinidas, funciones comunes y operadores de flujo de control Introducción a MQL5 (Parte 2): Variables predefinidas, funciones comunes y operadores de flujo de control
      En este artículo, seguiremos familiarizándonos con el lenguaje de programación MQL5. Esta serie de artículos no es solo un tutorial, sino también una puerta de entrada al mundo de la programación. ¿Qué hace especiales a estos artículos? Hemos procurado que las explicaciones sean sencillas para que los conceptos complejos resulten accesibles a todos. Aunque el material es accesible, para obtener los mejores resultados será necesario reproducir activamente todo lo que vamos a tratar. Solo así obtendremos el máximo beneficio de estos artículos.
      Redes neuronales: así de sencillo (Parte 71): Previsión de estados futuros basada en objetivos (GCPC) Redes neuronales: así de sencillo (Parte 71): Previsión de estados futuros basada en objetivos (GCPC)
      En trabajos anteriores, hemos introducido el método del Decision Transformer y varios algoritmos derivados de él. Asimismo, hemos experimentado con distintos métodos de fijación de objetivos. Durante los experimentos, hemos trabajado con distintas formas de fijar objetivos, pero el aprendizaje de la trayectoria ya recorrida por parte del modelo siempre quedaba fuera de nuestra atención. En este artículo, queremos presentar un método que llenará este vacío.