Creación de un panel de indicadores de fuerza relativa (RSI) dinámico, multisímbolo y multiperíodo en MQL5
Introducción
En este artículo, le guiaremos a través del proceso de creación de un panel de indicadores dinámico multisímbolo y multiperiodo RSI (Relative Strength Index) en MetaQuotes Language 5 (MQL5) para MetaTrader 5 (MT5). Esta completa guía explorará la definición, funcionalidad y aplicaciones prácticas de un panel de control RSI personalizado, así como los pasos necesarios para desarrollarlo utilizando MetaQuotes Language 5 (MQL5).
Un panel dinámico de RSI sirve como una poderosa herramienta para los operadores, proporcionando una visión consolidada de los valores RSI a través de múltiples símbolos y marcos temporales. Esto permite un proceso de toma de decisiones más informado al identificar condiciones de sobrecompra o sobreventa en el mercado. Al visualizar los datos del RSI en una única interfaz, los operadores pueden evaluar rápidamente las condiciones del mercado y adaptar sus estrategias en consecuencia.
Cubriremos las siguientes áreas clave:
- Inicialización del panel de control: Configuración del entorno, creación de botones principales y visualización de marcos de tiempo y símbolos.
- Actualizaciones en tiempo real: Implementación de la funcionalidad para calcular y mostrar los valores RSI de forma dinámica, con actualizaciones basadas en datos de mercado en directo.
- Creación y actualización de botones: Explicación detallada de las funciones utilizadas para crear y actualizar botones, garantizando que el panel de control sea fácil de usar e informativo.
- Personalización y uso práctico: Cómo personalizar el panel de control para adaptarlo a las necesidades de negociación individuales e integrarlo en su estrategia de negociación.
Al final de este artículo, comprenderá a fondo cómo crear y utilizar un panel RSI multisímbolo y multiperíodo en MQL5. Esto mejorará su conjunto de herramientas comerciales, mejorará su capacidad para analizar las tendencias del mercado y, en última instancia, le ayudará a tomar decisiones comerciales más informadas. Para ello, utilizaremos los siguientes puntos:
- Descripción general y elementos de la ilustración
- Implementación en MQL5
- Conclusión
En este trayecto, utilizaremos ampliamente MetaQuotes Language 5 (MQL5) como entorno de codificación base de nuestro Entorno de Desarrollo Integrado (Integrated Development Environment, IDE) en MetaEditor, y ejecutaremos los archivos en el terminal de trading MetaTrader 5 (MT5). Por lo tanto, contar con las versiones mencionadas anteriormente será de suma importancia. Profundicemos en los entresijos del desarrollo de esta poderosa herramienta comercial.
Descripción general y elementos de la ilustración
Crearemos un panel integral que consolide los valores RSI en múltiples símbolos comerciales y marcos temporales, brindando a los operadores una herramienta poderosa para el análisis del mercado. Describiremos y cubriremos todo en detalle para garantizar una comprensión completa. Nuestro desarrollo se centrará en los siguientes elementos clave:
- Inicialización del panel de control:
Creación del botón principal: El primer paso en nuestro proceso de inicialización será crear un botón principal que indique la base y punto de referencia del panel de control. Este botón servirá como control principal para el panel de control y proporcionará un punto focal para la interfaz de usuario. Estará ubicado estratégicamente en la parte superior del tablero para brindar un fácil acceso y una visibilidad clara.
Botones de marco temporal: A continuación, crearemos botones que representen diferentes marcos temporales para presentar los datos de otros indicadores de varios periodos de símbolos. Estos botones estarán dispuestos horizontalmente junto al botón principal y estarán diseñados para mostrar valores RSI para cada período respectivo. Cada botón de período de tiempo estará etiquetado con la abreviatura del período correspondiente, lo que permitirá a los operadores identificar rápidamente y hacer comparaciones de datos en diferentes períodos de tiempo de un vistazo.
- Botones de símbolos:
Lista dinámica de símbolos: Para ofrecer una visión completa del mercado, generaremos botones para cada símbolo de negociación disponible en la plataforma MetaTrader 5 del usuario y añadido a la vigilancia del mercado. Estos botones se crearán dinámicamente y aparecerán verticalmente debajo del botón principal. El símbolo comercial actualmente activo se resaltará con un color distintivo (por ejemplo, lima o verde) para que sea fácilmente reconocible. Esta función garantizará que los operadores puedan identificar rápidamente el símbolo activo y monitorear sus valores RSI en tiempo real.
Botón principal: En la parte inferior de la lista de símbolos, añadiremos un botón base que abarque el ancho de todos los botones de marco temporal combinados. Este botón recibirá el nombre del panel, o más bien su título, pero puede utilizarse para mostrar el estado de la señal del símbolo actual o servir como resumen o pie de página de la lista de símbolos. Proporcionará una demarcación clara entre los botones de símbolos y la visualización del valor RSI, garantizando que el cuadro de mandos esté bien organizado y sea fácil de navegar.
- Actualizaciones en tiempo real:
Cálculo del RSI: Para garantizar que el cuadro de mandos proporciona información precisa y actualizada, calcularemos los valores del RSI para cada símbolo y marco temporal en tiempo real utilizando funciones MQL5 incorporadas. Esta función calculará el RSI basándose en los precios de cierre del periodo seleccionado, proporcionando un indicador esencial del impulso del mercado. Los valores del RSI se almacenarán en una matriz y se actualizarán en cada tick para reflejar los últimos datos del mercado.
Visualización dinámica: Los valores RSI calculados se mostrarán dinámicamente en los botones correspondientes. Para mejorar la claridad visual, aplicaremos un sistema de codificación por colores basado en umbrales RSI predefinidos. Si el valor RSI está por debajo de 30, lo que indica una condición de sobreventa, el color de fondo del botón cambiará a verde. Si el valor RSI está por encima de 70, lo que indica una condición de sobrecompra, el color de fondo cambiará a rojo. Para valores RSI entre 30 y 70, el color de fondo permanecerá blanco, lo que indica una condición neutral. Esta pantalla dinámica permitirá a los operadores evaluar rápidamente el estado del mercado y tomar decisiones comerciales informadas.
Para ilustrar todo el proceso, esto es lo que pretendemos tener al final.

Para ilustrar todo el proceso de desarrollo, desglosaremos cada elemento en pasos detallados, proporcionando fragmentos de código y explicaciones. Al final de este artículo, usted tendrá un tablero RSI totalmente funcional que puede personalizar e integrar en su estrategia de negociación.
Implementación en MQL5
El panel de indicadores se basará en un asesor experto. Para crear un asesor experto (EA), en su terminal MetaTrader 5, haga clic en la pestaña Herramientas y marque MetaQuotes Language Editor, o pulse F4 en su teclado. También puede hacer clic en el icono IDE (Entorno de Desarrollo Integrado) de la barra de herramientas. Esto abrirá el entorno MetaQuotes Language Editor, que permite escribir robots comerciales, indicadores técnicos, scripts y bibliotecas de funciones.

Una vez abierto MetaEditor, en la barra de herramientas, navega hasta la pestaña Archivo y marca Nuevo Archivo, o simplemente pulsa CTRL + N, para crear un nuevo documento. También puede hacer clic en el icono Nuevo de la pestaña Herramientas. Aparecerá una ventana emergente del Asistente MQL (MQL Wizard).

En el Asistente que aparece, marque Asesor Experto (plantilla) y haga clic en Siguiente.

