Conjunto de ferramentas para negociação manual rápida: funcionalidade básica

Alexander Fedosov | 15 outubro, 2020

Sumário

Introdução

Atualmente mais e mais traders estão mudando para sistemas de negociação automáticos que ou requerem configuração inicial ou estão totalmente automatizados. No entanto, ainda existe uma parte considerável de traders que negociam manualmente à moda antiga, deixando dentro dos seus sistemas de negociação um lugar para a tomada de decisões humanas em cada situação de negociação específica. Às vezes, essas situações se desenvolvem muito rapidamente, sendo que ao negociar manualmente (por exemplo, no scalping) a precisão e a oportunidade de entrar no mercado são importantes. Para tais situações, são necessárias ferramentas que garantam o mais rápido possível a implementação da ação planejada para entrar ou sair do mercado. Por isso, decidi implementar uma funcionalidade básica própria para atender a essas necessidades.

Ideia por trás do conjunto de ferramentas

Primeiro, é preciso determinar um conjunto de ações básicas para negociação manual e desenvolver uma ferramenta que permita fazer isso o mais rápido e convenientemente possível. Tal negociação manual deve ocorrer de acordo com um determinado sistema de negociação, o que implica entrar no mercado por uma das seguintes formas: ordens a mercado ou ordens pendentes. Por esse motivo, as categorias principais do conjunto de ferramentas trabalharão precisamente com esses dois tipos de ordens. Além disso, será necessário escolher as tarefas que o trader realizará durante a negociação e reduzir o tempo e o número de ações necessárias para fazê-las.

Fig. 1 Janela principal do conjunto de ferramentas

A figura 1 apresenta duas categorias, nomeadamente ordem a mercado e ordem pendente. Também escolhi três tarefas básicas que às vezes precisam ser resolvidas rapidamente e, ao mesmo tempo, não podem ser realizadas numa única ação. Como é sabido, muitos aplicativos, incluindo o terminal MetaTrader 5, têm seu próprio conjunto básico de teclas de atalho para chamar rapidamente determinado comando ou ação. Isso também será levado em consideração neste aplicativo. Entre colchetes é indicada uma tecla de atalho que ao ser pressionada desencadeia a ação especificada ou, no caso de trabalhar com ordens, abre uma janela para trabalhar com eles. Adicionalmente, ficará disponível um recurso para trabalhar com o mouse. Isto quer dizer que, por exemplo, poderemos fechar todas as ordens lucrativas tanto através de a tecla de atalho C (do inglês close) como através do botão "Fechar todas as lucrativas". 

Ao pressionar a tecla de atalho M (do inglês market) ou no botão "Ordem a mercado", a janela "Configurações: Ordem a mercado" é aberta. Ela já fornece um conjunto de ferramenta de entrada de dados para colocar ordens de compra ou venda a mercado. 

Fig. 2 Janela para configuração e criação de ordens a mercado

Além da configuração básica, semelhante à do terminal, será possível selecionar um lote não só usando um valor numérico, mas também com base no percentual do saldo da conta de negociação. Ademais, poderemos definir Take Profit e Stop Loss não apenas em formato de preço, mas também em pontos. As ações Comprar e Vender, conforme acima, podem ser realizadas de duas maneiras: clicando no botão correspondente ou pressionando a tecla de atalho indicada entre colchetes. 

Agora, ao clicar em P (do inglês pending), chegaremos à janela de configurações de ordens pendentes. Aqui são quatro. 

Fig. 3 Janela para configuração e criação de ordens pendentes

Assim como na janela de ordens a mercado, nas ordens pendentes é possível selecionar um lote numérico ou percentual do saldo e selecionar Stop Loss e Take Profit em formato de preço ou pip. 


Implementando um conjunto de ferramentas

Primeiro, vamos criar a estrutura inicial do projeto. Na pasta Experts, criamos uma pasta Simple Trading e vários arquivos, como mostrado na Fig. 4 logo abaixo.

Fig. 4 Estrutura de arquivo do projeto

Vejamos o propósito dos arquivos criados:

Primeiro, vamos para Program.mqh, anexamos as bibliotecas necessárias para a implementação das funções de interface e trading, e criamos uma classe CFast Trading.

//+------------------------------------------------------------------+
//|                                                      Program.mqh |
//|                                                         Alex2356 |
//|                    https://www.mql5.com/en/users/alex2356/seller |
//+------------------------------------------------------------------+
#include <EasyAndFastGUI\WndEvents.mqh>
#include <DoEasy25\Engine.mqh>
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Enumeration for switching the interface language                 |
//+------------------------------------------------------------------+
enum LANG
{
   RUSSIAN,       // Russian
   ENGLISH        // English
};
//+------------------------------------------------------------------+
//| Class for creating an application                                |
//+------------------------------------------------------------------+
class CFastTrading : public CWndEvents
{
public:
                     CFastTrading(void);
                    ~CFastTrading(void);
   //--- Initialization/deinitialization
   void              OnInitEvent(void);
   void              OnDeinitEvent(const int reason);
   //--- Timer
   void              OnTimerEvent(void);
   //--- Chart event handler
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
};
//+------------------------------------------------------------------+
//| Adding GUI elements                                              |
//+------------------------------------------------------------------+
#include "MainWindow.mqh"
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CFastTrading::CFastTrading(void)
{
}
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CFastTrading::~CFastTrading(void)
{
}
//+------------------------------------------------------------------+
//| Initialization                                                    |
//+------------------------------------------------------------------+
void CFastTrading::OnInitEvent(void)
{
}
//+------------------------------------------------------------------+
//| Deinitialization                                                 |
//+------------------------------------------------------------------+
void CFastTrading::OnDeinitEvent(const int reason)
{
//--- Remove the interface
   CWndEvents::Destroy();
}
//+------------------------------------------------------------------+
//| Timer                                                            |
//+------------------------------------------------------------------+
void CFastTrading::OnTimerEvent(void)
{
   CWndEvents::OnTimerEvent();
//---
}
//+------------------------------------------------------------------+
//| Chart event handler                                              |
//+------------------------------------------------------------------+
void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
}
//+------------------------------------------------------------------+

Agora vamos para o arquivo do EA SimpleTrading.mq5, anexamos a ele Program.mqh e criamos uma instância da classe recém-criada. Também definimos os parâmetros de entrada, entre os quais estarão:

//+------------------------------------------------------------------+
//|                                                SimpleTrading.mq5 |
//|                                                         Alex2356 |
//|                    https://www.mql5.com/en/users/alex2356/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, Alexander Fedosov"
#property link      "https://www.mql5.com/en/users/alex2356"
#property version   "1.00"
//--- Include application class
#include "Program.mqh"
//+------------------------------------------------------------------+
//| Expert Advisor input parameters                                  |
//+------------------------------------------------------------------+
input int                  Inp_BaseFont      =  10;                  // Base FontSize 
input color                Caption           =  C'0,130,225';        // Caption Color
input color                Background        =  clrWhite;            // Back color
input LANG                 Language          =  ENGLISH;             // Interface language
input ulong                MagicNumber       =  1111;                // Magic Number
//---
CFastTrading program;
ulong tick_counter;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   tick_counter=GetTickCount();
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   program.OnDeinitEvent(reason);
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
//---
}
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer(void)
{
   program.OnTimerEvent();
}
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int    id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam)
{
   program.ChartEvent(id,lparam,dparam,sparam);
//---
   if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI)
   {
      Print("End in ",GetTickCount()-tick_counter," ms");
   }
}
//+------------------------------------------------------------------+

Para que os parâmetros de entrada do EA estejam disponíveis para esta classe, é necessário criar nela variáveis, às quais serão atribuídos os valores de suas configurações de EA e os métodos pelos quais isso acontecerá. Criamos variáveis na seção privada da classe.

private:
//---
   color             m_caption_color;
   color             m_background_color;
//---
   int               m_base_font_size;
   int               m_m_edit_index;
   int               m_p_edit_index;
//---
   ulong             m_magic_number;
//---
   string            m_base_font;
//---
   LANG              m_language;

Geramos os métodos na seção pública:

   //--- Caption color
   void              CaptionColor(const color clr);
   //--- Background color
   void              BackgroundColor(const color clr);
   //--- Font size
   void              FontSize(const int font_size);
   //--- Font name
   void              FontName(const string font_name);
   //--- Setting the interface language
   void              SetLanguage(const LANG lang);
   //--- Setting the magic number
   void              SetMagicNumber(ulong magic_number);

