English Русский 中文 Español Deutsch 日本語
preview
Criando um Painel de Administração de Trading em MQL5 (Parte IX): Organização do Código (V): Classe AnalyticsPanel

Criando um Painel de Administração de Trading em MQL5 (Parte IX): Organização do Código (V): Classe AnalyticsPanel

MetaTrader 5Exemplos |
39 1
Clemence Benjamin
Clemence Benjamin

Conteúdo


Introdução

Nas seções anteriores, apresentei o conceito de um Painel de Análises que, naquela etapa, consistia principalmente em elementos estáticos da interface, como um gráfico de setores exibindo a proporção entre ganhos e perdas. No entanto, esses componentes não possuíam atualizações de dados em tempo real, limitando sua utilidade em ambientes de negociação dinâmicos. Nesta discussão, damos um passo significativo à frente ao aprimorar tanto o design quanto a funcionalidade da interface, com foco em como recuperar e exibir dados de negociação em tempo real.

Nossa abordagem de desenvolvimento aproveita a Biblioteca Padrão do MQL5, particularmente as classes encontradas no diretório \Include\Controls. Estenderemos e personalizaremos classes existentes, como CLabel, CEdit e CDialog, para criar componentes de interface responsivos e orientados por dados. Esses componentes formam a base do nosso Painel de Administração de Trading em constante evolução, projetado como uma ferramenta modular composta por várias interfaces, oferecendo gerenciamento abrangente de conta e estratégia, bem como uma interface de comunicação via Telegram para controle remoto e notificações em tempo real.

Embora o objetivo principal continue sendo o desenvolvimento desse painel avançado, as técnicas compartilhadas aqui, especialmente aquelas relacionadas à aquisição de dados em tempo real e às atualizações dinâmicas da interface, podem ser aplicadas a diversos projetos em MQL5 além de interfaces administrativas, incluindo Expert Advisors, indicadores personalizados e ferramentas interativas de aprendizado.


Visão geral da classe AnalyticsPanel

Como parte de uma abordagem de desenvolvimento modular adequada para programas MQL5 de grande escala e para promover a reutilização e a manutenção do código, estamos criando um arquivo de cabeçalho dedicado para a classe AnalyticsPanel. Essa classe foi projetada para encapsular tanto o layout visual do painel de análises quanto a recuperação e a exibição, em tempo real, de dados de mercado.

Além de fornecer métricas padrão da conta, o painel exibirá diversos valores de indicadores técnicos que alimentam uma estratégia personalizada que denominei Estratégia de Confluência. Essa estratégia baseia-se no princípio da confluência, no qual os sinais de vários indicadores são comparados para gerar um sinal de negociação unificado. Se não houver concordância entre os indicadores, o painel simplesmente exibirá uma mensagem de "Sem Consenso", evitando assim sinais falsos ou fracos.

A classe AnalyticsPanel incluirá métodos para inicializar e atualizar o layout do painel, atualizar os valores dos rótulos em tempo real e gerenciar o feedback visual dos sinais com base na lógica da estratégia. Abaixo, incluí um layout visual do design do painel e, na discussão seguinte, examinaremos os detalhes da implementaçãoque deram vida a ele.

AnalyticsPanelDesign

Recursos do AnalyticsPanel

Como parte da nossa jornada contínua de desenvolvimento, após concluirmos o desenvolvimento da nossa classe, nós a integraremos ao programa principal do Expert Advisor, o New_Admin_Panel. Essa integração reforça a arquitetura modular do nosso sistema e destaca as vantagens de construir componentes reutilizáveis e independentes que podem interagir perfeitamente dentro de uma estrutura de aplicação maior. Abaixo está uma imagem destacando o resultado final do nosso produto, no qual os sinais de negociação são gerados apenas quando todos os valores dos indicadores se alinham para formar um forte consenso de compra ou de venda.

AnalyticsPanel em Tempo Real

AnalyticsPanel em Tempo Real