En las propiedades generales del Asesor Experto, en la sección nombre, indique el nombre de archivo de su experto. Tenga en cuenta que para especificar o crear una carpeta si no existe, se utiliza la barra invertida antes del nombre del EA. Por ejemplo, aquí tenemos «Experts\» por defecto. Esto significa que nuestro EA se creará en la carpeta Experts y podremos encontrarlo allí. Las demás secciones son bastante sencillas, pero puede seguir el enlace que figura al final del Asistente para saber cómo realizar el proceso con precisión.

Después de proporcionar el nombre de archivo de Asesor Experto que desee, haga clic en Siguiente, haga clic en Siguiente y, a continuación, haga clic en Finalizar. Después de hacer todo esto, ya estamos listos para codificar y programar nuestro panel de indicadores.
En primer lugar, tendremos que crear una función para los botones que se van a realizar. Esto será de gran utilidad ya que nos permitirá reutilizar la misma función al crear características similares en lugar de tener que repetir todo el proceso al crear objetos similares. También nos ahorrará mucho tiempo y espacio haciendo que el proceso sea rápido, sencillo y los fragmentos de código cortos.
Para crear los botones, crearemos una función que toma 11 argumentos o parámetros.
//+------------------------------------------------------------------+ //| Function to create a button | //+------------------------------------------------------------------+ bool createButton(string objName, string text, int xD, int yD, int xS, int yS, color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE, string font = "Arial Rounded MT Bold" ) { ... }
La firma de la función lo ilustra todo. Es una función booleana que tiene el nombre «createButton», lo que significa que devolverá dos banderas booleanas, true o false, en caso de éxito o fracaso respectivamente. Para comprender fácilmente sus parámetros, vamos a esbozarlos y explicarlos individualmente a continuación.
- objName (string): Para comprender fácilmente sus parámetros, vamos a esbozarlos y explicarlos individualmente a continuación. Cada botón debe tener un nombre único para diferenciarlo de otros objetos del gráfico. Este nombre se utiliza para hacer referencia al botón para actualizaciones y modificaciones.
- text (string): Define el texto que se mostrará en el botón. Puede ser cualquier texto, como «RSI», «BUY», o cualquier otra etiqueta relevante para el propósito del botón.
- xD (int): El parámetro especifica la distancia horizontal del botón desde la esquina especificada del gráfico. La unidad está en píxeles.
- yD (int): Similar a xD, este parámetro define la distancia vertical del botón desde la esquina especificada del gráfico.
- xS (int): El parámetro especifica la anchura del botón en píxeles. Determina la anchura con la que aparecerá el botón en el gráfico.
- yS (int): Define la altura del botón en píxeles. Determina la altura a la que aparecerá el botón en el gráfico.

- clrTxt (color): Este parámetro establece el color del texto que aparece en el botón. El color se puede especificar utilizando las constantes de color predefinidas en MQL5, como clrBlack, clrWhite, clrRed, etc.
- clrBg (color): Define el color de fondo del botón. También se especifica mediante constantes de color predefinidas y determina el color de relleno del botón.
- fontSize (int): Este parámetro opcional especifica el tamaño de la fuente utilizada para el texto del botón. Por defecto es 12 si no se proporciona. El tamaño de la fuente determina el tamaño del texto que aparece en el botón.
- clrBorder (color): Este otro parámetro opcional establece el color del borde del botón. Por defecto es «clrNONE» si no se proporciona, lo que significa que no se aplica ningún color de borde. Si se especifica, el color del borde puede mejorar la visibilidad y la estética del botón.
- font (string): Sin embargo, este otro parámetro opcional define el tipo de fuente utilizado para el texto del botón. Por defecto es «Arial Rounded MT Bold» si no se indica. La fuente determina el estilo del texto que aparece en el botón.
En la definición 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 por defecto 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 por defecto es ninguno, lo que significa que si no se especifica el valor del color durante la llamada a la función, no se aplicará ningún color al borde de nuestra etiqueta rectangular.
Dentro del cuerpo de la función, alojado entre llaves ({}), definimos nuestros procedimientos de creación de objetos.
// Attempt to create the button if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails return (false); // Return false if creation fails }
Comenzamos utilizando una sentencia if para comprobar si el objeto no está 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_BUTTON", es decir, un objeto para crear y diseñar el panel de indicadores personalizado. 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, en última instancia la función ObjectCreate devuelve false, claramente no tiene sentido continuar, volvemos con 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 y, por lo tanto, para obtener el último error, necesitamos borrar el error anterior. Esto se consigue llamando a la función ResetLastError, que es una función MQL5 incorporada, justo antes de nuestra lógica de creación de objetos.
ResetLastError(); // Reset the last error code
El propósito de la función es poner a cero el valor de la función GetLastError, que obtiene el código de error de la última operación que encontró un error. Al llamarlo, nos aseguramos de que cualquier código de error anterior se borra antes de proceder con las siguientes operaciones. Este paso es esencial porque nos permite tratar los nuevos errores de forma independiente, sin interferencias 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 de tipo datetime, entero, color, booleano o string.
// Set button properties ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected
Centrémonos en la lógica de la primera propiedad.
ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance
Aquí, utilizamos la función incorporada ObjectSetInteger y pasamos los parámetros respectivamente. Los parámetros son los que se describen a continuación.
- Identificador del gráfico: Es el identificador del gráfico. «0» se refiere al gráfico actual (ID del gráfico). Estamos ajustando las propiedades de un objeto dentro de este gráfico.
- Nombre: Es el nombre del objeto. «objName» representa el nombre único asignado al objeto etiqueta rectángulo.
- Propiedad: Es la ID 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: Es el valor de la propiedad. El valor asignado a «xD» determina a qué distancia a la derecha (o a la izquierda, si es negativo) se situará horizontalmente la esquina superior izquierda de nuestra etiqueta rectangular desde el borde izquierdo del gráfico.
Del mismo modo, establecemos las demás propiedades utilizando el mismo formato. «OBJPROP_YDISTANCE» configura la propiedad de distancia Y de la etiqueta rectángulo. El valor «yD» determina a qué distancia vertical del borde superior del gráfico se situará la esquina superior izquierda de la etiqueta rectangular. En otras palabras, controla la colocación vertical de la etiqueta dentro del área del gráfico. Establece la distancia Y desde la esquina. OBJPROP_XSIZE» y “OBJPROP_YSIZE” establecen la anchura y la altura del rectángulo respectivamente.
Para posicionar nuestro objeto, utilizamos la propiedad «OBJPROP_CORNER» para determinar la esquina en la que queremos que se encuentre nuestro objeto en la ventana del gráfico.
ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position
La propiedad sólo 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 está 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 gráfica, esto es lo que tenemos.

El resto de las propiedades son sencillas. Les hemos añadido comentarios para facilitar su comprensión. A continuación, basta con redibujar el gráfico 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 to reflect the new button
Finalmente, devolvemos true significando que la creación y actualización de las propiedades del objeto fue un éxito.
return (true); // Return true if creation is successful
El código completo de la función responsable de la creación de un objeto botón en la ventana del gráfico es el siguiente.
//+------------------------------------------------------------------+ //| Function to create a button | //+------------------------------------------------------------------+ bool createButton(string objName, string text, int xD, int yD, int xS, int yS, color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE, string font = "Arial Rounded MT Bold" ) { ResetLastError(); // Reset the last error code // Attempt to create the button if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails return (false); // Return false if creation fails } // Set button properties ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected ChartRedraw(0); // Redraw the chart to reflect the new button return (true); // Return true if creation is successful }
Ahora que tenemos la función que necesitamos para crear un botón, usémosla para crear el panel de indicadores. Necesitaremos nombres de objeto y para gestionar fácilmente la interacción de los nombres de objeto, es mucho más fácil definir macros.
// Define button identifiers and properties #define BTN1 "BTN1"
Utilizamos la palabra clave #define para definir una macro llamada «BTN1» con el valor «BTN1» para almacenar fácilmente el nombre de nuestro botón principal, en lugar de tener que volver a escribir repetidamente el nombre en cada instancia en la que creamos la sección del botón, lo que nos ahorra significativamente tiempo y reduce las posibilidades de proporcionar erróneamente el nombre. Básicamente, las macros se utilizan para sustituir texto durante la compilación.
Del mismo modo, definimos otras macros que utilizaremos.
#define Desc "Desc " #define Symb "Symb " #define Base "Base " #define RSI "RSI " #define XS1 90 #define XS2 100 #define YS1 25 #define clrW clrWhite #define clrB clrBlack #define clrW_Gray C'230,230,230'
Aquí, utilizamos la macro «Desc» como prefijo para los nombres de los botones que describen varios marcos temporales, lo que nos permite generar nombres únicos para estos botones de descripción añadiendo índices y cadenas adicionales, como «Desc0», «Desc1», etc. Del mismo modo, la macro «Symb» se utiliza como prefijo para los nombres de los botones que representan distintos símbolos bursátiles, lo que nos ayuda a crear identificadores únicos como «Symb0», «Symb1», etc. La macro «Base» sirve como prefijo para el nombre del botón base, proporcionando una convención de nomenclatura clara y coherente para los componentes de nuestro panel de control. Para gestionar los botones relacionados con el RSI, utilizamos la macro «RSI», que garantiza identificadores únicos para los botones que muestran valores RSI en distintos símbolos y marcos temporales.
Para las dimensiones, «XS1» establecerá la anchura de determinados botones, mientras que «XS2» e «YS1» especifican la anchura y la altura de otros botones, respectivamente, estandarizando el tamaño de nuestros elementos GUI. Definimos macros de color, «clrW» y «clrB», para poder referirnos cómodamente a los colores blanco y negro en nuestro código. MQL5 tiene predefinidas variables de formato color y eso es lo que asignamos y a lo que nos referimos cuando usamos «clrWhite» para un color blanco; los colores web.
Sección de formato de color 1: 
Sección de formato de color 2:

Además, definimos un color gris personalizado como «clrW_Gray» C'230,230,230' para utilizarlo como color de fondo o de borde, garantizando un estilo visual coherente en todo el cuadro de mandos. Para tener más control sobre los colores, representamos la última macro en formato de literales. Esto toma el formato «C“000,000,000”» donde los ceros triples pueden ser cualquier numeral de 0 a 255. El formato adoptado es el RGB (rojo, verde y azul). Los tres valores representan los componentes rojo, verde y azul, respectivamente, en una escala de 0 a 255. Así, 230,230,230 se traduce en un tono casi blanco.
Tendremos que definir los plazos o periodos específicos de los símbolos que utilizaremos en nuestro panel de control. Por tanto, necesitaremos almacenarlos y la forma más sencilla de hacerlo es en matrices a las que se pueda acceder fácilmente.
// Define the timeframes to be used ENUM_TIMEFRAMES periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H4, PERIOD_D1};
Definimos un array estático de tipo ENUM_TIMEFRAMES para especificar los marcos temporales que se utilizarán en nuestro cuadro de mando. Nombramos la matriz como «periods», e incluimos los siguientes marcos temporales específicos: PERIOD_M1 (1 minuto), PERIOD_M5 (5 minutos), PERIOD_H1 (1 hora), PERIOD_H4 (4 horas) y PERIOD_D1 (1 día). Al enumerar estos periodos en la matriz «periods», nos aseguramos de que nuestro cuadro de mandos muestre los valores del RSI para cada uno de estos periodos distintos, proporcionando una visión completa de las tendencias del mercado en múltiples periodos. Esta configuración nos permitirá iterar sobre la matriz y aplicar nuestros cálculos y creaciones de botones de manera uniforme para cada período especificado más tarde. La variable de tipo de datos utilizada para definir la matriz es una enumeración que consiste en todos los plazos disponibles en MetaTrader 5 (MT5). Puede utilizar cualquiera siempre que se disponga de él explícitamente. Aquí tiene una lista de todos los plazos que puede utilizar.

