Herramientas de trading de MQL5 (Parte 3): Creación de un panel de control con análisis de múltiples marcos temporales para el trading estratégico
Introducción
En nuestro artículo anterior, la parte 2, mejoramos una herramienta de asistencia al trading en MetaQuotes Language 5 (MQL5) añadiéndole una retroalimentación visual dinámica para aumentar la interactividad. Ahora, nos centramos en desarrollar un panel de control con escáner multitemporal para ofrecer señales de trading en tiempo real que ayuden en la toma de decisiones estratégicas. Presentamos una interfaz basada en cuadrícula con señales controladas por indicadores y un botón de cierre, y estos avances se desarrollan en los siguientes apartados:
Estas secciones nos guían hacia la creación de un panel de control de operaciones intuitivo y potente.
El plan del panel de control del escáner
Nuestro objetivo es crear un panel de control multitemporal que proporcione señales de trading claras y en tiempo real para mejorar la toma de decisiones estratégicas. El panel de control contará con un diseño en cuadrícula que mostrará señales de compra y venta en distintos marcos temporales, lo que nos permitirá evaluar rápidamente las condiciones del mercado sin tener que cambiar de gráfico. Se incluirá un botón de cierre para permitir cerrar fácilmente el panel, lo que garantizará una experiencia de usuario limpia y flexible que se adapte a nuestras necesidades de negociación.
Incorporaremos señales de indicadores clave, entre los que se incluyen el Índice de Fuerza Relativa (RSI), el Oscilador Estocástico (STOCH), el Commodity Channel Index (CCI), el Índice Direccional Medio (ADX) y el Awesome Oscillator (AO), diseñados para identificar posibles oportunidades de negociación con umbrales de fuerza personalizables. Sin embargo, la elección de los indicadores o los datos de acción del precio que se utilicen depende de usted. Esta configuración nos ayudará a detectar tendencias y cambios de tendencia en diferentes marcos temporales, lo que permite aplicar estrategias tanto a corto como a largo plazo. Nuestro objetivo es crear una herramienta intuitiva y optimizada que proporcione información útil sin dejar de ser fácil de usar, allanando el camino para futuras mejoras como alertas automatizadas o indicadores adicionales. A continuación puede verse una vista previa del resultado previsto.

