English Русский 中文 Deutsch 日本語
preview
Operar con el Calendario Económico MQL5 (Parte 2): Creación de un Panel de Noticias

Operar con el Calendario Económico MQL5 (Parte 2): Creación de un Panel de Noticias

MetaTrader 5Trading |
60 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Introducción

En este artículo, partimos de nuestra análisis previo en la Parte 1 del Calendario Económico de MetaQuotes Language 5 (MQL5), donde nos centramos en dominar las funciones necesarias para recuperar y analizar eventos de noticias económicas. Ahora, daremos el siguiente paso creando un panel de control de noticias que proporcione a los operadores una interfaz cómoda para acceder a datos económicos críticos en tiempo real. Este panel de control ayudará a agilizar los procesos de toma de decisiones al destacar las noticias relevantes que podrían influir en los movimientos del mercado. Los temas que cubriremos incluyen:

  1. Diseño del panel
  2. Configuración del panel en MQL5
  3. Conclusión

Con estos componentes, pretendemos mejorar la experiencia comercial al proporcionar una herramienta eficaz para monitorear eventos económicos en tiempo real para las noticias económicas en MQL5.


Diseño del panel

El diseño del panel del tablero es un paso crucial en la creación de una herramienta eficaz para supervisar los acontecimientos económicos utilizando el Calendario económico MQL5. Nuestro objetivo es crear una interfaz fácil de usar y visualmente atractiva que presente información importante de forma clara y concisa. Un tablero de control bien estructurado nos permitirá evaluar rápidamente la relevancia y el impacto de los eventos económicos en nuestras estrategias comerciales.

Al diseñar el panel del tablero, deberemos comenzar por identificar los componentes clave que deben mostrarse. Estos componentes generalmente incluyen el nombre del evento, la hora programada, la moneda afectada, el nivel de importancia y una breve descripción del evento. Para mejorar la usabilidad, organizaremos esta información en formato de tabla, donde cada fila representará un evento económico diferente. Nuestra intención es hacer que la tabla sea fácilmente legible, utilizando colores contrastantes para diferentes niveles de importancia para permitir una rápida identificación de eventos de alto impacto.

Para que el panel sea más atractivo, elegiremos elementos visuales, como bordes, fondos y fuentes para crear una apariencia limpia y profesional. El diseño permitirá una fácil navegación, asegurando que podamos localizar rápidamente la información que necesitamos sin abrumarnos con detalles excesivos. Al mantener el diseño intuitivo y sencillo, permitiremos que los comerciantes se concentren en tomar decisiones informadas basadas en los eventos económicos que se muestran en el panel del tablero. El panel se organizará como se mencionó anteriormente y contará con los componentes ilustrados a continuación:

COMPONENTES DEL PANEL

Con una comprensión clara de nuestros objetivos, profundicemos en el proceso de automatización. En el artículo anterior, nos centramos en dominar las funciones del Calendario Económico MQL5 para recuperar y analizar eficazmente eventos de noticias económicas. Si aún no lo ha hecho, consulte ese contenido para asegurarse de estar bien preparado para seguirnos mientras procedemos a crear nuestro panel de noticias. ¡Comencemos!


Configuración del panel en MQL5

En esta sección, nos centraremos en configurar el panel del tablero creando los elementos necesarios del panel utilizando MQL5. Primero, necesitaremos crear funciones para los tres elementos que necesitaremos: la etiqueta del rectángulo, el botón y las etiquetas de texto. Este enfoque será enormemente beneficioso ya que nos permitirá reutilizar las mismas funciones al crear características similares, eliminando la necesidad de repetir todo el proceso para cada nuevo objeto. Al hacerlo, ahorramos tiempo y espacio, hacemos que el proceso sea rápido y sencillo y mantenemos nuestros fragmentos de código concisos.

Para crear la etiqueta del rectángulo, crearemos una función que tome diez argumentos o parámetros. Esta función definirá las propiedades del rectángulo, como su posición, tamaño, color y estilo, permitiéndonos personalizar la apariencia visual de la etiqueta según los requisitos de diseño de nuestro tablero.

//+------------------------------------------------------------------+
//|     Function to create rectangle label                           |
//+------------------------------------------------------------------+

bool createRecLabel(string objName, int xD, int yD, int xS, int yS,
                    color clrBg, int widthBorder, color clrBorder = clrNONE,
                    ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID) {

...
}

La firma de la función lo ilustra todo. Es una función booleana que tiene el nombre "createRecLabel", lo que significa que devolverá dos indicadores booleanos, verdadero o falso, en caso de éxito o fracaso respectivamente. Para comprender fácilmente sus parámetros, los detallaremos y explicaremos individualmente a continuación.

  • "objName:" Este parámetro representa el nombre único del objeto de etiqueta de rectángulo. Sirve como identificador del elemento gráfico que se está creando.
  • «xD e yD:» Estos parámetros determinan las distancias X e Y desde la esquina donde se colocará la etiqueta del rectángulo. Piense en ellos como las coordenadas que definen la esquina superior izquierda del rectángulo en relación con el gráfico.
  • «xS e yS»: Estos parámetros especifican la anchura y la altura del rectángulo. El valor «xS» determina la anchura horizontal del rectángulo, mientras que «yS» controla su altura vertical.

