English Русский 中文 Español Deutsch 日本語
Expert Advisor multiplataforma: Gerenciador de ordens

Expert Advisor multiplataforma: Gerenciador de ordens

MetaTrader 5Integração | 18 julho 2017, 09:45
5 164 0
Enrico Lambino
Enrico Lambino

Tabela de conteúdos

Introdução

Como discutido em artigos anteriores desta série (1, 2, 3), MetaTrader 4 e MetaTrader 5 têm algumas diferenças que complicam colar de maneira simples o arquivo-fonte MQL4 e seu processamento realizado pelo compilador MQL5. Uma das diferenças mais óbvias entre as plataformas é a maneira em que elas realizam operações de negociação. Neste artigo é discutida a criação da classe COrderManager. Sozinho ou em conjunto com outras classes auxiliares, ele é que iria assumir a execução das operações de negociação, bem como a assistência a transações em que o Expert Advisor tenha entrado.


Objetivos

O gerenciador de ordens neste artigo será capaz de executar as seguintes operações:

  1. Cálculo do tamanho do lote
  2. Stop Loss e Take Profit
  3. Parâmetros variados necessários para a entrada numa transação (tempo de expiração, comentários, Magic de ordens)
  4. Algumas pré-condições antes de enviar a ordem
  5. Gerenciamento de ordens e histórico de ordens

Muitas vezes é melhor delegar a parte responsável pelo cálculo de lotes ao campo do objeto, pois há muitas maneiras de calcular o tamanho ideal do lote da seguinte transação (dependendo da estratégia de negociação envolvida). O mesmo pode ser dito sobre o cálculo dos níveis de stop-loss e take-profit.

Os vários parâmetros necessários para a abertura de uma ordem, tais como a data de expiração, comentários e mágico, exigem menos complexidade e é por isso que são processados com sucesso pelo gerenciador de ordens sozinho.

Antes de o Expert Advisor abrir uma transação no mercado, é necessário que sejam estabelecidas certas condições prévias. Estas incluem sinais de negociação com base em condições de mercado, restrições de tempo, bem como o número máximo de transações ativas num dado momento e o número máximo de transações a serem abertas durante todo o período de trabalho do Expert Advisor (entre OnInit e OnDeinit). Para o gerenciador de ordens, as duas últimas condições serão incluídas como condição antes de uma ordem ser finalmente enviada ao mercado. Isso será implementado para que transações duplicadas não ocorram durante a operação do Expert Advisor. Por outro lado, as outras condições prévias podem ser delegadas a determinados componentes externos ao gerenciador de ordens.


Implementação básica

Semelhante a outros objetos de classe discutidos nesta série de artigos, é necessário encontrar um terreno comum entre MQL5 e MQL4 e implementá-lo na classe base, enquanto as partes onde a implementação diverge devem ser implementadas em descendentes específicos da classe base. Ao desenvolver a classe base, temos que entender as seguintes áreas no processamento de pedidos de negociação:

  1. Como o envio de solicitações de negociação é diferente
  2. Como a documentação das atividades de negociação é diferente
  3. Existem alguns recursos na linguagno MQL5 que não possuem equivalentes na MQL4

Há algumas coisas que são diferentes no MQL4 e MQL5. Consideremos a função OrderSend (mql4, mql5), como mostrado na documentação para ambas as plataformas:

(MQL4)

int  OrderSend(
   string   symbol,              // símbolo
   int      cmd,                 // operação
   double   volume,              // volume
   double   price,               // preço
   int      slippage,            // slippage
   double   stoploss,            // stop loss
   double   takeprofit,          // ake profit
   string   comment=NULL,        // comentário
   int      magic=0,             // número mágico
   datetime expiration=0,        // data de expiração da ordem pendente
   color    arrow_color=clrNONE  // cor
   );

(MQL5)

bool  OrderSend(
   MqlTradeRequest&  request,      // estrutura de solicitação
   MqlTradeResult&   result        // estrutura da resposta
   );

A função no MQL4 tem uma abordagem mais direta. A função no MQL5 é um pouco mais complicada, mas reduz o número de parâmetros para apenas dois, que contêm os dados (struct) para a solicitação e o resultado, respectivamente. Este obstáculo foi amplamente abordado num artigo anterior sobre a importação de certos componentes da biblioteca padrão MQL5 para MQL4, em particular as classes CExpertTrade e CExpertTradeX. Assim, o gerenciador de ordens simplesmente utiliza essas classes para garantir a compatibilidade entre as duas línguas ao realizar solicitações de negociação.

Outro aspecto é a forma como é processada a saída da transação ou o cancelamento de uma ordem no MetaTrader 4 e no MetaTrader 5. Embora não haja muita diferença na forma como as ordens pendentes são excluídas, existe uma grande diferença na forma como as ordens de mercado (MQL4) ou as posições (MQL5) são retiradas do mercado. No MQL4, o fechamento de uma ordem de mercado é realizado chamando a função OrderClose. No MetaTrader 5, o mesmo efeito é atingido chamando a função PositionClose ou enviando uma solicitação de negociação com o mesmo volume que a posição atual e oposta à posição atual.

No MetaTrader 5, todas as ações comerciais estão documentadas. Independentemente do fato de uma ação ser uma entrada no mercado, modificação ou saída, essa ação deixa uma pegada, e os dados relativos a essas ações são acessíveis ao Expert Advisor. No MetaTrader 4, não é assim. Por exemplo, quando uma ID de bilhete é atribuída a uma ordem pendente, a mesma identificação é frequentemente usada durante toda a vida dessa ordem, mesmo que tenha atingido seu preço de gatilho e se torne uma ordem de mercado, antes de ter deixado o mercado. Para ver a progressão total de uma determinada transação, é preciso examinar o diário do Expert Advisor, o que podem ser uma tarefa demorada. Além disso, os arquivos de registro devem ser legíveis pelos humanos, e não há funções MQL4 integradas que tornem mais fácil para um Expert Advisor acessar essas informações.

