English Русский 中文 Español Deutsch 日本語
preview
Explorando a magia dos períodos de negociação com o auxílio do Frames Analyzer

Explorando a magia dos períodos de negociação com o auxílio do Frames Analyzer

MetaTrader 5Testador | 20 janeiro 2023, 10:42
515 0
Anatoli Kazharski
Anatoli Kazharski

Conteúdo


Introdução

Este artigo apresenta uma ferramenta valiosa para operadores que buscam uma análise aprofundada dos resultados da otimização de seus algoritmos de negociação. Com ela, é possível melhorar os resultados da negociação automatizada de maneira eficiente e sem gastar muito tempo e recursos.

O que é o Frames Analyzer? Bem, ele é uma biblioteca plugável para análise de quadros de otimização que pode ser anexada a qualquer EA. Ela permite analisar os resultados da otimização de parâmetros de suas estratégias de negociação tanto durante o processo de teste quanto fora dele, lendo arquivos MQD ou bancos de dados criados após a otimização de parâmetros.

Trata-se de um avanço exclusivo, que nunca foi apresentado desta forma anteriormente. Embora a ideia tenha sido formulada há mais de três anos, foi implementada completamente pela primeira vez pelo membro ativo da comunidade MQL, fxsaber.

Algumas das suas implementações beta foram discutidas em detalhes em alguns dos meus artigos.

A combinação de todas essas ideias resultou em uma ferramenta bastante interessante, informativa e útil, que será abordada em detalhes neste artigo.


Descrição da ferramenta

O módulo contém uma interface gráfica integrada criada usando a biblioteca EasyAndFastGUI v2.0, que não requer codificação adicional. Basta acessar algumas funções principais do MQL (como mostrado abaixo).

A interface gráfica tem em várias seções. Vamos considerá-las.


Aba Frames

Elementos:

  • O botão Open DB permite abrir um banco de dados com quadros de otimização. Esse botão só está disponível no aplicativo Frames Analyzer, fora do testador. Quando usado, o EA funciona no modo de leitura de um banco de dados criado pelo EA após a otimização de parâmetros. 
  • O botão Open MQD-file permite abrir um arquivo MQD com quadros de otimização. Esse botão só está disponível no aplicativo Frames Analyzer, fora do testador. Quando usado, o EA funciona no modo de leitura de um arquivo MQD criado pelo testador após a otimização de parâmetros. Este botão será removido na versão comercial.
  • O campo Curves total permite especificar o número de curvas de saldo exibidas no gráfico simultaneamente.
  • O botão Replay frames inicia a reprodução dos quadros.
  • A área Optimization result mostra o gráfico de saldo com os resultados da otimização.
  • Profit/Loss é o gráfico de todos os resultados.

Se o módulo Frames Analyzer estiver anexado ao Expert Advisor como uma biblioteca, durante a otimização de parâmetros o testador abrirá um gráfico com o Expert Advisor Frames Analyzer e exibirá os resultados intermediários (curvas de saldo). 

Visualização de resultados durante a otimização de parâmetros

Visualização de resultados durante a otimização de parâmetros


Aba Results

No topo estão duas tabelas:

  • Botão Add to favorites para adicionar resultados aos favoritos.
  • Campo de entrada Removed intervals para especificar o número de períodos excluídos com séries não lucrativas.
  • Lista suspensa Criterion para selecionar resultados pelo critério especificado:
    • Profit - 100 resultados são selecionados do número total (e em outros critérios também) com base no saldo máximo.
    • Trades - 100 resultados são selecionados com base no número máximo de negociações.
    • DD (drawdown) - 100 resultados são selecionados com base no rebaixamento mínimo.
    • RF (recovery factor) - 100 resultados são selecionados com base no fator de recuperação máximo.
    • Esses mesmos critérios com o prefixo BI_ têm o mesmo sentido, só que ficam assim após a exclusão da série de negociações não lucrativas.
  • Tabela com os cem melhores resultados de saldo máximo. As células da coluna relacionadas a resultados aprimorados (com o prefixo BI_) são coloridas de maneira diferente para facilitar sua identificação visual (veja a captura de tela abaixo). As células da coluna com parâmetros externos também são destacadas com um cor específica para facilitar sua identificação visual.
  • Tabela com os resultados após a exclusão de períodos de perda do histórico de negociação, incluindo todos os resultados intermediários.

