Eventos de negociação no MetaTrader 5

10 janeiro 2014, 14:29
MetaQuotes
0
1 839

Introdução

Todos os comandos para executar operações são transmitidos para o servidor de negociação do terminal do cliente MetaTrader 5 através de solicitações de envio. Cada solicitação deve ser devidamente preenchida de acordo com a operação solicitada, caso contrário não passará da validação preliminar e não será aceita pelo servidor para processamento adicional.

As solicitações aceitas pelo servidor de negociação são armazenadas na forma de pedidos que podem ficar tanto pendentes ou executados imediatamente pelo preço de mercado. Os pedidos são armazenados no servidor até que sejam preenchidos ou cancelados. O resultado de uma execução de pedido é um acordo.

Um acordo muda a posição da negociação por um determinado símbolo, ele pode abrir, fechar, aumentar, diminuir ou reverter a posição. Portanto, uma posição aberta é sempre um resultado de execução de um ou mais acordos. Informações mais detalhadas são fornecidas no artigo Pedidos, posições, e acordos no MetaTrader 5.

Este artigo descreve conceitos, termos e processos que fluem dentro do prazo desde o envio de uma solicitação até seu movimento no histórico de negociação após ser processado.


Transmissão de solicitação do terminal do cliente para o servidor de negociação

Para executar uma operação de negociação você deve enviar um pedido para o sistema de negociação. Uma solicitação é sempre enviada ao servidor de negociação através do envio de um pedido do terminal do cliente. A estrutura de uma solicitação deve ser preenchida corretamente, independentemente de como você negocie - manualmente ou utilizando um programa MQL5.

Para executar uma operação de negociação manualmente, você deve abrir a janela de diálogo de preenchimento de uma solicitação de negociação com a tecla F9. Ao negociar automaticamente através do MQL5, as solicitações são enviadas utilizando a função OrderSend(). Uma vez que um grande número de solicitações incorretas podem causar uma indesejável sobrecarga do servidor de negociação, cada solicitação deve ser verificada antes de ser enviada utilizando a função OrderCheck(). O resultado de uma verificação de uma solicitação é colocado a uma variável descrita pela estrutura MqlTradeCheckresult.

Importante: Cada solicitação é verificada quanto à correção no terminal do cliente antes de ser enviada para o servidor de negociação. Solicitações deliberadamente incorretas (para comprar um milhão de lotes ou comprar por um preço negativo) não são transmitidas ​fora do terminal do cliente. Isso é feito para proteger os servidores de negociação de uma massa de solicitação incorreta causada por um erro em um programa MQL5.

Uma vez que uma solicitação chega para ao servidor de negociação, ela passa pela verificação primária:

  • Se você tem ativos suficientes para executar a operação de negociação;
  • Se o preço especificado está correto: preços abertos, Stop Loss, Take Profit, etc.;
  • Se o preço especificado está presente no fluxo de preço para execução instantânea;
  • Se os níveis de Stop Loss e Take Profit estão ausentes no modo Execução de Mercado;
  • Se o volume está correto: o volume mínimo e o volume máximo, o passo, o volume máximo da posição (SYMBOL_VOLUME_MIN, SYMBOL_VOLUME_MAX, SYMBOL_VOLUME_STEP and SYMBOL_VOLUME_LIMIT);
  • Estado do símbolo: seção de cota ou de negociação, possibilidade de negociação pelo símbolo, um modo específico de negociação (ex. apenas fechamento de posições), etc.;
  • Estado da conta de negociação: limitações diferentes para tipos de contas específicas;
  • Outras verificações, dependendo da operação de negociação solicitada.
Uma solicitação incorreta que não passe pela verificação primária no servidor é rejeitada. O terminal de cliente é sempre informado sobre o resultado da verificação de uma solicitação ao enviar uma resposta. A resposta do servidor de negociação pode ser obtida a partir de uma variável do tipo MqlTradeResult, que é transmitida como o segundo parâmetro na função OrderSend() ao enviar uma solicitação.

Envio de solicitações de negociação ao servidor de negociação do terminal de cliente

Se uma solicitação passa pela verificação primária por exatidão, ela será colocada na espera de solicitação para ser processada. Como resultado de um processo de solicitação, um pedido (comando para executar uma operação de negociação) é criado na base do servidor de negociação. Entretanto, há dois tipos de solicitações que não resultam na criação de um pedido:

  1. Uma solicitação de mudança de uma posição (mudar seu Stop Loss e/ou seu Take Profit);
  2. Uma solicitação para modificar um pedido pendente (seus níveis de preço e data de vencimento).