E vamos implementá-los:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CaptionColor(const color clr)
{
   m_caption_color=clr;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::BackgroundColor(const color clr)
{
   m_background_color=clr;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::FontSize(const int font_size)
{
   m_base_font_size=font_size;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::FontName(const string font_name)
{
   m_base_font=font_name;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::SetLanguage(const LANG lang)
{
   m_language=lang;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::SetMagicNumber(const ulong magic_number)
{
   m_magic_number=magic_number;
}

Agora, na inicialização do Expert Advisor, iremos aplicá-las:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   tick_counter=GetTickCount();
//--- Initialize class variables
   program.FontSize(Inp_BaseFont);
   program.BackgroundColor(Background);
   program.CaptionColor(Caption);
   program.SetLanguage(Language);
   program.SetMagicNumber(MagicNumber);
//---
   return(INIT_SUCCEEDED);
}

Vamos adicionar um métodoCreateGUI() que criará toda a interface, mas por enquanto ela estará vazia. À medida que os elementos da IU são criados, ela será preenchida.

//--- Create the graphical interface of the program
   bool              CreateGUI(void);

Ela já pode ser adicionada à inicialização do EA:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   tick_counter=GetTickCount();
//--- Initialize class variables
   program.FontName("Trebuchet MS");
   program.FontSize(Inp_BaseFont);
   program.BackgroundColor(Background);
   program.CaptionColor(Caption);
   program.SetLanguage(Language);
   program.SetMagicNumber(MagicNumber);
//--- Set up the trading panel
   if(!program.CreateGUI())
   {
      Print(__FUNCTION__," > Failed to create graphical interface!");
      return(INIT_FAILED);
   }
//---
   return(INIT_SUCCEEDED);
}

Agora vamos criar a janela principal do aplicativo. Para fazer isso, adicionamos o métodoCreateMainWindow() na seção protegida de nossa classe. 

protected:
   //--- forms
   bool              CreateMainWindow(void);

Por outra parte, vamos colocar sua implementação no arquivo MainWindow.mqh edepois disso vamos chamá-la no CreateGUI(). Como para implementar este método já usei a substituição de macro CAPTION_NAME, ela deve ser criada num arquivo Defines.mqh criado especialmente para isso.

//+------------------------------------------------------------------+
//| Creates a form for orders                                        |
//+------------------------------------------------------------------+
bool CFastTrading::CreateMainWindow(void)
{
//--- Add a window pointer to the window array
   CWndContainer::AddWindow(m_main_window);
//--- Properties
   m_main_window.XSize(400);
   m_main_window.YSize(182);
//--- Coordinates
   int x=5;
   int y=20;
   m_main_window.CaptionHeight(22);
   m_main_window.IsMovable(true);
   m_main_window.CaptionColor(m_caption_color);
   m_main_window.CaptionColorLocked(m_caption_color);
   m_main_window.CaptionColorHover(m_caption_color);
   m_main_window.BackColor(m_background_color);
   m_main_window.FontSize(m_base_font_size);
   m_main_window.Font(m_base_font);
//--- Create the form
   if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y))
      return(false);
//---
   return(true);
}
//+------------------------------------------------------------------+
//| Creates the graphical interface of the program                   |
//+------------------------------------------------------------------+
bool CFastTrading::CreateGUI(void)
{
//--- Create the main application window
   if(!CreateMainWindow())
      return(false);
//--- Complete GUI creation
   CWndEvents::CompletedGUI();
   return(true);
}

Agora vamos criar uma nova substituição de macro.

//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#include "Program.mqh"
#define CAPTION_NAME                   (m_language==RUSSIAN ? "Быстрый трейдинг" : "Fast Trading")

Após compilar o projeto, obtemos a janela principal do aplicativo. Agora vamos adicionar botões (Fig. 1) que realizarão as ações descritas acima. Para implementá-los, criaremos um método universal na seção protegida de nossa classe CreateButton().

//--- Buttons
   bool              CreateButton(CWindow &window,CButton &button,string text,color baseclr,int x_gap,int y_gap,int w_number);
  

Vamos implementá-lo em MainWindow.mqh e aplicá-lo no corpo do método de criação da janela principal.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateButton(CWindow &window,CButton &button,string text,color baseclr,int x_gap,int y_gap,int w_number)
{
//--- Store the window pointer
   button.MainPointer(window);
//--- Set properties before creation
   button.XSize(170);
   button.YSize(40);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(baseclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(baseclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create a control element
   if(!button.CreateButton(text,x_gap,window.CaptionHeight()+y_gap))
      return(false);
//--- Add a pointer to the element to the database
   CWndContainer::AddToElementsArray(w_number,button);
   return(true);
}
//+------------------------------------------------------------------+
//| Creates a form for orders                                        |
//+------------------------------------------------------------------+
bool CFastTrading::CreateMainWindow(void)
{
//--- Add a window pointer to the window array
   CWndContainer::AddWindow(m_main_window);
//--- Properties
   m_main_window.XSize(400);
   m_main_window.YSize(182);
//--- Coordinates
   int x=5;
   int y=20;
   m_main_window.CaptionHeight(22);
   m_main_window.IsMovable(true);
   m_main_window.CaptionColor(m_caption_color);
   m_main_window.CaptionColorLocked(m_caption_color);
   m_main_window.CaptionColorHover(m_caption_color);
   m_main_window.BackColor(m_background_color);
   m_main_window.FontSize(m_base_font_size);
   m_main_window.Font(m_base_font);
   m_main_window.TooltipsButtonIsUsed(true);
//--- Create the form
   if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y))
      return(false);
//---
   if(!CreateButton(m_main_window,m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',20,10,0))
      return(false);
   if(!CreateButton(m_main_window,m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',210,10,0))
      return(false);
   if(!CreateButton(m_main_window,m_order_button[2],MARKET_ORDERS_PROFIT_CLOSE+"(C)",C'87,128,255',20,60,0))
      return(false);
   if(!CreateButton(m_main_window,m_order_button[3],MARKET_ORDERS_LOSS_CLOSE+"(D)",C'87,128,255',20,110,0))
      return(false);
   if(!CreateButton(m_main_window,m_order_button[4],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',210,60,0))
      return(false);
   return(true);
}

Mas como aqui também são usadas novas substituições de macro, vamos para Defines.mqh e vamos escrevê-las em duas línguas.

//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#include "Program.mqh"
#define CAPTION_NAME                   (m_language==RUSSIAN ? "Быстрый трейдинг" : "Fast Trading System")
#define MARKET_ORDER_NAME              (m_language==RUSSIAN ? "Рыночный ордер" : "Marker Order")
#define PENDING_ORDER_NAME             (m_language==RUSSIAN ? "Отложенный ордер" : "Pending Order")
#define MARKET_ORDERS_PROFIT_CLOSE     (m_language==RUSSIAN ? "Закрыть все прибыльные" : "Close all profitable")
#define MARKET_ORDERS_LOSS_CLOSE       (m_language==RUSSIAN ? "Закрыть все убыточные" : "Close all losing")
#define PEND_ORDERS_ALL_CLOSE          (m_language==RUSSIAN ? "Закрыть все отложенные" : "Close all pending")

Como resultado, ao escolher o inglês, temos a seguinte variação.

Fig. 5 Janela principal do aplicativo.

A versão em russo é mostrada na Fig. 1 acima. Conforme afirmado no ideia por trás, o aplicativo deverá ter mais duas janelas para trabalhar ordens a mercado e pendentes. Portanto, iremos criá-las e associá-las aos botões Market Order e Pending Order conforme acordado acima: clicando sobre eles, bem como utilizando as teclas de atalho M e P, respectivamente. Por isso, adicionamos os dois métodos CreateMarketOrdersWindow() e CreatePendingOrdersWindow() à seção protegida da classe e os implementamos no arquivo MainWindow.mqh.

   bool              CreateMarketOrdersWindow(void);
   bool              CreatePendingOrdersWindow(void);
//+------------------------------------------------------------------+
//| Market order creation and editing window                         |
//+------------------------------------------------------------------+
bool CFastTrading::CreateMarketOrdersWindow(void)
{
//--- Add a window pointer to the window array
   CWndContainer::AddWindow(m_orders_windows[0]);
//--- Properties
   m_orders_windows[0].XSize(450);
   m_orders_windows[0].YSize(242+58);
//--- Coordinates
   int x=m_order_button[0].XGap();
   int y=m_order_button[0].YGap()+60;
//---
   color clrmain=C'87,128,255';
//---
   m_orders_windows[0].CaptionHeight(22);
   m_orders_windows[0].IsMovable(true);
   m_orders_windows[0].CaptionColor(clrmain);
   m_orders_windows[0].CaptionColorLocked(clrmain);
   m_orders_windows[0].CaptionColorHover(clrmain);
   m_orders_windows[0].BackColor(m_background_color);
   m_orders_windows[0].BorderColor(clrmain);
   m_orders_windows[0].FontSize(m_base_font_size);
   m_orders_windows[0].Font(m_base_font);
   m_orders_windows[0].WindowType(W_DIALOG);
//--- Create the form
   if(!m_orders_windows[0].CreateWindow(m_chart_id,m_subwin,CAPTION_M_ORD_NAME,x,y))
      return(false);
   return(true);
}
//+------------------------------------------------------------------+
//| Pending order creation and editing window                        |
//+------------------------------------------------------------------+
bool CFastTrading::CreatePendingOrdersWindow(void)
{
//--- Add a window pointer to the window array
   CWndContainer::AddWindow(m_orders_windows[1]);
//--- Properties
   m_orders_windows[1].XSize(600);
   m_orders_windows[1].YSize(580);
//--- Coordinates
   int x=m_order_button[0].XGap();
   int y=m_order_button[0].YGap()+60;
//---
   color clrmain=C'31,209,111';
//---
   m_orders_windows[1].CaptionHeight(22);
   m_orders_windows[1].IsMovable(true);
   m_orders_windows[1].CaptionColor(clrmain);
   m_orders_windows[1].CaptionColorLocked(clrmain);
   m_orders_windows[1].CaptionColorHover(clrmain);
   m_orders_windows[1].BackColor(m_background_color);
   m_orders_windows[1].BorderColor(clrmain);
   m_orders_windows[1].FontSize(m_base_font_size);
   m_orders_windows[1].Font(m_base_font);
   m_orders_windows[1].WindowType(W_DIALOG);
//--- Create the form
   if(!m_orders_windows[1].CreateWindow(m_chart_id,m_subwin,CAPTION_P_ORD_NAME,x,y))
      return(false);
   return(true);
}

Como ambas as janelas usam novas substituições de macro, nós as adicionamos ao arquivo correspondente:

#define CAPTION_M_ORD_NAME             (m_language==RUSSIAN ? "Настройка: Рыночный Ордер" : "Setting: Market Order")
#define CAPTION_P_ORD_NAME             (m_language==RUSSIAN ? "Настройка: Отложенный Ордер" : "Setting: Pending Order")

Agora vamos chamar as janelas recém-criadas no método base CreateGUI() que gera a interface de aplicativo:

//+------------------------------------------------------------------+
//| Creates the graphical interface of the program                   |
//+------------------------------------------------------------------+
bool CFastTrading::CreateGUI(void)
{
//--- Create the main application window
   if(!CreateMainWindow())
      return(false);
   if(!CreateMarketOrdersWindow())
      return(false);
   if(!CreatePendingOrdersWindow())
      return(false);
//--- Complete GUI creation
   CWndEvents::CompletedGUI();
   return(true);
}

Como as duas janelas criadas são janelas de diálogo, elas não serão visíveis ao iniciar o aplicativo. Além disso, é necessário criar um mecanismo para exibi-las. Mas já decidimos que elas serão uma abertura clicando nos botões Ordem a mercado e Pendente, bem como nas teclas de atalho. Para fazer isso, encontramos o corpo do método na classe base OnEvent() e anotamos as condições necessárias para nossa tarefa.

//+------------------------------------------------------------------+
//| Chart event handler                                              |
//+------------------------------------------------------------------+
void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- Pressing the button event
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
   {
      //---
      if(lparam==m_order_button[0].Id())
         m_orders_windows[0].OpenWindow();
      //---
      if(lparam==m_order_button[1].Id())
         m_orders_windows[1].OpenWindow();
   }
//--- Key press event
   if(id==CHARTEVENT_KEYDOWN)
   {
      //--- Opening a market order window
      if(lparam==KEY_M)
      {
         if(m_orders_windows[0].IsVisible())
         {
            m_orders_windows[0].CloseDialogBox();
         }
         else
         {
            if(m_orders_windows[1].IsVisible())
            {
               m_orders_windows[1].CloseDialogBox();
            }
            //---
            m_orders_windows[0].OpenWindow();
         }
      }
      //--- Opening a pending order window
      if(lparam==KEY_P)
      {
         if(m_orders_windows[1].IsVisible())
         {
            m_orders_windows[1].CloseDialogBox();
         }
         else
         {
            if(m_orders_windows[0].IsVisible())
            {
               m_orders_windows[0].CloseDialogBox();
            }
            //---
            m_orders_windows[1].OpenWindow();
         }
      }
   }
}

Agora podemos compilar o projeto. Vamos tentar abrir caixas de diálogo usando teclas de atalho. O resultado deve ser semelhante ao da Fig. 6 abaixo:

Fig. 6 Abrindo e alternando janelas usando teclas de atalho.

Aqui também é possível notar que não é necessário fechar uma caixa de diálogo para abrir outra. Ocorre a transferência de um para a outra, o que economiza tempo e permite, se necessário, passar de ordens a mercado para ordens pendentes, bastando pressionar um botão. Como outro pequeno toque de conveniência, decidi adicionar caixas de diálogo de fechamento usando a tecla Esc. Ao testar a abertura algumas vezes, houve um desejo de fechá-los por meio deste botão, por isso, vamos adicionar o seguinte código à seção do Evento de pressionamento de botão no teclado:

      //--- Exiting the order placing window
      if(lparam==KEY_ESC)
      {
         if(m_orders_windows[0].IsVisible())
         {
            m_orders_windows[0].CloseDialogBox();
         }
         else if(m_orders_windows[1].IsVisible())
         {
            m_orders_windows[1].CloseDialogBox();
         }
      }

Vamos dar uma olhada mais de perto nas janelas criadas e começar com a janela de Ordens a mercado. Como lembramos da Figura 2, é necessário criar dois blocos para gerenciar as ordens de compra e venda. Primeiro, vamos criar dois elementos de interface, isto é, frames com o novo método CreateFrame().

bool              CreateFrame(CWindow &window,CFrame &frame,const int x_gap,const int y_gap,string caption,int w_number);

Sua implementação será a seguinte:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateFrame(CWindow &window,CFrame &frame,const int x_gap,const int y_gap,string caption,int w_number)
{
//--- Store the pointer to the main control
   frame.MainPointer(window);
//---
   color clrmain=clrNONE;
   if(caption==BUY_ORDER)
      clrmain=C'88,212,210';
   else if(caption==SELL_ORDER)
      clrmain=C'236,85,79';
//---
   frame.YSize(110);
   frame.LabelColor(clrmain);
   frame.BorderColor(clrmain);
   frame.BackColor(m_background_color);
   frame.GetTextLabelPointer().BackColor(m_background_color);
   frame.Font(m_base_font);
   frame.FontSize(m_base_font_size);
   frame.AutoXResizeMode(true);
   frame.AutoXResizeRightOffset(10);
//--- Create a control element
   if(!frame.CreateFrame(caption,x_gap,window.CaptionHeight()+y_gap))
      return(false);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(w_number,frame);
   return(true);
}

Ao implementar a criação de um quadro, temos duas novas substituições de macro para os cabeçalhos dos quadros, portanto, vamos adicioná-los ao Defines.mqh:

#define BUY_ORDER                      (m_language==RUSSIAN ? "Buy-ордер" : "Buy-order")
#define SELL_ORDER                     (m_language==RUSSIAN ? "Sell-ордер" : "Sell-order")

Para aplicar este método, é preciso ir até o final do corpo CreateMarketOrdersWindow() para criação de uma janela de ordens a mercado e escrever o seguinte:

...
//--- Create the form
   if(!m_orders_windows[0].CreateWindow(m_chart_id,m_subwin,CAPTION_M_ORD_NAME,x,y))
      return(false);
//--- BUY BLOCK
   if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1))
      return(false);
//--- SELL BLOCK
   if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1))
      return(false);
   return(true);
}

Agora vamos ver o resultado:

Fig. 7 Blocos de ordens a mercado.

Cada um dos blocos na Fig. 7 conterá as 4 categorias principais de elementos de interface:

Seguimos com a implementação em fases de cada categoria. Para os cabeçalhos vamos criar um método genérico CreateLabel().

//+------------------------------------------------------------------+
//| Creates the text label                                           |
//+------------------------------------------------------------------+
bool CFastTrading::CreateLabel(CWindow &window,CTextLabel &text_label,const int x_gap,const int y_gap,string label_text,int w_number)
{
//--- Store the window pointer
   text_label.MainPointer(window);
//---
   text_label.Font(m_base_font);
   text_label.FontSize(m_base_font_size);
   text_label.XSize(80);
   text_label.BackColor(m_background_color);
   text_label.IsCenterText(true);
//--- Creating a label
   if(!text_label.CreateTextLabel(label_text,x_gap,window.CaptionHeight()+y_gap))
      return(false);
//--- Add a pointer to the element to the database
   CWndContainer::AddToElementsArray(w_number,text_label);
   return(true);
}

Acima, complementamos o método de criação da janela de ordem a mercado, agora iremos complementá-lo levando em consideração a implementação atual.

//--- BUY BLOCK
   if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1))
      return(false);
