Creación de un Panel de administración de operaciones en MQL5 (Parte IX): Organización del código (V): Clase AnalyticsPanel
Contenido
- Introducción
- Descripción general de la clase AnalyticsPanel
- Implementación de MQL5
- Pruebas
- Conclusión
Introducción
En secciones anteriores, presenté el concepto de «panel de análisis», que, en ese momento, consistía principalmente en elementos de interfaz estáticos, como un gráfico circular que mostraba el ratio de operaciones ganadoras/perdedoras. Sin embargo, estos componentes carecían de actualizaciones de datos en tiempo real, lo que limitaba su utilidad en entornos de trading dinámicos. En este análisis damos un paso importante, mejorando tanto el diseño como la funcionalidad de la interfaz, centrándonos en cómo obtener y mostrar datos de trading en tiempo real.
Nuestro enfoque de desarrollo se basa en la Biblioteca estándar de MQL5, en concreto en las clases que se encuentran en el directorio \Include\Controls\. Ampliaremos y personalizaremos las clases existentes, como CLabel, CEdit y CDialog, para crear componentes de interfaz de usuario responsivos y basados en datos. Estos elementos constituyen la base de nuestro panel de administración de operaciones en constante evolución, diseñado como una herramienta modular con múltiples interfaces que ofrece una gestión integral de cuentas y estrategias, así como una interfaz de comunicación a través de Telegram para el control remoto y las notificaciones en tiempo real.
Si bien el objetivo principal sigue siendo el desarrollo de este panel avanzado, las técnicas que se comparten aquí, especialmente aquellas que implican la obtención de datos en tiempo real y las actualizaciones dinámicas de la interfaz de usuario, se pueden aplicar a varios proyectos de MQL5 más allá de las interfaces administrativas, incluidos los Asesores Expertos, los indicadores personalizados y las herramientas de aprendizaje interactivas.
Descripción general de la clase AnalyticsPanel
Como parte de un enfoque de desarrollo modular adecuado para programas MQL5 a gran escala, y para promover la reutilización y el mantenimiento del código, estamos creando un archivo de encabezado de clase dedicado para AnalyticsPanel. Esta clase está diseñada para englobar tanto la disposición visual del panel de análisis como la recuperación y visualización en tiempo real de los datos de mercado.
Además de proporcionar métricas de cuenta estándar, el panel mostrará varios valores de indicadores técnicos que se integran en una estrategia personalizada que he denominado Estrategia de Confluencia. Esta estrategia se basa en el principio de confluencia, donde se comparan las señales de varios indicadores para generar una señal de trading basada en consenso. Si no se encuentra un acuerdo entre los indicadores, el panel simplemente muestra el mensaje "Sin consenso", evitando así señales falsas o débiles.
La clase AnalyticsPanel incluirá métodos para inicializar y actualizar el diseño del panel, actualizar los valores de las etiquetas en tiempo real y gestionar la retroalimentación de la señal visual en función de la lógica de la estrategia. A continuación, he incluido un diseño visual del panel y, en la siguiente sección, analizaremos los detalles de implementación que le dieron vida.

Características de AnalyticsPanel
Como parte de nuestro proceso de desarrollo continuo, una vez que hayamos desarrollado nuestra clase, la integraremos en el programa principal del Asesor Experto, llamado New_Admin_Panel. Esta integración refuerza la arquitectura modular de nuestro sistema y pone de relieve las ventajas de crear componentes reutilizables e independientes que puedan interactuar sin problemas dentro de un marco de aplicación más amplio. A continuación se muestra una imagen que destaca el rendimiento final de nuestro producto, donde las señales de trading se generan solo cuando todos los valores de los indicadores coinciden para formar un fuerte consenso para una decisión de compra o venta.

AnalyticsPanel en vivo
Al integrar AnalyticsPanel en New_Admin_Panel, obtenemos varios beneficios prácticos.
- Monitorización centralizada: Ahora se puede acceder a análisis en tiempo real, como ratios de victorias/derrotas, cambios en el capital y resúmenes de operaciones, desde la interfaz principal sin necesidad de herramientas adicionales.
- Experiencia de usuario mejorada: La combinación de análisis con funcionalidades básicas como la ejecución de estrategias y la mensajería de Telegram proporciona un flujo de trabajo más unificado e intuitivo para el usuario.
- Escalabilidad y mantenimiento del código: Separar los componentes de la interfaz en clases como AnalyticsPanel mejora la legibilidad del código, facilita las pruebas y admite futuras actualizaciones con mínimas interrupciones.
- Reutilización entre proyectos: El diseño modular permite que el AnalyticsPanel (y otros paneles) se reutilicen o adapten en otros Asesores Expertos o herramientas de trading.
- Señal de trading: La versión que tenemos aquí proporciona señales de confluencia útiles.
Implementación de MQL5
Al diseñar nuestro panel de análisis, seguimos principios de programación orientada a objetos que separan los componentes esenciales en diferentes niveles de acceso para mejorar la claridad, la seguridad y la facilidad de mantenimiento. La clase está estructurada de manera que ciertas características estén disponibles para otras partes del programa; estas son los elementos "públicos" que sirven como interfaz para interactuar con el panel. Otros detalles internos, que tienen que ver con el funcionamiento interno y la gestión de datos, se mantienen ocultos a los componentes externos; estos son los elementos "privados". Este método de encapsulación nos permite presentar un esquema claro y organizado para nuestro panel, lo que facilita a los futuros desarrolladores la comprensión del rol y el propósito de cada sección.
Antes de definir la clase CAnalyticsPanel, se incluyen dos archivos de encabezado críticos:
Dialog.mqh – Este encabezado proporciona acceso a las clases relacionadas con los diálogos, incluida CAppDialog, que sirve como clase base para crear el panel personalizado.
Label.mqh – Esto incluye la definición de la clase CLabel, que se utiliza para crear y gestionar todas las etiquetas de texto que se muestran en el panel.
Estas inclusiones son necesarias para que el panel personalizado tenga acceso a las estructuras de control de la interfaz de usuario estándar de MQL5. Sin ellos, la clase del panel no podría heredar de CAppDialog ni crear controles de etiquetas.
También necesitamos definir macros para el espaciado y el diseño (AP_GAP, AP_DEFAULT_LABEL_HEIGHT) para mantener su estructura visual a la vez receptiva y limpia.#include <Controls\Dialog.mqh> #include <Controls\Label.mqh> // Use unique macro names for layout in this class: #define AP_DEFAULT_LABEL_HEIGHT 20 #define AP_GAP 10
El siguiente esquema desglosa los componentes principales de nuestra clase en cinco áreas clave:
1. Estructura y propósito de la clase
La clase CAnalyticsPanel es un panel de control muy intuitivo e informativo, diseñado para ofrecer información en tiempo real sobre las operaciones y las condiciones del mercado directamente en un gráfico de MetaTrader 5. Derivado de la clase CAppDialog de la biblioteca estándar de MQL5, aprovecha la programación orientada a objetos para mostrar datos estructurados en secciones ordenadas. El panel incluye etiquetas con información sobre las cuentas, operaciones abiertas, cotizaciones de mercado, indicadores y un resumen de señales sintetizado, lo que permite a los operadores supervisar todo desde una única ventana compacta.
//+------------------------------------------------------------------+ //| Analytics Panel Class | //+------------------------------------------------------------------+ class CAnalyticsPanel : public CAppDialog { protected: // Helper: Create a text label. // The label's text color is set as provided and its background is forced to black. bool CreateLabelEx(CLabel &label, int x, int y, int width, int height, string label_name, string text, color clr) { // Construct a unique control name by combining the dialog name and label_name. string unique_name = m_name + "_" + label_name; if(!label.Create(m_chart_id, unique_name, m_subwin, x, y, x+width, y+height)) { Print("Failed to create label: ", unique_name, " Err=", GetLastError()); return false; } if(!Add(label)) { Print("Failed to add label: ", unique_name, " Err=", GetLastError()); return false; } if(!label.Text(text)) { Print("Failed to set text for label: ", unique_name); return false; } label.Color(clr); // Set text color label.Color(clrBlack); // Force background to black return true; } // Account and Trade Data Labels: CLabel m_lblBalance; CLabel m_lblEquity; CLabel m_lblMargin; CLabel m_lblOpenTrades; // PnL is split into two labels: CLabel m_lblPnLName; CLabel m_lblPnLValue; // Market Data Labels (split Bid/Ask): CLabel m_lblBidName; CLabel m_lblBidValue; CLabel m_lblAskName; CLabel m_lblAskValue; CLabel m_lblSpread; // Indicator Section Separator: CLabel m_lblIndicatorSeparator; // New header for indicator section: CLabel m_lblIndicatorHeader; // Indicator Labels (static name and dynamic value labels): CLabel m_lblRSIName; CLabel m_lblRSIValue; CLabel m_lblStochName; CLabel m_lblStochK; // %K value CLabel m_lblStochD; // %D value CLabel m_lblCCIName; CLabel m_lblCCIValue; CLabel m_lblWilliamsName; CLabel m_lblWilliamsValue; // New Summary Labels (for consensus across indicators): CLabel m_lblSignalHeader; // Header label for summary column ("SIGNAL") CLabel m_lblExpectation; // Text summary (e.g., "Buy" or "Sell") CLabel m_lblConfirmation; // Confirmation symbol (e.g., up or down arrow) // Previous values for dynamic color changes (market data): double m_prevBid; double m_prevAsk; public: CAnalyticsPanel(); virtual ~CAnalyticsPanel(); // Create the panel and initialize UI controls. virtual bool CreatePanel(const long chart, const string name, const int subwin, int x1, int y1, int x2, int y2); // Update the panel with the latest account, trade, market and indicator data. void UpdatePanel(); void Toggle(); private: void CreateControls(); void UpdateAccountInfo(); void UpdateTradeStats(); void UpdateMarketData(); void UpdateIndicators(); };
A continuación, proporciono una descripción detallada y una explicación de la variable.
Variables de miembro
Etiquetas de interfaz de usuario para datos de cuenta y operaciones:
Etiquetas de cuenta:
- m_lblBalance, m_lblEquity y m_lblMargin muestran el balance de la cuenta, la equidad y el margen, respectivamente.
- m_lblOpenTrades muestra el número de operaciones actualmente abiertas.
- m_lblPnLName: Una etiqueta estática que simplemente muestra el texto "PnL:" (esta permanece en negro).
- m_lblPnLValue: Una etiqueta dinámica que muestra el valor numérico de PnL y cambia de color (verde para ganancias, rojo para pérdidas).
Etiquetas de datos de mercado:
Estas etiquetas muestran los valores de compra/venta y la información del spread:
- m_lblBidName y m_lblBidValue para el precio de la oferta.
- m_lblAskName y m_lblAskValue para el precio de venta.
- m_lblSpread muestra el spread actual en pips.
- Los colores de los valores de compra y venta se actualizan dinámicamente para reflejar los cambios del mercado (por ejemplo, azul cuando el valor aumenta y rojo cuando disminuye).
Sección de indicadores:
- Un separador (m_lblIndicatorSeparator) distingue visualmente la sección del indicador del resto del panel.
- Una etiqueta de encabezado (m_lblIndicatorHeader) muestra "INDICATORS", identificando claramente la sección.
Esta sección incluye varios indicadores:
- RSI: Dos etiquetas, m_lblRSIName y m_lblRSIValue, muestran el nombre ("RSI:") y su valor actual.
- Oscilador estocástico: Este indicador se divide en dos partes con:
- m_lblStochName: La parte estática ("Stoch:").
- m_lblStochK y m_lblStochD: Muestran los valores dinámicos para %K y %D.
- CCI: m_lblCCIName y m_lblCCIValue muestran el CCI (Commodity Channel Index).
- Williams %R: m_lblWilliamsName y m_lblWilliamsValue muestran los valores de Williams %R.
Sección de Resumen (Consenso):
- Una etiqueta de encabezado (m_lblSignalHeader) muestra la palabra «SIGNAL» encima de la columna de resumen.
- Se utilizan dos etiquetas, m_lblExpectation y m_lblConfirmation, para mostrar la señal de consenso de todos los indicadores:
- m_lblExpectation ofrece un resumen de texto como «Buy», «Sell» o «No Consensus».
- m_lblConfirmation muestra una confirmación simbólica (una flecha hacia arriba «↑» para comprar, una flecha hacia abajo «↓» para vender o un guion «-» si no se alcanza un consenso).
Estado interno para datos de mercado:
Dos variables, m_prevBid y m_prevAsk, almacenan los valores anteriores de oferta y demanda para ayudar a determinar si estos valores están aumentando o disminuyendo, lo que a su vez influye en la codificación por colores utilizada para estas etiquetas.
2. Método auxiliar para la creación de etiquetas
Una función de utilidad fundamental dentro de la clase es CreateLabelEx, un método reutilizable para crear y personalizar etiquetas de texto en el panel. Este método engloba todos los pasos necesarios para crear una etiqueta, asignarle un nombre único, configurar su tamaño y ubicación, aplicar el texto y los colores deseados y añadirla a la lista de controles del panel. Si algún paso de este proceso falla, se registran los mensajes de error correspondientes para facilitar la depuración.
bool CAnalyticsPanel::CreateLabelEx(CLabel &label, int x, int y, int width, int height, string label_name, string text, color clr) { string unique_name = m_name + "_" + label_name; if(!label.Create(m_chart_id, unique_name, m_subwin, x, y, x+width, y+height)) { Print("Failed to create label: ", unique_name, " Err=", GetLastError()); return false; } if(!Add(label)) { Print("Failed to add label: ", unique_name, " Err=", GetLastError()); return false; } if(!label.Text(text)) { Print("Failed to set text for label: ", unique_name); return false; } label.Color(clr); // Set text color label.BackColor(clrBlack); // Force background to black return true; }
3. Organización del diseño de la interfaz de usuario
El método CreateControls organiza todos los elementos de la interfaz de usuario en el panel. Aquí organizamos las diferentes secciones: información de la cuenta (balance, equidad y margen), estadísticas de operaciones (operaciones abiertas y ganancias y pérdidas), datos de mercado (compra, venta y spread) y la sección de indicadores. En la sección de indicadores, creamos encabezados como «INDICATORS» y «SIGNAL» para identificar claramente los grupos de información. El código utiliza constantes predefinidas para la altura de las etiquetas y los espacios entre ellas, con el fin de garantizar que todos los elementos estén espaciados de manera uniforme y organizados visualmente.
//+------------------------------------------------------------------+ //| CreateControls: Instantiate and add UI controls | //+------------------------------------------------------------------+ void CAnalyticsPanel::CreateControls() { int curX = AP_GAP; int curY = AP_GAP; int labelWidth = 150; // Account Information Labels: CreateLabelEx(m_lblBalance, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Balance", "Balance: $0.00", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; CreateLabelEx(m_lblEquity, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Equity", "Equity: $0.00", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; CreateLabelEx(m_lblMargin, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Margin", "Margin: 0", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Trade Statistics Labels: CreateLabelEx(m_lblOpenTrades, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "OpenTrades", "Open Trades: 0", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // PnL labels: CreateLabelEx(m_lblPnLName, curX, curY, 50, AP_DEFAULT_LABEL_HEIGHT, "PnLName", "PnL:", clrBlack); CreateLabelEx(m_lblPnLValue, curX+50, curY, labelWidth-50, AP_DEFAULT_LABEL_HEIGHT, "PnLValue", "$0.00", clrLime); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Market Data Labels: CreateLabelEx(m_lblBidName, curX, curY, 40, AP_DEFAULT_LABEL_HEIGHT, "BidName", "Bid:", clrDodgerBlue); CreateLabelEx(m_lblBidValue, curX+40, curY, 100, AP_DEFAULT_LABEL_HEIGHT, "BidValue", "0.00000", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; CreateLabelEx(m_lblAskName, curX, curY, 40, AP_DEFAULT_LABEL_HEIGHT, "AskName", "Ask:", clrDodgerBlue); CreateLabelEx(m_lblAskValue, curX+40, curY, 100, AP_DEFAULT_LABEL_HEIGHT, "AskValue", "0.00000", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; CreateLabelEx(m_lblSpread, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Spread", "Spread: 0.0 pips", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Indicator Section Separator: CreateLabelEx(m_lblIndicatorSeparator, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "Separator", "------------------------------------------", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Indicator Section Header: CreateLabelEx(m_lblIndicatorHeader, curX, curY, labelWidth, AP_DEFAULT_LABEL_HEIGHT, "IndicatorHeader", "INDICATORS", clrDodgerBlue); // Summary Column Header for Signal: CreateLabelEx(m_lblSignalHeader, curX+250, curY , 100, AP_DEFAULT_LABEL_HEIGHT, "SignalHeader", "SIGNAL", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Indicator Labels: // RSI: CreateLabelEx(m_lblRSIName, curX, curY, 50, AP_DEFAULT_LABEL_HEIGHT, "RSIName", "RSI:", clrDodgerBlue); CreateLabelEx(m_lblRSIValue, curX+50, curY, 90, AP_DEFAULT_LABEL_HEIGHT, "RSIValue", "N/A", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Stochastic: CreateLabelEx(m_lblStochName, curX, curY, 60, AP_DEFAULT_LABEL_HEIGHT, "StochName", "Stoch:", clrDodgerBlue); CreateLabelEx(m_lblStochK, curX+60, curY, 70, AP_DEFAULT_LABEL_HEIGHT, "StochK", "K: N/A", clrDodgerBlue); CreateLabelEx(m_lblStochD, curX+150, curY, 70, AP_DEFAULT_LABEL_HEIGHT, "StochD", "D: N/A", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // CCI: CreateLabelEx(m_lblCCIName, curX, curY, 50, AP_DEFAULT_LABEL_HEIGHT, "CCIName", "CCI:", clrDodgerBlue); CreateLabelEx(m_lblCCIValue, curX+50, curY, 90, AP_DEFAULT_LABEL_HEIGHT, "CCIValue", "N/A", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Williams %R: CreateLabelEx(m_lblWilliamsName, curX, curY, 70, AP_DEFAULT_LABEL_HEIGHT, "WilliamsName", "Williams:", clrDodgerBlue); CreateLabelEx(m_lblWilliamsValue, curX+70, curY, 70, AP_DEFAULT_LABEL_HEIGHT, "WilliamsValue", "N/A", clrDodgerBlue); curY += AP_DEFAULT_LABEL_HEIGHT + AP_GAP; // Summary Column: Expectation text and Confirmation symbol. CreateLabelEx(m_lblExpectation, curX+250, curY - (AP_DEFAULT_LABEL_HEIGHT+AP_GAP)*4, 100, AP_DEFAULT_LABEL_HEIGHT, "Expectation", "Expect: N/A", clrDodgerBlue); CreateLabelEx(m_lblConfirmation, curX+300, curY - (AP_DEFAULT_LABEL_HEIGHT+AP_GAP)*4, 50, AP_DEFAULT_LABEL_HEIGHT, "Confirmation", "N/A", clrDodgerBlue); ChartRedraw(m_chart_id); }
4. Actualizaciones de datos en tiempo real para información de cuentas y mercados
Mediante las funciones integradas de MQL5, el panel obtiene información de la cuenta, como el balance, la equidad y el margen, así como datos de mercado como los precios bid/ask y el spread. Los métodos UpdateAccountInfo y UpdateTradeStats consultan directamente la cuenta mediante funciones como AccountInfoDouble(). Mientras tanto, UpdateMarketData obtiene datos de cotización en tiempo real mediante SymbolInfoTick(), compara los valores actuales con los anteriores y actualiza la codificación por colores de las etiquetas de compra y venta en función de los cambios. Esto permite a los operadores ver el estado más reciente de sus cuentas y del mercado en tiempo real.
//+------------------------------------------------------------------+ //| UpdateAccountInfo: Refresh account-related data | //+------------------------------------------------------------------+ void CAnalyticsPanel::UpdateAccountInfo() { double balance = AccountInfoDouble(ACCOUNT_BALANCE); double equity = AccountInfoDouble(ACCOUNT_EQUITY); double margin = AccountInfoDouble(ACCOUNT_MARGIN); m_lblBalance.Text("Balance: $" + DoubleToString(balance, 2)); m_lblEquity.Text("Equity: $" + DoubleToString(equity, 2)); // Directly display the raw margin value. m_lblMargin.Text("Margin: " + DoubleToString(margin, 2)); } //+------------------------------------------------------------------+ //| UpdateTradeStats: Refresh trade statistics | //+------------------------------------------------------------------+ void CAnalyticsPanel::UpdateTradeStats() { int total = PositionsTotal(); double pnl = 0; for(int i = 0; i < total; i++) { ulong ticket = PositionGetTicket(i); if(ticket != 0) pnl += PositionGetDouble(POSITION_PROFIT); } // Update PnL labels: m_lblPnLName.Text("PnL:"); // static remains black m_lblPnLValue.Text("$" + DoubleToString(pnl, 2)); if(pnl < 0) m_lblPnLValue.Color(clrRed); else m_lblPnLValue.Color(clrLime); m_lblOpenTrades.Text("Open Trades: " + IntegerToString(total)); } //+------------------------------------------------------------------+ //| UpdateMarketData: Refresh market data display | //+------------------------------------------------------------------+ void CAnalyticsPanel::UpdateMarketData() { MqlTick last_tick; if(SymbolInfoTick(Symbol(), last_tick)) { // Update Bid value and color based on change: color bidColor = clrBlue; if(m_prevBid != 0) bidColor = (last_tick.bid >= m_prevBid) ? clrBlue : clrRed; m_lblBidValue.Color(bidColor); m_lblBidValue.Text(DoubleToString(last_tick.bid, 5)); // Update Ask value and color based on change: color askColor = clrBlue; if(m_prevAsk != 0) askColor = (last_tick.ask >= m_prevAsk) ? clrBlue : clrRed; m_lblAskValue.Color(askColor); m_lblAskValue.Text(DoubleToString(last_tick.ask, 5)); m_prevBid = last_tick.bid; m_prevAsk = last_tick.ask; // Update Spread: double spread_pips = (last_tick.ask - last_tick.bid) * 1e4; m_lblSpread.Text("Spread: " + DoubleToString(spread_pips, 1) + " pips"); } }
5. Cálculo de indicadores técnicos y generación de una señal de consenso
El método UpdateIndicators recupera los valores de varios indicadores técnicos (RSI, estocástico, CCI y Williams %R) llamando a las funciones de los indicadores y leyendo sus datos más recientes. El valor de cada indicador se compara con umbrales predefinidos para determinar si sugiere una condición de compra o de venta. Por ejemplo, un RSI inferior a 30 se interpreta como una señal de compra, mientras que uno superior a 70 sugiere una señal de venta. A continuación, el panel consolida estas señales individuales en un consenso general; si todos los indicadores coinciden, aparece una señal clara de "Compra" o "Venta" en la sección de resumen junto con un símbolo de flecha. Esto proporciona a los operadores una señal visual rápida sobre las condiciones del mercado.
//+------------------------------------------------------------------+ //| UpdateIndicators: Calculate and display various indicators | //+------------------------------------------------------------------+ void CAnalyticsPanel::UpdateIndicators() { // Local suggestion variables (1 = Buy, -1 = Sell, 0 = Neutral) int suggestionRSI = 0, suggestionStoch = 0, suggestionCCI = 0, suggestionWilliams = 0; // --- RSI --- int handleRSI = iRSI(Symbol(), PERIOD_CURRENT, 14, PRICE_CLOSE); double rsi[1]; color rsiValueColor = clrGreen; // default if(CopyBuffer(handleRSI, 0, 0, 1, rsi) > 0) { if(rsi[0] < 30) { rsiValueColor = clrBlue; // buy condition suggestionRSI = 1; } else if(rsi[0] > 70) { rsiValueColor = clrRed; // sell condition suggestionRSI = -1; } m_lblRSIValue.Color(rsiValueColor); m_lblRSIValue.Text(DoubleToString(rsi[0], 2)); } IndicatorRelease(handleRSI); // --- Stochastic --- int handleStoch = iStochastic(Symbol(), PERIOD_CURRENT, 5, 3, 3, MODE_SMA, STO_LOWHIGH); double stochK[1], stochD[1]; if(CopyBuffer(handleStoch, 0, 0, 1, stochK) > 0 && CopyBuffer(handleStoch, 1, 0, 1, stochD) > 0) { // Initialize color variables. color colorK = clrGreen; color colorD = clrGreen; if(stochK[0] > stochD[0]) { colorK = clrBlue; colorD = clrRed; suggestionStoch = 1; } else if(stochK[0] < stochD[0]) { colorK = clrRed; colorD = clrBlue; suggestionStoch = -1; } else suggestionStoch = 0; m_lblStochK.Color(colorK); m_lblStochD.Color(colorD); m_lblStochK.Text("K: " + DoubleToString(stochK[0], 4)); m_lblStochD.Text("D: " + DoubleToString(stochD[0], 4)); } IndicatorRelease(handleStoch); // --- CCI --- int handleCCI = iCCI(Symbol(), PERIOD_CURRENT, 14, PRICE_TYPICAL); double cci[1]; color cciValueColor = clrGreen; if(CopyBuffer(handleCCI, 0, 0, 1, cci) > 0) { if(cci[0] < -100) { cciValueColor = clrBlue; // buy condition suggestionCCI = 1; } else if(cci[0] > 100) { cciValueColor = clrRed; // sell condition suggestionCCI = -1; } m_lblCCIValue.Color(cciValueColor); m_lblCCIValue.Text(DoubleToString(cci[0], 2)); } IndicatorRelease(handleCCI); // --- Williams %R --- int handleWPR = iWPR(Symbol(), PERIOD_CURRENT, 14); double williams[1]; color williamsValueColor = clrGreen; if(CopyBuffer(handleWPR, 0, 0, 1, williams) > 0) { if(williams[0] > -80) { williamsValueColor = clrBlue; // buy condition suggestionWilliams = 1; } else if(williams[0] < -20) { williamsValueColor = clrRed; // sell condition suggestionWilliams = -1; } m_lblWilliamsValue.Color(williamsValueColor); m_lblWilliamsValue.Text(DoubleToString(williams[0], 2)); } IndicatorRelease(handleWPR); // --- Consensus Summary --- int consensus = 0; if(suggestionRSI != 0 && suggestionRSI == suggestionStoch && suggestionRSI == suggestionCCI && suggestionRSI == suggestionWilliams) consensus = suggestionRSI; if(consensus == 1) { m_lblExpectation.Text("Buy"); m_lblExpectation.Color(clrBlue); m_lblConfirmation.Text("↑"); // Up arrow for Buy m_lblConfirmation.Color(clrBlue); } else if(consensus == -1) { m_lblExpectation.Text("Sell"); m_lblExpectation.Color(clrRed); m_lblConfirmation.Text("↓"); // Down arrow for Sell m_lblConfirmation.Color(clrRed); } else { m_lblExpectation.Text("No Consensus"); m_lblExpectation.Color(clrOrangeRed); m_lblConfirmation.Text("-"); m_lblConfirmation.Color(clrOrangeRed); } }
Integración del panel CAnalytics en el panel de administración principal
1. En la parte superior del archivo del EA se incluye el archivo de encabezado AnalyticsPanel.mqh para PROPORCIONAR acceso a la clase CAnalyticsPanel:
#include <AnalyticsPanel.mqh> Esto garantiza que el compilador reconozca la clase y sus miembros durante la compilación.
2. Declarar un puntero global
Se declara un puntero global de tipo CAnalyticsPanel, que se utilizará para gestionar dinámicamente la instancia del panel:
CAnalyticsPanel *g_analyticsPanel = NULL; Este puntero permite comprobar si el panel existe y gestionar su creación, destrucción y visibilidad de forma dinámica.
3. Crea un botón para abrir el panel
En la función CreateAdminPanel() se crea un botón con la etiqueta "Analytics Panel" y se agrega al panel de administración principal. Este botón sirve como disparador para que el usuario abra el panel.
bool CreateAdminPanel() { // Analytics Panel Button if(!CreateButton(g_analyticsButton, ANALYTICS_BTN_NAME, INDENT_LEFT, y, INDENT_LEFT+btnWidth, y+BUTTON_HEIGHT, "Analytics Panel")) return false; return true; }
4. Gestionar eventos de clic en botones
La función HandleAnalytics() se encarga de gestionar el ciclo de vida del panel. Comprueba si el panel ya se ha creado. Si no es así, crea una instancia del CAnalyticsPanel, llama a su método CreatePanel() con las coordenadas de posicionamiento y activa o desactiva su visibilidad. De lo contrario, simplemente activa o desactiva el panel.
//------------------------------------------------------------------ // Handle Analytics Panel button click void HandleAnalytics() { if(g_analyticsPanel == NULL) { g_analyticsPanel = new CAnalyticsPanel(); // Coordinates for Analytics panel (adjust as needed) if(!g_analyticsPanel.CreatePanel(g_chart_id, "AnalyticsPanel", g_subwin, 900, 20, 900+500, 20+460)) { delete g_analyticsPanel; g_analyticsPanel = NULL; Print("Failed to create Analytics Panel"); return; } } g_analyticsPanel.Toggle(); ChartRedraw(); }
5. Reenvío de eventos
En la función AdminPanelOnEvent(), los eventos como clics o interacciones del usuario se reenvían a g_analyticsPanel si está visible. Esto garantiza que el panel pueda responder de forma independiente a las acciones del usuario sin entrar en conflicto con el panel principal ni con otros subpaneles.
if(g_analyticsPanel != NULL && g_analyticsPanel.IsVisible()) return g_analyticsPanel.OnEvent(id, lparam, dparam, sparam);
6. Actualización dinámica del panel
Dentro de la función OnTick(), si existe el panel de análisis, se invoca periódicamente su método UpdatePanel(). Esto permite que el panel actualice dinámicamente los datos analíticos o el contenido de la interfaz de usuario que muestra durante la ejecución del EA.
void OnTick() { if(g_analyticsPanel != NULL) { g_analyticsPanel.UpdatePanel(); } }
7. Limpieza
En la función OnDeinit() el panel se destruye correctamente y la memoria se libera llamando a Destroy() y eliminando el puntero. Esto es fundamental para evitar fugas de memoria cuando se elimina o se vuelve a compilar el EA.
Pruebas
Tuve una experiencia increíble durante las pruebas. Era casi como estar allí en directo, viendo cómo se actualizaban los valores en el panel con cada tick. Logré realizar una operación utilizando la estrategia de confluencia, pero desafortunadamente, cuando comencé a grabar, la señal ya había cambiado a "Sin consenso". Aun así, estoy encantado de compartir los resultados. ¡Vean la imagen a continuación!

Demostración del funcionamiento del panel de análisis
En la imagen superior, el valor de las operaciones abiertas se actualizaba con cada cierre de posición. Empezamos con 21 posiciones y terminamos con 18.

Todos los paneles son accesibles desde el panel principal.
Conclusión
Hemos ampliado con éxito nuestro programa mediante la integración del Panel de Análisis. Esta incorporación demuestra que se pueden integrar muchos más subpaneles utilizando el mismo enfoque modular aplicado a los demás paneles. Esta discusión marca la conclusión de la Parte (IX) de la serie. Sin embargo, esto no significa que el trabajo esté terminado; aún quedan varias áreas por perfeccionar. Dicho esto, el concepto central ya está bien establecido y se presenta con claridad.
Tanto los principiantes como los desarrolladores experimentados pueden beneficiarse de esta serie en diferentes niveles. Hay muchas notas interesantes que se pueden sacar de esta experiencia. Estas ideas sientan una base sólida para desarrollos aún más avanzados en el futuro.
En los resultados presentados, había dibujado líneas de canal en el gráfico y, casualmente, el panel de análisis generó una señal de compra. Esta señal fue el resultado de la confluencia de todos los indicadores mencionados, alineándose perfectamente con la zona de soporte del canal. Realicé la operación con confianza basándome en esta estrategia de confluencia. La prueba se realizó en una cuenta demo; como siempre, recomiendo encarecidamente a todos los operadores que realicen pruebas exhaustivas en una cuenta demo antes de arriesgar dinero real.
La ventaja de este sistema es que los operadores pueden acceder instantáneamente a información crucial del mercado y de la cuenta a través del panel analítico, al tiempo que se benefician del poder de análisis de la señal basada en la confluencia. Un aspecto que se puede mejorar es la falta de alertas para la estrategia; añadir notificaciones haría que el sistema fuera aún más fácil de usar y garantizaría que no se pierda ninguna oportunidad.
Tanto el archivo de cabecera como el programa principal se adjuntan a continuación. No olvides compartir tus opiniones y reflexiones en la sección de comentarios. ¡Hasta la próxima publicación! ¡Que disfruten del desarrollo y de sus operaciones comerciales!
| Nombre del archivo | Especificación |
|---|---|
| AnalyticsPanel.mqh | Un archivo de encabezado que define la estructura y el comportamiento del Panel de Análisis, el cual agrupa varios indicadores para generar señales de trading basadas en la confluencia. |
| New_Admin_Panel.mqh | El programa EA principal de MQL5 que inicializa y gestiona toda la interfaz del panel de administración, incluyendo la navegación, la autenticación y la integración de diversos subpaneles, como «Comunicaciones», «Análisis» y «Gestión de operaciones». |
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/17397
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.
Redes neuronales en el trading: Extracción eficiente de características para una clasificación precisa (Mantis)
Búsqueda oscilatoria determinista (DOS) — Deterministic Oscillatory Search (DOS)
Particularidades del trabajo con números del tipo double en MQL4
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 19): ZigZag Analyzer
- 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