Ao integrar o AnalyticsPanel ao New_Admin_Panel, passamos a contar com diversos benefícios práticos.

  • Monitoramento Centralizado: análises em tempo real, como proporções de ganhos/perdas, alterações no patrimônio líquido e resumos das negociações, agora podem ser acessadas pela interface principal, sem a necessidade de ferramentas separadas.
  • Experiência do Usuário Aprimorada: combinar análises com funcionalidades principais, como execução de estratégias e mensagens via Telegram, proporciona um fluxo de trabalho mais unificado e intuitivo para o usuário
  • Escalabilidade e Manutenção do Código: separar os componentes da interface em classes, como AnalyticsPanel, melhora a legibilidade do código, facilita os testes e oferece suporte a futuras atualizações com o mínimo de interrupções.
  • Reutilização entre Projetos: o design modular permite que o AnalyticsPanel (e outros painéis) seja reutilizado ou adaptado em outros Expert Advisors ou utilitários de negociação.
  • Sinal de Negociação: a versão que temos aqui fornece sinais de confluência úteis.



Implementação em MQL5

Ao projetar nosso Painel de Análises, seguimos os princípios da programação orientada a objetos, que separam os componentes essenciais em diferentes níveis de acesso para melhorar a clareza, a segurança e a facilidade de manutenção. A classe é estruturada de forma que determinados recursos estejam disponíveis para outras partes do programa. São os elementos públicos ("public"), que servem como interface para interação com o painel. Outros detalhes internos, que tratam do funcionamento interno e do gerenciamento de dados, permanecem ocultos dos componentes externos. São os elementos privados ("private"). Esse método de encapsulamento nos permite apresentar uma estrutura clara e organizada para o nosso painel, facilitando para futuros desenvolvedores compreenderem o papel e a finalidade de cada seção. 

Antes de definir a classe CAnalyticsPanel, dois arquivos de cabeçalho essenciais são incluídos:

Dialog.mqh – Este arquivo de cabeçalho fornece acesso às classes relacionadas a diálogos, incluindo CAppDialog, que serve como classe base para a criação do painel personalizado.

Label.mqh – Este arquivo inclui a definição da classe CLabel, utilizada para criar e gerenciar todos os rótulos de texto exibidos no painel.

Essas inclusões são necessárias para dar ao painel personalizado acesso aos controles padrão de interface do MQL5. Sem elas, a classe do painel não seria capaz de herdar de CAppDialog nem de criar controles de rótulo.

Também precisamos definir macros para espaçamento e layout (AP_GAP, AP_DEFAULT_LABEL_HEIGHT) para manter a estrutura visual limpa, consistente e bem espaçada ou bem organizada.
#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

A estrutura a seguir divide os principais componentes da nossa classe em cinco áreas principais:

1. Estrutura e Finalidade da Classe

A classe CAnalyticsPanel atua como um painel altamente visual e informativo, projetado para atualizações em tempo real sobre negociações e condições de mercado diretamente em um gráfico do MetaTrader 5. Derivada da classe CAppDialog da Biblioteca Padrão do MQL5, ela utiliza programação orientada a objetos para exibir dados estruturados em seções organizadas. O painel inclui rótulos para detalhes da conta, negociações abertas, cotações de mercado, indicadores e um resumo consolidado dos sinais, permitindo que os traders monitorem tudo em uma única janela 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();
};

Forneci abaixo uma descrição detalhada e uma explicação para as variáveis.

Variáveis-Membro

Rótulos da Interface para Dados da Conta e das Negociações:

Rótulos da Conta:

  • m_lblBalance, m_lblEquity e m_lblMargin exibem, respectivamente, o saldo da conta, o patrimônio líquido e a margem.
Estatísticas das Negociações:
  • m_lblOpenTrades exibe o número de negociações atualmente abertas.
As informações de PnL (Lucro e Prejuízo) são divididas em dois rótulos:
  1. m_lblPnLName: Um rótulo estático que simplesmente exibe o texto "PnL:" (permanece na cor preta).
  2. m_lblPnLValue: Um rótulo dinâmico que exibe o valor numérico do PnL e altera sua cor (verde para lucro e vermelho para prejuízo).

