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

Criando um Painel Administrador de Trading em MQL5 (Parte IX): Organização de Código (II): Modularização

MetaTrader 5Exemplos |
19 0
Clemence Benjamin
Clemence Benjamin

Conteúdo:



Introdução

Esta discussão marca um avanço na criação de um Expert Advisor de Painel Admin com manutenção facilitada. A organização de código introduzida no artigo anterior aprimorou significativamente nosso código principal, e hoje damos mais um passo modularizando componentes-chave em arquivos externos. Essa abordagem garante que futuras atualizações se concentrem na melhoria de componentes individuais sem interromper outras partes do código.

Um exemplo claro do benefício disso ocorreu quando precisei refinar o Painel de Comunicação. Percorrer uma grande base de código monolítica para localizar a seção relevante foi algo esmagador. Ao dividir o código em módulos estruturados, simplificamos a navegação, tornando o desenvolvimento e a manutenção muito mais eficientes.

Nossa inspiração vem de projetos bem estruturados que exemplificam boas práticas de organização de código. Hoje, implementaremos a modularização introduzindo classes personalizadas para funcionalidades essenciais que definem nosso programa. Abaixo, apresentamos a lista completa de módulos potenciais que planejamos desenvolver.

Arquivo do Módulo Descrição
AdminHomeDialog.mqh Este declara a seção central do Painel Administrador de Trading, fornecendo acesso a outras utilidades dentro do programa.
Aunthentication.mqh Este módulo gerencia a autenticação do usuário, incluindo verificação de senha e autenticação de dois fatores.
ThemeManager.mqh Responsável por gerenciar a aparência e o estilo do seu painel administrador.
Telegram.mqh Contém funções e classes para interação com o Telegram, geralmente para envio de mensagens e notificações.
CommunicationsDialog.mqh Será responsável por lidar com a interface do usuário (UI) e as interações relacionadas aos recursos de comunicação dentro do seu Painel Admin. 
AnalyticsDialog.mqh Para exibir e gerenciar dados analíticos, como estatísticas de trades, métricas de desempenho ou gráficos visuais dentro de um painel de diálogo.
 TradeManagementDialog.mqh Este lidará com a criação da UI de tarefas relacionadas a trades, onde os usuários podem executar e gerenciar operações de forma eficiente.

Após a criação bem-sucedida desses arquivos, eles podem ser incluídos no código principal.

#include <Telegram.mqh>
#include <Authentication.mqh>
#include <AdminHomeDialog.mqh>
#include  <AnalyticsDialog.mqh>
#include <TradeManagementDialog.mqh>
#include <CommunicationDialog.mqh>

Todas as declarações dos componentes do painel serão colocadas nos arquivos include, enquanto o código principal conterá principalmente definições. Como as definições geralmente são menores que as declarações, essa abordagem mantém o programa principal limpo e organizado, melhorando a legibilidade e a manutenção.

Tenho certeza de que você já consegue visualizar como nosso projeto está evoluindo com essas inovações. Na próxima seção, forneceremos uma explicação detalhada da modularização, seguida de sua implementação neste projeto.

Declarações e Implementação

Relação entre o código principal e o arquivo de cabeçalho



Visão Geral da Discussão

Com uma breve visão geral apresentada na introdução acima, agora exploraremos a modularização com mais profundidade antes de mergulhar no desenvolvimento e na implementação dos componentes do nosso código. Cada módulo será explicado detalhadamente, com a análise da funcionalidade de cada linha de código.

Por fim, integraremos todos os módulos em uma nova base de código principal para o Painel Administrador de Trading, essencialmente reconstruindo-o do zero com estrutura e eficiência aprimoradas.

Ao final desta discussão, teremos desenvolvido, integrado e testado os seguintes arquivos:

  • AdminHomeDialog.mqh
  • Authentication.mqh
  • Telegram.mqh

Modularization

Em programação MQL5, modularização refere-se à prática de dividir um programa em partes menores, independentes e reutilizáveis, principalmente por meio do uso de classes, funções e arquivos include. Essa abordagem permite que desenvolvedores encapsulem funcionalidades específicas em módulos ou classes, como a criação de componentes de interface ou lógica de trading, que podem ser incluídos ou instanciados conforme necessário em diferentes partes de uma aplicação ou até mesmo em múltiplas aplicações. Ao fazer isso, o código se torna mais gerenciável, mais fácil de manter e menos propenso a erros, já que mudanças em um módulo não impactam necessariamente outros, promovendo reutilização de código, melhorando a legibilidade e facilitando o desenvolvimento colaborativo no ambiente MetaTrader 5.

Nesse contexto, já delineamos os subcomponentes do nosso novo programa na introdução acima. Além disso, existem outros recursos disponíveis para leitura adicional sobre esse tema, e encontrei diversas abordagens para aplicar modularização em diferentes artigos.

Nos próximos passos, vou guiá-lo pelo desenvolvimento detalhado de cada módulo, garantindo uma compreensão clara de sua implementação e integração.


Implementação do Código

Agora é o momento de aplicar nosso conhecimento em MQL5 para desenvolver os principais componentes do nosso EA Painel Administrador de Trading. A ótima notícia é que esses arquivos foram projetados para serem facilmente adaptados e integrados aos seus próprios projetos.

Estrutura Principal de um Arquivo de Cabeçalho em MQL5:

Um arquivo de cabeçalho em MQL5, normalmente com a extensão .mqh, serve como local para definir classes, constantes, enumerações e protótipos de funções que podem ser incluídos em outros scripts MQL5 ou expert advisors. Aqui está a estrutura típica baseada em outros arquivos de cabeçalho nativos:

  1. Metadados do Arquivo: Inclui informações de copyright, links e controle de versão.
  2. Instruções Include: Lista outros arquivos de cabeçalho ou bibliotecas dos quais o cabeçalho atual depende.
  3. Defines/Constantes: Define macros ou constantes usadas dentro da classe ou por outras partes do código para valores consistentes.
  4. Declaração de Classe: Declara a classe com sua herança (se houver), membros privados, métodos públicos e métodos protegidos.
  5. Mapeamento de Eventos: Usa macros para definir como eventos são mapeados para funções-membro em programação orientada a eventos.
  6. Implementações de Métodos: Embora não seja estritamente necessário para todos os arquivos de cabeçalho, neste caso as implementações de métodos são incluídas, o que é comum para classes menores onde o encapsulamento não é crítico. Por razões de desempenho em MQL5, incluir a implementação pode reduzir a sobrecarga de chamadas de função.
  7. Construtores e Destrutores: Fazem parte da definição da classe, especificando como objetos da classe são criados e destruídos.

Com base no esboço acima, aqui está um modelo de código de exemplo:

// Meta Data here on top.
#include   // Include other necessary libraries or headers
#include 

//+------------------------------------------------------------------+
//| Defines                                                          |
//+------------------------------------------------------------------+
#define CONSTANT_NAME1    (value1)  // Constants or macro definitions
#define CONSTANT_NAME2    (value2)

//+------------------------------------------------------------------+
//| Class CClass                                                     |
//| Usage: Description of class purpose                              |
//+------------------------------------------------------------------+
class CClass : public CParentClass  // Inherits from another class if needed
  {
private:
   // Private member variables
   CSomeControl   m_control;  // Example control member

public:
   CClass(void);              // Constructor
   ~CClass(void);             // Destructor
   virtual bool   Create(/* parameters */);  // Virtual method for polymorphism
   virtual bool   OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

protected:
   // Protected methods or members
   bool           CreateSomeControl(void);
   void           SomeEventHandler(void);

  };
//+------------------------------------------------------------------+
//| Event Handling                                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CClass)
   ON_EVENT(SOME_EVENT,m_control,SomeEventHandler)
EVENT_MAP_END(CParentClass)

// Constructor implementation
CClass::CClass(void)
  {
  }

// Destructor implementation
CClass::~CClass(void)
  {
  }

// Method implementations if included in the header
bool CClass::Create(/* parameters */)
  {
   // Implementation of create method
  }

// Event handler examples
void CClass::SomeEventHandler(void)
  {
   // Handle the event
  }

//+------------------------------------------------------------------+

Para criar arquivos de cabeçalho no MetaEditor, abra um novo arquivo pressionando Ctrl + N ou navegando manualmente pelo menu. Na janela pop-up, selecione Include (*.mqh) e prossiga para iniciar a edição. Por padrão, o modelo gerado inclui alguns comentários como notas de orientação.

Consulte a imagem abaixo:

criar um arquivo include

Criar um novo arquivo de cabeçalho no MetaEditor

Aqui está o modelo padrão de arquivo de cabeçalho, que inclui algumas notas comentadas para orientação.

//+------------------------------------------------------------------+
//|                                                     Telegram.mqh |
//|                                Copyright 2024, Clemence Benjamin |
//|             https://www.mql5.com/en/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
// #define MacrosHello   "Hello, world!"
// #define MacrosYear    2010
//+------------------------------------------------------------------+
//| DLL imports                                                      |
//+------------------------------------------------------------------+
// #import "user32.dll"
//   int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
// #import "my_expert.dll"
//   int      ExpertRecalculate(int wParam,int lParam);
// #import
//+------------------------------------------------------------------+
//| EX5 imports                                                      |
//+------------------------------------------------------------------+
// #import "stdlib.ex5"
//   string ErrorDescription(int error_code);
// #import
//+------------------------------------------------------------------+

Arquivo de Cabeçalho Admin Home

Nesta seção, desenvolvemos a classe CAdminHomeDialog, que serve como a interface principal do painel administrador em nosso programa MQL5. Ela integra arquivos de cabeçalho essenciais para controles de diálogo e botão, ao mesmo tempo em que utiliza constantes predefinidas para manter dimensões e espaçamentos do painel consistentes.

//+------------------------------------------------------------------+
//| AdminHomeDialog.mqh                                              |
//+------------------------------------------------------------------+
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>

//+------------------------------------------------------------------+
//| Defines                                                          |
//+------------------------------------------------------------------+
#define ADMIN_PANEL_WIDTH   (335)
#define ADMIN_PANEL_HEIGHT  (350)
#define INDENT_LEFT         (11)
#define INDENT_TOP          (11)
#define INDENT_RIGHT        (11)
#define INDENT_BOTTOM       (11)
#define CONTROLS_GAP_X      (5)
#define CONTROLS_GAP_Y      (5)
#define BUTTON_WIDTH        (250)
#define BUTTON_HEIGHT       (40)

