English Русский 中文 Español Deutsch 日本語
preview
Criando um Painel Administrativo de Negociação em MQL5 (Parte IX): Organização do Código (IV): Classe do Painel de Gerenciamento de Negociações

Criando um Painel Administrativo de Negociação em MQL5 (Parte IX): Organização do Código (IV): Classe do Painel de Gerenciamento de Negociações

MetaTrader 5Exemplos |
22 0
Clemence Benjamin
Clemence Benjamin

Conteúdo


Introdução

Em nossas discussões anteriores, apresentamos a organização de código como uma estratégia crucial para permitir uma expansão suave e escalável do projeto Painel Administrador de Negociação, seguindo o princípio da separação de interesses. Essa abordagem me permitiu focar em cada subpainel dentro do NewAdminPanel, garantindo que nosso programa permaneça modular e bem estruturado. Ao desenvolver cada recurso de forma independente, podemos refiná-los para oferecer a melhor funcionalidade possível.

Antes, nosso Painel de Negociação tinha recursos limitados; agora, ele oferece funcionalidades muito mais amplas. Ele oferece a flexibilidade de executar negociações rápidas enquanto incorpora gerenciamento de risco integrado, tudo dentro da mesma interface. Ordens pendentes também podem ser configuradas diretamente no painel, simplificando o processo de negociação.

Esta atualização aborda dois grandes desafios:

  1. Construir programas grandes e bem organizados que sejam mais fáceis de desenvolver e manter.
  2. Garantir acesso rápido às operações de negociação dentro de um EA tudo-em-um que integra uma interface de comunicação, uma interface de gerenciamento de negociações e, em um futuro próximo, uma interface de análise.

Abaixo, apresentarei uma visão geral desta discussão, mostrando como percorreremos essas melhorias até o fim.


Visão Geral da Discussão

O principal objetivo dos nossos artigos é mostrar aplicações práticas do MQL5 em diversos projetos. Hoje, exploraremos o desenvolvimento de uma classe de Painel de Gerenciamento de Negociações, tendo em mente que, em MQL5, um cabeçalho de classe reúne declarações relacionadas. Nesse contexto, todos os recursos de negociação que pretendemos incluir em nosso painel herdarão de cabeçalhos de classes integradas como CTrade, CDialog, CLabel e CEdit.

Quando a classe estiver completamente desenvolvida, integraremos seus métodos ao programa principal, o EA NewAdminPanel. Nossa discussão não estaria completa sem compartilhar os resultados dos testes e fornecer os arquivos-fonte, permitindo que você revise a implementação, aproveite algumas ideias e experimente o código para aprimorar seus próprios projetos.

Nesta etapa, decidi centralizar a criação do painel inicial dentro do programa principal, pois isso não aumentou significativamente o tamanho do código. Embora a abordagem anterior tivesse suas vantagens, optei por esta estrutura para simplificar o desenvolvimento reduzindo dependências. Meu objetivo é manter o programa principal focado enquanto utilizo uma classe dedicada para cada recurso específico. Os elementos principais da interface agora são criados diretamente dentro do programa principal, resultando em um design mais simplificado e eficiente. Como resultado, não estamos mais chamando os métodos da classe AdminHomeDialog no NewAdminPanel. 

A imagem abaixo ilustra o que criaremos ao final da nossa discussão. Mas isso é apenas o começo, pois, uma vez construído, ele servirá como uma base sólida para futuras melhorias e aperfeiçoamentos.

Painel de Gerenciamento de Negociações (ao final da discussão)

Vantagens deste Projeto

As ferramentas que estamos desenvolvendo aqui oferecem diversos benefícios importantes:

  • Negociação rápida – Execute negociações de forma eficiente.
  • Gerenciamento de risco – Defina Stop Loss (S/L) e Take Profit (T/P) antes de enviar ordens, além de planejar suas negociações antecipadamente utilizando ordens pendentes.
  • Recursos de comunicação – Envie mensagens para outros traders através do painel de comunicação que construímos anteriormente.
  • Aprenda MQL5 avançado – Obtenha conhecimentos mais aprofundados sobre programação orientada a objetos em MQL5.
  • Participação da comunidade – Compartilhe suas ideias e experiências na seção de comentários.
  • Reutilização – O CTradeManagementPanel pode ser utilizado em outros projetos.
Nas próximas seções, vamos mergulhar em uma análise detalhada dos recursos da nossa classe Painel de Gerenciamento de Negociações, seguida de sua integração ao programa principal.


Classe do Painel de Gerenciamento de Negociações (CTradeManagementPanel)

1. Configurando a Base com Cabeçalhos e Macros

Antes de construir qualquer coisa, precisamos trazer as ferramentas corretas. O primeiro passo é incluir os arquivos de cabeçalho responsáveis pela execução de negociações, elementos da interface do usuário e gerenciamento de eventos. Em vez de espalhar valores de layout, como tamanhos de botões e espaçamentos, por todo o código, definimos macros logo no início. Isso garante consistência no design da interface e facilita ajustes futuros nas dimensões sem precisar procurar em vários arquivos.

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

//+------------------------------------------------------------------+
//| Defines for dimensions and spacing                               |
//+------------------------------------------------------------------+
#define BUTTON_WIDTH          100
#define BUTTON_HEIGHT         30
#define DELETE_BUTTON_WIDTH   130   
#define EDIT_WIDTH            80
#define EDIT_HEIGHT           20
#define DEFAULT_LABEL_HEIGHT  20    
#define SECTION_LABEL_WIDTH   250
#define GAP                   10
#define INPUT_LABEL_GAP       3     

2. Definindo a Classe Principal e Seus Componentes