DISTANCIA Y TAMAÑO

  • «clrBg:» El parámetro «clrBg» representa el color de fondo de la etiqueta del rectángulo. Elija un color que contraste bien con el fondo del gráfico o complemente otros elementos.
  • "widthBorder:" Este parámetro define el ancho del borde alrededor del rectángulo. Si desea un borde, establezca un valor positivo; de lo contrario, use cero para no tener borde.
  • "clrBorder:" Parámetro opcional para el color del borde. Si desea un borde, especifique un color (por ejemplo, "clrNONE" para ningún color de borde).
  • "borderType:" Especifica el tipo de borde para el rectángulo. Las opciones incluyen estilos planos, elevados u otros. Para un borde plano simple, utilice BORDER_FLAT.
  • «borderStyle:» Si eliges un borde plano, este parámetro determina el estilo de la línea (por ejemplo, continua, discontinua). Utilice STYLE_SOLID para una línea continua.

En la firma de la función, deberías haber notado que algunos de los argumentos ya están inicializados con algún valor. El valor de inicialización representa el valor predeterminado que se asignará a ese parámetro en caso de que se ignore durante la llamada a la función. Por ejemplo, nuestro color de borde predeterminado es ninguno, lo que significa que si el valor del color no se especifica durante la llamada a la función, no se aplicará ningún color al borde de nuestra etiqueta de rectángulo. 

Dentro del cuerpo de la función, alojado entre llaves ({}), definimos nuestros procedimientos de creación de objetos.

// Create a rectangle label object
if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) {
  Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError);
  return (false); // Return false if object creation fails
}

Comenzamos utilizando una instrucción if para comprobar si el objeto no se ha creado. Se utiliza la función ObjectCreate, un booleano que toma 6 argumentos. Esta función crea un objeto con el nombre, tipo y coordenadas iniciales especificados en la subventana del gráfico especificada. Primero, especificamos la ventana del gráfico, 0 significa que el objeto se creará en la ventana principal. Luego, proporcionamos el nombre del objeto. Este es el nombre que se asignará de forma única a un objeto específico. El tipo de objeto que queremos crear es del tipo OBJ_RECTANGLE_LABEL, lo que significa un objeto para crear y diseñar la interfaz gráfica personalizada. Luego procedemos a proporcionar la subventana, 0 para la subventana actual. Por último, proporcionamos los valores de tiempo y precio como cero (0) ya que no los adjuntaremos al gráfico sino a las coordenadas de la ventana del gráfico. Los píxeles se utilizan para establecer el mapeo.

Si la creación del objeto falla, la función ObjectCreate devuelve finalmente false, por lo que claramente no tiene sentido continuar, devolvemos un error. En este caso, informamos del error imprimiéndolo en el diario junto al código de error y devolviendo false. Podría haber un error anterior, por lo que para obtener el último error, debemos borrar el error anterior. Esto se consigue llamando a la función «ResetLastError», que es una función integrada en MQL5, justo antes de nuestra lógica de creación de objetos.

ResetLastError(); // Reset any previous error codes

El objetivo de la función es establecer en cero el valor de la variable predefinida _LastError, que almacena el código de error de la última operación en la que se produjo un error. Al llamarlo, nos aseguramos de que se borren todos los códigos de error anteriores antes de continuar con las siguientes operaciones. Este paso es esencial porque nos permite manejar errores nuevos de forma independiente sin interferencia de estados de error anteriores.

Si no volvemos hasta este punto significa que hemos creado el objeto y por lo tanto podemos continuar con la actualización de propiedades del objeto. Una función incorporada "ObjectSet..." establece el valor de la propiedad del objeto correspondiente. La propiedad del objeto debe ser del tipo fecha y hora (datetime), entero (int), color, booleano o carácter (string).

// Set properties for the rectangle label
ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner
ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the rectangle
ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the rectangle
ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Rectangle background color
ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); // Border type
ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); // Border style (only if borderType is flat)
ObjectSetInteger(0, objName, OBJPROP_WIDTH, widthBorder); // Border width (only if borderType is flat)
ObjectSetInteger(0, objName, OBJPROP_COLOR, clrBorder); // Border color (only if borderType is flat)
ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Not a background object
ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Not selectable
ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable
ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected

Concentrémonos en la primera propiedad lógica.

ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner

Aquí utilizamos la función integrada ObjectSetInteger y pasamos los parámetros respectivamente. Los parámetros son los que se describen a continuación.

  • ID del gráfico: Es el identificador del gráfico. «0» se refiere al gráfico actual. (chart ID). Estamos ajustando las propiedades de un objeto dentro de este gráfico.
  • Nombre: Este es el nombre del objeto. "objName" representa el nombre único asignado al objeto de etiqueta de rectángulo.
  • Identificador de propiedad: Es el identificador de la propiedad del objeto y su valor puede ser uno de los valores de la enumeración ENUM_OBJECT_PROPERTY_INTEGER. OBJPROP_XDISTANCE especifica que estamos modificando la propiedad de distancia X.
  • Valor de la propiedad: Este es el valor de la propiedad. El valor asignado a «xD» determina la distancia a la derecha (o a la izquierda, si es negativo) a la que se colocará horizontalmente la esquina superior izquierda de nuestra etiqueta rectangular desde el borde izquierdo del gráfico.

Del mismo modo, configuramos las demás propiedades utilizando el mismo formato. OBJPROP_YDISTANCE configura la propiedad de distancia Y de la etiqueta del rectángulo. El valor «yD» determina la distancia vertical entre la esquina superior izquierda de la etiqueta del rectángulo y el borde superior del gráfico. En otras palabras, controla la ubicación vertical de la etiqueta dentro del área del gráfico. Esto establece la distancia Y desde la esquina. Los parámetros «OBJPROP_XSIZE» y «OBJPROP_YSIZE» establecen el ancho y el alto del rectángulo, respectivamente. 

Para posicionar nuestro objeto, utilizamos la propiedad OBJPROP_CORNER para determinar la esquina en la que queremos que nuestro objeto aparezca en la ventana del gráfico.

ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner

La propiedad solo puede ser de 4 tipos:

  • CORNER_LEFT_UPPER: El centro de coordenadas se encuentra en la esquina superior izquierda del gráfico.
  • CORNER_LEFT_LOWER: El centro de coordenadas se encuentra en la esquina inferior izquierda del gráfico.
  • CORNER_RIGHT_LOWER: El centro de coordenadas se encuentra en la esquina inferior derecha del gráfico.
  • CORNER_RIGHT_UPPER: El centro de coordenadas se encuentra en la esquina superior derecha del gráfico.

En una representación fotográfica, esto es lo que tenemos.

ESQUINAS

El resto de las propiedades son sencillas. Les hemos añadido comentarios para facilitar su comprensión. A continuación, simplemente volvemos a dibujar el gráfico utilizando la función ChartRedraw para que los cambios surtan efecto automáticamente sin tener que esperar a que se produzca un cambio en las cotizaciones o en los eventos del gráfico.

ChartRedraw(0); // Redraw the chart

Por último, devolvemos true, lo que significa que la creación y actualización de las propiedades del objeto se han realizado correctamente.

return (true); // Return true if object creation and property settings are successful

El código completo responsable de la creación de un objeto rectángulo en la ventana del gráfico es el siguiente.

bool createRecLabel(string objName, int xD, int yD, int xS, int yS,
                    color clrBg, int widthBorder, color clrBorder = clrNONE,
                    ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID) {
    ResetLastError(); // Reset any previous error codes
    
    // Create a rectangle label object
    if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) {
        Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError);
        return (false); // Return false if object creation fails
    }
    
    // Set properties for the rectangle label
    ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the rectangle
    ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the rectangle
    ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
    ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Rectangle background color
    ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); // Border type
    ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); // Border style (only if borderType is flat)
    ObjectSetInteger(0, objName, OBJPROP_WIDTH, widthBorder); // Border width (only if borderType is flat)
    ObjectSetInteger(0, objName, OBJPROP_COLOR, clrBorder); // Border color (only if borderType is flat)
    ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Not a background object
    ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Not selectable
    ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable
    ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected
    
    ChartRedraw(0); // Redraw the chart
    
    return (true); // Return true if object creation and property settings are successful
}

Para crear un objeto de botón, se utiliza el mismo enfoque funcional. El código para crear una función de botón personalizada es el siguiente.

//+------------------------------------------------------------------+
//|     Function to create button                                    |
//+------------------------------------------------------------------+

bool createButton(string objName, int xD, int yD, int xS, int yS,
                  string txt = "", color clrTxt = clrBlack, int fontSize = 12,
                  color clrBg = clrNONE, color clrBorder = clrNONE,
                  string font = "Arial Rounded MT Bold") {
    // Reset any previous errors
    ResetLastError();

    // Attempt to create the button object
    if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) {
        // Print an error message if creation fails
        Print(__FUNCTION__, ": failed to create the button! Error code = ", _LastError);
        return (false);
    }

    // Set properties for the button
    ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the button
    ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the button
    ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
    ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Text displayed on the button
    ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color
    ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size
    ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name
    ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Background color
    ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Border color
    ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background
    ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Button state (not pressed)
    ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable
    ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected

    // Redraw the chart to display the button
    ChartRedraw(0);

    return (true); // Button creation successful
}

Las diferencias en el código son que un objeto rectángulo no puede contener texto, pero un botón sí incluye un texto descriptivo de la funcionalidad del botón en caso de que sea necesario. Por lo tanto, para los parámetros de entrada, consideramos las propiedades del texto, en nuestro caso el valor del texto, el color, el tamaño de la fuente y el nombre de la fuente. El tipo de borde de nuestro botón es estático y, por lo tanto, eliminamos sus propiedades y conservamos solo el color del borde. 

El tipo de objeto que creamos es OBJ_BUTTON, lo que significa que creamos un objeto gráfico de botón. Sus puntos de anclaje se establecen en píxeles. La propiedad del borde que conservamos es el color del borde y reemplazamos el resto con las propiedades de entrada de texto.

Por último, necesitamos la función del último elemento, que es la etiqueta de texto. La etiqueta de texto elimina la necesidad de un objeto de fondo y, por lo tanto, su implementación es bastante más fácil que el resto de funciones. Solo necesitamos el texto y por eso nos concentramos en las propiedades del texto. Su código es el siguiente.

//+------------------------------------------------------------------+
//|     Function to create text label                                |
//+------------------------------------------------------------------+

bool createLabel(string objName, int xD, int yD,
                 string txt, color clrTxt = clrBlack, int fontSize = 12,
                 string font = "Arial Rounded MT Bold") {
    // Reset any previous errors
    ResetLastError();

    // Attempt to create the label object
    if (!ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0)) {
        // Print an error message if creation fails
        Print(__FUNCTION__, ": failed to create the label! Error code = ", _LastError);
        return (false);
    }

    // Set properties for the label
    ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner
    ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
    ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Text displayed on the label
    ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color
    ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size
    ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name
    ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background
    ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Label state (not active)
    ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable
    ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected

    // Redraw the chart to display the label
    ChartRedraw(0);

    return (true); // Label creation successful
}

Las principales diferencias en esta estructura de código con respecto a la función del botón son el tamaño del objeto y las propiedades del borde. En la firma de la función, eliminamos los tamaños de los objetos así como las propiedades del borde. Definimos nuestro tipo de objeto como OBJ_LABEL para indicar que dibujamos etiquetas según las coordenadas de etiqueta definidas en la ventana del gráfico. Finalmente, eliminamos los parámetros de tamaño y borde y eso es todo. Así de fácil.