Existem alguns recursos no MQL5 que simplesmente não estão disponíveis no MQL4. Um exemplo disso é o tipo de preenchimento de ordem. O MQL5 possui as seguintes opções de preenchimento de volume solicitado para uma ordem:

  • ORDER_FILLING_FOK — quando o volume solicitado não puder ser preenchido, a ordem será cancelada
  • ORDER_FILLING_IOC — quando o volume solicitado não puder ser preenchido, será usado o volume máximo disponível e cancelado o volume restante.
  • ORDER_FILLING_RETURN — quando o volume solicitado não puder ser preenchido, será usado o volume máximo disponível. A ordem com o volume restante permanece no mercado.

No MetaTrader 4, uma solicitação de negociação é simplesmente preenchida ou não (cancelada), o que é essencialmente equivalente a ORDER_FILLING_FOK, enquanto as outras duas opções de preenchimento não estão disponíveis.

No entanto, essas políticas de preenchimento só são implementadas sempre que o volume solicitado excede o volume disponível no mercado, o que não acontece com frequência especialmente para configurações com baixo risco e/ou baixo saldo da conta. ORDER_FILLING_IOC e ORDER_FILLING_RETURN podem ser difíceis, se não impossíveis ou impraticáveis, de implementar no MQL4, principalmente porque os EAs não têm nenhum meio para determinar a quantidade de volume disponível para uma determinada solicitação de negociação (e, se assim fosse, essas informações podem ser altamente voláteis e frequentemente sujeitas a mudanças).

Assim, para garantir a compatibilidade entre MQL4 e MQL5, ORDER_FILLING_FOK será a única opção de preenchimento utilizada (o que também é o padrão no MetaTrader 5). Enquanto isso, há eventos em que um EA calcula o tamanho do lote para uma solicitação de negociação que excede SYMBOL_VOLUME_MAX, que é o volume máximo permitido para qualquer transação definida pela corretora. O MetaTrader 5 aborda isso dividindo automaticamente a transação em várias negociações separadas, mas esse recurso não está disponível no MetaTrader 4 (causando o cancelamento da solicitação de negociação). Assim, é melhor para um EA multiplataforma verificar se a solicitação corresponde ao volume máximo disponível antecipadamente (de preferência após receber ou calcular o volume para a transação) antes de enviar uma solicitação de negociação para entrada no mercado usando o gerenciador de ordens.

A figura a seguir ilustra amplamente como o gerenciador de ordens implementará a entrada para a transação:

Diagrama de entrada

Conforme mostrado na figura, o método começa preparando os dados necessários para a operação. Se a posição estiver disponível para abertura e as condições prévias forem satisfeitas, o método prosseguirá com a abertura da ordem. Caso contrário, o processo é finalizado. Antes de enviar a solicitação de negociação, os valores exigidos devem ser calculados. Se a solicitação for confirmada, o resultado será verificado e uma nova instância COrder será criada, que será então adicionada à lista de ordens/posições atuais (m_orders).

Métodos que apenas executam cálculos puros podem ser encontrados dentro da classe base. Os métodos que chamam funções, que são diferentes entre as duas línguas, estendem os métodos base para suas respectivas classes específicas de linguagem. Com este método, no entanto, há muito pouca diferença em como a operação deve ser realizada. Assim, o método da classe base é puramente virtual e as implementações podem ser encontradas separadamente entre as duas versões. Encontraremos a implementação do método da classe base da seguinte forma:

COrder* COrderManagerBase::TradeOpen(const string,ENUM_ORDER_TYPE)
  {
   return NULL;
  }

Cálculo do volume de negociação

Como mencionado anteriormente, o cálculo do volume de negociação para a próxima transação é melhor delegado a outro objeto de classe, que será um membro da classe do gerenciador de ordens. Esta abordagem também é usada na biblioteca EAs da Biblioteca padrão MQL5. Aqui está o código para o método LotSizeCalculate, que é o método responsável pelo cálculo do volume da próxima transação:

double COrderManagerBase::LotSizeCalculate(const double price,const ENUM_ORDER_TYPE type,const double stoploss)
  {
   if(CheckPointer(m_moneys))
      return m_moneys.Volume(m_symbol.Name(),0,type,stoploss);
   return m_lotsize;
  }

O método verifica a ponteiro para a instância CMoneys, que é apenas um recipiente para os objetos de gerenciamento de dinheiro usados ​​pelo gerenciador de solicitações (da mesma forma que o COrders é um recipiente para instâncias COrder). Esses objetos de gerenciamento do dinheiro serão discutidos num artigo separado. Neste ponto, pelo menos, basta saber que existe um componente separado que lida com o cálculo de lote, e que o lote calculado será válido. No caso de nenhuma instância de gerenciamento de dinheiro ter sido fornecida ao gerenciador de solicitações, ele simplesmente usará o tamanho do lote padrão por meio de seu membro de classe, m_lotsize.

Cálculo do Stop-Loss e Take-Profit

O cálculo de Stoploss e Takeprofit é conseguido através dos métodos StopLossCalculate e TakeProfitCalculate, respectivamente. Os seguintes trechos de código mostram como cada um desses métodos é implementado no gerenciador de ordens:

double COrderManagerBase::StopLossCalculate(const ENUM_ORDER_TYPE type,const double price)
  {
   if(CheckPointer(m_main_stop))
      return m_main_stop.StopLossTicks(type,price);
   return 0;
  }