//--- Headers
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[0],20,30,LOT,1))
      return(false);
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[1],20+80+20,30,TP,1))
      return(false);
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[2],20+(80+20)*2,30,SL,1))
      return(false);
//--- SELL BLOCK
   if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1))
      return(false);
//--- Headers
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[3],20,170,LOT,1))
      return(false);
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[4],20+80+20,170,TP,1))
      return(false);
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[5],20+(80+20)*2,170,SL,1))
      return(false);
   return(true);
}

Existem mais três novas substituições de macro para Lote, Take Profit e Stop Loss. Por isso, adicionamos seus nomes em dois idiomas:

#define LOT                            (m_language==RUSSIAN ? "Лот" : "Lot")
#define TP                             (m_language==RUSSIAN ? "Тейк профит" : "Take Profit")
#define SL                             (m_language==RUSSIAN ? "Стоп лосс" : "Stop Loss")

 A próxima categoria são os botões de alternância. O método CreateSwitchButton() será especialmente criado para eles. 

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateSwitchButton(CWindow &window,CButton &button,string text,int x_gap,int y_gap,int w_number)
{
//--- Store the window pointer
   button.MainPointer(window);
   color baseclr=clrSlateBlue;
   color pressclr=clrIndigo;
//--- Set properties before creation
   button.XSize(80);
   button.YSize(24);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(pressclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(pressclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
   button.TwoState(true);
//--- Create a control element
   if(!button.CreateButton(text,x_gap,window.CaptionHeight()+y_gap))
      return(false);
//--- Add a pointer to the element to the database
   CWndContainer::AddToElementsArray(w_number,button);
   return(true);
}

Também vamos aplicá-lo para ambos os blocos no mesmo método CreateMarketWindow():

...
//--- BUY BLOCK
   if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1))
      return(false);
//--- Headers
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[0],20,30,LOT,1))
      return(false);
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[1],20+80+20,30,TP,1))
      return(false);
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[2],20+(80+20)*2,30,SL,1))
      return(false);
//--- Toggle buttons
   for(int i=0; i<3; i++)
      if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*i,60,1))
         return(false);
//--- SELL BLOCK
   if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1))
      return(false);
//--- Headers
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[3],20,170,LOT,1))
      return(false);
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[4],20+80+20,170,TP,1))
      return(false);
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[5],20+(80+20)*2,170,SL,1))
      return(false);
//--- Toggle buttons
   for(int i=3; i<6; i++)
      if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*(i-3),170+30,1))
         return(false);
   return(true);
}

Teremos de ver mais profundamente a seguinte categoria de campos de entrada, pois exigirá um entendimento adicional de que esses elementos de interface devem ter certas restrições de uso. Por exemplo, é necessário ter em mente que os valores para o campo de entrada Lote devem ser limitados pela especificação do símbolo atual, bem como pelas especificações da conta de negociação. Quer dizer, ao editar este campo, é necessário levar em consideração parâmetros como o lote mínimo e máximo possível, bem como o incremento mínimo para alterá-lo. Com base nisso, criamos CreateLotEdit() e configuramos seu campo de entrada assim:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateLotEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(window);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX));
   text_edit.StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP));
   text_edit.MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN));
   text_edit.SpinEditMode(true);
   text_edit.SetDigits(2);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap))
      return(false);
   text_edit.SetValue(string(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN)));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(w_number,text_edit);
   return(true);
}

Além disso, criamos campos de entrada para Stop Loss e Take Profit. Adicionamos tudo o que foi criado acima ao mesmo método de criação de janela de ordens a mercado.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateTakeProfitEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(window);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(9999);
   text_edit.StepValue(1);
   text_edit.MinValue(0);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap))
      return(false);
   text_edit.SetValue(string(150));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(w_number,text_edit);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateStopLossEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(window);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.MaxValue(9999);
   text_edit.StepValue(1);
   text_edit.MinValue(0);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap))
      return(false);
   text_edit.SetValue(string(150));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(w_number,text_edit);
   return(true);
}

E a última categoria de elementos de interface dessa janela serão os dois botões Comprar e Vender. E os respectivos métodos CreateBuyButton() e CreateSellButton() para adicioná-los.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateBuyButton(CWindow &window,CButton &button,string text,int x_gap,int y_gap,int w_number)
{
//--- Store the window pointer
   button.MainPointer(window);
   color baseclr=C'88,212,210';
//--- Set properties before creation
   button.XSize(120);
   button.YSize(40);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(baseclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(baseclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create a control element
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to the element to the database
   CWndContainer::AddToElementsArray(w_number,button);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateSellButton(CWindow &window,CButton &button,string text,int x_gap,int y_gap,int w_number)
{
//--- Store the window pointer
   button.MainPointer(window);
   color baseclr=C'236,85,79';
//--- Set properties before creation
   button.XSize(120);
   button.YSize(40);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(baseclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(baseclr);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Create a control element
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Add a pointer to the element to the database
   CWndContainer::AddToElementsArray(w_number,button);
   return(true);
}

Com a adição de dois botões concluímos a implementação do método CreateMarketWindow():

//+------------------------------------------------------------------+
//| Market order creation and editing window                         |
//+------------------------------------------------------------------+
bool CFastTrading::CreateMarketOrdersWindow(void)
{
//--- Add a window pointer to the window array
   CWndContainer::AddWindow(m_orders_windows[0]);
//--- Properties
   m_orders_windows[0].XSize(450);
   m_orders_windows[0].YSize(242+58);
//--- Coordinates
   int x=m_order_button[0].XGap();
   int y=m_order_button[0].YGap()+60;
//---
   color clrmain=C'87,128,255';
//---
   m_orders_windows[0].CaptionHeight(22);
   m_orders_windows[0].IsMovable(true);
   m_orders_windows[0].CaptionColor(clrmain);
   m_orders_windows[0].CaptionColorLocked(clrmain);
   m_orders_windows[0].CaptionColorHover(clrmain);
   m_orders_windows[0].BackColor(m_background_color);
   m_orders_windows[0].BorderColor(clrmain);
   m_orders_windows[0].FontSize(m_base_font_size);
   m_orders_windows[0].Font(m_base_font);
   m_orders_windows[0].WindowType(W_DIALOG);
//--- Create the form
   if(!m_orders_windows[0].CreateWindow(m_chart_id,m_subwin,CAPTION_M_ORD_NAME,x,y))
      return(false);
//--- BUY BLOCK
   if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1))
      return(false);
//--- Headers
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[0],20,30,LOT,1))
      return(false);
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[1],20+80+20,30,TP,1))
      return(false);
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[2],20+(80+20)*2,30,SL,1))
      return(false);
//--- Toggle buttons
   for(int i=0; i<3; i++)
      if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*i,60,1))
         return(false);
//--- Edits
   if(!CreateLotEdit(m_orders_windows[0],m_lot_edit[0],20,60+34,1))
      return(false);
   if(!CreateTakeProfitEdit(m_orders_windows[0],m_tp_edit[0],20+(80+20),60+34,1))
      return(false);
   if(!CreateStopLossEdit(m_orders_windows[0],m_sl_edit[0],20+(80+20)*2,60+34,1))
      return(false);
//--- The Buy button
   if(!CreateBuyButton(m_orders_windows[0],m_buy_execute,BUY+"(B)",m_orders_windows[0].XSize()-(120+20),103,1))
      return(false);
//--- SELL BLOCK
   if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1))
      return(false);
//--- Headers
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[3],20,170,LOT,1))
      return(false);
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[4],20+80+20,170,TP,1))
      return(false);
   if(!CreateLabel(m_orders_windows[0],m_m_text_labels[5],20+(80+20)*2,170,SL,1))
      return(false);
//--- Toggle buttons
   for(int i=3; i<6; i++)
      if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*(i-3),170+30,1))
         return(false);
//--- Edits
   if(!CreateLotEdit(m_orders_windows[0],m_lot_edit[1],20,170+30+35,1))
      return(false);
   if(!CreateTakeProfitEdit(m_orders_windows[0],m_tp_edit[1],20+80+20,170+30+35,1))
      return(false);
   if(!CreateStopLossEdit(m_orders_windows[0],m_sl_edit[1],20+(80+20)*2,170+30+35,1))
      return(false);
//--- The Sell button
   if(!CreateSellButton(m_orders_windows[0],m_sell_execute,SELL+"(S)",m_orders_windows[0].XSize()-(120+20),242,1))
      return(false);
   return(true);
}

Além disso, não nos esquecemos de adicionar duas novas substituições de macro:

#define BUY                            (m_language==RUSSIAN ? "Купить" : "Buy")
#define SELL                           (m_language==RUSSIAN ? "Продать" : "Sell")

Compilamos o projeto neste estágio e obtemos o seguinte resultado:

Fig. 8 Interface completa da janela para criação de ordens a mercado.

Mas, por enquanto, isto é apenas um modelo. Já para o seu desenvolvimento, agora devem ser resolvidas as seguintes tarefas:

Antes de começar a implementar o mecanismo de alternância de botões para alterar nomes, é necessário definir seus valores padrão, uma vez que na Fig. 8 são visíveis alguns traços. Para definir o nome, apresentamos o método SetButtonParam(), especialmente porque será útil para alterá-lo no futuro.

//+------------------------------------------------------------------+
//| Setting the button text                                          |
//+------------------------------------------------------------------+
void CFastTrading::SetButtonParam(CButton &button,string text)
{
   button.LabelText(text);
   button.Update(true);
}

Vamos para o manipulador de eventos e entramos na seção Evento após a conclusão da criação da interface e usando nosso método SetButtonParam() atribuímos nomes a todos os botões de alternância.

//--- UI creation completion
   if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI)
   {
      //---
      SetButtonParam(m_switch_button[0],LOT);
      SetButtonParam(m_switch_button[1],POINTS);
      SetButtonParam(m_switch_button[2],POINTS);
      SetButtonParam(m_switch_button[3],LOT);
      SetButtonParam(m_switch_button[4],POINTS);
      SetButtonParam(m_switch_button[5],POINTS);
   }

Aqui temos mais uma substituição de macro para o nome dos botões que devemos adicionar ao Defines.mqh:

#define POINTS                         (m_language==RUSSIAN ? "Пункты" : "Points")

Os preparativos para a criação do mecanismo de alternância estão prontos. Criamos uma função ButtonSwitch() que rastreará o estado do botão (pressionado/solto) do comutador e, com base nisso, atribuímos um nome a ele.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::ButtonSwitch(CButton &button,long lparam,string state1,string state2)
{
   if(lparam==button.Id())
   {
      if(!button.IsPressed())
         SetButtonParam(button,state1);
      else
         SetButtonParam(button,state2);
   }
}

Como a alternância do nome ocorre pelo evento de pressionamento de botão, vamos chamar o método criado em apenas uma seção de manipulador de eventos.

//--- Pressing the button event
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
   {
      //---
      if(lparam==m_order_button[0].Id())
         m_orders_windows[0].OpenWindow();
      //---
      if(lparam==m_order_button[1].Id())
         m_orders_windows[1].OpenWindow();
      //---
      ButtonSwitch(m_switch_button[0],lparam,LOT,PERC_DEPO);
      ButtonSwitch(m_switch_button[1],lparam,POINTS,PRICE);
      ButtonSwitch(m_switch_button[2],lparam,POINTS,PRICE);
      ButtonSwitch(m_switch_button[3],lparam,LOT,PERC_DEPO);
      ButtonSwitch(m_switch_button[4],lparam,POINTS,PRICE);
      ButtonSwitch(m_switch_button[5],lparam,POINTS,PRICE);
   }

Também descreveremos novas substituições de macro em ambos os idiomas:

#define PERC_DEPO                      (m_language==RUSSIAN ? "% Депозит" : "% Deposit")
#define PRICE                          (m_language==RUSSIAN ? "Цена" : "Price")

Compilamos o projeto novamente e obtemos a mudança de nome clicando no botão comutador:

Fig. 9 Alteração dos nomes dos botões comutadores.

Seguimos em frente. A próxima tarefa é alterar as propriedades dos campos de entrada dependendo do estado do botão comutador correspondente. Para fazer isso, apresentaremos três novos métodos para cada campo de entrada: LotMarketSwitch(),TakeMarketSwitch(),StopMarketSwitch(). Para o primeiro método, haverá uma alternância do valor do lote para porcentagem do saldo da conta.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::LotMarketSwitch(long lparam)
{
   for(int i=0; i<2; i++)
   {
      if(lparam==m_switch_button[i*3].Id())
      {
         if(m_switch_button[i*3].IsPressed())
         {
            m_lot_edit[i].SetDigits(0);
            m_lot_edit[i].StepValue(1);
            m_lot_edit[i].MaxValue(100);
            m_lot_edit[i].MinValue(1);
            m_lot_edit[i].SetValue(string(2));
            m_lot_edit[i].GetTextBoxPointer().Update(true);
         }
         else
         {
            m_lot_edit[i].SetDigits(2);
            m_lot_edit[i].StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP));
            m_lot_edit[i].MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN));
            m_lot_edit[i].MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX));
            m_lot_edit[i].SetValue(string(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN)));
            m_lot_edit[i].GetTextBoxPointer().Update(true);
         }
      }
   }
}

Para os outros dois métodos, será do valor de definição de nível em pontos para valor de preço.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::TakeMarketSwitch(long lparam)
{
   for(int i=0; i<2; i++)
   {
      if(lparam==m_switch_button[3*i+1].Id())
      {
         if(m_switch_button[3*i+1].IsPressed())
         {
            MqlTick tick;
            if(SymbolInfoTick(Symbol(),tick))
            {
               m_tp_edit[i].SetDigits(_Digits);
               m_tp_edit[i].StepValue(_Point);
               m_tp_edit[i].SetValue(string(tick.ask));
               m_tp_edit[i].GetTextBoxPointer().Update(true);
            }
         }
         else
         {
            m_tp_edit[i].SetDigits(0);
            m_tp_edit[i].StepValue(1);
            m_tp_edit[i].SetValue(string(150));
            m_tp_edit[i].GetTextBoxPointer().Update(true);
         }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::StopMarketSwitch(long lparam)
{
   for(int i=0; i<2; i++)
   {
      if(lparam==m_switch_button[3*i+2].Id())
      {
         if(m_switch_button[3*i+2].IsPressed())
         {
            MqlTick tick;
            if(SymbolInfoTick(Symbol(),tick))
            {
               m_sl_edit[i].SetDigits(_Digits);
               m_sl_edit[i].StepValue(_Point);
               m_sl_edit[i].SetValue(string(tick.bid));
               m_sl_edit[i].GetTextBoxPointer().Update(true);
            }
         }
         else
         {
            m_sl_edit[i].SetDigits(0);
            m_sl_edit[i].StepValue(1);
            m_sl_edit[i].SetValue(string(150));
            m_sl_edit[i].GetTextBoxPointer().Update(true);
         }
      }
   }
}

Agora, vamos chamar todos os três métodos no manipulador de eventos na seção Evento ao clicar no botão:

      // --- Switch Lot/Percent of balance
      LotMarketSwitch(lparam);
      //--- Switch Take Profit Points/Price
      TakeMarketSwitch(lparam);
      //--- Switch Stop Loss Points/Price
      StopMarketSwitch(lparam);

E verificamos o que resulta no final:

Fig.10 Alternância de modos de campos de entrada na janela de ordens a mercado.

A última tarefa é colocar ordens a mercado de acordo com as configurações e valores dos campos de entrada usando um botão ou tecla de atalho. Para fazer isso, criamos três novos métodos:

Vamos criá-los na seção privada de nossa classe base CFastTrading e implementá-lo nela.

//+------------------------------------------------------------------+
//| Trading Environment Initialization                               |
//+------------------------------------------------------------------+
void CFastTrading::OnInitTrading()
{
   string array_used_symbols[];
//--- Fill in the array of used symbols
   CreateUsedSymbolsArray(SYMBOLS_MODE_CURRENT,"",array_used_symbols);
//--- Set the type of the used symbol list in the symbol collection and fill in the list of symbol timeseries
   m_trade.SetUsedSymbols(array_used_symbols);
//--- Pass all existing collections to the trading class
   m_trade.TradingOnInit();
   m_trade.TradingSetMagic(m_magic_number);
   m_trade.TradingSetLogLevel(LOG_LEVEL_ERROR_MSG);
//--- Set synchronous passing of orders for all used symbols
   m_trade.TradingSetAsyncMode(false);
//--- Set correct order expiration and filling types to all trading objects
   m_trade.TradingSetCorrectTypeExpiration();
   m_trade.TradingSetCorrectTypeFilling();
}

Chamamos esse método no construtor da classe:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CFastTrading::CFastTrading(void)
{
   OnInitTrading();
}

Antes de implementar funções de negociação deve-se ter em mente que sua execução será realizada com base em dois eventos diferentes.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::SetBuyOrder(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buy_execute.Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_B))
   {
      //---
      double lot;
      if(m_switch_button[0].IsPressed())
         lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[0].GetValue()));
      else
         lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[0].GetValue()));
      if(m_switch_button[1].IsPressed() && m_switch_button[2].IsPressed())
      {
         double tp=double(m_tp_edit[0].GetValue());
         double sl=double(m_sl_edit[0].GetValue());
         if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp))
            return(true);
      }
      else if(!m_switch_button[1].IsPressed() && !m_switch_button[2].IsPressed())
      {
         int tp=int(m_tp_edit[0].GetValue());
         int sl=int(m_sl_edit[0].GetValue());
         if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp))
            return(true);
      }
      else if(m_switch_button[1].IsPressed() && !m_switch_button[2].IsPressed())
      {
         double tp=double(m_tp_edit[0].GetValue());
         int sl=int(m_sl_edit[0].GetValue());
         if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp))
            return(true);
      }
      else if(!m_switch_button[1].IsPressed() && m_switch_button[2].IsPressed())
      {
         int tp=int(m_tp_edit[0].GetValue());
         double sl=double(m_sl_edit[0].GetValue());
         if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp))
            return(true);
      }
   }
   return(false);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::SetSellOrder(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_sell_execute.Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_S))
   {
      //---
      double lot;
      if(m_switch_button[3].IsPressed())
         lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[1].GetValue()));
      else
         lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[1].GetValue()));
      //---
      if(m_switch_button[4].IsPressed() && m_switch_button[5].IsPressed())
      {
         double tp=double(m_tp_edit[1].GetValue());
         double sl=double(m_sl_edit[1].GetValue());
         if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp))
            return(true);
      }
      else if(!m_switch_button[4].IsPressed() && !m_switch_button[5].IsPressed())
      {
         int tp=int(m_tp_edit[1].GetValue());
         int sl=int(m_sl_edit[1].GetValue());
         if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp))
            return(true);
      }
      else if(!m_switch_button[4].IsPressed() && m_switch_button[5].IsPressed())
      {
         int tp=int(m_tp_edit[1].GetValue());
         double sl=double(m_sl_edit[1].GetValue());
         if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp))
            return(true);
      }
      else if(m_switch_button[4].IsPressed() && !m_switch_button[5].IsPressed())
      {
         double tp=double(m_tp_edit[1].GetValue());
         int sl=int(m_sl_edit[1].GetValue());
         if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp))
            return(true);
      }
   }
   return(false);
}

Embora ambos os métodos sejam chamados da mesma maneira como acontece com outros no manipulador de eventos, eles estarão fora de qualquer uma das seções de identificação de eventos pelo motivo indicado acima. A implementação atual das funções acima usa o método LotPercent() para calcular o tamanho do lote no modo 'como uma porcentagem do saldo da conta'. 

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CFastTrading::LotPercent(string symbol,ENUM_ORDER_TYPE trade_type,double price,double percent)
{
   double margin=0.0;
//--- checks
   if(symbol=="" || price<=0.0 || percent<1 || percent>100)
      return(0.0);
//--- calculate margin requirements for 1 lot
   if(!OrderCalcMargin(trade_type,symbol,1.0,price,margin) || margin<0.0)
      return(0.0);
//---
   if(margin==0.0)
      return(0.0);
//--- calculate maximum volume
   double volume=NormalizeDouble(AccountInfoDouble(ACCOUNT_BALANCE)*percent/100.0/margin,2);
//--- normalize and check limits
   double stepvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);
   if(stepvol>0.0)
      volume=stepvol*MathFloor(volume/stepvol);
//---
   double minvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN);
   if(volume<minvol)
      volume=0.0;
//---
   double maxvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX);
   if(volume>maxvol)
      volume=maxvol;
//--- return volume
   return(volume);
}

Assim fica concluído o trabalho com ordens a mercado. Agora vamos para a janela de criação de ordens pendentes e, usando o mesmo procedimento realizado com a janela das ordens a mercado, vamos criar uma interface passo a passo. Não faz sentido cobrir toda a sequência de ações, já que são muitos momentos repetitivos, mas vamos nos deter em novas situações.