Ahora que tenemos las funciones que necesitamos para crear una interfaz gráfica de usuario (Graphical User Interface, GUI), usémoslas para crear el panel. Necesitaremos los nombres de los objetos y, para gestionar fácilmente la interacción entre los nombres de los objetos, es mucho más sencillo definir macros. 

#define MAIN_REC "MAIN_REC"

Utilizamos la palabra clave #define para definir una macro llamada «MAIN_REC» con el valor «MAIN_REC» para almacenar fácilmente el nombre base de nuestro rectángulo principal, en lugar de tener que volver a escribir el nombre repetidamente cada vez que creamos el nivel, lo que nos ahorra mucho tiempo y reduce las posibilidades de introducir el nombre incorrectamente. Básicamente, las macros se utilizan para sustituir texto durante la compilación.

Nuestro código se basará principalmente en la sección de inicialización experta, ya que queremos crear el panel en la instancia de inicialización. Por lo tanto, el controlador de eventos OnInit albergará la mayor parte de la estructura del código. 

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

int OnInit(){

   ...
   
   return(INIT_SUCCEEDED);
}

La función OnInit es un controlador de eventos que se invoca en la instancia de inicialización del experto para realizar las inicializaciones necesarias, si es preciso. 

Luego llamamos a la función para crear una etiqueta de rectángulo escribiendo su nombre y proporcionando sus parámetros.

//--- Create main rectangle label for the dashboard panel
createRecLabel(MAIN_REC,50,50,740,410,clrSeaGreen,1);

Aquí, el nombre de nuestro rectángulo es «MAIN_REC», tal y como se define en la macro. La distancia a lo largo del eje x, la escala de tiempo y fecha, desde la esquina superior izquierda de la ventana del gráfico es de 50 píxeles, y la distancia a lo largo del eje y, la escala de precios, es de 50 píxeles. El ancho es de 740 píxeles y el alto es de 410 píxeles, respectivamente. Elegimos el color de fondo verde mar, con un ancho de borde de 1, y el resto de los parámetros por defecto. Para que los píxeles tengan un rango aproximado, puede reducir la escala del gráfico a 0 y el número de barras entre dos coordenadas de retículo será igual al número de píxeles en la escala horizontal. En un ejemplo, esto es lo que queremos decir.

CRUZ

Se han omitido los demás parámetros, lo que significa que los valores predeterminados se aplicarán automáticamente. Es decir, el tipo de borde será plano y el estilo de línea será una línea sólida continua. Tras la compilación, esto es lo que tenemos actualmente.

PANEL PRINCIPAL

Para crear los submarcos, nuevamente declaramos explícitamente las macros respectivas para los mismos.

#define SUB_REC1 "SUB_REC1"
#define SUB_REC2 "SUB_REC2"

Luego llamamos a la misma función para crear los submarcos. Queremos que nuestros marcos estén dentro del marco del panel base y, por lo tanto, requeriremos que usemos un color ligeramente diferente. Para lograr esto, utilizamos un color blanco y verde y un margen de 3 y 5 píxeles.

//--- Create sub-rectangle labels within the main panel for different sections
createRecLabel(SUB_REC1,50+3,50+30,740-3-3,410-30-3,clrWhite,1);
createRecLabel(SUB_REC2,50+3+5,50+30+50+27,740-3-3-5-5,410-30-3-50-27-10,clrGreen,1);

Aquí, estamos configurando dos subsecciones adicionales, «SUB_REC1» y «SUB_REC2», dentro del panel principal del tablero para ayudar a organizar y separar el contenido visualmente. Mediante la función «createRecLabel», posicionamos «SUB_REC1» añadiendo un desplazamiento de 3 píxeles desde los bordes izquierdo y derecho y 30 píxeles desde la parte superior del rectángulo principal «MAIN_REC», creando así una sección enmarcada dentro del panel principal. Definimos su anchura como «740-3-3» para que quepa dentro de los márgenes reducidos y su altura como «410-30-3» para dejar espacio arriba y abajo, lo que permite que encaje perfectamente dentro del rectángulo principal. Esta subsección está configurada en color blanco, estableciendo un fondo neutro que contrasta con el color verde mar del panel principal para mejorar la claridad visual.

A continuación, utilizamos «createRecLabel» para añadir «SUB_REC2», una sección adicional dentro de «SUB_REC1», y la posicionamos con desplazamientos más precisos para obtener un diseño organizado y en capas. Para lograrlo, establecemos la coordenada X inicial como «50+3+5», posicionándola más dentro de «SUB_REC1» para definirla visualmente como un área diferenciada dentro de esta subsección. Establecemos la coordenada Y como «50+30+50+27» para tener en cuenta los desplazamientos verticales tanto del rectángulo principal como del primer subrectángulo. El ancho, «740-3-3-5-5», encaja perfectamente «SUB_REC2» en el espacio horizontal restante, mientras que la altura, «410-30-3-50-27-10», permite una región equilibrada y separada. Al establecer «SUB_REC2» en verde, se añade un fuerte contraste, lo que indica que se trata de un área en la que se mostrarán datos críticos. Esta cuidadosa superposición de rectángulos es esencial para establecer un panel estructurado y visualmente navegable para el tablero. Tras la compilación obtenemos los siguientes resultados:

MARCOS DE PANELES

Hasta este punto, la configuración de nuestros marcos, márgenes y límites para nuestro panel está completa. Luego procedemos a agregar las demás utilidades del panel, sus propiedades y efectos. Para empezar, vamos a darle un título al panel.

#define HEADER_LABEL "HEADER_LABEL"

//---