Por último, todavía, en la variable global, tendremos que crear un manejador del indicador que contendrá nuestro indicador y una matriz donde almacenaremos los datos del indicador para los distintos plazos y símbolos a utilizar. Esto se consigue mediante el siguiente fragmento de código.
// Global variables int handleName_Id; // Variable to store the handle ID for the RSI indicator double rsi_Data_Val[]; // Array to store the RSI values
Declaramos una variable de tipo de datos integer llamada «handleName_Id» para almacenar el ID de manejador para el indicador RSI. Cuando creamos un indicador RSI para un símbolo y un marco temporal específico, devolverá una ID de manejador único. La ID se almacenará entonces en el manejador del indicador, lo que nos permitirá hacer referencia a este indicador RSI específico en operaciones posteriores, como recuperar sus valores para su posterior análisis. Aquí es donde entra en juego la segunda matriz de variables. Definimos una matriz dinámica doble llamada «rsi_Data_Val» para almacenar los valores RSI obtenidos del indicador. Cuando recuperemos los datos RSI, los valores se copiarán en esta matriz. Al hacer de esta matriz una variable global, nos aseguramos de que los datos RSI son accesibles en todo el programa, lo que nos permite utilizar los valores para las actualizaciones en tiempo real y mostrarlos en los botones de nuestro panel de control.
Nuestro panel se creará primero en la sección de inicialización del Asesor Experto, y por lo tanto vamos a echar un vistazo a lo que hace el controlador de eventos de inicialización.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { ... return(INIT_SUCCEEDED); // Return initialization success }
La función OnInites un manejador de eventos que se llama en la instancia de inicialización de expertos para hacer las inicializaciones necesarias si es necesario. Está diseñado para realizar todas las tareas de configuración inicial necesarias para garantizar que el Asesor Experto funcione correctamente. Esto incluye la creación de elementos de la interfaz de usuario, la inicialización de variables y el establecimiento de las condiciones necesarias para que el programa funcione correctamente durante su ejecución. En nuestro caso, lo utilizaremos para inicializar los elementos del panel de control.
A continuación llamamos a la función para crear un botón escribiendo su nombre y proporcionando sus parámetros.
// Create the main button for the pair with specific properties createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray);
Aquí, el nombre de nuestro botón es «BTN1» como en la definición de la macro. El segundo parámetro se utiliza para especificar el texto que se mostrará en las proximidades de los botones. Nuestra distancia a lo largo del eje x, la escala de fecha y hora, desde la esquina superior derecha de la ventana del gráfico es de 600 píxeles, y la distancia a lo largo del eje y, la escala de precios, es de 50 píxeles. La anchura se toma de la macro ya predefinida para facilitar la referencia a los botones posteriores. La anchura es «XS1», de 90 píxeles, y la altura es «YSI», de 25 píxeles. Elegimos que el color de nuestro texto sea «clrW», que es el color blanco, con un fondo de color gris, un tamaño de fuente de 15 y un color de borde de botón gris. Para conseguir 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 del retículo será igual al número de píxeles de la escala horizontal. A modo de ejemplo, esto es lo que queremos decir.

El otro parámetro se ha omitido, lo que significa que se aplicará automáticamente el valor por defecto, es decir, el nombre de la fuente será «Arial Rounded MT Bold». Tras la recopilación, esto es lo que tenemos actualmente.

Aunque tuviéramos todos los parámetros con los valores por defecto que se indican a continuación, los resultados serían siempre los mismos.
// Create the main button for the pair with specific properties createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray, "Arial Rounded MT Bold");
A continuación, queremos crear botones para cada marco temporal predefinido para mostrar la información correspondiente del RSI (Relative Strength Index). Podemos hacer esto estáticamente llamando a la función para crear los botones para cada elemento que queramos crear pero eso haría nuestro código muy largo y pesado. Así, emplearemos un formato dinámico que nos ayudará a crear los botones en iteraciones supervisadas.
// Loop to create buttons for each timeframe with the corresponding RSI label for(int i = 0; i < ArraySize(periods); i++) { createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrGray, 13, clrGray); }
Iniciamos un bucle for que itera a través de la matriz «periods», que contiene los plazos especificados que habíamos definido y rellenado anteriormente. Usamos la función ArraySize para asegurarnos de que el bucle cubre todos los elementos del array. La función es simple y toma un único argumento y simplemente devuelve el número de elementos de una matriz seleccionada, «periods» en nuestro caso. Dentro del bucle, llamamos a la función «createButton» para crear un botón para cada periodo. El nombre del botón se construye concatenando la macro «Desc» (definida como "Desc “) con el índice ”i" convertido a cadena mediante la función IntegerToString, asegurando que cada botón tenga un nombre único, como «Desc 0», «Desc 1», etc. La función convierte un valor de tipo entero en una cadena de longitud especificada y devuelve la cadena obtenida. Toma 3 parámetros de entrada, pero los 2 últimos son opcionales. El primer parámetro es el número de conversión, índice "i" en nuestro caso. Utilizamos una función personalizada para generar la etiqueta del botón, la función «truncPrds», que trunca la representación de string del marco temporal a un formato más legible (por ejemplo, «M1», «M5») y añade « RSI 14» para indicar que este botón mostrará el valor RSI con un periodo de 14. El fragmento de código de la función es el siguiente:
// Function to truncate the ENUM_TIMEFRAMES string for display purposes string truncPrds(ENUM_TIMEFRAMES period) { // Extract the timeframe abbreviation from the full ENUM string string prd = StringSubstr(EnumToString(period), 7); return prd; // Return the truncated string }
La función toma un período de tipo ENUM_TIMEFRAMES como parámetro y comienza convirtiendo el valor ENUM a su representación de string mediante la función EnumToString . Esto suele dar como resultado una cadena que incluye un prefijo, que no necesitamos para la visualización. Para eliminar esta parte innecesaria, utilizamos la función StringSubstr para extraer una subcadena a partir del séptimo carácter. Esto truncará la cadena a una forma más corta y legible que sea adecuada para su visualización en la interfaz de usuario. Por último, devolvemos la string truncada, proporcionando una representación limpia y concisa del marco temporal que se puede utilizar para etiquetar botones en nuestro panel de control. Para entender por qué necesitamos esta función, he aquí una ilustración.
Lógica adoptada:
Print("BEFORE: ",EnumToString(periods[i])); Print("AFTER: ",truncPrds(periods[i]));
Las declaraciones impresas.

Ahora puede ver claramente que los puntos no truncados son más largos que los truncados y contienen los 7 caracteres innecesarios «PERIOD_», incluido el carácter de subrayado, que finalmente eliminamos.
La coordenada "X" de cada botón se calcula dinámicamente, partiendo de un valor inicial de 600 - «XS1» y ajustándolo restando «XS2» (el ancho del botón) multiplicado por el índice "i". Este posicionamiento garantizará que cada botón se sitúe a la izquierda del anterior, creando una alineación horizontal. La coordenada "Y" se fija a 50 píxeles de la parte superior del gráfico, manteniendo una posición vertical coherente para todos los botones de marco temporal. A continuación, las dimensiones del botón se fijan utilizando los valores definidos por las macros «XS2» para la anchura (100 píxeles) e «YS1» para la altura (25 píxeles). Además, establecemos el color del texto de cada botón en «clrWhite», que es un color blanco, el color de fondo en gris, el tamaño de la fuente en 13 y, por último, el color del borde en gris. Tras la compilación, esto es lo que obtenemos.

Por ejemplo, el color de fondo podría no ser de su agrado. Puede utilizar el que considere oportuno. Todo lo que tienes que hacer es alterar los colores a tu gusto estético. Por ejemplo, para tener un fondo azul y un borde negro definido, puede adoptar los siguientes cambios de código.
createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrDodgerBlue, 13, clrBlack);
Aquí, cambiamos el color de fondo a azul y el color del borde a negro. Tras la compilación, esto es lo que obtenemos:

Observe cómo los botones se vuelven elegantes y estéticamente atractivos. Sin embargo, para mantener la coherencia del artículo, utilizaremos los colores predeterminados que no son llamativos. Usaremos colores llamativos cuando creemos e identifiquemos señales válidas más adelante en el artículo.
A continuación, necesitamos crear otra serie de botones de símbolos verticales adoptando nuevamente el formato de representación dinámica. Para los símbolos, no necesitamos definir símbolos específicos en una matriz y usarlos para la visualización. Podemos acceder automáticamente a los símbolos que ofrece el broker. Para lograr esto, utilizamos un bucle "for" para iterar a través de todos los símbolos proporcionados por el broker y elegimos los necesarios si es necesario.
// Loop to create buttons for each symbol for(int i = 0; i < SymbolsTotal(true); i++) { ... }
Para obtener los símbolos proporcionados por el bróker, utilizamos una función MQL5 incorporada SymbolsTotal. La función devuelve el número de símbolos disponibles (seleccionados en Observación del Mercado o todos). Solo toma un único parámetro booleano de entrada que, si se establece como verdadero, la función devolverá la cantidad de símbolos seleccionados en Observación del Mercado. Si el valor es falso, devolverá el número total de todos los símbolos. Para entender esto claramente, imprimamos los valores cuando el valor del parámetro de entrada de la función se establece en falso.
// Loop to create buttons for each symbol for(int i = 0; i < SymbolsTotal(false); i++) { Print("Index ",i,": Symbol = ",SymbolName(i,false)); ... }
En el bucle "for", establecemos el indicador del valor seleccionado en falso para poder acceder a toda la lista de símbolos. En la sentencia "Print", hemos utilizado otra función SymbolName para obtener el nombre del símbolo en la lista por posición. El segundo parámetro de la función especifica el modo de solicitud basado en los criterios de selección de símbolos de Observación del Mercado. Si el valor es verdadero, el símbolo se toma de la lista de símbolos seleccionados en Observación del Mercado. Si el valor es falso, el símbolo se toma de la lista general. Tras la compilación, esto es lo que obtenemos.

Continuación.

Puede ver que todos los símbolos están seleccionados. En este caso, se seleccionan e imprimen 396 símbolos. Ahora imagine un caso en el que muestra todos los símbolos del gráfico. Es demasiado, ¿verdad? No caben en un solo gráfico y, si lo intentas, la fuente será tan pequeña que no podrás ver los símbolos con facilidad, o simplemente ensuciarán tu gráfico. Además, ni siquiera todos los símbolos te sirven. En esta coyuntura, puede plantearse tener sólo unos pocos que sean de máxima prioridad y dejar de lado el resto. La selección de los mejores pares de sus favoritos se supone que estarán en la sección de Observación del Mercado. Aquí es donde colocas tus símbolos favoritos para echar un vistazo a las cotizaciones y controlar sus movimientos de un vistazo. Por lo tanto, a partir de esta Observación del Mercado escogeremos los símbolos de la pantalla. Para hacerlo factible, fijamos el valor de las dos funciones en verdadero.
// Loop to create buttons for each symbol for(int i = 0; i < SymbolsTotal(true); i++) { Print("Index ",i,": Symbol = ",SymbolName(i,true)); ... }
Tras la compilación, esto es lo que obtenemos:

Observe que los símbolos de Observación del Mercado, que son 12, son los que se imprimen en el diario de la caja de herramientas en el mismo orden cronológico. Las hemos resaltado en rojo y negro, respectivamente, para facilitar su distinción y consulta. Para crear los botones verticales dinámicos, ésta es la lógica que utilizamos.
createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray);
Aquí, comenzamos construyendo el nombre del botón concatenando la macro «Symb» (definida como "Symb “) con el índice "i", convertido a una cadena usando la función IntegerToString , asegurando que cada botón tenga un identificador único como «Symb 0», «Symb 1», y así sucesivamente. Establecemos la etiqueta del botón con el nombre del símbolo de negociación en el índice «i», obtenido mediante la función SymbolName que recupera el nombre del símbolo, con el parámetro true que garantiza que el nombre está en la lista de Observación del Mercado. Fijamos la coordenada "X" del botón en 600 píxeles, alineando todos los botones de símbolos verticalmente. La coordenada "Y" se calcula dinámicamente como (50 + «YS1») + «i» multiplicado por «YS1», lo que posiciona cada botón secuencialmente hacia abajo sumando la altura del botón («YS1», que es de 25 píxeles) multiplicada por el índice «i» al desplazamiento inicial de 50 píxeles más «YS1». Especificamos las dimensiones del botón utilizando los valores «XS1» (90 píxeles de anchura) e «YS1» (25 píxeles de altura). Establecemos el color del texto en «clrW» (blanco), el color de fondo en gris, el tamaño de la fuente en 11 y, por último, el color del borde en gris. Tras la compilación, éste es el resultado:

Esta es la lista de todos los símbolos que están en Observación del Mercado. Si por casualidad añades o eliminas los símbolos, se crearán automáticamente, lo que confirma nuestro empeño en utilizar una lógica dinámica para su creación. Eliminemos algunos de los pares, concretamente los 3 últimos, y veamos si es así.

Puede ver que se hace automáticamente. Ahora volveremos a añadir los símbolos eliminados y continuaremos elaborando una lógica que ayude a identificar y priorizar el símbolo seleccionado en ese momento y a distinguirlo del resto de botones de símbolos.
if (SymbolName(i, true) == _Symbol) { createButton(Symb + IntegerToString(i), "*" + SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrB, clrLimeGreen, 11, clrW); } else { createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray); }
Aquí, en lugar de crear los botones con texturas similares, creamos los botones y diferenciamos la apariencia del botón para el símbolo activo en ese momento. Comenzamos comprobando si el nombre del símbolo en el índice «i» coincide con el símbolo activo actual, recuperado por la variable predefinida _Symbol. Si hay una coincidencia, creamos un botón con un aspecto distinto para resaltar el símbolo activo. Para el símbolo activo, fijamos el color del texto en «clrB» (negro), el color de fondo en verde lima, el tamaño de letra en 11 y el color del borde en «clrW» (blanco). Esta combinación de colores distinta resaltará el símbolo activo para facilitar su identificación. Si el símbolo no coincide con el símbolo activo, la función por defecto toma el relevo y crea el botón con el esquema de apariencia estándar, garantizando que los símbolos inactivos se muestren de forma coherente y visualmente distinta del símbolo activo. Tras la recopilación, esto es lo que hemos conseguido.

