English Русский 中文 Español Deutsch 日本語
Como facilitar a detecção e recuperação de erros em um código Expert Advisor

Como facilitar a detecção e recuperação de erros em um código Expert Advisor

MetaTrader 4Sistemas de negociação | 22 fevereiro 2016, 09:23
1 433 0
Roman Kramar
Roman Kramar

Introdução

O desenvolvimento do EAs de trading na linguagem MQL4, a partir dos pontos de vista de vários aspectos, não é uma questão fácil:

  • em primeiro lugar, a algoritmização de qualquer sistema de trading já é por si só um problema, pois muitos detalhes precisam ser levados em consideração, desde as peculiaridades de um EA algoritmizado e até o ambiente específico do MetaTrader 4;
  • segundo, mesmo a presença de algoritmos de EA adicionais não eliminam as dificuldades que aparecem ao transferir o algoritmo desenvolvido para a linguagem de programação MQL4.

Devemos fazer justiça para a plataforma de trading MetaTrader 4 - a existência da linguagem de programação para trading escrito do Expert Advisors já é um grande passo adiante em comparação com as alternativas disponíveis anteriormente. O compilador é de grande ajuda para escrever EAs corretos. Imediatamente depois de clicar em 'Compilar' ele irá informar sobre todos os erros de sintaxe no seu código. Se nós lidamos com uma linguagem interpretada, esses erros seriam encontradas apenas durante a operação do EA, e isso teria aumentado a dificuldade e período de desenvolvimento. No entanto, salvo erros de sintaxe, qualquer EA pode conter erros lógicos. Agora vamos lidar com esses erros.

Erros no uso das funções incorporadas

Embora nenhum EA de trading possa funcionar sem usar funções embutidas, vamos tentar facilitar a nossa vida ao analisar os erros devolvidos por essas funções. Primeiro vamos ver os resultados da operação de funções diretamente relacionadas com operações de trading, porque ignorar erros em tais funções pode ser crítico para efeitos do EA. No entanto outros argumentos referem-se também a outras funções embutidas.

Infelizmente, u uso de opções MQL4 não pode escrever uma biblioteca generalizada para o processamento de todas as situações de erro possíveis. Em cada caso individual, precisamos processar os erros separadamente. Ainda há uma boa notícia - não precisamos processar todos os erros, muitos deles são simplesmente eliminados na fase de desenvolvimento do EA. Mas, para fazer isso, eles devem ser detectados a tempo. Como exemplo, veremos 2 erros típicos do EA no MQL4:

1) Erro 130 - ERR_INVALID_STOPS
2) Erro 146 - ERR_TRADE_CONTEXT_BUSY

Um dos casos, quando o primeiro erro aparece, é a tentativa de um EA de colocar uma ordem pendente muito perto do mercado. Em alguns casos, sua existência pode estragar seriamente as características do EA. Por exemplo, suponha que o EA, tendo aberto uma posição lucrativa, corta o lucro a cada 150 pontos. Se na próxima tentativa de erro, ocorrer o erro 130 e o preço voltar ao nível anterior de paragem, isso pode privá-lo de seu lucro legal. Apesar de tal possibilidade, este erro pode ser eliminado desde o início, inserindo no código EA a função de levar em consideração a distância minimamente aceitável entre um preço e paragens.

O segundo erro 'contexto do transações ocupado' não pode ser totalmente eliminado. Quando diversos EAs operam em um terminal podemos enfrentar a situação, quando um dos Expert Advisors está tentando abrir uma posição e quando o segundo está fazendo o mesmo. Consequentemente, este erro deve ser sempre processado.

Então, nós sempre precisamos saber, se alguma das funções embutidas retornam o erro durante a operação do EA. Isso pode ser feito usando a seguinte função adicional simples:

#include <stderror.mqh>
#include <stdlib.mqh>
 
void logError(string functionName, string msg, int errorCode = -1)
  {
    Print("ERROR: in " + functionName + "()");
    Print("ERROR: " + msg );
    
    int err = GetLastError();
    if(errorCode != -1) 
        err = errorCode;
        
    if(err != ERR_NO_ERROR) 
      {
        Print("ERROR: code=" + err + " - " + ErrorDescription( err ));
      }    
  }

Num caso mais simples deve ser utilizado da seguinte forma:


void openLongTrade()
  {
    int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask + 5, 5, 0, 0);
    if(ticket == -1) 
        logError("openLongTrade", "could not open order");
  }

O primeiro parâmetro da função logError() mostra o nome da função em que foi detectado um erro, no nosso caso é a função openLongTrade(). Se o nosso Expert Advisor chamar a função OrderSend() em diversos lugares, teremos a oportunidade de determinar exatamente o caso em que ocorreu o erro. O segundo parâmetro fornece a descrição do erro, isto permite compreender em que local exato dentro da função openLongTrade o erro ocorreu. Esta pode ser uma breve descrição, ou um descrição detalhada, incluindo os valores de todos os parâmetros passados para a função incorporada.

Eu prefiro a última variante pois, se um erro ocorrer, pode-se obter de imediato todas as informações necessárias para a análise. Imagine que, antes de chamar OrderSend(), o preço atual, com exceção do último preço conhecido, tenha mudado muito. E como resultado, um erro ocorrerá no arquivo log do EA e as linhas seguintes aparecerão:

 ERROR: in openLongTrade()
 ERROR: could not open order
 ERROR: code=138 - requote

Isto é, será claro:

  1. em qual função o erro ocorreu;
  2. a que se refere (no nosso caso, refere-se a tentativa de abrir uma posição);
  3. exatamente qual erro apareceu (código de erro e a sua descrição).

Agora vamos ver o terceiro parâmetro opcional da função logError(). Ela é necessária quando queremos processar um certo tipo de erro e outros erros serão incluídos em um arquivo log, como anteriormente:

void updateStopLoss(double newStopLoss)
  {
    bool modified = OrderModify(OrderTicket(), OrderOpenPrice(), 
                newStopLoss, OrderTakeProfit(), OrderExpiration());
    
    if(!modified)
      {
        int errorCode = GetLastError();
        if(errorCode != ERR_NO_RESULT ) 
            logError("updateStopLoss", "failed to modify order", errorCode);
      }
  }

Aqui na função updateStopLoss() a função embutida OrderModify() é chamada. Esta função diferente ligeiramente da OrderSend() em termos de processamento de erros. Se nenhum parâmetro de um pedido de alteração difere de seus parâmetros atuais, a função retornará o erro ERR_NO_RESULT. Se tal situação é aceitável no seu EA, devemos ignorar este erro. Para este fim analisamos o valor retornado por GetLastError(). Se um erro com o código ERR_NO_RESULT ocorrer, não devemos escrever nada no arquivo log.

No entanto, se ocorrer outro erro, devemos relatar como fizemos anteriormente. Exatamente para esta finalidade salvaremos o resultado da função GetLastError() em uma variável intermediária e iremos passá-lo usando o terceiro parâmetro na função logError(). Na verdade, a função incorporada GetLastError() zera automaticamente o último erro do código depois que é chamada. Se não passarmos o código de erro para o logError(), o arquivo log do EA conteria um erro com o código 0 e descrição "nenhum erro".

Ações semelhantes devem ser feitas durante o processamento de outros erros, por exemplo, requotes. A ideia principal é processar somente erros que precisam ser processados e passar os outros para a função logError(). Neste caso, vamos saber se ocorreu um erro inesperado durante a operação do EA. Depois de analisar o arquivo log, saberemos se este erro necessita de um processamento separado ou pode ser eliminado melhorando o código do EA. Tal abordagem torna nossa vida mais fácil e encurta o tempo gasto para correção de erros.


Diagnóstico de erros lógicos

Erros lógicos em um código do Expert Advisor podem ser bem problemáticos. A ausência de opção passo a passo no EA, torna o combate a esses erros uma tarefa bastante desagradável. A principal ferramenta de diagnóstico de tais erros, atualmente, é a função integrada Print(). Ao usá-la, podemos imprimir valores atuais das variáveis importantes e registrar o fluxo de operação do EA. Ao depurar um EA durante testes com visualização, a função incorporada Comment() também pode ser útil. Como regra geral, quando o trabalho errado de um EA é confirmado, temos que adicionar uma chamada temporária da função Print() e gravar o estado interno do EA nos lugares assumidos de aparência erro.

Mas, para a detecção de situações difíceis de erro difíceis, às vezes, precisamos adicionar dúzias de tais chamadas de diagnóstico da função Print(). E depois da detecção e recuperação do problema, as chamadas de função tem que ser apagadas ou comentadas, a fim de não sobrecarregar o arquivo log do EA e para não tornar seu teste mais lento. Se o código EA já incluir a função Print() para a gravação periódica de diferentes estados, a situação é ainda pior. Em seguida, as chamadas da função temporária Print() não podem ser apagadas por uma simples pesquisa de 'Print' no código EA. Temos que pensar se desejamos excluir ou não uma função.

Por exemplo, ao gravar erros das funções OrderSend(), OrderModify() e OrderClose() é útil imprimir em um arquivo log o valor atual das variáveis Bid e Ask. Isso facilita achar motivos de erros como ERR_INVALID_STOPS e ERR_OFF_QUOTES.

Para escrever esta informação de diagnóstico em um arquivo log, recomendo o uso da seguinte função adicional:

void logInfo(string msg)
  {
    Print("INFO: " + msg);
  }

Pelos seguintes motivos:

  • Primeiro, agora tal função não será confundida com 'Print' durante a pesquisa;
  • Em segundo lugar, esta função tem uma peculiaridade mais útil que será discutida mais tarde.

Demora muito tempo para adicionar e excluir chamadas de diagnóstico temporárias da função Print (). É por isso que sugiro mais uma abordagem eficiente para a detecção de erros de lógica em um código e ajudo a economizar nosso tempo. Vamos analisar a seguinte função:

void openLongTrade(double stopLoss)
  {
    int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask, 5, stopLoss, 0);
    if(ticket == -1) 
        logError("openLongTrade", "could not open order");
  }

Neste caso, enquanto abrimos uma posição longa, é claro que em uma operação EA correta o valor do parâmetro stopLoss não pode ser maior ou igual ao preço de compra atual. Isto é, corrigir a declaração de um modo que ao chamar a função openLongTrade(), o condição StopLoss<bid seja sempre cumprida. como sabemos na fase de escrever a função analisada, podemos usá-la da seguinte maneira:


void openLongTrade( double stopLoss )
  {
    assert("openLongTrade", stopLoss < Bid, "stopLoss < Bid");
    
    int ticket = OrderSend(Symbol(), OP_BUY, 1.0, Ask, 5, stopLoss, 0);
    if(ticket == -1) 
        logError("openLongTrade", "could not open order");
  }

Isto é, inserimos a nossa declaração no código usando a nova função adicional assert(). A função em si é bastante simples:


void assert(string functionName, bool assertion, string description = "")
  {
    if(!assertion) 
        Print("ASSERT: in " + functionName + "() - " + description);
  }

O primeiro parâmetro da função é o nome da função em que nossa condição é verificada (por analogia com a função logError()). O segundo parâmetro mostra os resultados desta verificação da condição. E o terceiro parâmetro indica a sua descrição. Como resultado, se uma condição de espera não for cumprida, o arquivo log do EA irá conter as seguintes informações:

  1. o nome da função em que a condição não estava preenchida;
  2. descrição desta condição.

Como descrição podemos exibir, por exemplo, a condição em si, ou uma descrição mais detalhada contendo os valores das variáveis controladas no momento de verificação da condição, se isso ajudar a encontrar as causas de erro.

Claro que o exemplo apresentado é simplificado ao máximo. Mas eu espero que reflita a ideia muito claramente. Como a funcionalidade do EA cresce, vemos claramente como ele deve funcionar e quais condições e parâmetros de entrada são aceitáveis e quais não são. Ao inseri-lo em um código EA usando a função assert(), obtemos uma informação útil sobre o lugar em que a operação lógica do EA está quebrada. Além disso, eliminamos parcialmente a necessidade de adicionar e excluir chamadas temporárias da função Print () pois a função assert() gera mensagens de diagnóstico no arquivo log do EA somente no momento de detectar discrepâncias nas condições esperadas.

Um método mais eficaz seria utilizar esta função antes de cada operação de divisão. Na verdade, este ou aquele erro de lógica às vezes pode resultar em divisão por zero. Nesses casos, o Expert Advisor pára de funcionar e uma única linha aparece no arquivo log: 'divisão zero'. E se a operação de divisão for usada muitas vezes no código, pode ser bastante difícil detectar o local onde ocorreu o erro. E aqui a função assert() pode ser muito útil. Simplesmente precisamos inserir a verificação correspondente antes de cada operação de divisão:

assert("buildChannel", distance > 0, "distance > 0");
double slope = delta / distance;

E agora, em caso de uma divisão por zero, nós apenas olhamos através do arquivo log para descobrir onde exatamente o erro aparece.



Analisando o arquivo log do EA para detectar erros

As funções sugeridas para erros de gravação ajudam a encontrá-los facilmente no arquivo log. Só precisamos abrir o arquivo log no modo texto e procurar pelas palavras "ERROR:" e "ASSERT:". No entanto, às vezes enfrentamos situações, quando durante o desenvolvimento omitimos os resultados de chamadas desta ou daquela função integrada. Às vezes, a divisão por zero é omitida. Como podemos detectar mensagens sobre erros deste tipo entre milhares de linhas contendo informações sobre posições abertas, fechadas e alteradas? Se você enfrentar este problema, recomendo o seguinte caminho.

Abra o Microsoft Excel e baixe o arquivo log de operação do EA como um arquivo CSV, indicando como um separador deve ser usado em um ou vários espaços. Agora habilite "Autofilter". Isto lhe fornece uma oportunidade conveniente para olhar através das opções de filtro em duas colunas vizinhas (você vai entender facilmente, que colunas), para entender se o arquivo log contém erros escritos para ele por um terminal ou não. Todas as entradas geradas por funções logInfo(), logError() e assert(), começam com o prefixo ("INFO:", "ERROR:" e "ASSERT:"). As entradas do terminal sobre erros também podem ser facilmente vistas entre algumas variantes de entradas típicas sobre como trabalhar com ordens.

Esta tarefa pode ser resolvido de uma maneira mais elegante. Por exemplo, se você está bem familiarizado com as ferramentas de processamento de arquivos de texto, você pode escrever um pequeno script, que irá exibir apenas as linhas do arquivo log do EA que se referem a erros de operação do EA. Eu recomendo fortemente executar cada Expert Advisor através do verificador em um período extenso e, em seguida, analisar o arquivo de registro de sua operação utilizando os métodos descritos. Isto permite detectar a maioria dos possíveis erros antes de executar o EA em uma conta demo. Depois disso, a mesma análise de tempos em tempos do seu arquivo log de operação vai ajudar a detectar erros a tempo, peculiares à operação do EA somente em contas em tempo real.


Conclusão

As funções adicionais descritas e os métodos simples permitem simplificar o processo de detecção de erros e recuperação do código EA escrito na linguagem de programação MQL4. Para sua conveniência, as funções descritas acima foram incluídas em arquivos anexados. Basta adicionar o arquivo para o seu EA usando o diretório #include. Espero que os métodos descritos sejam utilizado com sucesso pelos operadores que se preocupam com a robustez e a correção do Expert Advisors.


Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/1473

Arquivos anexados |
debug.mqh (1.07 KB)
Strings: Tabela de Símbolos ASCII e seus usos Strings: Tabela de Símbolos ASCII e seus usos
Neste artigo iremos analisar a tabelo de símbolos ASCII e o modo de utilização. Também vamos lidar com algumas novas funções com o princípio de funcionamento baseado nas peculiaridades da tabela ASCII, e, em seguida, vamos criar uma nova biblioteca que incluirá essas funções. Elas são muito populares em outras linguagens de programação mas não são incluídos na lista de funções embutidas. Além disso, vamos examinar em detalhes os conceitos básicos do trabalho com strings. Acho que você certamente aprenderá algo novo sobre este tipo útil de dados.
Aplicação Prática dos Indicadores de Cluster no FOREX Aplicação Prática dos Indicadores de Cluster no FOREX
Indicadores de cluster são conjuntos de indicadores que dividem pares de moedas correntes em moedas distintas. Os indicadores permitem traçar a flutuação da moeda corrente, determinar o potencial de formação de novas tendências de moeda, receber sinais do mercado e seguir posições de médio e longo prazo.
Linguagem MQL4 para Iniciantes. Introdução Linguagem MQL4 para Iniciantes. Introdução
Esta sequência de artigos destina-se para operadores que não sabem nada sobre programação, mas desejam aprender a linguagem MQL4 o mais rápido possível em pouco tempo e sem dificuldades. Se você tem medo de frases como "orientação de objetos" ou "três matrizes dimensionais", este artigo é o que você precisa. As aulas são projetadas para fornecerem resultados rapidamente. Além disso, a informação é entregue de forma compreensível. Não iremos aprofundar na parte teórica, mas você vai ganhar o benefício prático já a partir da primeira aula.
Pesquisa: Estimativa de operadores que utilizam o Terminal Móvel Pesquisa: Estimativa de operadores que utilizam o Terminal Móvel
Infelizmente, não há projeções claras disponíveis neste momento sobre o futuro do trading móvel. No entanto, há uma série de especulações em torno deste assunto. Em nossa tentativa de resolver essa ambiguidade decidimos realizar uma pesquisa entre os operadores para saber suas opiniões sobre nossos terminais móveis. Através dos esforços desta pesquisa, conseguimos estabelecer uma imagem clara do que os nossos clientes pensam sobre o produto, bem como os seus pedidos e desejos em desenvolvimentos futuros de nossos terminais móveis.