//--- Create the header label with text "MQL5 Economic Calendar"
createLabel(HEADER_LABEL,50+3+5,50+5,"MQL5 Economic Calendar",clrWhite,15);

Aquí definimos el identificador de etiqueta «HEADER_LABEL» con el valor «HEADER_LABEL» para mantener la coherencia y facilitar la referencia a esta etiqueta específica en todo nuestro código. Esta etiqueta servirá como encabezado para nuestro panel de control, mostrando de forma destacada el título «Calendario económico MQL5».

A continuación, utilizando la función «createLabel», creamos la etiqueta del encabezado en la posición especificada. Establecemos su coordenada X como «50+3+5», colocándola ligeramente a la derecha del borde del panel principal para garantizar que se alinee dentro del rectángulo «SUB_REC1» y no se superponga con ningún margen. La coordenada Y, «50+5», lo coloca unos pocos píxeles por debajo del borde superior del rectángulo principal, lo que garantiza la legibilidad. Para mayor visibilidad, hemos establecido el color del texto en blanco con un tamaño de fuente de «15», creando un encabezado llamativo y destacado que marca el propósito del panel de control. Este encabezado anclará el diseño visual del tablero, comunicando inmediatamente su propósito a los usuarios. Esto es lo que obtenemos.

EL TÍTULO

Eso fue un éxito. Ahora podemos proceder a crear los encabezados del panel. Para esto, utilizaremos el método más simple, que es simplemente definir los títulos del encabezado y colocarlos en un array, para luego usar un bucle para colocarlos dinámicamente ya que están en una sola fila. Sin embargo, también tendremos que definir los tamaños de los botones de forma diferente, ya que tendrán anchos variables debido a las longitudes de los encabezados individuales. A continuación se muestra la lógica que utilizaremos para lograrlo.

string array_calendar[] = {"Date","Time","Cur.","Imp.","Event","Actual","Forecast","Previous"};
int buttons[] = {80,50,50,40,281,60,70,70};

Definimos dos matrices para organizar las etiquetas y dimensiones de los botones en el tablero. La primera matriz, «array_calendar», contiene cadenas para cada encabezado de columna que mostraremos, especificando el tipo de información: «Fecha», «Hora», «Cur.» (Moneda), «Imp.» (Impacto/Importancia), «Evento», «Real», «Previsión» y «Anterior». Cada cadena representa una etiqueta para una categoría de datos, lo que nos ayuda a comprender qué mostrará cada sección del panel.

La segunda matriz, «buttons», contiene números enteros que representan los anchos (en píxeles) de cada botón asociado con las respectivas columnas en «array_calendar». Estos anchos se adaptan a cada tipo de datos para garantizar que el diseño permanezca alineado y organizado visualmente. Por ejemplo, los 80 píxeles de la columna «Fecha» permiten formatos de fecha más largos, mientras que los anchos más estrechos, como los 50 píxeles, se establecen para las columnas «Hora» y «Cur.», que requieren menos espacio. En conjunto, estas matrices ayudan a optimizar la creación de los encabezados de columna y los botones del panel de control, estableciendo una base estructurada para otros elementos de la interfaz de usuario (UI). A partir de aquí, podemos utilizar un bucle para crear los encabezados de forma dinámica.

#define ARRAY_CALENDAR "ARRAY_CALENDAR"

//---

//--- Initialize starting x-coordinate for button positioning
int startX = 59;
   
//--- Loop through the array_calendar elements to create buttons
for (int i=0; i<ArraySize(array_calendar); i++){
   //--- Create each button for calendar categories
   createButton(ARRAY_CALENDAR+IntegerToString(i),startX,132,buttons[i],25,array_calendar[i],clrWhite,13,clrGreen,clrNONE,"Calibri Bold");
   startX += buttons[i]+3; //--- Update x-coordinate for the next button
}

Aquí, inicializamos y posicionamos los botones basándonos en los elementos «array_calendar» para etiquetar cada categoría en nuestro panel de control. En primer lugar, definimos el identificador «ARRAY_CALENDAR» para la serie de botones del calendario. A continuación, establecemos «startX» en «59» como coordenada x inicial, lo que posicionará el primer botón horizontalmente en el panel.

A continuación, utilizamos un bucle for para iterar cada elemento de «array_calendar» y crear un botón. Para cada iteración, llamamos a la función «createButton» y pasamos un ID único para cada botón añadiendo el índice del bucle a «ARRAY_CALENDAR». Esto garantiza que cada ID de botón sea único, haciendo referencia a categorías como «Date», «Time», «Cur.», etc. Especificamos la posición «startX» y utilizamos valores de «buttons» para definir el ancho de cada botón, asegurando la alineación con la categoría de datos correspondiente. Cada botón también recibe propiedades de estilo, incluyendo un color de fuente (blanco), tamaño de fuente («13») y colores de fondo (verde para el botón y ninguno para el borde), configurados en la fuente «Calibri Bold». Después de crear cada botón, ajustamos «startX» añadiendo el ancho del botón actual más un margen de «3» píxeles, espaciando los botones de manera uniforme para la siguiente iteración. Tras la compilación, tenemos el siguiente resultado.

ENCABEZADOS

Después de crear los encabezados, ahora necesitamos crear la otra subsección para mostrar la hora, el número de eventos noticiosos identificados y los niveles de impacto. Primero comenzaremos obteniendo las noticias de la parte anterior de la serie.

//--- Declare variables for tracking news events and status
int totalNews = 0;
bool isNews = false;
MqlCalendarValue values[]; //--- Array to store calendar values

//--- Define start and end time for calendar event retrieval
datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_H12);
datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_H12);