A classe CAdminHomeDialog estende CAppDialog e inclui quatro botões principais, m_tradeMgmtButton, m_commButton, m_analyticsButton e m_showAllButton — que fornecem navegação fluida para diferentes seções do painel administrador. A estrutura da classe permanece enxuta, com construtor e destrutor mínimos, enquanto o método Create garante que todos os botões sejam inicializados corretamente para uma experiência de usuário suave.

//+------------------------------------------------------------------+
//| CAdminHomeDialog class                                           |
//+------------------------------------------------------------------+
class CAdminHomeDialog : public CAppDialog
{
private:
    CButton m_tradeMgmtButton;
    CButton m_commButton;
    CButton m_analyticsButton;
    CButton m_showAllButton;

public:
    CAdminHomeDialog(void) {}
    ~CAdminHomeDialog(void) {}

    virtual bool Create(const long chart, const string name, const int subwin,
                       const int x1, const int y1, const int x2, const int y2);
    virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam);
};

//+------------------------------------------------------------------+
//| Create                                                           |
//+------------------------------------------------------------------+
bool CAdminHomeDialog::Create(const long chart, const string name, const int subwin,
                             const int x1, const int y1, const int x2, const int y2)
{
    if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2))
        return false;

    if(!CreateTradeMgmtButton()) return false;
    if(!CreateCommButton()) return false;
    if(!CreateAnalyticsButton()) return false;
    if(!CreateShowAllButton()) return false;

    return true;
}

As interações do usuário são tratadas dentro do método OnEvent, onde cliques nos botões acionam mensagens de depuração e chamam seus respectivos manipuladores de evento: OnClickTradeManagement, OnClickCommunications, OnClickAnalytics e OnClickShowAll. Esses manipuladores atualmente registram as interações, mas serão expandidos conforme aprimorarmos a funcionalidade.

//+------------------------------------------------------------------+
//| Event Handling (Enhanced Debugging)                              |
//+------------------------------------------------------------------+
bool CAdminHomeDialog::OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    if(id == CHARTEVENT_OBJECT_CLICK)
    {
        Print("Clicked object: ", sparam); // Debug which object was clicked
        
        if(sparam == m_tradeMgmtButton.Name())
        {
            Print("Trade Management button detected");
            OnClickTradeManagement();
            return true;
        }
        else if(sparam == m_commButton.Name())
        {
            Print("Communications button detected");
            OnClickCommunications();
            return true;
        }
        else if(sparam == m_analyticsButton.Name())
        {
            Print("Analytics button detected");
            OnClickAnalytics();
            return true;
        }
        else if(sparam == m_showAllButton.Name())
        {
            Print("Show All button detected");
            OnClickShowAll();
            return true;
        }
    }
    return CAppDialog::OnEvent(id, lparam, dparam, sparam);
}

//+------------------------------------------------------------------+
//| Button Click Handlers                                            |
//+------------------------------------------------------------------+
void CAdminHomeDialog::OnClickTradeManagement() { Print("Trade Management Panel clicked"); }
void CAdminHomeDialog::OnClickCommunications()  { Print("Communications Panel clicked"); }
void CAdminHomeDialog::OnClickAnalytics()       { Print("Analytics Panel clicked"); }
void CAdminHomeDialog::OnClickShowAll()         { Print("Show All clicked"); }

Os métodos de criação de botões — CreateTradeMgmtButton, CreateCommButton, CreateAnalyticsButton e CreateShowAllButton — geram dinamicamente botões com identificadores únicos, posicionamento preciso e rótulos bem definidos. O botão “Show All” inclusive incorpora um emoji para aprimorar a interface do usuário. À medida que continuarmos o desenvolvimento, melhorias e refinamentos adicionais serão introduzidos para otimizar desempenho e usabilidade.

//+------------------------------------------------------------------+
//| Control Creation Methods                                         |
//+------------------------------------------------------------------+
bool CAdminHomeDialog::CreateTradeMgmtButton()
{
    int x = INDENT_LEFT;
    int y = INDENT_TOP;
    return m_tradeMgmtButton.Create(m_chart_id, m_name+"_TradeBtn", m_subwin,
                                  x, y, x+BUTTON_WIDTH, y+BUTTON_HEIGHT)
        && m_tradeMgmtButton.Text("Trade Management Panel")
        && Add(m_tradeMgmtButton);
}

bool CAdminHomeDialog::CreateCommButton()
{
    int x = INDENT_LEFT;
    int y = INDENT_TOP + BUTTON_HEIGHT + CONTROLS_GAP_Y;
    return m_commButton.Create(m_chart_id, m_name+"_CommBtn", m_subwin,
                             x, y, x+BUTTON_WIDTH, y+BUTTON_HEIGHT)
        && m_commButton.Text("Communications Panel")
        && Add(m_commButton);
}

bool CAdminHomeDialog::CreateAnalyticsButton()
{
    int x = INDENT_LEFT;
    int y = INDENT_TOP + (BUTTON_HEIGHT + CONTROLS_GAP_Y) * 2;
    return m_analyticsButton.Create(m_chart_id, m_name+"_AnalyticsBtn", m_subwin,
                                  x, y, x+BUTTON_WIDTH, y+BUTTON_HEIGHT)
        && m_analyticsButton.Text("Analytics Panel")
        && Add(m_analyticsButton);
}