Quando o cursor estiver sobre uma determinada tabela, você pode alternar as linhas usando as teclas: UP, DOWN, PAGE UP, PAGE DOWN, HOME, END. As teclas LEFT e RIGHT o ajudarão a mover-se rapidamente nas tabelas horizontalmente.

Na parte inferior há uma área com duas abas: Balances, Top 100 results. Vamos considerá-las.


Aba Balances

Tem dois gráficos:

  • À esquerda está o gráfico com o saldo inicial e com todos os resultados intermediários melhorados após a exclusão das séries de negociações perdedoras. Assim, é possível ver como o gráfico de saldo muda após a exclusão da próxima série não lucrativa.
  • À direita está o gráfico com o resultado final melhorado do saldo (que é o melhor no gráfico da esquerda) e todos os saldos de cada período de negociação separadamente.

Com o destaque das linhas nas tabelas, os gráficos são atualizados.

Saldos finais e saldos aprimorados após a remoção de períodos não lucrativos

Saldos finais e saldos melhorados após a remoção de períodos não lucrativos


Aba Top 100 results

Tem também dois gráficos:

  • À esquerda está o gráfico dos 100 principais resultados de lucro.
  • À direita está o gráfico dos 100 principais resultados para o critério especificado: Profit, Trades, DD, RF, BI_Profit, BI_Trades, BI_DD, BI_RF.

Ao destacar as linhas nas tabelas, estaremos atualizando os resultados nos gráficos. Por exemplo, abaixo é possível ver qual resultado é destacado à esquerda (curva de saldo preta) e qual é o resultado após excluir séries de negociações perdedoras no gráfico à direita.

100 melhores saldos finais e aprimorados após remover períodos não lucrativos

100 melhores saldos finais e aprimorados após a remoção de períodos não lucrativos

Também é possível especificar o número de períodos não lucrativos a serem removidos do histórico para melhorar o resultado.

Assume-se que, continuando a negociar com estes parâmetros do Expert Advisor e excluindo os períodos onde foram encontradas séries de negociações perdedoras, é possível melhorar significativamente os resultados da própria negociação. Na verdade, às vezes um algoritmo de negociação aparentemente não lucrativo pode se tornar lucrativo com essa abordagem.

Conforme mencionado acima, é possível salvar nossos resultados favoritos em uma tabela à parte. O botão Add to favorites é usado para fazer isso. Os resultados adicionados são destacados com uma cor diferente na tabela de resultados gerais.

Os resultados adicionados aos favoritos são destacados com uma cor diferente

Os resultados adicionados aos favoritos são destacados com uma cor diferente


Aba Favorites

Também tem duas tabelas e dois gráficos. Todas as informações aqui presentes são como na guia Results. A única diferença é que os resultados selecionados podem ser salvos em arquivos e em um banco de dados. Aqui, além de tabelas e gráficos, podemos encontrar os seguintes botões na parte superior da janela:

  • Deleted selected - excluir o resultado selecionado dos favoritos tanto na tabela à esquerda quanto no banco de dados.
  • Delete all - excluir todos os resultados tanto dos favoritos na tabela à esquerda quanto no banco de dados.
  • Save parameters - salvar parâmetros em um arquivo ou em um banco de dados.

Dados e gráficos de resultados selecionados

Dados e gráficos dos resultados selecionados

Ao clicar no botão Save parameters todos os resultados favoritos são salvos:

  • na tabela FAVORITE_RESULTS no banco de dados
  • nos arquivos do diretório MQL5/Files/Reports/FramesA/[CURRENT_TIME] se estiver no modo de leitura de arquivo MQD
  • no local MQL5/Files/Reports/[CURRENT_TIME] [EXPERT_NAME], se estiver no modo FRAME_MODE (imediatamente após a otimização)

Todos os resultados são salvos em pastas separadas com o número da passada como o nome da pasta. Este será um arquivo set com parâmetros externos e algumas capturas de tela de tabelas e gráficos discutidos acima. O prefixo no nome de todos os arquivos é o número da passada:

  • 422462.set - arquivo set com parâmetros externos do Expert Advisor.
  • 422462_balance_sub_bi.png - captura de tela do saldo após excluir todos os períodos com séries de negociações não lucrativas, bem como todos os saldos separadamente para cada intervalo restante.
  • 422462_balances_bi.png - captura de tela de todos os saldos após excluir todos os períodos com séries de negociações não lucrativas.
  • 422462_bi_table.png - captura de tela da tabela de resultados finais após a exclusão de todos os períodos com séries de negociações não lucrativas.
  • 422462_gui.png - captura de tela completa da GUI.

Exemplo de arquivo  set com os parâmetros do EA:

; this file contains last used input parameters for testing/optimizing FA expert advisor
; Experts\Advisors\ExpertMACD.ex5
;
Inp_Expert_Title=ExpertMACD||0.0||0.0||0.0||N
Inp_Signal_MACD_PeriodFast=15||5.0||5.0||30.0||Y
Inp_Signal_MACD_PeriodSlow=25||5.0||5.0||30.0||Y
Inp_Signal_MACD_PeriodSignal=25||5.0||5.0||30.0||Y
Inp_Signal_MACD_TakeProfit=270||30.0||5.0||300.0||Y
Inp_Signal_MACD_StopLoss=115||20.0||5.0||200.0||Y


Banco de dados de resultados de otimização

Conforme mencionado acima, a ferramenta Frames Analyzer após a otimização dos parâmetros no testador salva todos os resultados da otimização em um banco de dados. E não só! Após cada otimização de parâmetros, um novo banco de dados é criado no diretório local do terminal MQL5/Files/DB. O nome do banco de dados consiste na hora da criação e no nome do EA: [CURRENT_TIME] [EXPERT_NAME].db.

São criadas três tabelas no total:

A tabela OPTIMIZATION_RESULTS que armazenará os seguintes dados (colunas):

  • Pass - número da passada.
  • Profit - lucro obtido.
  • Trades - número de negociações.
  • PF - fator de lucro (Profit factor).
  • DD - rebaixamento.
  • RF - fator de recuperação (Recovery factor).
  • Colunas com todos os parâmetros externos do EA:
    • Parâmetro 1
    • Parâmetro 2
    • etc.
  • Deals - histórico de negociações usado para calcular os saldos a cada passada.

Tabela com dados de todos os passos de otimização

Tabela com dados de todas as passadas da otimização

Tabela EXPERT_PARAMETERS. Ela contém os parâmetros externos do Expert Advisor, bem como seu nome e diretório onde localizado durante a otimização de parâmetros. Esta tabela tem duas colunas:

  • Parameter - nome do parâmetro.
  • Value - valor do parâmetro.

Abaixo está um exemplo para o EA ExpertMACD que vem como parte do pacote padrão do terminal de negociação Os parâmetros externos (INPUT_n) usam a coluna Value para armazenar o nome do parâmetro externo e os parâmetros de otimização (delimitados por ||) usados para a passada em questão.

Tabela com os dados do EA

Tabela com os dados do EA

Outros parâmetros necessários para a operação do Expert Advisor poderão ser adicionados a esta tabela posteriormente.

A tabela FAVORITE_RESULTS armazena os resultados de otimização selecionados. Ela tem apenas três colunas:

  • FavoriteID - ID da posição.
  • Pass - número da passada para saber que dados da tabela OPTIMIZATION_RESULTS obter.
  • RemovedIntervals - períodos excluídos durante os quais não é suposto negociar no mercado real.

Tabela de resultados de otimização selecionados com períodos excluídos

Tabela de resultados de otimização selecionados com períodos excluídos

Desse modo, é possível obter tudo o que precisamos do banco de dados após a otimização e análise dos resultados para o ulterior trabalho do Expert Advisor.


Como usar o Frames Analyzer?

Antes de começar, você precisa baixar o Frames Analyzer para o seu computador a partir desta página. Ao executar este Expert Advisor no seu terminal, você notará que todos os gráficos e tabelas no Frames Analyzer estarão vazios. Isso é esperado, pois essa ferramenta precisa de resultados de otimização para funcionar corretamente. Se você não pode esperar para experimentá-lo, você pode baixar um arquivo de banco de dados com resultados de otimização que eu preparei para você no final deste artigo. Depois, você precisa colocar esse arquivo no diretório MQL5/Files/DB. Então, você poderá carregá-lo através da interface do usuário do EA Frames Analyzer. Na guia Frames, clique no botão Open DB. Uma caixa de diálogo será aberta para selecionar o arquivo de banco de dados, como mostrado abaixo. Observe que, neste caso, já tenho vários arquivos de banco de dados nesse diretório.

Selecionando o arquivo de banco de dados na caixa de diálogo

Selecionando um arquivo de banco de dados na caixa de diálogo

Mas para que o Frames Analyzer salve os resultados de otimização de seu Expert Advisor, você precisa anexá-lo como uma biblioteca no código de seu EA. Isso é bastante simples de fazer.

Depois de baixar o Frames Analyzer para o seu computador, ele estará no diretório MQL5/Experts/Market. Então você pode anexá-lo ao arquivo principal (*.mq5) de seu EA da seguinte forma:

#import "..\Experts\Market\FramesA.ex5"
  void OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam);
  void OnTesterEvent(void);
  void OnTesterInitEvent(void);
  void OnTesterPassEvent(void);
  void OnTesterDeinitEvent(void);
#import

Como pode ser visto acima, várias funções precisam ser importadas do EA Frames Analyzer. Elas são essenciais para processar eventos na operação da interface gráfica e para coletar dados durante a otimização dos parâmetros do EA no testador. Para fazer isso, é necessário chamar essas funções em funções similares dentro do Expert Advisor, como mostrado abaixo:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) {
  FramesA::OnEvent(id, lparam, dparam, sparam);
}
//+------------------------------------------------------------------+
//| Test completion event handler                                    |
//+------------------------------------------------------------------+
double OnTester(void) {
  FramesA::OnTesterEvent();
  return(0.0);
}
//+------------------------------------------------------------------+
//| TesterInit function                                              |
//+------------------------------------------------------------------+
void OnTesterInit(void) {
  FramesA::OnTesterInitEvent();
}
//+------------------------------------------------------------------+
//| TesterPass function                                              |
//+------------------------------------------------------------------+
void OnTesterPass(void) {
  FramesA::OnTesterPassEvent();
}
//+------------------------------------------------------------------+
//| TesterDeinit function                                            |
//+------------------------------------------------------------------+
void OnTesterDeinit(void) {
  FramesA::OnTesterDeinitEvent();
}
//+------------------------------------------------------------------+

Parece tudo muito simples, não é?

É importante mencionar que, se você estiver utilizando a biblioteca EasyAndFastGUI v2.0 em seus projetos para criar interfaces gráficas, que é usada na ferramenta Frames Analyzer, então não é necessário fazer qualquer ação extra para fazê-los funcionarem juntos dentro de um aplicativo MQL. O Frames Analyzer, quando embutido no seu Expert Advisor, funciona de forma independente no modo quadro (FRAME_MODE) e não interferirá no trabalho da interface gráfica do seu aplicativo.

Caso não tenha a biblioteca EasyAndFastGUI v2.0 e o Frames Analyzer, também funcionará.

No final do artigo você pode encontrar um exemplo do EA ExpertMACD com o módulo Frames Analyzer já anexado.


Implementado períodos excluídos em EAs

Bem, você tem a ferramenta Frames Analyzer. Você a anexou ao seu Expert Advisor, otimizou os parâmetros e agora possui um banco de dados com os resultados de todas as passadas, incluindo os valores dos parâmetros externos correspondentes. Além disso, em uma tabela separada, você salvou resultados de otimização selecionados, excluindo intervalos de tempo não desejáveis, os quais podem ser aplicados à sua estratégia de negociação.

Vejamos um exemplo de como isso pode ser implementado.

Suponha que você queira incluir a seleção de intervalos de tempo de negociação através da interface gráfica do usuário (GUI). Seria útil ter um elemento na GUI que exibe os períodos excluídos, durante os quais a negociação não é recomendada. Se você possui a biblioteca EasyAndFastGUI 2.0 para criar interfaces gráficas avançadas, você já tem essa opção. Uma das últimas atualizações adicionou outro elemento exclusivo, CTimeRanges, que permite trabalhar com intervalos de tempo. Em seguida, vamos criar uma interface gráfica usando esse elemento e examiná-lo em detalhes.

A interface gráfica terá os seguintes elementos:

  • CWindow - forma que contém os controles.
  • CButton - botão para abrir o arquivo de banco de dados.
  • CComboBox - lista suspensa que contém os números das passadas presentes na tabela de resultados de otimização selecionados.
  • CTimeRanges - faixa de tempo que exibe intervalos não recomendados para negociar no mercado real.

Para criar essa interface gráfica, é preciso apenas de algumas linhas de código:

void CApp::CreateGUI(void) {

//--- Form
  m_window1.ResizeMode(true);
  m_window1.ThemeButtonIsUsed(true);
  CCoreCreate::CreateWindow(m_window1, "TIME TRADE RANGES", 1, 1, 350, 100, true, true, true, true);
  
//--- Button
  CCoreCreate::CreateButton(m_button1, m_window1, 0, "Open DB...", 10, 30, 100);
  
//--- Combobox
  string items1[] = {"12345", "19876", "45678", "23456", "67890"};
  CCoreCreate::CreateCombobox(m_combobox1, m_window1, 0, "Passes: ", 120, 30, 135, 90, items1, 103, 0);
  
//--- Time ranges
  string time_ranges[] = {
    "00:45:00 - 01:20:01",
    "08:55:00 - 09:25:01",
    "12:55:00 - 13:50:01",
    "15:30:00 - 17:39:59",
    "20:10:00 - 21:05:00"
  };
  m_time_ranges1.SetTimeRanges(time_ranges);
  m_time_ranges1.AutoXResizeMode(true);
  m_time_ranges1.AutoXResizeRightOffset(5);
  CCoreCreate::CreateTimeRanges(m_time_ranges1, m_window1, 0, 5, 60, 390);
}

O exemplo acima mostra como você pode definir intervalos de tempo no elemento TimeRanges durante os quais você não deseja negociar.

Ao iniciar qualquer aplicativo MQL com essa interface gráfica, você verá o seguinte resultado no gráfico:

Demonstração do elemento TimeRanges da biblioteca EasyAndFastGUI v2.0

Demonstração do elemento TimeRanges da biblioteca EasyAndFastGUI v2.0

A captura de tela acima apresenta um elemento da interface gráfica de usuário que é uma escala de tempo intradiária. Esse elemento dinâmico pode se adaptar automaticamente à largura do elemento pai (neste caso, a forma). A linha tracejada e pontilhada marca a hora atual, próxima à qual um rótulo de texto com a hora atual é exibido. Quando o cursor passa sobre a linha do tempo, uma linha sólida vertical é desenhada sob o cursor com um rótulo de texto do tempo para o qual ele está apontando. 

Isso se mostrou bastante informativo, mas isso não é tudo! Se você clicar na escala, o elemento gera um evento personalizado com o ID ON_CLICK_TIME_RANGE, que pode ser processado em seu próprio aplicativo MQL. O parâmetro dparam conterá o número de segundos decorridos desde o início do dia, e o parâmetro sparam conterá o intervalo de tempo removido ou uma string vazia se nenhum dos intervalos definidos estiver sob o cursor durante o clique. Além disso, você pode ativar o modo em que o elemento TimeRanges pode ser aberto clicando em todo o gráfico.

Aqui está o código que permite capturar e manipular este evento:

void CApp::OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam) {

  if(id == CHARTEVENT_CUSTOM + ON_CLICK_TIME_RANGE) {
    
    if(m_time_ranges1.Id() == lparam) {
      Print(__FUNCTION__, " > pressed time: ", ::TimeToString((datetime)dparam, TIME_MINUTES|TIME_SECONDS), "; pressed time range: ", sparam);
      
      string time_ranges[];
      m_time_ranges1.GetTimeRanges(time_ranges);
      
      ArrayPrint(time_ranges);
      return;
    }
    return;
  }
}

Como se pode ver na listagem de código acima, antes de nada o ID do evento é verificado e, se este for um evento personalizado ON_CLICK_TIME_RANGE, verificamos o ID do elemento que é passado no parâmetro lparam no evento. Em seguida, enviamos os dados recebidos para o log. Como exemplo, também mostramos como obter os intervalos definidos em um elemento usando o método CTimeRanges()::GetTimeRanges() e exibir a matriz resultante no log.

Os logs dos EAs (guia Experts) mostrará algo assim:

CApp::OnEvent > pressed time: 16:34:54; pressed time range: 15:30:00 - 17:39:59
"00:45:00-01:20:01" "08:55:00-09:25:01" "12:55:00-13:50:01" "15:30:00-17:39:59" "20:10:00-21:05:00"

O exemplo a seguir mostra como uma matriz de números de passadas é obtida da tabela de resultados favoritos (FAVORITE_RESULTS) no banco de dados.

