English Русский 中文 Deutsch 日本語
preview
Creación de un Panel de administración de operaciones en MQL5 (Parte IX): Organización del código (V): Clase AnalyticsPanel

Creación de un Panel de administración de operaciones en MQL5 (Parte IX): Organización del código (V): Clase AnalyticsPanel

MetaTrader 5Ejemplos |
47 1
Clemence Benjamin
Clemence Benjamin

Contenido


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.

Diseño de panel de análisis

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

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.
Estadísticas de operaciones:
  • m_lblOpenTrades muestra el número de operaciones actualmente abiertas.
La información de PnL (ganancias y pérdidas) se divide en dos categorías:
  1. m_lblPnLName: Una etiqueta estática que simplemente muestra el texto "PnL:" (esta permanece en negro).
  2. 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:
          1. m_lblStochName: La parte estática ("Stoch:").
          2. 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:
    1. m_lblExpectation ofrece un resumen de texto como «Buy», «Sell» o «No Consensus».
    2. 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!

Probando el panel de análisis y el panel de administración de operaciones

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.

AnalyticsPanel

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».

Volver al índice

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

Archivos adjuntos |
AnalyticsPanel.mqh (32.77 KB)
New_Admin_Panel.mq5 (19.61 KB)
amrhamed83
amrhamed83 | 16 abr 2025 en 11:17
¿puede enviar todos los archivos juntos en el archivo adjunto?
Redes neuronales en el trading: Extracción eficiente de características para una clasificación precisa (Mantis) Redes neuronales en el trading: Extracción eficiente de características para una clasificación precisa (Mantis)
Le presentamos a Mantis, un modelo básico ligero para la clasificación de series temporales basado en el Transformer con preentrenamiento contrastivo y atención híbrida que ofrece precisión y escalabilidad récord.
Búsqueda oscilatoria determinista (DOS) — Deterministic Oscillatory Search (DOS) Búsqueda oscilatoria determinista (DOS) — Deterministic Oscillatory Search (DOS)
El algoritmo de búsqueda oscilatoria determinista (DOS) es un método de optimización global innovador que combina las ventajas de los algoritmos de gradiente y enjambre sin usar números aleatorios. El mecanismo de oscilaciones e inclinaciones de aptitud permite a DOS explorar espacios de búsqueda complejos de manera determinista.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 19): ZigZag Analyzer Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 19): ZigZag Analyzer
Todos los traders que se basan en el movimiento de los precios utilizan manualmente las líneas de tendencia para confirmar las tendencias e identificar posibles niveles de cambio de tendencia o de continuación. En esta serie dedicada a la creación de un conjunto de herramientas para el análisis de la acción del precio, presentamos una herramienta diseñada para trazar líneas de tendencia inclinadas que facilitan el análisis del mercado. Esta herramienta simplifica el proceso para los traders al destacar claramente las tendencias y los niveles clave que resultan esenciales para evaluar eficazmente la evolución de los precios.