Rótulos de Dados de Mercado:

Esses rótulos exibem os valores de bid/ask e as informações sobre o spread:

  • m_lblBidName e m_lblBidValue para o preço de compra (bid).
  • m_lblAskName e m_lblAskValue para o preço de venda (ask).
  • m_lblSpread exibe o spread atual em pips.
  • As cores dos valores de bid e ask são atualizadas dinamicamente para refletir as mudanças do mercado (por exemplo, azul quando o valor aumenta e vermelho quando diminui).

Seção de Indicadores:

  • Um separador (m_lblIndicatorSeparator) distingue visualmente a seção de indicadores do restante do painel.
  • Um rótulo de cabeçalho (m_lblIndicatorHeader) exibe "INDICATORS", identificando claramente a seção.

A seção inclui diversos indicadores:

  • RSI: Dois rótulos, m_lblRSIName e m_lblRSIValue, exibem o nome ("RSI:") e seu valor atual.
  • Oscilador Estocástico: Este indicador é dividido em duas partes:
          1. m_lblStochName: A parte estática ("Stoch:").
          2. m_lblStochK e m_lblStochD: Exibem os valores dinâmicos de %K e %D.
  • CCI: m_lblCCIName e m_lblCCIValue exibem o Índice de Canal de Commodities (Commodity Channel Index).
  • Williams %R: m_lblWilliamsName e m_lblWilliamsValue exibem os valores do Williams %R.

Seção de Resumo (Consenso):

  • Um rótulo de cabeçalho (m_lblSignalHeader) exibe a palavra "SIGNAL" acima da coluna de resumo.
  • Dois rótulos, m_lblExpectation e m_lblConfirmation, são utilizados para exibir o sinal de consenso de todos os indicadores:
    1. m_lblExpectation fornece um resumo em texto, como "Compra", "Venda" ou "Sem Consenso".
    2. m_lblConfirmation exibe uma confirmação simbólica (uma seta para cima "↑" para compra, uma seta para baixo "↓" para VENDA ou um hífen "-" caso nenhum consenso seja alcançado).

Estado Interno para Dados de Mercado:

Duas variáveis, m_prevBid e m_prevAsk, armazenam os valores anteriores de bid e ask para ajudar a determinar se esses valores estão aumentando ou diminuindo, orientando assim a codificação por cores utilizada nesses rótulos.

2. Método Auxiliar para Criação de Rótulos

Uma função utilitária central dentro da classe é CreateLabelEx, um método reutilizável para criar e personalizar rótulos de texto no painel. Esse método encapsula todas as etapas necessárias para instanciar um rótulo, atribuir-lhe um nome exclusivo, configurar seu tamanho e posição, aplicar o texto e as cores desejados e adicioná-lo à lista de controles do painel. Se qualquer etapa desse processo falhar, mensagens de erro apropriadas serão registradas para fins de depuração.

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. Organizando o Layout da Interface do Usuário

O método CreateControls organiza todos os elementos da interface do usuário no painel. Aqui organizamos diferentes seções: informações da conta (saldo, patrimônio líquido e margem), estatísticas das negociações (negociações abertas e lucro e prejuízo), dados de mercado (bid, ask e spread) e a seção de indicadores. Na seção de indicadores, criamos cabeçalhos como "INDICATORS" e "SIGNAL" para identificar claramente os grupos de informações. O código utiliza constantes predefinidas para a altura dos rótulos e os espaçamentos, garantindo que todos os elementos fiquem uniformemente distribuídos e visualmente organizados.

//+------------------------------------------------------------------+
//| 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. Atualizações de Dados em Tempo Real para Informações da Conta e do Mercado

Utilizando as funções integradas do MQL5, o painel recupera informações da conta, como saldo, patrimônio líquido e valor bruto da margem, bem como dados de mercado, como os preços de bid e ask e o spread. Os métodos UpdateAccountInfo e UpdateTradeStats consultam diretamente a conta utilizando funções como AccountInfoDouble(). Enquanto isso, UpdateMarketData obtém dados de ticks em tempo real usando SymbolInfoTick(), compara os valores atuais com os anteriores e atualiza a codificação por cores dos rótulos de bid e ask com base nas alterações. Isso permite que os traders visualizem o estado mais recente da conta e do mercado à medida que as mudanças ocorrem.

//+------------------------------------------------------------------+
//| 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. Calculando Indicadores Técnicos e Gerando um Sinal de Consenso

O método UpdateIndicators recupera os valores de diversos indicadores técnicos, como RSI, Estocástico, CCI e Williams %R, chamando as funções dos indicadores e lendo seus dados mais recentes. O valor de cada indicador é comparado com limites predefinidos para determinar se ele sugere uma condição de compra ou de venda. Por exemplo, um RSI abaixo de 30 é interpretado como um sinal de compra, enquanto um valor acima de 70 sugere venda. Em seguida, o painel consolida esses sinais individuais em um consenso geral. Se todos os indicadores concordarem, um sinal claro de "Compra" ou "Venda" será exibido na seção de resumo juntamente com um símbolo de seta. Isso fornece aos traders uma indicação visual rápida das condições do 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);
   }
}

Integrando a CAnalyticsPanel ao Painel Principal de Administração

1. Na parte superior do arquivo do EA, o arquivo de cabeçalho AnalyticsPanel.mqh é incluído para FORNECER acesso à classe CAnalyticsPanel:

#include <AnalyticsPanel.mqh>

Isso garante que o compilador reconheça a classe e seus membros durante a compilação.

2. Declarando um Ponteiro Global

É declarado um ponteiro global do tipo CAnalyticsPanel, que será utilizado para gerenciar dinamicamente a instância do painel:

CAnalyticsPanel *g_analyticsPanel = NULL;

Esse ponteiro ajuda a controlar se o painel existe e permite sua criação, destruição e alternância de visibilidade de forma dinâmica.

3. Criando um Botão para Abrir o Painel

Na função CreateAdminPanel(), é criado um botão com o rótulo "Analytics Panel", que é adicionado ao painel principal de administração. Esse botão serve como acionador para que o usuário abra o painel.

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. Tratando os Eventos de Clique do Botão

A função HandleAnalytics() é responsável por gerenciar o ciclo de vida do painel. Ela verifica se o painel já foi criado. Caso contrário, instancia a CAnalyticsPanel, chama seu método CreatePanel() com as coordenadas de posicionamento e alterna sua visibilidade. Caso contrário, apenas alterna o painel entre visível e oculto.

//------------------------------------------------------------------
// 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. Encaminhamento de Eventos

Na função AdminPanelOnEvent(), eventos como cliques ou interações do usuário são encaminhados para o g_analyticsPanel, caso ele esteja visível. Isso garante que o painel possa responder de forma independente às ações do usuário, sem entrar em conflito com o painel principal ou com outros subpainéis.

if(g_analyticsPanel != NULL && g_analyticsPanel.IsVisible())
      return g_analyticsPanel.OnEvent(id, lparam, dparam, sparam);

6. Atualizando o Painel Dinamicamente

Dentro da função OnTick(), se o painel de análises existir, seu método UpdatePanel() será chamado regularmente. Isso permite que o painel atualize dinamicamente as análises exibidas ou o conteúdo da interface durante a execução do EA.

void OnTick()
  {

    if(g_analyticsPanel != NULL)  
    {
      g_analyticsPanel.UpdatePanel();
      
    }
          
  }

7. Limpeza

Na função OnDeinit(), o painel é corretamente destruído e a memória é liberada chamando Destroy() e excluindo o ponteiro. Isso é essencial para evitar vazamentos de memória quando o EA for removido ou recompilado.


Testes

Tive uma experiência de testes incrível. Parecia quase ao vivo, observando os valores sendo atualizados no painel a cada tick. Consegui realizar uma negociação utilizando a estratégia de confluência, mas, infelizmente, quando comecei a gravar, o sinal já havia mudado para "Sem Consenso". Mesmo assim, estou muito feliz em compartilhar os resultados. Veja a imagem abaixo!

Testando o Painel de Análises e o New_Admin_Panel de negociação

Demonstração do funcionamento do AnalyticsPanel

Na imagem acima, o valor de Open Trades foi atualizado a cada encerramento de posição. Começamos com 21 posições e concluímos com 18.

AnalyticsPanel

Todos os painéis são acessíveis a partir do painel principal



Conclusão

Expandimos com sucesso o nosso programa integrando o Analytics Panel. Essa adição demonstra que muitos outros subpainéis podem ser incorporados utilizando a mesma abordagem modular aplicada aos demais painéis. Esta discussão marca a conclusão da Parte (IX) da série. No entanto, isso não significa que o trabalho esteja concluído. Ainda existem diversas áreas que podem ser aprimoradas. Dito isso, o conceito principal agora está bem estabelecido e claramente apresentado.

Tanto iniciantes quanto desenvolvedores experientes podem se beneficiar desta série em diferentes níveis. Há muitas observações valiosas que podem ser aproveitadas. Essas ideias estabelecem uma base sólida para desenvolvimentos ainda mais avançados no futuro.

Nos resultados apresentados, eu havia desenhado linhas de canal no gráfico e, coincidentemente, um sinal de compra foi gerado pelo Analytics Panel. Esse sinal foi resultado da confluência de todos os indicadores listados, alinhando-se perfeitamente à zona de suporte do canal. Realizei a negociação com confiança com base nessa estratégia de confluência. O teste foi realizado em uma conta demo. Como sempre, recomendo fortemente que todos os traders testem exaustivamente em uma conta demo antes de arriscar dinheiro real.

O ponto forte dessa configuração é que os traders podem obter acesso imediato a informações essenciais do mercado e da conta por meio do painel de análises, ao mesmo tempo em que se beneficiam do poder analítico do sinal baseado em confluência. Uma área que ainda pode ser aprimorada é a ausência de alertas para a estratégia. A adição de notificações tornaria o sistema ainda mais amigável ao usuário e garantiria que nenhuma oportunidade fosse perdida.

Tanto o arquivo de cabeçalho quanto o programa principal estão anexados abaixo. Não deixe de compartilhar sua opinião na seção de comentários. Até a próxima publicação. Bom desenvolvimento e ótimas negociações!

Nome do Arquivo Especificação
AnalyticsPanel.mqh Um arquivo de cabeçalho que define a estrutura e o comportamento do Painel de Análises, que agrega múltiplos indicadores para gerar sinais de negociação baseados em confluência.
New_Admin_Panel.mqh O programa principal do EA em MQL5 que inicializa e gerencia toda a interface do painel de administração, incluindo navegação, autenticação e integração de diversos subpainéis, como Comunicações, Análises e Gerenciamento de Negociações.

Voltar ao Conteúdo

Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/17397

Arquivos anexados |
AnalyticsPanel.mqh (32.77 KB)
New_Admin_Panel.mq5 (19.61 KB)
Últimos Comentários | Ir para discussão (1)
amrhamed83
amrhamed83 | 16 abr. 2025 em 11:17
Você poderia, por favor, enviar todos os arquivos juntos no anexo?
Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5
Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.
Redes neurais no trading: uma visão unificada sobre espaço e tempo (Global-Local Attention) Redes neurais no trading: uma visão unificada sobre espaço e tempo (Global-Local Attention)
Continuamos a trabalhar na implementação das abordagens propostas pelos autores do framework Extralonger. Desta vez, vamos nos concentrar na implementação do módulo Global-Local Spatial Attention em MQL5, examinando tanto sua estrutura quanto sua integração prática ao fluxo computacional geral.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Algoritmo de otimização do dingo - Dingo Optimization Algorithm (DOA) Algoritmo de otimização do dingo - Dingo Optimization Algorithm (DOA)
O artigo apresenta um novo método metaheurístico baseado nas estratégias de caça dos dingos australianos: ataque em grupo, perseguição e busca de carniça. Vamos ver como o algoritmo de otimização do dingo (DOA) se sai em termos algorítmicos.