![Princípios básicos dos testes no MetaTrader 5](https://c.mql5.com/2/68/midjourney_image_239_100_922_3_600x314.jpg)
Princípios básicos dos testes no MetaTrader 5
Por que precisamos de um Strategy Tester
A ideia da negociação automatizada é atraente devido ao fato de que o robô de negociação pode trabalhar por 24 horas por dia, sete dias por semana. O robô não se cansa, não fica em dúvida ou assustado, é totalmente livre de quaisquer problemas psicológicos. É o suficiente para claramente formalizar as regras de negociação e implementá-las nos algoritmos e o robô está pronto para trabalhar incansavelmente. Mas, primeiro, você deve se certificar de que as seguintes duas importantes condições sejam satisfeitas:
- O Expert Advisor realiza operações de negociação de acordo com as regras do sistema de negociação?
- A estratégia de negociação, implementada no EA, demonstra um lucro no histórico?
Para responder a estas perguntas, recorremos ao Strategy Tester, incluso no terminal cliente MetaTrader 5.
Modos de geração de tick
Um Expert Advisor é um programa, escrito no MQL5 que é executado a todo momento em resposta a algum evento externo. O EA possui uma função correspondente (handler de evento) para cada evento predefinido.
O evento NewTick (alteração de preço) é o principal evento do EA e, então, precisamos gerar uma sequência de tick para testar o EA. Existem 3 modos de geração de tick implementados no Strategy Tester do terminal de cliente MetaTrader:
- A cada tick;
- OHLC de 1 minuto (os preços OHLC com barras por minuto);
- Abrindo apenas preços.
O básico e o mais detalhado é o modo "Every tick" (a cada tick), os outros dois modos são as simplificações do modo básico e serão descritos em comparação ao modo "Every tick". Considere todos os três modos a fim de entender as diferenças entre eles.
"Every Tick" (A cada tick)
Os dados de quotes históricos para instrumentos financeiros são transferidos a partir do servidor de negociação para o terminal cliente MetaTrader 5 em forma de barras por minuto acumulado. Informações detalhadas sobre a ocorrência de solicitações e a construção dos quadros de tempo necessários podem ser obtidas a partir do capítulo Organizando o Acesso de Dados da Referência MQL5.
O elemento mínimo do histórico de preço é a barra de minuto, a partir da qual você pode obter informações sobre os quatro valores de preço:
- Aberto - o preço no qual a barra de minuto foi aberta;
- Alto - o máximo que foi alcançado durante esta barra por minuto;
- Baixo - o mínimo que foi alcançado durante esta barra por minuto;
- Fechado - o preço de fechamento da barra.
A nova barra de minuto não é aberta no momento quando o novo minuto começa (número de segundos se torna igual a 0), mas quando um tick ocorre - uma mudança de preço em no mínimo um ponto. A figura mostra a barra do primeiro minuto da nova semana de negociação que possui o tempo de abertura de 2011.01.10 00:00. O intervalo de preço entre sexta e segunda, que podemos ver no gráfico, é comum, já que a taxas de moeda flutuam mesmo nos finais de semana em resposta às novas entradas.
Fig. 1. A abertura de preço entre sexta e segunda
Para esta barra apenas sabemos que a barra de minuto foi aberta em 10 de janeiro de 2011, às 00 horas 00 minutos, mas não sabemos nada sobre os segundos. Pode ter sido aberta às 00:00:12 ou 00:00:36 (12 ou 36 segundos após o início do novo dia) ou qualquer outro momento dentro daquele minuto. Mas, sabemos que o preço de Abertura do EURUSD estava em 1.28940 no momento de abertura da nova barra de minuto.
Também não sabemos dentro de qual segundo o tick, correspondente ao preço de fechamento da barra de minuto considerada, foi recebido. Sabemos apenas uma coisa - o último preço de fechamento da barra de minuto. Para este minuto, o preço era de 1.28958. O tempo de exposição dos preços High (alto) e Low (baixo) também é desconhecido, mas sabemos os preços máximos e mínimos que eram de 1.28958 e 1.28940, respectivamente.
Para testar a estratégia de negociação, precisamos de uma sequência de ticks, onde o trabalho do Expert Advisor será simulado. Desta forma, a cada barra de minuto, conhecemos os 4 pontos de controle, onde os preços definitivamente estavam. Se a barra tiver apenas 4 ticks, isso será informação suficiente para realizar o teste, mas geralmente o volume de tick é maior de 4.
Portanto, existe a necessidade de gerar pontos de controle adicionais para ticks, que ocorreu entre os preços Open (abrir), High (alto), Low (baixo) e Close (fechar). Os princípios do modo de geração de ticks "Cada tick" é descrito em O Algoritmo de Geração de Ticks dentro do Testador de Estratégia do Terminal MetaTrader 5, uma figura que é apresentada abaixo.
Fig. 2. Algoritmo de geração de ticks
Quando estiver testando no modo "Cada tick", a função OnTick() do EA será chamada a cada ponto de controle. Cada ponto de controle é um tick a partir de uma sequência gerada. O EA receberá o tempo e o preço do tick simulado, como se estivesse trabalhando online.
Importante: O modo de teste "Cada tick" é o mais preciso mas, ao mesmo tempo, o que consome mais tempo. Para um teste inicial da maioria das estratégias de comércio, geralmente é suficiente utilizar um dos outros dois modos de teste.
"OHLC de um minuto"
O modo "Cada tick" é o mais preciso dos três modos mas, ao mesmo tempo, é o mais lento. A execução do manipulador OnTick() ocorre a cada tick, enquanto o volume de tick pode ser bastante grande. Para uma estratégia, na qual a sequência de tick do movimento de preços por toda a barra não importa, há um modo de simulação mais rápido e menos minucioso - "OHLC de um minuto".
No modo "OHLC de um minuto", a sequência de ticks é construída apenas pelos preços OHLC das barras de minuto, o número dos pontos de controle gerados é significativamente reduzido - e, consequentemente, o tempo de teste. O lançamento da função OnTick() é realizado em todos os pontos de controle, que são construídos pelos preços das barras de minuto OHLC.
A recusa em gerar ticks intermediários adicionais entre os preços Open, High, Low e Close, leva a uma aparência de determinismo rígido no desenvolvimento dos preços, a partir do momento em que o preço Open é determinado. Isto torna possível criar um "graal de teste", que mostra um belo gráfico ascendente do balanço de teste.
Um exemplo deste Graal é apresentado na base de código - Grr-al.
Fig. 3. O Grr-al Expert Advisor utiliza as características especiais dos preços OHLC
A figura mostra um gráfico bem atraente deste teste EA. Como ele foi obtido? Sabemos 4 preços para uma barra de minuto, e também sabemos que o primeiro é o preço Open, e o último é o preço Close. Temos os preços High e Low entre eles, e a sequência de sua ocorrência é desconhecida, mas é conhecido que o preço High é maior ou igual ao preço Open (e o preço Low é menor ou igual ao preço Open).
É suficiente determinar o tempo de recebimento do preço Open, e então analisar o próximo tick, para determinar se o preço High ou Low vem antes disso. Se o preço estiver abaixo do preço Open, então temos um preço Low e compra neste tick, o próximo tick corresponderá ao preço High, no qual fecharemos a compra e abriremos para venda. O próximo tick é o último, este é o preço Close, e fecharemos a venda nele.
Se após o preço recebermos um tick com o preço maior que o preço de abertura, então a sequência de negociações está invertida. Processe uma barra de minuto neste modo de "trapaça" e aguarde a próxima.
Quando estiver testando tal EA na história, tudo ocorre suavemente, mas uma vez que lançamos ele online, a verdade começa a ser revelada - a linha de balanço permanece estável, mas se dirige para baixo. Para expor este truque, precisamos simplesmente executar o EA em cada modo "Cada tick".
Observação: Se os resultados do EA nos modos de teste menos minuciosos ("OHLC de 1 minuto" e "apenas preços Open") parecem bons demais, certifique-se de testá-los no modo "Cada tick".
"Apenas preços open"
Neste modo, os ticks são gerados com base nos preços OHLC do prazo selecionado para testes. A função OnTick() do Expert Advisor é executada apenas no começo da barra no preço Open. Devido a esta característica, os níveis de parada e pendente podem ser disparados em um preço que difere do especificado (especialmente quando estiver testando com prazos maiores). Em vez disso, temos uma oportunidade de executar rapidamente um teste de avaliação do Expert Advisor.
Uma exceção na geração de ticks no modo "Apenas preços open" são os períodos W1 e MN1: para estes ticks de prazo, são gerados os preços OHLC de cada dia, não preços OHLC na semana ou mês.
- O cálculo dos requisitos de margem;
- O acionamento dos níveis Stop Loss (interromper perda) e Take Profit (obter lucro);
- O acionamento dos pedidos pendentes;
- A remoção dos pedidos pendentes expirados.
Se não existirem posições em aberto ou pedidos pendentes, não necessitamos realizar estas verificações em ticks escondidos, e o aumento da velocidade pode ser bastante substancial. Este modo "Apenas preços open" serve bem para estratégias de teste, que processam negociações apenas na abertura da barra e não utilizam pedidos pendentes, bem como pedidos StopLoss e TakeProfit. Para a classe de tais estratégias, a precisão necessária do teste é preservada.
Vamos utilizar o Expert Advisor Moving Average do pacote padrão como um exemplo de EA que pode ser testado em cada modo. A lógica deste EA é construída de maneira que todas as decisões são tomadas na abertura da barra, e as negociações são realizadas imediatamente, sem o uso de pedidos pendentes.
Execute um teste do EA em EURUSD H1 em um intervalo de 2010.01.09 a 2010.31.12, e compare os gráficos. A figura mostra o gráfico de balanço do relatório de teste para todos os três modos.
Fig. 4. O gráfico de teste do EA Moving Average.mq5 do pacote padrão não depende do modo de teste (clique na imagem para aumentá-la)
Como você pode ver, os gráficos nos diferentes modos de teste são exatamente os mesmos para o EA Moving Average do pacote padrão.
Há algumas limitações no modo "Apenas preços open":
- Você não pode utilizar o modo de execução Random Delay (atraso aleatório);
- No Expert Advisor testado, você não pode acessar dados do prazo mais baixos do que os usados para teste/otimização. Por exemplo, se você executar o teste/otimização no período H1, você pode acessar os dados de H2, H3, H4, etc., mas não M30, M20, M10 etc. Além disso, os prazos maiores que são acessados devem ser múltiplos do prazo de teste. Por exemplo, se você executar o teste em M20, você não pode acessar dados de M30, mas é possível acessar H1. Estas limitações são conectadas com a impossibilidade de obter dados de prazos menores ou não-múltiplos das barras geradas durante teste/otimização;
- As limitações ao acessar dados de outros prazos também aplicam-se a outros símbolos cujos dados são usados pelo Expert Advisor. Neste caso, a limitação para cada símbolo depende do primeiro prazo acessado durante o teste/otimização. Suponha que, durante o teste no EURUSD H1, um Expert Advisor acessa dados de GBPUSD M20. Neste caso, o Expert Advisor também será capaz de utilizar dados de EURUSD H1, H2, etc., bem como GBPUSD M20,H1, H2, etc.
Observação: O modo "Apenas preços open" possui o tempo de teste mais rápido, mas não é o mais adequado para todas as estratégias de comércio. Selecione o modo de teste desejado com base nas características do sistema de comércio.
Para concluir a seção dos modos de geração de tick, vamos considerar uma comparação visual dos diferentes modos de geração de tick para EURUSD, para duas barras M15 em um intervalo de 2011.01.11 21:00:00 - 2011.01.11 21:30:00.
Os ticks foram salvos em diferentes arquivos, usando o EA WriteTicksFromTester.mq5 e o final destes nomes de arquivo são especificados nos parâmetros de entrada filenameEveryTick, filenameOHLC e filenameOpenPrice.
Fig. 5. Podemos especificar as datas de início e fim dos ticks (as variáveis start e end) para o Expert Advisor WriteTicksFromTester
Para obter três arquivos com as sequências de três ticks (para cada um dos seguintes modos "Cada tick", "OHLC de um minuto" e "Apenas preços open"), o EA foi executado três vezes nos modos correspondentes, em execuções únicas. Então os dados destes três arquivos foram exibidos no gráfico, usando o indicador TicksFromTester.mq5. O código do indicador está anexado a este artigo.
Fig. 6. A sequência de tick no Strategy Tester do terminal MetaTrader 5 em três diferentes modos de teste
Predefinidamente, todas as operações de arquivo na linguagem MQL5 são criadas dentro do "sandbox de arquivo" e, durante o teste, o EA tem acesso apenas aos seu próprio "sandbox de arquivo". Para que o indicador e o EA trabalhem com os arquivos de uma pasta durante o teste, usamos a flag FILE_COMMON. Um exemplo do código do EA:
//--- open the file file=FileOpen(filename,FILE_WRITE|FILE_CSV|FILE_COMMON,";"); //--- check file handle if(file==INVALID_HANDLE) { PrintFormat("Error in opening of file %s for writing. Error code=%d",filename,GetLastError()); return; } else { PrintFormat("The file will be created in %s folder",TerminalInfoString(TERMINAL_COMMONDATA_PATH)); }
Para ler os dados no indicador usamos a flag FILE_COMMON. Isso nos permitiu evitar a transferência manual de arquivos necessários de uma pasta para outra.
//--- open the file int file=FileOpen(fname,FILE_READ|FILE_CSV|FILE_COMMON,";"); //--- check file handle if(file==INVALID_HANDLE) { PrintFormat("Error in open of file %s for reading. Error code=%d",fname,GetLastError()); return; } else { PrintFormat("File will be opened from %s",TerminalInfoString(TERMINAL_COMMONDATA_PATH)); }
Simulação de spread
A diferença de preço entre os preços Bid e Ask é chamada de spread. Durante o teste o spread não é modelado, mas tirado dos dados do histórico. Se o spread for menor ou igual a zero nos dados do histórico, então o spread para o tempo dos dados do histórico solicitado é usado pelo agente de teste.
No Strategy Tester, o spread é sempre considerado flutuante. Ou seja, SymbolInfoInteger (símbolo, SYMBOL_SPREAD_FLOAT) sempre retorna verdadeiro.
Além disso, os dados do histórico contém valores de tick e volumes de negociação. Para a recuperação e armazenamento de dados, usamos a estrutura MqlRates especial:
struct MqlRates { datetime time; // opening bar time double open; // opening price Open double high; // the highest price High double low; // the lowest price Low double close; // the closing price Close long tick_volume; // the tick volume int spread; // spread long real_volume; // market volume };
As variáveis globais do terminal cliente
Durante o teste, as variáveis globais do terminal cliente também são emuladas, mas não são relacionadas com as atuais variáveis globais do terminal, o que pode ser visto no terminal usando o botão F3. Isso significa que todas as operações com variáveis globais do terminal, durante o teste, acontecem fora do terminal cliente (no agente de teste).
O cálculo dos indicadores durante teste
No modo de tempo real, os valores do indicador são calculados a cada tick. O Strategy Tester adotou um modelo econômico para calcular indicadores - indicadores são recalculados apenas imediatamente antes da execução do EA. Isso significa que o recálculo dos indicadores é feito antes da chamada das funções OnTick(), OnTrade() e OnTime().
Não importa se há ou não uma chamada pelo indicador no handler de evento específico, todos os indicadores, os manipuladores que foram criados pelas funções iCustom() ou IndicatorCreate() serão recalculados antes de chamar o handler de evento.
Consequentemente, ao testar no modo "Every tick", o cálculo dos indicadores acontece antes da chamada da função OnTick().
Se o temporizador estiver no EA usando a função EventSetTimer() os indicadores serão recalculados antes de cada chamada do handler OnTimer(). Consequentemente, o tempo de teste pode ser altamente aumentado com o uso de indicadores, escrito de uma forma não ideal.
Carregamento do histórico durante o teste
O histórico de um símbolo testado é sincronizado e carregado pelo terminal a partir de um servidor de trade começando pelo processo de teste. Durante a primeira vez, o terminal carrega todo o histórico disponível de um símbolo com o objetivo de não solicitá-lo posteriormente. Futuramente, apenas dados novos são carregados.
Um agente de teste recebe o histórico de um símbolo testado a partir do terminal cliente exatamente após o início do teste. Se os dados de outros instrumentos são usados no processo de teste (por exemplo, um Expert Advisor que envolve várias moedas), o agente de teste solicita o histórico necessário do terminal cliente durante a primeira chamada de tal dado. Se os dados do histórico estiverem disponíveis no terminal, eles são imediatamente passados para o agente de teste. Se os dados estiverem indisponíveis, o terminal os solicita e os baixa do servidor e, então, os passa para o agente de teste.
Os dados de instrumentos adicionais também são necessários para calcular taxas cruzadas para operações de trade. Por exemplo, ao testar uma estratégia no EURCHF com a moeda de depósito em dólar americano, antes do processamento da primeira operação de trading, o agente de teste solicita os dados do histórico de EURUSD e USDCHF para o terminal cliente, embora a estratégia não contenha a chamada de uso direto destes símbolos.
Antes de testar uma estratégia de várias moedas é recomendado baixar todos os dados do histórico necessários para o terminal cliente. Isso ajuda a evitar atrasos na otimização/teste associados com o download dos dados necessários. Você pode baixar o histórico, por exemplo, abrindo os gráficos adequados e levá-los para o início do histórico. Um exemplo de carregamento forçado do histórico no terminal está disponível na seção Organizing Access to Data (Organizando o Acesso a Dados) da Referência MQL5.
Teste envolvendo várias moedas
O Strategy Tester nos permite realizar um teste de estratégias, negociando com vários símbolos. Tais EAs são convenientemente referidos como Expert Advisors que envolvem diversas moedas, visto que originalmente, nas plataformas anteriores, o teste era realizado apenas para um único símbolo. No Strategy Tester do terminal MetaTrader 5, podemos modelar a negociação para todos os símbolos disponíveis.
O testador carrega o histórico dos símbolos utilizados a partir do terminal cliente (não do servidor de trade!) automaticamente durante a primeira chamada dos dados de símbolo.
O agente de teste baixa apenas o histórico que está faltando, com uma pequena margem para providenciar os dados necessários no histórico, para o cálculo dos indicadores no início do teste. Para os quadros de tempo D1 e menores, o volume mínimo de histórico baixado é de um ano.
Assim, se executarmos um teste em um intervalo de 2010.11.01-2010.12.01 (testando para um intervalo de um mês) com um período de M15 (cada barra é igual a 15 minutos), então, o terminal será solicitado pelo histórico para o instrumento para todo o ano de 2010. Para o quadro de tempo semanal, solicitaremos um histórico de 100 barras, o que é cerca de 2 anos (um ano tem 52 semanas). Para testar em um quadro de tempo mensal, o agente solicitará o histórico de 8 anos (12 meses x 8 anos = 96 meses).
Se não houverem barras necessárias, a data inicial do teste será automaticamente trocada do passado para o presente para fornecer a reserva necessária de barras antes do teste.
Durante o teste, o "Market Watch" é emulado também, dele se pode obter as informações sobre símbolos.
Predefinidamente, no começo do teste, existe apenas um símbolo no "Market Watch" do Strategy Tester - o símbolo que o teste está executando. Todos os símbolos necessários são conectados ao "Market Watch" do Strategy Tester (não terminal!) automaticamente quando designados a isso.
Antes de começar o teste de um Expert Advisor para diversas moedas, é necessário selecionar os símbolos necessários para teste no "Market Watch" do terminal e carregar os dados necessários. Durante a primeira chamada de um símbolo "estranho", seu histórico será automaticamente sincronizado entre o agente de teste e o terminal cliente. Um símbolo "estranho" é o símbolo diferente daquele no qual o teste está executando.
A consulta dos dados de um "outro" símbolo ocorre nos seguintes casos:
- Ao usar a função de indicadores técnicos e IndicatorCreate() no quadro de tempo/símbolo;
- Solicitação aos dados do "Market Watch" para outro símbolo:
- Solicitação da série de tempo para um quadro de tempo/símbolo usando as seguintes funções:
No momento da primeira chamada de um "outro" símbolo, o processo de teste é interrompido e é feito o download do histórico para o quadro de tempo/símbolo do terminal para o agente de teste. Ao mesmo tempo, é criada a sequência de tick para este símbolo.
Uma sequência de tick individual é gerada para cada símbolo, de acordo com o modo de geração de tick selecionado. Você também pode solicitar o histórico explicitamente para os símbolos desejados chamando a função SymbolSelect() no handler OnInit() - será feito imediatamente o download do histórico antes do teste do Expert Advisor.
Assim, não há necessidade de esforços extras para realizar o teste com diversas moedas no terminal cliente MetaTrader 5. Apenas abra os gráficos dos símbolos adequados no terminal cliente. Será automaticamente feito o upload do histórico a partir do servidor de trade para todos os símbolos necessários, desde que ele contenha estes dados.
Simulação do tempo no Strategy Tester
Durante o teste, a hora local TimeLocal() é sempre igual ao horário do servidor TimeTradeServer(). Por sua vez, a hora do servidor é sempre igual ao horário correspondente ao horário GMT - TimeGMT(). Assim, todas estas funções mostram a mesma hora durante o teste.
A ausência de diferença entre o horário GMT, local e do servidor no Strategy Tester é feita deliberadamente no caso de não haver conexão com o servidor. Os resultados do teste devem sempre ser os mesmos, independente se há ou não conexão. As informações sobre o horário do servidor não estão armazenadas localmente e são retiradas do servidor.
A função OnTimer() no Strategy Tester
O MQL5 fornece a oportunidade de manusear eventos de tempo. A chamada do handler OnTimer() é feita independente do modo de teste.
Isso significa que se um teste estiver sendo executado no modo "Open prices only" (Apenas preços abertos) para o período H4, e o EA tiver um ajuste de tempo para uma chamada por segundo, então a abertura de cada barra H4 no handler OnTick() será chamado uma vez, e o handler OnTimer() será chamado 14400 vezes (3600 segundos * 4 horas). A quantidade na qual o tempo de teste do EA será aumentada depende da lógica do EA.
Para verificar a dependência do tempo de teste da frequência dada do temporizador, foi escrito um simples EA sem quaisquer operações de negociação.
//--- input parameters input int timer=1; // timer value, sec input bool timer_switch_on=true; // timer on //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- run the timer if timer_switch_on==true if(timer_switch_on) { EventSetTimer(timer); } //--- return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- stop the timer EventKillTimer(); } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //--- // take no actions, the body of the handler is empty } //+------------------------------------------------------------------+
As medições de tempo de teste foram realizadas em diferentes valores do parâmetro do temporizador (periodicidade do evento Timer). Nos dados obtidos, traçamos um tempo de teste como função de período de tempo.
Fig. 7. Tempo de teste como uma função do período do temporizador
Pode ser claramente visto que o menor é o temporizador de parâmetro, durante a inicialização da função EventSetTimer (Temporizador), o menor é o período (Period) entre as chamadas do handler OnTimer() e o maior é o tempo de teste T, sob as mesmas outras condições.
A função Sleep() no Strategy Tester
A função Sleep() permite que o EA ou script suspenda a execução do programa mql5 por um instante quando está trabalhando no gráfico. Isso pode ser útil quando estiver solicitando dados, o que não é está pronto no momento da solicitação e você precisa aguardar até que esteja pronto. Um exemplo detalhado da função Sleep() pode ser encontrado na seção Data access arrangement (Organização do acesso a dados).
O processo de teste não é prolongado pelas chamadas Sleep(). Quando você chama a função Sleep(), os ticks gerados são "executados" dentro de um atraso especificado, o que pode resultar no acionamento de ordens pendentes, paradas, etc. Após a chamada da função Sleep(), o tempo simulado no Strategy Tester aumenta por intervalo, especificado no parâmetro da função Sleep.
Se como um resultado da execução da função Sleep(), o tempo atual no Strategy Tester subir além do período de teste, nesse caso você receberá uma mensagem de erro "Infinite Sleep loop detected while testing" (Loop sleep infinito detectado durante o teste). Se você receber essa mensagem de erro, os resultados do teste não são rejeitados, todos os cálculos são realizados em seu volume completo (o número de negócios, subsidência, etc.), e os resultados deste teste são passados para o terminal.
A função Sleep() não funcionará em OnDeinit(), porque após ser chamada, o tempo de teste garantidamente ultrapassará a faixa do intervalo de teste.
Fig. 8. O esquema de uso da função Sleep() no Strategy Tester do terminal MetaTrader 5
Utilização do Strategy Tester para otimizar problemas em cálculos matemáticos
O testador no terminal MetaTrader 5 pode ser usado não apenas para testar estratégias de negociação, mas também para cálculos matemáticos. Para usá-lo é necessário selecionar o modo "Math calculations" (Cálculos matemáticos):
Neste caso, apenas três funções serão chamadas: OnInit(), OnTester() e OnDeinit(). No modo "Math calculations" o Strategy Tester não cria nenhum tick e faz download de histórico.
O Strategy Tester trabalha no modo "Math calculations" também se você especificar uma data inicial maior que a data final.
Ao utilizar o testador para resolver problemas matemáticos, não ocorre o upload de histórico e a geração de ticks.
Um problema matemático comum para solução no Strategy Tester do MetaTrader 5 - procurando por um extremo de uma função com muitas variáveis.
Para resolver, precisamos:
- O cálculo do valor da função deve ser colocado na função OnTester();
- Os parâmetros da função devem ser definidos como variáveis de entrada do Expert Advisor;
Compile o EA, abra a janela "Strategy Tester". Na guia "Input parameters" (Parâmetros de entrada), selecione as variáveis de entrada desejadas e defina o conjunto de valores dos parâmetros especificando os valores de início, parada e incremento para cada uma das variáveis da função.
Selecione o tipo de otimização - "Slow complete algorithm" (Algoritmo completo lento), "Full search of parameters space" (busca completa de espaço de parâmetros) ou "Fast genetic based algorithm" (Algoritmo genético rápido). Para uma busca simples do extremo da função, é melhor escolher a otimização rápida, mas se quiser calcular os valores para todo o conjunto de variáveis, então é melhor usar a otimização lenta.
Selecione o modo "Math calculation" e usando o botão "Start" (Iniciar), execute o procedimento de otimização. Observe quando estiver otimizando que o Strategy Tester procurará pelos valores máximos da função. Para encontrar um mínimo local, retorne o inverso do valor da função computada a partir da função OnTester:
return(1/function_value);
É necessário verificar se o function_value (valor da função) não é igual a zero, já que caso contrário podemos obter um erro crítico dividindo por zero.
Não há outra forma, é mais conveniente e não distorce os resultados da otimização, isso foi sugerido pelos leitores deste artigo:
return(-function_value);
Esta opção não requer a verificação do function_value_ por ser igual a zero, e a superfície dos resultados de otimização em uma representação 3D tem o mesmo formato, mas é espelhada do original.
Como um exemplo, providenciamos a função sink():
O código do EA para encontrar o extremo desta função é posicionado na função OnTester():
//+------------------------------------------------------------------+ //| Sink.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //--- input parameters input double x=-3.0; // start=-3, step=0.05, stop=3 input double y=-3.0; // start=-3, step=0.05, stop=3 //+------------------------------------------------------------------+ //| Tester function | //+------------------------------------------------------------------+ double OnTester() { //--- double sink=MathSin(x*x+y*y); //--- return(sink); } //+------------------------------------------------------------------+Realize uma otimização e veja os resultados de otimização na forma de um gráfico 2D.
Fig. 9. Os resultados da otimização completa da função sink (x*x+y*y) como gráfico 2D
Quanto melhor é o valor para um dado par de parâmetros (x, y), mais saturada é a cor. Como esperado da visualização da forma da fórmula sink(), seus valores formam círculos concêntricos com um centro em (0,0). Se pode ver no gráfico 3D que a função sink() não possui um extremo global único:
A sincronização das barras no modo "Open prices only" (Apenas preços abertos)
O testador no terminal cliente MetaTrader 5 nos permite verificar os chamados EAs de diversas moedas. Um EA de diversas moedas - é um EA que negocia com um ou mais símbolos.
O teste de estratégias que são negociadas em diversos símbolos, impõe algumas exigências técnicas adicionais no testador:
- A geração de ticks para estes símbolos;
- O cálculo dos valores do indicador para estes símbolos;
- O cálculo dos requerimentos de margem para estes símbolos;
- Sincronização das sequências de tick geradas para todos os símbolos de negociação.
O Strategy Tester cria e desempenha uma sequência de tick para cada instrumento de acordo com o modo de negociação selecionado. Ao mesmo tempo, uma nova barra para cada símbolo é aberta, independente de como a barra é aberta em outro símbolo. Isso significa que ao testar um EA de diversas moedas, um problema pode ocorrer (e geralmente ocorre), quando para um instrumento uma nova barra já está aberta e para o outro não está. Assim, no teste, tudo acontece exatamente como na realidade.
Esta simulação autêntica do histórico no testador não causa nenhum problema, desde que os modos "Every tick" e "1 minute OHLC" sejam usados. Para estes modos, ticks suficientes são gerados para um candlestick, para ser capaz de aguardar que ocorra a sincronização das barras de diferentes símbolos. Mas como testamos estratégias de diversas moedas no modo "Open prices only" se a sincronização das barras nos instrumentos de negociação é obrigatória? Neste modo, o EA é chamado apenas em um tick, o que corresponde ao tempo de abertura das barras.
Ilustraremos isso em um exemplo: se estivermos testando um EA em EURUSD e um novo candlestick horário abriu em EURUSD, então podemos facilmente reconhecer este fato - no teste no modo "Open prices only", o evento NewTick corresponde ao momento de abertura da barra no período de teste. Mas não há nenhuma garantia que o novo candlestick abriu no símbolo USDJPY que é usado no EA.
Sob circunstâncias normais, é o suficiente completar o trabalho da função OnTick() e verificar pela emergência de uma nova barra no USDJPY no próximo tick. Mas ao testar no modo "Open prices only", não haverá outro tick e então pode parecer que este modo não seja adequado para testar EAs de diversas moedas. Mas não é desta maneira - não esqueça que o testador no MetaTrader 5 comporta-se como na vida real. Você pode aguardar até que uma nova barra seja aberta em outros símbolos usando a função Sleep()!
O código do EA Synchronize_Bars_Use_Sleep.mq5, que mostra um exemplo da sincronização de barras no modo "Open prices only":
//+------------------------------------------------------------------+ //| Synchronize_Bars_Use_Sleep.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //--- input parameters input string other_symbol="USDJPY"; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- check symbol if(_Symbol==other_symbol) { PrintFormat("You have to specify the other symbol in input parameters or select other symbol in Strategy Tester!"); //--- forced stop testing return(-1); } //--- return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- static variable, used for storage of last bar time static datetime last_bar_time=0; //--- sync flag static bool synchonized=false; //--- if static variable isn't initialized if(last_bar_time==0) { //--- it's first call, save bar time and exit last_bar_time=(datetime)SeriesInfoInteger(_Symbol,Period(),SERIES_LASTBAR_DATE); PrintFormat("The last_bar_time variable is initialized with value %s",TimeToString(last_bar_time)); } //--- get open time of the last bar of chart symbol datetime curr_time=(datetime)SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE); //--- if times aren't equal if(curr_time!=last_bar_time) { //--- save open bar time to the static variable last_bar_time=curr_time; //--- not synchronized synchonized=false; //--- print message PrintFormat("A new bar has appeared on symbol %s at %s",_Symbol,TimeToString(TimeCurrent())); } //--- open time of the other symbol's bar datetime other_time; //--- loop until the open time of other symbol become equal to curr_time while(!(curr_time==(other_time=(datetime)SeriesInfoInteger(other_symbol,Period(),SERIES_LASTBAR_DATE)) && !synchonized)) { PrintFormat("Waiting 5 seconds.."); //--- wait 5 seconds and call SeriesInfoInteger(other_symbol,Period(),SERIES_LASTBAR_DATE) Sleep(5000); } //--- bars are synchronized synchonized=true; PrintFormat("Open bar time of the chart symbol %s: is %s",_Symbol,TimeToString(last_bar_time)); PrintFormat("Open bar time of the symbol %s: is %s",other_symbol,TimeToString(other_time)); //--- TimeCurrent() is not useful, use TimeTradeServer() Print("The bars are synchronized at ",TimeToString(TimeTradeServer(),TIME_SECONDS)); } //+------------------------------------------------------------------+
Perceba a última linha no EA, que mostra a hora atual quando o fato da sincronização foi estabelecido:
Print("The bars synchronized at ",TimeToString(TimeTradeServer(),TIME_SECONDS));
Para exibir a hora atual, usamos a função TimeTradeServer () em vez de TimeCurrent (). A função TimeCurrent() retorna a hora do último tick, que não muda após utilizar Sleep(). Execute o EA no modo "Open prices only" e você verá uma mensagem sobre a sincronização das barras.
Use a função TimeTradeServer() em vez de TimeCurrent(), se você precisar obter a hora atual do servidor, e não a hora da chegada do último tick.
Aqui está outra maneira de sincronizar barras - usando um temporizador. Um exemplo de tal EA é Synchronize_Bars_Use_OnTimer.mq5, que está anexado a este artigo.
A função IndicatorRelease() no testador
Após completar um único teste, um gráfico do instrumento é automaticamente aberto, e mostra as negociações completadas e os indicadores usados no EA. Isto ajuda a verificar visualmente os pontos de entrada e saída e compará-los com os valores dos indicadores.
Observação: Indicadores, mostrados no gráfico, que automaticamente abrem-se após a conclusão do teste, são calculados novamente após a conclusão do teste. Mesmo se estes indicadores foram usados no EA testado.
Mas em alguns casos, o programador pode desejar ocultar a informação na qual os indicadores foram envolvidos nos algoritmos de comércio. Por exemplo, o código do EA é alugado ou vendido como um arquivo executável, sem a provisão do código-fonte. Para este propósito, a função IndicatorRelease() é adequada.
Se o terminal definir um template com o nome tester.tpl no diretório/perfis/templates do terminal cliente, então ele será aplicado ao gráfico aberto. Em sua ausência, o template padrão é aplicado (default.tpl).
A função IndicatorRelease() é originalmente destinada para liberar a porção de cálculo do indicador, se ela não for mais necessária. Isto permite que você economize tanto memória quanto recursos de CPU, porque cada tick chama um cálculo de indicador. Seu segundo propósito é proibir a exibição de um indicador no gráfico de teste, após uma única execução de teste.
Para proibir a exibição do indicador no gráfico após o teste, chame IndicatorRelease() com o manipulador do indicador no manipulador OnDeinit(). A função OnDeinit() sempre é chamada após a conclusão e antes da exibição do gráfico de teste().
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- bool hidden=IndicatorRelease(handle_ind); if(hidden) Print("IndicatorRelease() successfully completed"); else Print("IndicatorRelease() returned false. Error code ",GetLastError()); }
Manipulação de eventos no testador
A presença do manipulador OnTick() no EA não é obrigatória para que ele esteja sujeito a teste em dados históricos no testador do MetaTrader 5. É suficiente para o tick do EA conter pelo menos um dos seguintes manipuladores de função:
- OnTick() - Handler do evento de chegada de um novo tick;
- OnTrade() - Handler do evento de negociação;
- OnTimer() - Handler do evento de chegada do sinal do temporizador;
- OnChartEvent () - Um handler para eventos de cliente.
Quando estiver testando no EA, podemos manipular eventos personalizados usando a função OnChartEvent(), mas nos indicadores esta função não pode ser chamada no testador. Mesmo que o indicador possua o handler de eventos OnChartEvent() e este indicador seja usado no EA testado, o indicador em si não receberá nenhum evento personalizado.
Durante o teste, um indicador pode gerar eventos personalizados usando a função EventChartCustom(), e o EA pode processar este evento em OnChartEvent().
Testando agentes
Os testes no terminal cliente do MetaTrader 5 são realizados usando agentes de teste. Agentes locais são criados e habilitados automaticamente. O número padrão de objetos locais corresponde ao número de núcleos em um computador.
Cada agente de teste possui sua própria cópia das variáveis globais, que não estão relacionadas ao terminal cliente. O terminal em si é o despachante, que distribui as tarefas aos agentes locais e remotos. Após executar uma tarefa no teste de um EA, com os parâmetros fornecidos, o agente retorna os resultados ao terminal. Com um único teste, apenas um agente é usado.
O agente armazena o histórico, recebido do terminal, em pastas separadas, por nome de instrumento, então o histórico para EURUSD é armazenado em uma pasta chamada EURUSD. Além disso, o histórico dos instrumentos é separado por suas fontes. A estrutura para armazenar o histórico é semelhante à seguinte:
Por exemplo, o histórico para EURUSD do servidor MetaQuotes-Demo pode ser armazenado na pasta tester_catalog\Agent-127.0.0.1-3000\bases\MetaQuotes-Demo\EURUSD.
Um agente local, após a conclusão do teste, entra em modo de standby, aguardando a próxima tarefa por 5 minutos, para não desperdiçar tempo na execução para a próxima chamada. Apenas após o término do período de espera, o agente local desativa-se e é descarregado da memória da CPU.
No caso de uma conclusão antecipada do teste pelo lado do usuário (o botão "Cancel" (Cancelar)), bem como o fechamento do terminal cliente, todos os agentes locais imediatamente interrompem seu serviço e são descarregados da memória.
A troca de dados entre o terminal e o agente
Quando você executa um teste, o terminal cliente prepara-se para enviar ao agente um número de blocos de parâmetro: - Parâmetros de entrada para teste (modo de simulação, o intervalo de teste, instrumentos, critérios de otimização, etc.);
- A lista dos símbolos selecionados no "Market Watch";
- A especificação do símbolo de testes (o tamanho do contrato, as margens permitidas para o mercado, para definir um StopLoss e Takeprofit, etc.);
- O Expert Advisor e os valores de seus parâmetros de entrada;
- Informações sobre arquivos adicionais (bibliotecas, indicadores, arquivos de dados - # property tester_ ...).
tester_indicator
Nome de um indicador personalizado no formato "indicator_name.ex5". Indicadores que requeiram teste são definidos automaticamente a partir da chamada da função iCustom(), se o parâmetro correspondente for definido por meio de uma constante string. Para todos os outros casos (utilização da função IndicatorCreate() ou utilização de uma string não constante no parâmetro que define o nome do indicador), esta propriedade é necessária.
tester_file
Nome do arquivo para um testador com a indicação de extensão, entre aspas duplas (como uma constante string). O arquivo especificado será passado ao testador. Arquivos de entrada a serem testados, se houver necessários, devem sempre ser especificados.
tester_library
Nome da biblioteca com a extensão, entre aspas duplas. Uma biblioteca pode possuir a extensão dll ou ex5. Bibliotecas que requerem teste são definidas automaticamente. Entretanto, se uma das bibliotecas for usada por um indicador personalizado, esta propriedade é necessária.
Para cada bloco de parâmetros, uma assinatura digital no formato de um hash MD5 é criada e enviada ao agente. O Hash MD5 é único para cada conjunto, seu volume é inúmeras vezes menor que a quantidade de informação sobre a qual ele é calculado.
O agente recebe um hash de blocos e compara-os com aqueles que já possui. Se a assinatura de um determinado bloco de parâmetro não estiver presente no agente ou o hash recebido for diferente do existente, o agente requisita este bloco de parâmetros. Isto reduz o tráfego entre o terminal e o agente.
Após o teste, o agente retorna ao terminal todos os resultados da execução, que são mostrados nas abas "Test Results" (Resultados do teste) e "Optimization Results" (Resultados de otimização): o lucro recebido, o número de negócios, o coeficiente Sharpe, o resultado da função OnTester(), etc.
Durante a otimização, o terminal entrega tarefas de teste aos agentes em pequenos pacotes, cada pacote contendo diversas tarefas (cada tarefa significa um único teste com um conjunto de parâmetros de entrada). Isto reduz o tempo de troca entre o terminal e o agente.
Os agentes nunca gravam os arquivos EX5 obtidos do terminal no disco rígido (EA, indicadores, bibliotecas, etc.) por motivos de segurança, de modo que um computador com um agente sendo executado não possa utilizar os dados enviados. Todos os outros arquivos, incluindo DLL, são gravados no sandbox. Em agentes remotos, você não pode testar EAs usando DLL.
Os resultados de testes são somados pelo terminal em um cache especial de resultados (o cache de resultados), para rápido acesso quando forem necessários. Para cada conjunto de parâmetros, o terminal pesquisa o cache de resultados para encontrar resultados já disponíveis das execuções anteriores, para evitar reexecuções. Se o resultado com tal conjunto de parâmetros não for encontrado, o agente é solicitado a realizar o teste.
Todo o tráfego entre o terminal e o agente é criptografado.
Usando a pasta compartilhada de todos os terminais clientes
Todos os agentes de teste são isolados uns dos outros e do terminal cliente: cada agente possui sua própria pasta, na qual seus logs são registrados. Além disso, todas as operações de arquivo durante o teste do agente ocorrem na pasta agent_name/MQL5/Files. Entretanto, podemos implementar a interação entre os agentes locais e o terminal cliente por meio de uma pasta compartilhada para todos os terminais clientes, se durante a abertura do arquivo você especificar a flag FILE_COMMON:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- the shared folder for all of the client terminals common_folder=TerminalInfoString(TERMINAL_COMMONDATA_PATH); //--- draw out the name of this folder PrintFormat("Open the file in the shared folder of the client terminals %s", common_folder); //--- open a file in the shared folder (indicated by FILE_COMMON flag) handle=FileOpen(filename,FILE_WRITE|FILE_READ|FILE_COMMON); ... further actions //--- return(0); }
Usando DLLs
Para acelerar a otimização, podemos usar não apenas agentes locais, como também agentes remotos. Neste caso, há algumas limitações para agentes remotos. Antes de mais nada, agentes remotos não exibem em seus logs os resultados da execução da função Print(), mensagens sobre a abertura e fechamento de posições. Um mínimo de informação é exibido no log para prevenir que EAs incorretamente escritos esgotem o computador no qual o agente remoto está trabalhando com mensagens.
Uma segunda limitação - a proibição do uso de DLL quando estiver testando EAs. Chamadas a DLLs são absolutamente proibidas em agentes remotos por razões de segurança. Em agentes locais, chamadas a DLL em EAs testadas são permitidas apenas com a permissão apropriada "Permitir importar DLL".
Fig. 10. A opção "Allow import DLL" (Permitir importar DLL) em programas mql5
Observação: Quando estiver usando algo recebido dos EAs (scripts, indicadores) que requeiram a permissão a chamadas a DLL, você deve estar ciente dos riscos que você assume quando permite esta opção nas configurações do terminal. Independentemente de como o EA será usado - para teste ou para execução em um gráfico.
Conclusão
O artigo diz respeito ao básico, o conhecimento que ajudará você a dominar rapidamente o teste de EAs no terminal cliente do MetaTrader 5:
- Três modos de geração de ticks;
- Cálculo de valores do indicador durante o teste;
- Teste envolvendo várias moedas;
- Simulação de tempo durante o teste;
- O trabalho das funções OnTimer(), OnSleep() e IndicatorRelease() no Strategy Tester;
- O trabalho dos agentes de teste durante chamadas a DLL;
- Usando a pasta compartilhada para todos os terminais clientes;
- A sincronização das barras no modo "Open prices only" (Apenas preços abertos);
- Manipulação de eventos.
O principal trabalho do Strategy Tester do terminal cliente é garantir a precisão necessária dos dados com o mínimo esforço do programador MQL5. Desenvolvedores fizeram bastante para que você não tivesse que reescrever seu código apenas para testar sua estratégia de comércio sobre dados históricos. É suficiente saber os básicos do teste e um EA corretamente escrito trabalhará da mesma maneira no testador e no modo online do gráfico.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/239
![Uso dos recursos no MQL5](https://c.mql5.com/2/0/Resources_MQL5.png)
![Eventos de negociação no MetaTrader 5](https://c.mql5.com/2/0/trade_events.png)
![Como Assinar Sinais de Negociação](https://c.mql5.com/2/0/signals_avatar.png)
![Ordens, posições e negócios no MetaTrader 5](https://c.mql5.com/2/0/TradeIndo_MQL5.png)
![MQL5 - Linguagem para estratégias de negociação inseridas no terminal do cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- 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
Olá.
Já alguns dias estou tendo problemas em testar o indicador HiLo no testador de estratégia.
Esta me aparecendo a seguinte mensagem de erro:
2014.06.05 16:50:18.010 program file Indicators\hilo__1_teste.ex5 read error
2014.06.05 16:50:18.010 program file Indicators\hilo__1_teste.ex5 read error
2014.06.05 16:50:17.126 program file Indicators\hilo__1_teste.ex5 read error
2014.06.05 16:50:17.126 loading of hilo__1_teste EURUSD,H1 failed
2014.06.05 16:50:17.126 program file Indicators\hilo__1_teste.ex5 read error
2014.06.05 16:50:17.126 loading of hilo__1_teste EURUSD,H1 failed
2014.06.05 16:50:17.126 program file Indicators\hilo__1_teste.ex5 read error
2014.06.05 16:50:17.126 2011.06.06 00:00:32 cannot load custom indicator 'hilo__1_teste' [4802]
2014.06.05 16:50:17.126 loading of hilo__1_teste EURUSD,Daily failed
2014.06.05 16:50:17.126 program file Indicators\hilo__1_teste.ex5 read error
2014.06.05 16:50:17.126 program file Indicators\hilo__1_teste.ex5 read error
...
2014.06.05 16:50:27.007 2011.06.06 00:11:59 Error: Indicator not handled. (handle=-1 | error code=4802)
2014.06.05 16:50:27.007 2011.06.06 00:11:59 cannot load custom indicator 'hilo__1_teste' [4802]
2014.06.05 16:50:27.007 loading of hilo__1_teste EURUSD,H1 failed
2014.06.05 16:50:27.007 program file Indicators\hilo__1_teste.ex5 read error
O que esta acontecendo? Alguem já teve este problema ou poderia me ajudar?
Olá.
Já alguns dias estou tendo problemas em testar o indicador HiLo no testador de estratégia.
Esta me aparecendo a seguinte mensagem de erro:
2014.06.05 16:50:18.010 program file Indicators\hilo__1_teste.ex5 read error
2014.06.05 16:50:18.010 program file Indicators\hilo__1_teste.ex5 read error
2014.06.05 16:50:17.126 program file Indicators\hilo__1_teste.ex5 read error
2014.06.05 16:50:17.126 loading of hilo__1_teste EURUSD,H1 failed
2014.06.05 16:50:17.126 program file Indicators\hilo__1_teste.ex5 read error
2014.06.05 16:50:17.126 loading of hilo__1_teste EURUSD,H1 failed
2014.06.05 16:50:17.126 program file Indicators\hilo__1_teste.ex5 read error
2014.06.05 16:50:17.126 2011.06.06 00:00:32 cannot load custom indicator 'hilo__1_teste' [4802]
2014.06.05 16:50:17.126 loading of hilo__1_teste EURUSD,Daily failed
2014.06.05 16:50:17.126 program file Indicators\hilo__1_teste.ex5 read error
2014.06.05 16:50:17.126 program file Indicators\hilo__1_teste.ex5 read error
...
2014.06.05 16:50:27.007 2011.06.06 00:11:59 Error: Indicator not handled. (handle=-1 | error code=4802)
2014.06.05 16:50:27.007 2011.06.06 00:11:59 cannot load custom indicator 'hilo__1_teste' [4802]
2014.06.05 16:50:27.007 loading of hilo__1_teste EURUSD,H1 failed
2014.06.05 16:50:27.007 program file Indicators\hilo__1_teste.ex5 read error
O que esta acontecendo? Alguem já teve este problema ou poderia me ajudar?
Bom