Conjunto de ferramentas para negociação manual rápida: trabalhando com ordens abertas e pendentes

Alexander Fedosov | 29 outubro, 2020

Sumário

Introdução

Anteriormente, criamos uma funcionalidade básica projetada para simplificar e acelerar o trabalho dos traders que preferem a negociação manual. Em primeiro lugar, a ênfase dada ao assunto era facilitar a definição de determinadas ordens, isto é, conseguir um conjunto de ferramentas mais focado na entrada no mercado com sua respectiva funcionalidade. No entanto, há muito se sabe que qualquer estratégia de negociação, seja manual ou automática, deve ter três estágios fundamentais ao trabalhar com os mercados. Tratam-se de regras para entrar no mercado, acompanhamento de posições abertas e suas condições de fechamento. Quanto à funcionalidade atual, apenas tem sido desenvolvido seu primeiro estágio, pelo que seria lógico agregar mais recursos para trabalhar com posições já abertas ou pendentes, bem como ampliar as condições de fechamento de transações. Neste caso, todos os cálculos devem ser fornecidos ao conjunto de ferramentas e a decisão em si será deixada para o trader. 


Formulação do problema

No início, devemos determinar o leque de tarefas que implementará a funcionalidade acima mencionada. Por isso, destacaremos as principais etapas para complementar o aplicativo atual:

Agora, vamos dar uma olhada em cada um dos estágios separadamente. 


Redesenhando a estrutura do aplicativo

Para maior clareza e compreensão, lembremo-nos do que temos até agora. A Fig. 1 abaixo mostra os blocos principais que executam tarefas em duas das três categorias: abrir/criar e fechar/excluir. A terceira categoria, conforme descrito acima, é o acompanhamento e edição manuais.


Fig. 1 Principais blocos do conjunto de ferramentas

Logo, para isso apresentaremos três guias. Na primeira, deixaremos a funcionalidade mostrada na Fig. 1, e nas outras duas trabalharemos com posições a mercado e ordens pendentes.


Fig. 2 Nova estrutura do aplicativo

A guia Negociação conterá tudo o que estava anteriormente na janela principal do conjunto de ferramentas. No Controle de posições abertas haverá uma tabela com as já abertas por meio deste aplicativo, bem como também a possibilidade de trabalhar com elas. O Controle de ordens pendentes também conterá apenas aquelas criadas pelo conjunto de ferramentas e elementos de interface para fechamento e edição dos colocados. Vamos considerá-los com mais detalhes.


Fig. 3 Guia Controle de posições a mercado

Assim, na Fig. 3, a guia Controle de posições a mercado inclui os seguintes elementos:

Agora, na Fig. 4, vamos dar uma olhada na guia Controle de ordens pendentes.


Fig. 4 Controle de ordens pendentes.

Aqui tudo quase que é a mesma coisa:

Voltaremos para a guia Negociação, ela também terá grandes mudanças. Em primeiro lugar, complementaremos as ferramentas existentes para fechar posições lucrativas e não lucrativas. Agora será possível fechar apenas posições Long ou Short. Além disso, apresentaremos interruptores de modos de fechamento.


Fig. 5 Expansão da funcionalidade para fechamento posições a mercado.

Como podemos ver na Fig. 5, temos 4 novos botões: Fechar BUY lucrativo, Fechar SELL lucrativo, Fechar BUY não lucrativo, Fechar SELL não lucrativo. Seu propósito é óbvio, mas, além disso, à direita deles foram adicionados interruptores, que deverão ser considerados em mais detalhes. Os interruptores são todos iguais, portanto, é bom fazer as descrições dos rótulos para todos eles.

Atenção: Ao escolher os dois últimos pontos, os valores são calculados apenas para as posições abertas por este aplicativo com o número mágico definido. Além disso, o valor é verificado para todas as posições que atendem a dois critérios: tipo de posição e número mágico.

Para maior clareza, vamos dar um exemplo de acordo com as configurações na Fig. 5: Fechar Todas as não Lucrativas e a opção sum>currency. Nesse caso, o conjunto de ferramentas encontrará todas as posições abertas por ele, somará seu lucro e, se for mais de 10 unidades na moeda de depósito, as fechará todas.


Complementando o conjunto de ferramentas

Tomaremos como base o projeto anterior do arquivo no final do artigo Conjunto de ferramentas para negociação manual rápida: funcionalidade básica. Em primeiro lugar, precisamos reconstruir a estrutura da janela principal de acordo com a Fig. 2 e para isso adicionamos o elemento de interface Tab, criando na classe base CProgram o método CreateTabs() e implementando-o no arquivo MainWindow.mqh.

//+------------------------------------------------------------------+
//| Create a group with tabs                                         |
//+------------------------------------------------------------------+
bool CFastTrading::CreateTabs(const int x_gap,const int y_gap)
{
//--- Store the pointer to the main control
   m_tab.MainPointer(m_main_window);
//--- Properties
   m_tab.Font(m_base_font);
   m_tab.FontSize(m_base_font_size);
   m_tab.LabelColor(clrWhite);
   m_tab.LabelColorHover(clrWhite);
   m_tab.IsCenterText(true);
   m_tab.AutoXResizeMode(true);
   m_tab.AutoYResizeMode(true);
   m_tab.AutoXResizeRightOffset(5);
   m_tab.AutoYResizeBottomOffset(5);
   m_tab.TabsYSize(27);
   m_tab.GetButtonsGroupPointer().Font(m_base_font);
   m_tab.GetButtonsGroupPointer().FontSize(m_base_font_size);
//--- Add tabs with the specified properties
   string tabs_names[3];
   tabs_names[0]=TRADING;
   tabs_names[1]=CAPTION_M_CONTROL_NAME;
   tabs_names[2]=CAPTION_P_CONTROL_NAME;
   for(int i=0; i<3; i++)
      m_tab.AddTab(tabs_names[i],180);
//--- Create a control element
   if(!m_tab.CreateTabs(x_gap,y_gap))
      return(false);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,m_tab);
   return(true);
}

No corpo do método criado, são introduzidas três novas substituições de macro que servem como nomes das guias, por isso, vamos adicioná-las em dois idiomas ao arquivo Defines.mqh:

#define TRADING                        (m_language==RUSSIAN ? "Трейдинг" : "Trading")
#define CAPTION_M_CONTROL_NAME         (m_language==RUSSIAN ? "Контроль рыночных позиций" : "Market Control")
#define CAPTION_P_CONTROL_NAME         (m_language==RUSSIAN ? "Контроль отложенных ордеров" : "Pending Control")

Antes de aplicar o método recém-criado, precisamos reconstruir os métodos para criar botões e atribuí-los à primeira guia. Para fazer isto, encontramos um método CreateButton() comum que os cria e o alteramos:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateButton(CButton &button,string text,color baseclr,int x_gap,int y_gap)
{
//--- Store the window pointer
   button.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(0,button);
//--- Set properties before creation
   button.XSize(180);
   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(0,button);
   return(true);
}

Agora aplicamos as alterações atuais ao método de criação da janela principal.

//+------------------------------------------------------------------+
//| 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(600);
   m_main_window.YSize(375);
//--- 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);
//--- Tabs
   if(!CreateTabs(5,22+27))
      return(false);
