Aprendendo MQL5 do iniciante ao profissional (Parte VII): Princípios de depuração de aplicativos MQL
Introdução
O artigo anterior desta série abordou os princípios para a criação de EAs em MQL5. Eu pretendia dedicar este artigo aos EAs confiáveis para o Mercado, levando em conta o tratamento de erros corrigíveis, inclusive os relacionados às respostas do servidor. E até comecei a escrevê-lo.
No entanto, os leitores dos artigos anteriores começaram a programar e a enviar perguntas por mensagem privada. Naturalmente, eram perguntas típicas de iniciantes, e, é claro eu estava disposto a respondê-las, como continuo estando. Enquanto respondia a essas perguntas, percebi que, antes de aprofundarmos mais a programação, precisava explicar como o próprio aluno poderia corrigir erros em seu código ou no código de outra pessoa. Por isso, decidi deixar os "EAs confiáveis" para o próximo artigo e falar sobre depuração agora.
Todas as pessoas cometem erros, algumas com mais frequência, outras com menos... Se você é iniciante em alguma área, provavelmente cometerá muitos erros típicos que um profissional experiente identificaria e evitaria quase que automaticamente. Ainda assim, os profissionais também erram. A diferença é que os erros deles, muitas vezes, são mais difíceis de detectar e têm consequências mais graves. Nesse sentido, a programação não difere de nenhuma outra atividade humana. No entanto, diferentemente de muitas outras áreas, os erros de programação são relativamente simples de corrigir, desde que o código do programa esteja disponível e haja tempo suficiente para isso.
A correção de erros em um programa é chamada de depuração. A depuração é uma etapa quase obrigatória na escrita de programas com mais de cem linhas. Embora viver sem erros seja impossível, corrigi-los é nosso "sagrado" direito. Por isso, neste artigo, falarei sobre os fundamentos da depuração de programas MQL5 por meio dos recursos integrados do ambiente MetaEditor. Acredito que os profissionais não encontrarão grandes revelações aqui, afinal esta série é voltada para iniciantes. Entretanto, se eu deixar algo passar ou explicar algo de forma incorreta, os comentários, como sempre, estão abertos a todos.
Examinando mensagens de erro
Os casos mais simples são erros de digitação e violações da sintaxe da linguagem. Por exemplo, se você estiver portando para MQL5 um indicador escrito em MQL4, provavelmente terá que corrigir várias chamadas de funções como iMA ou ObjectFind, pois essas funções existem nas duas versões da linguagem, mas recebem um número diferente de parâmetros e, muitas vezes, retornam valores com sentidos diferentes. Esses casos são bastante simples, pois o compilador os encontra automaticamente e exibe imediatamente informações detalhadas na janela correspondente. Por exemplo, tente compilar o seguinte código:
//+------------------------------------------------------------------+ //| ErrorsResearching.mq5 | //| Oleg Fedorov (aka certain) | //| mailto:coder.fedorov@gmail.com | //+------------------------------------------------------------------+ #property copyright "Oleg Fedorov (aka certain)" #property link "mailto:coder.fedorov@gmail.com" #property version "1.00" #property indicator_chart_window int g_someInt; // Здесь всё в порядке //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping g_some_Int = 5; // Здесь ошибка. Имя переменой указано неправильно //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
Exemplo 1. Código causador do erro de compilação
O resultado da compilação desse exemplo é mostrado na Figura 1. O compilador indicará um erro, pois o nome da variável dentro da função foi escrito incorretamente.

Figura 1. Mensagem de erro: variável não declarada
Vale a pena examinar com mais detalhes a janela mostrada na Figura 1, embora seus elementos sejam bastante óbvios. Na primeira coluna, é exibida a descrição do erro. À esquerda, aparece uma indicação da categoria à qual esse erro pertence. Neste caso:
- um triângulo amarelo indica avisos (warnings); em princípio, as linhas marcadas dessa forma podem ser ignoradas, mas, em alguns casos, sua presença pode levar a uma falha na lógica do programa, por isso ainda vale a pena analisá-las com atenção;
- um sinal vermelho de "stop" indica erros (errors), que impedem a compilação;
- pontos cinza indicam mensagens neutras, que não são erros, mas apenas exibem alguma informação.
A coluna seguinte à descrição do erro mostra o nome do arquivo em que o erro ocorreu; as duas últimas indicam, respectivamente, a linha e a coluna do erro. Tudo isso, em conjunto, fornece a coordenada exata em que o compilador "vê" o problema. Na maioria dos casos, para ir até esse ponto, basta clicar duas vezes na linha correspondente.
Às vezes, o compilador pode emitir duas ou mais mensagens por causa de um único erro. Por exemplo, se a operação de atribuição do exemplo 1 for movida para o escopo global, como foi feito no exemplo 2:
#property indicator_chart_window int g_someInt; // Здесь всё в порядке g_someInt = 5; // Здесь ошибка. Нельзя выполнять операции // (кроме инициализации) в глобальной области видимости
Exemplo 2. Tentativa de atribuir um valor no escopo global
O resultado da tentativa de compilação do exemplo 2 é mostrado na Figura 2.