bool CAdminHomeDialog::CreateShowAllButton()
{
    int x = INDENT_LEFT;
    int y = INDENT_TOP + (BUTTON_HEIGHT + CONTROLS_GAP_Y) * 3;
    return m_showAllButton.Create(m_chart_id, m_name+"_ShowAllBtn", m_subwin,
                                x, y, x+BUTTON_WIDTH, y+BUTTON_HEIGHT)
        && m_showAllButton.Text("Show All 💥")
        && Add(m_showAllButton);
}

Implementação de AdminHomeDialog.mqh no programa principal:

1. Inclusão via #include"AdminHomeDialog.mqh"

#include "AdminHomeDialog.mqh"

Incluir AdminHomeDialog.mqh torna a classe CAdminHomeDialog disponível no script principal. Sem essa inclusão, o compilador não reconheceria CAdminHomeDialog, resultando em erros. Essa abordagem modular permite que o script principal permaneça limpo enquanto mantém a implementação do diálogo em um arquivo separado para melhor organização e manutenção.

2. Declarar como CAdminHomeDialog ExtDialog;

CAdminHomeDialog ExtDialog;

Declarar ExtDialog como uma instância de CAdminHomeDialog permite que o script referencie e controle o Painel Admin Home em todo o programa. Esse objeto lida com a criação, visibilidade e gerenciamento de eventos do painel, tornando-o acessível em diferentes funções.

3. Criar usando ExtDialog dentro de CreateHiddenPanels()

bool CreateHiddenPanels()
{
    bool success = ExtDialog.Create(0, "Admin Home", 0, 
                    MAIN_DIALOG_X, MAIN_DIALOG_Y, 
                    MAIN_DIALOG_X + MAIN_DIALOG_WIDTH, 
                    MAIN_DIALOG_Y + MAIN_DIALOG_HEIGHT);
    if(success) 
    {
        ExtDialog.Hide();
        ChartRedraw();
    }
    return success;
}

O método Create() inicializa o painel com dimensões específicas e o posiciona corretamente no gráfico. Colocar isso dentro de CreateHiddenPanels() garante que o painel seja criado apenas uma vez durante a inicialização, mantendo o processo de configuração organizado e evitando reinicializações desnecessárias.

4. Exibido ou ocultado com base no status de autenticação em OnChartEvent()

if(authManager.IsAuthenticated())
{
    if(!ExtDialog.IsVisible())
    {
        ExtDialog.Show();
        ChartRedraw();
    }
    ExtDialog.ChartEvent(id, lparam, dparam, sparam);
}
else
{
    if(ExtDialog.IsVisible()) 
    {
        ExtDialog.Hide();
    }
}

O Painel Admin Home deve estar acessível apenas após autenticação bem-sucedida. Verificar authManager. IsAuthenticated() garante que usuários não autorizados não possam interagir com o painel. Se a autenticação for válida, o painel é exibido; caso contrário, permanece oculto, reforçando segurança e controle de acesso.

5. Destruído em OnDeinit() quando o script é removido

void OnDeinit(const int reason)
{
    ExtDialog.Destroy(reason);
}

Quando o expert é removido do gráfico, chamar ExtDialog. Destroy() garante que os recursos alocados para o painel sejam totalmente liberados. Isso evita possíveis vazamentos de memória ou objetos gráficos órfãos que poderiam interferir em futuras instâncias do script.

Arquivo de Cabeçalho Telegram

Para criar um arquivo de cabeçalho do Telegram, a função Telegram é copiada diretamente para a fonte do cabeçalho devido à sua simplicidade e operação direta. No entanto, essa abordagem pode diferir para outros arquivos que exigem uma configuração mais estruturada, envolvendo classes, métodos, construtores e destrutores, conforme mencionado anteriormente. Portanto, este é o arquivo de cabeçalho mais simples da nossa lista de criações. Ao modularizar a função, reduzimos o tamanho do código principal, e a função pode ser facilmente reutilizada em outros projetos onde for necessária.

//+------------------------------------------------------------------+
//|                                                     Telegram.mqh |
//|                                Copyright 2024, Clemence Benjamin |
//|             https://www.mql5.com/en/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
//+------------------------------------------------------------------+
//| Telegram.mqh - Telegram Communication Include File               |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Send the message to Telegram                                     |
//+------------------------------------------------------------------+
bool SendMessageToTelegram(string message, string chatId, string botToken)
  {
   string url = "https://api.telegram.org/bot" + botToken + "/sendMessage";
   string jsonMessage = "{\"chat_id\":\"" + chatId + "\", \"text\":\"" + message + "\"}";

   char postData[];
   ArrayResize(postData, StringToCharArray(jsonMessage, postData) - 1);

   int timeout = 5000;
   char result[];
   string responseHeaders;
   int responseCode = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, postData, result, responseHeaders);

   if (responseCode == 200)
     {
      Print("Message sent successfully: ", message);
      return true;
     }
   else
     {
      Print("Failed to send message. HTTP code: ", responseCode, " Error code: ", GetLastError());
      Print("Response: ", CharArrayToString(result));
      return false;
     }
  }
//+------------------------------------------------------------------+

Implementação do cabeçalho Telegram no código principal