//---
   if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',20,10))
      return(false);
   if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',210,10))
      return(false);
   if(!CreateButton(m_order_button[2],MARKET_ORDERS_PROFIT_CLOSE+"(C)",C'87,128,255',20,60))
      return(false);
   if(!CreateButton(m_order_button[3],MARKET_ORDERS_LOSS_CLOSE+"(D)",C'87,128,255',20,110))
      return(false);
   if(!CreateButton(m_order_button[4],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',210,60))
      return(false);
//---
   return(true);
}

Como visto aqui, adicionadas guias primeiro, mais tarde, atualizamos as chamadas dos métodos que criam botões e redimensionamos o tamanho da janela principal. Após compilar o projeto, receberemos o resultado na forma de transferência de todos os botões para a primeira das três guias criadas:


Fig. 6 Criando guias e movendo os botões para a primeira delas

De acordo com o esquema da Fig. 5, criaremos botões e campos de entrada adicionais para implementar a funcionalidade acima mencionada. Se para botões grandes usamos o método atualizado CreateButton(), para criar os campos de entrada e interruptores é necessário inserir métodos adicionais: CreateModeButton() — botão seletor de modo, CreateModeEdit() — campo de entrada de valores. Sua implementação completa:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateModeButton(CButton &button,string text,int x_gap,int y_gap)
{
//--- Store the window pointer
   button.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(0,button);
   color baseclr=clrDarkViolet;
//--- Set properties before creation
   button.XSize(80);
   button.YSize(20);
   button.Font(m_base_font);
   button.FontSize(9);
   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(0,button);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateModeEdit(CTextEdit &text_edit,int x_gap,int y_gap)
{
//--- Store the window pointer
   text_edit.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(0,text_edit);
//--- Properties
   text_edit.XSize(80);
   text_edit.YSize(20);
   text_edit.Font(m_base_font);
   text_edit.FontSize(m_base_font_size);
   text_edit.SetDigits(2);
   text_edit.MaxValue(99999);
   text_edit.StepValue(0.01);
   text_edit.MinValue(0.01);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0.01));
   text_edit.IsLocked(true);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}

Agora, o método para criar a janela principal usando os métodos acima e adicionar botões da Fig. 5 ficará assim:

//+------------------------------------------------------------------+
//| 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(600);
   m_main_window.YSize(375);
//--- 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);
//--- Tabs
   if(!CreateTabs(5,22+27))
      return(false);
//---
   if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',10,10))
      return(false);
   if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',300,10))
      return(false);
   if(!CreateButton(m_order_button[2],CLOSE_ALL_PROFIT+"(C)",C'87,128,255',10,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[3],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',300,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[4],CLOSE_BUY_PROFIT,C'87,128,255',10,10+45*2))
      return(false);
   if(!CreateButton(m_order_button[5],CLOSE_SELL_PROFIT,C'87,128,255',10,10+45*3))
      return(false);
   if(!CreateButton(m_order_button[6],CLOSE_ALL_LOSS+"(D)",C'87,128,255',10,10+45*4))
      return(false);
   if(!CreateButton(m_order_button[7],CLOSE_BUY_LOSS,C'87,128,255',10,10+45*5))
      return(false);
   if(!CreateButton(m_order_button[8],CLOSE_SELL_LOSS,C'87,128,255',10,10+45*6))
      return(false);
//---
   for(int i=0; i<6; i++)
   {
      if(!CreateModeButton(m_mode_button[i],"all",205-10,10+45*(i+1)))
         return(false);
      if(!CreateModeEdit(m_mode_edit[i],204-10,30+45*(i+1)))
         return(false);
   }
//---
   return(true);
}

Aqui também foram introduzidas novas substituições de macro, por isso, adicionamos seus valores em ambos os idiomas para Defines.mqh:

#define CLOSE_BUY_PROFIT               (m_language==RUSSIAN ? "Fechar BUY lucrativos" : "Close BUY Profit")
#define CLOSE_SELL_PROFIT              (m_language==RUSSIAN ? "Fechar SELL lucrativos" : "Close SELL Profit")
#define CLOSE_ALL_PROFIT               (m_language==RUSSIAN ? "Fechar TODAS as lucrativas" : "Close ALL Profit")
#define CLOSE_BUY_LOSS                 (m_language==RUSSIAN ? "Fechar BUY não lucrativos" : "Close BUY Losing")
#define CLOSE_SELL_LOSS                (m_language==RUSSIAN ? "Fechar SELL não lucrativos" : "Close SELL Losing")
#define CLOSE_ALL_LOSS                 (m_language==RUSSIAN ? "Fechar TODAS as não lucrativas" : "Close ALL Losing")

Compilamos o projeto e obtemos o seguinte resultado intermediário:


Fig. 7 Adicionando botões e seletores de modo

No entanto, esta é apenas uma implementação visual, uma fachada. O próximo passo é atribuir a cada um dos elementos adicionados sua própria tarefa lógica. Antes de tudo, configuramos o mecanismo dos interruptores, porque todos os sucessores irão se referir aos seus valores e estados. Para objetos de botão, criamos um novo método ModeButtonSwitch(). Sua tarefa será alternar os modos pressionamento de botão.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::ModeButtonSwitch(CButton &button,long lparam,string &states[])
{
   if(lparam==button.Id())
   {
      int size=ArraySize(states);
      for(int i=0; i<size; i++)
      {
         if(button.LabelText()==states[i])
         {
            if(i==size-1)
            {
               SetButtonParam(button,states[0]);
               break;
            }
            else
            {
               SetButtonParam(button,states[i+1]);
               break;
            }
         }
      }
   }
}

Já o segundo método ModeEditSwitch() é necessário para que as configurações dos campos de entrada correspondam ao modo selecionado pelo botão. Por exemplo, os pontos são números inteiros, e, ao inserir um valor na moeda de depósito, são necessários números com 2 dígitos.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::ModeEditSwitch(long lparam,string &states[])
{
   for(int i=0; i<6; i++)
   {
      if(lparam==m_mode_button[i].Id())
      {
         if(m_mode_button[i].LabelText()==states[1])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(0);
            m_mode_edit[i].StepValue(1);
            m_mode_edit[i].MaxValue(9999);
            m_mode_edit[i].MinValue(1);
            m_mode_edit[i].SetValue(string(20));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=1;
         }
         else if(m_mode_button[i].LabelText()==states[2])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(2);
            m_mode_edit[i].StepValue(0.01);
            m_mode_edit[i].MaxValue(99999);
            m_mode_edit[i].MinValue(0.01);
            m_mode_edit[i].SetValue(string(0.1));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=2;
         }
         else if(m_mode_button[i].LabelText()==states[3])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(0);
            m_mode_edit[i].StepValue(1);
            m_mode_edit[i].MaxValue(9999);
            m_mode_edit[i].MinValue(1);
            m_mode_edit[i].SetValue(string(20));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=3;
         }
         else if(m_mode_button[i].LabelText()==states[4])
         {
            m_mode_edit[i].IsLocked(false);
            m_mode_edit[i].SetDigits(2);
            m_mode_edit[i].StepValue(0.01);
            m_mode_edit[i].MaxValue(99999);
            m_mode_edit[i].MinValue(0.01);
            m_mode_edit[i].SetValue(string(0.1));
            m_mode_edit[i].GetTextBoxPointer().Update(true);
            m_current_mode[i]=4;
         }
         else
         {
            m_mode_edit[i].IsLocked(true);
            m_current_mode[i]=0;
         }
      }
   }
}

Na implementação atual, foi introduzido um array estático m_current_mode com o tamanho correspondente ao número de interruptores de modos, ou seja, 6. Ele registra os modos selecionados pelo usuário para cada botão que fecha a posição. Para fazer com que tudo que acabamos de adicionar funcione, vamos ao nosso manipulador de eventos OnEvent() e na seção de eventos de pressionamento de botão, adicionamos o seguinte código:

      //---
      string states[5]= {"all",">points",">currency","sum>points","sum>currency"};
      for(int i=0; i<6; i++)
         ModeButtonSwitch(m_mode_button[i],lparam,states);
      //---
      ModeEditSwitch(lparam,states);

Compilamos o projeto. Agora podemos ver, na Fig. 8, que, ao alternar o modo, as propriedades dos campos de entrada também mudam.


Fig. 8 Alternando o modo de fechamento de posições a mercado

O próximo passo é implementar a lógica das ações de acordo com a descrição dos botões, vinculando-os aos modos introduzidos. Já temos duas ações, estas são "Fechar Todas lucrativas" e "Fechar Todas não lucrativas". No entanto, dada a introdução dos modos de fechamento, é necessário complementá-los. Essas ações correspondem aos métodos CloseAllMarketProfit() e CloseAllMarketLoss().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[0]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            int profit_pp;
            if(type==POSITION_TYPE_BUY)
               profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
            else
               profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
               {
                  switch(m_current_mode[0])
                  {
                  case  3:
                     sum_pp+=profit_pp;
                     break;
                  case  4:
                     sum_cur+=profit_cur+swap;
                     break;
                  }
               }
         }
      }
      //--- 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_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         int profit_pp;
         //---
         if(type==POSITION_TYPE_BUY)
            profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
         else
            profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
         //--- if MagicNumber matches
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
            {
               if(   (m_current_mode[0]==0 && profit_cur+swap>0) ||                                   // Close all profitable positions
                     (m_current_mode[0]==1 && profit_pp>int(m_mode_edit[0].GetValue())) ||            // Close all positions having profit more than N points
                     (m_current_mode[0]==2 && profit_cur+swap>double(m_mode_edit[0].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                     (m_current_mode[0]==3 && sum_pp>int(m_mode_edit[0].GetValue())) ||               // Close all positions with the total profit more than N points
                     (m_current_mode[0]==4 && sum_cur>double(m_mode_edit[0].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                 )
               {
                  //--- 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
                  bool res=true;
                  for(int j=0; j<5; j++)
                  {
                     res=OrderSend(request,result);
                     if(res && result.retcode==TRADE_RETCODE_DONE)
                        break;
                     else
                        PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                  }
               }
            }
      }
   }
}

Esta é uma modificação do método Fechamento de todas as posições a mercado. Como lembramos, anteriormente foi introduzido o array estático m_current_mode que controlava a seleção do modo para cada uma das ações no botão. Por isso, ele é calculado para os modos 4 e 5, que correspondem ao fechamento de todas as posições com base no lucro total em pontos ou na moeda de depósito. A seguir, é feita novamente uma seleção que pertence ao nosso conjunto de ferramentas e dependendo do modo de fechamento selecionado são verificadas as condições, e se estas últimas forem atendidas, todas as posições de mercado criadas pelo conjunto de ferramentas serão encerradas.

Por analogia, mudamos também o segundo método que fecha todas as posições não lucrativas:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseAllMarketLoss(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[6].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
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate losses
      if(m_current_mode[3]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double loss_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            int loss_pp;
            //---
            if(type==POSITION_TYPE_BUY)
               loss_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
            else
               loss_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
               {
                  switch(m_current_mode[3])
                  {
                  case  3:
                     sum_pp+=loss_pp;
                     break;
                  case  4:
                     sum_cur+=loss_cur+swap;
                     break;
                  }
               }
         }
      }
      //--- 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 loss_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         int loss_pp;
         if(type==POSITION_TYPE_BUY)
            loss_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
         else
            loss_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
         //--- if MagicNumber matches
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
            {
               if(   (m_current_mode[3]==0 && loss_cur+swap<0) ||
                     (m_current_mode[3]==1 && loss_pp<-int(m_mode_edit[3].GetValue())) ||
                     (m_current_mode[3]==2 && loss_cur+swap<-double(m_mode_edit[3].GetValue())) ||
                     (m_current_mode[3]==3 && sum_pp<-int(m_mode_edit[3].GetValue())) ||
                     (m_current_mode[3]==4 && sum_cur<-double(m_mode_edit[3].GetValue()))
                 )
               {
                  //--- 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
                  bool res=true;
                  for(int j=0; j<5; j++)
                  {
                     res=OrderSend(request,result);
                     if(res && result.retcode==TRADE_RETCODE_DONE)
                        break;
                     else
                        PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                  }
               }
            }
      }
   }
}

Além disso, para os modos de verificação do valor da perda de todas as posições abertas em pontos ou moeda de depósito, é realizado o cálculo e, em seguida, dependendo da modalidade de fechamento, são verificadas as condições de fechamento de todas as posições a mercado criadas pelo aplicativo.

Agora passamos para novas ações com posições abertas, isto é, Fechamento de posições Buy/Sell, lucrativas ou não. Na verdade, estes são casos especiais dos dois métodos descritos acima. Nos atuais, tudo será igual, exceto que será adicionada a filtragem por tipo. Para começar, vamos criar métodos cuja função será realizar as ações especificadas:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseBuyMarketProfit(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[4].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_U))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[1]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_BUY)
                  {
                     int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                     switch(m_current_mode[1])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- 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_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_BUY)
               {
                  int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                  if(   (m_current_mode[1]==0 && profit_cur+swap>0) ||                                   // Close all profitable positions
                        (m_current_mode[1]==1 && profit_pp>int(m_mode_edit[1].GetValue())) ||            // Close all positions having profit more than N points
                        (m_current_mode[1]==2 && profit_cur+swap>double(m_mode_edit[1].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                        (m_current_mode[1]==3 && sum_pp>int(m_mode_edit[1].GetValue())) ||               // Close all positions with the total profit more than N points
                        (m_current_mode[1]==4 && sum_cur>double(m_mode_edit[1].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                    )
                  {
                     //--- 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
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                     //--- sending a request
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                     }
                  }
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseBuyMarketLoss(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[7].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_H))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[4]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_BUY)
                  {
                     int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                     switch(m_current_mode[1])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- 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_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_BUY)
               {
                  int profit_pp=int((SymbolInfoDouble(position_symbol,SYMBOL_BID)-PositionGetDouble(POSITION_PRICE_OPEN))/_Point);
                  if(   (m_current_mode[4]==0 && profit_cur+swap<0) ||                                   // Close all profitable positions
                        (m_current_mode[4]==1 && profit_pp<-int(m_mode_edit[4].GetValue())) ||            // Close all positions having profit more than N points
                        (m_current_mode[4]==2 && profit_cur+swap<-double(m_mode_edit[4].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                        (m_current_mode[4]==3 && sum_pp<-int(m_mode_edit[4].GetValue())) ||               // Close all positions with the total profit more than N points
                        (m_current_mode[4]==4 && sum_cur<-double(m_mode_edit[4].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                    )
                  {
                     //--- 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
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
                     request.type =ORDER_TYPE_SELL;
                     //--- sending a request
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                     }
                  }
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseSellMarketProfit(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[5].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_J))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[2]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_SELL)
                  {
                     int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                     switch(m_current_mode[2])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- 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_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_SELL)
               {
                  int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                  if(   (m_current_mode[2]==0 && profit_cur+swap>0) ||                                   // Close all profitable positions
                        (m_current_mode[2]==1 && profit_pp>int(m_mode_edit[2].GetValue())) ||            // Close all positions having profit more than N points
                        (m_current_mode[2]==2 && profit_cur+swap>double(m_mode_edit[2].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                        (m_current_mode[2]==3 && sum_pp>int(m_mode_edit[2].GetValue())) ||               // Close all positions with the total profit more than N points
                        (m_current_mode[2]==4 && sum_cur>double(m_mode_edit[2].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                    )
                  {
                     //--- 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
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                     //--- sending a request
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                     }
                  }
               }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::CloseSellMarketLoss(int id,long lparam)
{
   if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[8].Id()) ||
         (id==CHARTEVENT_KEYDOWN && lparam==KEY_L))
   {
      //--- declare the request and the result
      MqlTradeRequest request;
      MqlTradeResult  result;
      int total=PositionsTotal(); // the number of open positions
      int sum_pp=0;
      double sum_cur=0.0;
      //--- Calculate profit for modes 4 and 5
      if(m_current_mode[5]>2)
      {
         for(int i=total-1; i>=0; i--)
         {
            //--- order parameters
            ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
            string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
            ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
            ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
            double profit_cur=PositionGetDouble(POSITION_PROFIT);
            double swap=PositionGetDouble(POSITION_SWAP);
            //---
            if(magic==m_magic_number)
               if(position_symbol==Symbol())
                  if(type==POSITION_TYPE_SELL)
                  {
                     int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                     switch(m_current_mode[1])
                     {
                     case  3:
                        sum_pp+=profit_pp;
                        break;
                     case  4:
                        sum_cur+=profit_cur+swap;
                        break;
                     }
                  }
         }
      }
      //--- 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_cur=PositionGetDouble(POSITION_PROFIT);
         double swap=PositionGetDouble(POSITION_SWAP);
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
               if(type==POSITION_TYPE_SELL)
               {
                  int profit_pp=int((PositionGetDouble(POSITION_PRICE_OPEN)-SymbolInfoDouble(position_symbol,SYMBOL_ASK))/_Point);
                  if(   (m_current_mode[5]==0 && profit_cur+swap<0) ||                                   // Close all profitable positions
                        (m_current_mode[5]==1 && profit_pp<-int(m_mode_edit[5].GetValue())) ||            // Close all positions having profit more than N points
                        (m_current_mode[5]==2 && profit_cur+swap<-double(m_mode_edit[5].GetValue())) ||   // Close all positions having profit more than N deposit currency units
                        (m_current_mode[5]==3 && sum_pp<-int(m_mode_edit[5].GetValue())) ||               // Close all positions with the total profit more than N points
                        (m_current_mode[5]==4 && sum_cur<-double(m_mode_edit[5].GetValue()))              // Close all positions with the total profit more than N deposit currency units
                    )
                  {
                     //--- 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
                     request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
                     request.type =ORDER_TYPE_BUY;
                     //--- sending a request
                     bool res=true;
                     for(int j=0; j<5; j++)
                     {
                        res=OrderSend(request,result);
                        if(res && result.retcode==TRADE_RETCODE_DONE)
                           break;
                        else
                           PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
                     }
                  }
               }
      }
   }
}

Após a criação e implementação, vamos chamá-los no corpo do manipulador de eventos OnEvent() fora de quaisquer seções.

//---
   CloseBuyMarketProfit(id,lparam);
   CloseSellMarketProfit(id,lparam);
   CloseBuyMarketLoss(id,lparam);
   CloseSellMarketLoss(id,lparam);

Para cada uma das ações criadas, além da de pressionar um botão, foram criados eventos ao pressionar teclas de atalho. No código, você pode remapear. Bem, para maior clareza, exibimos seus valores ao lado dos nomes das ações nos botões.

//---
   if(!CreateButton(m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',10,10))
      return(false);
   if(!CreateButton(m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',300,10))
      return(false);
   if(!CreateButton(m_order_button[2],CLOSE_ALL_PROFIT+"(C)",C'87,128,255',10,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[3],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',300,10+45*1))
      return(false);
   if(!CreateButton(m_order_button[4],CLOSE_BUY_PROFIT+"(U)",C'87,128,255',10,10+45*2))
      return(false);
   if(!CreateButton(m_order_button[5],CLOSE_SELL_PROFIT+"(J)",C'87,128,255',10,10+45*3))
      return(false);
   if(!CreateButton(m_order_button[6],CLOSE_ALL_LOSS+"(D)",C'87,128,255',10,10+45*4))
      return(false);
   if(!CreateButton(m_order_button[7],CLOSE_BUY_LOSS+"(H)",C'87,128,255',10,10+45*5))
      return(false);
   if(!CreateButton(m_order_button[8],CLOSE_SELL_LOSS+"(L)",C'87,128,255',10,10+45*6))
      return(false);

Isso resultará nas seguintes atribuições:


Fig. 9 Atribuição de teclas de atalho para ações adicionadas

Assim concluímos a funcionalidade da guia Negociação. Agora passamos para a próxima etapa, que consiste na criação de uma tabela de posições abertas pelo conjunto de ferramentas e na possibilidade de gerenciá-las na guia Controle de posições a mercado. A Fig. 3 no início do artigo mostra um esquema para a criação de elementos de interface, que consiste em três campos de entrada, dois botões e a tabela em si. Vamos começar a criá-los. Primeiro, criaremos três campos de entrada para editar o lote, Stop-Loss e Take-Profit de posições abertas. Eles serão os métodos CreateLotControl(),CreateStopLossControl(),CreateTakeProfitControl().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateLotControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,text_edit);
//--- 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(_Point);
   text_edit.SetDigits(_Digits);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateTakeProfitControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,text_edit);
//--- 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(_Point);
   text_edit.SetDigits(_Digits);
   text_edit.SpinEditMode(true);
   text_edit.GetTextBoxPointer().XGap(1);
   text_edit.GetTextBoxPointer().XSize(80);
//--- Create a control element
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
   text_edit.SetValue(string(0));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateStopLossControl(CTextEdit &text_edit,const int x_gap,const int y_gap,int tab)
{
//--- Store the pointer to the main control
   text_edit.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,text_edit);
//--- 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(_Point);
   text_edit.SetDigits(_Digits);
   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,y_gap))
      return(false);
   text_edit.SetValue(string(0));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}

E chamamos os novos métodos no corpo CreateMainWindow().

//--- Input field for editing open positions
   if(!CreateLotControl(m_lot_edit[6],375,3,1))
      return(false);
   if(!CreateStopLossControl(m_sl_edit[6],375+80,3,1))
      return(false);
   if(!CreateTakeProfitControl(m_tp_edit[6],375+83*2,3,1))
      return(false);

Para os botões Alterar e Fechar, também criamos os dois métodos novos CreateModifyButton() e CreateCloseButton() que os implementam.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateModifyButton(CButton &button,string text,int x_gap,int y_gap,int tab)
{
//--- Store the window pointer
   button.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,button);
   color baseclr=clrDarkOrange;
   color pressclr=clrOrange;
//--- 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);
//--- 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(0,button);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::CreateCloseButton(CButton &button,string text,int x_gap,int y_gap,int tab)
{
//--- Store the window pointer
   button.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(tab,button);
   color baseclr=clrCrimson;
   color pressclr=clrFireBrick;
//--- 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);
//--- 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(0,button);
   return(true);
}

Da mesma forma os adicionamos ao método de criação da janela principal:

//--- Position editing/closing buttons
   if(!CreateModifyButton(m_small_button[0],MODIFY,622,3,1))
      return(false);
   if(!CreateCloseButton(m_small_button[1],CLOSE,622+80,3,1))
      return(false);

Aqui há duas novas substituições de macro para localizar a interface, por isso, vamos para Defines.mqh e definimos seus valores:

#define MODIFY                         (m_language==RUSSIAN ? "Изменить" : "Modify")
#define CLOSE                          (m_language==RUSSIAN ? "Закрыть" : "Close")

Agora avançamos para a tabela em si. Para fazer isso, criamos o método CreatePositionsTable(), o implementamos e também adicionamos ao método da janela principal do aplicativo.

//+------------------------------------------------------------------+
//| Create a table of positions                                      |
//+------------------------------------------------------------------+
bool CFastTrading::CreatePositionsTable(CTable &table,const int x_gap,const int y_gap)
{
#define COLUMNS2_TOTAL 9
//--- Store the pointer to the main control
   table.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(1,table);
//--- Array of column widths
   int width[COLUMNS2_TOTAL];
   ::ArrayInitialize(width,80);
   width[0]=100;
   width[1]=110;
   width[2]=100;
   width[3]=60;
   width[6]=90;
//--- Array of text alignment in columns
   ENUM_ALIGN_MODE align[COLUMNS2_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
//--- Array of text offset along the X axis in the columns
   int text_x_offset[COLUMNS2_TOTAL];
   ::ArrayInitialize(text_x_offset,7);
//--- Array of column image offsets along the X axis
   int image_x_offset[COLUMNS2_TOTAL];
   ::ArrayInitialize(image_x_offset,3);
//--- Array of column image offsets along the Y axis
   int image_y_offset[COLUMNS2_TOTAL];
   ::ArrayInitialize(image_y_offset,2);
//--- Properties
   table.Font(m_base_font);
   table.FontSize(m_base_font_size);
   table.XSize(782);
   table.CellYSize(24);
   table.TableSize(COLUMNS2_TOTAL,1);
   table.TextAlign(align);
   table.ColumnsWidth(width);
   table.TextXOffset(text_x_offset);
   table.ImageXOffset(image_x_offset);
   table.ImageYOffset(image_y_offset);
   table.ShowHeaders(true);
   table.HeadersColor(C'87,128,255');
   table.HeadersColorHover(clrCornflowerBlue);
   table.HeadersTextColor(clrWhite);
   table.IsSortMode(false);
   table.LightsHover(true);
   table.SelectableRow(true);
   table.IsZebraFormatRows(clrWhiteSmoke);
   table.DataType(0,TYPE_LONG);
   table.AutoYResizeMode(true);
   table.AutoYResizeBottomOffset(5);
//--- Create a control element
   if(!table.CreateTable(x_gap,y_gap))
      return(false);
//--- Set the header titles
   table.SetHeaderText(0,TICKET);
   table.SetHeaderText(1,SYMBOL);
   table.SetHeaderText(2,TYPE_POS);
   table.SetHeaderText(3,PRICE);
   table.SetHeaderText(4,VOLUME);
   table.SetHeaderText(5,SL);
   table.SetHeaderText(6,TP);
   table.SetHeaderText(7,SWAP);
   table.SetHeaderText(8,PROFIT);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,table);
   return(true);
}

A tabela terá 9 colunas. Além disso, definimos nela a largura das colunas, uma vez que os nomes das colunas diferem em comprimento. E definimos os títulos das colunas aplicando localização, ou seja, para cada coluna criamos nossa própria substituição de macros no arquivo Defines.mqh.

#define SYMBOL                         (m_language==RUSSIAN ? "Символ" : "Symbol")
#define VOLUME                         (m_language==RUSSIAN ? "Объем" : "Volume")
#define TYPE_POS                       (m_language==RUSSIAN ? "Тип позиции" : "Position Type")
#define SWAP                           (m_language==RUSSIAN ? "Своп" : "Swap")
#define PROFIT                         (m_language==RUSSIAN ? "Прибыль" : "Profit")

No entanto, se compilarmos o projeto agora, veremos que a tabela, os botões e os campos de entrada ficarão fora da janela à direita. Por isso, é necessário adicionar um mecanismo que altere a largura da janela principal do aplicativo, a partir do preenchimento com elementos da interface. Para fazer isso, criamos o método WindowRezise().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::WindowResize(int x_size,int y_size)
{
   m_main_window.GetCloseButtonPointer().Hide();
   m_main_window.ChangeWindowWidth(x_size);
   m_main_window.ChangeWindowHeight(y_size);
   m_main_window.GetCloseButtonPointer().Show();
}

Já no manipulador de eventos criamos um novo evento ao clicar numa guia. Nele definimos a largura da janela principal para cada guia.

//--- Tab switching event
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_TAB)
   {
      if(m_tab.SelectedTab()==0)
         WindowResize(600,m_main_window.YSize());
      if(m_tab.SelectedTab()==1)
         WindowResize(782+10,m_main_window.YSize());
      if(m_tab.SelectedTab()==2)
         WindowResize(682+10,m_main_window.YSize());
   }

Agora tudo será mostrado corretamente, conforme a Fig. 3. O passo a seguir será obter informações sobre as posições abertas pelo aplicativo e exibi-las na tabela criada. Para fazer isso, devemos designar três estágios de preparação de dados e sua exibição na tabela:

Para inicialização, criamos a função InitializePositionsTable() e fazemos a seleção a partir de todas as posições abertas com base no símbolo atual e número mágico. Obtemos o número de posições que atentem às nossas condições. Adicionamos à tabela o mesmo número de linhas.

//+------------------------------------------------------------------+
//| Initializing the table of positions                              |
//+------------------------------------------------------------------+
void CFastTrading::InitializePositionsTable(void)
{
//--- Get symbols of open positions
   int total=PositionsTotal(); // the number of open positions
   int cnt=0;
//--- Delete all rows
   m_table_positions.DeleteAllRows();
//--- Set the number of rows equal to the number of positions
   for(int i=0; i<total; i++)
   {
      //--- order parameters
      ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
      //--- if MagicNumber matches
      if(magic==m_magic_number)
         if(position_symbol==Symbol())
         {
            m_table_positions.AddRow(cnt);
            cnt++;
         }
   }
//--- If there are positions
   if(cnt>0)
   {
      //--- Set the values in the table
      SetValuesToPositionsTable();
      //--- Update the table
      UpdatePositionsTable();
   }
}

A seguir, é verificado se foi encontrada pelo menos uma posição aberta pelo nosso conjunto de ferramentas. Se esse for o caso, estabelecemos as informações sobre as posições abertas nas linhas recém-criadas com ajuda do método SetValuePositionTable().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::SetValuesToPositionsTable(void)
{
//---
   int cnt=0;
   for(int i=PositionsTotal()-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 stoploss=PositionGetDouble(POSITION_SL);
      double takeprofit=PositionGetDouble(POSITION_TP);
      double swap=PositionGetDouble(POSITION_SWAP);
      double profit=PositionGetDouble(POSITION_PROFIT);
      double openprice=PositionGetDouble(POSITION_PRICE_OPEN);
      string pos=(type==POSITION_TYPE_BUY)?"BUY":"SELL";
      profit+=swap;
      //--- if MagicNumber matches
      if(magic==m_magic_number)
         if(position_symbol==Symbol())
         {
            m_table_positions.SetValue(0,i,string(position_ticket));
            m_table_positions.SetValue(1,i,string(position_symbol));
            m_table_positions.SetValue(2,i,pos);
            m_table_positions.SetValue(3,i,string(openprice));
            m_table_positions.SetValue(4,i,string(volume));
            m_table_positions.SetValue(5,i,string(stoploss));
            m_table_positions.SetValue(6,i,string(takeprofit));
            m_table_positions.SetValue(7,i,string(swap));
            m_table_positions.SetValue(8,i,DoubleToString(profit,2));
            //---
            m_table_positions.TextColor(2,i,(pos=="BUY")? clrForestGreen : clrCrimson);
            m_table_positions.TextColor(8,i,(profit!=0)? (profit>0)? clrForestGreen : clrCrimson : clrSilver);
            cnt++;
         }
   }
}

E após preencher os dados, atualizamos a tabela com ajuda de UpdatePositionsTable():

//+------------------------------------------------------------------+
//| Update the table of positions                                    |
//+------------------------------------------------------------------+
void CFastTrading::UpdatePositionsTable(void)
{
//--- Update the table
   m_table_positions.Update(true);
   m_table_positions.GetScrollVPointer().Update(true);
}

Para que as alterações feitas no projeto tenham efeito, é necessário configurá-la corretamente. Para fazer isto, vamos para o arquivo SimpleTrading.mq5 e na função OnInit() adicionamos a chamada do método de inicialização da classe do aplicativo:

//+------------------------------------------------------------------+
//| 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);
   }
   program.OnInitEvent();
//---
   return(INIT_SUCCEEDED);
}

Isso deve ser feito depois do método de criação da interface do aplicativo CreateGUI(). Agora vamos ao corpo de OnInitEvent() e chamamos a inicialização da nossa tabela. 

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
void CFastTrading::OnInitEvent(void)
{
   InitializePositionsTable();
}

Agora na tabela todas as informações sobre as posições abertas são exibidas corretamente. No entanto, estas informações são relevantes apenas no momento em que o aplicativo é inciado e devem ser atualizadas constantemente. Isso deve ser feito no manipulador de eventos de negociação OnTrade(), bem como na função OnTick(). Nos eventos de negociação, vamos acompanhar o número de posições abertas atuais e seus parâmetros, já no tick vamos atualizar as informações sobre o lucro atual de cada ordem.

Para fazer isso, criamos um evento na seção pública da classe base OnTradeEvent() e em seu corpo chamamos a inicialização de tabela. 

//+------------------------------------------------------------------+
//| Trade event                                                      |
//+------------------------------------------------------------------+
void CFastTrading::OnTradeEvent(void)
{
//--- If a new trade
      InitializePositionsTable();
}

No manipulador de eventos de negociação chamaremos o novo evento em si:

//+------------------------------------------------------------------+
//| Trade function                                                   |
//+------------------------------------------------------------------+
void OnTrade(void)
{
   program.OnTradeEvent();
}

Com essas ações que realizamos nós configuramos a exibição das posições abertas. Para atualizar o lucro na seção pública da classe CFastTrading criamos o método UpdatePositionProfit():

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::UpdatePositionProfit(void)
{
//---
   int cnt=0;
   for(int i=PositionsTotal()-1; i>=0; i--)
   {
      //--- order parameters
      ulong  position_ticket=PositionGetTicket(i);                                      // position ticket
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
      double swap=PositionGetDouble(POSITION_SWAP);
      double profit=PositionGetDouble(POSITION_PROFIT);
      profit+=swap;
      //--- if MagicNumber matches
      if(magic==m_magic_number)
         if(position_symbol==Symbol())
         {
            m_table_positions.SetValue(8,i,DoubleToString(profit,2));
            //---
            m_table_positions.TextColor(8,i,(profit!=0)? (profit>0)? clrForestGreen : clrCrimson : clrSilver);
            cnt++;
         }
   }
//---
   if(cnt>0)
      UpdatePositionsTable();
}

E o chamamos em OnTick():

void OnTick()
{
   program.UpdatePositionProfit();
}

Assim fica concluído o trabalho com a tabela, agora vamos gerar a possibilidade de editar e fechar posições abertas localizadas na lista atual. Para isso, fazemos com que ao clicar numa linha na tabela os campos de entrada exibam o lote, Take-Profit e Stop-Loss da posição selecionada para continuar a trabalhar com ela.

//--- Event of clicking on a table row
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
   {
      //--- Check the element ID
      if(lparam==m_table_positions.Id())
      {
         //---
         int row=m_table_positions.SelectedItem();
         m_lot_edit[6].SetValue(string(m_table_positions.GetValue(4,row)));
         m_sl_edit[6].SetValue(string(m_table_positions.GetValue(5,row)));
         m_tp_edit[6].SetValue(string(m_table_positions.GetValue(6,row)));
         m_lot_edit[6].GetTextBoxPointer().Update(true);
         m_sl_edit[6].GetTextBoxPointer().Update(true);
         m_tp_edit[6].GetTextBoxPointer().Update(true);
      }
   }

Compilamos o projeto e obtemos, como mostrado na Fig. 10, os valores da posição a mercado selecionada nos campos de entrada.


Fig. 10 Selecionando uma posição aberta para posterior edição.

A seguir, criamos os dois métodos ModifyPosition() e ClosePosition() que, após clicar no botão Alterar e Fechar, aplicarão a ação correspondente à posição aberta selecionada. 

//+------------------------------------------------------------------+
//| Modifying a selected open position                               |
//+------------------------------------------------------------------+
bool CFastTrading::ModifyPosition(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[0].Id())
   {
//--- Get index and symbol
      if(m_table_positions.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_positions.SelectedItem();
      ulong ticket=(ulong)m_table_positions.GetValue(0,row);
//---
      if(PositionSelectByTicket(ticket))
      {
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- calculation and rounding of the Stop Loss and Take Profit values
         double sl=NormalizeDouble((double)m_sl_edit[6].GetValue(),_Digits);
         double tp=NormalizeDouble((double)m_tp_edit[6].GetValue(),_Digits);
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- set the operation parameters
         request.action  =TRADE_ACTION_SLTP; // trading operation type
         request.position=ticket;            // position ticket
         request.symbol=Symbol();            // symbol
         request.sl      =sl;                // position Stop Loss
         request.tp      =tp;                // position Take Profit
         request.magic=m_magic_number;       // position MagicNumber
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::ClosePosition(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[1].Id())
   {
      //--- Get index and symbol
      if(m_table_positions.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_positions.SelectedItem();
      ulong ticket=(ulong)m_table_positions.GetValue(0,row);
//---
      if(PositionSelectByTicket(ticket))
      {
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);    // position type
         string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbol
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- 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 if(type==POSITION_TYPE_SELL)
         {
            request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
            request.type =ORDER_TYPE_BUY;
         }
         //--- check volume
         double position_volume=PositionGetDouble(POSITION_VOLUME);
         double closing_volume=(double)m_lot_edit[6].GetValue();
         if(closing_volume>position_volume)
            closing_volume=position_volume;
         //--- setting request
         request.action   =TRADE_ACTION_DEAL;
         request.position =ticket;
         request.symbol   =Symbol();
         request.volume   =NormalizeLot(Symbol(),closing_volume);
         request.magic    =m_magic_number;
         request.deviation=5;
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}

Assim concluímos a implementação de tarefas na guia Controle de posições a mercado. Vamos passar para o desenvolvimento da guia Controle de ordens pendentes. Para fazer isso, no final do corpo do método da janela principal CreateMainWindow(), adicionamos o código que acrescenta a mesma funcionalidade da guia anterior, nomeadamente a tabela de registro de ordens pendentes, botões e campos de entrada para serem editadas.

//--- Create a table of pending orders
   if(!CreateOrdersTable(m_table_orders,0,22+5))
      return(false);
//--- Input fields for editing pending orders
   if(!CreateLotControl(m_pr_edit[4],360,3,2))
      return(false);
   if(!CreateStopLossControl(m_sl_edit[7],360+80,3,2))
      return(false);
   if(!CreateTakeProfitControl(m_tp_edit[7],360+80*2,3,2))
      return(false);
//--- Pending order modifying/deleting orders
   if(!CreateModifyButton(m_small_button[2],MODIFY,361+80*3,3,2))
      return(false);
   if(!CreateCloseButton(m_small_button[3],REMOVE,361+80*3,3+24,2))
      return(false);

Este complemento tem um método novo que adiciona uma tabela para registro de ordens pendentes, bem como um nova substituição de macros para o botão que exclui a ordem selecionada. Vamos vem em mais detalhes como criar uma tabela:

//+------------------------------------------------------------------+
//| Creates a table of pending orders                                |
//+------------------------------------------------------------------+
//---
bool CFastTrading::CreateOrdersTable(CTable &table,const int x_gap,const int y_gap)
{
#define COLUMNS1_TOTAL 7
//--- Store the pointer to the main control
   table.MainPointer(m_tab);
//--- Attach to tab
   m_tab.AddToElementsArray(2,table);
//--- Array of column widths
   int width[COLUMNS1_TOTAL];
   ::ArrayInitialize(width,80);
   width[0]=100;
   width[2]=100;
//--- Array of text offset along the X axis in the columns
   int text_x_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(text_x_offset,7);
//--- Array of column image offsets along the X axis
   int image_x_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(image_x_offset,3);
//--- Array of column image offsets along the Y axis
   int image_y_offset[COLUMNS1_TOTAL];
   ::ArrayInitialize(image_y_offset,2);
//--- Array of text alignment in columns
   ENUM_ALIGN_MODE align[COLUMNS1_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
   align[6]=ALIGN_LEFT;
//--- Properties
   table.Font(m_base_font);
   table.FontSize(m_base_font_size);
   table.XSize(602);
   table.CellYSize(24);
   table.TableSize(COLUMNS1_TOTAL,1);
   table.TextAlign(align);
   table.ColumnsWidth(width);
   table.TextXOffset(text_x_offset);
   table.ImageXOffset(image_x_offset);
   table.ImageYOffset(image_x_offset);
   table.ShowHeaders(true);
   table.HeadersColor(C'87,128,255');
   table.HeadersColorHover(clrCornflowerBlue);
   table.HeadersTextColor(clrWhite);
   table.IsSortMode(false);
   table.LightsHover(true);
   table.SelectableRow(true);
   table.IsZebraFormatRows(clrWhiteSmoke);
   table.AutoYResizeMode(true);
   table.AutoYResizeBottomOffset(5);
   table.DataType(0,TYPE_LONG);
   table.DataType(1,TYPE_STRING);
   table.DataType(2,TYPE_STRING);
   table.DataType(3,TYPE_DOUBLE);
   table.DataType(4,TYPE_DOUBLE);
   table.DataType(5,TYPE_DOUBLE);
//--- Create a control element
   if(!table.CreateTable(x_gap,y_gap))
      return(false);
//--- Set the header titles
   table.SetHeaderText(0,TICKET);
   table.SetHeaderText(1,SYMBOL);
   table.SetHeaderText(2,TYPE_POS);
   table.SetHeaderText(3,VOLUME);
   table.SetHeaderText(4,PRICE);
   table.SetHeaderText(5,SL);
   table.SetHeaderText(6,TP);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,table);
   return(true);
}

Aqui, o número de colunas e sua ordem mudam. Porque ao editar posições abertas a mercado, podemos alterar o lote e os valores de Take-Profit e Stop-Loss. No caso de ordens pendentes, aqui deve ser editado o preço de abertura em vez do lote. Montamos o projeto e obtemos a seguinte visualização na guia Controle de ordens pendentes:


Fig. 11 Interface para trabalhar com ordens pendentes.

A criação da guia anterior é parecida com o que temos pela frente. Primeiro, encontramos todas as ordens que pertencem ao nosso conjunto de ferramentas com ajuda de InitializeOrdersTable():

//+------------------------------------------------------------------+
//| Initializing the table of positions                              |
//+------------------------------------------------------------------+
void CFastTrading::InitializeOrdersTable(void)
{
//---
   int total=OrdersTotal();
   int cnt=0;
//--- Delete all rows
   m_table_orders.DeleteAllRows();
//--- Set the number of rows equal to the number of positions
   for(int i=0; i<total; i++)
   {
      //--- order parameters
      ulong  order_ticket=OrderGetTicket(i);                                   // order ticket
      string order_symbol=OrderGetString(ORDER_SYMBOL);                        // symbol
      ulong  magic=OrderGetInteger(ORDER_MAGIC);                               // position MagicNumber
      //--- if MagicNumber matches
      if(magic==m_magic_number)
         if(order_symbol==Symbol())
         {
            m_table_orders.AddRow(cnt);
            cnt++;
         }
   }
//--- If there are positions
   if(cnt>0)
   {
      //--- Set the values in the table
      SetValuesToOrderTable();
      //--- Update the table
      UpdateOrdersTable();
   }
}

Se forem encontradas ordens pendentes, inserimos informações sobre elas na tabela através do método SetValuesToOrderTable():

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::SetValuesToOrderTable(void)
{
//---
   int cnt=0;
   ulong    ticket;
   for(int i=0; i<OrdersTotal(); i++)
   {
      //--- order parameters
      if((ticket=OrderGetTicket(i))>0)
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // symbol
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // order MagicNumber
         double volume=OrderGetDouble(ORDER_VOLUME_INITIAL);                           // order volume
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // order type
         double price=OrderGetDouble(ORDER_PRICE_OPEN);
         double stoploss=OrderGetDouble(ORDER_SL);
         double takeprofit=OrderGetDouble(ORDER_TP);
         string pos="";
         if(type==ORDER_TYPE_BUY_LIMIT)
            pos="Buy Limit";
         else if(type==ORDER_TYPE_SELL_LIMIT)
            pos="Sell Limit";
         else if(type==ORDER_TYPE_BUY_STOP)
            pos="Buy Stop";
         else if(type==ORDER_TYPE_SELL_STOP)
            pos="Sell Stop";
         //---
         if(magic==m_magic_number)
            if(position_symbol==Symbol())
            {
               m_table_orders.SetValue(0,i,string(ticket));
               m_table_orders.SetValue(1,i,string(position_symbol));
               m_table_orders.SetValue(2,i,pos);
               m_table_orders.SetValue(3,i,string(volume));
               m_table_orders.SetValue(4,i,string(price));
               m_table_orders.SetValue(5,i,string(stoploss));
               m_table_orders.SetValue(6,i,string(takeprofit));
               cnt++;
            }
      }
   }
}

E atualizamos os dados definidos por meio do método UpdateOrdersTable():

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CFastTrading::UpdateOrdersTable(void)
{
//--- Update the table
   m_table_orders.Update(true);
   m_table_orders.GetScrollVPointer().Update(true);
}

Para anexar tudo isso ao aplicativo, procedemos de forma semelhante às ações da guia anterior, nomeadamente inicializamos a tabela com ordens pendentes:

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
void CFastTrading::OnInitEvent(void)
{
   InitializeOrdersTable();
   InitializePositionsTable();
}

E repetimos a mesma coisa no manipulador de eventos de negociação:

//+------------------------------------------------------------------+
//| Trade event                                                      |
//+------------------------------------------------------------------+
void CFastTrading::OnTradeEvent(void)
{
//--- 
   InitializePositionsTable();
   InitializeOrdersTable();
}

Para que, ao clicar numa linha da tabela, os parâmetros da ordem pendente sejam transferidos para os campos de entrada adicionamos código na seção correspondente do manipulador de eventos:

//--- Event of clicking on a table row
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
   {
      //--- Check the element ID
      if(lparam==m_table_positions.Id())
      {
         //---
         int row=m_table_positions.SelectedItem();
         m_lot_edit[6].SetValue(string(m_table_positions.GetValue(4,row)));
         m_sl_edit[6].SetValue(string(m_table_positions.GetValue(5,row)));
         m_tp_edit[6].SetValue(string(m_table_positions.GetValue(6,row)));
         m_lot_edit[6].GetTextBoxPointer().Update(true);
         m_sl_edit[6].GetTextBoxPointer().Update(true);
         m_tp_edit[6].GetTextBoxPointer().Update(true);
      }
      //--- Check the element ID
      if(lparam==m_table_orders.Id())
      {
         //---
         int row=m_table_orders.SelectedItem();
         m_pr_edit[4].SetValue(string(m_table_orders.GetValue(4,row)));
         m_sl_edit[7].SetValue(string(m_table_orders.GetValue(5,row)));
         m_tp_edit[7].SetValue(string(m_table_orders.GetValue(6,row)));
         m_pr_edit[4].GetTextBoxPointer().Update(true);
         m_sl_edit[7].GetTextBoxPointer().Update(true);
         m_tp_edit[7].GetTextBoxPointer().Update(true);
      }
   }

A última coisa que nos resta fazer é atribuir as ações necessárias aos botões Alterar e Excluir. Para fazer isso, criamos os métodos ModifyOrder() e RemoveOrder()

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::ModifyOrder(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[2].Id())
   {
      //--- Get index and symbol
      if(m_table_orders.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_orders.SelectedItem();
      ulong ticket=(ulong)m_table_orders.GetValue(0,row);
      //---
      if(OrderSelect(ticket))
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // symbol
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // order MagicNumber
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // order type
         //--- calculation and rounding of the Stop Loss and Take Profit values
         double sl=NormalizeDouble((double)m_sl_edit[7].GetValue(),_Digits);
         double tp=NormalizeDouble((double)m_tp_edit[7].GetValue(),_Digits);
         double price=NormalizeDouble((double)m_pr_edit[4].GetValue(),_Digits);
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- set the operation parameters
         request.action=TRADE_ACTION_MODIFY; // trading operation type
         request.order = ticket;             // order ticket
         request.symbol=Symbol();            // symbol
         request.sl      =sl;                // position Stop Loss
         request.tp      =tp;                // position Take Profit
         request.price=price;                // new price
         request.magic=m_magic_number;       // position MagicNumber
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CFastTrading::RemoveOrder(long lparam)
{
//--- Check the element ID
   if(lparam==m_small_button[3].Id())
   {
      //--- Get index and symbol
      if(m_table_orders.SelectedItem()==WRONG_VALUE)
         return(false);
      int row=m_table_orders.SelectedItem();
      ulong ticket=(ulong)m_table_orders.GetValue(0,row);
      //---
      if(OrderSelect(ticket))
      {
         string position_symbol=OrderGetString(ORDER_SYMBOL);                          // symbol
         ulong  magic=OrderGetInteger(ORDER_MAGIC);                                    // order MagicNumber
         ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);            // order type
         //--- declare the request and the result
         MqlTradeRequest request;
         MqlTradeResult  result;
         //--- zeroing the request and result values
         ZeroMemory(request);
         ZeroMemory(result);
         //--- set the operation parameters
         request.action=TRADE_ACTION_REMOVE;             // trading operation type
         request.order = ticket;                         // order ticket
         //--- sending a request
         bool res=true;
         for(int j=0; j<5; j++)
         {
            res=OrderSend(request,result);
            if(res && result.retcode==TRADE_RETCODE_DONE)
               return(true);
            else
               PrintFormat("OrderSend error %d",GetLastError());  // if unable to send the request, output the error code
         }
      }
   }
//---
   return(false);
}

Vamos chamá-los num manipulador de eventos fora de qualquer seção:

      //---
      if(ModifyOrder(lparam))
         return;
      if(RemoveOrder(lparam))
         return;

No final do artigo gostaria de acrescentar mais algo que facilite trabalhar com ordens pendentes. Isto é, possibilidade de definir o preço de abertura de uma ordem pendente pré-selecionada clicando no gráfico. Para uma melhor compreensão, esta ação é ilustrada na Fig. 12 abaixo:


Fig. 12 Definição do preço de abertura de uma ordem pendente.

O procedimento é o seguinte: 

Desta forma, podemos definir de forma muito rápida e conveniente o valor desejado do preço da ordem pendente, se for preciso fazer isso à vista. Para uma configuração mais precisa, inserimos o valor do preço no campo de entrada com o teclado. A implementação desta ação é bastante simples. No manipulador de eventos da classe base, criamos uma seção com um evento click no gráfico e escrevemos o seguinte código nele:

//--- The event of clicking on the chart
   if(id==CHARTEVENT_CLICK)
   {
      for(int i=0; i<4; i++)
      {
         if(m_pr_edit[i].GetTextBoxPointer().TextEditState())
         {
            m_last_index=i;
            break;
         }
         else
         {
            if(m_last_index>=0)
            {
               //---
               datetime dt    =0;
               int      window=0;
               //--- convert X and Y coordinates to date/time
               if(ChartXYToTimePrice(0,(int)lparam,(int)dparam,window,dt,m_xy_price))
               {
                  m_pr_edit[m_last_index].SetValue(DoubleToString(m_xy_price));
                  m_pr_edit[m_last_index].GetTextBoxPointer().Update(true);
                  m_last_index=-1;
               }
            }
         }
      }
   }

Nele determinamos qual dos campos deve ser editado, lembremos que, ao clicar no gráfico, obtemos o preço desejado e o inserimos no campo de entrada recém-selecionado. As principais novidades podem ser conferidas 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