O terminal do cliente recebe uma mensagem que a solicitação foi aceita e colocada no sistema secundário de negociação da plataforma do MetaTrader 5. O servidor coloca a solicitação aceita para a fila de solicitação para processamento posterior que pode resultar em:

  • Colocação de um pedido pendente;
  • Execução de um pedido instantâneo pelo preço de mercado;
  • Modificação de um pedido ou posição.

A vida útil de uma solicitação na fila do servidor tem um limite de três minutos. Uma vez que o período se exceda, a solicitação é removida da lista de solicitações.


Envio de eventos de negociação do servidor de negociação ao terminal do cliente

O modelo de evento e as funções de event handler (gerenciador de evento) estão implementadas na linguagem do MQL5. Isto significa que em resposta a qualquer evento predefinido o ambiente de execução chama a função apropriada - o event handler (gerenciador de evento). Para processamento de eventos de negociação há a função predefinida OnTrade(); o código para trabalhar com pedidos, posições e acordos deve ser colocado dentro dela. Esta função é chamada apenas por Expert Advisors, não será utilizada em indicadores e scripts mesmo se você adicionar lá uma função com o mesmo nome e tipo.

Os eventos de negociação são gerados pelo servidor no caso de:

  • Mudança de pedidos ativos;
  • Mudança de posições;
  • Mudança de acordos;
  • Mudança de histórico de negociação.

Observe que uma operação pode causar a ocorrência de vários eventos. Por exemplo, o funcionamento de um pedido pendente leva à ocorrência de dois eventos:

  1. Aparecimento de um acordo que esteja escrito para o histórico de negociação.
  2. Movimento do pedido pendente a partir da lista de ativos à lista de pedidos do histórico (o pedido é movido para o histórico).

Um outro exemplo de múltiplos eventos é a realização de diversos acordos com base em um único pedido, no caso do volume exigido não poder ser obtido a partir de uma única oferta oposta. O servidor de negociação cria e envia as mensagens sobre cada evento para o terminal do cliente. É por isso que a função OnTrade () pode ser chamada por várias vezes para aparentemente um único evento. Este é um simples exemplo do procedimento de processamento de pedido no sistema secundário de negociação da plataforma MetaTrader 5.

Aqui está um exemplo: enquanto um pedido pendente para a compra de 10 lotes de EURUSD aguarda para ser executado, ofertas opostas para a venda de 1, 4 e 5 lotes aparecem. Esses três pedidos juntos fornecem o volume necessário de 10 lotes, para que eles sejam executados um por um, se a política de preenchimento permitir a execução de operação de negociação em partes.

Como resultado da execução de 4 pedidos, o servidor executará 3 acordos de 1, 4 e 5 lotes com base nas solicitações opostas existentes. Quantos eventos de negociação serão gerados neste caso? A primeira solicitação oposta para vender um lote levará à execução de acordo do lote 1. Este é o primeiro evento de negociação (acordo de lote 1). Mas o pedido pendente para a compra de 10 lotes também é alterado, agora é um pedido para a compra de 9 lotes de EURUSD. A mudança de volume do pedido pendente é o segundo evento de negociação (mudança do volume de um pedido pendente).

Geração de eventos de negociação

Para a segunda dose de 4 lotes os outros dois eventos de negociação serão gerados, uma mensagem sobre cada um deles será enviada para o terminal do cliente que iniciou o pedido pendente inicial para a compra de 10 lotes de EURUSD.

O último acordo de 5 lotes levará à ocorrência de três eventos de negociação:

  1. O acordo de 5 lotes;
  2. A mudança de volume;
  3. Mudança do pedido para o histórico de negociação.

Como um resultado da execução da operação, o terminal de cliente recebe 7 eventos de negociação Trade um após o outro (presume-se que a ligação entre o terminal do cliente e o servidor de negociação seja estável e nenhuma mensagem seja perdida). Estas mensagens devem ser processadas no Expert Advisor utilizando a função OnTrade().

Importante: Cada mensagem sobre uma negociação, Trade pode aparecer como resultado de uma ou de diversas solicitações. Cada solicitação pode levar à ocorrência de diversos eventos de negociação. Você não pode contar com a afirmação "Uma solicitação - um evento de Trade", uma vez que o processamento de eventos pode ser realizado em diversos estágios e cada operação pode mudar o estado de pedidos, posições e do histórico de negociação.


Processamento de pedidos pelo Servidor Trade

Todos os pedidos que esperam por sua execução serão movidos para o histórico no final - ou a condição para sua execução será satisfeita ou eles serão cancelados. Existem diversas variantes de um cancelamento de pedido:

  • Execução de um acordo com base de outro pedido;
  • Rejeição de um pedido pelo fornecedor;
  • Cancelamento do pedido na demanda do trader (solicitação manual ou uma solicitação automática do programa MQL5 );
  • Vencimento do pedido, que é determinado tanto pelo trader ao enviar a solicitação ou por condições de negócios do sistema de negociação fornecido;
  • Falta de ativos na conta de negociação para realizar o negócio no momento em que as condições da sua execução forem satisfatórias;
  • Pedido é cancelado devido à política de preenchimento (um pedido parcialmente preenchido é cancelado).

Independentemente da razão pela qual um pedido ativo é movido para o histórico, a mensagem sobre a alteração é enviada para o terminal do cliente. Mensagens sobre o evento de negociação não são todas para os terminais do cliente, mas para aqueles conectados à conta correspondente.



Importante: O fato de aceitar uma solicitação pelo servidor de negociação nem sempre leva à execução da operação solicitada. Significa que a solicitação passou pela validação após ter chegado ao servidor de negociação.

Essa é a razão pela qual a documentação da função OrderSend() diz:

Valor de retorno

No caso de uma verificação básica bem sucedida de uma solicitação, a função OrderSend() retorna autêntica - isso não é um sinal de execução bem sucedida de uma operação de negociação. Para uma descrição mais detalhada dos resultados de execução de funções, analise os campos de estrutura MqlTradeResult.


Atualização de negociação e de histórico no terminal do cliente

Mensagens sobre eventos de negociação e mudanças no histórico de negociação chegam através de canais separados. Ao enviar uma solicitação para compra utilizando a função OrderSend(), você pode obter a nota do pedido, que é criada como resultado da validação bem sucedida da solicitação. Ao mesmo tempo, o pedido em si pode não aparecer no terminal do cliente e uma tentativa de selecioná-lo utilizando o OrderSelect() pode falhar.


Todas as mensagens do servidor de negociação chegam ao terminal do cliente independentemente

Na figura acima, você pode ver como o servidor de negociação informa sobre a nota do pedido para o programa MQL5, mas a mensagem sobre o evento de negociação Trade (aparecimento de novo pedido) ainda não chegou. A mensagem sobre a mudança da lista de pedidos ativos não chegou bem.

Pode haver uma situação, quando a mensagem de Trade sobre o aparecimento de um novo pedido chega ao programa quando um acordo sobre a sua base já foi realizado, então, o pedido já está ausente na lista dos pedidos ativos, isto é, no histórico. Esta é uma situação real, uma vez que a velocidade de processamento de solicitações é muito maior em comparação com a velocidade atual de entrega de uma mensagem através de uma rede.


Gerenciamento de eventos de Trade no MQL5

Todas as operações sobre servidor de negócios e sobre envio de mensagens sobre eventos de negociação são executadas assimetricamente. Só há um método seguro para descobrir o que exatamente mudou na conta de negociação. Este método é memorizar o estado de negociação e o histórico de negociação e então compará-los com o novo estado.

O algoritmo de rastreamento dos eventos de negociação em Expert Advisors é o seguinte:

  1. Declarar os contadores de pedido, posições e acordos no âmbito global.
  2. Determinar a profundidade do histórico de negociação que será solicitado para o cache do programa MQL5. Quanto mais histórico carregarmos ao cache, mais recursos do terminal e computador são consumidos.
  3. Inicializar os contadores de pedidos, posições e acordos na função OnInit.
  4. Determinar as funções de handler (gerenciador) às quais serão solicitadas o histórico da negociação para o cache.
  5. Lá, depois de carregar o histórico de negociação, também vamos encontrar o que aconteceu com a conta de negociação, comparando o estado memorizado com o atual.

Este é o algoritmo mais simples, que permite descobrir se o número de posições abertas (por pedido, acordos) foi mudado e qual é a direção da mudança. Se houver alterações, então podemos obter informações posteriores mais detalhadas. Se o número de pedidos não for alterado, mas os próprios pedidos são modificados, precisa-se de uma abordagem diferente e, portanto, esta variante não é abordada neste artigo.

Mudanças do contador podem ser verificadas nas funções OnTrade() e OnTick() de um Expert Advisor.

Vamos escrever um passo a passo de exemplo do programa.

1. O contador de pedido, acordos e posições no âmbito global.

int          orders;            // number of active orders
int          positions;         // number of open positions
int          deals;             // number of deals in the trade history cache
int          history_orders;    // number of orders in the trade history cache
bool         started=false;     // flag of initialization of the counters


2. A profundidade do histórico de negociação a ser carregada é configurada nos dias de variável de entrada (carregue o histórico de negociação para o número de dias especificado nesta variável).

input    int days=7;            // depth of the trade history in days

//--- set the limit of the trade history on the global scope
datetime     start;             // start date of the trade history in cache
datetime     end;               // end date of the trade history in cache


3. Inicialização dos contadores e os limites do histórico de negociação. Retire a inicialização dos contadores da função InitCounters() para melhor leitura do código:

int OnInit()
  {
//---
   end=TimeCurrent();
   start=end-days*PeriodSeconds(PERIOD_D1);
   PrintFormat("Limits of the trade history to be loaded: start - %s, end - %s",
               TimeToString(start),TimeToString(end));
   InitCounters();
//---
   return(0);
  }

A função InitCounters () tenta carregar o histórico de negociação no cache e, no caso de sucesso, ele inicializa todos os contadores. Além disso, se o histórico é carregado com êxito, o valor da variável global "iniciada" é definida como "autêntica", o que indica que os contadores foram inicializados com sucesso.

//+------------------------------------------------------------------+
//|  initialization of the counters of positions, orders and deals   |
//+------------------------------------------------------------------+
void InitCounters()
  {
   ResetLastError();
//--- load history
   bool selected=HistorySelect(start,end);
   if(!selected)
     {
      PrintFormat("%s. Failed to load the history from %s to %s to the cache. Error code: %d",
                  __FUNCTION__,TimeToString(start),TimeToString(end),GetLastError());
      return;
     }
//--- get current values
   orders=OrdersTotal();
   positions=PositionsTotal();
   deals=HistoryDealsTotal();
   history_orders=HistoryOrdersTotal();
   started=true;
   Print("The counters of orders, positions and deals are successfully initialized");
  }


4. A verificação de alterações no estado da conta de negociação é executada nos handlers (gerenciadores) OnTick() e OnTrade(). A variável 'iniciada' é verificada primeiro - se o seu valor é 'autêntico', a função SimpleTradeProcessor() é chamada, caso contrário, a função de inicialização dos contadores InitCounters() é chamada.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(started) SimpleTradeProcessor();
   else InitCounters();
  }
//+------------------------------------------------------------------+
//| called when the Trade event occurs                               |
//+------------------------------------------------------------------+
void OnTrade()
  {
   if(started) SimpleTradeProcessor();
   else InitCounters();
  }

5. A função SimpleTradeProcessor() verifica se o número de pedidos, acordos e posições foi alterado. Após a realização de todas as verificações, chamamos a função CheckStartDateInTradeHistory() que move o valor 'início' para mais perto do momento atual, caso seja necessário.

//+------------------------------------------------------------------+
//| simple example of processing changes in trade and history        |
//+------------------------------------------------------------------+
void SimpleTradeProcessor()
  {
   end=TimeCurrent();
   ResetLastError();
//--- load history
   bool selected=HistorySelect(start,end);
   if(!selected)
     {
      PrintFormat("%s. Failed to load the history from %s to %s to the cache. Error code: %d",
                  __FUNCTION__,TimeToString(start),TimeToString(end),GetLastError());
      return;
     }

//--- get current values
   int curr_orders=OrdersTotal();
   int curr_positions=PositionsTotal();
   int curr_deals=HistoryDealsTotal();
   int curr_history_orders=HistoryOrdersTotal();

//--- check whether the number of active orders has been changed
   if(curr_orders!=orders)
     {
      //--- number of active orders is changed
      PrintFormat("Number of orders has been changed. Previous number is %d, current number is %d",
                  orders,curr_orders);
     /*
       other actions connected with changes of orders
     */
      //--- update value
      orders=curr_orders;
     }

//--- change in the number of open positions
   if(curr_positions!=positions)
     {
      //--- number of open positions has been changed
      PrintFormat("Number of positions has been changed. Previous number is %d, current number is %d",
                  positions,curr_positions);
      /*
      other actions connected with changes of positions
      */
      //--- update value
      positions=curr_positions;
     }

//--- change in the number of deals in the trade history cache
   if(curr_deals!=deals)
     {
      //--- number of deals in the trade history cache has been changed
      PrintFormat("Number of deals has been changed. Previous number is %d, current number is %d",
                  deals,curr_deals);
      /*
       other actions connected with change of the number of deals
       */
      //--- update value
      deals=curr_deals;
     }

//--- change in the number of history orders in the trade history cache
   if(curr_history_orders!=history_orders)
     {
      //--- the number of history orders in the trade history cache has been changed
      PrintFormat("Number of orders in the history has been changed. Previous number is %d, current number is %d",
                  history_orders,curr_history_orders);
     /*
       other actions connected with change of the number of order in the trade history cache
      */
     //--- update value
     history_orders=curr_history_orders;
     }
//--- check whether it is necessary to change the limits of trade history to be requested in cache
   CheckStartDateInTradeHistory();
  }

A função CheckStartDateInTradeHistory() calcula a data de início de solicitação do histórico de negociação para o momento atual (curr_start) e compara-a com a variável 'início'. Se a diferença entre elas for maior do que um dia, em seguida, 'início' é corrigida e os contadores dos pedidos de histórico e acordos são atualizados.

//+------------------------------------------------------------------+
//|  Changing start date for the request of trade history            |
//+------------------------------------------------------------------+
void CheckStartDateInTradeHistory()
  {
//--- initial interval, as if we started working right now
   datetime curr_start=TimeCurrent()-days*PeriodSeconds(PERIOD_D1);
//--- make sure that the start limit of the trade history period has not gone 
//--- more than 1 day over intended date
   if(curr_start-start>PeriodSeconds(PERIOD_D1))
     {
      //--- we need to correct the date of start of history loaded in the cache
      start=curr_start;
      PrintFormat("New start limit of the trade history to be loaded: start => %s",
                  TimeToString(start));

      //--- now load the trade history for the corrected interval again
      HistorySelect(start,end);

      //--- correct the counters of deals and orders in the history for further comparison
      history_orders=HistoryOrdersTotal();
      deals=HistoryDealsTotal();
     }
  }

O código completo do Expert Advisor DemoTradeEventProcessing.mq5 está anexo ao artigo.


Conclusão

Todas as operações da plataforma de negociação online no MetaTrader 5 são executadas de forma assíncrona e as mensagens sobre todas as alterações em uma conta de negociação são enviadas de forma independente uma da outra. Portanto, não há nenhum motivo em tentar controlar eventos individuais baseando-se na regra "Uma solicitação - um evento de negociação". Se você precisa determinar com precisão o que exatamente é alterado quando um evento de Trade chega, então você deve analisar todas as negociações, posições e pedidos a cada chamada do handler (gerenciador) OnTrade comparando seu estado atual com o anterior.

Traduzido do russo pela MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/ru/articles/232

Arquivos anexados |
Ordens, posições e negócios no MetaTrader 5 Ordens, posições e negócios no MetaTrader 5

A criação robusta de um robô de negócio não pode ser feita sem um entendimento dos mecanismos do sistema de negócio do MetaTrader 5. O terminal do cliente recebe as informações sobre as posições, ordens e negócios a partir do servidor de negócio. Para manipular estes dados adequadamente utilizando o MQL5, é necessário ter um bom entendimento da interação entre o programa MQL5 e o terminal do cliente.

Guia para testes e otimização de Expert Advisors no MQL5 Guia para testes e otimização de Expert Advisors no MQL5

Este artigo explica o processo passo-a-passo de identificação e solução de erros de código, assim como os passos para testar e otimizar os parâmetros de entrada do Expert Advisor. Você aprenderá como utilizar o Strategy Tester do terminal do cliente MetaTrader 5 para descobrir o melhor símbolo e definição dos parâmetros de entrada para seu Expert Advisor.

Princípios básicos dos testes no MetaTrader 5 Princípios básicos dos testes no MetaTrader 5

Quais são as diferenças entre os três modos de teste no MetaTrader 5 e o que deve ser especialmente buscado? Como o teste de um EA, negociando simultaneamente em múltiplos instrumentos, acontece? Quando e como os valores dos indicadores são calculados durante o teste e como os eventos são manuseados? Como sincronizar as barras a partir de diferentes instrumentos durante o teste no modo "apenas preços abertos"? Este artigo tem como objetivo fornecer respostas a estas e muitas outras questões.

Uso dos recursos no MQL5 Uso dos recursos no MQL5

Os programas MQL5 não apenas automatizam os cálculos de rotina, mas também podem criar um ambiente gráfico com muitos recursos. As funções para criar controles realmente interativos são agora virtualmente de mesmo valor que aqueles nas linguagens clássicas de programação. Se você quiser escrever um programa independente completo no MQL5, use recursos nele. Programas com recursos são mais fáceis de manter e distribuir.