Isso é feito simplesmente em 2 etapas:

1. Incluir o arquivo no código principal da seguinte forma.

#include<Telegram.mqh>

O acima funciona apenas se ele estiver armazenado no diretório MQL5/Include; caso contrário, o nome da subpasta deve ser informado da seguinte forma:

#include <FolderName\Telegram.mqh> // Substitua FolderName pelo nome real do local

2. Por fim, precisamos chamar a função no código principal quando necessário, da seguinte forma:

SendMessageToTelegram("Your verification code: " + ActiveTwoFactorAuthCode, 
                         TwoFactorAuthChatId, TwoFactorAuthBotToken)

Desenvolvendo um Arquivo de Cabeçalho de Autenticação

A partir dos desenvolvimentos anteriores, você viu a evolução da lógica de prompt de segurança, que tem sido usada de forma consistente em todas as versões do Painel Admin. Essa lógica também pode ser adaptada para outros projetos relacionados a painéis, especialmente ao desenvolver um módulo classificado para sua funcionalidade. Neste estágio, estamos desenvolvendo Authentication.mqh, que consolida toda a lógica de segurança usada no antigo Painel Admin. Compartilharei o código abaixo e, em seguida, apresentarei uma explicação de como ele funciona.

//+------------------------------------------------------------------+
//|                                          authentication.mqh      |
//|                           Copyright 2024, Clemence Benjamin      |
//|        https://www.mql5.com/en/users/billionaire2024/seller      |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.0"
#property strict

// Authentication Dialog Coordinates
#define AUTH_DIALOG_X         100
#define AUTH_DIALOG_Y         100
#define AUTH_DIALOG_WIDTH     300
#define AUTH_DIALOG_HEIGHT    200

#define PASS_INPUT_X          20
#define PASS_INPUT_Y          50
#define PASS_INPUT_WIDTH      260  // Wider input field
#define PASS_INPUT_HEIGHT     30

#define PASS_LABEL_X          20
#define PASS_LABEL_Y          20
#define PASS_LABEL_WIDTH      200
#define PASS_LABEL_HEIGHT     20

#define FEEDBACK_LABEL_X      20
#define FEEDBACK_LABEL_Y      100
#define FEEDBACK_LABEL_WIDTH  260
#define FEEDBACK_LABEL_HEIGHT 40

// Button spacing adjustments
#define LOGIN_BTN_X           20
#define LOGIN_BTN_Y           130
#define LOGIN_BTN_WIDTH       120
#define LOGIN_BTN_HEIGHT      30

#define CANCEL_BTN_X          160  // Added 20px spacing from login button
#define CANCEL_BTN_Y          130
#define CANCEL_BTN_WIDTH      120
#define CANCEL_BTN_HEIGHT     30

// Two-Factor Authentication Dialog Coordinates
#define TWOFA_DIALOG_X        100
#define TWOFA_DIALOG_Y        100
#define TWOFA_DIALOG_WIDTH    300
#define TWOFA_DIALOG_HEIGHT   200

#define TWOFA_INPUT_X         20
#define TWOFA_INPUT_Y         50
#define TWOFA_INPUT_WIDTH     180
#define TWOFA_INPUT_HEIGHT    30

#define TWOFA_LABEL_X         20
#define TWOFA_LABEL_Y         20
#define TWOFA_LABEL_WIDTH     260
#define TWOFA_LABEL_HEIGHT    20

#define TWOFA_FEEDBACK_X      20
#define TWOFA_FEEDBACK_Y      100
#define TWOFA_FEEDBACK_WIDTH  260
#define TWOFA_FEEDBACK_HEIGHT 40

#define TWOFA_VERIFY_BTN_X    60
#define TWOFA_VERIFY_BTN_Y    130
#define TWOFA_VERIFY_WIDTH    120
#define TWOFA_VERIFY_HEIGHT   30

#define TWOFA_CANCEL_BTN_X    140
#define TWOFA_CANCEL_BTN_Y    130
#define TWOFA_CANCEL_WIDTH    60
#define TWOFA_CANCEL_HEIGHT   30

#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
#include <Controls\Edit.mqh>
#include <Controls\Label.mqh>
#include <Telegram.mqh>

class CAuthenticationManager {
private:
    CDialog m_authDialog;
    CDialog m_2faDialog;
    CEdit m_passwordInput;
    CEdit m_2faCodeInput;
    CLabel m_passwordLabel;
    CLabel m_feedbackLabel;
    CLabel m_2faLabel;
    CLabel m_2faFeedback;
    CButton m_loginButton;
    CButton m_closeAuthButton;
    CButton m_2faLoginButton;
    CButton m_close2faButton;

    string m_password;
    string m_2faChatId;
    string m_2faBotToken;
    int m_failedAttempts;
    bool m_isAuthenticated;
    string m_active2faCode;

public:
    CAuthenticationManager(string password, string twoFactorChatId, string twoFactorBotToken) :
        m_password(password),
        m_2faChatId(twoFactorChatId),
        m_2faBotToken(twoFactorBotToken),
        m_failedAttempts(0),
        m_isAuthenticated(false),
        m_active2faCode("")
    {
    }

    ~CAuthenticationManager()
    {
        m_authDialog.Destroy();
        m_2faDialog.Destroy();
    }

    bool Initialize() {
        if(!CreateAuthDialog() || !Create2FADialog()) {
            Print("Authentication initialization failed");
            return false;
        }
        m_2faDialog.Hide();  // Ensure 2FA dialog starts hidden
        return true;
    }

    bool IsAuthenticated() const { return m_isAuthenticated; }

    void HandleEvent(const int id, const long &lparam, const double &dparam, const string &sparam) {
        if(id == CHARTEVENT_OBJECT_CLICK) {
            if(sparam == "LoginButton") HandleLoginAttempt();
            else if(sparam == "2FALoginButton") Handle2FAAttempt();
            else if(sparam == "CloseAuthButton") m_authDialog.Hide();
            else if(sparam == "Close2FAButton") m_2faDialog.Hide();
        }
    }

private:
    bool CreateAuthDialog() {
    if(!m_authDialog.Create(0, "Authentication", 0, 
       AUTH_DIALOG_X, AUTH_DIALOG_Y, 
       AUTH_DIALOG_X + AUTH_DIALOG_WIDTH, 
       AUTH_DIALOG_Y + AUTH_DIALOG_HEIGHT)) 
       return false;

    if(!m_passwordInput.Create(0, "PasswordInput", 0, 
       PASS_INPUT_X, PASS_INPUT_Y, 
       PASS_INPUT_X + PASS_INPUT_WIDTH, 
       PASS_INPUT_Y + PASS_INPUT_HEIGHT) ||
       !m_passwordLabel.Create(0, "PasswordLabel", 0, 
       PASS_LABEL_X, PASS_LABEL_Y, 
       PASS_LABEL_X + PASS_LABEL_WIDTH, 
       PASS_LABEL_Y + PASS_LABEL_HEIGHT) ||
       !m_feedbackLabel.Create(0, "AuthFeedback", 0, 
       FEEDBACK_LABEL_X, FEEDBACK_LABEL_Y, 
       FEEDBACK_LABEL_X + FEEDBACK_LABEL_WIDTH, 
       FEEDBACK_LABEL_Y + FEEDBACK_LABEL_HEIGHT) ||
       !m_loginButton.Create(0, "LoginButton", 0, 
       LOGIN_BTN_X, LOGIN_BTN_Y, 
       LOGIN_BTN_X + LOGIN_BTN_WIDTH, 
       LOGIN_BTN_Y + LOGIN_BTN_HEIGHT) ||
       !m_closeAuthButton.Create(0, "CloseAuthButton", 0, 
       CANCEL_BTN_X, CANCEL_BTN_Y, 
       CANCEL_BTN_X + CANCEL_BTN_WIDTH, 
       CANCEL_BTN_Y + CANCEL_BTN_HEIGHT))
       return false;       

        m_passwordLabel.Text("Enter Password:");
        m_feedbackLabel.Text("");
        m_feedbackLabel.Color(clrRed);
        m_loginButton.Text("Login");
        m_closeAuthButton.Text("Cancel");

        m_authDialog.Add(m_passwordInput);
        m_authDialog.Add(m_passwordLabel);
        m_authDialog.Add(m_feedbackLabel);
        m_authDialog.Add(m_loginButton);
        m_authDialog.Add(m_closeAuthButton);
        
        m_authDialog.Show();
        return true;
    }

    bool Create2FADialog() {
        if(!m_2faDialog.Create(0, "2FA Verification", 0, 
           TWOFA_DIALOG_X, TWOFA_DIALOG_Y, 
           TWOFA_DIALOG_X + TWOFA_DIALOG_WIDTH, 
           TWOFA_DIALOG_Y + TWOFA_DIALOG_HEIGHT))
            return false;

        if(!m_2faCodeInput.Create(0, "2FAInput", 0, 
           TWOFA_INPUT_X, TWOFA_INPUT_Y, 
           TWOFA_INPUT_X + TWOFA_INPUT_WIDTH, 
           TWOFA_INPUT_Y + TWOFA_INPUT_HEIGHT) ||
           !m_2faLabel.Create(0, "2FALabel", 0, 
           TWOFA_LABEL_X, TWOFA_LABEL_Y, 
           TWOFA_LABEL_X + TWOFA_LABEL_WIDTH, 
           TWOFA_LABEL_Y + TWOFA_LABEL_HEIGHT) ||
           !m_2faFeedback.Create(0, "2FAFeedback", 0, 
           TWOFA_FEEDBACK_X, TWOFA_FEEDBACK_Y, 
           TWOFA_FEEDBACK_X + TWOFA_FEEDBACK_WIDTH, 
           TWOFA_FEEDBACK_Y + TWOFA_FEEDBACK_HEIGHT) ||
           !m_2faLoginButton.Create(0, "2FALoginButton", 0, 
           TWOFA_VERIFY_BTN_X, TWOFA_VERIFY_BTN_Y, 
           TWOFA_VERIFY_BTN_X + TWOFA_VERIFY_WIDTH, 
           TWOFA_VERIFY_BTN_Y + TWOFA_VERIFY_HEIGHT) ||
           !m_close2faButton.Create(0, "Close2FAButton", 0, 
           TWOFA_CANCEL_BTN_X, TWOFA_CANCEL_BTN_Y, 
           TWOFA_CANCEL_BTN_X + TWOFA_CANCEL_WIDTH, 
           TWOFA_CANCEL_BTN_Y + TWOFA_CANCEL_HEIGHT))
            return false;

        m_2faLabel.Text("Enter verification code:");
        m_2faFeedback.Text("");
        m_2faFeedback.Color(clrRed);
        m_2faLoginButton.Text("Verify");
        m_close2faButton.Text("Cancel");

        m_2faDialog.Add(m_2faCodeInput);
        m_2faDialog.Add(m_2faLabel);
        m_2faDialog.Add(m_2faFeedback);
        m_2faDialog.Add(m_2faLoginButton);
        m_2faDialog.Add(m_close2faButton);
        
        return true;
    }

    void HandleLoginAttempt() {
        if(m_passwordInput.Text() == m_password) {
            m_isAuthenticated = true;
            m_authDialog.Hide();
            m_2faDialog.Hide();  // Ensure both dialogs are hidden
        } else {
            if(++m_failedAttempts >= 3) {
                Generate2FACode();
                m_authDialog.Hide();
                m_2faDialog.Show();
            } else {
                m_feedbackLabel.Text(StringFormat("Invalid password (%d attempts left)", 
                                                 3 - m_failedAttempts));
            }
        }
    }

    void Handle2FAAttempt() {
        if(m_2faCodeInput.Text() == m_active2faCode) {
            m_isAuthenticated = true;
            m_2faDialog.Hide();
            m_authDialog.Hide();  // Hide both dialogs on success
        } else {
            m_2faFeedback.Text("Invalid code - please try again");
            m_2faCodeInput.Text("");
        }
    }

    void Generate2FACode() {
        m_active2faCode = StringFormat("%06d", MathRand() % 1000000);
        SendMessageToTelegram("Your verification code: " + m_active2faCode, 
                             m_2faChatId, m_2faBotToken);
    }
};
//+------------------------------------------------------------------+

A classe CAuthenticationManager no módulo fornecido gerencia um processo de autenticação de usuário em várias etapas para uma aplicação baseada em MQL5. Ela administra tanto a autenticação por senha quanto a autenticação de dois fatores (2FA) por meio de uma interface baseada em diálogos. O processo de autenticação começa com o usuário inserindo uma senha em uma caixa de diálogo. Se a senha estiver correta, o acesso é concedido imediatamente. No entanto, se a senha falhar, o sistema rastreia as tentativas incorretas e, após três entradas inválidas, aciona um segundo diálogo para 2FA. O processo de 2FA envolve a geração de um código de verificação de seis dígitos, que é enviado ao usuário via Telegram, que deve inserir o código correto para prosseguir.

O módulo utiliza coordenadas predefinidas para os elementos de diálogo a fim de garantir um layout consistente, e inclui mecanismos de feedback para informar os usuários sobre erros ou autenticação bem-sucedida. A classe também integra a biblioteca Telegram.mqh para lidar com o envio de códigos de 2FA. Ela foi projetada com extensibilidade em mente, permitindo fácil modificação das configurações de senha e 2FA, além de garantir que ambos os diálogos sejam ocultados quando a autenticação for bem-sucedida. Esse design simplifica a experiência do usuário enquanto mantém recursos robustos de segurança.


O Novo Painel Admin de Trading

Neste estágio, estamos integrando todos os módulos desenvolvidos anteriormente para construir um Painel Admin mais estruturado e eficiente. Essa versão aprimorada melhora a organização e a modularidade, permitindo que seus componentes sejam facilmente compartilhados e reutilizados em outras aplicações dentro do terminal.

 O Novo Painel Admin inclui AdminHomeDialog.mqh para a interface gráfica e Authentication.mqh para o gerenciamento de autenticação. O EA define parâmetros de entrada para um chat ID do Telegram e um token de bot para verificação de 2FA. Durante a inicialização (OnInit), ele tenta inicializar a autenticação e criar um painel oculto (CreateHiddenPanels), falhando caso qualquer um dos processos não seja bem-sucedido.

A função OnChartEvent processa eventos do gráfico, lidando com a autenticação antes de exibir ou ocultar o painel AdminHomeDialog com base no status de autenticação. Se autenticado, garante que o painel esteja visível e encaminha os eventos para o painel. Caso contrário, ele oculta o painel. A função de desinicialização (OnDeinit) garante que o diálogo seja destruído corretamente. Esse design assegura acesso seguro ao Painel Admin Home, exigindo autenticação antes de conceder controle sobre seus recursos.

Aqui está o código completo do novo programa:

//+------------------------------------------------------------------+
//|                                              New Admin Panel.mq5 |
//|                                Copyright 2024, Clemence Benjamin |
//|             https://www.mql5.com/en/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.00"

// Panel coordinate defines
#define MAIN_DIALOG_X        30
#define MAIN_DIALOG_Y        80
#define MAIN_DIALOG_WIDTH    335
#define MAIN_DIALOG_HEIGHT   350

#include "AdminHomeDialog.mqh"
#include <Authentication.mqh>

// Input parameters for authentication
input string TwoFactorChatID = "YOUR_CHAT_ID";
input string TwoFactorBotToken = "YOUR_BOT_TOKEN";
string AuthPassword = "2024";

CAdminHomeDialog ExtDialog;
CAuthenticationManager authManager(AuthPassword, TwoFactorChatID, TwoFactorBotToken);

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{  
    if(!authManager.Initialize() || !CreateHiddenPanels())
    {
        Print("Initialization failed");
        return INIT_FAILED;
    }
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    ExtDialog.Destroy(reason);
}

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
    authManager.HandleEvent(id, lparam, dparam, sparam);
    
    if(authManager.IsAuthenticated())
    {
        if(!ExtDialog.IsVisible())
        {
            ExtDialog.Show();
            ChartRedraw();
        }
        // Handle dialog events only when authenticated
        ExtDialog.ChartEvent(id, lparam, dparam, sparam);
    }
    else
    {
        if(ExtDialog.IsVisible()) 
        {
            ExtDialog.Hide();
            
        }
    }
}

//+------------------------------------------------------------------+
//| Create hidden panels                                             |
//+------------------------------------------------------------------+
bool CreateHiddenPanels()
{
    bool success = ExtDialog.Create(0, "Admin Home", 0, 
                    MAIN_DIALOG_X, MAIN_DIALOG_Y, 
                    MAIN_DIALOG_X + MAIN_DIALOG_WIDTH, 
                    MAIN_DIALOG_Y + MAIN_DIALOG_HEIGHT);
    if(success) 
    {
        ExtDialog.Hide();
        ChartRedraw();
    }
    return success;
}


Teste

Aqui apresentamos os resultados dos testes após o desenvolvimento dos componentes individuais e sua integração lógica no novo Painel Admin. Compilamos e executamos o programa com sucesso no gráfico, conforme mostrado abaixo.

Testando o Novo Painel Admin

Testando o Novo Painel Admin no gráfico EURUSD



Conclusão

Tenho certeza de que você pode perceber como simplificamos a organização do nosso Painel Admin. Ao herdar de CAppDialog, melhoramos significativamente a responsividade da aplicação, permitindo arrastar o painel livremente pelo gráfico pela primeira vez. Além disso, o botão de minimizar, herdado da classe base, permite minimizar a aplicação, proporcionando uma visualização do gráfico sem obstruções enquanto ela continua em execução em segundo plano. O painel pode ser maximizado a qualquer momento quando necessário, garantindo usabilidade contínua.

Nosso foco em legibilidade, escalabilidade e modularidade nos levou a desenvolver cada componente separadamente. Essa abordagem melhora a reutilização de código, permitindo integrar esses módulos em outros programas simplesmente incluindo os arquivos relevantes e chamando métodos essenciais. Seguindo adiante, planejamos concluir os componentes restantes que tornarão o novo Painel Admin ainda mais poderoso. Esses incluem CommunicationsDialog.mqh, TradeManagementDialog.mqh e AnalyticsDialog.mqh, cada um projetado para ser reutilizável em diferentes aplicações.

Como ponto de partida, você pode tentar implementar Telegram.mqh em seu próprio programa para ver como ele se integra facilmente. Anexei todos os arquivos necessários abaixo — aproveite para testá-los e desenvolvê-los ainda mais!

Arquivo Descrição
Novo Painel Admin .mqh O Painel Admin mais recente incorpora o conceito de modularidade.
Telegram.mqh Para transmitir mensagens e notificações via Telegram.
Authentication.mqh Este arquivo contém todas as declarações de segurança e a lógica completa.
AdminHomeDialog.mqh Para criação do diálogo da página inicial do administrador. Ele contém todas as declarações de coordenadas.

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

Arquivos anexados |
Telegram.mqh (1.62 KB)
Authentication.mqh (8.92 KB)
AdminHomeDialog.mqh (12.02 KB)
Desenvolvimento do Toolkit de Análise de Price Action (Parte 13): Ferramenta RSI Sentinel Desenvolvimento do Toolkit de Análise de Price Action (Parte 13): Ferramenta RSI Sentinel
A análise de price action pode ser realizada de forma eficaz por meio da identificação de divergências, utilizando indicadores técnicos como o RSI para fornecer sinais cruciais de confirmação. Neste conteúdo, é explicado como a análise automatizada de divergência do RSI pode identificar continuações de tendência e reversões, oferecendo percepções valiosas sobre o sentimento do mercado.
Construindo um Indicador Keltner Channel com Gráficos Canvas Personalizados em MQL5 Construindo um Indicador Keltner Channel com Gráficos Canvas Personalizados em MQL5
Neste artigo, construímos um indicador Keltner Channel com gráficos canvas personalizados em MQL5. Detalhamos a integração de médias móveis, cálculos de ATR e visualização aprimorada do gráfico. Também abordamos o backtesting para avaliar o desempenho do indicador e obter insights práticos de trading.
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.
Estratégia evolutiva de adaptação da matriz de covariância, Covariance Matrix Adaptation Evolution Strategy (CMA-ES) Estratégia evolutiva de adaptação da matriz de covariância, Covariance Matrix Adaptation Evolution Strategy (CMA-ES)
Vamos explorar um dos algoritmos mais interessantes de otimização sem gradiente, que aprende a compreender a geometria da função objetivo. Consideraremos a implementação clássica do CMA-ES com uma pequena modificação, substituindo a distribuição normal por uma distribuição de potência. Uma análise detalhada da matemática do algoritmo, a implementação prática e uma avaliação honesta, onde o CMA-ES é imbatível e onde é melhor não aplicá-lo.