Otimização Walk Forward Contínua (Parte 4): Gerenciamento de Otimização (Otimizador Automático)

Andrey Azatskiy | 16 junho, 2020

Introdução

Este é o próximo artigo da série "Otimização Walk Forward Contínua". Aqui, eu apresento o programa criado, que implementa a otimização automática programada. Os artigos anteriores descreveram os detalhes da implementação do programa no lado do terminal e no lado da biblioteca, que é usado para trabalhar com os relatórios de otimização, que foram gerados. Você pode conferir estes artigos nos links abaixo:

  1. Otimização Walk Forward Contínua (parte 1): Trabalhando com os Relatórios de Otimização
  2. Otimização Walk Forward Contínua (Parte 2): Mecanismo para a criação de um relatório de otimização para qualquer robô
  3. Otimização Walk Forward Contínua (Parte 3): Método de Adaptação de um Robô ao Otimizador Automático

O artigo atual demonstra a imagem geral do produto criado e serve como uma instrução. A funcionalidade do otimizador automático, que foi criado, pode ser estendida. Assim, o algoritmo que discutiremos mais adiante pode ser facilmente substituído pelo seu próprio algoritmo de otimizações, permitindo que você implemente qualquer ideia desejada. Além disso, uma descrição adicional divulga a estrutura interna do programa criado. O principal objetivo do artigo é descrever o funcionamento do aplicativo resultante e seus recursos. O fato é que meus colegas às vezes achavam difícil entendê-lo rapidamente. No entanto, o otimizador automático é muito fácil de configurar e usar — eu mostrarei isso mais adiante. Assim, o artigo pode ser tratado como uma instrução de uso do aplicativo, que cobre todas as possíveis dificuldades e os detalhes de configuração.


Descrição do funcionamento do Otimizador Automático

Para prosseguir com a análise do programa criado, nós precisamos primeiro definir o objetivo deste projeto. Nós decidimos usar uma abordagem científica na negociação e começamos a criar algoritmos de negociação claramente programados (independentemente de lidarmos com robôs baseados em indicadores ou aqueles que aplicam lógica fuzzy e redes neurais — todos eles são algoritmos programados que executam tarefas específicas). Portanto, a abordagem para a seleção dos resultados da otimização também deve ser formalizada. Em outras palavras, se durante o processo de negociação nos abstrairmos sobre as causalidades, o processo de preparação para a negociação também deverá ser automatizado. Caso contrário, nós podemos selecionar os resultados que gostamos aleatoriamente, o que é mais próximo da intuição do que da negociação sistemática. Essa ideia é o primeiro motivo que me incentivou a criar esse aplicativo. O próximo é a possibilidade de testar algoritmos otimizando-os — através da otimização de Walk Forward contínua mostrada na figura abaixo.   


A otimização de Walk Forward contínua alterna entre os passes da otimização do histórico (amarelo) e do forward (verde) em um determinado intervalo de tempo. Suponha que você tenha um histórico de 10 anos. Nós determinamos que o período de otimização deve consistir em um intervalo igual a 1 ano e um intervalo de forward de 1 trimestre (ou 3 meses). Como resultado, nós temos um intervalo igual a 1.25 anos (1 ano + 1 trimestre) para um passe de otimização + um teste de forward. Na figura, cada linha caracteriza esse intervalo de tempo. 

Em seguida, nós produzimos o mesmo tipo de processo:

  1. Otimizamos no período do histórico selecionado (1 ano).
  2. Selecionamos os melhores parâmetros entre os resultados obtidos no intervalo otimizado, usando um método de escolha fixo.
  3. Nos deslocamos para o intervalo de forward e executamos um teste, voltamos para o primeiro passo — agora com um intervalo deslocado. Repetimos isso até que o período de tempo atual seja atingido.
  4. Após coletar os resultados dos testes de cada trimestre, nós obtemos a avaliação final da viabilidade do algoritmo.
  5. O último passe de otimização (para o qual não há teste de forward) pode ser lançado para negociação.

Assim, nós obtemos um método para testar a sustentabilidade dos algoritmos via otimização. No entanto, neste caso, nós devemos otimizar os algoritmos após cada expiração do período de forward. Em outras palavras, nós definimos um determinado intervalo para a re-otimização do algoritmo, fixamos a metodologia para a seleção de parâmetros e executamos primeiro esse procedimento no histórico e, depois, repetimos ele após o término do teste de forward alocado para aquele período. 

Primeiramente, essa técnica de otimização nos permite ter uma lógica de otimização claramente definida, que nos permite receber um resultado livre da intervenção humana.

Em segundo lugar, executando a otimização em novos ativos ou novos algoritmos usando uma técnica semelhante, nós obtemos uma imagem completa dos testes de estresse do algoritmo. Quando o método de seleção de parâmetros e o parâmetro otimizado são fixos e inalterados em todas as otimizações futuras, nós recebemos um teste de estresse contínuo, que nos permitirá perceber se o mercado não se adequa mais à nossa estratégia.

Em terceiro lugar, nós recebemos muitos quadros de otimização em um período bastante pequeno, o que aumenta a confiabilidade dos testes realizados. Por exemplo, com a divisão mencionada acima em uma otimização de 1 ano e um trimestre de forward, um intervalo de 2 anos fornece 4 testes de estresse e 1 otimização final.


Configuração de inicialização da otimização

Agora que nós discutimos o processo de execução do aplicativo, vamos descrever o seu método de uso. Esta série de artigos é uma continuação lógica dos meus artigos anteriores sobre a interface gráfica para o gerenciamento do processo de otimização:

  1. Gerenciando otimizações (Parte I)
  2. Gerenciando otimizações (Parte II)

Os artigos apresentam uma descrição de como executar a otimização ou um teste na plataforma como um processo controlado. O mesmo método é usado nesta série. No entanto, uma das diferenças básicas é que o processo de controle é implementado não como um complemento à plataforma, mas como um programa independente. Essa abordagem permite o uso de todas as plataformas instaladas no computador. Na série anterior de artigos, nós criamos uma extensão que pode ser iniciada a partir de uma plataforma em funcionamento. A extensão pode usar todas as plataformas instaladas no computador, além daquela em que ela foi lançada. O aplicativo atual também tem acesso a todas as plataformas instaladas no computador, mas funciona apenas com uma plataforma por vez, podendo iniciar qualquer plataforma desejada.

Para garantir a operação bem-sucedida do otimizador automático, verificamos se a plataforma selecionada está fechada antes de iniciar o aplicativo. 

O aplicativo funciona da seguinte maneira:

  1. Durante o próximo lançamento do processo, não importa se é uma otimização ou um teste, o aplicativo inicia uma plataforma e delega todo o processo de teste à plataforma. Após a conclusão do teste ou otimização, a plataforma é encerrada.
  2. Após o teste ou a otimização, o robô gera um relatório com os resultados da otimização (para obter mais detalhes, leia os artigos anteriores desta série).
  3. O otimizador automático sabe onde está o relatório e, portanto, ele lê e processa o relatório. Após o processamento, é iniciada uma nova etapa de otimização e teste ou a otimização é concluída e os resultados são exibidos na guia "Result". 

O mecanismo de otimização implementado será considerado posteriormente, enquanto o objetivo da parte atual é descrever o processo de operação do programa com o mínimo de detalhes técnicos. Não esqueça que você pode adicionar seu próprio algoritmo na parte do código responsável pela otimização. O otimizador automático é um programa de controle que inicia os processos de otimização, mas a lógica desses processos pode ser diferente. Aqui está uma captura de tela da guia principal do aplicativo resultante.


Se você olhar mais atentamente a captura de tela, a primeira caixa de combinação chamada "Select Optimizer" e localizada na área verde, fornece a seleção dos tipos de otimização implementados no programa. 