Figura 2. Um erro pode gerar várias mensagens
Neste caso, o compilador tenta interpretar nossa variável como uma nova declaração. No entanto, essa declaração, além de não conter o tipo da variável, usa um nome que já existe, declarado uma linha acima. Fica claro que, neste caso, basta mover a linha de atribuição de volta para dentro de qualquer função, e os dois erros desaparecerão.
Esse exemplo deixa uma dica bastante clara: na maioria das vezes, os erros devem ser corrigidos um por um, de cima para baixo, e a compilação deve ser tentada novamente após cada correção. É claro que essa regra não é rígida. Se, por exemplo, o compilador emitir várias mensagens do mesmo tipo, como variáveis desconhecidas ou funções não declaradas, naturalmente é possível corrigi-las todas de uma vez.
E, para concluir esta seção, trago uma pequena lista de palavras-chave para quem ainda não conhece inglês o suficiente para ler mensagens de erro com facilidade. Este pequeno glossário serve apenas para ajudar você a se orientar nas mensagens do compilador no início e dificilmente substituirá o uso de dicionários comuns, mas pode ajudar iniciantes que ainda não conseguiram parar para aprender bem o idioma.
| Inglês | Português | Descrição |
|---|---|---|
| unexpected | inesperado | Na maioria das vezes, aparece quando alguma ação ou algum símbolo importante foi omitido, como o ponto e vírgula no final de uma instrução. Por exemplo, no exemplo 2, o compilador considera que, na declaração da variável, faltou indicar o tipo de dado. |
| undeclared | não declarado | Em geral, aparece quando o programador erra o nome de uma variável ou esquece de declarar uma função que ele chama em algum ponto do código. |
| unbalanced | desbalanceado | Normalmente se refere a parênteses, colchetes ou chaves. Por exemplo, há uma abertura, mas não há o fechamento correspondente, ou o contrário. |
| missing | ausente | O compilador não encontrou algo importante. |
| already | já | Normalmente aparece no contexto de "já existe". |
| wrong | incorreto, errado | Na maioria das vezes, aparece em mensagens sobre número incorreto de parâmetros em uma chamada de função. |
| invalid | inválido, incorreto | Por exemplo, o código a seguir int array_variable[]; array_variable = 5;causará o erro "invalid array access", isto é, "acesso inválido ao array", pois tentei gravar um número diretamente na variável, e não em uma célula do array. |
Tabela 1. Glossário em inglês encontrado em mensagens de erro
Erros em tempo de execução
Depois de examinarmos os erros de compilação, a parte principal da depuração só começa, pois, a partir daí, é preciso verificar se o nosso programa funciona corretamente. É muito comum o programador, por falta de atenção, deixar passar alguma etapa importante ao montar o algoritmo, alterar a variável errada por causa de copy-paste ou executar as ações fora da ordem necessária...
Assim, o código do exemplo da última linha da Tabela 1 contém, na verdade, dois erros. Mesmo que adicionemos os colchetes e o índice do elemento na segunda linha desse código, o programa ainda assim não funcionará. O problema é que o array array_variable foi declarado como dinâmico, mas seu tamanho não é definido em nenhum lugar, portanto, não é possível gravar nada nele.
Para minha infelicidade, o compilador deixará esse erro passar sem problemas, pois, formalmente, tudo estará de acordo com a sintaxe da linguagem. No entanto, em tempo de execução, o programa exibirá uma mensagem de erro: "índice do array fora do intervalo permitido" ("array out of range"). Essas mensagens, assim como todas as outras mensagens do seu programa, podem ser consultadas na aba "Experts" do terminal (Figura 3).

Figura 3. Mensagem de erro em tempo de execução
Aliás, erros "out of range", como neste exemplo, ocorrem com muita frequência. Os casos mais típicos são:
- os dados de cotações de determinado período ainda não foram carregados, mas o programa tenta lê-los;
- índice do array especificado incorretamente; por exemplo, o programador esquece que o último índice de um array é 1 menor que seu tamanho, como na terceira linha comentada do exemplo a seguir:
int testArray[3]={3,4,5}; int size = ArraySize(testArray); // Print (testArray[size]); // Ошибка! Программа пытается обратиться к несуществующему элементу массива Print (testArray[size-1]); // Всё в порядке
Exemplo 3. Caso típico de possível erro de índice de array
Nem todos os erros no algoritmo geram mensagens críticas do programa. Muitas vezes, o indicador simplesmente "não desenha", ou desenha algo completamente diferente do que foi planejado; o EA não negocia mesmo quando há situações de trade claramente visíveis no histórico; o script "trava" de tal forma que é preciso reiniciar o terminal... É aí que precisamos procurar os erros. Os métodos de busca mais típicos que eu uso estão descritos abaixo.
Por onde começar a depuração
Nesta seção, reuni recomendações que, para programadores experientes, parecem tão óbvias que dispensam qualquer explicação. Porém, os iniciantes, a julgar pelas pessoas com quem conversei pessoalmente ou em fóruns, cometem periodicamente os mesmos erros, tropeçam sempre nos mesmos pontos e fazem, das mais diferentes formas, a mesma pergunta: "Por onde começar?". Portanto...
Comece pelo começo. Se o código for seu, é claro que você o conhece melhor do que ninguém. Se for de outra pessoa, tente entender o que ele faz. Mas, no início, não mergulhe nos detalhes. Dê uma primeira olhada de cima, como uma visão geral.
- Lembre-se de que, nos EAs e indicadores, tudo começa pela função OnInit, enquanto, nos scripts e serviços, pela OnStart. Cada uma dessas funções é executada exatamente uma vez, na inicialização do programa, embora, nos serviços, geralmente se adicione um laço infinito para processar determinados eventos durante a negociação.
- Nos indicadores, a função OnCalculate é chamada a cada tick; nos EAs, é chamada OnTick. Além disso, em EAs ou indicadores, pode ser configurado um temporizador cujos eventos são tratados pela função OnTimer em intervalos regulares.
- Essas cinco funções (OnInit, OnStart, OnCalculate, OnTick, OnTimer) são pontos de entrada. É por elas que qualquer análise de código de terceiros deve começar. Se houver comentários dentro delas, isso é uma grande vantagem: basta examiná-los. É bem provável que os comentários destaquem os principais blocos da lógica. Se não houver comentários, tente identificar rapidamente quais funções são usadas e entender, pelos nomes, o que elas fazem. Se o código estiver bem estruturado e o autor não tiver colocado tudo dentro de uma única função, você está com sorte. Se não, também não é um desastre, embora talvez a depuração leve mais tempo. Em qualquer caso, tente entender a execução do algoritmo em blocos relativamente grandes, como "cálculo de parâmetros", "verificação de condições" (por exemplo, desenhar ou não desenhar), "percorrer todos os candles" e assim por diante. Naturalmente, a lista está longe de ser completa e depende muito da tarefa.
- Veja a lista de funções adicionais declaradas neste arquivo usando o botão especial na barra de ferramentas (
) ou a combinação de teclas <Alt>+<M>, e tente entender, pelos nomes, qual é a finalidade delas. - Para iniciantes, costuma ser extremamente útil inserir, nos pontos-chave, comentários com os nomes dos blocos, caso esses comentários ainda não existam. Também é muito útil desenhar um fluxograma do algoritmo, por exemplo, usando a linguagem DRAKON ou qualquer outra ferramenta de sua preferência. Repito mais uma vez: nesta etapa, vale a pena desenhar o fluxograma com blocos relativamente grandes.
- Se você adicionou um buffer ao indicador e, de repente, o indicador não desenha mais nada, verifique se:
- ao associar buffers a arrays, nas chamadas da função SetIndexBuffer, a numeração não se perdeu, ou seja, se todos os números seguem em sequência e sem lacunas, começando em 0;
- a propriedade indicator_buffers corresponde exatamente ao número total de buffers, e a propriedade indicator_plots descreve exatamente a quantidade de buffers visíveis na tela;
- as propriedades indicator_type, indicator_color etc. também estão com a numeração correta, lembrando que seus números começam em 1, e você descreveu cada buffer exibido, seja por meio de propriedades, seja por meio de funções integradas.
Uso de mensagens de depuração
O método mais antigo para encontrar erros é exibir mensagens com a função Print, ou PrintFormat, para quem preferir, nos pontos-chave do programa.
Há muito tempo, essa era a única maneira de entender por que o programa não funcionava como deveria. Mas, mesmo hoje, esse método de depuração continua popular, porque é simples e rápido e, além disso, permite salvar a saída gerada como uma espécie de documento, que pode ser analisado até mesmo no transporte público ou sentado em um banco no parque.
Naturalmente, essas mensagens devem ser informativas e permitir que o programador entenda exatamente em que ponto da execução ele está e o que está acontecendo ali. Muitas vezes, são adicionadas combinações especiais de caracteres que não aparecem no uso comum, como "~~" ou algo semelhante, para identificar o tipo da mensagem ou de onde ela foi emitida. O princípio básico de uso é muito simples: as mensagens são inseridas nos pontos-chave do código e, com elas, o programador consegue monitorar o que acontece no programa em execução.
Vamos tentar usar mensagens para entender e corrigir os problemas do código a seguir:
void OnStart() { //--- int i; //--- Print("Program starting, 'i' hasn't initialized"); for(i=0;i<3;i++); { Print("In the cycle 'i="+i+"'"); } Print("After cycle 'i="+i+"'"); }
Exemplo 4. Código do programa que não funciona corretamente.
Como exercício, tente responder quantas mensagens este programa exibirá e quais exatamente. Recomendo responder a essas perguntas antes de começar a ler o próximo parágrafo.
Provavelmente, quem escreveu esse código esperava que o programa exibisse cinco mensagens: duas fora do laço e três dentro. Mas será que isso vai funcionar neste caso? Vamos verificar:
Program starting, 'i' hasn't initialized In the cycle 'i=3' After cycle 'i=3'
Exemplo 5. Resultado do programa do exemplo 3.
O resultado é inesperado... Por que apenas três mensagens? Por que, dentro do laço, é exibido o número '3', embora devessem aparecer os números de 0 a 2? O número da segunda linha deve ser igual ao número da terceira? Vamos pensar.
- As três etapas do nosso programa são executadas: antes do laço, dentro do laço e depois do laço, e isso é bom.
- A primeira etapa passa sem erros.
- Na terceira etapa, obtemos o resultado esperado, portanto, podemos considerar que ela também está correta.
- Na segunda etapa, é exibido o mesmo número que na terceira, ou seja, o laço é executado... embora não exatamente como deveria.
Com base no que foi dito acima, podemos supor que o problema está em algum ponto entre a expressão for(i=0; ...) e a exibição dos dados necessários. Vamos examinar com mais atenção o que há ali no meio... Na verdade, ali há uma chave e um ponto e vírgula.
As chaves estão balanceadas; caso contrário, teríamos recebido uma mensagem de erro de compilação, e não os avisos exibidos. Ela serve para agrupar uma ou mais instruções simples em um bloco e, neste caso, cumpre essa tarefa com sucesso. Entre as chaves, tudo está correto: está exatamente o que precisamos, nem mais, nem menos.
O ponto e vírgula indica o fim de alguma instrução. Neste caso... o de uma instrução vazia. Ou seja, neste caso, o laço apenas incrementa inutilmente a variável de controle três vezes, e só depois a variável incrementada no laço é exibida ao usuário. Parece que tudo deve funcionar se removermos esse ponto e vírgula. Vamos verificar:
Program starting, 'i' hasn't initialized In the cycle 'i=0' In the cycle 'i=1' In the cycle 'i=2' After cycle 'i=3'
Exemplo 6. Resultado do exemplo corrigido
Funcionou! Problema resolvido.
De fato, depois de depurar por esse método, o código acaba ficando cheio de mensagens espalhadas, completamente desnecessárias para o usuário final. Se você está criando um aplicativo que não é apenas para uso próprio, provavelmente, ao final da depuração, será preciso localizar e remover toda essa massa de mensagens. Mas fazer isso com uma busca simples é incômodo, embora às vezes seja necessário. Por isso, programadores experientes, quando precisam adicionar mensagens de depuração ao código, muitas vezes as combinam com diretivas especiais do pré-processador.
Se a nossa tarefa fosse exibir os valores sucessivos do contador do laço, e as demais mensagens não fossem necessárias no aplicativo final, o código adequado e prático teria a seguinte forma:
#define DEBUG //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- int i; //--- #ifdef DEBUG Print("Program starting, 'i' hasn't initialized"); #endif for(i=0;i<3;i++) // NO semicolon!!! { Print("In the cycle 'i="+i+"'"); } #ifdef DEBUG Print("After cycle 'i="+i+"'"); #endif }
Exemplo 7. Uso de diretivas de compilação condicional.
Neste exemplo, declarei uma macro chamada DEBUG e, em seguida, nos trechos do programa necessários apenas para a depuração, pedi ao pré-processador que incluísse o código de exibição das mensagens no arquivo compilado somente se a macro DEBUG existir. A existência da macro é verificada com a diretiva #ifdef. Em outras palavras, tudo o que estiver entre #ifdef e #endif não será compilado se não houver uma macro chamada DEBUG no programa. Dessa forma, é possível ativar e desativar esses fragmentos simplesmente adicionando ou removendo a primeira linha, em um único ponto do projeto, mesmo que ele contenha vários arquivos.
E, para concluir o capítulo, vale dizer que esse erro específico, do exemplo 4, poderia ter sido encontrado de forma ainda mais simples se, já na compilação, tivéssemos prestado atenção ao aviso que o compilador tentou nos comunicar:
empty controlled statement found ErrorsResearching.mq5 23 19
Exemplo 8. Aviso do compilador sobre uma instrução de controle vazia
Essa mensagem diz que o compilador encontrou uma "instrução de controle vazia". Se você vir uma mensagem desse tipo, muito provavelmente as coordenadas indicadas apontam exatamente para um ponto e vírgula extra. Verifique sempre!
Uso do depurador interativo em programas MQL5
O MetaEditor, assim como muitas IDEs modernas, traz um depurador interativo integrado. A palavra "interativo" significa que, durante a depuração, é possível interagir com o depurador. O princípio básico dessa interação é que o programa é compilado em um modo especial, voltado para depuração. Esse modo permite acompanhar o funcionamento do programa diretamente no editor, linha por linha, e ver como as variáveis mudam nos pontos-chave do nosso programa.
No entanto, se não tomarmos medidas especiais, o programa será executado rápido demais e não veremos nada. Por isso, inserimos no editor marcas especiais chamadas "pontos de interrupção" (em inglês, breakpoints). Elas permitem pausar a execução dentro das funções do nosso programa e verificar os valores das variáveis no momento da pausa. Quando todos os valores das variáveis no ponto de interrupção estiverem claros, será possível executar a próxima instrução ou retomar a execução até o próximo ponto.
O processo de depuração em si me parece bastante intuitivo, mas, por via das dúvidas, vou descrever os botões em que você deve clicar.
- Os pontos de interrupção normalmente são colocados logo no início da execução ou depois de cada pausa.
- Para colocar ou remover um ponto de interrupção, é preciso posicionar o cursor em qualquer linha no corpo da função e usar um dos métodos:
-
clicar duas vezes na coluna que contém os números das linhas do programa atual;

Figura 4. Alternância do ponto de interrupção por clique duplo
- tecla <F9>;
- pelo menu de contexto da linha desejada ou pelo item "Depuração" no menu principal -> "Alternar ponto de interrupção" (Figura 5).

Figura 5. Menu para pontos de interrupção.
- Se você colocou vários pontos de interrupção, mas eles não são mais necessários, é possível removê-los todos de uma vez pelo menu "Depuração" (na Figura 5, o item desejado fica no menu à direita, logo abaixo da moldura vermelha) ou pela combinação de teclas <Ctrl>+<Shift>+<F9>.
É possível controlar o programa no modo de depuração usando a parte da barra de ferramentas mostrado na Figura 6.

Figura 6. Parte da barra de ferramentas destinada à depuração.
O botão à esquerda (
) inicia a depuração com dados históricos; o botão à direita (
), com dados reais. O botão azul abre o testador especial; o botão verde abre um novo gráfico diretamente no terminal. Estas são algumas diferenças entre os modos:
- se você precisa entender como funciona o programa que está sendo depurado, ou seja, executar a depuração passo a passo na barra zero, não há diferença essencial entre os botões, embora, é claro, a diferença entre o testador e o terminal ainda seja enorme;
- no testador, não é possível usar outras ferramentas de análise além do programa em execução;
- por outro lado, nele está disponível a otimização dos parâmetros do programa;
- é possível acompanhar, passo a passo, como o programa se comporta ao longo de um período mais extenso;
- os dados do terminal carregam mais rapidamente na inicialização;
- normalmente, com dados reais é conveniente depurar programas que não executam operações, especialmente indicadores e scripts;
- mas é melhor depurar a negociação automática com dados históricos, pelo menos para não sofrer prejuízos.
Qualquer que seja o modo de depuração escolhido, depois de iniciar o programa, ficarão disponíveis os botões de execução passo a passo do código, localizados à direita do painel e destacados por uma moldura na Figura 5. Cada um deles executa o próximo passo do programa, mas faz isso de maneira diferente.
- O botão mais à esquerda (
) executa o próximo passo entrando em cada função cujo código esteja aberto, exceto nas funções padrão do terminal. - O botão do meio (
) executa cada função em um único passo, como se a contornasse por fora, independentemente de ser uma função integrada do terminal ou uma função do usuário. - O botão mais à direita desse grupo (
) permite concluir a função atual em um único passo e voltar ao ponto de onde ela foi chamada.
O botão (
) encerra a sessão de depuração atual.
O botão (
) pausa o programa em execução em um ponto aleatório, normalmente no início da função OnCalculate ou de outras funções que o terminal chama periodicamente.
No painel inferior da janela de depuração, à esquerda, é exibida toda a pilha de chamadas: as últimas funções chamadas até chegar à função atual. À direita, fica a lista de todas as variáveis dessa função. Os valores nessa janela mudam automaticamente assim que forem alterados no programa.

Figura 7. Painel inferior em modo de depuração: lista de variáveis e pilha de chamadas
Na parte direita desse painel, também é possível adicionar algumas expressões avaliadas, como nomes de variáveis (por exemplo, globais) ou operações de tipo aritmético, lógico ou bit a bit. Para adicionar uma expressão à lista, você pode:
- selecionar a expressão no texto do programa e pressionar <Shift>+<F9>;
- clicar com o botão direito na expressão selecionada e escolher "Adicionar inspeção" (em inglês, "Add watch") no menu de contexto;
- clicar duas vezes em uma linha livre na janela da lista de variáveis e inserir a expressão diretamente.
Para visualizar o conteúdo de um array ou de uma estrutura, é preciso clicar duas vezes no item correspondente. No entanto, é melhor evitar expandir arrays como high ou time em indicadores: às vezes, isso pode "travar" até mesmo um computador potente por um período bastante longo. É melhor simplesmente inserir, em uma linha separada, o nome do array com o índice, por exemplo, time[i].
Se for necessário exibir um membro específico de alguma estrutura ou classe, o autocompletar funciona muito bem: você pode colocar um ponto no fim do nome ou pressionar <Ctrl>+<Espaço>, e a lista suspensa comum de sugestões deverá aparecer. Mas, infelizmente, não será possível adicionar chamadas de função não será possível.
Como exercício, proponho analisar algum indicador padrão, por exemplo, ZigZag. Abra o arquivo "Indicators -> Examples -> ZigZag.mq5" e execute no seu editor cada ação descrita nos próximos itens.
-
A primeira coisa que precisamos fazer é entender quais funções definidas pelo usuário esse indicador possui. Para ir até uma função, basta clicar no nome dela na lista (Figura 8).

Figura 8. Lista de funções do indicador ZigZag
-
Vamos definir os pontos de interrupção. Comecemos pela função OnInit, marcando seu início (no momento em que escrevo este artigo, no meu caso, é a linha 41). Em OnCalculate, marque o início (linha 66) e o ponto em que começa a busca por extremos (linha 118), para ver qual é a diferença entre o comportamento dos botões (
) e (
). -
Agora podemos colocar nosso programa em execução. Neste caso, qualquer um dos botões à esquerda serve. Suponhamos que pressionemos o verde (
) . -
Assim que o depurador parar pela primeira vez na função OnInit, você pode examiná-la rapidamente, certificar-se de que ela é trivial e de que tudo nela está claro, e pressionar tranquilamente o botão (
) para passar à próxima função. - A próxima interrupção será na função OnCalculate, na primeira linha, caso você tenha seguido as recomendações. Vale a pena analisar essa função em detalhes, porque ela contém muitas soluções úteis para quem escreve indicadores. Portanto, sinta-se à vontade para examiná-la e use os botões (
) ou (
) para avançar para o próximo passo. - Para analisar laços for, while e do ... while, você pode percorrê-los algumas vezes por completo, passo a passo, e depois usar pontos de interrupção após o fim de cada laço, continuar a execução com o botão (
) ou (
), até chegar à linha 118. - Percorra o próximo laço pelo menos uma vez com o botão (
) (passo com entrada) e observe como os valores mudam na pilha de chamadas (mostrada na Figura 7, à esquerda). - Percorra o laço pela segunda vez com o botão (
) (passar sem entrar), para ver a diferença. - O restante do código do indicador não trará nada novo em técnicas de depuração, portanto, use as técnicas que você já estudou para entender o que está acontecendo.
- Para encerrar a depuração, pressione o botão (
).
Perfilamento
Se você tem vários programas em um gráfico e há muitos gráficos desse tipo, é importante que cada programa funcione rapidamente. No entanto, às vezes acontece de o programador usar algoritmos inadequados e, embora a tarefa seja resolvida, ainda assim fica impossível usar o programa por causa da lentidão. Isso é especialmente relevante em máquinas pouco potentes, como netbooks não muito novos, que ainda hoje às vezes aparecem entre traders em pleno funcionamento.
Para identificar qual função impede o programa de "respirar" normalmente, ou, mais precisamente, como o tempo é distribuído entre as funções, usa-se o perfilamento. Nesse processo, o MetaEditor calcula o tempo gasto em cada função do nosso programa e, se esse tempo tiver alguma relevância, exibe esse tempo em um diagrama específico.
Vamos tentar perfilar o mesmo ZigZag. Para isso, é possível usar a barra de ferramentas correspondente (Figura 9) ou o menu "Depuração" (Figura 10). O perfilamento não é necessário com tanta frequência, por isso os desenvolvedores não previram combinações de teclas para ele.
![]()
Figura 9. Painel de controle do perfilamento.

Figura 10. Seção de perfilamento no menu "Depuração"
Assim como na depuração, o perfilamento pode ser iniciado com dados reais ou com dados históricos, usando o botão verde para dados reais e o azul para dados históricos. O segundo comando (
) interrompe o procedimento de perfilamento (nas Figuras 9 e 10, ele aparece desativado).
Todo o processo é simples, quase primitivo:
- iniciamos o procedimento de perfilamento com o botão "Start";
- aguardamos algum tempo, para que o programa consiga executar-se por completo;
- interrompemos o procedimento com o botão "Stop";
- examinamos os gráficos e corrigimos o que for possível (no caso do ZigZag, provavelmente não será preciso corrigir nada...).
No meu caso, ao fazer o perfilamento do ZigZag padrão (em um daqueles netbooks antigos), obtive a seguinte tabela (Figura 11):

Figura 11. Resultados do perfilamento do indicador padrão ZigZag
Verificou-se que a chamada de SetIndexBuffer ocupa a maior parte do tempo de OnInit, enquanto Highest e Lowest são tão rápidas que nem entram nessa comparação. Como esperado, OnCalculate foi a mais lenta, pois é nela que realmente ocorre a maior parte do processamento e, além disso, ela é chamada com muito mais frequência do que OnInit. É possível que a velocidade dessa função possa ser melhorada de forma perceptível.
Os desenvolvedores da MetaQuotes otimizaram muito bem o código desse indicador, mas... talvez seja justamente você quem crie um algoritmo mais rápido?
Conclusão
A depuração de aplicativos é uma das etapas mais complexas do ciclo de programação. Em alguns projetos, ela pode ocupar até 70% do tempo, às vezes até mais. Mesmo projetos que funcionam bem e têm milhares de usuários podem conter erros. O que dizer, então, do código de iniciantes? Ainda assim, espero que os métodos de depuração descritos neste artigo ajudem programadores iniciantes a encontrar seus próprios erros com mais facilidade e a estudar algoritmos de terceiros de forma eficaz.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/18075
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Caminhe em novos trilhos: Personalize indicadores no MQL5
Redes neurais em trading: decomposição em vez de escalonamento: construção dos módulos
Está chegando o novo MetaTrader 5 e MQL5
Implementação do circuito quântico de Quantum Reservoir Computing (QRC)
- 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