Implementación en MQL5
Para crear el programa en MQL5, tendremos que definir los metadatos del programa y, a continuación, definir algunas constantes de nombre de objetos, que nos ayudarán a referirnos a los objetos del panel de control y a gestionarlos fácilmente.
//+------------------------------------------------------------------+ //| TimeframeScanner Dashboard EA.mq5 | //| Copyright 2025, Allan Munene Mutiiria. | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Allan Munene Mutiiria." #property link "https://t.me/Forex_Algo_Trader" #property version "1.00" // Define identifiers and properties for UI elements #define MAIN_PANEL "PANEL_MAIN" //--- Main panel rectangle identifier #define HEADER_PANEL "PANEL_HEADER" //--- Header panel rectangle identifier #define HEADER_PANEL_ICON "PANEL_HEADER_ICON" //--- Header icon label identifier #define HEADER_PANEL_TEXT "PANEL_HEADER_TEXT" //--- Header title label identifier #define CLOSE_BUTTON "BUTTON_CLOSE" //--- Close button identifier #define SYMBOL_RECTANGLE "SYMBOL_HEADER" //--- Symbol rectangle identifier #define SYMBOL_TEXT "SYMBOL_TEXT" //--- Symbol text label identifier #define TIMEFRAME_RECTANGLE "TIMEFRAME_" //--- Timeframe rectangle prefix #define TIMEFRAME_TEXT "TIMEFRAME_TEXT_" //--- Timeframe text label prefix #define HEADER_RECTANGLE "HEADER_" //--- Header rectangle prefix #define HEADER_TEXT "HEADER_TEXT_" //--- Header text label prefix #define RSI_RECTANGLE "RSI_" //--- RSI rectangle prefix #define RSI_TEXT "RSI_TEXT_" //--- RSI text label prefix #define STOCH_RECTANGLE "STOCH_" //--- Stochastic rectangle prefix #define STOCH_TEXT "STOCH_TEXT_" //--- Stochastic text label prefix #define CCI_RECTANGLE "CCI_" //--- CCI rectangle prefix #define CCI_TEXT "CCI_TEXT_" //--- CCI text label prefix #define ADX_RECTANGLE "ADX_" //--- ADX rectangle prefix #define ADX_TEXT "ADX_TEXT_" //--- ADX text label prefix #define AO_RECTANGLE "AO_" //--- AO rectangle prefix #define AO_TEXT "AO_TEXT_" //--- AO text label prefix #define BUY_RECTANGLE "BUY_" //--- Buy rectangle prefix #define BUY_TEXT "BUY_TEXT_" //--- Buy text label prefix #define SELL_RECTANGLE "SELL_" //--- Sell rectangle prefix #define SELL_TEXT "SELL_TEXT_" //--- Sell text label prefix #define WIDTH_TIMEFRAME 90 //--- Width of timeframe and symbol rectangles #define WIDTH_INDICATOR 70 //--- Width of indicator rectangles #define WIDTH_SIGNAL 90 //--- Width of BUY/SELL signal rectangles #define HEIGHT_RECTANGLE 25 //--- Height of all rectangles #define COLOR_WHITE clrWhite //--- White color for text and backgrounds #define COLOR_BLACK clrBlack //--- Black color for borders and text #define COLOR_LIGHT_GRAY C'230,230,230' //--- Light gray color for signal backgrounds #define COLOR_DARK_GRAY C'105,105,105' //--- Dark gray color for indicator backgrounds
Comenzamos por definir el marco de la interfaz de usuario para nuestro panel de control del escáner de múltiples marcos temporales utilizando la directiva #define para crear constantes como «MAIN_PANEL» y «HEADER_PANEL» para los rectángulos del panel principal y del encabezado, y «HEADER_PANEL_ICON», «HEADER_PANEL_TEXT» y «CLOSE_BUTTON» para el icono, el título y el botón de cierre del encabezado.
Definimos los identificadores para la estructura de cuadrícula del panel de control. Para el símbolo, establecemos «SYMBOL_RECTANGLE» y «SYMBOL_TEXT», mientras que los prefijos «TIMEFRAME_RECTANGLE» y «TIMEFRAME_TEXT» se encargan de las filas de los marcos temporales. Utilizamos los prefijos «HEADER_RECTANGLE» y «HEADER_TEXT» para los encabezados de columna, y prefijos como «RSI_RECTANGLE», «STOCH_RECTANGLE» y «BUY_RECTANGLE», junto con los correspondientes «RSI_TEXT», «STOCH_TEXT» y «BUY_TEXT», para las celdas de indicadores y señales.
Configuramos los tamaños con «WIDTH_TIMEFRAME» (90 píxeles), «WIDTH_INDICATOR» (70 píxeles), «WIDTH_SIGNAL» (90 píxeles) y «HEIGHT_RECTANGLE» (25 píxeles). Definimos los colores utilizando «COLOR_WHITE» y «COLOR_BLACK» para el texto y los bordes, «COLOR_LIGHT_GRAY» («C'230,230,230'») para los fondos de las señales y «COLOR_DARK_GRAY» («C'105,105,105'») para los indicadores, lo que garantiza un diseño uniforme y claro. A continuación, debemos definir algunas variables globales más que utilizaremos a lo largo del programa.
bool panel_is_visible = true; //--- Flag to control panel visibility // Define the timeframes to be used ENUM_TIMEFRAMES timeframes_array[] = {PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M20, PERIOD_M30, PERIOD_H1, PERIOD_H2, PERIOD_H3, PERIOD_H4, PERIOD_H8, PERIOD_H12, PERIOD_D1, PERIOD_W1}; //--- Array of timeframes for scanning // Global variables for indicator values double rsi_values[]; //--- Array to store RSI values double stochastic_values[]; //--- Array to store Stochastic signal line values double cci_values[]; //--- Array to store CCI values double adx_values[]; //--- Array to store ADX values double ao_values[]; //--- Array to store AO values
Aquí declaramos la variable booleana «panel_is_visible» y le asignamos el valor «true», lo que determina si el panel de control se muestra en el gráfico. Esta opción nos permite activar o desactivar la visibilidad del panel de control según sea necesario, sobre todo cuando no necesitamos actualizaciones de datos. A continuación, definimos la matriz «timeframes_array» utilizando el tipo ENUM_TIMEFRAMES, en la que se enumeran los periodos desde «PERIOD_M1» (1 minuto) hasta «PERIOD_W1» (semanal). Esta matriz especifica los marcos temporales que analizará el panel de control, lo que nos permite examinar las señales del mercado en múltiples horizontes temporales de forma estructurada. Si no necesitas algunos de estos marcos temporales o quieres ajustarlos, basta con modificar las enumeraciones.
Para almacenar los datos de los indicadores, creamos las matrices de tipo double «rsi_values», «stochastic_values», «cci_values», «adx_values» y «ao_values». Estas matrices contienen los valores calculados para el Índice de Fuerza Relativa, el Oscilador Estocástico, el Commodity Channel Index (CCI), el Índice Direccional Medio (ADX) y el Oscilador Awesome, respectivamente, lo que nos permite procesar y mostrar de forma eficaz las señales de trading para cada marco temporal. Ahora podemos definir algunas funciones auxiliares que utilizaremos para formatear el nombre del marco temporal mostrado y para apoyar la lógica de señalización, tal y como se indica a continuación.
//+------------------------------------------------------------------+ //| Truncate timeframe enum to display string | //+------------------------------------------------------------------+ string truncate_timeframe_name(int timeframe_index) //--- Function to format timeframe name { string timeframe_string = StringSubstr(EnumToString(timeframes_array[timeframe_index]), 7); //--- Extract timeframe name return timeframe_string; //--- Return formatted name } //+------------------------------------------------------------------+ //| Calculate signal strength for buy/sell | //+------------------------------------------------------------------+ string calculate_signal_strength(double rsi, double stochastic, double cci, double adx, double ao, bool is_buy) //--- Function to compute signal strength { int signal_strength = 0; //--- Initialize signal strength counter if(is_buy && rsi < 40) signal_strength++; //--- Increment for buy if RSI is oversold else if(!is_buy && rsi > 60) signal_strength++; //--- Increment for sell if RSI is overbought if(is_buy && stochastic < 40) signal_strength++; //--- Increment for buy if Stochastic is oversold else if(!is_buy && stochastic > 60) signal_strength++; //--- Increment for sell if Stochastic is overbought if(is_buy && cci < -70) signal_strength++; //--- Increment for buy if CCI is oversold else if(!is_buy && cci > 70) signal_strength++; //--- Increment for sell if CCI is overbought if(adx > 40) signal_strength++; //--- Increment if ADX indicates strong trend if(is_buy && ao > 0) signal_strength++; //--- Increment for buy if AO is positive else if(!is_buy && ao < 0) signal_strength++; //--- Increment for sell if AO is negative if(signal_strength >= 3) return is_buy ? "Strong Buy" : "Strong Sell"; //--- Return strong signal if 3+ conditions met if(signal_strength >= 2) return is_buy ? "Buy" : "Sell"; //--- Return regular signal if 2 conditions met return "Neutral"; //--- Return neutral if insufficient conditions }
Aquí definimos la función «truncate_timeframe_name», que toma un parámetro entero «timeframe_index» para dar formato a los nombres de los marcos temporales con vistas a su visualización. En el interior, utilizamos la función StringSubstr para extraer una subcadena del resultado de la función EnumToString aplicada a «timeframes_array[timeframe_index]», a partir de la posición 7, y la almacenamos en «timeframe_string». A continuación, devolvemos «timeframe_string», que proporciona un nombre de intervalo de tiempo claro y fácil de leer para el usuario.
Creamos la función «calculate_signal_strength» para determinar las señales de compra o venta en función de los valores del indicador. Inicializamos la variable entera «signal_strength» con el valor cero para contar las condiciones que se cumplen. En el caso del Índice de Fuerza Relativa, incrementamos «signal_strength» si «is_buy» es verdadero y «rsi» es inferior a 40 (sobreventa) o si «is_buy» es falso y «rsi» supera 60 (sobrecompra). Del mismo modo, comprobamos los indicadores «stochastic» (por debajo de 40 o por encima de 60), «CCI» (por debajo de -70 o por encima de 70) y «ao» (positivo para comprar, negativo para vender), incrementando el valor de «signal_strength» cada vez que se cumple una de estas condiciones.
También evaluamos el índice de dirección medio (ADX) y aumentamos el valor de «signal_strength» si «adx» supera 40, lo que indica una tendencia fuerte tanto en situaciones de compra como de venta. Si «signal_strength» alcanza un valor de 3 o más, devolvemos «Strong Buy» si «is_buy» es verdadero, o «Strong Sell» en caso contrario. Si el valor es 2, devolvemos «Buy» o «Sell», y si es inferior, devolvemos «Neutral», lo que permite una clasificación clara de las señales en el panel de control. A continuación, ya podemos definir las funciones que nos permitirán crear los objetos.
//+------------------------------------------------------------------+ //| Create a rectangle for the UI | //+------------------------------------------------------------------+ bool create_rectangle(string object_name, int x_distance, int y_distance, int x_size, int y_size, color background_color, color border_color = COLOR_BLACK) //--- Function to create a rectangle { ResetLastError(); //--- Reset error code if(!ObjectCreate(0, object_name, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { //--- Create rectangle object Print(__FUNCTION__, ": failed to create Rectangle: ERR Code: ", GetLastError()); //--- Log creation failure return(false); //--- Return failure } ObjectSetInteger(0, object_name, OBJPROP_XDISTANCE, x_distance); //--- Set x position ObjectSetInteger(0, object_name, OBJPROP_YDISTANCE, y_distance); //--- Set y position ObjectSetInteger(0, object_name, OBJPROP_XSIZE, x_size); //--- Set width ObjectSetInteger(0, object_name, OBJPROP_YSIZE, y_size); //--- Set height ObjectSetInteger(0, object_name, OBJPROP_CORNER, CORNER_RIGHT_UPPER); //--- Set corner to top-right ObjectSetInteger(0, object_name, OBJPROP_BGCOLOR, background_color); //--- Set background color ObjectSetInteger(0, object_name, OBJPROP_BORDER_COLOR, border_color); //--- Set border color ObjectSetInteger(0, object_name, OBJPROP_BORDER_TYPE, BORDER_FLAT); //--- Set flat border style ObjectSetInteger(0, object_name, OBJPROP_BACK, false); //--- Set to foreground ChartRedraw(0); //--- Redraw chart return(true); //--- Return success } //+------------------------------------------------------------------+ //| Create a text label for the UI | //+------------------------------------------------------------------+ bool create_label(string object_name, string text, int x_distance, int y_distance, int font_size = 12, color text_color = COLOR_BLACK, string font = "Arial Rounded MT Bold") //--- Function to create a label { ResetLastError(); //--- Reset error code if(!ObjectCreate(0, object_name, OBJ_LABEL, 0, 0, 0)) { //--- Create label object Print(__FUNCTION__, ": failed to create Label: ERR Code: ", GetLastError()); //--- Log creation failure return(false); //--- Return failure } ObjectSetInteger(0, object_name, OBJPROP_XDISTANCE, x_distance); //--- Set x position ObjectSetInteger(0, object_name, OBJPROP_YDISTANCE, y_distance); //--- Set y position ObjectSetInteger(0, object_name, OBJPROP_CORNER, CORNER_RIGHT_UPPER); //--- Set corner to top-right ObjectSetString(0, object_name, OBJPROP_TEXT, text); //--- Set label text ObjectSetString(0, object_name, OBJPROP_FONT, font); //--- Set font ObjectSetInteger(0, object_name, OBJPROP_FONTSIZE, font_size); //--- Set font size ObjectSetInteger(0, object_name, OBJPROP_COLOR, text_color); //--- Set text color ObjectSetInteger(0, object_name, OBJPROP_ANCHOR, ANCHOR_CENTER); //--- Center text ChartRedraw(0); //--- Redraw chart return(true); //--- Return success }
Para permitir la creación de los objetos, definimos la función «create_rectangle» con los parámetros «object_name», «x_distance», «y_distance», «x_size», «y_size», «background_color» y «border_color». Utilizamos la función ResetLastError, creamos un OBJ_RECTANGLE_LABEL con la función ObjectCreate y, si falla, registramos los errores con la función «Print», devolviendo el valor false.
Configuramos las propiedades del rectángulo mediante la función ObjectSetInteger para la posición, el tamaño, «CORNER_RIGHT_UPPER», «background_color», «border_color» y «BORDER_FLAT», asegurándonos de que se muestre en primer plano. Utilizamos la función ChartRedraw y devolvemos «true». Para el texto, definimos la función «create_label» con los parámetros «object_name», «text», «x_distance», «y_distance», «font_size», «text_color» y «font».
Utilizamos la función «ResetLastError», creamos un «OBJ_LABEL» con la función «ObjectCreate» y registramos los errores si falla. Utilizamos la función «ObjectSetInteger» para la posición, el tamaño, el color y «ANCHOR_CENTER», y la función ObjectSetString para «text» y «font». Utilizamos la función «ChartRedraw» y devolvemos «true». Con estas funciones, ya podemos crear los objetos de panel iniciales que nos servirán de punto de partida en el controlador de eventos «OnInit».
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() //--- Initialize EA { create_rectangle(MAIN_PANEL, 632, 40, 617, 374, C'30,30,30', BORDER_FLAT); //--- Create main panel background create_rectangle(HEADER_PANEL, 632, 40, 617, 27, C'60,60,60', BORDER_FLAT); //--- Create header panel background create_label(HEADER_PANEL_ICON, CharToString(91), 620, 54, 18, clrAqua, "Wingdings"); //--- Create header icon create_label(HEADER_PANEL_TEXT, "TimeframeScanner", 527, 52, 13, COLOR_WHITE); //--- Create header title create_label(CLOSE_BUTTON, CharToString('r'), 32, 54, 18, clrYellow, "Webdings"); //--- Create close button // Create header rectangle and label create_rectangle(SYMBOL_RECTANGLE, 630, 75, WIDTH_TIMEFRAME, HEIGHT_RECTANGLE, clrGray); //--- Create symbol rectangle create_label(SYMBOL_TEXT, _Symbol, 585, 85, 11, COLOR_WHITE); //--- Create symbol label // Create summary and indicator headers (rectangles and labels) string header_names[] = {"BUY", "SELL", "RSI", "STOCH", "CCI", "ADX", "AO"}; //--- Define header titles for(int header_index = 0; header_index < ArraySize(header_names); header_index++) { //--- Loop through headers int x_offset = (630 - WIDTH_TIMEFRAME) - (header_index < 2 ? header_index * WIDTH_SIGNAL : 2 * WIDTH_SIGNAL + (header_index - 2) * WIDTH_INDICATOR) + (1 + header_index); //--- Calculate x position int width = (header_index < 2 ? WIDTH_SIGNAL : WIDTH_INDICATOR); //--- Set width based on header type create_rectangle(HEADER_RECTANGLE + IntegerToString(header_index), x_offset, 75, width, HEIGHT_RECTANGLE, clrGray); //--- Create header rectangle create_label(HEADER_TEXT + IntegerToString(header_index), header_names[header_index], x_offset - width/2, 85, 11, COLOR_WHITE); //--- Create header label } // Create timeframe rectangles and labels, and summary/indicator cells for(int timeframe_index = 0; timeframe_index < ArraySize(timeframes_array); timeframe_index++) { //--- Loop through timeframes // Highlight current timeframe color timeframe_background = (timeframes_array[timeframe_index] == _Period) ? clrLimeGreen : clrGray; //--- Set background color for current timeframe color timeframe_text_color = (timeframes_array[timeframe_index] == _Period) ? COLOR_BLACK : COLOR_WHITE; //--- Set text color for current timeframe create_rectangle(TIMEFRAME_RECTANGLE + IntegerToString(timeframe_index), 630, (75 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (1 + timeframe_index), WIDTH_TIMEFRAME, HEIGHT_RECTANGLE, timeframe_background); //--- Create timeframe rectangle create_label(TIMEFRAME_TEXT + IntegerToString(timeframe_index), truncate_timeframe_name(timeframe_index), 585, (85 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (1 + timeframe_index), 11, timeframe_text_color); //--- Create timeframe label // Create summary and indicator cells for(int header_index = 0; header_index < ArraySize(header_names); header_index++) { //--- Loop through headers for cells string cell_rectangle_name, cell_text_name; //--- Declare cell name and label variables color cell_background = (header_index < 2) ? COLOR_LIGHT_GRAY : COLOR_BLACK; //--- Set cell background color switch(header_index) { //--- Select cell type case 0: cell_rectangle_name = BUY_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = BUY_TEXT + IntegerToString(timeframe_index); break; //--- Buy cell case 1: cell_rectangle_name = SELL_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = SELL_TEXT + IntegerToString(timeframe_index); break; //--- Sell cell case 2: cell_rectangle_name = RSI_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = RSI_TEXT + IntegerToString(timeframe_index); break; //--- RSI cell case 3: cell_rectangle_name = STOCH_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = STOCH_TEXT + IntegerToString(timeframe_index); break; //--- Stochastic cell case 4: cell_rectangle_name = CCI_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = CCI_TEXT + IntegerToString(timeframe_index); break; //--- CCI cell case 5: cell_rectangle_name = ADX_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = ADX_TEXT + IntegerToString(timeframe_index); break; //--- ADX cell case 6: cell_rectangle_name = AO_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = AO_TEXT + IntegerToString(timeframe_index); break; //--- AO cell } int x_offset = (630 - WIDTH_TIMEFRAME) - (header_index < 2 ? header_index * WIDTH_SIGNAL : 2 * WIDTH_SIGNAL + (header_index - 2) * WIDTH_INDICATOR) + (1 + header_index); //--- Calculate x position int width = (header_index < 2 ? WIDTH_SIGNAL : WIDTH_INDICATOR); //--- Set width based on cell type create_rectangle(cell_rectangle_name, x_offset, (75 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (1 + timeframe_index), width, HEIGHT_RECTANGLE, cell_background); //--- Create cell rectangle create_label(cell_text_name, "-/-", x_offset - width/2, (85 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (1 + timeframe_index), 10, COLOR_WHITE); //--- Create cell label } } // Initialize indicator arrays ArraySetAsSeries(rsi_values, true); //--- Set RSI array as timeseries ArraySetAsSeries(stochastic_values, true); //--- Set Stochastic array as timeseries ArraySetAsSeries(cci_values, true); //--- Set CCI array as timeseries ArraySetAsSeries(adx_values, true); //--- Set ADX array as timeseries ArraySetAsSeries(ao_values, true); //--- Set AO array as timeseries return(INIT_SUCCEEDED); //--- Return initialization success }
En el controlador de eventos OnInit, inicializamos la interfaz de usuario del panel de control del escáner de múltiples marcos temporales. Utilizamos la función «create_rectangle» para dibujar el «MAIN_PANEL» en la posición (632, 40) con un tamaño de 617 x 374 píxeles en «C’30,30,30’» y el «HEADER_PANEL» en la misma posición con una altura de 27 píxeles en «C’60,60,60’». Utilizamos la función «create_label» para añadir el «HEADER_PANEL_ICON» con un carácter Wingdings en la posición (620, 54). Utilizamos los caracteres predeterminados de MQL5 y empleamos la función CharToString para convertir el código del carácter en una cadena. Este es el código de carácter que hemos utilizado, el 91, pero puedes usar el que prefieras.