Después de crear todos los botones de símbolos necesarios, vamos a tener un pie de página que significa el final de la matriz de visualización de símbolos y añadir un poco de información de resumen a la misma. Se ha adaptado el siguiente fragmento de código.
// Create the base button for the RSI Dashboard at the end if (i == SymbolsTotal(true) - 1) { createButton(Base + IntegerToString(i), "RSI DashBoard", 600, (50 + YS1) + (i * YS1) + YS1, XS1 + XS2 * ArraySize(periods), YS1, clrW, clrGray, 11, clrGray); }
Para crear el botón base del panel RSI y asegurarnos de que se sitúa al final de la lista de símbolos, empezamos comprobando si el índice actual «i» es igual al número total de símbolos menos uno, lo que significa que es el último símbolo de la lista. Si esta condición es verdadera, procedemos a crear el botón base. Utilizamos la función «createButton» para definir el aspecto y la posición de este botón base. Para construir el nombre del botón, concatenamos la cadena «Base» con el índice «i», proporcionando un identificador único, y establecemos su etiqueta como «RSI Dashboard». Se trata de un valor arbitrario y puedes cambiarlo por otro si lo consideras oportuno. A continuación, colocamos el botón en una coordenada "X" de 600 píxeles y una coordenada "Y" calculada como (50 + «YS1») + (i * «YS1») + «YS1», asegurándonos de que aparece justo debajo del último botón de símbolo. Definimos el ancho del botón como «XS1» + «XS2» multiplicado por la función ArraySize que devuelve el total de elementos del array «periods», para abarcar el ancho de todos los botones de marco temporal combinados, mientras que definimos la altura a «YS1» (25 píxeles). La combinación de colores del botón adopta el aspecto estándar. Generalmente, este botón base actuará como una etiqueta clara para todo el panel RSI, proporcionando un ancla visual en la parte inferior de la lista de símbolos. A continuación se muestran los resultados clave.

Por último, ahora tenemos que crear botones que muestren los valores del RSI para cada combinación de símbolo y marco temporal. Para ello se utiliza el siguiente fragmento de código.
// Loop to create buttons for RSI values for each symbol and timeframe for (int j = 0; j < ArraySize(periods); j++) { createButton(RSI + IntegerToString(j) + " " + SymbolName(i, true), "-/-", (600 - XS1) + (j * -XS2), (50 + YS1) + (i * YS1), XS2 - 1, YS1 - 1, clrB, clrW, 12, clrW); } }
Iniciamos el bucle con un contador entero «j», que representa el índice del marco temporal. Para cada marco temporal, llamamos a la función «createButton» para configurar un botón que mostrará el valor RSI para el símbolo y el marco temporal actuales. Generamos el nombre del botón concatenando la string del RSI con el índice «j» y el nombre del símbolo, lo que garantiza un identificador único para cada botón. Establecemos la etiqueta del botón en «-/-», que más tarde actualizaremos con el valor RSI real. De momento, asegurémonos de que podemos producir un diseño de cuadrícula símbolo-período. Colocamos el botón en una coordenada "X" de (600 - «XS1») + (j * - «XS2»), lo que dispone los botones horizontalmente, espaciados por «XS2» (100 píxeles) con ajustes para tener en cuenta la anchura del botón. Del mismo modo, fijamos la coordenada "Y" en (50 + «YS1») + (i * «YS1»), asegurándonos de que los botones se colocan verticalmente en función del índice del símbolo. Las dimensiones de los botones son «XS2» - 1 para la anchura e «YS1» - 1 para la altura. La negación de 1 garantiza que dejamos un límite de 1 píxel para que haya una apariencia de cuadrícula de botones. A continuación, establecemos el esquema de color de los botones con un color de texto «clrB» (negro), un color de fondo «clrW» (blanco), un tamaño de fuente de 12 y un color de borde «clrW» (blanco). Esta configuración organizará los botones RSI en un diseño de cuadrícula, cada uno asociado a un símbolo y un marco temporal específicos, proporcionando una visión clara y estructurada de los valores RSI a lo largo de diferentes periodos. Tras la compilación, esto es lo que obtenemos:

Ahora que podemos crear el diseño general de la cuadrícula del cuadro de mandos, sólo tenemos que obtener los valores de los indicadores y actualizar los botones. Antes de llegar a ese punto, podemos previsualizar los objetos creados haciendo clic con el botón derecho del ratón en cualquier parte del gráfico y seleccionando la opción «Lista de objetos» en la ventana emergente. También puede pulsar «Ctrl + B». En la ventana emergente, haz clic en «Listar todos» y aparecerá la lista de los elementos que hemos creado.

Ahora estamos seguros de que creamos los objetos cronológicamente en el gráfico con sus respectivos nombres únicos. Así nos aseguramos de no dejar ningún cabo suelto. Puedes ver que por ejemplo en los símbolos, tenemos el primer símbolo como «Symb 0», que lo distingue de los otros símbolos. Si no hubiéramos concatenado los nombres de los símbolos con los objetos respectivos, todos los botones tendrían el mismo nombre de símbolo, lo que daría lugar a un error en la creación de los demás, ya que una vez creado, se queda fijo y ningún otro botón asumirá el mismo nombre. Así que, técnicamente, sólo se crearía un botón y se ignoraría el resto. ¡Qué astuto! Bien, ahora continuamos creando los valores de inicialización.
// Loop to initialize RSI values and update buttons for(int i = 0; i < SymbolsTotal(true); i++) { for (int j = 0; j < ArraySize(periods); j++) { // Get the handle ID for the RSI indicator for the specific symbol and timeframe handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE); ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array ... }
Aquí, utilizamos dos bucles para iterar a través de cada símbolo de negociación y marco temporal para inicializar los valores RSI y actualizar los botones correspondientes. Comenzamos con un bucle externo con índice «i» que selecciona un símbolo de negociación en la vigilancia del mercado, y dentro de este bucle, tenemos un bucle "for" interno, que itera a través de cada marco temporal definido, para cada símbolo seleccionado. Esto significa que, por ejemplo, seleccionamos «AUDUSD», iteramos a través de todos los plazos definidos, es decir, «M1», «M5», «H1», «H4» y «D1». Para comprender fácilmente la lógica, veamos una impresión.
// Loop to initialize RSI values and update buttons for(int i = 0; i < SymbolsTotal(true); i++) { Print("SELECTED SYMBOL = ",SymbolName(i, true)); for (int j = 0; j < ArraySize(periods); j++) { Print("PERIOD = ",EnumToString(periods[j])); ... }
Esto es lo que tenemos.

A continuación, para cada combinación de símbolo y marco temporal, obtenemos el asa del indicador RSI utilizando la función iRSI con los parámetros: símbolo, marco temporal, periodo del RSI como 14, y tipo de precio como los precios de cierre. El manejador «handleName_Id» no es más que un número entero que se define y almacena en algún lugar de la memoria de nuestro ordenador y nos permite interactuar con los datos del indicador RSI. Por defecto, el entero comienza en el índice 10, y si hay algún otro indicador creado, se incrementa en uno, es decir, 10, 11, 12, 13, ... y así sucesivamente. Para ilustrarlo, imprimámoslo.
Print("PERIOD = ",EnumToString(periods[j]),": HANDLE ID = ",handleName_Id);
Tras la compilación, esto es lo que tenemos:

Puede ver que la ID de las manejadores empieza en 10, y como tenemos 12 símbolos y 5 periodos para cada símbolo, prevemos un total de (12 * 5 = 60) 60 manejadores de indicadores. Pero como empezamos nuestra indexación en 10, tendremos que incluir los 9 primeros valores en el resultado para obtener la ID del manejador final. Matemáticamente, será 60 + 9, lo que da como resultado (60 + 9 = 69) 69. Confirmemos si es correcto mediante una representación visual.

Era correcto. A continuación, establecemos la matriz «rsi_Data_Val» para que se utilice como serie temporal llamando a la función ArraySetAsSeries y estableciendo el indicador en verdadero para confirmar la acción. Esta configuración garantizará que los valores RSI más recientes se encuentren al principio de la matriz. A continuación, procedemos a copiar los valores RSI en el array utilizando la función CopyBuffer , donde 0 indica el índice del buffer, 0 es la posición de inicio, 1 especifica el número de puntos de datos a recuperar, y «rsi_Data_Val» es el array de destino donde almacenamos los datos recuperados para su posterior análisis. Imprimamos los datos en el diario y veamos qué obtenemos. Usamos la función ArrayPrint para imprimir el array dinámico.
ArrayPrint(rsi_Data_Val);
Esto es lo que obtenemos:

Ha sido increíble. Obtenemos los datos correctos. Puede confirmar los datos en la ventana del indicador y en la ventana de datos con los que recuperamos. Esta es una clara ilustración de que tenemos los datos y podemos proceder. Puedes ver la belleza de confirmar todo lo que hacemos. En cada nueva lógica que añada al panel de control, se recomienda compilar y ejecutar una prueba para afirmar que se obtienen los resultados previstos. Ahora sólo tenemos que utilizar los valores del indicador para actualizar el panel de control y habremos terminado.
// Update the button with the RSI value and colors update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), clrBlack, clrW_Gray, clrWhite);
En este caso, utilizamos una función personalizada «update_Button» para actualizar los botones del panel de control con los datos del indicador correspondiente. La lógica de la función es la siguiente.
//+------------------------------------------------------------------+ //| Function to update a button | //+------------------------------------------------------------------+ bool update_Button(string objName, string text, color clrTxt = clrBlack, color clrBG = clrWhite, color clrBorder = clrWhite ) { int found = ObjectFind(0, objName); // Find the button by name // Check if the button exists if (found < 0) { ResetLastError(); // Reset the last error code Print("UNABLE TO FIND THE BTN: ERR Code: ", GetLastError()); // Print error message if button is not found return (false); // Return false if button is not found } else { // Update button properties ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBG); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ChartRedraw(0); // Redraw the chart to reflect the updated button return (true); // Return true if update is successful } }
Esta función es idéntica a la que utilizamos para crear un botón. La diferencia es que en lugar de crear el botón, encontramos el botón utilizando la función ObjectFind. Si tiene éxito, devuelve el número de la subventana (0 significa la ventana principal del gráfico), en la que se encuentra el objeto. Si no se encuentra el objeto, la función devuelve un número negativo. Así, si el valor de retorno es menor que 0, significa que es un número negativo que indica la ausencia del objeto e informamos del error imprimiendo el código de error, y antes, reiniciamos el posible error anterior, y devolvemos falso para terminar la función. Si pasa, encontramos el objeto y simplemente procedemos a actualizar las propiedades del objeto, es decir, el texto, los esquemas de color del texto, el fondo y el borde. Tras la compilación, esto es lo que obtenemos:

Excelente. Ya tenemos los datos del indicador en la cuadrícula. Fíjate en lo bien que se mapean los datos con una sola línea de código. Confirme también que los datos que teníamos antes, es decir, 55,27, para un marco temporal de 1 hora del símbolo actual están correctamente rellenados. Para hacerlo aún más sofisticado, definamos las condiciones de entrada en el mercado basándonos en los niveles de sobrecompra y sobreventa y cambiemos los colores para facilitar la consulta. Así que ahora, en lugar de utilizar colores fijos, vamos a utilizar colores dinámicos.
// Declare variables for button colors color text_clr, bg_clr, border_clr; // Determine button colors based on RSI value if (rsi_Data_Val[0] < 30) { text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen; } else if (rsi_Data_Val[0] > 70) { text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed; } else { text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite; } // Update the button with the RSI value and colors update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr);
En primer lugar, declaramos tres variables para los colores de los botones: «text_clr» para el color del texto, «bg_clr» para el color de fondo y «border_clr» para el color del borde. A continuación, determinamos los colores adecuados en función del valor RSI almacenado en la matriz de datos RSI. Si el valor del RSI es inferior a 30, lo que suele indicar que el activo está sobrevendido, establecemos el color del texto en blanco, el color de fondo en verde y el color del borde en verde, una combinación que resalta el botón en verde para indicar una posible oportunidad de compra.
Del mismo modo, si el valor del RSI es superior a 70, lo que sugiere que el activo está sobrecomprado, establecemos el color del texto en blanco, el color de fondo en rojo y el color del borde en rojo, lo que indica una posible oportunidad de venta con un botón rojo. Para valores de RSI entre 30 y 70, se mantiene el esquema de colores por defecto, que refleja un estado estándar o neutro. Por último, actualizamos la apariencia del botón llamando a la función «update_Button» y pasándole los respectivos parámetros del botón. Esto garantiza que cada botón del cuadro de mandos refleje con precisión el estado actual del RSI y comunique visualmente las condiciones del mercado. Para ver el logro, he aquí una representación visual:

Perfecto. Ahora hemos creado un panel de indicadores dinámico y receptivo que muestra las condiciones actuales del mercado en el gráfico, con destacados de referencia más fáciles de habilitar.
El código fuente completo responsable de la inicialización del panel indicador es el siguiente:
//+------------------------------------------------------------------+ //| ADVANCED IND DASHBOARD.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" // Define button identifiers and properties #define BTN1 "BTN1" #define Desc "Desc " #define Symb "Symb " #define Base "Base " #define RSI "RSI " #define XS1 90 #define XS2 100 #define YS1 25 #define clrW clrWhite #define clrB clrBlack #define clrW_Gray C'230,230,230' // Define the timeframes to be used ENUM_TIMEFRAMES periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H4, PERIOD_D1}; // Function to truncate the ENUM_TIMEFRAMES string for display purposes string truncPrds(ENUM_TIMEFRAMES period) { // Extract the timeframe abbreviation from the full ENUM string string prd = StringSubstr(EnumToString(period), 7); return prd; // Return the truncated string } // Global variables int handleName_Id; // Variable to store the handle ID for the RSI indicator double rsi_Data_Val[]; // Array to store the RSI values //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Create the main button for the pair with specific properties createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray); // Loop to create buttons for each timeframe with the corresponding RSI label for(int i = 0; i < ArraySize(periods); i++) { //Print("BEFORE: ",EnumToString(periods[i])); //Print("AFTER: ",truncPrds(periods[i])); createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrGray, 13, clrGray); } // Loop to create buttons for each symbol for(int i = 0; i < SymbolsTotal(true); i++) { //Print("Index ",i,": Symbol = ",SymbolName(i,true)); // Check if the symbol is the current symbol being traded if (SymbolName(i, true) == _Symbol) { createButton(Symb + IntegerToString(i), "*" + SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrB, clrLimeGreen, 11, clrW); } else { createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray); } // Create the base button for the RSI Dashboard at the end if (i == SymbolsTotal(true) - 1) { createButton(Base + IntegerToString(i), "RSI DashBoard", 600, (50 + YS1) + (i * YS1) + YS1, XS1 + XS2 * ArraySize(periods), YS1, clrW, clrGray, 11, clrGray); } // Loop to create buttons for RSI values for each symbol and timeframe for (int j = 0; j < ArraySize(periods); j++) { createButton(RSI + IntegerToString(j) + " " + SymbolName(i, true), "-/-", (600 - XS1) + (j * -XS2), (50 + YS1) + (i * YS1), XS2 - 1, YS1 - 1, clrB, clrW, 12, clrW); } } // Loop to initialize RSI values and update buttons for(int i = 0; i < SymbolsTotal(true); i++) { //Print("SELECTED SYMBOL = ",SymbolName(i, true)); for (int j = 0; j < ArraySize(periods); j++) { //Print("PERIOD = ",EnumToString(periods[j])); // Get the handle ID for the RSI indicator for the specific symbol and timeframe handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE); //Print("PERIOD = ",EnumToString(periods[j]),": HANDLE ID = ",handleName_Id); ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array //ArrayPrint(rsi_Data_Val); // Declare variables for button colors color text_clr, bg_clr, border_clr; // Determine button colors based on RSI value if (rsi_Data_Val[0] < 30) { text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen; } else if (rsi_Data_Val[0] > 70) { text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed; } else { text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite; } // Update the button with the RSI value and colors update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr); } } return(INIT_SUCCEEDED); // Return initialization success } //+------------------------------------------------------------------+ //| Function to create a button | //+------------------------------------------------------------------+ bool createButton(string objName, string text, int xD, int yD, int xS, int yS, color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE, string font = "Arial Rounded MT Bold" ) { ResetLastError(); // Reset the last error code // Attempt to create the button if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails return (false); // Return false if creation fails } // Set button properties ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected ChartRedraw(0); // Redraw the chart to reflect the new button return (true); // Return true if creation is successful } //+------------------------------------------------------------------+ //| Function to update a button | //+------------------------------------------------------------------+ bool update_Button(string objName, string text, color clrTxt = clrBlack, color clrBG = clrWhite, color clrBorder = clrWhite ) { int found = ObjectFind(0, objName); // Find the button by name // Check if the button exists if (found < 0) { ResetLastError(); // Reset the last error code Print("UNABLE TO FIND THE BTN: ERR Code: ", GetLastError()); // Print error message if button is not found return (false); // Return false if button is not found } else { // Update button properties ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBG); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ChartRedraw(0); // Redraw the chart to reflect the updated button return (true); // Return true if update is successful } }
Incluso si creamos el panel con todos los elementos, necesitaremos actualizarlo en cada tick para que los datos en el panel reflejen los datos más recientes. Eso se consigue en el manejador de eventos OnTick.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { ... }
Esta es una función manejadora de eventos void que se llama cada vez que hay cambios en las cotizaciones de precios. Para actualizar los valores del indicador, necesitaremos ejecutar alguna parte del código de inicialización aquí para que obtengamos los últimos valores.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Loop to update RSI values and buttons on each tick for(int i = 0; i < SymbolsTotal(true); i++) { for (int j = 0; j < ArraySize(periods); j++) { // Get the handle ID for the RSI indicator for the specific symbol and timeframe handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE); ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array // Declare variables for button colors color text_clr, bg_clr, border_clr; // Determine button colors based on RSI value if (rsi_Data_Val[0] < 30) { text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen; } else if (rsi_Data_Val[0] > 70) { text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed; } else { text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite; } // Update the button with the RSI value and colors update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr); } } }
Aquí, sólo tenemos que copiar y pegar el fragmento de código en la sección de inicialización que tiene dos bucles "for"; interior y exterior, y actualizar los valores del indicador. Esto garantiza que, en cada tick, actualicemos los valores a los últimos datos recuperados. De este modo, el panel de control resulta muy dinámico, vivo y atractivo. En un Formato de Intercambio de Gráficos (Graphics Interchange Format, GIF), este es el hito que hemos alcanzado:

Por último, tenemos que deshacernos de los objetos que hemos creado, es decir, el panel de indicadores, una vez que el EA se elimina del gráfico. Esto asegurará que el tablero de indicadores sea destruido dejando el gráfico limpio. Por lo tanto, la función es crucial para mantener un entorno comercial limpio y eficiente. Eso se logra en la función manejadora de eventos OnDeinit, que se llama cada vez que el Asesor Experto se elimina del gráfico.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // remove all dashboard objects ObjectsDeleteAll(0,BTN1); ObjectsDeleteAll(0,Desc); ObjectsDeleteAll(0,Symb); ObjectsDeleteAll(0,Base); ObjectsDeleteAll(0,RSI); ChartRedraw(0); }
Aquí, llamamos a la función ObjectDeleteAll para eliminar todos los objetos con el nombre de prefijo especificado. La función elimina todos los objetos del tipo especificado utilizando prefijos en los nombres de objeto. La lógica aquí es bastante sencilla. Llamamos a la función para cada uno de los prefijos definidos: «BTN1», “Desc”, “Symb”, “Base” y “RSI”, ya que de este modo se eliminarán todos los objetos asociados a estos prefijos, eliminando efectivamente todos los botones y elementos gráficos del gráfico. Por último, llamamos a la función ChartRedraw para refrescar el gráfico y reflejar la eliminación de estos objetos, asegurándonos de que el gráfico está actualizado y libre de cualquier elemento residual creado por el programa. Esto es lo que tenemos:

Ahora hemos creado un panel de indicadores totalmente funcional en MQL5. El código fuente completo responsable de la creación del panel es el siguiente:
//+------------------------------------------------------------------+ //| ADVANCED IND DASHBOARD.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" // Define button identifiers and properties #define BTN1 "BTN1" #define Desc "Desc " #define Symb "Symb " #define Base "Base " #define RSI "RSI " #define XS1 90 #define XS2 100 #define YS1 25 #define clrW clrWhite #define clrB clrBlack #define clrW_Gray C'230,230,230' // Define the timeframes to be used ENUM_TIMEFRAMES periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H4, PERIOD_D1}; // Function to truncate the ENUM_TIMEFRAMES string for display purposes string truncPrds(ENUM_TIMEFRAMES period) { // Extract the timeframe abbreviation from the full ENUM string string prd = StringSubstr(EnumToString(period), 7); return prd; // Return the truncated string } // Global variables int handleName_Id; // Variable to store the handle ID for the RSI indicator double rsi_Data_Val[]; // Array to store the RSI values //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Create the main button for the pair with specific properties createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray); // Loop to create buttons for each timeframe with the corresponding RSI label for(int i = 0; i < ArraySize(periods); i++) { //Print("BEFORE: ",EnumToString(periods[i])); //Print("AFTER: ",truncPrds(periods[i])); createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrGray, 13, clrGray); } // Loop to create buttons for each symbol for(int i = 0; i < SymbolsTotal(true); i++) { //Print("Index ",i,": Symbol = ",SymbolName(i,true)); // Check if the symbol is the current symbol being traded if (SymbolName(i, true) == _Symbol) { createButton(Symb + IntegerToString(i), "*" + SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrB, clrLimeGreen, 11, clrW); } else { createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray); } // Create the base button for the RSI Dashboard at the end if (i == SymbolsTotal(true) - 1) { createButton(Base + IntegerToString(i), "RSI DashBoard", 600, (50 + YS1) + (i * YS1) + YS1, XS1 + XS2 * ArraySize(periods), YS1, clrW, clrGray, 11, clrGray); } // Loop to create buttons for RSI values for each symbol and timeframe for (int j = 0; j < ArraySize(periods); j++) { createButton(RSI + IntegerToString(j) + " " + SymbolName(i, true), "-/-", (600 - XS1) + (j * -XS2), (50 + YS1) + (i * YS1), XS2 - 1, YS1 - 1, clrB, clrW, 12, clrW); } } // Loop to initialize RSI values and update buttons for(int i = 0; i < SymbolsTotal(true); i++) { //Print("SELECTED SYMBOL = ",SymbolName(i, true)); for (int j = 0; j < ArraySize(periods); j++) { //Print("PERIOD = ",EnumToString(periods[j])); // Get the handle ID for the RSI indicator for the specific symbol and timeframe handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE); //Print("PERIOD = ",EnumToString(periods[j]),": HANDLE ID = ",handleName_Id); ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array //ArrayPrint(rsi_Data_Val); // Declare variables for button colors color text_clr, bg_clr, border_clr; // Determine button colors based on RSI value if (rsi_Data_Val[0] < 30) { text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen; } else if (rsi_Data_Val[0] > 70) { text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed; } else { text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite; } // Update the button with the RSI value and colors update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr); } } return(INIT_SUCCEEDED); // Return initialization success } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // remove all dashboard objects ObjectsDeleteAll(0,BTN1); ObjectsDeleteAll(0,Desc); ObjectsDeleteAll(0,Symb); ObjectsDeleteAll(0,Base); ObjectsDeleteAll(0,RSI); ChartRedraw(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Loop to update RSI values and buttons on each tick for(int i = 0; i < SymbolsTotal(true); i++) { for (int j = 0; j < ArraySize(periods); j++) { // Get the handle ID for the RSI indicator for the specific symbol and timeframe handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE); ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array // Declare variables for button colors color text_clr, bg_clr, border_clr; // Determine button colors based on RSI value if (rsi_Data_Val[0] < 30) { text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen; } else if (rsi_Data_Val[0] > 70) { text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed; } else { text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite; } // Update the button with the RSI value and colors update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr); } } } //+------------------------------------------------------------------+ //| Function to create a button | //+------------------------------------------------------------------+ bool createButton(string objName, string text, int xD, int yD, int xS, int yS, color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE, string font = "Arial Rounded MT Bold" ) { ResetLastError(); // Reset the last error code // Attempt to create the button if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails return (false); // Return false if creation fails } // Set button properties ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected ChartRedraw(0); // Redraw the chart to reflect the new button return (true); // Return true if creation is successful } //+------------------------------------------------------------------+ //| Function to update a button | //+------------------------------------------------------------------+ bool update_Button(string objName, string text, color clrTxt = clrBlack, color clrBG = clrWhite, color clrBorder = clrWhite ) { int found = ObjectFind(0, objName); // Find the button by name // Check if the button exists if (found < 0) { ResetLastError(); // Reset the last error code Print("UNABLE TO FIND THE BTN: ERR Code: ", GetLastError()); // Print error message if button is not found return (false); // Return false if button is not found } else { // Update button properties ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBG); // Set background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color ChartRedraw(0); // Redraw the chart to reflect the updated button return (true); // Return true if update is successful } }
Conclusión
En conclusión, la creación de un panel de indicadores RSI multisímbolo y multiperiodo en MetaQuotes Language 5 (MQL5) ofrece a los operadores una herramienta útil para el análisis del mercado. Este panel muestra los valores del RSI en tiempo real en diferentes símbolos y marcos temporales, lo que ayuda a los operadores a tomar decisiones informadas con mayor rapidez.La construcción del panel de control implicó configurar componentes, crear botones y actualizarlos en función de los datos en tiempo real mediante funciones MQL5. El producto final es funcional y fácil de usar.
Este artículo muestra cómo se puede utilizar MQL5 para crear herramientas prácticas de trading. Los operadores pueden replicar o modificar este panel de control para adaptarlo a sus necesidades específicas, apoyando estrategias de negociación tanto semiautomatizadas como manuales en un entorno de mercados en evolución.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/15356
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
De principiante a experto: El viaje esencial a través del trading con MQL5
Análisis del sentimiento en Twitter con sockets
Implementación de una estrategia de trading con Bandas de Bollinger en MQL5: Guía paso a paso
Características del Wizard MQL5 que debe conocer (Parte 29): Continuación sobre las tasas de aprendizaje con MLP
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso