
Como implementar seus próprios critérios de otimização
Introdução
De vez em quando ouvimos opiniões sobre a necessidade de aumentar o conjunto de critérios de optimização no verificador MT4. Ainda assim, você pode imaginar que não importa quais critérios os desenvolvedores acrescentem, sempre haverá usuários e situações que exigem outros critérios. Este problema pode ser resolvido dentro da MQL4 e do MetaTrader? Sim, pode. Este artigo mostra a implementação do uso de um critério de otimização personalizado no exemplo do Expert Advisor padrão Média Móvel. O critério aqui é a relação lucro/levantamento de crédito.
Expert Advisor
Vamos começar com o critério de otimização. Para seu cálculo, precisamos traçar durante o teste a máxima do balanço e o levantamento de crédito. A fim de não depender da lógica da operação do Expert Advisor, vamos adicionar as strings do código existente no início da função start().
if (AccountEquity() > MaxEqu) MaxEqu = AccountEquity(); if (MaxEqu-AccountEquity() > MaxDD) MaxDD = MaxEqu-AccountEquity();
Para processar o último crédito, elas devem ser duplicadas no deinit(). Depois disso, podemos calcular o valor do critério de otimização.
Criterion = (AccountBalance()-StartBalance)/MaxDD;
Agora podemos começar a parte principal - a manutenção do processo de otimização. Temos um problema - o MQL4 não têm os meios internos de determinar o final da otimização. A única saída que conheço é a chamada "otimização em um contador". O significado é o seguinte: o único parâmetro variável do Expert Advisor é o contador variável especial externo. Ainda aqui é uma consequência grave - perdemos a possibilidade de alterar os parâmetros reais do Expert Advisor de forma padrão e devemos fornecer isto nós mesmos. Outra desvantagem é que o cache de otimização do nosso amigo se transforma no nosso inimigo. Mas o fim justifica os meios, então vamos continuar.
Vamos adicionar variáveis externas:
extern int Counter = 1; // Counter of the tester's running extern int TestsNumber = 200; // he check digit - total number of runs extern int MovingPeriodStepsNumber = 20; // Number of optimization steps for MovingPeriod extern int MovingShiftStepsNumber = 10; // Number of optimization steps for MovingShift extern double MovingPeriodLow = 150; // Lower limit of the optimization range for MovingPeriod extern double MovingShiftLow = 1; // Lower limit of the optimization range for MovingShift extern double MovingPeriodStep = 1; // Optimization step for MovingPeriod extern double MovingShiftStep = 1; // Optimization step for MovingShift
Primeiro vem o contador. A próxima variável é a de verificação (e informação). Em seguida, atribuímos o número de passos, limite inferior e etapa de otimização para as duas variáveis internas de média móvel destinadas a otimização. Você pode ver alguns excessos: se vamos fazer o exame completo (e vamos fazê-lo) o produto do MovingPeriodStepsNumber e MovingShiftStepsNumber deve ser igual ao TestsNumber.
Depois que cada teste executa o Expert Advisor, isto termina plenamente o seu trabalho e a próxima execução pode ser considerada sua reencarnação. Nós temos duas formas de organizar "o armazenamento genético": variáveis globais e um arquivo separado. Usaremos os dois.
Vamos modificar a função init():
int init() { if (IsTesting() && TestsNumber > 0) { if (GlobalVariableCheck("FilePtr")==false || Counter == 1) { FilePtr = 0; GlobalVariableSet("FilePtr",0); } else { FilePtr = GlobalVariableGet("FilePtr"); } MovingPeriod = MovingPeriodLow+((Counter-1)/MovingShiftStepsNumber)*MovingPeriodStep; MovingShift = MovingShiftLow+((Counter-1)%MovingShiftStepsNumber)*MovingShiftStep; StartBalance = AccountBalance(); MaxEqu = 0; MaxDD = 0; } return(0); }
Nossa adição está localizada dentro das condições de operação unicamente no verificador e no TestsNumber diferente de zero. Assim, a tarefa TestsNumber=0 irá transformar o Expert Advisor em um padrão de Média Móvel. Enquanto estamos discutindo o processo de otimização, devemos usar qualquer possibilidade de aceleração do processo. É por isso que o código começa com o fornecimento de gestão por meio (através da execução de verificadores) do indicador de arquivo usando uma variável global. Em seguida, calculamos valores de parâmetros mutáveis e inicializamos variáveis utilizadas para o cálculo do critério de otimização.
O trabalho principal deve ser feito na função deinit(). Mediante os resultados dos testes, iremos salvar em um arquivo de texto os valores do critério de otimização, os valores dos parâmetros otimizados e o número de execuções do teste. Após o fim da otimização, os resultados serão classificados de acordo com o critério de otimização e salvos no mesmo arquivo. Então, precisamos processar três situações: a primeira partida, a segunda partida e todos as outras. Vamos usar o contador de execução do verificador para esta separação.
Processamento da primeira partida:
if (Counter == 1) { // First run, create/initialize a datafile. h=FileOpen("test.txt",FILE_CSV|FILE_WRITE,';'); FileWrite(h,Criterion,MovingPeriod,MovingShift,Counter); // Remember the position of the file pointer after writing in the global variable FilePtr = FileTell(h); GlobalVariableSet("FilePtr",FilePtr); FileClose(h);
A peculiaridade de processar outros inícios é que os novos dados são adicionados ao arquivo:
} else { // After the first start is processed, the data are added into the file h=FileOpen("test.txt",FILE_CSV|FILE_READ|FILE_WRITE,';'); // It is time to use the file pointer written in the global variable FilePtr = GlobalVariableGet("FilePtr"); FileSeek(h,FilePtr, SEEK_SET); FileWrite(h,Criterion,MovingPeriod,MovingShift,Counter); // Remember the file pointer position once again FilePtr = FileTell(h); GlobalVariableSet("FilePtr",FilePtr);
Agora vamos processar a última partida:
if (Counter == TestsNumber) { ArrayResize(Data,TestsNumber); // Returns the file pointer to the beginning FileSeek(h,0,SEEK_SET); // Read from the file the results of all testings int i = 0; while (i<TestsNumber && FileIsEnding(h)== false) { for (int j=0;j<4;j++) { Data[i][j]=FileReadNumber(h); } i++; } // Sort the array according to our optimization criterion ArraySort(Data,WHOLE_ARRAY,0,MODE_DESCEND); // Now let us arrange the results. Reopen the file FileClose(h); h=FileOpen("test.txt",FILE_CSV|FILE_WRITE,' '); FileWrite(h," Criterion"," MovingPeriod"," MovingShift"," Counter"); for (i=0;i<TestsNumber;i++) { FileWrite(h,DoubleToStr(Data[i][0],10)," ",Data[i][1]," ",Data[i][2]," ",Data[i][3]); }
A matriz foi preliminarmente inicializada como dados duplos[][4]. Isto é tudo! Vamos limpar:
GlobalVariableDel("FilePtr"); } FileClose(h); } }
Compile, abra o verificador e selecione o Expert Advisor. Em seguida, abra a folha de propriedades do Expert Advisor e verifique quatro posições:
- O produto de MovingPeriodStepsNumber da MovingShiftStepsNumber DEVE ser igual ao TestsNumber.
- A otimização deve ser realizada APENAS para o contador,
- A faixa de otimização DEVE ser de 1 para números de teste com o passo 1.
- O algoritmo genético deve ser desativado.
Inicie a otimização. Após o final, mude para a pasta [Metatrader]\tester\ arquivos e exiba o resultado no arquivo test.txt. O autor fez isto paraEURUSD_H1 a partir de meados de 2004 sobre os preços de abertura e obteve os seguintes resultados:
E agora vamos voltar para a afirmação de que o cache é nosso inimigo. O fato é que, quando levamos os resultados dos testes a partir do cache, as funções init() e deinit() não estão iniciadas. Como resultado, no reinício da otimização todas as variantes ou parte das variantes estão desaparecidas. Além disso, embora o número real de execuções sejam menores do que o número de testes, os dados da matriz irão conter alguns zeros. O autor sugere duas formas de eliminar o "efeito cache": recompilação de um Expert Advisor ou o fechamento/ pausa/abertura da janela do verificador.
A interferência do cache pode ser detectada por uma contagem independente dos testes. Há três inserções comentadas para a organização de tal contagem usando uma variável global especial, sugeridas no código do EA em anexo:
// Code of the independent counter if (GlobalVariableCheck("TestsCnt")==false || Counter == 1) { TestsCnt = 0; GlobalVariableSet("TestsCnt",0); } else { TestsCnt = GlobalVariableGet("TestsCnt"); }
// Code of the independent counter TestsCnt++; GlobalVariableSet("TestsCnt",TestsCnt);
// Code of the independent counter GlobalVariableDel("TestsCnt");
Uma última coisa. Um leitor atento deve ter notado o fato de que nós podemos fazer sem a variável FilePtr (e a variável global de acompanhamento) - os dados são escritos no final do arquivo e lidos desde o início. Então, para que serve isto? A resposta é a seguinte: este Expert Advisor é destinado para a demonstração do método de manutenção de otimização. O método permite gerenciar a operação em tempo real com os resultados de testes anteriores. E aqui o canal do ponteiro do arquivo pode ser bastante útil, bem como o contador independente. Como um exemplo de tarefas que exigem ser trabalhadas com os resultados anteriores em tempo real, podemos citar a gestão dos testes de fora da amostra e a implementação do próprio algoritmo genético.
Conclusão
As causas para tal interesse para este problema foram temas do fórum http://forum.mql4.com.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/1498





- 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