A continuación, creamos el «HEADER_PANEL_TEXT» con «TimeframeScanner» en (527, 52) y el «CLOSE_BUTTON» en (32, 54), pero esta vez usamos una fuente distinta y asignamos la letra «r» a la cadena. Aquí tienes una muestra de los diferentes símbolos tipográficos que puedes utilizar.

Configuramos la visualización del símbolo utilizando la función «create_rectangle» para el tipo «SYMBOL_RECTANGLE» en la posición (630, 75), con un tamaño de «WIDTH_TIMEFRAME» por «HEIGHT_RECTANGLE», en color gris. Utilizamos la función «create_label» para colocar el «SYMBOL_TEXT» en la posición (585, 85) junto al símbolo actual. Para los encabezados, definimos la matriz «header_names» con títulos como «BUY» y «RSI», y utilizamos un bucle para crear «HEADER_RECTANGLE» en y=75 con desplazamientos en el eje x basados en «WIDTH_SIGNAL» y «WIDTH_INDICATOR», y etiquetas «HEADER_TEXT» en y=85 utilizando la función «create_label».
Creamos la tabla de marcos temporales recorriendo el array «timeframes_array». Utilizamos la función «create_rectangle» para «TIMEFRAME_RECTANGLE» en x=630, con desplazamientos en y a partir de (75 + «HEIGHT_RECTANGLE») ajustados en -(1 + «timeframe_index»), y coloreado con «timeframe_background». Utilizamos la función «create_label» para «TIMEFRAME_TEXT» con los nombres generados por la función «truncate_timeframe_name». En las celdas, utilizamos un bucle para crear «BUY_RECTANGLE», «RSI_RECTANGLE», etc., con la función «create_rectangle», utilizando «cell_background», y añadimos etiquetas «-/-» con la función «create_label». Inicializamos matrices de indicadores como «rsi_values» mediante la función ArraySetAsSeries, configurándolas como series temporales para el tratamiento de datos. Devolvemos INIT_SUCCEEDED para confirmar que la inicialización se ha realizado correctamente, estableciendo el diseño y la estructura de datos del panel de control. Tras la compilación, obtenemos el siguiente resultado.