double COrderManagerBase::TakeProfitCalculate(const ENUM_ORDER_TYPE type,const double price)
  {
   if(CheckPointer(m_main_stop))
      return m_main_stop.TakeProfitTicks(type,price);
   return 0;
  }

O cálculo dos níveis stop é delegado a um objeto de classe separado, que será um membro do gerenciador de ordens (isso será discutido em outro artigo). O objeto stop também terá suas implementações separadas para MetaTrader 4 e MetaTrader 5. No entanto, no caso de nenhum ponteiro para um objeto stop ser fornecido ao gerenciador de ordens, o Stoploss ou Takeprofit calculados serão por padrão iguais a zero (sem SL/TP).

Fechamento da ordem ou posição

A figura a seguir ilustra como o gerenciador de ordens fechará posições e excluirá ordens:

diagrama para saída

Conforme mostrado na figura, o método primeiro verifica se o ponteiro para uma instância de COrder é válido. Então, ele procede a obter as instâncias corretas de objetos e transações, que são necessários para processar a saída da solicitação. Em seguida, ele exclui ou fecha a ordem, dependendo de seu tipo. Após um encerramento/exclusão bem-sucedido da ordem, a instância COrder é movida da lista de ordens ativas para a lista do histórico do gerenciador de ordens (arquivado). O método também define os sinalizadores que marcam o objeto como fechado.

Validação de configurações

A validação das configurações no gerenciador de ordens será alcançada chamando o método Validate da classe. O código para este método está mostrado abaixo:

bool COrderManagerBase::Validate(void) const
  {
   if(CheckPointer(m_moneys)==POINTER_DYNAMIC)
     {
      if(!m_moneys.Validate())
         return false;
     }
   if(CheckPointer(m_stops)==POINTER_DYNAMIC)
     {
      if(!m_stops.Validate())
         return false;
     }
   return true;
  }

Este código é semelhante ao método ValidationSettings que é frequentemente encontrado em algumas classes dentro da Biblioteca Padrão MQL5. Ele simplesmente chama os métodos Validate de seus membros de objeto e retorna false sempre que um desses objetos falha na validação (finalmente, a função OnInit do Expert Advisor falha ou retorna INIT_FAILED). O método deve ser chamado durante a execução da função de inicialização do Expert Advisor.

Contagem do número de transações

O total de transações é o número total de operações em que o gerenciador de ordens tem entrado até o momento, incluindo aquelas que já estão no histórico, desde que o EA ou script foi executado. O total de transações refere-se ao número de trades atuais na conta, enquanto o histórico de ordens total refere-se aos encontrados no próprio registro do histórico do gerenciador de ordens. Assim:

int COrderManagerBase::OrdersTotal(void) const
  {
   return m_orders.Total();
  }
int COrderManagerBase::OrdersHistoryTotal(void) const
  {
   return m_orders_history.Total();
  }
int COrderManagerBase::TradesTotal(void) const
  {
   return m_orders.Total()+m_orders_history.Total()+m_history_count;
  }
Em ambos os casos, usamos o conceito padrão do MetaTrader 4 ao contar ordens (cada ordem é uma posição, seja no mercado ou pendente). Por exemplo, na implementação do MQL4, veríamos as duas primeiras cadeias de caracteres do método TradeOpen da seguinte maneira:
int trades_total =TradesTotal();
int orders_total = OrdersTotal();

É importante notar que aqui OrdersTotal se refere ao método nativo da classe, não à função OrderTotal encontrada no MQL4 e MQL5. Se quisermos usar as funções nativas das linguagens, OrderTotal deve ser chamado usando o operador de resolução de escopo que precede o nome da função:

int orders_total = ::OrdersTotal();

Arquivamento da instância COrder

Como o gerenciador de ordens manterá sua própria lista independente de transações abertas, semelhante como em MetaTrader 4 e MetaTrader 5 (mas compatível com ambos), tem que ter uma maneira de sinalizar instâncias COrder como já inseridas no histórico. As ordens são armazenadas em m_orders ou m_orders_history - que são instâncias da classe COrders - dependendo se já são históricos ou ainda estão ativas no mercado. Como resultado, o Expert Advisor multiplataforma para ambas as versões terá de verificar se a transação ou a ordem foi fechada ou não.

Uma vez que as duas plataformas diferem na forma como documentam as transações introduzidas no mercado, o gerenciador de ordens precisará manter o seu próprio registo independente de transações introduzidas. Após uma abertura bem-sucedida de uma ordem, uma instância COrder será criada, ela será adicionada à m_orders. Assim que a ordem ou a posição sair do mercado, o gerente de ordens terá que mover a instância COrder para m_orders_history. O método ArchiveOrder da classe, que será usado para ambas as versões, é mostrado abaixo:

bool COrderManagerBase::ArchiveOrder(COrder *order)
  {
   return m_orders_history.Add(order);
  }

Implementação específica MQL4

Abertura da ordem ou posição

O seguinte fragmento de código mostra o método TradeOpen do descendente MQL4 específico da classe CorderManagerBase:

COrder* COrderManager::TradeOpen(const string symbol,ENUM_ORDER_TYPE type)
  {
   int trades_total = TradesTotal();
   int orders_total = OrdersTotal();
   m_symbol = m_symbol_man.Get(symbol);
   if (!CheckPointer(m_symbol))
      return NULL;
   if(!IsPositionAllowed(type))
      return NULL;
   if(m_max_orders>orders_total && (m_max_trades>trades_total || m_max_trades<=0))
     {
      ENUM_ORDER_TYPE ordertype = type;
      double price=PriceCalculate(ordertype);
      double sl=0,tp=0;
      if(CheckPointer(m_main_stop)==POINTER_DYNAMIC)
        {
         sl = m_main_stop.StopLossCustom()?m_main_stop.StopLossCustom(symbol,type,price):m_main_stop.StopLossCalculate(symbol,type,price);
         tp = m_main_stop.TakeProfitCustom()?m_main_stop.TakeProfitCustom(symbol,type,price):m_main_stop.TakeProfitCalculate(symbol,type,price);
        }
      double lotsize=LotSizeCalculate(price,type,sl);
      ulong ticket = SendOrder(type,lotsize,price,sl,tp);
      if (ticket>0)
      {
         if (OrderSelect((int)ticket,SELECT_BY_TICKET))
            return m_orders.NewOrder(OrderTicket(),OrderSymbol(),OrderMagicNumber(),(ENUM_ORDER_TYPE)::OrderType(),::OrderLots(),::OrderOpenPrice());
      }            
     }
   return NULL;
  }

A função aceita dois parâmetros, isto é, o nome do símbolo ou instrumento e o tipo de ordem a ser aberta. Começa obtendo os valores necessários para o processamento da solicitação: o total de transações, o total de ordens e o objeto de símbolo para a ordem (como indicado pelo primeiro argumento do método).

Ao satisfazer as 2 condições prévias (ordens máximas e transações máximas), o método passa para o cálculo dos níveis Stop-Loss e Take-Profit, bem como do volume do negociação (usando membros de objetos especiais). Finalmente, ele envia a ordem, e após uma operação bem sucedida, cria uma nova instância COrder e armazena-a na lista de ordens ativas (dentro do gerenciador de ordens).

Fechamento da ordem ou posição

O seguinte fragmento de código mostra o método CloseOrder do descendente MQL4 específico da classe COrderManagerBase:

bool COrderManager::CloseOrder(COrder *order,const int index=-1)
  {
   bool closed=true;
   if(CheckPointer(order)==POINTER_DYNAMIC)
     {
      if(!CheckPointer(m_symbol) || StringCompare(m_symbol.Name(),order.Symbol())!=0)
         m_symbol=m_symbol_man.Get(order.Symbol());
      if(CheckPointer(m_symbol))
         m_trade=m_trade_man.Get(order.Symbol());
      if(order.Volume()>0)
        {
         if(order.OrderType()==ORDER_TYPE_BUY || order.OrderType()==ORDER_TYPE_SELL)
            closed=m_trade.OrderClose((ulong)order.Ticket());
         else
            closed=m_trade.OrderDelete((ulong)order.Ticket());
        }
      if(closed)
        {
         int idx = index>=0?index:FindOrderIndex(GetPointer(order));
         if(ArchiveOrder(m_orders.Detach(idx)))
           {
            order.Close();
            order.Volume(0);
           }
        }
     }
   return closed;
  }

Como veremos mais adiante, a versão MQL4 é muito mais simples do que a versão MQL5, principalmente porque tem apenas um modo de margem (cobertura). E, mesmo se a cobertura for desativada pela corretora, o processo de fechamento da ordem permanecerá inalterado, isto é: a ordem pendente é removida, enquanto a de mercado é fechada.

A função aceita dois parâmetros, o objeto da ordem e seu índice na lista de ordens/posições ativas. Se o ponteiro para o objeto de clase COrder for válido, obteremos a instância correta de CExpertTradeX e CSymbolInfo para fechar a ordem e, em seguida, colocá-la no histórico do terminal de negociação chamando a função apropriada.

Assim que o pedido ou posição for encerrado, o objeto COrder precisará ser atualizado. Primeiro, ele é removido da lista de ordens ativas e depois transferida no final da lista das ordens históricas. Então, o objeto é marcado como fechado, e seu volume zerado.

O segundo argumento do método da classe aceita um parâmetro opcional (índice). Isso é para acelerar o processamento da solicitação se o índice da instância do COrder numa matriz de pedidos já for conhecido antecipadamente (geralmente é o caso, uma vez que as ordens geralmente precisam ser processadas). No caso de o índice não ser conhecido, o método pode ser chamado com apenas um único argumento e o método chamaria outro método de classe, FindOrderIndex, que será responsável por localizar a posição da instância COrder na matriz de ordens.


Implementação específica MQL5

Abertura da ordem ou posição

O seguinte fragmento de código mostra o método TradeOpen do descendente MQL5 específico da classe COrderManagerBase:

COrder* COrderManager::TradeOpen(const string symbol,ENUM_ORDER_TYPE type)
  {
   double lotsize=0.0,price=0.0;
   int trades_total =TradesTotal();
   int orders_total = OrdersTotal();
   m_symbol=m_symbol_man.Get(symbol);
   if(!IsPositionAllowed(type))
      return NULL;
   if(m_max_orders>orders_total && (m_max_trades>trades_total || m_max_trades<=0))
     {
      price=PriceCalculate(type);
      lotsize=LotSizeCalculate(price,type,m_main_stop==NULL?0:m_main_stop.StopLossCalculate(symbol,type,price));
      if (SendOrder(type,lotsize,price,0,0))
         return m_orders.NewOrder((int)m_trade.ResultOrder(),m_trade.RequestSymbol(),    (int)m_trade.RequestMagic(),m_trade.RequestType(),m_trade.ResultVolume(),m_trade.ResultPrice());
     }      
   return NULL;
  }

Como podemos ver, a implementação não difere muito do que acontece no MQL4. No entanto, uma das principais diferenças é que, neste código, não precisamos obter os valores dos níveis Stop-Loss e Take-Profit. A razão para isso é que a forma como os níveis stop se comportam no MetaTrader 5 é diferente daquele da plataforma-antecessora. Um programador que tenha experiência em trabalhar com Expert Advisors MQL5 saberá que, nesta biblioteca, vamos usar ordens pendentes como uma contrapartida para os níveis stop no MQL4 instalados no lado da corretora.

Fechamento da ordem ou posição

O seguinte fragmento de código mostra o método СloseOrder do descendente MQL4 específico da classe CorderManagerBase:

bool COrderManager::CloseOrder(COrder *order,const int index=-1)   {    bool closed=true;    COrderInfo ord;    if(!CheckPointer(order))       return true;    if(order.Volume()<=0)       return true;    if(!CheckPointer(m_symbol) || StringCompare(m_symbol.Name(),order.Symbol())!=0)       m_symbol=m_symbol_man.Get(order.Symbol());    if(CheckPointer(m_symbol))       m_trade=m_trade_man.Get(order.Symbol());    if(ord.Select(order.Ticket()))    {       closed=m_trade.OrderDelete(order.Ticket());    }      else      {       ResetLastError();       if(IsHedging())       {          closed=m_trade.PositionClose(order.Ticket());       }         else         {          if(COrder::IsOrderTypeLong(order.OrderType()))             closed=m_trade.Sell(order.Volume(),0,0,0);          else if(COrder::IsOrderTypeShort(order.OrderType()))             closed=m_trade.Buy(order.Volume(),0,0,0);         }      }    if(closed)      {

      if(ArchiveOrder(m_orders.Detach(index)))         {          order.Close();          order.Volume(0);         }      }    return closed;   }

Ao fechar uma ordem ou uma posição no MQL5, devemos considerar o modo de contagem de posições (compensação ou cobertura). Mas primeiro precisamos identificar se o item a ser fechado ou não é uma ordno MQL5 ou uma posição MQL5. Podemos usar as funções OrderSelect e HistoryOrderSelect para realizar isso, mas para reduzir o código necessário para este método e facilitar o processo, simplesmente usaremos a classe COrderInfo da Biblioteca Padrão MQL5.

Uma ordem no MQL5 vem como resultado de uma solicitação de negociação, que geralmente resulta numa transação ou um conjunto de transações (aproximadamente equivalente a uma ordem de mercado do MetaTrader 4). No entanto, se o pedido não for executado no modo Instant Execution, ele se refere a uma ordem pendente (em oposição no MetaTrader 4, onde as ordens podem ser de mercado ou pendentes). Agora, para diferenciar o elemento para sair, o método primeiro verifica se o elemento é uma ordem pendente usando COrderInfo e, em caso afirmativo, essa ordem pendente é excluída. Se falhar a seleção usando COrderInfo, então temos certeza de que é uma ordem de mercado ou uma posição. Para o modo de cobertura, a posição é simplesmente fechada usando a função PositionClose. Caso contrário, no modo de compensação, simplesmente "neutralizamos" a posição ao entrar numa posição oposta com um volume equivalente.

Criação da instância СOrder

Vimos como o gerenciador de ordens abre e fecha transações. No artigo anterior, também mostramos uma maneira de como a classe CExpertTrade pode ser alterada para torná-la compatível com ambas as plataformas de negociação. Agora, há uma diferença na forma como os níveis Stop-Loss e Take-Profit são implementados nas duas plataformas de negociação, e isto é apenas processado pelo gerenciador de ordens. O resto do processo é definido após a inicialização da instância COrder, que é chamada pelo método NewOrder de classe COrders. O código a seguir mostra o método Init de classe COrdersBase:

COrder* COrdersBase::NewOrder(const ulong ticket,const string symbol,const int magic,const ENUM_ORDER_TYPE type,const double volume,const double price)
  {
   COrder *order=new COrder(ticket,symbol,type,volume,price);
   if(CheckPointer(order)==POINTER_DYNAMIC)
      if(InsertSort(GetPointer(order)))
      {  
         order.Magic(magic);
         order.Init(GetPointer(this),m_stops);
         return order;
      }  
   return NULL;
  }

Como podemos ver, o método Init de classe COrder aceita um determinado objeto personalizado (CStops) como seu segundo argumento. Ele é um recipiente para objetos stop (como o objeto m_main_stop mostrado anteriormente). Esse objeto de classe será discutido num artigo separado.

Alteração da ordem ou posição

Ainda não mostramos nenhum código que permita ao gerenciador de ordens modificar uma posição existente. Isso pode ser delegado a outro objeto de stop (CStop e CorderStop), que será discutido num artigo separado. Esses objetos devem ser responsáveis ​​por qualquer atualização ou modificação dos níveis de stop da posição, bem como coordenação com o objeto COrder ao qual pertencem.

No MetaTrader 4, o preço de abertura da ordem pendente pode ser modificado várias vezes. Mas esto não é o caso no MetaTrader 5. Desta vez, a versão MQL5 é o componente limitante, então adotaremos o padrão MQL5. A modificação de uma ordem pendente exigirá a exclusão da ordem pendente existente e a criação de uma nova com propriedades atualizadas.

Exemplo

Como exemplo, implementaremos um Expert Advisor com uso dos objetos de classe discutidos até agora nesta série de artigos. Depois de criar o arquivo-fonte do Expert Advisor no MetaEditor, começaremos com a referência à biblioteca:

#include "MQLx\Base\OrderManager\OrderManagerBase.mqh"

Observe que, nesta declaração, estamos usando aspas ao invés de "<" e ">". Vamos colocar a biblioteca no mesmo diretório que o arquivo de código-fonte do Expert Advisor.

Para este EA, vamos exigir pelo menos três ponteiros, que devem ser declarados globalmente dentro do programa(COrderManager, CSymbolInfo e CSymbolManager):

COrderManager *order_manager;
CSymbolManager *symbol_manager;
CSymbolInfo *symbol_info;

Dentro da função OnInit, teremos que inicializar esses três ponteiros, especialmente para a instância CSymbolInfo, que requer atribuir um nome específico de um instrumento durante a inicialização.

int OnInit()
  {
//---
   order_manager = new COrderManager();
   symbol_manager = new CSymbolManager();
   symbol_info = new CSymbolInfo();
   if (!symbol_info.Name(Symbol()))
   {
      Print("symbol not set");
      return (INIT_FAILED);
   }   
   symbol_manager.Add(GetPointer(symbol_info));   
   order_manager.Init(symbol_manager,NULL);
//---
   return(INIT_SUCCEEDED);
  }

Dentro da função OnDeinit, teremos que excluir esses três ponteiros para que eles não "comam" memória (pelo menos dentro da plataforma de negociação):

void OnDeinit(const int reason)
  {
//---
   delete symbol_info;
   delete symbol_manager;
   delete order_manager;
  }

Na função OnTick, temos que implementar a estratégia atual. O Expert Advisor neste exemplo usará um método simples para determinar uma nova barra (verificando a contagem de barras no gráfico). A contagem de barras anterior deve ser armazenada numa variável estática (ou variável global). O mesmo é verdadeiro para a variável direction, que será usada para armazenar a direção anterior em que o EA abriu a transação (ou zero, se é a primeira transação). No entanto, uma vez que a função utilizada para contar as barras no gráfico difere entre as duas plataformas, teremos que dividir a implementação a este respeito:

static int bars = 0;
static int direction = 0;
int current_bars = 0;
#ifdef __MQL5__
   current_bars = Bars(NULL,PERIOD_CURRENT);
#else 
   current_bars = Bars;
#endif

Para a versão MQL4, estamos simplesmente usando a variável predefinida Bars (uma chamada para a função iBars, também pode ser usada). Por outro lado, para a versão MQL5, estamos usando uma chamada para a função Bars.

O próximo fragmento de código implementa o comportamento real do Expert Advisor que pode ser observado. Se uma discrepância entre as barras anterior e atual é detectada, o EA começa iniciando as cotações do símbolo (CSymbolInfo), para ser usado em operações futuras. Em seguida, verifica se há uma transação anterior para fechamento. Se alguém for encontrada, o EA fecha e procede ao processamento de outras transações, com base na direção anterior. O código termina ao atualizar a contagem de barras no EA. 

if (bars<current_bars)
   {   
      symbol_info.RefreshRates();
      COrder *last = order_manager.LatestOrder();
      if (CheckPointer(last) && !last.IsClosed())
         order_manager.CloseOrder(last);
      if (direction<=0)
      {
         Print("Entering buy trade..");
         order_manager.TradeOpen(Symbol(),ORDER_TYPE_BUY,symbol_info.Ask());
         direction = 1;
      }
      else
      {
         Print("Entering sell trade..");
         order_manager.TradeOpen(Symbol(),ORDER_TYPE_SELL,symbol_info.Bid());
         direction = -1;
      }   
      bars = current_bars;
   }

Para garantir que usamos o mesmo código para a versão inicial e para todas as versões futuras do Expert Advisor, movemos o código que fizemos até agora para um arquivo de cabeçalho e, em seguida, fazemos referência ao arquivo principal principal (cada um para versões MQL4 e MQL5). Ambos os arquivos-fonte (test_ordermanager.mq4 ou test_ordermanager.mq5, dependendo da plataforma de destino) terão uma única linha de código referenciando o arquivo de cabeçalho principal:

#include "test_ordermanager.mqh"

As tabelas a seguir mostram os resultados da execução do EA no MetaTrader 4 e os modos de compensação e cobertura do MetaTrader 5, conforme eles apareceram em seus respectivos relatórios do Testador de estratégias. Por razões de brevidade, apenas as 10 primeiras transações foram incluídas no artigo (os relatórios completos podem ser encontrados no pacote zip no final deste artigo).

MT4:

# Hora Tipo Ordem Volume Preço S / L T / P Lucro Balanço
1. 2017.01.02 00:00 Buy 1. 0.10 1.05102 0.00000 0.00000
2 2017.01.02 01:00 Close 1. 0.10 1.05172 0.00000 0.00000 7.00 10007.00
3 2017.01.02 01:00 Sell 2 0.10 1.05172 0.00000 0.00000
4 2017.01.02 02:00 Filled 2 0.10 1.05225 0.00000 0.00000 -5.30 10001.70
5 2017.01.02 02:00 Buy 3 0.10 1.05225 0.00000 0.00000
6 2017.01.02 03:00 Close 3 0.10 1.05192 0.00000 0.00000 -3.30 9998.40
7 2017.01.02 03:00 Sell 4 0.10 1.05192 0.00000 0.00000
8 2017.01.02 04:00 Close 4 0.10 1.05191 0.00000 0.00000 0.10 9998.50
9 2017.01.02 04:00 Buy 5 0.10 1.05191 0.00000 0.00000
10 2017.01.02 05:00 Close 5 0.10 1.05151 0.00000 0.00000 -4.00 9994.50
11 2017.01.02 05:00 Sell 6 0.10 1.05151 0.00000 0.00000
12 2017.01.02 06:00 Close 6 0.10 1.05186 0.00000 0.00000 -3.50 9991.00
13 2017.01.02 06:00 Buy 7 0.10 1.05186 0.00000 0.00000
14 2017.01.02 07:00 Close 7 0.10 1.05142 0.00000 0.00000 -4.40 9986.60
15 2017.01.02 07:00 Sell 8 0.10 1.05142 0.00000 0.00000
16 2017.01.02 08:00 Close 8 0.10 1.05110 0.00000 0.00000 3.20 9989.80
17 2017.01.02 08:00 Buy 9 0.10 1.05110 0.00000 0.00000
18 2017.01.02 09:00 Close 9 0.10 1.05131 0.00000 0.00000 2.10 9991.90
19 2017.01.02 09:00 Sell 10 0.10 1.05131 0.00000 0.00000
20 2017.01.02 10:00 Close 10 0.10 1.05155 0.00000 0.00000 -2.40 9989.50


MT5 (netting):

Tempo de abertura Ordem Símbolo Tipo Volume Preço S / L T / P Hora Estado Comentário
2017.01.02 00:00:00 2 EURUSD Buy 0.10 / 0.10 1.05140 2017.01.02 00:00:00 preenchidos
2017.01.02 01:00:00 3 EURUSD Sell 0.10 / 0.10 1.05172 2017.01.02 01:00:00 preenchidos
2017.01.02 01:00:00 4 EURUSD Sell 0.10 / 0.10 1.05172 2017.01.02 01:00:00 preenchidos
2017.01.02 02:00:00 5 EURUSD Buy 0.10 / 0.10 1.05293 2017.01.02 02:00:00 preenchidos
2017.01.02 02:00:00 6 EURUSD Buy 0.10 / 0.10 1.05293 2017.01.02 02:00:00 preenchidos
2017.01.02 03:00:00 7 EURUSD Sell 0.10 / 0.10 1.05192 2017.01.02 03:00:00 preenchidos
2017.01.02 03:00:00 8 EURUSD Sell 0.10 / 0.10 1.05192 2017.01.02 03:00:00 preenchidos
2017.01.02 04:00:00 9 EURUSD Buy 0.10 / 0.10 1.05234 2017.01.02 04:00:00 preenchidos
2017.01.02 04:00:00 10 EURUSD Buy 0.10 / 0.10 1.05234 2017.01.02 04:00:00 preenchidos
2017.01.02 05:00:00 11 EURUSD Sell 0.10 / 0.10 1.05151 2017.01.02 05:00:00 preenchidos
2017.01.02 05:00:00 12 EURUSD Sell 0.10 / 0.10 1.05151 2017.01.02 05:00:00 preenchidos
2017.01.02 06:00:00 13 EURUSD Buy 0.10 / 0.10 1.05230 2017.01.02 06:00:00 preenchidos
2017.01.02 06:00:00 14 EURUSD Buy 0.10 / 0.10 1.05230 2017.01.02 06:00:00 preenchidos
2017.01.02 07:00:00 15 EURUSD Sell 0.10 / 0.10 1.05142 2017.01.02 07:00:00 preenchidos
2017.01.02 07:00:00 16 EURUSD Sell 0.10 / 0.10 1.05142 2017.01.02 07:00:00 preenchidos
2017.01.02 08:00:00 17 EURUSD Buy 0.10 / 0.10 1.05169 2017.01.02 08:00:00 preenchidos
2017.01.02 08:00:00 18 EURUSD Buy 0.10 / 0.10 1.05169 2017.01.02 08:00:00 preenchidos
2017.01.02 09:00:00 19 EURUSD Sell 0.10 / 0.10 1.05131 2017.01.02 09:00:00 preenchidos
2017.01.02 09:00:00 20 EURUSD Sell 0.10 / 0.10 1.05131 2017.01.02 09:00:00 preenchidos
2017.01.02 10:00:00 21 EURUSD Buy 0.10 / 0.10 1.05164 2017.01.02 10:00:00 preenchidos


MT5 (hedging):














Tempo de abertura Ordem Símbolo Tipo Volume Preço S / L T / P Hora Estado Comentário
2017.01.02 00:00:00 2 EURUSD Buy 0.10 / 0.10 1.05140 2017.01.02 00:00:00 preenchidos
2017.01.02 01:00:00 3 EURUSD Sell 0.10 / 0.10 1.05172 2017.01.02 01:00:00 preenchidos
2017.01.02 01:00:00 4 EURUSD Sell 0.10 / 0.10 1.05172 2017.01.02 01:00:00 preenchidos
2017.01.02 02:00:00 5 EURUSD Buy 0.10 / 0.10 1.05293 2017.01.02 02:00:00 preenchidos
2017.01.02 02:00:00 6 EURUSD Buy 0.10 / 0.10 1.05293 2017.01.02 02:00:00 preenchidos
2017.01.02 03:00:00 7 EURUSD Sell 0.10 / 0.10 1.05192 2017.01.02 03:00:00 preenchidos
2017.01.02 03:00:00 8 EURUSD Sell 0.10 / 0.10 1.05192 2017.01.02 03:00:00 preenchidos
2017.01.02 04:00:00 9 EURUSD Buy 0.10 / 0.10 1.05234 2017.01.02 04:00:00 preenchidos
2017.01.02 04:00:00 10 EURUSD Buy 0.10 / 0.10 1.05234 2017.01.02 04:00:00 preenchidos
2017.01.02 05:00:00 11 EURUSD Sell 0.10 / 0.10 1.05151 2017.01.02 05:00:00 preenchidos
2017.01.02 05:00:00 12 EURUSD Sell 0.10 / 0.10 1.05151 2017.01.02 05:00:00 preenchidos
2017.01.02 06:00:00 13 EURUSD Buy 0.10 / 0.10 1.05230 2017.01.02 06:00:00 preenchidos
2017.01.02 06:00:00 14 EURUSD Buy 0.10 / 0.10 1.05230 2017.01.02 06:00:00 preenchidos
2017.01.02 07:00:00 15 EURUSD Sell 0.10 / 0.10 1.05142 2017.01.02 07:00:00 preenchidos
2017.01.02 07:00:00 16 EURUSD Sell 0.10 / 0.10 1.05142 2017.01.02 07:00:00 preenchidos
2017.01.02 08:00:00 17 EURUSD Buy 0.10 / 0.10 1.05169 2017.01.02 08:00:00 preenchidos
2017.01.02 08:00:00 18 EURUSD Buy 0.10 / 0.10 1.05169 2017.01.02 08:00:00 preenchidos
2017.01.02 09:00:00 19 EURUSD Sell 0.10 / 0.10 1.05131 2017.01.02 09:00:00 preenchidos
2017.01.02 09:00:00 20 EURUSD Sell 0.10 / 0.10 1.05131 2017.01.02 09:00:00 preenchidos
2017.01.02 10:00:00 21 EURUSD Buy 0.10 / 0.10 1.05164 2017.01.02 10:00:00 preenchidos