//+------------------------------------------------------------------+
//| Pending order creation and editing window                        |
//+------------------------------------------------------------------+
bool CFastTrading::CreatePendingOrdersWindow(void)
{
//--- Add a window pointer to the window array
   CWndContainer::AddWindow(m_orders_windows[1]);
//--- Properties
   m_orders_windows[1].XSize(600);
   m_orders_windows[1].YSize(580);
//--- Coordinates
   int x=m_order_button[0].XGap();
   int y=m_order_button[0].YGap()+60;
//---
   color clrmain=C'31,209,111';
//---
   m_orders_windows[1].CaptionHeight(22);
   m_orders_windows[1].IsMovable(true);
   m_orders_windows[1].CaptionColor(clrmain);
   m_orders_windows[1].CaptionColorLocked(clrmain);
   m_orders_windows[1].CaptionColorHover(clrmain);
   m_orders_windows[1].BackColor(m_background_color);
   m_orders_windows[1].BorderColor(clrmain);
   m_orders_windows[1].FontSize(m_base_font_size);
   m_orders_windows[1].Font(m_base_font);
   m_orders_windows[1].WindowType(W_DIALOG);
//--- Create the form
   if(!m_orders_windows[1].CreateWindow(m_chart_id,m_subwin,CAPTION_P_ORD_NAME,x,y))
      return(false);
//---BUY-STOP BLOCK
   if(!CreateFrame(m_orders_windows[1],m_frame[2],10,20,BUYSTOP_ORDER,2))
      return(false);
//--- Headers
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[0],20,60,PRICE,2))
      return(false);
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[1],20+80+20,30,LOT,2))
      return(false);
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[2],20+(80+20)*2,30,TP,2))
      return(false);
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[3],20+(80+20)*3,30,SL,2))
      return(false);
//--- Switches
   for(int i=0; i<3; i++)
      if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*i,60,2))
         return(false);
//--- Edits
   if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[0],20,60+35,2))
      return(false);
   if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[2],20+(80+20),60+35,2))
      return(false);
   if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[2],20+(80+20)*2,60+35,2))
      return(false);
   if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[2],20+(80+20)*3,60+35,2))
      return(false);
//--- Buy Stop placing button
   if(!CreateBuyButton(m_orders_windows[1],m_buystop_execute,"Buy Stop ( 1 )",m_orders_windows[1].XSize()-(120+20),103,2))
      return(false);
//---SELL-STOP BLOCK
   if(!CreateFrame(m_orders_windows[1],m_frame[3],10,160,SELLSTOP_ORDER,2))
      return(false);
//--- Headers
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[4],20,170+30,PRICE,2))
      return(false);
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[5],20+80+20,170,LOT,2))
      return(false);
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[6],20+(80+20)*2,170,TP,2))
      return(false);
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[7],20+(80+20)*3,170,SL,2))
      return(false);
//--- Switches
   for(int i=3; i<6; i++)
      if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*(i-3),170+30,2))
         return(false);
//--- Edits
   if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[1],20,170+30+35,2))
      return(false);
   if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[3],20+(80+20),170+30+35,2))
      return(false);
   if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[3],20+(80+20)*2,170+30+35,2))
      return(false);
   if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[3],20+(80+20)*3,170+30+35,2))
      return(false);
//--- Sell Stop placing button
   if(!CreateSellButton(m_orders_windows[1],m_sellstop_execute,"Sell Stop ( 2 )",m_orders_windows[1].XSize()-(120+20),242,2))
      return(false);
//---BUY-LIMIT BLOCK
   if(!CreateFrame(m_orders_windows[1],m_frame[4],10,300,BUYLIMIT_ORDER,2))
      return(false);
//--- Headers
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[8],20,330,PRICE,2))
      return(false);
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[9],20+80+20,310,LOT,2))
      return(false);
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[10],20+(80+20)*2,310,TP,2))
      return(false);
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[11],20+(80+20)*3,310,SL,2))
      return(false);
//--- Switches
   for(int i=6; i<9; i++)
      if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*(i-6),330,2))
         return(false);
//--- Edits
   if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[2],20,365,2))
      return(false);
   if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[4],20+(80+20),365,2))
      return(false);
   if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[4],20+(80+20)*2,365,2))
      return(false);
   if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[4],20+(80+20)*3,365,2))
      return(false);
//--- Buy Limit placing button
   if(!CreateBuyButton(m_orders_windows[1],m_buylimit_execute,"Buy Limit ( 3 )",m_orders_windows[1].XSize()-(120+20),382,2))
      return(false);
//---SELL-LIMIT BLOCK
   if(!CreateFrame(m_orders_windows[1],m_frame[5],10,440,SELLLIMIT_ORDER,2))
      return(false);
//--- Headers
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[12],20,470,PRICE,2))
      return(false);
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[13],20+80+20,450,LOT,2))
      return(false);
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[14],20+(80+20)*2,450,TP,2))
      return(false);
   if(!CreateLabel(m_orders_windows[1],m_p_text_labels[15],20+(80+20)*3,450,SL,2))
      return(false);
//--- Switches
   for(int i=9; i<12; i++)
      if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*(i-9),470,2))
         return(false);
//--- Edits
   if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[3],20,505,2))
      return(false);
   if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[5],20+(80+20),505,2))
      return(false);
   if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[5],20+(80+20)*2,505,2))
      return(false);
   if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[5],20+(80+20)*3,505,2))
      return(false);
//--- Sell Limit placing button
   if(!CreateSellButton(m_orders_windows[1],m_selllimit_execute,"Sell Limit ( 4 )",m_orders_windows[1].XSize()-(120+20),522,2))
      return(false);
//---
   return(true);
}

Como podemos ver na lista acima, ela usa os mesmos métodos de criação de elementos de interface da janela para trabalhar com ordens a mercado. No entanto, aqui aparece um novo método. Ele é CreatePriceEdit(),. Pelo nome podemos adivinhar facilmente que se trata de um campo de entrada para definir o preço de uma ordem pendente.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreatePriceEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(window);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(24);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.StepValue(_Point);
   text_edit.SpinEditMode(true);
   text_edit.SetDigits(_Digits);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap))
      return(false);
//---
   MqlTick tick;
   if(SymbolInfoTick(Symbol(),tick))
      text_edit.SetValue(string(tick.bid));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(w_number,text_edit);
   return(true);
}

Após sua implementação, o modelo inicial estará pronto, mas a interface ainda não ficará funcional. Ela precisa ser configurada. Aqui vamos seguir a mesma ordem de antes: configuramos os botões de alternância, associamos seu estado às propriedades dos campos de entrada e criamos funções para colocar ordens pendentes.

Vamos para o manipulador de eventos OnEvent(), seção Completando a interface, e definimos os nomes dos botões de alternância:

//--- UI creation completion
   if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI)
   {
      //---
      SetButtonParam(m_switch_button[0],LOT);
      SetButtonParam(m_switch_button[1],POINTS);
      SetButtonParam(m_switch_button[2],POINTS);
      SetButtonParam(m_switch_button[3],LOT);
      SetButtonParam(m_switch_button[4],POINTS);
      SetButtonParam(m_switch_button[5],POINTS);
      //---
      SetButtonParam(m_p_switch_button[0],LOT);
      SetButtonParam(m_p_switch_button[1],POINTS);
      SetButtonParam(m_p_switch_button[2],POINTS);
      SetButtonParam(m_p_switch_button[3],LOT);
      SetButtonParam(m_p_switch_button[4],POINTS);
      SetButtonParam(m_p_switch_button[5],POINTS);
      SetButtonParam(m_p_switch_button[6],LOT);
      SetButtonParam(m_p_switch_button[7],POINTS);
      SetButtonParam(m_p_switch_button[8],POINTS);
      SetButtonParam(m_p_switch_button[9],LOT);
      SetButtonParam(m_p_switch_button[10],POINTS);
      SetButtonParam(m_p_switch_button[11],POINTS);
   }

Vamos para a seção Evento de pressionamento de botão e implementamos um mecanismo para alternar o estado dos botões com mudança de nome:

      //---
      ButtonSwitch(m_p_switch_button[0],lparam,LOT,PERC_DEPO);
      ButtonSwitch(m_p_switch_button[1],lparam,POINTS,PRICE);
      ButtonSwitch(m_p_switch_button[2],lparam,POINTS,PRICE);
      ButtonSwitch(m_p_switch_button[3],lparam,LOT,PERC_DEPO);
      ButtonSwitch(m_p_switch_button[4],lparam,POINTS,PRICE);
      ButtonSwitch(m_p_switch_button[5],lparam,POINTS,PRICE);
      ButtonSwitch(m_p_switch_button[6],lparam,LOT,PERC_DEPO);
      ButtonSwitch(m_p_switch_button[7],lparam,POINTS,PRICE);
      ButtonSwitch(m_p_switch_button[8],lparam,POINTS,PRICE);
      ButtonSwitch(m_p_switch_button[9],lparam,LOT,PERC_DEPO);
      ButtonSwitch(m_p_switch_button[10],lparam,POINTS,PRICE);
      ButtonSwitch(m_p_switch_button[11],lparam,POINTS,PRICE);

Para vincular os estados dos botões de alternância aos campos de entrada e suas propriedades, criaremos os três novos métodos LotPendingSwitch(), TakePendingSwitch(), StopPendingSwitch()

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::LotPendingSwitch(long lparam)
{
   for(int i=0; i<4; i++)
   {
      if(lparam==m_p_switch_button[3*i].Id())
      {
         if(m_p_switch_button[3*i].IsPressed())
         {
            m_lot_edit[i+2].SetDigits(0);
            m_lot_edit[i+2].StepValue(1);
            m_lot_edit[i+2].MaxValue(100);
            m_lot_edit[i+2].MinValue(1);
            m_lot_edit[i+2].SetValue(string(2));
            m_lot_edit[i+2].GetTextBoxPointer().Update(true);
         }
         else
         {
            m_lot_edit[i+2].SetDigits(2);
            m_lot_edit[i+2].StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP));
            m_lot_edit[i+2].MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN));
            m_lot_edit[i+2].MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX));
            m_lot_edit[i+2].SetValue(string(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN)));
            m_lot_edit[i+2].GetTextBoxPointer().Update(true);
         }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::TakePendingSwitch(long lparam)
{
   for(int i=0; i<4; i++)
   {
      if(lparam==m_p_switch_button[3*i+1].Id())
      {
         if(m_p_switch_button[3*i+1].IsPressed())
         {
            MqlTick tick;
            if(SymbolInfoTick(Symbol(),tick))
            {
               m_tp_edit[i+2].SetDigits(_Digits);
               m_tp_edit[i+2].StepValue(_Point);
               m_tp_edit[i+2].SetValue(string(tick.ask));
               m_tp_edit[i+2].GetTextBoxPointer().Update(true);
            }
         }
         else
         {
            m_tp_edit[i+2].SetDigits(0);
            m_tp_edit[i+2].StepValue(1);
            m_tp_edit[i+2].SetValue(string(150));
            m_tp_edit[i+2].GetTextBoxPointer().Update(true);
         }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::StopPendingSwitch(long lparam)
{
   for(int i=0; i<4; i++)
   {
      if(lparam==m_p_switch_button[3*i+2].Id())
      {
         if(m_p_switch_button[3*i+2].IsPressed())
         {
            MqlTick tick;
            if(SymbolInfoTick(Symbol(),tick))
            {
               m_sl_edit[i+2].SetDigits(_Digits);
               m_sl_edit[i+2].StepValue(_Point);
               m_sl_edit[i+2].SetValue(string(tick.bid));
               m_sl_edit[i+2].GetTextBoxPointer().Update(true);
            }
         }
         else
         {
            m_sl_edit[i+2].SetDigits(0);
            m_sl_edit[i+2].StepValue(1);
            m_sl_edit[i+2].SetValue(string(150));
            m_sl_edit[i+2].GetTextBoxPointer().Update(true);
         }
      }
   }
}

Agora vamos chamá-los no manipulador de eventos da seção clicando no botão:

      // --- Switch Lot/Percent of balance
      LotPendingSwitch(lparam);
      //--- Switch Take Profit Points/Price
      TakePendingSwitch(lparam);
      //--- Switch Stop Loss Points/Price
      StopPendingSwitch(lparam);

Compilamos o projeto e vemos o que acabamos de criar:

Fig. 11 Modos de alternância de campos de entrada para ordens pendentes.

Bem, os alternadores de modo do campo de entrada estão prontas e a alteração da propriedade também funciona. Assim, vamos continuar criando as funções necessárias para colocar ordens pendentes. Aqui também agimos por analogia com as ordens a mercado. Criamos quatro métodos para cada tipo.

   bool              SetBuyStopOrder(int id,long lparam);
   bool              SetSellStopOrder(int id,long lparam);
   bool              SetBuyLimitOrder(int id,long lparam);
   bool              SetSellLimitOrder(int id,long lparam);

E vamos implementá-los. Definimos uma tecla de atalho para cada uma das ordens pendentes, além do evento de pressionamento no botão criado.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::SetBuyStopOrder(int id,long lparam)
{
   if(!m_orders_windows[1].IsVisible())
      return(false);
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buystop_execute.Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_1))
   {
      //---
      double lot;
      if(m_p_switch_button[0].IsPressed())
         lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[2].GetValue()));
      else
         lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[2].GetValue()));
      //---
      double pr=double(m_pr_edit[0].GetValue());
      //---
      if(m_p_switch_button[1].IsPressed() && m_p_switch_button[2].IsPressed())
      {
         double tp=double(m_tp_edit[2].GetValue());
         double sl=double(m_sl_edit[2].GetValue());
         if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
      else if(!m_p_switch_button[1].IsPressed() && !m_p_switch_button[2].IsPressed())
      {
         int tp=int(m_tp_edit[2].GetValue());
         int sl=int(m_sl_edit[2].GetValue());
         if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
      else if(m_p_switch_button[1].IsPressed() && !m_p_switch_button[2].IsPressed())
      {
         double tp=double(m_tp_edit[2].GetValue());
         int sl=int(m_sl_edit[2].GetValue());
         if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
      else if(!m_p_switch_button[1].IsPressed() && m_p_switch_button[2].IsPressed())
      {
         int tp=int(m_tp_edit[2].GetValue());
         double sl=double(m_sl_edit[2].GetValue());
         if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
   }
   return(false);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::SetSellStopOrder(int id,long lparam)
{
   if(!m_orders_windows[1].IsVisible())
      return(false);
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_sellstop_execute.Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_2))
   {
      //---
      double lot;
      if(m_p_switch_button[3].IsPressed())
         lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[3].GetValue()));
      else
         lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[3].GetValue()));
      //---
      double pr=double(m_pr_edit[1].GetValue());
      //---
      if(m_p_switch_button[4].IsPressed() && m_p_switch_button[5].IsPressed())
      {
         double tp=double(m_tp_edit[3].GetValue());
         double sl=double(m_sl_edit[3].GetValue());
         if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
      else if(!m_p_switch_button[4].IsPressed() && !m_p_switch_button[5].IsPressed())
      {
         int tp=int(m_tp_edit[3].GetValue());
         int sl=int(m_sl_edit[3].GetValue());
         if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
      else if(m_p_switch_button[4].IsPressed() && !m_p_switch_button[5].IsPressed())
      {
         double tp=double(m_tp_edit[3].GetValue());
         int sl=int(m_sl_edit[3].GetValue());
         if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
      else if(!m_p_switch_button[4].IsPressed() && m_p_switch_button[5].IsPressed())
      {
         int tp=int(m_tp_edit[3].GetValue());
         double sl=double(m_sl_edit[3].GetValue());
         if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
   }
   return(false);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::SetBuyLimitOrder(int id,long lparam)
{
   if(!m_orders_windows[1].IsVisible())
      return(false);
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buylimit_execute.Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_3))
   {
      //---
      double lot;
      if(m_p_switch_button[6].IsPressed())
         lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[4].GetValue()));
      else
         lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[4].GetValue()));
      //---
      double pr=double(m_pr_edit[2].GetValue());
      //---
      if(m_p_switch_button[7].IsPressed() && m_p_switch_button[8].IsPressed())
      {
         double tp=double(m_tp_edit[4].GetValue());
         double sl=double(m_sl_edit[4].GetValue());
         if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
      else if(!m_p_switch_button[7].IsPressed() && !m_p_switch_button[8].IsPressed())
      {
         int tp=int(m_tp_edit[4].GetValue());
         int sl=int(m_sl_edit[4].GetValue());
         if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
      else if(m_p_switch_button[7].IsPressed() && !m_p_switch_button[8].IsPressed())
      {
         double tp=double(m_tp_edit[4].GetValue());
         int sl=int(m_sl_edit[4].GetValue());
         if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
      else if(!m_p_switch_button[7].IsPressed() && m_p_switch_button[8].IsPressed())
      {
         int tp=int(m_tp_edit[4].GetValue());
         double sl=double(m_sl_edit[4].GetValue());
         if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
   }
   return(false);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::SetSellLimitOrder(int id,long lparam)
{
   if(!m_orders_windows[1].IsVisible())
      return(false);
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_selllimit_execute.Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_4))
   {
      //---
      double lot;
      if(m_p_switch_button[9].IsPressed())
         lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[5].GetValue()));
      else
         lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[5].GetValue()));
      //---
      double pr=double(m_pr_edit[3].GetValue());
      //---
      if(m_p_switch_button[10].IsPressed() && m_p_switch_button[11].IsPressed())
      {
         double tp=double(m_tp_edit[5].GetValue());
         double sl=double(m_sl_edit[5].GetValue());
         if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
      else if(!m_p_switch_button[10].IsPressed() && !m_p_switch_button[11].IsPressed())
      {
         int tp=int(m_tp_edit[5].GetValue());
         int sl=int(m_sl_edit[5].GetValue());
         if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
      else if(m_p_switch_button[10].IsPressed() && !m_p_switch_button[11].IsPressed())
      {
         double tp=double(m_tp_edit[5].GetValue());
         int sl=int(m_sl_edit[5].GetValue());
         if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
      else if(!m_p_switch_button[10].IsPressed() && m_p_switch_button[11].IsPressed())
      {
         int tp=int(m_tp_edit[5].GetValue());
         double sl=double(m_sl_edit[5].GetValue());
         if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number))
            return(true);
      }
   }
   return(false);
}

Agora vamos para o manipulador de eventos e o complementamos no início usando a chamada dos métodos recém-criados:

//+------------------------------------------------------------------+
//| Chart event handler                                              |
//+------------------------------------------------------------------+
void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//---
   SetBuyOrder(id,lparam);
   SetSellOrder(id,lparam);
//---
   SetBuyStopOrder(id,lparam);
   SetSellStopOrder(id,lparam);
   SetBuyLimitOrder(id,lparam);
   SetSellLimitOrder(id,lparam);