//--- Set a specific country code filter (e.g., "US" for USD)
string country_code = "US";
string currency_base = SymbolInfoString(_Symbol,SYMBOL_CURRENCY_BASE);

//--- Retrieve historical calendar values within the specified time range
int allValues = CalendarValueHistory(values,startTime,endTime,NULL,NULL);

//--- Print the total number of values retrieved and the array size
Print("TOTAL VALUES = ",allValues," || Array size = ",ArraySize(values));

Esto nos proporciona los valores de eventos históricos recuperados del Calendario Económico MQL5 y, por lo tanto, en lugar de simplemente imprimirlos, podemos mostrarlos en el tablero. Aquí está la lógica que aplicamos.

#define TIME_LABEL "TIME_LABEL"

//---

//--- Create label displaying server time and total number of news events found
createLabel(TIME_LABEL,70,85,"Server Time: "+TimeToString(TimeCurrent(),
           TIME_DATE|TIME_SECONDS)+"   |||   Total News: "+
           IntegerToString(allValues),clrBlack,14,"Times new roman bold");

Aquí, creamos una etiqueta para mostrar la hora actual del servidor junto con el número total de eventos de noticias recuperados. Primero, definimos el identificador "TIME_LABEL" para referenciar de forma única esta etiqueta dentro de nuestro panel de control.

A continuación, llamamos a la función "createLabel" para generar la etiqueta en sí. Especificamos la posición de la etiqueta proporcionando las coordenadas "70" y "85", que indican dónde aparecerá la etiqueta en el panel. El texto de la etiqueta se construye dinámicamente utilizando la función TimeToString, que formatea la hora actual del servidor obtenida mediante la función TimeCurrent en formato de fecha y segundos. Concatenamos esta hora formateada con la cadena «||| Total News: » y convertimos la variable «allValues», que contiene el recuento de eventos de noticias, en una cadena utilizando la función IntegerToString. Esto crea una etiqueta completa que muestra tanto la hora del servidor como el número total de eventos de noticias encontrados. Diseñamos la etiqueta con color negro, un tamaño de fuente de 14 y utilizamos la fuente "Times New Roman bold" para una clara visibilidad. Siguiendo la misma lógica, creamos también la etiqueta de impacto.

#define IMPACT_LABEL "IMPACT_LABEL"

//---

//--- Create label for displaying "Impact" category header
createLabel(IMPACT_LABEL,70,105,"Impact: ",clrBlack,14,"Times new roman bold");

Tras la compilación, obtenemos el siguiente resultado.

ETIQUETAS DE VALORES E IMPACTO

Eso fue un éxito. Ahora necesitamos pasar a mostrar los respectivos botones de impacto con sus respectivas etiquetas y colores, para que los usuarios puedan saber a qué se refiere cada nivel de impacto. 

//--- Define labels for impact levels and size of impact display areas
string impact_labels[] = {"None", "Low", "Medium", "High"};
int impact_size = 100;

//--- Loop through impact levels to create buttons for each level
for (int i=0; i<ArraySize(impact_labels); i++){
   color impact_color = clrBlack, label_color = clrBlack; //--- Default colors for label and button

   //--- Assign color based on impact level
   if (impact_labels[i] == "None"){label_color = clrWhite;}
   else if (impact_labels[i] == "Low"){impact_color = clrYellow;}
   else if (impact_labels[i] == "Medium"){impact_color = clrOrange;}
   else if (impact_labels[i] == "High"){impact_color = clrRed;}

   //--- Create button for each impact level
   createButton(IMPACT_LABEL+string(i),140+impact_size*i,105,impact_size,25,impact_labels[i],label_color,12,impact_color,clrBlack);
}

Aquí, definimos las etiquetas para los diferentes niveles de impacto asociados con eventos económicos y el tamaño de las áreas de visualización para estos indicadores de impacto. En primer lugar, declaramos una matriz llamada «impact_labels» que contiene cadenas que representan los distintos niveles de impacto: «None», «Low», «Medium» y «High». Además, inicializamos una variable entera «impact_size» con un valor de 100, que determina el ancho de los botones que se crearán para cada nivel de impacto.

A continuación, entramos en un bucle que itera sobre la matriz «impact_labels», utilizando la función ArraySize para determinar el número total de niveles de impacto. Dentro de este bucle, primero establecemos los colores predeterminados para el botón y la etiqueta usando el color negro. Luego usamos declaraciones condicionales para asignar colores específicos según el nivel de impacto actual. Si el nivel de impacto es «None», cambiamos el «label_color» a blanco. Si el nivel es «Low», establecemos «impact_color» en amarillo. Para «Medium» asignamos «impact_color» naranja, y para «High» designamos «impact_color» como rojo. Por último, llamamos a la función «createButton» para generar un botón para cada nivel de impacto, posicionándolo mediante una coordenada x dinámica calculada con «140 + impact_size * i», manteniendo una coordenada y fija de 105 y proporcionando las dimensiones y colores correspondientes. Aquí está el logro actual.

BOTONES DE IMPACTO

Eso fue un éxito. Ahora podemos pasar a agregar los datos del calendario real al panel. Sin embargo, antes de eso, necesitaremos particionar el segundo submarco para poder darle al panel un aspecto más profesional, en lugar de simplemente colocar datos en él. Logramos esto mediante la siguiente lógica.

//--- Limit the total number of values to display
int valuesTotal = (allValues <= 11) ? allValues : 11;

//--- Initialize starting y-coordinate for displaying news data
int startY = 162;

//--- Loop through each calendar value up to the maximum defined total
for (int i = 0; i < valuesTotal; i++){

   //--- Set alternating colors for each data row holder
   color holder_color = (i % 2 == 0) ? C'213,227,207' : clrWhite;

   //--- Create rectangle label for each data row holder
   createRecLabel(DATA_HOLDERS+string(i),62,startY-1,716,26,holder_color,1,clrBlack);

   //--- Increment y-coordinate for the next row of data
   startY += 25;
   Print(startY); //--- Print current y-coordinate for debugging
}

Limitamos el número total de valores a mostrar en nuestro tablero definiendo una variable entera llamada "valuesTotal". Utilizamos un operador condicional (ternario) para comprobar si «allValues» es menor o igual que «11». Si es así, establecemos «valuesTotal» en «allValues»; de lo contrario, lo establecemos en «11». Este enfoque garantizará que no intentemos mostrar más de «11» noticias, lo que mantendrá nuestro panel de control ordenado y manejable.

A continuación, inicializamos una variable entera «startY» con un valor de «162», que sirve como coordenada y inicial para posicionar los datos de noticias en el panel. A continuación, entramos en un bucle que itera desde «0» hasta «valuesTotal», procesando de forma efectiva cada valor del calendario que queremos mostrar. Dentro de este bucle, definimos el color para cada contenedor de fila utilizando un patrón alternativo basado en el índice actual «i». Si «i» es par, establecemos «holder_color» en un color gris claro representado por «C'213,227,207'»; si «i» es impar, lo establecemos en blanco. Después de determinar el color, llamamos a la función «createRecLabel» para generar una etiqueta rectangular para cada contenedor de filas de datos, situada en «62» en el eje x, «startY - 1» en el eje y, con una anchura de «716», una altura de «26» y un borde de color negro. Por último, incrementamos «startY» en «25» para ajustar la coordenada y de la siguiente fila de datos, asegurándonos de que cada entrada se muestre de forma secuencial. Con fines de depuración, imprimimos el valor actual de «startY», lo que nos permite realizar un seguimiento de la posición vertical de cada fila de datos a medida que se crea. Aquí está el logro actual.

TITULARES DE DATOS

Es posible que hayas notado que utilizamos una variable macro «DATA_HOLDERS» durante la creación de los submarcos del soporte. Así es como lo hemos definido.

#define DATA_HOLDERS "DATA_HOLDERS"
#define ARRAY_NEWS "ARRAY_NEWS"

También hemos definido la macro «ARRAY_NEWS» para facilitar la creación de los datos correspondientes que se van a asignar dentro de los contenedores de datos relativos a los encabezados de columna. Para completar los datos, necesitaremos que para cada titular seleccionado, iteremos a través de todos los datos para un valor de evento específico y obtengamos sus datos, que mostraremos. Esto se hará dentro del primer bucle y tendrá la siguiente lógica.

//--- Initialize starting x-coordinate for each data entry
int startX = 65;

//--- Loop through calendar data columns
for (int k=0; k<ArraySize(array_calendar); k++){

   MqlCalendarEvent event; //--- Declare event structure
   CalendarEventById(values[i].event_id,event); //--- Retrieve event details by ID

   MqlCalendarCountry country; //--- Declare country structure
   CalendarCountryById(event.country_id,country); //--- Retrieve country details by event's country ID

   //--- Print event details for debugging
   Print("Name = ",event.name,", IMP = ",EnumToString(event.importance),", COUNTRY = ",country.name,", TIME = ",values[i].time);

   //--- Skip event if currency does not match the selected country code
   // if (StringFind(_Symbol,country.currency) < 0) continue;

   //--- Prepare news data array with time, country, and other event details
   string news_data[ArraySize(array_calendar)];
   news_data[0] = TimeToString(values[i].time,TIME_DATE); //--- Event date
   news_data[1] = TimeToString(values[i].time,TIME_MINUTES); //--- Event time
   news_data[2] = country.currency; //--- Event country currency

   //--- Determine importance color based on event impact
   color importance_color = clrBlack;
   if (event.importance == CALENDAR_IMPORTANCE_LOW){importance_color=clrYellow;}
   else if (event.importance == CALENDAR_IMPORTANCE_MODERATE){importance_color=clrOrange;}
   else if (event.importance == CALENDAR_IMPORTANCE_HIGH){importance_color=clrRed;}

   //--- Set importance symbol for the event
   news_data[3] = ShortToString(0x25CF);

   //--- Set event name in the data array
   news_data[4] = event.name;

   MqlCalendarValue value; //--- Declare calendar value structure
   CalendarValueById(values[i].id,value); //--- Retrieve actual, forecast, and previous values

   //--- Populate actual, forecast, and previous values in the news data array
   news_data[5] = DoubleToString(value.GetActualValue(),3);
   news_data[6] = DoubleToString(value.GetForecastValue(),3);
   news_data[7] = DoubleToString(value.GetPreviousValue(),3);

   //--- Create label for each news data item
   if (k == 3){
      createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY-(22-12),news_data[k],importance_color,22,"Calibri");
   }
   else {
      createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY,news_data[k],clrBlack,12,"Calibri");
   }

   //--- Increment x-coordinate for the next column
   startX += buttons[k]+3;
}

Aquí, inicializamos una variable entera llamada «startX» con un valor de 65, que servirá como coordenada x inicial para posicionar cada entrada de datos relacionada con los eventos del calendario. A continuación, entramos en un bucle que recorre cada columna del «array_calendar» utilizando el índice «k». Dentro de este bucle, declaramos una variable de estructura «event» de tipo MqlCalendarEvent, que se utilizará para almacenar los detalles de un evento específico del calendario. Recuperamos los detalles del evento llamando a la función CalendarEventById, pasando el ID del evento desde la matriz «values» y almacenando los resultados en «event».