Note-se que, para os modos de cobertura e compensação no MT5, os resultados são idênticos. Embora a implementação subjacente seja a mesma, a diferença reside no fato de que, no modo de compensação, uma posição é neutralizada mediante abertura de uma posição oposta do mesmo volume, enquanto, no modo de hedge, a posição é fechada de forma semelhante ao que muitos traders estão acostumados com MetaTrader 4. No modo de cobertura poderíamos ver uma mensagem como a seguinte:

PE      0       16:19:15.747    Trade   2017.01.02 01:00:00   instant sell 0.10 EURUSD at 1.05172, close #2 (1.05172 / 1.05237 / 1.05172)
GP      0       16:19:15.747    Trades  2017.01.02 01:00:00   deal #3 sell 0.10 EURUSD at 1.05172 done (based on order #3)
DS      0       16:19:15.747    Trade   2017.01.02 01:00:00   deal performed [#3 sell 0.10 EURUSD at 1.05172]

Observe o gráfico rotulado como "close # 2" à direita na primeira cadeia de caracteres. O modo de cobertura indica qual transação particular deve ser neutralizada (fechado). Por outro lado, no modo de cobertura, apenas veríamos uma mensagem como a seguinte:

PG      0       16:20:51.958    Trade   2017.01.02 01:00:00   instant sell 0.10 EURUSD at 1.05172 (1.05172 / 1.05237 / 1.05172)
MQ      0       16:20:51.958    Trades  2017.01.02 01:00:00   deal #3 sell 0.10 EURUSD at 1.05172 done (based on order #3)
KN      0       16:20:51.958    Trade   2017.01.02 01:00:00   deal performed [#3 sell 0.10 EURUSD at 1.05172]

Neste modo, é menos óbvio se a transação realizada para abertura de uma nova ordem, ou simplesmente a fim de neutralizar uma posição existente.

Visão geral da estrutura

A classe COrderManager é um dos objetos de classe mais complexos a serem discutidos nesta série de artigos. Para dar uma ideia de como o gerenciador de ordens final se pareceria com seus membros de objetos, considere o seguinte diagrama:

Visão geral da estrutura do gerenciador de ordens

Em palavras simples, o gerenciador de pedidos conteria duas instâncias COrders (atuais e históricas), que atuariam como contêineres para as ordens inseridas pelo gerenciador de ordens (COrder). Cada uma dessas ordens pode acomodar níveis de stop (um ou mais) e cada um desses níveis pode ter seus próprios métodos de Trailing (um ou mais). Embora a maioria dos Expert Advisor não consiga encontrar uso para tal complexidade, algumas estratégias, especialmente aquelas que lidam com múltiplos níveis de suporte e resistência, encontrarão essa estrutura útil. Esses membros de classe serão discutidos em futuros artigos.

Fim do artigo

Neste artigo, discutimos a classe COrderManager, que é responsável pelo gerenciamento das operações de negociação como parte do EA. A classe COrderManager foi projetada de forma a poder lidar com MQL4 e MQL5 para que os traders e programadores que codificam Expert Advisors possam garantir a compatibilidade entre plataformas no código que eles escrevem no arquivo-fonte ou no arquivo de cabeçalho.


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

Arquivos anexados |
Expert Advisor multiplataforma: Sinais Expert Advisor multiplataforma: Sinais
No artigo, são discutidas as classes CSignal e CSignals, que serão usadas em Expert Advisors multiplataforma. Além disso, serão examinadas as diferenças entre MQL4 e MQL5 quanto à organização de dados necessários para avaliar os sinais de negociação obtidos. O resultado será um código compatível com os compiladores das duas versões.
Previsão de movimentos do mercado utilizando a classificação Bayesiana e indicadores com base na análise de espectro singular Previsão de movimentos do mercado utilizando a classificação Bayesiana e indicadores com base na análise de espectro singular
Nesta pesquisa, são consideradas uma ideologia e metodologia a fim de construir um sistema de recomendação para negociar rápido com base na combinação de possibilidades de previsão com ajuda da Análise de Espetro Singular (SSA) e o método de aprendizado de máquina baseado no teorema de Bayes.
Indicadores personalizados e infográficos no CCanvas Indicadores personalizados e infográficos no CCanvas
O artigo considera novos tipos de indicadores com uma implementação estrutural mais complexa. Ele também descreve o desenvolvimento de indicadores do tipo pseudo-3D e infográficos dinâmicos.
Exemplo de indicador que constrói uma linha de suporte e resistência Exemplo de indicador que constrói uma linha de suporte e resistência
O artigo mostra a implementação de um indicador para construção de linhas de suporte e de resistência com base em condições formais. Você não só poderá aplicar o indicador, mas também entenderá quão fácil é realizá-lo. Agora você será capaz de formular as condições para desenhar linhas alterando o código do indicador ligeiramente para atender às suas necessidades.