void CApp::GetPassNumbersOfFavoriteResults(const string db_filename, ulong &passes[]) {

  ::ResetLastError();
  uint flags = DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE;
  int db_handle = ::DatabaseOpen(db_filename, flags);
  if(db_handle == INVALID_HANDLE) {
    ::Print(__FUNCTION__, " > DB: ", db_filename, " open failed with code: ", ::GetLastError());
    return;
  }
  
  string command = "SELECT (Pass) FROM FAVORITE_RESULTS;";
  
  int db_query = ::DatabasePrepare(db_handle, command);
  if(db_query == INVALID_HANDLE) {
    ::Print(__FUNCTION__, " > DB: ", db_filename, " request failed with code ", ::GetLastError());
    ::DatabaseClose(db_handle);
    return;
  }

  for(int i = 0; ::DatabaseRead(db_query); i++) {
    int pass_number;
    ::ResetLastError();
    if(::DatabaseColumnInteger(db_query, 0, pass_number)) {
      int prev_size = ::ArraySize(passes);
      ::ArrayResize(passes, prev_size + 1);
      passes[prev_size] = (ulong)pass_number;
    } 
    else {
      ::Print(__FUNCTION__, " > DatabaseColumnInteger() error: ", ::GetLastError());
    }
  }
//--- Finish working with the database
  ::DatabaseFinalize(db_query);
  ::DatabaseClose(db_handle);
}

Assim que tivermos os números de passadas, podemos obter os períodos excluídos especificando o número da passada usando o método GetFavoriteRemovedIntervalsByPassNumber():

string CApp::GetFavoriteRemovedIntervalsByPassNumber(const ulong pass_number) {

  ::ResetLastError();
  uint flags = DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE;
  int db_handle = ::DatabaseOpen(m_db_filename, flags);
  if(db_handle == INVALID_HANDLE) {
    ::Print(__FUNCTION__, " > DB: ", m_db_filename, " open failed with code: ", ::GetLastError());
    return(NULL);
  }
  
  string command = "SELECT (RemovedIntervals) FROM FAVORITE_RESULTS WHERE Pass=" + (string)pass_number + ";";
  
  int db_query = ::DatabasePrepare(db_handle, command);
  if(db_query == INVALID_HANDLE) {
    ::Print(__FUNCTION__, " > DB: ", m_db_filename, " request failed with code ", ::GetLastError());
    ::DatabaseClose(db_handle);
    return(NULL);
  }
  
  string time_ranges = "";
  
  if(::DatabaseRead(db_query)) {
    ::ResetLastError();
    if(!::DatabaseColumnText(db_query, 0, time_ranges)) {
      ::Print(__FUNCTION__, " > DatabaseColumnText() error: ", ::GetLastError());
    }
  }
//--- Finish working with the database
  ::DatabaseFinalize(db_query);
  ::DatabaseClose(db_handle);
  
  return(time_ranges);
}

Ao clicar no botão Open DB... para abrir um arquivo com um banco de dados, o método OnClickOpenDB() é chamado, e este abre uma caixa de diálogo para selecionar um arquivo no diretório MQL5/Files/DB. Se o arquivo for selecionado, ele será salvo para uso posterior em outros métodos. Em seguida, usando o método GetPassNumbersOfFavoriteResults(), cujo código foi fornecido na listagem anterior, em uma matriz obtemos os números das passadas a partir da tabela de resultados favoritos. Adicionamos esses dados à lista suspensa e selecionamos o primeiro item.

bool CApp::OnClickOpenDB(const int id) {

  if(m_button1.Id() != id) {
    return(false);
  }

  string filenames[];
  string folder = "DB";
  string filter = "DB files (*.db)|*.db|SQLite files (*.sqlite)|*.sqlite|All files (*.*)|*.*";
  if(::FileSelectDialog("Select database file to upload", folder, filter,
                        FSD_FILE_MUST_EXIST, filenames) > 0) {
    
    int filenames_total = ::ArraySize(filenames);
    if(filenames_total < 1) {
      return(false);
    }
    
    m_db_filename = filenames[0];
    
    ::Print(__FUNCTION__, " > m_db_filename: ", m_db_filename);
    
    ulong passes[];
    GetPassNumbersOfFavoriteResults(m_db_filename, passes);
    
    ::ArrayPrint(passes);
    
    CListView *list_view = m_combobox1.GetListViewPointer();
    
    int passes_total = ::ArraySize(passes);
    if(passes_total > 0) {
      list_view.Clear();
      for(int i = 0; i < passes_total; i++) {
        list_view.AddItem(i, (string)passes[i]);
      }
      m_combobox1.SelectItem(0);
      m_combobox1.GetButtonPointer().Update(true);
      list_view.Update(true);
    }
  }
  return(true);
}