A continuación, declaramos otra variable de estructura «country» de tipo MqlCalendarCountry para almacenar información sobre el país asociado al evento del calendario. Utilizamos la función CalendarCountryById para rellenar «country» con detalles basados en el ID del país del evento. Con fines de depuración, imprimimos los detalles clave del evento, como el nombre del evento, su nivel de importancia (convertido a una cadena mediante la función EnumToString), el nombre del país y la hora del evento almacenada en «values[i].time».

A continuación, preparamos una matriz de cadenas llamada «news_data» con un tamaño igual al de «array_calendar» para almacenar la información relacionada con el evento. El primer elemento de «news_data» se establece en la fecha del evento, con formato de cadena utilizando la función TimeToString con el indicador «TIME_DATE». El segundo elemento captura la hora del evento, formateada utilizando el indicador «TIME_MINUTES». El tercer elemento almacena la moneda del país del evento.

A continuación, determinamos el color de importancia para el evento en función de su nivel de impacto, inicializando una variable «importance_color» en negro. Comprobamos el valor de «event.importance» y, en función de su valor (bajo, moderado o alto), asignamos el color adecuado: amarillo para bajo, naranja para moderado y rojo para alto.

También establecemos el cuarto elemento de «news_data» en un símbolo que representa el nivel de importancia del evento, utilizando «ShortToString(0x25CF)» para crear un círculo relleno. Al quinto elemento se le asigna el nombre del evento obtenido de «event.name».

Para obtener los valores reales, previstos y anteriores del evento, declaramos otra variable de estructura llamada «value» de tipo MqlCalendarValue y utilizamos la función CalendarValueById para rellenar esta estructura basándonos en el ID del evento almacenado en «values[i].id». Los elementos sexto, séptimo y octavo de «news_data» se rellenan con los valores reales, previstos y anteriores, respectivamente, con un formato de tres decimales utilizando la función DoubleToString.

Por último, creamos una etiqueta para cada elemento de datos de noticias utilizando la función «createLabel». Si «k» es igual a «3», aplicamos el color de importancia; de lo contrario, utilizamos el color negro por defecto. La coordenada x de cada etiqueta viene determinada por «startX», que luego incrementamos añadiendo el ancho del botón de la matriz «buttons», lo que garantiza que cada columna de datos se coloque correctamente para una visualización clara. Tras la compilación, obtenemos el siguiente resultado.

PANEL RELLENADO

Eso fue un éxito. Ahora hemos creado un panel de Calendario Económico MQL5 que muestra los datos de noticias en el gráfico para facilitar su referencia. En cuanto al hito actual, simplemente obtenemos todos los datos. En las próximas partes de la serie, mejoraremos el panel integrando filtros, integrando actualizaciones de datos en tiempo real y utilizando los datos de noticias para fines comerciales.


Conclusión

En conclusión, hemos sentado con éxito las bases para nuestro Calendario económico MQL5 mediante la creación de un panel interactivo que muestra los acontecimientos económicos más importantes en un formato fácil de usar. Al implementar funciones como la recuperación de datos del calendario, la categorización visual de eventos según su importancia y el etiquetado intuitivo, podemos mantenernos informados sobre los movimientos significativos del mercado. Esta configuración inicial no solo mejora la experiencia del usuario, sino que también proporciona una base sólida para futuras mejoras que llevarán nuestro panel de control al siguiente nivel.

En las próximas partes de esta serie, integraremos funcionalidades adicionales, como filtros de noticias, que nos ayudarán a centrarnos en la información más relevante para nuestras estrategias en MQL5. También implementaremos actualizaciones en tiempo real para garantizar que nuestro panel refleje los últimos datos económicos a medida que estén disponibles. Además, nos centraremos en hacer que el panel sea responsivo, lo que le permitirá adaptarse perfectamente a diferentes tamaños de pantalla e interacciones del usuario. En última instancia, nuestro objetivo es aprovechar estos datos para facilitar la toma de decisiones informadas en materia de negociación, transformando nuestro calendario económico en una potente herramienta para los operadores que buscan sacar partido de la volatilidad del mercado. Manténgase atento.

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

Archivos adjuntos |
Redes neuronales en el trading: Agente con memoria multinivel (Final) Redes neuronales en el trading: Agente con memoria multinivel (Final)
Continuamos el trabajo iniciado de creación del framework FinMem, que utiliza enfoques de memoria multinivel que imitan los procesos cognitivos humanos. Esto permite al modelo no solo procesar eficazmente datos financieros complejos, sino también adaptarse a nuevas señales, mejorando sustancialmente la precisión y eficacia de las decisiones de inversión en mercados que cambian dinámicamente.
Del básico al intermedio: Eventos (I) Del básico al intermedio: Eventos (I)
Con todo lo que se ha mostrado hasta ahora, creo que ya podemos comenzar a implementar algún tipo de aplicación para ejecutarla directamente en el gráfico de algún símbolo. Aunque, antes de poder hacer esto, necesitamos hablar de algo que resulta bastante confuso para los principiantes: el hecho de que las aplicaciones desarrolladas en MQL5 y destinadas a visualizarse en un gráfico no se crean del mismo modo que hemos visto hasta ahora. En este artículo, empezaremos a entenderlo un poco mejor.
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.
Modelos polinómicos en el trading Modelos polinómicos en el trading
Este artículo trata sobre los polinomios ortogonales. Su uso puede suponer la base de un análisis más preciso y eficaz de la información del mercado, de modo que el tráder pueda tomar decisiones más informadas.