En la imagen podemos ver que el panel de control ya está listo. Ahora solo nos queda añadir los valores de los indicadores y utilizarlos para el análisis. Ya tenemos una función para el análisis, solo necesitamos obtener los datos. Para lograrlo fácilmente, creamos una función que se encarga de toda la lógica de actualización dinámica.
//+------------------------------------------------------------------+ //| Update indicator values | //+------------------------------------------------------------------+ void updateIndicators() //--- Update dashboard indicators { for(int timeframe_index = 0; timeframe_index < ArraySize(timeframes_array); timeframe_index++) { //--- Loop through timeframes // Initialize indicator handles int rsi_indicator_handle = iRSI(_Symbol, timeframes_array[timeframe_index], 14, PRICE_CLOSE); //--- Create RSI handle int stochastic_indicator_handle = iStochastic(_Symbol, timeframes_array[timeframe_index], 14, 3, 3, MODE_SMA, STO_LOWHIGH); //--- Create Stochastic handle int cci_indicator_handle = iCCI(_Symbol, timeframes_array[timeframe_index], 20, PRICE_TYPICAL); //--- Create CCI handle int adx_indicator_handle = iADX(_Symbol, timeframes_array[timeframe_index], 14); //--- Create ADX handle int ao_indicator_handle = iAO(_Symbol, timeframes_array[timeframe_index]); //--- Create AO handle // Check for valid handles if(rsi_indicator_handle == INVALID_HANDLE || stochastic_indicator_handle == INVALID_HANDLE || cci_indicator_handle == INVALID_HANDLE || adx_indicator_handle == INVALID_HANDLE || ao_indicator_handle == INVALID_HANDLE) { //--- Check if any handle is invalid Print("Failed to create indicator handle for timeframe ", truncate_timeframe_name(timeframe_index)); //--- Log failure continue; //--- Skip to next timeframe } // Copy indicator values if(CopyBuffer(rsi_indicator_handle, 0, 0, 1, rsi_values) <= 0 || //--- Copy RSI value CopyBuffer(stochastic_indicator_handle, 1, 0, 1, stochastic_values) <= 0 || //--- Copy Stochastic signal line value CopyBuffer(cci_indicator_handle, 0, 0, 1, cci_values) <= 0 || //--- Copy CCI value CopyBuffer(adx_indicator_handle, 0, 0, 1, adx_values) <= 0 || //--- Copy ADX value CopyBuffer(ao_indicator_handle, 0, 0, 1, ao_values) <= 0) { //--- Copy AO value Print("Failed to copy buffer for timeframe ", truncate_timeframe_name(timeframe_index)); //--- Log copy failure continue; //--- Skip to next timeframe } // Update RSI color rsi_text_color = (rsi_values[0] < 30) ? clrBlue : (rsi_values[0] > 70) ? clrRed : COLOR_WHITE; //--- Set RSI text color update_label(RSI_TEXT + IntegerToString(timeframe_index), DoubleToString(rsi_values[0], 2), rsi_text_color); //--- Update RSI label // Update Stochastic (Signal Line only) color stochastic_text_color = (stochastic_values[0] < 20) ? clrBlue : (stochastic_values[0] > 80) ? clrRed : COLOR_WHITE; //--- Set Stochastic text color update_label(STOCH_TEXT + IntegerToString(timeframe_index), DoubleToString(stochastic_values[0], 2), stochastic_text_color); //--- Update Stochastic label // Update CCI color cci_text_color = (cci_values[0] < -100) ? clrBlue : (cci_values[0] > 100) ? clrRed : COLOR_WHITE; //--- Set CCI text color update_label(CCI_TEXT + IntegerToString(timeframe_index), DoubleToString(cci_values[0], 2), cci_text_color); //--- Update CCI label // Update ADX color adx_text_color = (adx_values[0] > 25) ? clrBlue : COLOR_WHITE; //--- Set ADX text color update_label(ADX_TEXT + IntegerToString(timeframe_index), DoubleToString(adx_values[0], 2), adx_text_color); //--- Update ADX label // Update AO color ao_text_color = (ao_values[0] > 0) ? clrGreen : (ao_values[0] < 0) ? clrRed : COLOR_WHITE; //--- Set AO text color update_label(AO_TEXT + IntegerToString(timeframe_index), DoubleToString(ao_values[0], 2), ao_text_color); //--- Update AO label // Update Buy/Sell signals string buy_signal = calculate_signal_strength(rsi_values[0], stochastic_values[0], cci_values[0], adx_values[0], ao_values[0], true); //--- Calculate buy signal string sell_signal = calculate_signal_strength(rsi_values[0], stochastic_values[0], cci_values[0], adx_values[0], ao_values[0], false); //--- Calculate sell signal color buy_text_color = (buy_signal == "Strong Buy") ? COLOR_WHITE : COLOR_WHITE; //--- Set buy text color color buy_background = (buy_signal == "Strong Buy") ? clrGreen : (buy_signal == "Buy") ? clrSeaGreen : COLOR_DARK_GRAY; //--- Set buy background color update_rectangle(BUY_RECTANGLE + IntegerToString(timeframe_index), buy_background); //--- Update buy rectangle update_label(BUY_TEXT + IntegerToString(timeframe_index), buy_signal, buy_text_color); //--- Update buy label color sell_text_color = (sell_signal == "Strong Sell") ? COLOR_WHITE : COLOR_WHITE; //--- Set sell text color color sell_background = (sell_signal == "Strong Sell") ? clrRed : (sell_signal == "Sell") ? clrSalmon : COLOR_DARK_GRAY; //--- Set sell background color update_rectangle(SELL_RECTANGLE + IntegerToString(timeframe_index), sell_background); //--- Update sell rectangle update_label(SELL_TEXT + IntegerToString(timeframe_index), sell_signal, sell_text_color); //--- Update sell label // Release indicator handles IndicatorRelease(rsi_indicator_handle); //--- Release RSI handle IndicatorRelease(stochastic_indicator_handle); //--- Release Stochastic handle IndicatorRelease(cci_indicator_handle); //--- Release CCI handle IndicatorRelease(adx_indicator_handle); //--- Release ADX handle IndicatorRelease(ao_indicator_handle); //--- Release AO handle } }
Para gestionar fácilmente la actualización de los valores del panel de control, implementamos la función «updateIndicators» para actualizar los valores de los indicadores y las señales. Recorremos la matriz «timeframes_array» utilizando el índice «timeframe_index» y procesamos cada intervalo de tiempo. Utilizamos las funciones iRSI, «iStochastic», «iCCI», iADX y «iAO» para crear identificadores de indicadores como «rsi_indicator_handle» para el símbolo y el intervalo de tiempo actuales, configurando parámetros como un RSI de 14 períodos y un CCI de 20 períodos. Todos los ajustes de los indicadores se pueden personalizar para adaptarlos a tus necesidades, así que no te limites a los valores predeterminados.
A continuación, comprobamos si algún identificador, como «rsi_indicator_handle», es igual a INVALID_HANDLE, lo que indicaría un error en la creación. En ese caso, utilizamos la función «Print» para registrar el error junto con el resultado de la función «truncate_timeframe_name» y pasamos al siguiente intervalo de tiempo. Utilizamos la función CopyBuffer para obtener los valores más recientes y almacenarlos en matrices como «rsi_values»; si alguno falla, registramos el error y continuamos. Actualizamos las etiquetas de los indicadores mediante la función «update_label». Por ejemplo, establecemos «rsi_text_color» en función de «rsi_values[0]» (azul si es <30, rojo si es >70, y «COLOR_WHITE» en los demás casos) y actualizamos «RSI_TEXT» con el valor formateado de la función «DoubleToString». Repetimos este proceso para «stochastic_values», «cci_values», «adx_values» y «ao_values», aplicando la lógica de colores (por ejemplo, verde para los «ao_values» positivos).
Calculamos las señales utilizando la función «calculate_signal_strength», a la que pasamos «rsi_values[0]» y otros parámetros, para obtener «buy_signal» y «sell_signal». Establecemos «buy_background» (por ejemplo, en verde para «Strong Buy») y utilizamos la función «update_rectangle» para «BUY_RECTANGLE», actualizando «BUY_TEXT» con «update_label». Hacemos lo mismo con «sell_background» y «SELL_TEXT». Por último, utilizamos la función IndicatorRelease para liberar referencias como «rsi_indicator_handle», lo que garantiza una gestión eficiente de los recursos. Las funciones auxiliares que hemos utilizado se definen a continuación.
//+------------------------------------------------------------------+ //| Update rectangle background color | //+------------------------------------------------------------------+ bool update_rectangle(string object_name, color background_color)//--- Function to update rectangle color { int found = ObjectFind(0, object_name); //--- Find rectangle object if(found < 0) { //--- Check if object not found ResetLastError(); //--- Reset error code Print("UNABLE TO FIND THE RECTANGLE: ", object_name, ". ERR Code: ", GetLastError()); //--- Log error return(false); //--- Return failure } ObjectSetInteger(0, object_name, OBJPROP_BGCOLOR, background_color); //--- Set background color ChartRedraw(0); //--- Redraw chart return(true); //--- Return success } //+------------------------------------------------------------------+ //| Update label text and color | //+------------------------------------------------------------------+ bool update_label(string object_name, string text, color text_color) //--- Function to update label { int found = ObjectFind(0, object_name); //--- Find label object if(found < 0) { //--- Check if object not found ResetLastError(); //--- Reset error code Print("UNABLE TO FIND THE LABEL: ", object_name, ". ERR Code: ", GetLastError()); //--- Log error return(false); //--- Return failure } ObjectSetString(0, object_name, OBJPROP_TEXT, text); //--- Set label text ObjectSetInteger(0, object_name, OBJPROP_COLOR, text_color); //--- Set text color ChartRedraw(0); //--- Redraw chart return(true); //--- Return success }
Definimos la función «update_rectangle», que toma como parámetros «object_name» y «background_color» para modificar el aspecto de un rectángulo. Utilizamos la función ObjectFind para localizar el rectángulo y guardamos el resultado en «found». Si «found» es menor que 0, lo que indica que el objeto no se ha encontrado, utilizamos la función ResetLastError, registramos el error con la función «Print» y «GetLastError», y devolvemos «false». Actualizamos el fondo del rectángulo utilizando la función «ObjectSetInteger» para establecer «OBJPROP_BGCOLOR» en «background_color». Utilizamos la función ChartRedraw para actualizar el gráfico y devolvemos «true» si la operación se ha realizado correctamente. Para las actualizaciones de texto, definimos la función «update_label» con los parámetros «object_name», «text» y «text_color».
Utilizamos la función «ObjectFind» para comprobar si la etiqueta existe y, si el valor de «found» es negativo, utilizamos la función «ResetLastError», registramos el error con la función «Print» y devolvemos «false». Utilizamos la función ObjectSetString para establecer «OBJPROP_TEXT» en «text» y la función ObjectSetInteger para establecer «OBJPROP_COLOR» en «text_color». Utilizamos la función «ChartRedraw» para actualizar el gráfico y devolvemos «true», lo que permite actualizar las etiquetas de forma dinámica. Ahora podemos llamar a la función de actualización en cada tick para actualizar el panel de control.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() //--- Handle tick events { if (panel_is_visible) { //--- Check if panel is visible updateIndicators(); //--- Update indicators } }
Aquí, en el controlador de eventos OnTick, simplemente llamamos a la función «updateIndicators» si el panel está visible para aplicar las actualizaciones. Por último, debemos eliminar los objetos que hemos creado para que desaparezcan del gráfico cuando ya no los necesitemos.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) //--- Deinitialize EA { ObjectDelete(0, MAIN_PANEL); //--- Delete main panel ObjectDelete(0, HEADER_PANEL); //--- Delete header panel ObjectDelete(0, HEADER_PANEL_ICON); //--- Delete header icon ObjectDelete(0, HEADER_PANEL_TEXT); //--- Delete header title ObjectDelete(0, CLOSE_BUTTON); //--- Delete close button ObjectsDeleteAll(0, SYMBOL_RECTANGLE); //--- Delete all symbol rectangles ObjectsDeleteAll(0, SYMBOL_TEXT); //--- Delete all symbol labels ObjectsDeleteAll(0, TIMEFRAME_RECTANGLE); //--- Delete all timeframe rectangles ObjectsDeleteAll(0, TIMEFRAME_TEXT); //--- Delete all timeframe labels ObjectsDeleteAll(0, HEADER_RECTANGLE); //--- Delete all header rectangles ObjectsDeleteAll(0, HEADER_TEXT); //--- Delete all header labels ObjectsDeleteAll(0, RSI_RECTANGLE); //--- Delete all RSI rectangles ObjectsDeleteAll(0, RSI_TEXT); //--- Delete all RSI labels ObjectsDeleteAll(0, STOCH_RECTANGLE); //--- Delete all Stochastic rectangles ObjectsDeleteAll(0, STOCH_TEXT); //--- Delete all Stochastic labels ObjectsDeleteAll(0, CCI_RECTANGLE); //--- Delete all CCI rectangles ObjectsDeleteAll(0, CCI_TEXT); //--- Delete all CCI labels ObjectsDeleteAll(0, ADX_RECTANGLE); //--- Delete all ADX rectangles ObjectsDeleteAll(0, ADX_TEXT); //--- Delete all ADX labels ObjectsDeleteAll(0, AO_RECTANGLE); //--- Delete all AO rectangles ObjectsDeleteAll(0, AO_TEXT); //--- Delete all AO labels ObjectsDeleteAll(0, BUY_RECTANGLE); //--- Delete all buy rectangles ObjectsDeleteAll(0, BUY_TEXT); //--- Delete all buy labels ObjectsDeleteAll(0, SELL_RECTANGLE); //--- Delete all sell rectangles ObjectsDeleteAll(0, SELL_TEXT); //--- Delete all sell labels ChartRedraw(0); //--- Redraw chart }
Por último, implementamos el proceso de limpieza mediante la función OnDeinit, que se ejecuta cuando se elimina el asesor experto. Utilizamos la función ObjectDelete para eliminar elementos individuales de la interfaz de usuario, empezando por el rectángulo «MAIN_PANEL», seguido de «HEADER_PANEL», «HEADER_PANEL_ICON», «HEADER_PANEL_TEXT» y «CLOSE_BUTTON», asegurándonos de que los componentes del panel principal y del encabezado se eliminen del gráfico.
Eliminamos sistemáticamente todos los objetos del panel de control utilizando la función ObjectsDeleteAll para cada tipo de elemento. Eliminamos todos los rectángulos y etiquetas asociados a «SYMBOL_RECTANGLE» y «SYMBOL_TEXT», «TIMEFRAME_RECTANGLE» y «TIMEFRAME_TEXT», así como «HEADER_RECTANGLE» y «HEADER_TEXT», con lo que se borran las visualizaciones de símbolos, marcos temporales y encabezados. También eliminamos los objetos relacionados con los indicadores, entre los que se incluyen «RSI_RECTANGLE», «STOCH_RECTANGLE», «CCI_RECTANGLE», «ADX_RECTANGLE» y «AO_RECTANGLE», junto con sus respectivas etiquetas de texto, como «RSI_TEXT».
Completamos la limpieza utilizando la función «ObjectsDeleteAll» para eliminar todos los objetos «BUY_RECTANGLE» y «SELL_RECTANGLE», junto con sus etiquetas «BUY_TEXT» y «SELL_TEXT», eliminando así todos los elementos relacionados con las señales. Por último, utilizamos la función ChartRedraw para actualizar el gráfico, garantizando así un estado visual limpio tras la desinicialización. Por último, debemos ocuparnos del botón de cierre para que, al hacer clic en él, se cierre el panel de control y se desactiven las actualizaciones posteriores.
//+------------------------------------------------------------------+ //| Expert chart event handler | //+------------------------------------------------------------------+ void OnChartEvent(const int event_id, //--- Event ID const long& long_param, //--- Long parameter const double& double_param, //--- Double parameter const string& string_param) //--- String parameter { if (event_id == CHARTEVENT_OBJECT_CLICK) { //--- Check for object click event if (string_param == CLOSE_BUTTON) { //--- Check if close button clicked Print("Closing the panel now"); //--- Log panel closure PlaySound("alert.wav"); //--- Play alert sound panel_is_visible = false; //--- Hide panel ObjectDelete(0, MAIN_PANEL); //--- Delete main panel ObjectDelete(0, HEADER_PANEL); //--- Delete header panel ObjectDelete(0, HEADER_PANEL_ICON); //--- Delete header icon ObjectDelete(0, HEADER_PANEL_TEXT); //--- Delete header title ObjectDelete(0, CLOSE_BUTTON); //--- Delete close button ObjectsDeleteAll(0, SYMBOL_RECTANGLE); //--- Delete all symbol rectangles ObjectsDeleteAll(0, SYMBOL_TEXT); //--- Delete all symbol labels ObjectsDeleteAll(0, TIMEFRAME_RECTANGLE); //--- Delete all timeframe rectangles ObjectsDeleteAll(0, TIMEFRAME_TEXT); //--- Delete all timeframe labels ObjectsDeleteAll(0, HEADER_RECTANGLE); //--- Delete all header rectangles ObjectsDeleteAll(0, HEADER_TEXT); //--- Delete all header labels ObjectsDeleteAll(0, RSI_RECTANGLE); //--- Delete all RSI rectangles ObjectsDeleteAll(0, RSI_TEXT); //--- Delete all RSI labels ObjectsDeleteAll(0, STOCH_RECTANGLE); //--- Delete all Stochastic rectangles ObjectsDeleteAll(0, STOCH_TEXT); //--- Delete all Stochastic labels ObjectsDeleteAll(0, CCI_RECTANGLE); //--- Delete all CCI rectangles ObjectsDeleteAll(0, CCI_TEXT); //--- Delete all CCI labels ObjectsDeleteAll(0, ADX_RECTANGLE); //--- Delete all ADX rectangles ObjectsDeleteAll(0, ADX_TEXT); //--- Delete all ADX labels ObjectsDeleteAll(0, AO_RECTANGLE); //--- Delete all AO rectangles ObjectsDeleteAll(0, AO_TEXT); //--- Delete all AO labels ObjectsDeleteAll(0, BUY_RECTANGLE); //--- Delete all buy rectangles ObjectsDeleteAll(0, BUY_TEXT); //--- Delete all buy labels ObjectsDeleteAll(0, SELL_RECTANGLE); //--- Delete all sell rectangles ObjectsDeleteAll(0, SELL_TEXT); //--- Delete all sell labels ChartRedraw(0); //--- Redraw chart } } }
En el controlador de eventos OnChartEvent, detectamos los clics sobre objetos cuando el identificador del evento es CHARTEVENT_OBJECT_CLICK, y el objeto en el que se ha hecho clic es el botón de cierre, y reproducimos un sonido de alerta mediante la función PlaySound para avisar al usuario de que el panel se va a desactivar; a continuación, desactivamos la visibilidad del panel y utilizamos la misma lógica que usamos para borrar el gráfico en OnDeinit para borrar el panel de control. Tras la compilación, obtenemos el siguiente resultado.

En la imagen podemos ver que el panel de control se ha actualizado con los datos de los indicadores y las señales de trading mostradas. Lo que queda ahora es probar el funcionamiento del proyecto, y eso se aborda en la sección siguiente.
Backtesting
Hemos realizado el backtesting, y a continuación se muestra la visualización compilada en un único archivo GIF (Graphics Interchange Format).

Conclusión
En conclusión, hemos desarrollado un panel de control con escáner para múltiples marcos temporales en MQL5, que integra un diseño de cuadrícula estructurado, señales de indicadores en tiempo real y un botón de cierre interactivo para mejorar las decisiones estratégicas de trading. Hemos mostrado el diseño y la implementación de estas funciones, garantizando su eficacia mediante una inicialización sólida y actualizaciones dinámicas adaptadas a nuestras necesidades operativas. Puedes personalizar este panel de control según tus preferencias, lo que mejorará considerablemente tu capacidad para supervisar las señales del mercado y actuar en consecuencia en distintos marcos temporales.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/18319
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.
Utilizando redes neuronales en MetaTrader
Aplicación del modelo de Grey en el análisis técnico de series temporales financieras
Particularidades del trabajo con números del tipo double en MQL4
Detección y clasificación de patrones fractales mediante aprendizaje automático
- 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