Assim fica concluída uma funcionalidade básica para trabalhar com todo tipo de ordens. Mas, como o propósito deste aplicativo é facilitar o trading manual, decidi adicionar uma função pequena para acelerar o posicionamento de ordens a mercado e pendentes. Seu princípio é o seguinte: ao definir os valores dos campos de entrada nos diferentes modos usando as setas para cima e para baixo à esquerda do campo, é alterado o número de acordo com o incremento especificado. Tomemos como exemplo quando o preço de uma ordem pendente é definido pressionando para cima e para baixo, nesse caso a alteração em EURUSD será de 1,08500 para 1,85001. Isso é lento e não é muito conveniente. Por isso, ao selecionar um campo de entrada para editar um valor com as teclas de atalho Para cima/Para baixo, adicionamos a alteração do valor num incremento multiplicado por 10. Para fazer isso, criamos o método ArrowSwitch().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::ArrowSwitch(long lparam)
{
//--- Prices
   for(int i=0; i<4; i++)
   {
      if(m_pr_edit[i].GetTextBoxPointer().TextEditState())
      {
         if(lparam==KEY_UP)
         {
            //--- Get the new value
            double value=StringToDouble(m_pr_edit[i].GetValue())+m_pr_edit[i].StepValue()*10;
            //--- Increase by one step and check for exceeding the limit
            m_pr_edit[i].SetValue(DoubleToString(value),false);
         }
         else if(lparam==KEY_DOWN)
         {
            //--- Get the new value
            double value=StringToDouble(m_pr_edit[i].GetValue())-m_pr_edit[i].StepValue()*10;
            //--- Increase by one step and check for exceeding the limit
            m_pr_edit[i].SetValue(DoubleToString(value),false);
         }
      }
   }
//--- Lot
   for(int i=0; i<6; i++)
   {
      if(m_lot_edit[i].GetTextBoxPointer().TextEditState())
      {
         if(lparam==KEY_UP)
         {
            //--- Get the new value
            double value=StringToDouble(m_lot_edit[i].GetValue())+m_lot_edit[i].StepValue()*10;
            //--- Increase by one step and check for exceeding the limit
            m_lot_edit[i].SetValue(DoubleToString(value),false);
         }
         else if(lparam==KEY_DOWN)
         {
            //--- Get the new value
            double value=StringToDouble(m_lot_edit[i].GetValue())-m_lot_edit[i].StepValue()*10;
            //--- Increase by one step and check for exceeding the limit
            m_lot_edit[i].SetValue(DoubleToString(value),false);
         }
      }
   }
//--- Take Profit, Stop Loss
   for(int i=0; i<6; i++)
   {
      //---
      if(m_tp_edit[i].GetTextBoxPointer().TextEditState())
      {
         if(lparam==KEY_UP)
         {
            //--- Get the new value
            double value=StringToDouble(m_tp_edit[i].GetValue())+m_tp_edit[i].StepValue()*10;
            //--- Increase by one step and check for exceeding the limit
            m_tp_edit[i].SetValue(DoubleToString(value),false);
         }
         else if(lparam==KEY_DOWN)
         {
            //--- Get the new value
            double value=StringToDouble(m_tp_edit[i].GetValue())-m_tp_edit[i].StepValue()*10;
            //--- Increase by one step and check for exceeding the limit
            m_tp_edit[i].SetValue(DoubleToString(value),false);
         }
      }
      //---
      if(m_sl_edit[i].GetTextBoxPointer().TextEditState())
      {
         if(lparam==KEY_UP)
         {
            //--- Get the new value
            double value=StringToDouble(m_sl_edit[i].GetValue())+m_sl_edit[i].StepValue()*10;
            //--- Increase by one step and check for exceeding the limit
            m_sl_edit[i].SetValue(DoubleToString(value),false);
         }
         else if(lparam==KEY_DOWN)
         {
            //--- Get the new value
            double value=StringToDouble(m_sl_edit[i].GetValue())-m_sl_edit[i].StepValue()*10;
            //--- Increase by one step and check for exceeding the limit
            m_sl_edit[i].SetValue(DoubleToString(value),false);
         }
      }
   }
}

E vamos chamá-lo no manipulador de eventos na seção Pressionamento de tecla.

//--- Keypress
   if(id==CHARTEVENT_KEYDOWN)
   {
      //---
      ArrowSwitch(lparam);
....

Na Fig. 12, você pode observar a diferença entre a velocidade de alteração do valor editado para o necessário.

Fig. 12 Mudança rápida de valores de campos de entrada usando teclas de atalho.

Assim já fica implementado o conjunto de ferramentas para a colocação de ordens a mercado e pendentes, resta ir para a janela principal do aplicativo e complementá-lo com ações para as ordens existentes, como mostrado na primeira figura no início do artigo. Serão três ações: fechar todas as ordens lucrativas a mercado, fechar todas as ordens não lucrativas e excluir todas as ordens pendentes atuais. Isso só se aplica a ordens com um número mágico predefinido nas configurações e que, além disso, foram feitas por nosso aplicativo. Também deve ser lembrado que ao colocar ordens com o número mágico atual e, depois, alterar este último nas configurações do EA, as ordens existentes segundo o número anterior serão ignoradas como se não estivessem relacionadas ao aplicativo atual.

Embarcamos na implementação das ações declaradas. Criamos três novos métodos:

   void              CloseAllMarketProfit(int id,long lparam);
   void              CloseAllMarketLoss(int id,long lparam);
   void              DeleteAllPending(int id,long lparam);

E implementamos cada um deles. 

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseAllMarketProfit(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[2].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_C))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double profit=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //--- if MagicNumber matches
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(profit+swap>0)
               {
                  //--- zeroing the request and result values
                  ZeroMemory(request);
                  ZeroMemory(result);
                  //--- set the operation parameters
                  request.action   =TRADE_ACTION_DEAL;        // trading operation type
                  request.position =position_ticket;          // position ticket
                  request.symbol   =position_symbol;          // symbol
                  request.volume   =volume;                   // position volume
                  request.deviation=5;                        // allowable price deviation
                  request.magic    =m_magic_number;           // position MagicNumber
                  //--- Set order price and type depending on the position type
                  if(type==POSITION_TYPE_BUY)
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                  }
                  else
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                  }
                  //--- sending a request
                  if(!OrderSend(request,result))
                     PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseAllMarketLoss(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[3].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_D))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      ZeroMemory(request);
      ZeroMemory(result);
      int total=PositionsTotal(); // the number of open positions
      //--- iterate over open positions
      for(int i=total-1; i>=0; i--)
      {
         //--- order parameters
         ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);              // the number of decimal places
         ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
         double volume=PositionGetDouble(POSITION_VOLUME);                                 // position volume
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         double profit=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //--- if MagicNumber matches
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(profit+swap<0)
               {
                  //--- zeroing the request and result values
                  ZeroMemory(request);
                  ZeroMemory(result);
                  //--- set the operation parameters
                  request.action   =TRADE_ACTION_DEAL;        // trading operation type
                  request.position =position_ticket;          // position ticket
                  request.symbol   =position_symbol;          // symbol
                  request.volume   =volume;                   // position volume
                  request.deviation=5;                        // allowable price deviation
                  request.magic    =m_magic_number;           // position MagicNumber
                  //--- Set order price and type depending on the position type
                  if(type==POSITION_TYPE_BUY)
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                  }
                  else
                  {
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                  }
                  //--- sending a request
                  if(!OrderSend(request,result))
                     PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::DeleteAllPending(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[4].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_R))
   {
      //--- declare and initialize the trade request and result of trade request
      MqlTradeRequest request;
      MqlTradeResult  result;
      //--- iterate over all placed pending orders
      for(int i=OrdersTotal()-1; i>=0; i--)
      {
         ulong  order_ticket=OrderGetTicket(i);                   // order ticket
         ulong  magic=OrderGetInteger(ORDER_MAGIC);               // order MagicNumber
         //--- if MagicNumber matches
         if(magic==m_magic_number)
         {
            //--- zeroing the request and result values
            ZeroMemory(request);
            ZeroMemory(result);
            //--- set the operation parameters
            request.action= TRADE_ACTION_REMOVE;                  // trading operation type
            request.order = order_ticket;                         // order ticket
            //--- sending a request
            if(!OrderSend(request,result))
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
}

Repare que os métodos criados também estão submetidos à regra para realizar uma ação com base em dois eventos diferentes: botão na janela principal do aplicativo e tecla de atalho. Todas as três funções serão chamadas no manipulador de eventos, fora de qualquer seção.

//+------------------------------------------------------------------+
//| Chart event handler                                              |
//+------------------------------------------------------------------+
void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//---
   SetBuyOrder(id,lparam);
   SetSellOrder(id,lparam);
//---
   SetBuyStopOrder(id,lparam);
   SetSellStopOrder(id,lparam);
   SetBuyLimitOrder(id,lparam);
   SetSellLimitOrder(id,lparam);
//---
   CloseAllMarketProfit(id,lparam);
   CloseAllMarketLoss(id,lparam);
//---
   DeleteAllPending(id,lparam);

Assim fica concluído o desenvolvimento da funcionalidade básica para trabalho manual rápido. O aplicativo criado pode ser visto no vídeo abaixo.



Fim do artigo

No final do artigo é anexado um arquivo com todos os arquivos listados, classificados por pastas. Por isso, para um correto funcionamento, basta colocar a pasta MQL5 na raiz do terminal. E para encontrar a raiz do terminal onde está localizada a pasta MQL5, no MetaTrader 5 é necessário pressionar a combinação de teclas Ctrl+Shift+D ou usar o menu contextual, conforme mostrado na Figura 13 abaixo.


Fig. 13 Busca da pasta MQL5 na raiz do terminal MetaTrader 5