Eventos de negociação no MetaTrader 5
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.
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.
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:
- Uma solicitação de mudança de uma posição (mudar seu Stop Loss e/ou seu Take Profit);
- 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:
- Aparecimento de um acordo que esteja escrito para o histórico de negociação.
- 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).
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:
- O acordo de 5 lotes;
- A mudança de volume;
- 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().
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.
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.
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:
- Declarar os contadores de pedido, posições e acordos no âmbito global.
- 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.
- Inicializar os contadores de pedidos, posições e acordos na função OnInit.
- Determinar as funções de handler (gerenciador) às quais serão solicitadas o histórico da negociação para o cache.
- 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 Ltd.
Artigo original: https://www.mql5.com/ru/articles/232
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso