
Como Lidar Com o Erro 146, ("Trade context busy")
1. O que é o "Trade Context" (Contexto de Negociação) em termos do Terminal Cliente MetaTrader 4
Extraído da Referência do MetaEditor:
Em outras palavras, apenas um Expert (Script) poderá negociar por vez. Todos os outros Experts que tentarem iniciar a negociação serão interrompidos pelo erro 146. Este artigo irá encontrar soluções para este problema.
2. Função IsTradeAllowed()
A maneira mais simples de descobrir se o contexto de negociação está ocupado é usar a função chamada IsTradeAllowed().
Extraído da Referência do MetaEditor:
"bool IsTradeAllowed()
Retorna true se é permitido o Expert negociar e se a thread para negociação não estiver ocupada, caso contrário, ele retornará false.
Isto significa que se pode tentar negociar apenas se a função IsTradeAllowed() retorna TRUE.
A verificação deve ser feita pouco antes de uma operação de negociação.
Um exemplo errado do uso da função:
int start() { // verifica se o contexto de negociação está livre if(!IsTradeAllowed()) { // se a função IsTradeAllowed() retornar FALSE, ela informa o usuário sobre isso, Print("O Contexto de negociação está ocupado! O Expert não poderá abrir posição! "); // e encerra a operação do Expert. Ele será reiniciado quando o próximo tick // aparecer return(-1); } else { // se a função IsTradeAllowed() retornar TRUE, ele informa o usuário sobre isso // e continua a trabalhar Print("O contexto de negociação está livre! Nós continuamos a trabalhar..."); } // verifica se a entrada no mercado deve ser feita agora ... // calcula os níveis de Stop Loss e Take Profit e o tamanho do lote ... // Abre uma posição if(OrderSend(...) < 0) Alert("Erro ao abrir uma posição # ", GetLastError()); return(0); }
Neste exemplo, o status do contexto de negociação é verificado no início da função start(). É uma ideia errada: O contexto de negociação pode ser ocupado por um outro Expert durante o tempo gasto pelo nosso Expert em calcular tudo (a necessidade da entrada no mercado, os níveis de Stop Loss e Take Profit, tamanho do lote, etc.). Nesse caso, a tentativa de abrir uma posição não terá sucesso.
Um exemplo adequado do uso da função:
int start() { // verifica se a entrada no mercado deve ser feita agora ... // calcula os níveis de Stop Loss e Take Profit e o tamanho do lote ... // agora vamos verificar se o contexto de negociação está livre if(!IsTradeAllowed()) { Print("O Contexto de negociação está ocupado! O Expert não poderá abrir posição! "); return(-1); } else Print("O contexto de negociação está livre! Tentando abrir uma posição ... "); // Se a verificação teve sucesso, abre uma posição if(OrderSend(...) < 0) Alert("Erro ao abrir uma posição # ", GetLastError()); return(0); }
O estado do contexto de negociação é verificado aqui imediatamente antes da abertura da posição, e a probabilidade de que outro Expert interponha entre estas duas ações será muito menor. (Apesar de tudo, ela ainda existe. Isso será considerado abaixo).
Este método possui duas desvantagens essenciais:
- ainda é provável que os Experts verifiquem o estado simultaneamente e, tendo recebido os resultados positivos, ele irá tentar negociar ao mesmo tempo
- se a verificação falhar, o Expert irá tentar negociar novamente apenas no próximo tick; tal atraso é altamente indesejável
O segundo problema pode ser resolvido com bastante facilidade: ele precisa apenas esperar que o contexto de negociação se torne livre. Em seguida, o Expert irá começar a negociar imediatamente após o outro Expert ter terminado.
Isso provavelmente será parecido com este:
int start() { // verifica se a entrada no mercado deve ser feita agora ... // calcula os níveis de Stop Loss e Take Profit e o tamanho do lote ... // verifica se o contexto de negociação está livre if(!IsTradeAllowed()) { Print("O Contexto de negociação está ocupado! Aguarda até que ele esteja livre..."); // loop infinito while(true) { // se o Expert foi interrompido pelo usuário, parar a operação if(IsStopped()) { Print("O Expert foi parada pelo utilizador!"); return(-1); } // se contexto de negociação ficou livre, encerra o loop e inicia a negociação if(IsTradeAllowed()) { Print("O contexto de negociação ficou livre!"); break; } // Se nenhuma condição de quebra do loop for satisfeita, "esperar" por 0.1 segundos // e reiniciar a verificação Sleep(100); } } else Print("O contexto de negociação está livre! Tentando abrir uma posição..."); // tentando abrir uma posição if(OrderSend(...) < 0) Alert("Erro ao abrir uma posição # ", GetLastError()); return(0); }Nesta realização atual, temos alguns pontos problemáticos novamente:
- já que a função IsTradeAllowed() é responsável não só pelo status do contexto de negociação, mas também pela ativação desativação da negociaçao dos Experts, o Expert pode "travar" em um loop infinito; ele somente irá parar, se este for removido do gráfico manualmente
- se o Expert aguardar, por apenas alguns segundos, até que o contexto de negociação esteja livre, os preços podem se alterar, sendo impossível de negociar com eles - os dados devem ser atualizados, a abertura, os níveis de Take Profit e Stop Loss da posição a ser aberta devem ser recalculados
O código corrigido será parecido com este:
// tempo (em segundos) em que o Expert irá aguardar até a negociação // contexto está livre (se ele está ocupado) int MaxWaiting_sec = 30; int start() { // verifica se a entrada no mercado deve ser feita agora ... // calcula os níveis de Stop Loss e Take Profit e o tamanho do lote ... // verifica se o contexto de negociação está livre if(!IsTradeAllowed()) { int StartWaitingTime = GetTickCount(); Print("O Contexto de negociação está ocupado! Aguarda até que ele esteja livre..."); // loop infinito while(true) { // se o Expert foi interrompido pelo usuário, parar a operação if(IsStopped()) { Print("O Expert foi parada pelo utilizador!"); return(-1); } // se ele esperou mais tempo do que o especificado na variável chamada // MaxWaiting_sec, interrompe o funcionamento também if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000) { Print("O limite de espera (" + MaxWaiting_sec + " sec) foi excedido!"); return(-2); } // se o contexto de negociação ficou livre, if(IsTradeAllowed()) { Print("O contexto de negociação está livre!"); // atualiza as informações do mercado RefreshRates(); // recalcula os níveis de Stop Loss e Take Profit ... // sai do loop e inicia a negociação break; } // Se nenhuma condição de quebra do loop for satisfeita, "esperar" por 0.1 // segundos e, em seguida, reiniciar a verificação Sleep(100); } } else Print("O contexto de negociação está livre! Tentando abrir uma posição..."); // tentando abrir uma posição if(OrderSend(...) < 0) Alert("Erro ao abrir uma posição # ", GetLastError()); return(0); }No exemplo acima, nós adicionamos:
- atualização da informação de mercado (RefreshRates()) e consequentemente o recálculo do Stop Loss e Take Profit
- tempo máximo de espera MaxWaiting_sec, após a superação dos quais o Expert irá parar a operação
O código acima já pode ser utilizado em seus Experts.
O toque final: Vamos colocar toda a verificação relativa em uma função separada. Isto irá simplificar a integração nos Experts e sua utilização.
///////////////////////////////////////////////////////////////////////////////// // int _IsTradeAllowed( int MaxWaiting_sec = 30 ) // // a função verifica o estado do contexto de negociação. Códigos de retorno: // 1 - o contexto de negociação está livre, a negociação é permitida // 0 - o contexto de negociação está ocupado, mas ficou livre. A negociação é permitida somente após // as informações do mercado terem sido atualizadas. // -1 - o contexto de negociação está ocupado, esperar a interrupção pelo usuário (Expert foi removido do // gráfico, o terminal foi fechado, o período de gráfico e/ou símbolo foi alterado, etc.) // -2 - o contexto de negociação está ocupado, o limite de espera for atingido (MaxWaiting_sec). // Possivelmente, o Expert não tem permissão para realizar negociações (opção "Permitir negociação automatizada" // nas configurações do Expert). // // MaxWaiting_sec - tempo (em segundos) durante o qual a função irá esperar // até que o contexto de negociação esteja livre (caso ela esteja ocupada). Por padrão ela é igual a 30. ///////////////////////////////////////////////////////////////////////////////// int _IsTradeAllowed(int MaxWaiting_sec = 30) { // verifica se o contexto de negociação está livre if(!IsTradeAllowed()) { int StartWaitingTime = GetTickCount(); Print("O Contexto de negociação está ocupado! Aguarda até que ele esteja livre..."); // loop infinito while(true) { // se o Expert foi interrompido pelo usuário, parar a operação if(IsStopped()) { Print("O Expert foi encerrado pelo usuário!"); return(-1); } // se o tempo de espera exceder o tempo especificado no // MaxWaiting_sec, interrompe o funcionamento também if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000) { Print("O limite de espera foi excedido(" + MaxWaiting_sec + " segundos)!"); return(-2); } // se o contexto de negociação ficou livre, if(IsTradeAllowed()) { Print("O contexto de negociação ficou livre!"); return(0); } // Se nenhuma condição de quebra do loop for satisfeita, "esperar" por 0.1 // segundos e, em seguida, reiniciar a verificação Sleep(100); } } else { Print("O contexto de negociação está livre!"); return(1); } }
Um modelo do Expert que utiliza a função:
int start() { // verifica se a entrada no mercado deve ser feita agora ... // calcula os níveis de Stop Loss e Take Profit e o tamanho do lote ... // verifica se o contexto de negociação está livre int TradeAllow = _IsTradeAllowed(); if(TradeAllow < 0) { return(-1); } if(TradeAllow == 0) { RefreshRates(); // recalcula os níveis de Take Profit e Stop Loss ... } // Abre uma posição if(OrderSend(...) < 0) Alert("Erro ao abrir uma posição # ", GetLastError()); return(0); }
Vamos tirar algumas conclusões:
A função IsTradeAllowed() é fácil de usar e serve idealmente para a diferenciação de acessos ao contexto de negociação para dois ou três Experts que trabalham simultaneamente. Devido a algumas desvantagens dos mesmos, seu uso não garante que o erro 146 não ocorra quando há muitos Experts trabalhando simultaneamente. Ela também pode causar que o Expert fique "pendurado", caso a opção "Permitir negociação automatizada" estiver desativada.
É por isso que vamos considerar uma solução alternativa para este problema - a variável global como um "semáforo".
3. Variáveis Globais do Terminal do Cliente
Em primeiro lugar, a definição:
As variáveis globais do terminal do cliente são variáveis acessíveis a todos os Experts, scripts e indicadores. Isto significa que uma variável global criada por um Expert pode ser utilizada em outros Experts (no nosso caso, a distribuição de acessos).
Existem várias funções fornecidas em MQL 4 para se trabalhar com variáveis globais:
- GlobalVariableCheck() - para verificar se existe uma variável global
- GlobalVariableDel() - para remover uma variável global
- GlobalVariableGet() - para obter o valor da variável global
- GlobalVariableSet() - para criar ou modificar uma variável global
- GlobalVariableSetOnCondition() - para alterar o valor da variável global especificada pelo usuário. Ela difere da GlobalVariableSet() em que o novo valor será definido apenas em um determinado valor anterior. É esta função, que é a função chave para se criar um semáforo.
- GlobalVariablesDeleteAll() - para apagar todas as variáveis globais (Eu não posso imaginar quem possa precisar disso:)
Por que a GlobalVariableSetOnCondition() é utilizada, mas não a combinação das funções GlobalVariableGet() e GlobalVariableSet()? Pelas mesmas razões: Pode decorrer algum tempo entre o uso de duas funções. E outro Expert pode interpor para o chaveamento do semáforo. Mas isso não é o que precisamos.
4. O Conceito Básico do Semáforo
O Expert que fará a negociação deve verificar o estado do semáforo. Se o semáforo mostrar a "luz vermelha" (variável global = 1), isso significa que um outro Expert está negociando, por isso é necessário esperar. Se ele mostrar "luz verde" (variável global = 0), a negociação pode ser iniciada imediatamente (mas não se esqueça de definir a "luz vermelha" para outros Experts).
Assim, temos que criar 2 funções: uma para definir a "luz vermelha", outra para definir a "luz verde". Em face disso, a tarefa é simples. Mas não vamos tirar conclusões precipitadas, mas tentar formular a sequência de ações a serem executadas por cada função (vamos nomeá-las de TradeIsBusy() e TradeIsNotBusy()) e, finalmente, executa-las.
5. Função TradeIsBusy()
Como já foi dito antes, a principal tarefa da função será de esperar até que a "luz verde" apareça e para ligar a "luz vermelha". Além disso, temos de verificar se existe a variável global, e criá-la, caso contrário. Esta verificação seria mais razoável (e mais eficiente) de realizar a partir da função init() do Expert. Mas, em seguida, há uma probabilidade de que o usuário apague ela e nenhum dos Experts em funcionamento seriam capazes de negociar. É por isso que nós vamos colocá-la no corpo da função criada.
Tudo isto deve ser acompanhado por informações visualizadas e processamento de erros que podem ocorrer quando se trabalha com a variável global. A "suspensão" não deve ser esquecida também: O tempo de operação da função deve ser limitada.
Isto é o que finalmente teremos:
///////////////////////////////////////////////////////////////////////////////// // int TradeIsBusy( int MaxWaiting_sec = 30 ) // // A função substitui o valor de TradeIsBusy de 0 para 1. // Se TradeIsBusy = 1, no momento do lançamento, a função espera até que TradeIsBusy seja 0, // e, em seguida, substitui ela. // Se não houver a variável global TradeIsBusy, a função cria ela. // Códigos de retorno: // 1 - concluído com sucesso. A variável global TradeIsBusy foi designada com o valor 1 // -1 - TradeIsBusy = 1 no momento do lançamento da função, a espera foi interrompida pelo utilizador // (O expert foi removido do gráfico, o terminal foi fechado, o período e/ou um símbolo gráfico // foi alterado, etc.) // -2 - TradeIsBusy = 1 no momento do lançamento da função, o limite de espera foi excedido // (MaxWaiting_sec) ///////////////////////////////////////////////////////////////////////////////// int TradeIsBusy( int MaxWaiting_sec = 30 ) { // No teste, não há nenhuma razão para dividir o contexto de negociação - apenas encerrar // a função if(IsTesting()) return(1); int _GetLastError = 0, StartWaitingTime = GetTickCount(); //+------------------------------------------------------------------+ //| Verifica se existe uma variável global e, se não, cria ela | //+------------------------------------------------------------------+ while(true) { // se o Expert foi interrompido pelo usuário, parar a operação if(IsStopped()) { Print("O Expert foi encerrado pelo usuário!"); return(-1); } // se o tempo de espera exceder ao especificado na variável // MaxWaiting_sec, interrompe o funcionamento também if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000) { Print("Tempo de espera (" + MaxWaiting_sec + " sec) excedido!"); return(-2); } // verifica se existe a variável global // se isso acontecer, deixa o loop e vai para o bloco de alteração // valor TradeIsBusy if(GlobalVariableCheck( "TradeIsBusy" )) break; else // se a GlobalVariableCheck retornar FALSE, isso significa que ela não existe ou // ocorreu um erro durante a verificação { _GetLastError = GetLastError(); // se ainda for um erro, exibe a informação, aguarda 0,1 segundo, e // reinicia a verificação if(_GetLastError != 0) { Print("TradeIsBusy()-GlobalVariableCheck(\"TradeIsBusy\")-Erro #", _GetLastError ); Sleep(100); continue; } } // se não há nenhum erro, isso significa que não há apenas nenhuma variável global, ele tenta criar // ela // se a GlobalVariableSet > 0, isso significa que a variável global foi criado com sucesso. // Sai da função if(GlobalVariableSet( "TradeIsBusy", 1.0 ) > 0 ) return(1); else // se a GlobalVariableSet retornar um valor <= 0, isso significa que ocorreu um erro // na criação da variável { _GetLastError = GetLastError(); // exibe a informação, aguarda 0,1 segundo, e tenta novamente if(_GetLastError != 0) { Print("TradeIsBusy()-GlobalVariableSet(\"TradeIsBusy\",0.0 )-Erro #", _GetLastError ); Sleep(100); continue; } } } //+----------------------------------------------------------------------------------+ //| Se a execução da função tiver chegado a este ponto, isso significa que variável global | //| existe. | //| Aguarda até que TradeIsBusy fique = 0 e altera o valor de TradeIsBusy para 1 | //+----------------------------------------------------------------------------------+ while(true) { // se o Expert foi interrompido pelo usuário, parar a operação if(IsStopped()) { Print("O Expert foi encerrado pelo usuário!"); return(-1); } // se o tempo de espera exceder ao especificado na variável // MaxWaiting_sec, interrompe o funcionamento também if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000) { Print("O tempo de espera (" + MaxWaiting_sec + " sec) excedeu!"); return(-2); } // tenta mudar o valor de TradeIsBusy de 0 para 1 // se teve sucesso, deixa a função retornar 1 ( "concluída com sucesso") if(GlobalVariableSetOnCondition( "TradeIsBusy", 1.0, 0.0 )) return(1); else // se não, 2 razões para isso são possíveis: TradeIsBusy = 1 (então é necessário aguardar), ou // ocorreu um erro (isto é o que vamos verificar) { _GetLastError = GetLastError(); // Se ainda for um erro, exibe a informação e tenta novamente if(_GetLastError != 0) { Print("TradeIsBusy()-GlobalVariableSetOnCondition(\"TradeIsBusy\",1.0,0.0 )-Erro #", _GetLastError ); continue; } } // se não há nenhum erro, isso significa que TradeIsBusy = 1 (um outro Expert está negociando), então exibe // a informação e espera... Comment("Espere até que o outro Expert termine a negociação..."); Sleep(1000); Comment(""); } }
Bem, tudo parece estar claro por aqui:
- verificar se existe a variável global e, se não, cria ela
- na tentativa de alterar o valor da variável global de 0 para 1; ele irá acionar somente se seu valor for = 0.
A quantidade máxima de segundos durante os quais a função pode trabalhar é MaxWaiting_sec. A função não é nenhuma objeção a exclusão do Expert do gráfico.
As informações sobre todos os erros que ocorrem é exibido no log.
6. Função TradeIsNotBusy()
A função TradeIsNotBusy resolve o problema inverso: Ligad a "luz verde".
Ela não é limitada pelo tempo de operação e não pode ser encerrada pelo usuário. A motivação é bastante simples: se a "luz verde" está desligada, nenhum Expert será capaz de negociar.
Ele naturalmente não tem nenhum códigos de retorno: O resultado pode ser apenas uma conclusão bem sucedida.
Segue abaixo como ele se parece:
///////////////////////////////////////////////////////////////////////////////// // void TradeIsNotBusy() // // A função define o valor da variável global TradeIsBusy = 0. // Se TradeIsBusy não existir, a função criará ele. ///////////////////////////////////////////////////////////////////////////////// void TradeIsNotBusy() { int _GetLastError; // no teste, não há sentido de dividir o contexto de negociação - apenas encerrar // a função if(IsTesting()) { return(0); } while(true) { // Se o Expert foi encerrado pelo usuário, encerra seu funcionamento if(IsStopped()) { Print("O Expert foi encerrado pelo usuário!"); return(-1); } // tenta definir o valor da variável global = 0 (ou criar uma variável // global) // se a GlobalVariableSet retornar um valor > 0, isso significa que tudo // foi bem sucedido. Deixa a função if(GlobalVariableSet( "TradeIsBusy", 0.0 ) > 0) return(1); else // se a GlobalVariableSet retornar um valor <= 0, isso significa que ocorreu um erro // Exibe a informação, espera, e tenta novamente { _GetLastError = GetLastError(); if(_GetLastError != 0 ) Print("TradeIsNotBusy()-GlobalVariableSet(\"TradeIsBusy\",0.0)-Erro #", _GetLastError ); } Sleep(100); } }
7. Integração nos Experts e seu Uso
Agora temos 3 funções para distribuir o acesso ao fluxo de negociação. Para simplificar a sua integração nos Experts, nós podemos criar o arquivo TradeContext.mq4 e habilitá-lo usando a diretiva #include (arquivo anexado).
Aqui está o modelo do Expert que usa as funções TradeIsBusy() e TradeIsNotBusy():
#include <TradeContext.mq4> int start() { // verifica se a entrada no mercado deve ser feita agora ... // calcula os níveis de Stop Loss e Take Profit e o tamanho do lote ... // espera até que o contexto de negociação esteja livre e, em seguida, ocupa ele (se ocorrer um erro, // deixa-o) if(TradeIsBusy() < 0) return(-1); // atualiza as informações do mercado RefreshRates(); // recalcula os níveis de StopLoss e TakeProfit ... // Abre uma posição if(OrderSend(...) < 0) { Alert("Erro ao abrir uma posição # ", GetLastError()); } // define o contexto de negociação como livre TradeIsNotBusy(); return(0); }
Na utilização das funções TradeIsBusy() e TradeIsNotBusy(), apenas um problema pode ocorrer: Se o Expert for removido a partir do gráfico após o contexto de negociação ficar ocupado, a variável TradeIsBusy irá permanecer igual a 1. Outros Experts não serão capazes de negociar após isso.
O problema pode ser resolvido facilmente: O Expert não deve ser removido a partir do gráfico quando estiver negociando ;)
É também possível que a variável TradeIsBusy não foi zerada na no desligamento abrupto do terminal. Neste caso, a função TradeIsNotBusy() a partir da função init() do Expert poderá ser usada.
E, evidentemente, o valor da variável pode ser alterado manualmente em qualquer momento: botão F3 no terminal (isto é uma possibilidade não documentada para desativar todos os Experts de negociar).
komposter (komposterius@mail.ru), 2006.04.11
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/1412




- 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