O núcleo do painel de negociação é uma classe que gerencia tanto a interface quanto a lógica de execução de negociações. Ao herdar de uma classe base de diálogo, ela obtém recursos integrados de gerenciamento de interface, permitindo que nos concentremos na personalização do comportamento.

Dentro dessa classe, definimos variáveis-membro importantes. Elas incluem uma referência ao gráfico de negociação, um objeto de execução de negociações e uma coleção estruturada de controles da interface. Organizar esses elementos separadamente garante que a interface permaneça claramente dividida da lógica responsável pelo gerenciamento das ordens.

//+------------------------------------------------------------------+
//| Trade Management Panel class                                     |
//+------------------------------------------------------------------+
class CTradeManagementPanel : public CAppDialog
{
protected:
   // Store chart and subwindow values for helper functions.
   long m_chart;
   int  m_subwin;
   
   CTrade m_trade;  // Trade object for executing trades

   // Section Labels
   CLabel m_secQuickLabel;
   CLabel m_secPendingLabel;
   CLabel m_secAllOpsLabel;
   
   // Section 1: Quick Execution
   CButton m_buyButton;      // Market Buy button
   CButton m_sellButton;     // Market Sell button
   CEdit   m_volumeEdit;     // Volume input
   CLabel  m_lotLabel;       // "Lot:" label above volume input
   // TP and SL for market orders—with a label to their right
   CEdit   m_tpEdit;
   CLabel  m_tpRightLabel;   // "TP:" label
   CLabel  m_tpErrorLabel;
   CEdit   m_slEdit;
   CLabel  m_slRightLabel;   // "SL:" label
   CLabel  m_slErrorLabel;
   
   // Section 2: Pending Orders
   // Column Headers for pending orders
   CLabel  m_pendingPriceHeader;
   CLabel  m_pendingTPHeader;
   CLabel  m_pendingSLHeader;
   CLabel  m_pendingExpHeader;
   
   // Buy pending orders
   CButton m_buyLimitButton;
   CEdit   m_buyLimitPriceEdit;
   CEdit   m_buyLimitTPEdit;
   CEdit   m_buyLimitSLEdit;
   CEdit   m_buyLimitExpEdit;   // Expiration input for Buy Limit
   CButton m_buyStopButton;
   CEdit   m_buyStopPriceEdit;
   CEdit   m_buyStopTPEdit;
   CEdit   m_buyStopSLEdit;
   CEdit   m_buyStopExpEdit;    // Expiration input for Buy Stop
   
   // Sell pending orders
   CButton m_sellLimitButton;
   CEdit   m_sellLimitPriceEdit;
   CEdit   m_sellLimitTPEdit;
   CEdit   m_sellLimitSLEdit;
   CEdit   m_sellLimitExpEdit;  // Expiration input for Sell Limit
   CButton m_sellStopButton;
   CEdit   m_sellStopPriceEdit;
   CEdit   m_sellStopTPEdit;
   CEdit   m_sellStopSLEdit;
   CEdit   m_sellStopExpEdit;   // Expiration input for Sell Stop
   
   // Section 3: All Order Operations
   CButton m_closeAllButton;
   CButton m_closeProfitButton;
   CButton m_closeLossButton;
   CButton m_closeBuyButton;
   CButton m_closeSellButton;
   CButton m_deleteAllOrdersButton;
   CButton m_deleteLimitOrdersButton;
   CButton m_deleteStopOrdersButton;
   CButton m_deleteStopLimitOrdersButton;
   CButton m_resetButton;        
   
   //--- Helper: Create a text label using
   bool CreateLabelEx(CLabel &label, int x, int y, int height, string label_name, string text, color clr)
   {
      string unique_name = m_name + label_name;
      if(!label.Create(m_chart, unique_name, m_subwin, x, y, x, y+height))
         return false;
      if(!Add(label))
         return false;
      if(!label.Text(text))
         return false;
      label.Color(clr);
      return true;
   }
   
   //--- Helper: Create and add a button control
   bool CreateButton(CButton &button, string name, int x, int y, int w = BUTTON_WIDTH, int h = BUTTON_HEIGHT, color clr = clrWhite)
   {
      if(!button.Create(m_chart, name, m_subwin, x, y, x+w, y+h))
         return false;
      button.Text(name);
      button.Color(clr);
      if(!Add(button))
         return false;
      return true;
   }
   
   //--- Helper: Create and add an edit control
   bool CreateEdit(CEdit &edit, string name, int x, int y, int w = EDIT_WIDTH, int h = EDIT_HEIGHT)
   {
      if(!edit.Create(m_chart, name, m_subwin, x, y, x+w, y+h))
         return false;
      if(!Add(edit))
         return false;
      return true;
   }
   
   // Event handler declarations
   virtual void OnClickBuy();
   virtual void OnClickSell();
   virtual void OnClickBuyLimit();
   virtual void OnClickBuyStop();
   virtual void OnClickSellLimit();
   virtual void OnClickSellStop();
   virtual void OnClickCloseAll();
   virtual void OnClickCloseProfit();
   virtual void OnClickCloseLoss();
   virtual void OnClickCloseBuy();
   virtual void OnClickCloseSell();
   virtual void OnClickDeleteAllOrders();
   virtual void OnClickDeleteLimitOrders();
   virtual void OnClickDeleteStopOrders();
   virtual void OnClickDeleteStopLimitOrders();
   virtual void OnClickReset();  
   
   // Validation helpers for market orders
   bool ValidateBuyParameters(double volume, double tp, double sl, double ask);
   bool ValidateSellParameters(double volume, double tp, double sl, double bid);
   
public:
   CTradeManagementPanel();
   ~CTradeManagementPanel();
   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);
   void Toggle();
};