Selecionando o número da passada na lista suspensa, obteremos os períodos excluídos relacionados a este elemento no método SetTimeRange(), salvando-os no array global e configurando-os no elemento gráfico do tipo CTimeRanges.

void CApp::SetTimeRange(void) {

  CListView *list_view = m_combobox1.GetListViewPointer();
  
  int   index       = list_view.SelectedItemIndex();
  ulong pass_number = (int)list_view.GetValue(index);
  
  string removed_intervals = GetFavoriteRemovedIntervalsByPassNumber(pass_number);
  
  ::ArrayFree(m_time_ranges_str);
  ::StringSplit(removed_intervals, ::StringGetCharacter("|", 0), m_time_ranges_str);
  
  int ranges_total = ::ArraySize(m_time_ranges_str);
  for(int i = 0; i < ranges_total; i++) {
    ::StringReplace(m_time_ranges_str[i], " - ", "-");
  }
  
  ::ArrayPrint(m_time_ranges_str);
  
  m_time_ranges1.SetTimeRanges(m_time_ranges_str);
  m_time_ranges1.Update();
}

Em seguida, precisamos de uma estrutura para armazenar intervalos de tempo (períodos). Se você estiver usando a biblioteca EasyAndFastGUI 2.0, essa estrutura (TimeRange) já está no arquivo do elemento CTimeRanges:

struct TimeRange {
  MqlDateTime start;
  MqlDateTime end;
};

Se você não usar esta biblioteca gráfica, poderá declarar essa estrutura em seu código separadamente.

Como os intervalos de tempo excluídos já estão armazenados na forma de string no método SetTimeRange(), agora precisamos colocá-los em uma matriz de estruturas para que seja conveniente trabalhar com eles no futuro. Para fazer isso, usamos o método GetTimeRanges():

void CApp::GetTimeRanges(TimeRange &ranges[]) {
  
  ::ArrayFree(ranges);
  
  int ranges_total = ::ArraySize(m_time_ranges_str);
  for(int i = 0; i < ranges_total; i++) {
    
    string tr[];
    ::StringSplit(m_time_ranges_str[i], ::StringGetCharacter("-", 0), tr);
  
    int size = ::ArraySize(ranges);
    ::ArrayResize(ranges, size + 1);
    
    MqlDateTime start, end;
    ::TimeToStruct(::StringToTime(tr[0]), start);
    ::TimeToStruct(::StringToTime(tr[1]), end);
    
    datetime time1 = HourSeconds(start.hour) + MinuteSeconds(start.min) + start.sec;
    datetime time2 = HourSeconds(end.hour) + MinuteSeconds(end.min) + end.sec;
    ::TimeToStruct(time1, ranges[size].start);
    ::TimeToStruct(time2, ranges[size].end);
  }
}

Como exemplo, esses métodos podem ser usados assim ao manipular os eventos correspondentes (versão abreviada da classe do aplicativo):

class CApp : public CCoreCreate {
 private:
  TimeRange         m_time_ranges[];
  string            m_time_ranges_str[];
  
...

  virtual void      OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam);

  void              SetTimeRange(void);
  void              GetTimeRanges(TimeRange &ranges[]);
  
  int               HourSeconds(const int hour)     { return(hour * 60 * 60); }
  int               MinuteSeconds(const int minute) { return(minute * 60);    }
};

void CApp::OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam) {

  if(id == CHARTEVENT_CUSTOM + ON_CLICK_BUTTON) {
    if(OnClickOpenDB((int)lparam)) {
      SetTimeRange();
      GetTimeRanges(m_time_ranges);
      return;
    }
    return;
  }
  
  if(id == CHARTEVENT_CUSTOM + ON_CLICK_COMBOBOX_ITEM) {
    if(OnSelectPass((int)lparam)) {
      SetTimeRange();
      GetTimeRanges(m_time_ranges);
      return;
    }
    return;
  }
}

Agora podemos escrever o método CheckTradeTime() para verificar as condições de negociação apenas no horário permitido:

bool CApp::CheckTradeTime(void) {

  bool     is_trade_time = true;
  datetime current_time  = ::TimeCurrent();
  
  MqlDateTime time;
  ::TimeToStruct(current_time, time);
  
  int ranges_total = ::ArraySize(m_time_ranges);
  for(int i = 0; i < ranges_total; i++) {
  
    MqlDateTime start = m_time_ranges[i].start;
    MqlDateTime end   = m_time_ranges[i].end;
    
    datetime time_c = HourSeconds(time.hour) + MinuteSeconds(time.min) + time.sec;
    datetime time_s = HourSeconds(start.hour) + MinuteSeconds(start.min) + start.sec;
    datetime time_e = HourSeconds(end.hour) + MinuteSeconds(end.min) + end.sec;
    
    if(time_c >= time_s && time_c <= time_e) {
      is_trade_time = false;
      break;
    }
  }
  return(is_trade_time);
}

Tudo o que precisamos fazer agora é configurar uma chamada para o método CheckTradeTime(), por exemplo, na função OnTick():

  if(CheckTradeTime()) {
    Print(__FUNCTION__, " > trade time!");
  }

Você pode baixar um aplicativo pronto no final do artigo com uma demonstração do que foi descrito acima.

Demonstração de obtenção de intervalos de tempo excluídos da negociação

Demonstração de obtenção de intervalos de tempo excluídos da negociação


Considerações finais

Vamos resumir.

Depois de otimizar os parâmetros no testador de estratégia, para visualizar cada resultado, você deve executar testes individuais em ordem, e isso é muito demorado e ineficiente para encontrar os parâmetros que você gostaria de escolher ao negociar com uma conta real.

O que é o Frames Analyzer? o Frames Analyzer é uma ferramenta para analisar quadros de otimização durante o processo de otimização de parâmetros quer seja no testador de estratégia ou fora do mesmo. Ele permite ler arquivos MQD ou bancos de dados criados após a otimização de parâmetros Você poderá compartilhar esses resultados de otimização com outros usuários que tenham o Frames Analyzer para discutir resultados de otimização conjuntamente.

O "Frames Analyzer" permite visualizar os melhores resultados da otimização simultaneamente na forma de gráficos. Esses resultados podem ser selecionados aplicando vários critérios, como Profit, Trades, Drawdown, Profit Factor, Recovery Factor. Além disso, o Frames Analyzer possui um módulo interno de Best Intervals para identificar períodos de tempo não lucrativos, removendo-os do histórico de negociação para ver como isso afeta o resultado.

Os resultados selecionados podem ser salvos no banco de dados, bem como em arquivos set prontos com parâmetros externos do Expert Advisor. Os intervalos de tempo excluídos também são armazenados no banco de dados, permitindo que você os aplique em sua negociação para melhorar os resultados e maximizar os lucros. O Frames Analyzer deixa apenas os intervalos de tempo mais estatisticamente seguros para negociação!


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

Arquivos anexados |
MQL5.zip (5545.43 KB)
Aprendendo a construindo um Expert Advisor que opera de forma automática (Parte 11): Automação (III) Aprendendo a construindo um Expert Advisor que opera de forma automática (Parte 11): Automação (III)
Um sistema automático sem segurança não irá dar certo. Mas segurança não nasce sem que entendamos adequadamente algumas coisas. Neste artigo vamos entender é tão difícil alcançar a segurança máxima em sistemas automáticos.
Indicadores adaptativos Indicadores adaptativos
Neste artigo, exploraremos diferentes enfoques para desenvolver indicadores adaptativos. Esses indicadores se destacam pelo uso de feedback entre as entradas e saídas, o que permite que eles se adaptem de forma autônoma para processar séries temporais financeiras de forma eficiente.
Gestão de risco e capital utilizando Expert Advisors Gestão de risco e capital utilizando Expert Advisors
Este artigo é sobre o que você não pode ver em um relatório de backtest, o que você deve esperar usando um software de negociação automatizado, como gerenciar seu dinheiro se estiver usando expert advisors e como cobrir uma perda significativa para permanecer na atividade de negociação quando você está usando procedimentos automatizados.
DoEasy. Controles (Parte 23): apurando os objetos WinForms TabControl e SplitContainer DoEasy. Controles (Parte 23): apurando os objetos WinForms TabControl e SplitContainer
Neste artigo, incluiremos novos eventos de mouse relacionados aos limites das áreas de trabalho dos objetos WinForms e corrigiremos algumas falhas na funcionalidade dos controles TabControl e SplitContainer.