A interface gráfica do aplicativo é dividida em 2 guias principais. A guia Settings é a principal. É aqui que nós começamos a trabalhar quando precisamos iniciar ou interromper a otimização. A guia foi descrita em detalhes na segunda parte. A outra guia, "Result", é onde os resultados da otimização são exibidos.

Primeiro, ao iniciar o otimizador automático, nós precisamos selecionar a plataforma a ser usada. O princípio da seleção da plataforma é o mesmo da interface gráfica anterior usada para iniciar a otimização. Em outras palavras, o otimizador automático encontrará todas as plataforma instaladas neste computador (no entanto, apenas aqueles que usam uma instalação padrão, e não no modo portátil). 

A guia "Settings" é dividida em 4 seções. As bordas de cada parte da tela podem ser arrastadas. Isso é especialmente conveniente quando se trabalha com um algoritmo com muitos parâmetros de entrada.

Além disso, devido ao formato de texto dos arquivos configurados, não seremos capazes de distinguir entre os formatos dos parâmetros do algoritmo. Por exemplo, todos os parâmetros de enumeração são mostrados como int. Por isso, foi decidido exibir a lista de parâmetros como strings na segunda parte da tela. Se não for conveniente configurar as etapas de otimização e outros parâmetros do robô diretamente no otimizador automático, você poderá executar todas as configurações necessárias na plataforma. Depois de alterar as guias no testador (ou depois que a plataforma for encerrada), as configurações serão gravadas no arquivo desejado. Tudo o que você precisa fazer é selecionar o algoritmo necessário no otimizador automático — ele será carregado imediatamente com suas configurações.

É necessário a configuração dos seguintes campos para o início de uma otimização ou teste a partir do otimizador automático:

  1. Selecionar o EA.
  2. Verificar os parâmetros a serem otimizados e selecionar a faixa de variação (como na plataforma).
  3. Selecionar pelo menos um critério de classificação: isso afetará a classificação dos dados produzidos pelo robô e os melhores resultados serão lançados no intervalo de forward (pode ser omitido para uma execução de teste).
  4. Selecionar as datas de otimização de walk forward (ou as datas de teste, se você executar um teste).   
  5. Selecionar o otimizador necessário se você executar a otimização (lista suspensa "Select Optimiser:"). 
  6. Especificar o "Asset name" — o nome do símbolo para o qual a operação solicitada será executada. Em caso de erro, a plataforma não poderá executar o teste ou a otimização.
  7. Você pode usar "Directory prefix" para a especificação adicional do nome do passe de otimização que está salvando. Uma pasta com os resultados da otimização é criada em um diretório interno especial do programa após o final da otimização. O nome da pasta é definido da seguinte forma: "{Prefixo do diretório} {Otimizador selecionado} {Nome do Expert} {Nome do ativo}". Esses são os nomes mostrados na lista "Optimisation:", onde eles podem ser carregados para uma futura visualização e análise.
  8. A lista suspensa com os parâmetros "Rewrite" ou "Append" também é opcional. Ele define a ação a ser executada pelo otimizador automático se encontrar os resultados com o mesmo nome entre os arquivos salvos. Se "Rewrite" for selecionado, todos os arquivos serão reescritos com os novos. Se "Append" for selecionado, as datas de otimização correspondentes serão substituídas. Se o mesmo intervalo for encontrado na lista atual de intervalos da otimização e na lista daqueles salvos anteriormente, os resultados salvos serão substituídos pelos novos. Se os intervalos forem novos, eles serão adicionados aos existentes.

Após a instalação, clicamos em "Start/Stop" para iniciar o processo. Um clique repetido neste botão interromperá o processo de otimização. Durante o processo de otimização, seu status é exibido no ProgressBar e no rótulo de texto, localizado na parte inferior da janela do otimizador automático. Após o término da otimização, os resultados da otimização serão enviados para a guia "Result" e o ProgressBar será redefinido para o estado inicial. No entanto, se você executar um teste clicando em "Start/Stop", a plataforma não será encerrada automaticamente. Isso é feito por conveniência e permite ao usuário examinar todos os dados necessários. Depois de estudar os dados necessários, encerramos a plataforma manualmente para continuar a operação do otimizador automático. Por favor, não esqueça que a plataforma sempre deve estar fechada, porque o aplicativo deve poder gerenciar a plataforma independentemente.

Além disso, você deve configurar o otimizador antes de iniciar as otimizações. Esse não é um requisito obrigatório, mas a estrutura do gerenciador de otimização permite criar um otimizador personalizado, além de definir os parâmetros individuais para cada um deles. As configurações podem ser abertas clicando no botão "GUI" localizado próximo à caixa de combinação de seleção do otimizador. As configurações do otimizador implementado são as seguintes:

  1. Test on ticks — indica o método de teste de dados em testes do histórico e de forward. O método de otimização é especificado na primeira parte da janela "Settings", enquanto o método de teste é indicado nas configurações do otimizador. Se a opção estiver ativada, o teste será realizado usando os ticks. Se desativado, o teste será realizado no modo OHLC de 1 minuto. 
  2. Replace real dates to set — define se as datas de início e término da otimização real devem ser substituídas pelas datas passadas. A hora de início e término da otimização é salva usando um período de 1 minuto. Às vezes, se não houver negociação ou for feriado, as datas reais de início e término podem diferir das especificadas. Se essa opção estiver ativada, o otimizador definirá os dados mais familiares e saberá a que intervalo os resultados pertencem. No entanto, se você quiser ver as datas reais de negociação, desmarque esta opção.
  3. Use different shift for tick test — nós discutimos no segundo artigo que o slippage e o deslocamento podem ser adicionados aos resultados. Se os ticks forem usados para teste, o slippage poderá ser desativado ou totalmente reduzido. Esta opção foi adicionada exatamente para este caso. Só é ativado quando o "Test on ticks" está ativado. Para usar a opção, especificamos os parâmetros do algoritmo responsáveis pela indicação da comissão e do slippage e definimos os novos valores para eles. Ao especificar o parâmetro do robô responsável pelo desvio e configurá-lo como 0, você pode remover o desvio dos resultados no modo de teste de tick. Após especificar um parâmetro e seu valor, adicionamos este parâmetro à tabela através do parâmetro "Add" para salvar este parâmetro.

Não há necessidade de salvar os parâmetros inseridos (não há botão Save), pois eles são salvos automaticamente. Fechamos esta janela antes de iniciar a otimização para evitar alterações acidentais dos parâmetros durante o processo de otimização. 


Trabalhando com os resultados de otimização

Não interfira no processo após iniciar a otimização. Além disso, não remova o EA do gráfico até que o otimizador o interrompa. Caso contrário, o otimizador considerará esta situação um erro, porque as datas não corresponderão. Depois que o processo estiver concluído e o terminal fechado, o otimizador fará o upload do relatório de otimização para a guia Results, onde você pode avaliar o trabalho realizado. A estrutura da guia é mostrada na guia abaixo:

 

A guia de resultados também é dividida em partes, marcadas com números para facilitar a explicação. A primeira parte apresenta passes de otimização divididos entre guias (Selected pass e Optimisations). A primeira guia do contêiner chamada "Selected pass" contém os passes da otimização selecionados. Esses passes são divididos em duas guias ("Forward" e "History"). Vamos ver como os parâmetros de otimização são distribuídos entre essas guias. Por exemplo, as seguintes datas são especificadas:

  1. 01.01.2012 - 07.12.2012 - History
  2. 10.12.2012 - 07.03.2012 - Forward

A otimização será realizada no intervalo do histórico. Em seguida, os melhores parâmetros serão selecionados usando a filtragem e classificação (consulte a descrição no capítulo anterior). Dois testes são realizados após a seleção: um no intervalo do histórico e outro no de forward. Além disso, os resultados dos testes dos melhores parâmetros são adicionados à guia "Selected pass", onde são divididos entre as guias "History" e "Forward". Os passes de otimização são adicionados à guia "Optimisations", na qual é possível visualizar a lista completa de otimizações para qualquer intervalo do histórico. Considere a guia otimizações em mais detalhes.


A estrutura da tabela (a primeira parte da guia "Optimisations") é semelhante à estrutura das guias "Forward" e "History". Mas esta tabela mostra todos os passes de otimização dentro do intervalo solicitado. O intervalo de tempo desejado pode ser selecionado na caixa de combinação "Optimisation dates". Quando um novo intervalo é selecionado, a tabela inteira com os passes de otimização é atualizada. A segunda parte da guia é semelhante à terceira parte da janela "Settings" e todas as alterações nessas guias são sincronizadas.

Tanto as guias "Optimisations" quanto as "Selected pass" contêm o botão "Save to (*.csv)". Quando este botão é clicado em "Selected pass", um arquivo *.csv é criado contendo a lista de passes de otimização do histórico e do forward. Quando o botão é clicado em "Optimisations", as informações sobre todas as otimizações executadas são baixadas no arquivo *csv apropriado. Os botões "Sort" e "Filter" estão disponíveis apenas na guia "Optimisations". Seu objetivo é fornecer a filtragem e a classificação dos passes de otimização resultantes, de acordo com as configurações especificadas na segunda parte da guia "Optimisations". Na prática, essa opção não é necessária porque o otimizador automático usa o mesmo mecanismo. No entanto, a opção permite o uso de qualquer filtragem personalizada desejada. 

Ambas as tabelas são interativas. Um único clique em uma linha da tabela atualiza a segunda e a terceira parte da guia "Result", de acordo com o passe de otimização selecionado. Um clique duplo inicia um teste na plataforma. Nesse caso, a plataforma não será fechada após o término do teste. Portanto, você deve fechá-la manualmente depois de estudar os resultados. Alguns parâmetros do testador podem ser configurados antes de iniciar um teste: a segunda parte da guia "Results".

  

As datas de início e término do teste são atualizadas automaticamente de acordo com o intervalo de tempo selecionado. No entanto, eles podem ser alterados com um clique duplo na linha desejada. Você também pode selecionar o atraso da execução e o tipo de teste (ticks, OHLC etc.). "No delay" e "Every tick" são definidos por padrão. 

A terceira parte da guia mostra as estatísticas de negociação do passe de otimização selecionado (Daily PL e Max PL/DD) e os parâmetros do robô para o passe selecionado (guia Bot params). A guia "Max PL/DD" Não mostra o valor final dos lucros/perdas, mas mostra apenas o lucro total (a soma de todos os negócios rentáveis) e a perda total (a soma de todos os negócios com prejuízo). O lucro e o rebaixamento máximo registrados no momento da conclusão da negociação são exibidos na tabela com as otimizações e o resultado do teste. A guia Daily PL mostra o lucro diário médio e a perda diária média, da mesma forma que o relatório disponível na plataforma.

Outro algoritmo mais simples para trabalhar com o otimizador automático

Vamos considerar um exemplo pronto de um robô que trabalha com o otimizador automático. Nós já consideramos o modelo de algoritmo no terceiro artigo desta série. Agora, vamos adaptar o modelo para um algoritmo no estilo C. Primeiro, nós consideraremos o próprio algoritmo. Ele é um algoritmo de 2 médias móveis. As posições são fechadas por um Stop Loss fixo ou por um Take Profit fixo. A implementação das funções que descrevem a lógica do EA é removida do código abaixo, pois esse não é o objetivo do exemplo.

//+------------------------------------------------------------------+
//|                                                     SimpleMA.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
#define TESTER_ONLY

input int ma_fast = 10; // MA fast
input int ma_slow = 50; // MA slow
input int _sl_ = 20; // SL
input int _tp_ = 60; // TP
input double _lot_ = 1; // Lot size

int ma_fast_handle,ma_slow_handle;
const double tick_size = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
CTrade trade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

#ifdef TESTER_ONLY
   if(MQLInfoInteger(MQL_TESTER)==0 &&
      MQLInfoInteger(MQL_OPTIMIZATION)==0)
     {
      Print("This expert was created for demonstration! It is not enabled for real trading !");
      ExpertRemove();
      return(INIT_FAILED);
     }
#endif

   ma_fast_handle = iMA(_Symbol,PERIOD_CURRENT,ma_fast,0,MODE_EMA,PRICE_CLOSE);
   ma_slow_handle = iMA(_Symbol,PERIOD_CURRENT,ma_slow,0,MODE_EMA,PRICE_CLOSE);

   if(ma_fast_handle == INVALID_HANDLE ||
      ma_slow_handle == INVALID_HANDLE)
     {
      ExpertRemove();
      return(INIT_FAILED);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(ma_fast_handle != INVALID_HANDLE)
      IndicatorRelease(ma_fast_handle);
   if(ma_slow_handle != INVALID_HANDLE)
      IndicatorRelease(ma_slow_handle);
  }

enum Direction
  {
   Direction_Long,
   Direction_Short,
   Direction_None
  };

//+------------------------------------------------------------------+
//| Calculate stop                                                   |
//+------------------------------------------------------------------+
double get_sl(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Calculate take                                                   |
//+------------------------------------------------------------------+
double get_tp(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Open position according to direction                             |
//+------------------------------------------------------------------+
void open_position(const double price,const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Get direction                                                    |
//+------------------------------------------------------------------+
Direction get_direction()
  {
   ...
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(!PositionSelect(_Symbol))
     {
      Direction direction = get_direction();
      if(direction != Direction_None)
         open_position(iClose(_Symbol,PERIOD_CURRENT,0),direction);
     }
  }
//+------------------------------------------------------------------+

Esta parte do código do EA é básica. É necessário analisar as alterações que nós precisamos produzir para disponibilizar o EA no otimizador automático. 

Observe que a tarefa não exigiu um EA eficiente; portanto, provavelmente este robô estará tendo prejuízo. O EA tem uma restrição para evitar a sua execução acidental em negociações reais. Para remover a limitação (e poder iniciá-lo na negociação real), comente a definição TESTER_ONLY

O que nós fazemos na OnOnit é instanciar os indicadores de média móvel. Consequentemente, os indicadores devem ser excluídos no OnDeinit. A enumeração Direction declarada no código é usada para determinar a direção. As posições são abertas pela classe CTrade na função open_position. Toda a lógica é descrita em quatro linhas de código, no retorno de chamada da OnTick. Agora, vamos adicionar a inclusão da funcionalidade necessária ao robô.

//+------------------------------------------------------------------+
//|                                                     SimpleMA.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
#include <History manager/AutoLoader.mqh> // Include CAutoUploader
#define TESTER_ONLY

input int ma_fast = 10; // MA fast
input int ma_slow = 50; // MA slow
input int _sl_ = 20; // SL
input int _tp_ = 60; // TP
input double _lot_ = 1; // Lot size

// Comission and price shift (Article 2) 
input double _comission_ = 0; // Comission
input int _shift_ = 0; // Shift

int ma_fast_handle,ma_slow_handle;
const double tick_size = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
CTrade trade;
CAutoUploader * auto_optimiser; // Pointer to CAutoUploader class (Article 3)
CCCM _comission_manager_; // Comission manager (Article 2)

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

#ifdef TESTER_ONLY
   if(MQLInfoInteger(MQL_TESTER)==0 &&
      MQLInfoInteger(MQL_OPTIMIZATION)==0)
     {
      Print("This expert was created for demonstration! It is not enabled for real trading !");
      ExpertRemove();
      return(INIT_FAILED);
     }
#endif

   ma_fast_handle = iMA(_Symbol,PERIOD_CURRENT,ma_fast,0,MODE_EMA,PRICE_CLOSE);
   ma_slow_handle = iMA(_Symbol,PERIOD_CURRENT,ma_slow,0,MODE_EMA,PRICE_CLOSE);

   if(ma_fast_handle == INVALID_HANDLE ||
      ma_slow_handle == INVALID_HANDLE)
     {
      ExpertRemove();
      return(INIT_FAILED);
     }

   // Set Commission and shift
   _comission_manager_.add(_Symbol,_comission_,_shift_);

   // Add robot params
   BotParams params[];
   APPEND_BOT_PARAM(ma_fast,params);
   APPEND_BOT_PARAM(ma_slow,params);
   APPEND_BOT_PARAM(_sl_,params);
   APPEND_BOT_PARAM(_tp_,params);
   APPEND_BOT_PARAM(_lot_,params);
   APPEND_BOT_PARAM(_comission_,params);
   APPEND_BOT_PARAM(_shift_,params);

   // Add Instance CAutoUploader class (Article3)
   auto_optimiser = new CAutoUploader(&_comission_manager_,"SimpleMAMutex",params);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(ma_fast_handle != INVALID_HANDLE)
      IndicatorRelease(ma_fast_handle);
   if(ma_slow_handle != INVALID_HANDLE)
      IndicatorRelease(ma_slow_handle);

   // Delete CAutoUploaderclass (Article 3)
   delete auto_optimiser; 
  }

enum Direction
  {
   Direction_Long,
   Direction_Short,
   Direction_None
  };

//+------------------------------------------------------------------+
//| Calculate stop                                                   |
//+------------------------------------------------------------------+
double get_sl(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Calculate take                                                   |
//+------------------------------------------------------------------+
double get_tp(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Open position according to direction                             |
//+------------------------------------------------------------------+
void open_position(const double price,const Direction direction)
  {
   ...   
  }

//+------------------------------------------------------------------+
//| Get direction                                                    |
//+------------------------------------------------------------------+
Direction get_direction()
  {
   ...
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   auto_optimiser.OnTick(); // Save current date (Article 3)

   if(!PositionSelect(_Symbol))
     {
      Direction direction = get_direction();
      if(direction != Direction_None)
         open_position(iClose(_Symbol,PERIOD_CURRENT,0),direction);
     }
  }
//+------------------------------------------------------------------+

Todas as novas adições são marcadas em verde. Vamos considerá-los em ordem de aparência. Primeiramente, nós incluímos o arquivo de cabeçalho do AutoLoader descrito no artigo 3. Este arquivo contém a classe CAutoUploader, cuja tarefa é fazer o download do histórico de negociação acumulado. No retorno da chamada OnInit, nós adicionamos a comissão à classe CCCM apropriada, descrita no artigo 2. Além disso, nós instanciamos a classe CAutoUploader após adicionar os parâmetros do EA a ela. A instância da classe CAutoUploader é excluída no retorno da chamada OnDeinit, que inicializa a chamada de um destrutor que o relatório de negociação é salvo em um arquivo xml (artigo 1). 

A lógica do EA permanece inalterada, com exceção da chamada da OnTick, no qual o método OnTick da classe CAutoUploader é chamado. A solicitação permite salvar corretamente as datas de início e término do teste. A classe CAutoUploader funciona apenas no testador e não executa nenhuma ação na negociação real. 


Conclusão

O artigo apresentado descreve a funcionalidade do Gerenciador de Otimizações, que foi criado. Como já mencionado no começo deste artigo, o artigo deve ser tratado como uma instrução do aplicativo resultante. Os aspectos técnicos da implementação do aplicativo serão descritos em outros artigos. A seguir encontra-se o que está anexado ao artigo:

Para executar o programa de otimização automática, compile-o usando o Visual Studio IDE. Observe que o diretório MQL5/Include/Libraries deve conter a biblioteca "ReportManager.dll" descrita no primeiro artigo. Também está disponível no projeto em anexo o Otimizador Automático (é necessário compilar).