3. Estruturando a Interface do Usuário com Seções Lógicas

Uma interface bem organizada faz toda a diferença. Em vez de espalhar botões e campos de entrada aleatoriamente, dividimos o painel em três seções principais.

  1. A primeira seção é destinada às ordens de mercado rápidas, onde os usuários podem comprar ou vender instantaneamente, definir o volume da negociação e configurar níveis de stop loss e take profit.
  2. A segunda seção é dedicada às ordens pendentes. Aqui, os usuários podem especificar preços, horários de expiração e tipos de ordem, como Buy Limit ou Sell Stop.
  3. A terceira seção trata de ações em massa, como fechar todas as negociações, fechar apenas as lucrativas ou excluir ordens pendentes. Manter esses recursos agrupados garante que a interface permaneça fácil de navegar.

//+------------------------------------------------------------------+
//| Create the trade management panel                                |
//+------------------------------------------------------------------+
bool CTradeManagementPanel::Create(const long chart, const string name, const int subwin,
                                     const int x1, const int y1, const int x2, const int y2)
{
   // Save chart and subwin for later use.
   m_chart  = chart;
   m_subwin = subwin;
   
   if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2))
      return false;
   
   int curX = GAP;
   int curY = GAP;
   
   // Section 1: Quick Order Execution
   if(!CreateLabelEx(m_secQuickLabel, curX, curY-10, DEFAULT_LABEL_HEIGHT, "SecQuick", "Quick Order Execution:", clrBlack))
      return false;
   curY += DEFAULT_LABEL_HEIGHT + GAP;
   
   // Market order row:
   if(!CreateButton(m_buyButton, "Buy", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrGreen))
      return false;
   int volX = curX + BUTTON_WIDTH + GAP;
   if(!CreateLabelEx(m_lotLabel, volX, curY - DEFAULT_LABEL_HEIGHT, DEFAULT_LABEL_HEIGHT, "Lot", "Lot Size:", clrBlack))
      return false;
   if(!CreateEdit(m_volumeEdit, "VolumeEdit", volX, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   double minVolume = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
   m_volumeEdit.Text(DoubleToString(minVolume, 2));
   int sellBtnX = volX + EDIT_WIDTH + GAP;
   if(!CreateButton(m_sellButton, "Sell", sellBtnX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrRed))
      return false;
   curY += BUTTON_HEIGHT + GAP;
   
   // Next row: TP and SL for market orders
   if(!CreateEdit(m_tpEdit, "TPEdit", 4*curX+ GAP , curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_tpEdit.Text("0.00000");
   if(!CreateLabelEx(m_tpRightLabel, curX  + INPUT_LABEL_GAP, curY, DEFAULT_LABEL_HEIGHT, "TP", "TP:", clrBlack))
      return false;
   if(!CreateLabelEx(m_tpErrorLabel, curX, curY + DEFAULT_LABEL_HEIGHT, DEFAULT_LABEL_HEIGHT, "TPError", "", clrBlack))
      return false;
   int slX = 2*EDIT_WIDTH  ; 
   if(!CreateEdit(m_slEdit, "SLEdit", slX + 4*curX, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_slEdit.Text("0.00000");
   if(!CreateLabelEx(m_slRightLabel, slX  , curY, DEFAULT_LABEL_HEIGHT, "SL", "SL:", clrBlack))
      return false;
   if(!CreateLabelEx(m_slErrorLabel, slX, curY + DEFAULT_LABEL_HEIGHT, DEFAULT_LABEL_HEIGHT, "SLError", "", clrBlack))
      return false;
   curY += EDIT_HEIGHT + GAP*2;
   
   // Section 2: Pending Orders
   if(!CreateLabelEx(m_secPendingLabel, curX, curY, DEFAULT_LABEL_HEIGHT, "SecPend", "Pending Orders:", clrBlack))
      return false;
   curY += DEFAULT_LABEL_HEIGHT + GAP;
   
   // Column headers for pending orders (each label includes a colon)
   int headerX = curX + BUTTON_WIDTH + GAP;
   if(!CreateLabelEx(m_pendingPriceHeader, headerX, curY, DEFAULT_LABEL_HEIGHT, "PendPrice", "Price:", clrBlack))
      return false;
   if(!CreateLabelEx(m_pendingTPHeader, headerX + EDIT_WIDTH + GAP, curY, DEFAULT_LABEL_HEIGHT, "PendTP", "TP:", clrBlack))
      return false;
   if(!CreateLabelEx(m_pendingSLHeader, headerX + 2*(EDIT_WIDTH + GAP), curY, DEFAULT_LABEL_HEIGHT, "PendSL", "SL:", clrBlack))
      return false;
   if(!CreateLabelEx(m_pendingExpHeader, headerX + 3*(EDIT_WIDTH + GAP), curY, DEFAULT_LABEL_HEIGHT, "PendExp", "Expiration Date:", clrBlack))
      return false;
   curY += DEFAULT_LABEL_HEIGHT + GAP;
   
   // Prepare default expiration value as current day end time.
   datetime now = TimeCurrent();
   string exp_default = TimeToString(now, TIME_DATE) + " 23:59:59";
   
   // --- Buy Pending Orders ---
   // Buy Limit Order row:
   if(!CreateButton(m_buyLimitButton, "Buy Limit", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrBlue))
      return false;
   int pendingX = curX + BUTTON_WIDTH + GAP;
   if(!CreateEdit(m_buyLimitPriceEdit, "BuyLimitPrice", pendingX, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   string askStr = DoubleToString(SymbolInfoDouble(Symbol(), SYMBOL_ASK), 5);
   m_buyLimitPriceEdit.Text(askStr);
   int pending2X = pendingX + EDIT_WIDTH + GAP;
   if(!CreateEdit(m_buyLimitTPEdit, "BuyLimitTP", pending2X, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_buyLimitTPEdit.Text("0.00000");
   int pending3X = pending2X + EDIT_WIDTH + GAP;
   if(!CreateEdit(m_buyLimitSLEdit, "BuyLimitSL", pending3X, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_buyLimitSLEdit.Text("0.00000");
   int pending4X = pending3X + EDIT_WIDTH + GAP; // Double width for expiration input
   if(!CreateEdit(m_buyLimitExpEdit, "BuyLimitExp", pending4X, curY, 2*EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_buyLimitExpEdit.Text(exp_default);
   curY += BUTTON_HEIGHT + GAP;
   
   // Buy Stop Order row:
   if(!CreateButton(m_buyStopButton, "Buy Stop", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrBlue))
      return false;
   pendingX = curX + BUTTON_WIDTH + GAP;
   if(!CreateEdit(m_buyStopPriceEdit, "BuyStopPrice", pendingX, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_buyStopPriceEdit.Text(askStr);
   pending2X = pendingX + EDIT_WIDTH + GAP;
   if(!CreateEdit(m_buyStopTPEdit, "BuyStopTP", pending2X, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_buyStopTPEdit.Text("0.00000");
   pending3X = pending2X + EDIT_WIDTH + GAP;
   if(!CreateEdit(m_buyStopSLEdit, "BuyStopSL", pending3X, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_buyStopSLEdit.Text("0.00000");
   pending4X = pending3X + EDIT_WIDTH + GAP;
   if(!CreateEdit(m_buyStopExpEdit, "BuyStopExp", pending4X, curY, 2*EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_buyStopExpEdit.Text(exp_default);
   curY += BUTTON_HEIGHT + GAP;
   
   // --- Sell Pending Orders ---
   // Sell Limit Order row:
   if(!CreateButton(m_sellLimitButton, "Sell Limit", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrRed))
      return false;
   pendingX = curX + BUTTON_WIDTH + GAP;
   if(!CreateEdit(m_sellLimitPriceEdit, "SellLimitPrice", pendingX, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   string bidStr = DoubleToString(SymbolInfoDouble(Symbol(), SYMBOL_BID), 5);
   m_sellLimitPriceEdit.Text(bidStr);
   pending2X = pendingX + EDIT_WIDTH + GAP;
   if(!CreateEdit(m_sellLimitTPEdit, "SellLimitTP", pending2X, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_sellLimitTPEdit.Text("0.00000");
   pending3X = pending2X + EDIT_WIDTH + GAP;
   if(!CreateEdit(m_sellLimitSLEdit, "SellLimitSL", pending3X, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_sellLimitSLEdit.Text("0.00000");
   pending4X = pending3X + EDIT_WIDTH + GAP;
   if(!CreateEdit(m_sellLimitExpEdit, "SellLimitExp", pending4X, curY, 2*EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_sellLimitExpEdit.Text(exp_default);
   curY += BUTTON_HEIGHT + GAP;
   
   // Sell Stop Order row:
   if(!CreateButton(m_sellStopButton, "Sell Stop", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrRed))
      return false;
   pendingX = curX + BUTTON_WIDTH + GAP;
   if(!CreateEdit(m_sellStopPriceEdit, "SellStopPrice", pendingX, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_sellStopPriceEdit.Text(bidStr);
   pending2X = pendingX + EDIT_WIDTH + GAP;
   if(!CreateEdit(m_sellStopTPEdit, "SellStopTP", pending2X, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_sellStopTPEdit.Text("0.00000");
   pending3X = pending2X + EDIT_WIDTH + GAP;
   if(!CreateEdit(m_sellStopSLEdit, "SellStopSL", pending3X, curY, EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_sellStopSLEdit.Text("0.00000");
   pending4X = pending3X + EDIT_WIDTH + GAP;
   if(!CreateEdit(m_sellStopExpEdit, "SellStopExp", pending4X, curY, 2*EDIT_WIDTH, EDIT_HEIGHT))
      return false;
   m_sellStopExpEdit.Text(exp_default);
   curY += BUTTON_HEIGHT + GAP;
   
   // Section 3: All Order Operations
   if(!CreateLabelEx(m_secAllOpsLabel, curX, curY, DEFAULT_LABEL_HEIGHT, "SecAllOps", "All Order Operations:", clrBlack))
      return false;
   curY += DEFAULT_LABEL_HEIGHT + GAP;   
   
   int deleteX = curX + 2*BUTTON_WIDTH + 2*GAP; 
   int deleteY = curY  ;      
   CreateButton(m_closeAllButton, "Close All", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrRed);
   CreateButton(m_closeProfitButton, "Close Profit", curX + BUTTON_WIDTH + GAP, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrGreen);
   CreateButton(m_deleteLimitOrdersButton, "Delete Limits", deleteX, curY, DELETE_BUTTON_WIDTH, BUTTON_HEIGHT, clrRed);
   curY += BUTTON_HEIGHT + GAP;
   
   CreateButton(m_closeLossButton, "Close Loss", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrRed);
   CreateButton(m_closeBuyButton, "Close Buy", curX + BUTTON_WIDTH + GAP, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrGreen);
   CreateButton(m_deleteStopOrdersButton, "Delete Stops", deleteX, curY  , DELETE_BUTTON_WIDTH+10, BUTTON_HEIGHT, clrRed);
   CreateButton(m_resetButton, "Reset", deleteX+ 2*BUTTON_WIDTH-3*curX, curY, DELETE_BUTTON_WIDTH, 2*BUTTON_HEIGHT, clrRed);
   curY += BUTTON_HEIGHT + GAP;
   
   CreateButton(m_closeSellButton, "Close Sell", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrBlue);
   CreateButton(m_deleteAllOrdersButton, "Delete All", curX + BUTTON_WIDTH + GAP, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrRed);
   CreateButton(m_deleteStopLimitOrdersButton, "Delete StopLimits", deleteX, curY , DELETE_BUTTON_WIDTH+10, BUTTON_HEIGHT, clrDarkRed);
   curY += BUTTON_HEIGHT + GAP;
  
   return true;
}

4. Automatizando a Criação dos Controles da Interface com Funções Auxiliares

Posicionar manualmente cada botão e campo de entrada tornaria o código repetitivo e mais difícil de manter. Em vez disso, escrevemos funções auxiliares que lidam com a criação, posicionamento e estilização dos controles.

Ao centralizar essa lógica, mantemos o código da interface organizado. Se decidirmos alterar a aparência dos botões ou rótulos, precisaremos atualizar apenas uma função, em vez de modificar dezenas de linhas espalhadas pela base de código.

 //--- Helper: Create a text label using
   bool CreateLabelEx(CLabel &label, int x, int y, int height, string label_name, string text, color clr)
   {
      string unique_name = m_name + label_name;
      if(!label.Create(m_chart, unique_name, m_subwin, x, y, x, y+height))
         return false;
      if(!Add(label))
         return false;
      if(!label.Text(text))
         return false;
      label.Color(clr);
      return true;
   }
   
   //--- Helper: Create and add a button control
   bool CreateButton(CButton &button, string name, int x, int y, int w = BUTTON_WIDTH, int h = BUTTON_HEIGHT, color clr = clrWhite)
   {
      if(!button.Create(m_chart, name, m_subwin, x, y, x+w, y+h))
         return false;
      button.Text(name);
      button.Color(clr);
      if(!Add(button))
         return false;
      return true;
   }
   
   //--- Helper: Create and add an edit control
   bool CreateEdit(CEdit &edit, string name, int x, int y, int w = EDIT_WIDTH, int h = EDIT_HEIGHT)
   {
      if(!edit.Create(m_chart, name, m_subwin, x, y, x+w, y+h))
         return false;
      if(!Add(edit))
         return false;
      return true;
   }

5. Gerenciando Interações do Usuário por Meio de um Sistema Central de Eventos

Depois que a interface estiver pronta, precisamos de uma forma de lidar com as interações. Em vez de atribuir funções separadas para cada botão, utilizamos um despachante central de eventos.

Esse despachante monitora as ações do usuário, determina qual controle acionou o evento e chama a função apropriada. Isso mantém o tratamento de eventos limpo e organizado. Clicar em um botão para executar uma negociação enviará os dados de entrada para a lógica de execução de ordens, enquanto clicar em um botão de redefinição limpará os campos.

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
bool CTradeManagementPanel::OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   if(id == CHARTEVENT_OBJECT_CLICK)
   {
      if(sparam == m_buyButton.Name())            OnClickBuy();
      else if(sparam == m_sellButton.Name())        OnClickSell();
      else if(sparam == m_buyLimitButton.Name())    OnClickBuyLimit();
      else if(sparam == m_buyStopButton.Name())     OnClickBuyStop();
      else if(sparam == m_sellLimitButton.Name())   OnClickSellLimit();
      else if(sparam == m_sellStopButton.Name())    OnClickSellStop();
      else if(sparam == m_closeAllButton.Name())    OnClickCloseAll();
      else if(sparam == m_closeProfitButton.Name()) OnClickCloseProfit();
      else if(sparam == m_closeLossButton.Name())   OnClickCloseLoss();
      else if(sparam == m_closeBuyButton.Name())    OnClickCloseBuy();
      else if(sparam == m_closeSellButton.Name())   OnClickCloseSell();
      else if(sparam == m_deleteAllOrdersButton.Name())  OnClickDeleteAllOrders();
      else if(sparam == m_deleteLimitOrdersButton.Name()) OnClickDeleteLimitOrders();
      else if(sparam == m_deleteStopOrdersButton.Name())   OnClickDeleteStopOrders();
      else if(sparam == m_deleteStopLimitOrdersButton.Name()) OnClickDeleteStopLimitOrders();
      else if(sparam == m_resetButton.Name())       OnClickReset(); // Handle reset button click
   }
   return true;
}

6. Validando e Executando Ordens de Mercado

Antes de executar uma negociação, o sistema verifica os dados de entrada para evitar erros. Ele verifica se o volume da negociação está dentro dos limites aceitáveis e se os valores de stop loss e take profit estão definidos corretamente em relação ao preço de mercado.

Se os dados forem válidos, o objeto de execução de negociações processa a ordem. Caso contrário, o sistema alerta o usuário e impede que uma negociação incorreta seja enviada. Essa etapa de validação é crucial para evitar erros custosos e garantir que o painel funcione de forma confiável.

//+------------------------------------------------------------------+
//| Validate Buy order parameters (Market orders)                    |
//+------------------------------------------------------------------+
bool CTradeManagementPanel::ValidateBuyParameters(double volume, double tp, double sl, double ask)
{
   bool valid = true;
   m_tpErrorLabel.Text("");
   m_slErrorLabel.Text("");
   if(volume <= 0)
   {
      m_tpErrorLabel.Text("Invalid volume");
      m_tpErrorLabel.Color(clrRed);
      valid = false;
   }
   if(tp != 0 && tp <= ask)
   {
      m_tpErrorLabel.Text("Invalid TP");
      m_tpErrorLabel.Color(clrRed);
      valid = false;
   }
   if(sl != 0 && sl >= ask)
   {
      m_slErrorLabel.Text("Invalid SL");
      m_slErrorLabel.Color(clrRed);
      valid = false;
   }
   return valid;
}

//+------------------------------------------------------------------+
//| Validate Sell order parameters (Market orders)                   |
//+------------------------------------------------------------------+
bool CTradeManagementPanel::ValidateSellParameters(double volume, double tp, double sl, double bid)
{
   bool valid = true;
   m_tpErrorLabel.Text("");
   m_slErrorLabel.Text("");
   if(volume <= 0)
   {
      m_tpErrorLabel.Text("Invalid volume");
      m_tpErrorLabel.Color(clrRed);
      valid = false;
   }
   if(tp != 0 && tp >= bid)
   {
      m_tpErrorLabel.Text("Invalid TP");
      m_tpErrorLabel.Color(clrRed);
      valid = false;
   }
   if(sl != 0 && sl <= bid)
   {
      m_slErrorLabel.Text("Invalid SL");
      m_slErrorLabel.Color(clrRed);
      valid = false;
   }
   return valid;
}

7. Gerenciando Ordens Pendentes com Parâmetros Adicionais

Ordens pendentes exigem mais configurações do que ordens de mercado. Os usuários precisam especificar um preço de entrada e um horário de expiração, então ampliamos nossas funções de validação para lidar com essas entradas.

Uma função auxiliar interpreta as configurações de expiração, seja quando o usuário deseja um horário fixo ou uma opção "Good Till Canceled"(GTC) e operações de scalping / scalping. Essa estrutura garante que as ordens pendentes sigam as regras apropriadas antes de serem enviadas.

//+------------------------------------------------------------------+
//| Helper: Parse expiration input                                   |
//+------------------------------------------------------------------+
void ParseExpiration(string sExp, ENUM_ORDER_TYPE_TIME &type_time, datetime &expiration)
{
   if(StringCompare(StringToUpper(sExp), "GTC") == 0)
   {
      type_time = ORDER_TIME_GTC;
      expiration = 0;
   }
   else
   {
      type_time = ORDER_TIME_SPECIFIED;
      expiration = StringToTime(sExp);
   }
}

//+------------------------------------------------------------------+
//| Button click handlers - Pending Orders                           |
//+------------------------------------------------------------------+
void CTradeManagementPanel::OnClickBuyLimit()
{
   double price  = StringToDouble(m_buyLimitPriceEdit.Text());
   double tp     = StringToDouble(m_buyLimitTPEdit.Text());
   double sl     = StringToDouble(m_buyLimitSLEdit.Text());
   double volume = StringToDouble(m_volumeEdit.Text());
   string sExp   = m_buyLimitExpEdit.Text();
   ENUM_ORDER_TYPE_TIME type_time;
   datetime expiration;
   ParseExpiration(sExp, type_time, expiration);
   m_trade.BuyLimit(volume, price, Symbol(), sl, tp, type_time, expiration, "");
}

void CTradeManagementPanel::OnClickBuyStop()
{
   double price  = StringToDouble(m_buyStopPriceEdit.Text());
   double tp     = StringToDouble(m_buyStopTPEdit.Text());
   double sl     = StringToDouble(m_buyStopSLEdit.Text());
   double volume = StringToDouble(m_volumeEdit.Text());
   string sExp   = m_buyStopExpEdit.Text();
   ENUM_ORDER_TYPE_TIME type_time;
   datetime expiration;
   ParseExpiration(sExp, type_time, expiration);
   m_trade.BuyStop(volume, price, Symbol(), sl, tp, type_time, expiration, "");
}

void CTradeManagementPanel::OnClickSellLimit()
{
   double price  = StringToDouble(m_sellLimitPriceEdit.Text());
   double tp     = StringToDouble(m_sellLimitTPEdit.Text());
   double sl     = StringToDouble(m_sellLimitSLEdit.Text());
   double volume = StringToDouble(m_volumeEdit.Text());
   string sExp   = m_sellLimitExpEdit.Text();
   ENUM_ORDER_TYPE_TIME type_time;
   datetime expiration;
   ParseExpiration(sExp, type_time, expiration);
   m_trade.SellLimit(volume, price, Symbol(), sl, tp, type_time, expiration, "");
}

void CTradeManagementPanel::OnClickSellStop()
{
   double price  = StringToDouble(m_sellStopPriceEdit.Text());
   double tp     = StringToDouble(m_sellStopTPEdit.Text());
   double sl     = StringToDouble(m_sellStopSLEdit.Text());
   double volume = StringToDouble(m_volumeEdit.Text());
   string sExp   = m_sellStopExpEdit.Text();
   ENUM_ORDER_TYPE_TIME type_time;
   datetime expiration;
   ParseExpiration(sExp, type_time, expiration);
   m_trade.SellStop(volume, price, Symbol(), sl, tp, type_time, expiration, "");
}

8. Implementando Operações em Massa para um Gerenciamento Eficiente de Negociações

Gerenciar várias negociações manualmente pode ser cansativo, por isso incluímos recursos que permitem aos usuários aplicar ações a várias ordens ao mesmo tempo.

Os usuários podem fechar todas as negociações, fechar apenas as lucrativas ou perdedoras, ou excluir ordens pendentes. Os manipuladores de eventos dessas ações percorrem as posições abertas e aplicam a operação solicitada de forma eficiente. Isso torna o gerenciamento de múltiplas ordens muito mais rápido e fácil.

//+------------------------------------------------------------------+
//| Button click handlers - All Order Operations                     |
//+------------------------------------------------------------------+
void CTradeManagementPanel::OnClickCloseAll()
{
   for(int i = PositionsTotal()-1; i >= 0; i--)
      m_trade.PositionClose(PositionGetTicket(i));
}

void CTradeManagementPanel::OnClickCloseProfit()
{
   for(int i = PositionsTotal()-1; i >= 0; i--)
      if(PositionGetDouble(POSITION_PROFIT) > 0)
         m_trade.PositionClose(PositionGetTicket(i));
}

void CTradeManagementPanel::OnClickCloseLoss()
{
   for(int i = PositionsTotal()-1; i >= 0; i--)
      if(PositionGetDouble(POSITION_PROFIT) < 0)
         m_trade.PositionClose(PositionGetTicket(i));
}

void CTradeManagementPanel::OnClickCloseBuy()
{
   for(int i = PositionsTotal()-1; i >= 0; i--)
      if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
         m_trade.PositionClose(PositionGetTicket(i));
}

void CTradeManagementPanel::OnClickCloseSell()
{
   for(int i = PositionsTotal()-1; i >= 0; i--)
      if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
         m_trade.PositionClose(PositionGetTicket(i));
}

void CTradeManagementPanel::OnClickDeleteAllOrders()
{
   for(int i = OrdersTotal()-1; i >= 0; i--)
      m_trade.OrderDelete(OrderGetTicket(i));
}

void CTradeManagementPanel::OnClickDeleteLimitOrders()
{
   for(int i = OrdersTotal()-1; i >= 0; i--)
      if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_LIMIT ||
         OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_LIMIT)
         m_trade.OrderDelete(OrderGetTicket(i));
}

void CTradeManagementPanel::OnClickDeleteStopOrders()
{
   for(int i = OrdersTotal()-1; i >= 0; i--)
      if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP ||
         OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP)
         m_trade.OrderDelete(OrderGetTicket(i));
}

void CTradeManagementPanel::OnClickDeleteStopLimitOrders()
{
   for(int i = OrdersTotal()-1; i >= 0; i--)
      if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP_LIMIT ||
         OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP_LIMIT)
         m_trade.OrderDelete(OrderGetTicket(i));
}

9. Adicionando Controles de Redefinição e Visibilidade para Melhor Usabilidade

Para melhorar a experiência do usuário, adicionamos dois recursos pequenos, mas úteis. O primeiro é um botão de redefinição que limpa todos os campos de entrada, retornando o painel ao seu estado padrão. Isso é útil quando os usuários desejam recomeçar após fazer alterações.

O segundo recurso é um controle de visibilidade que permite aos usuários mostrar ou ocultar o painel. Isso mantém o espaço de trabalho organizado, ao mesmo tempo em que mantém o painel facilmente acessível quando necessário.

//+------------------------------------------------------------------+
//| Reset all input fields to default values                         |
//+------------------------------------------------------------------+
void CTradeManagementPanel::OnClickReset()
{
   // Reset volume to minimum allowed
   double minVolume = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
   m_volumeEdit.Text(DoubleToString(minVolume, 2));

   // Reset TP and SL for market orders
   m_tpEdit.Text("0.00000");
   m_slEdit.Text("0.00000");

   // Reset pending order prices to current ASK/BID
   double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
   double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
   m_buyLimitPriceEdit.Text(DoubleToString(ask, 5));
   m_buyStopPriceEdit.Text(DoubleToString(ask, 5));
   m_sellLimitPriceEdit.Text(DoubleToString(bid, 5));
   m_sellStopPriceEdit.Text(DoubleToString(bid, 5));

   // Reset pending order TP/SL to 0
   m_buyLimitTPEdit.Text("0.00000");
   m_buyLimitSLEdit.Text("0.00000");
   m_buyStopTPEdit.Text("0.00000");
   m_buyStopSLEdit.Text("0.00000");
   m_sellLimitTPEdit.Text("0.00000");
   m_sellLimitSLEdit.Text("0.00000");
   m_sellStopTPEdit.Text("0.00000");
   m_sellStopSLEdit.Text("0.00000");

   // Reset expiration dates to current day's end
   datetime now = TimeCurrent();
   string exp_default = TimeToString(now, TIME_DATE) + " 23:59:59";
   m_buyLimitExpEdit.Text(exp_default);
   m_buyStopExpEdit.Text(exp_default);
   m_sellLimitExpEdit.Text(exp_default);
   m_sellStopExpEdit.Text(exp_default);

   // Clear error messages
   m_tpErrorLabel.Text("");
   m_slErrorLabel.Text("");
}

Anexei o código-fonte completo abaixo deste artigo, que você pode baixar. Na próxima seção, vou guiá-lo através de um processo passo a passo sobre como integrar a classe Trade Management Panel ao programa principal.


Integração com (New_Admin_Panel)

1. Incluindo o cabeçalho do Trade Management no sistema New_Admin_Panel

Primeiramente, adicionamos o TradeManagementPanel incluindo seu arquivo de cabeçalho. Isso torna disponível dentro do nosso painel administrativo tudo o que precisamos para execução e gerenciamento de negociações. Simples, mas essencial.

#include <TradeManagementPanel.mqh>

2. Configurando uma Instância do Painel de Negociação

Precisamos que o painel de negociação esteja acessível em todo o EA, então o definimos como um ponteiro global. Dessa forma, podemos verificar se ele já foi criado e interagir com ele quando necessário.

CTradeManagementPanel  *g_tradePanel = NULL;

3. Adicionando um Botão de Gerenciamento de Negociação

Os usuários precisam de uma forma de abrir o painel de negociação, então adicionamos um botão dedicado dentro do painel inicial do administrador. Utilizamos uma função auxiliar para garantir consistência no layout e na estilização. Esse botão fica diretamente na interface, pronto para abrir o painel de negociação quando clicado.

CButton g_tradeMgmtButton;

4. Tornando o Painel de Negociação Clicável

É aqui que a implementação começa a ficar mais interessante. Criamos uma função que gerencia o ciclo de vida do painel de negociação.

  1. Se o painel de negociação não existir, nós o criamos.
  2. Se ele já existir, apenas alternamos entre exibi-lo e ocultá-lo.

void HandleTradeManagement()
{
   if(g_tradePanel == NULL)
   {
      g_tradePanel = new CTradeManagementPanel();
      if(!g_tradePanel.Create(g_chart_id, "TradeManagementPanel", g_subwin, 310, 20, 310+565, 20+510))
      {
         delete g_tradePanel;
         g_tradePanel = NULL;
         Print("Failed to create Trade Management Panel");
         return;
      }
   }
   g_tradePanel.Toggle();
   ChartRedraw();
}

Com essa configuração, os usuários obtêm acesso instantâneo ao gerenciamento de negociações com o clique de um botão.

5. Conectando o Gerenciamento de Negociação aos Eventos

Em seguida, garantimos que o painel de negociação responda às ações do usuário. Dentro do manipulador de eventos, encaminhamos as interações para o painel caso ele esteja aberto. Isso garante que o painel reaja em tempo real, seja executando ordens, ajustando stop losses ou fechando negociações.

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

6. Limpando Recursos Quando o EA é Encerrado

Quando o EA é removido, precisamos liberar memória e evitar vazamentos. Isso significa destruir corretamente o painel de negociação. 

if(g_tradePanel != NULL)
{
   g_tradePanel.Destroy(reason);
   delete g_tradePanel;
   g_tradePanel = NULL;
}

Nenhuma instância residual. Nenhum recurso desperdiçado. Apenas um encerramento limpo. O arquivo-fonte completo do New_Admin_Panel está anexado abaixo para download. Você pode seguir o mesmo procedimento, com algumas modificações, para integrar o Painel de Gerenciamento de Negociações aos seus próprios projetos. Na próxima seção, compartilharemos os resultados dos testes.


Testes

Após compilar o código com sucesso, executei-o na plataforma MetaTrader 5. Abaixo estão ilustrações mostrando a abertura bem-sucedida do Painel de Gerenciamento de Negociações a partir do Painel Inicial do Administrador. O Painel Inicial permite alternar entre exibir e ocultar o Painel de Gerenciamento de Negociações, oferecendo flexibilidade para visualizar completamente o gráfico quando necessário. Com o Painel de Gerenciamento de Negociações ativo, podemos operar seus controles diretamente no gráfico enquanto continuamos observando a ação dos preços, uma vantagem para operações de scalp trading. Veja a imagem abaixo.

Utilizando o Painel de Gerenciamento de Negociações


Conclusão

O desenvolvimento da classe Painel de Gerenciamento de Negociações representa mais um marco à medida que aplicamos a modularização para estruturar nosso código de forma eficaz, tornando componentes de grandes programas reutilizáveis em diferentes projetos. Como discutimos anteriormente, essa abordagem oferece inúmeros benefícios. Apresentamos o processo de desenvolvimento passo a passo, mostrando como ele pode ser implementado na prática.

Não afirmo ser perfeito nessa área, estou continuamente aprendendo e melhorando. Pode haver erros que pessoas mais experientes consigam identificar, e fico aberto a comentários construtivos para o benefício de todos. Aprecio sinceramente as vantagens que este painel oferece àqueles que o consideram útil.

Também gostaria de agradecer a Omega J Msigwa por sua contribuição para a base de código com o código-fonte do Painel Informativo, que forneceu insights úteis durante este desenvolvimento. Não hesite em expandir e modificar o código anexado para aprimorar suas habilidades em MQL5 e criar ferramentas ainda mais poderosas.

Arquivo Descrição
TradeManagementPanel.mqh Código-fonte do cabeçalho da classe que fornece uma interface gráfica para executar, gerenciar e modificar negociações de forma eficiente dentro do EA New_Admin_Panel. Ele também pode ser integrado a outros EAs.
New_Admin_Panel.mq5 O código-fonte do EA New_Admin_Panel funciona como uma interface centralizada para gerenciar execução de negociações, comunicações, análises e outras funções administrativas quando compilado para execução na plataforma de negociação.

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

Arquivos anexados |
New_Admin_Panel.mq5 (17.12 KB)
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.
Dominando Logs (Parte 6): Armazenando logs em um banco de dados Dominando Logs (Parte 6): Armazenando logs em um banco de dados
Este artigo explora o uso de bancos de dados para armazenar logs de forma estruturada e escalável. Ele aborda conceitos fundamentais, operações essenciais, configuração e implementação de um manipuladores (handler) de banco de dados em MQL5. Por fim, valida os resultados e destaca os benefícios dessa abordagem para otimização e monitoramento eficiente do sistema.
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.
Tabelas no paradigma MVC em MQL5: Integramos o componente Model ao componente View Tabelas no paradigma MVC em MQL5: Integramos o componente Model ao componente View
Neste artigo, criaremos a primeira versão do elemento de controle TableControl (TableView). Será uma tabela estática simples, criada com base nos dados de entrada definidos por dois arrays: um array de dados e um array de cabeçalhos de coluna.