English Русский 中文 Español Deutsch 日本語
Estudo de técnicas de análise de velas (Parte II): Busca automática de novos padrões

Estudo de técnicas de análise de velas (Parte II): Busca automática de novos padrões

MetaTrader 5Testador | 11 abril 2019, 08:51
2 753 1
Alexander Fedosov
Alexander Fedosov

Índice

Introdução

Os métodos existentes da análise de velas foram analisados no artigo anterior. Nós descobrimos que eles não são universais e não são adequados para todas as condições. Pelo contrário, antes de usá-los, você deve realizar uma verificação adicional para o instrumento financeiro específico, período gráfico, etc. ou seja, para as condições em que os padrões serão usados.

A abordagem especificamente projetada também permite a análise individual de cada padrão para um instrumento financeiro selecionado ou um conjunto de instrumentos. Ele também permite encontrar possíveis correlações entre os parâmetros pré-definidos, incluindo o período gráfico ou o intervalo da amostra, bem como as métricas especialmente inseridas, como frequência de ocorrência, probabilidade do movimento em uma determinada direção após a formação do padrão.

O método de análise apenas se referiu a 14 padrões selecionados a partir de uma grande variedade de formações de velas existentes. É impossível analisar todos os padrões um por um, portanto, outra solução foi encontrada. A principal característica de todos os padrões examinados anteriormente foi a base deles, ou seja, em que eles consistem. Quatro dos padrões de velas analisadas consistiam de uma vela e dez deles consistiam de duas velas. Os padrões de vela analisados consistiram em diferentes conjuntos ou sequências de seis tipos de velas.

A principal questão com os padrões é que eles apareceram há muito tempo, enquanto as propriedades, o comportamento e a dinâmica do mercado estão mudando constantemente. A fim de manter a tendência de análise de mercado, algumas alterações devem ser feitas na análise de padrões. Neste artigo, nós consideramos um sistema para buscar e testar novos padrões de velas com base nos tipos de velas conhecidos. 


Determinação da tarefa

Para desenvolver o novo algoritmo de geração de padrão de velas, nós precisamos definir as regras principais:

  • Novos padrões consistirão em um, dois ou três tipos simples de velas.
  • Os tipos simples de vela são: vela de alta, vela de baixa, spinning top, doji, marubozu e martelo.
  • Os tipos de vela serão divididos com base na direção: alta e baixa. A exceção é para a vela doji.
  • Tipos simples de velas podem ser repetidos. Exemplo: um padrão de duas velas de alta baixista.

O esquema geral de criação de novos padrões é exibido na Figura 1.


Fig.1 Novos algoritmos de criação de padrões.

Assim, nós temos um certo conjunto de velas, dos quais novos padrões serão formados. Esses novos padrões conterão de 1 a 3 velas com ou sem repetições. O agrupamento conterá 11 velas básicas, no total. Os padrões de velas gerados serão analisados de acordo com o mesmo princípio, que foi discutido no primeiro artigo.


Atualização do protótipo de interface

Uma guia separada estará disponível para trabalhar com os padrões gerados. A guia conterá elementos semelhantes, que estão disponíveis na guia existente Analyze. A única diferença é que a primeira coluna é nomeada por Set e o tamanho da tabela não será corrigido.

Fig.2 Guia para trabalhar com os padrões gerados.

A terceira guia Settings foi substancialmente revisada. A configuração adequada dos parâmetros nesta guia é essencial, e é por isso que nós vamos considerá-la com mais detalhes.

Fig.3 A guia Settings atualizada.

  1. Adicionado a numeração dos tipos simples de velas e seus nomes. Assim, nós podemos facilmente combinar a representação visual com a lista "Used candles".
  2. Adicionado a opção para selecionar o idioma da interface. Russo ou inglês.
  3. A lista de 'Used candles' que participam na formação de padrões. As caixas de seleção permitem a seleção dos tipos necessários para teste.
  4. Um grupo de botões comutáveis. Apenas uma posição pode ser selecionada. Dessa forma, nós evitamos a sobrecarga da lista de padrões gerados na guia AutoSearch.
  5. Dois botões comutáveis. 'Repeat' (com repetições) significa que pode haver apenas um tipo de vela no padrão. Um exemplo é um padrão que consiste de três velas Marubozu de baixa. Ao mesmo tempo, o padrão Spinning Top - Marubozu - o padrão Marubozu também pode existir.

Implementação de ferramentas

Nós Determinamos as principais adições que precisam ser implementadas no aplicativo existente. Agora vamos continuar com a implementação em si. Primeiramente, nós precisamos adicionar uma guia adicional. A indexação das guias será alterada. A guia AutoSearch terá o índice igual a 1, que foi usado anteriormente pela guia Settings. O índice 2 será usado para Settings. Isso deve ser considerado na vinculação dos elementos filho. Portanto, nós precisamos alterar o índice de todos os elementos filho da guia Settings de 1 para 2.

//+------------------------------------------------------------------+
//| Criação do grupo com guias                                       |
//+------------------------------------------------------------------+
bool CProgram::CreateTabs(const int x_gap,const int y_gap)
  {
#define TABS1_TOTAL 3
//--- Armazena o ponteiro para o controle principal
   m_tabs1.MainPointer(m_window1);
//--- Propriedades
   m_tabs1.IsCenterText(true);
   m_tabs1.PositionMode(TABS_TOP);
   m_tabs1.AutoXResizeMode(true);
   m_tabs1.AutoYResizeMode(true);
   m_tabs1.AutoXResizeRightOffset(3);
   m_tabs1.AutoYResizeBottomOffset(25);

//--- Adiciona as guias com as propriedades especificadas
   string tabs_names[TABS1_TOTAL]={"Analysis","Auto search","Settings"};
   for(int i=0; i<TABS1_TOTAL; i++)
      m_tabs1.AddTab(tabs_names[i],150);
//--- Criação do controle
   if(!m_tabs1.CreateTabs(x_gap,y_gap))
      return(false);
//--- Adiciona o objeto ao array comum dos grupos de objetos
   CWndContainer::AddToElementsArray(0,m_tabs1);
   return(true);
  }

Os elementos filho da guia AutoSearch são semelhantes aos da guia Analyze. A única diferença é o nome da primeira coluna e o esquema de cores. Isso permite distinguir visualmente os elementos da interface dessas guias.

//+------------------------------------------------------------------+
//| Cria um formulário para os controles                             |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
  {
//--- Adiciona o ponteiro ao array da janela
   CWndContainer::AddWindow(m_window1);
//--- Propriedades
   m_window1.XSize(750);
   m_window1.YSize(500);
   m_window1.FontSize(9);
   m_window1.IsMovable(true);
   m_window1.CloseButtonIsUsed(true);
   m_window1.CollapseButtonIsUsed(true);
   m_window1.FullscreenButtonIsUsed(true);
   m_window1.TooltipsButtonIsUsed(true);
//--- Criando o formulário
   if(!m_window1.CreateWindow(m_chart_id,m_subwin,caption_text,5,5))
      return(false);
//--- Guias
   if(!CreateTabs(3,43))
      return(false);
//--- A guia Analyze
//--- Campos de edição
   if(!CreateSymbolsFilter(m_symb_filter1,10,10,"Symbols",0))
      return(false);
   if(!CreateRequest(m_request1,250,10,"Search",0))
      return(false);
   if(!CreateRange(m_range1,485,10,"Range",0))
      return(false);
//--- Combobox
   if(!CreateComboBoxTF(m_timeframes1,350,10,"Timeframe",0))
      return(false);
//--- Criação de uma tabela de símbolos
   if(!CreateSymbTable(m_symb_table1,10,50,0))
      return(false);
//--- Criação da tabela de resultados
   if(!CreateTable1(m_table1,120,50,0))
      return(false);

//--- A guia AutoSearch
//--- Campos de edição
   if(!CreateSymbolsFilter(m_symb_filter2,10,10,"Symbols",1))
      return(false);
   if(!CreateRequest(m_request2,250,10,"Search",1))
      return(false);
   if(!CreateRange(m_range2,485,10,"Range",1))
      return(false);
//--- Combobox
   if(!CreateComboBoxTF(m_timeframes2,350,10,"Timeframe",1))
      return(false);
//--- Criação de uma tabela de símbolos
   if(!CreateSymbTable(m_symb_table2,10,50,1))
      return(false);
//--- Criação da tabela de resultados
   if(!CreateTable2(m_table2,120,50,1))
      return(false);

Como pode ser visto no código acima, a diferença diz respeito apenas a dois métodos CreateTable1() e CreateTable2(). Agora, vamos prosseguir para a guia Settings. Aqui a primeira mudança diz respeito aos recursos gráficos. Além disso, o elemento de configuração do parâmetro da vela foi adicionado ao elemento de criação. Isso é feito usando o método CreateNameCandle().

//+------------------------------------------------------------------+
//| Cria um elemento de configuração de velas                        |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\settings_dark.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\long.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\short.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\doji.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\spin.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\maribozu.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\hammer.bmp"
//---
bool CProgram::CreateCandle(CPicture &pic,CButton &button,CTextLabel &candlelabel,const string candlename,const int x_gap,const int y_gap,string path)
  {
//--- Armazena o ponteiro para o controle principal
   pic.MainPointer(m_tabs1);
//--- Anexa à guia
   m_tabs1.AddToElementsArray(2,pic);
//--- Propriedades
   pic.XSize(64);
   pic.YSize(64);
   pic.IconFile(path);
//--- Criação de um botão
   if(!pic.CreatePicture(x_gap,y_gap))
      return(false);
//--- Adiciona o ponteiro ao elemento da base
   CWndContainer::AddToElementsArray(0,pic);
   CreateButtonPic(pic,button,"Images\\EasyAndFastGUI\\Icons\\bmp16\\settings_dark.bmp");
   CreateNameCandle(candlelabel,x_gap,y_gap+pic.YSize(),candlename);
   return(true);
  }

Isso é tudo sobre as alterações relativas aos controles já criados. Agora vamos prosseguir para os novos. Uma delas é a opção de seleção de idioma da interface. O aplicativo suporta dois idiomas: Russo e inglês. A seleção do idioma é exibida na Figura 4.

Fig.4 Seleção do idioma da interface.

A mudança de idioma é realizada através do método ChangeLanguage(). De acordo com sua lógica, os componentes de texto são substituídos. O código do método é muito simples. Aqui está sua aplicação no manipulador de eventos:

//--- Seleção do item em uma caixa de combinação
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
     {
      //--- Alteração do período gráfico
      if(ChangePeriod1(lparam))
         Update(true);
      //--- Alteração do idioma da interface
      if(ChangeLanguage(lparam))
         Update(true);
     }

Como pode ser visto no código, quando um item em uma caixa de combinação é selecionado, o método de mudança de idioma determina qual dos itens no menu suspenso é selecionado e define o índice do idioma apropriado:

//+------------------------------------------------------------------+
//| Mudando o idioma da interface                                    |
//+------------------------------------------------------------------+
bool CProgram::ChangeLanguage(const long id)
  {
//--- Verifica o ID do elemento
   if(id!=m_lang_setting.Id())
      return(false);
   m_lang_index=m_lang_setting.GetListViewPointer().SelectedItemIndex();
//---
   if(m_lang_index==0)
     ....

A próxima seção é responsável pela seleção dos tipos de velas simples, dos quais os padrões para teste serão gerados. Existem 11 tipos. O controle é implementado como uma lista de nomes dos tipos de vela e caixas de seleção usados, que refletem a seleção. O método CreateListView() foi usado para implementação:

//+------------------------------------------------------------------+
//| Cria a lista                                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateListView(const int x_gap,const int y_gap)
  {
//--- Tamanho da lista
#define CANDLE_TOTAL 11
//--- Armazena o ponteiro para o controle principal
   m_listview1.MainPointer(m_tabs1);
//--- Anexa à guia
   m_tabs1.AddToElementsArray(2,m_listview1);
//--- Propriedades
   m_listview1.XSize(175);
   m_listview1.YSize(250);
   m_listview1.ItemYSize(19);
   m_listview1.LabelXGap(25);
   m_listview1.LightsHover(true);
   m_listview1.CheckBoxMode(true);
   m_listview1.ListSize(CANDLE_TOTAL);
   m_listview1.AutoYResizeMode(true);
   m_listview1.AutoYResizeBottomOffset(10);
   m_listview1.FontSize(10);
//--- Preenche a lista com os dados
   string cand_name[CANDLE_TOTAL]=
     {
      "Long — bullish",
      "Long — bearish",
      "Short — bullish",
      "Short — bearish",
      "Spinning Top — bullish",
      "Spinning Top — bearish",
      "Doji",
      "Marubozu — bullish",
      "Marubozu — bearish",
      "Hammer — bullish",
      "Hammer — bearish"
     };
   for(int r=0; r<CANDLE_TOTAL; r++)
     {
      m_listview1.SetValue(r,(string)(r+1)+". "+cand_name[r]);
     }
//--- Cria a lista
   if(!m_listview1.CreateListView(x_gap,y_gap))
      return(false);
//--- Adiciona o ponteiro ao elemento da base
   CWndContainer::AddToElementsArray(0,m_listview1);
   return(true);
  }

Os próximos dois controles estão diretamente relacionados à lista de tipos simples de velas. O primeiro é uma chave, que habilita ou desabilita as repetições. Como já mencionado na seção de definição de tarefas, a repetição está relacionada ao padrão que consiste em apenas um tipo de vela, independentemente de seu tamanho. 

Fig.5 Seleção do tipo de vela analisada e do modo de repetição.

O método responsável pela criação da chave Repeat é chamada de CreateDualButton():

//+------------------------------------------------------------------+
//| Criação da chave Repeat                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateDualButton(CButton &lbutton,CButton &rbutton,const int x_gap,const int y_gap,const string ltext,const string rtext)
  {
   CreateButton(lbutton,x_gap,y_gap,ltext);
   CreateButton(rbutton,x_gap+99,y_gap,rtext);
   return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateButton(CButton &button,const int x_gap,const int y_gap,const string text)
  {
//--- Salva o ponteiro para o controle principal
   button.MainPointer(m_tabs1);
//--- Anexa à guia
   m_tabs1.AddToElementsArray(2,button);
//--- Propriedades
   button.XSize(100);
   button.YSize(30);
   button.Font("Trebuchet");
   button.FontSize(10);
   button.IsCenterText(true);
   button.BorderColor(C'0,100,255');
   button.BackColor(clrAliceBlue);
   button.BackColorLocked(C'50,180,75');
   button.BorderColorLocked(C'50,180,75');
   button.LabelColorLocked(clrWhite);
//--- Criação do controle
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Adiciona o ponteiro ao elemento da base
   CWndContainer::AddToElementsArray(0,button);
   return(true);
  }

A operação do controle setting é monitorada no evento do clique esquerdo:

   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      ....
      //--- Se o botão foi pressionado
      if(lparam==m_button7.Id())
        {
         m_button7.IsLocked(true);
         m_button8.IsLocked(false);
        }
      else if(lparam==m_button8.Id())
        {
         m_button7.IsLocked(false);
         m_button8.IsLocked(true);
        }

Tendo analisado a parte visual da aplicação, vamos agora prosseguir para a parte de cálculo. Primeiro, nós precisamos determinar as configurações mínimas, a sequência de ações e os métodos de processamento para essas ações:

Etapa 1. Configuração dos dados de entrada.

Antes de começar a usar o aplicativo, nós precisamos selecionar os tipos de velas simples, a partir dos quais os padrões para a análise serão gerados. Em seguida, selecione se o modo Repeat está ativado. O modo de repetição implica o uso de apenas um tipo de vela na criação de um padrão de qualquer tamanho. Em seguida, selecione o número de velas em um padrão. Isso pode incluir padrões de um, dois ou três velas. Observe que pelo menos duas velas simples devem ser selecionados para a geração de padrões de dois ou três velas. Se você tentar gerar padrões com menos velas, o aplicativo retornará um erro, conforme mostrado na Figura 6.

Fig.6 Erro: apenas um tipo de vela é selecionado para os padrões de duas velas.

Etapa 2. Trabalhando com a guia AutoSearch.

Depois de definir os parâmetros de entrada adequados, alterne para a AutoSearch e selecione um símbolo para teste. Vamos ver em detalhes as capacidades dos instrumentos apresentados nesta guia.

  • Selecione e busque os símbolos da moeda em análise. No campo de entrada, você pode digitar parte de um símbolo ou os especificados, separados por vírgulas e clicar no botão Search. Você também pode usar a frase predefinida Major, que mostra os principais pares de moedas. Para ver todos os símbolos disponíveis na janela Market Watch, você deve desmarcar a caixa no canto superior esquerdo. Isso desativará a filtragem na janela de busca.
  • Em seguida, selecione período gráfico desejado na lista suspensa e o intervalo da amostra como o número de velas do período de tempo selecionado.
  • Depois de selecionar o instrumento ou a lista, clique no atual para iniciar a análise. Depois disso, o aplicativo gerará padrões configurados na etapa 1, executará cálculos apropriados e exibirá os dados em uma tabela. Depois de receber os dados em uma tabela, você pode alterar o período gráfico e os dados serão recalculados em tempo real.

Vamos ver em detalhes a tabela de dados resultante. Isso ajudará a entender o princípio de cálculo e a operação dos algoritmos que consideraremos mais adiante.


Fig.7 Exemplo de cálculo dos dados para o par EURUSD.  

Como pode ser visto na Figura 7, uma linha com o par de moedas analisadas é selecionada na tabela Symbol. O período gráfico M15 e a amostra na faixa de 8000 velas de 15 minutos são exibidas no topo da parte direita. A tabela de resultados apresenta seis colunas. Aqui nós vamos considerar apenas a primeira, enquanto as demais colunas foram descritas no primeiro artigo, na seção Desenvolvimento do protótipo de interface.

A primeira coluna é Set. Nas linhas abaixo, os valores são exibidos no formato de [1,1,2]. Três números entre colchetes significam que um padrão de três velas é usado. O padrão consiste de velas simples 1 e 2 exatamente na ordem especificada entre colchetes. Os números de velas usadas podem ser encontradas na aba Settings na seção Used candles.

Fig.8 A lista e o número de velas usadas na geração de padrões.  

Agora nós sabemos como configurar e iniciar o aplicativo, para podermos considerar a lógica de operação interna. Depois de definir os dados de entrada, nós clicamos no instrumento financeiro na tabela Symbol. O tratamento do clique no item da tabela chama o método ChangeSymbol2():

//--- Evento de pressionar o item de uma lista ou tabela
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
     {
      //--- Alteração do símbolo
      if(ChangeSymbol1(lparam))
         Update(true);
      if(ChangeSymbol2(lparam))
         m_table2.Update(true);
     }

Nesse método, além de definir os valores de informação na barra de estado, há mais dois métodos.

//+------------------------------------------------------------------+
//| Alteração do símbolo                                             |
//+------------------------------------------------------------------+
bool CProgram::ChangeSymbol2(const long id)
  {
//--- Verifica o ID do elemento
   if(id!=m_symb_table2.Id())
      return(false);
//--- Sai se a linha não estiver destacada
   if(m_symb_table2.SelectedItem()==WRONG_VALUE)
     {
      //--- Exibe a descrição completa do símbolo na barra de estado
      m_status_bar.SetValue(0,"No symbol selected for analysis");
      m_status_bar.GetItemPointer(0).Update(true);
      return(false);
     }
//--- Obtém um símbolo
   string symbol=m_symb_table2.GetValue(0,m_symb_table2.SelectedItem());
//--- Exibe a descrição completa do símbolo na barra de estado
   string val=(m_lang_index==0)?"Выбранный символ: ":"Selected symbol: ";
   m_status_bar.SetValue(0,val+::SymbolInfoString(symbol,SYMBOL_DESCRIPTION));
   m_status_bar.GetItemPointer(0).Update(true);
   if(!BuildingAutoSearchTable())
      return(false);
   GetPatternType(symbol,m_total_combination);
   return(true);
  }

O primeiro método é BuildingAutoSearchTable(). Ele cria padrões a partir da lista de velas simples selecionadas na guia Settings, na seção Used candles (Fig.8) e os exibe na primeira coluna da tabela de resultados.

//+------------------------------------------------------------------+
//| Recria a tabela de busca automática de padrões                   |
//+------------------------------------------------------------------+
bool CProgram::BuildingAutoSearchTable(void)
  {
//---
   if(!GetCandleCombitaion())
     {
      if(m_lang_index==0)
         MessageBox("Число выбранных свечей меньше размера исследуемого паттерна!","Ошибка");
      else if(m_lang_index==1)
         MessageBox("The number of selected candles is less than the size of the studied pattern!","Error");
      return(false);
     }
//--- Exclui todas as linhas
   m_table2.DeleteAllRows();
//--- Define o número de linhas pelo número de símbolos
   for(int i=0; i<ArraySize(m_total_combination); i++)
     {
      m_table2.AddRow(i);
      m_table2.SetValue(0,i,m_total_combination[i]);
     }
   m_table2.DeleteRow(ArraySize(m_total_combination));
//--- Atualiza a tabela
   m_table2.Update(true);
   m_table2.GetScrollVPointer().Update(true);
   m_table2.GetScrollHPointer().Update(true);
   return(true);
  }
//+------------------------------------------------------------------+
//| Gerando padrões baseados em velas simples                        |
//+------------------------------------------------------------------+
bool CProgram::GetCandleCombitaion(void)
  {
   string candlenumber[];
   int selected_candles=0,n;
   ArrayResize(candlenumber,m_total_candles);
//---
   for(int i=0;i<m_total_candles;i++)
     {
      if(m_listview1.GetState(i))
        {
         candlenumber[selected_candles]=(string)(i+1);
         selected_candles++;
        }
     }

   if((m_pattern_size==2 && selected_candles<2) || (m_pattern_size==3 && selected_candles<2) || selected_candles<1)
      return(false);
//--- Cálculo do número de combinações
   if(m_pattern_size>1)
      n=(m_button7.IsLocked())?(int)MathPow(selected_candles,m_pattern_size):(int)MathPow(selected_candles,m_pattern_size)-selected_candles;
   else
      n=selected_candles;
   ArrayResize(m_total_combination,n);

   n=0;
//--- Um conjunto de uma vela
   if(m_pattern_size==1)
     {
      for(int i=0;i<selected_candles;i++)
         m_total_combination[i]="["+candlenumber[i]+"]";
     }
//--- Um conjunto de duas velas
   else if(m_pattern_size==2)
     {
      //--- Modo Repeat ativado
      if(m_button7.IsLocked())
        {
         for(int i=0;i<selected_candles;i++)
           {
            for(int j=0;j<selected_candles;j++)
              {
               m_total_combination[n]="["+candlenumber[i]+","+candlenumber[j]+"]";
               n++;
              }
           }
        }
      //--- Modo Repeat desativado
      else if(m_button8.IsLocked())
        {
         for(int i=0;i<selected_candles;i++)
           {
            for(int j=0;j<selected_candles;j++)
              {
               if(j!=i)
                 {
                  m_total_combination[n]="["+candlenumber[i]+","+candlenumber[j]+"]";
                  n++;
                 }
              }
           }
        }
     }
//--- Conjunto de três velas
   else if(m_pattern_size==3)
     {
      //--- Modo Repeat ativado
      if(m_button7.IsLocked())
        {
         for(int i=0;i<selected_candles;i++)
           {
            for(int j=0;j<selected_candles;j++)
              {
               for(int k=0;k<selected_candles;k++)
                 {
                  m_total_combination[n]="["+candlenumber[i]+","+candlenumber[j]+","+candlenumber[k]+"]";
                  n++;
                 }
              }
           }
        }
      //--- Modo Repeat desativado
      else if(m_button8.IsLocked())
        {
         for(int i=0;i<selected_candles;i++)
           {
            for(int j=0;j<selected_candles;j++)
               for(int k=0;k<selected_candles;k++)
                 {
                  if(i==j && i==k)
                     continue;
                  m_total_combination[n]="["+candlenumber[i]+","+candlenumber[j]+","+candlenumber[k]+"]";
                  n++;
                 }
           }
        }
     }
   return(true);
  }

A criação de padrões baseados em um conjunto de velas é executada pelo método GetCandleCombination(). O objetivo do método é exibir todas as combinações possíveis de velas simples com base nos números de sequência das velas selecionadas, com ou sem repetições e levando em conta o tamanho selecionado no número de velas na seção padrão. 

Todas as combinações são gravadas no array de cadeias de caracteres m_total_combitaion[], e os dados são adicionados à tabela de resultados no método BuildingAutoSearchTable().

O segundo método, que é chamado depois BuildingAutoSearchTable(), é responsável pelo cálculo e exibição de outra base de dados na lista resultante de padrões gerados. Vamos considerar o método GetPatternType() mais detalhado - ele foi usado no primeiro artigo para o cálculo de padrões pré-definidos, mas foi simplificado. O objetivo desta tarefa é reconhecer os padrões predefinidos em um gráfico e analisá-los. Como esse método é usado para pesquisar padrões já existentes e gerados, a sobrecarga do método foi implementada:

   //--- Reconhecendo padrões
   bool              GetPatternType(const string symbol);
   bool              GetPatternType(const string symbol,string &total_combination[]);

Na segunda variante, nós usamos o array de dados de string criado anteriormente com os padrões gerados. O objetivo do método é identificar qualquer um dos nossos padrões gerados no gráfico, para avaliá-lo e calcular sua eficiência. A descrição da ideia e dos algoritmos é fornecida na seção definição do problema do primeiro artigo. Portanto, nós não vamos nos alongar sobre isso agora.

A eficiência do padrão encontrado foi avaliada anteriormente usando um array, à qual nós adicionamos as estimativas da categoria A, B e C para a tendência de alta e a tendência de baixa. Neste artigo, nós convertemos o array em uma estrutura. Isso permitiu o encurtamento do código resultante. A estrutura é a seguinte:

struct RATING_SET
  {
   int               a_uptrend;
   int               b_uptrend;
   int               c_uptrend;
   int               a_dntrend;
   int               b_dntrend;
   int               c_dntrend;
  };

 Para a nossa tarefa, nós precisamos deste conjunto de estimativas para cada um dos padrões gerados. Portanto, o array de estruturas RATING_SET é declarado no início do método. O tamanho do array corresponde ao número de padrões gerados ou ao tamanho do array de string m_total_combitaion[].

   RATING_SET ratings[];

//---
   total_patterns=ArraySize(total_combination);
...
   ArrayResize(ratings,total_patterns);

Esse array armazena todos os padrões gerados, que, no entanto, são apresentados como uma sequência a ser exibida na primeira coluna do resultado da tabela. Assim, o próximo estágio é extrair de cada índice de string de padrões de velas simples usados e convertê-los para o tipo de vela da CANDLE_STRUCTURE.

struct CANDLE_STRUCTURE
  {
   double            open,high,low,close;       // OHLC
   TYPE_TREND        trend;                     //Tendência
   bool              bull;                      //Vela de alta
   double            bodysize;                  //Vela de baixa
   TYPE_CANDLESTICK  type;                      //Tipo da vela
  };

Para este efeito, nós vamos realizar algumas conversões. Além disso, nós vamos considerar um método adicional que ajuda a converter o índice de velas em seu tipo.

//---
   for(int i=0;i<total_patterns;i++)
     {
      StringReplace(total_combination[i],"[","");
      StringReplace(total_combination[i],"]","");
      if(m_pattern_size>1)
        {
         ushort sep=StringGetCharacter(",",0);
         StringSplit(total_combination[i],sep,elements);
        }
      ZeroMemory(ratings[i]);
      m_pattern_total[i]=0;
      if(m_pattern_size==1)
         IndexToPatternType(cand1[i],(int)total_combination[i]);
      else if(m_pattern_size==2)
        {
         IndexToPatternType(cand1[i],(int)elements[0]);
         IndexToPatternType(cand2[i],(int)elements[1]);
        }
      else if(m_pattern_size==3)
        {
         IndexToPatternType(cand1[i],(int)elements[0]);
         IndexToPatternType(cand2[i],(int)elements[1]);
         IndexToPatternType(cand3[i],(int)elements[2]);
        }
     }

Faça um loop através de cada valor de string dos padrões gerados, remova os colchetes, aplique StringSplit() com base no separador "," e adicione as informações ao array elements[]. Observe que esse procedimento deve ser aplicado a padrões que consistem em mais de 1 vela, porque, para uma vela, não haverá vírgulas e nós só precisamos remover os colchetes. Agora vamos considerar o método IndexToPatternType(), ao qual nós inserimos CANDLE_STRUCTURE para preencher e processar os dados do array de padrões gerados.

void CProgram::IndexToPatternType(CANDLE_STRUCTURE &res,const int index)
  {
//--- Long - altista
   if(index==1)
     {
      res.bull=true;
      res.type=CAND_LONG;
     }
//--- Long - baixista
   else if(index==2)
     {
      res.bull=false;
      res.type=CAND_LONG;
     }
//--- Short - altista
   else if(index==3)
     {
      res.bull=true;
      res.type=CAND_SHORT;
     }
//--- Short - baixista
   else if(index==4)
     {
      res.bull=false;
      res.type=CAND_SHORT;
     }
//--- Spinning Top - altista
   else if(index==5)
     {
      res.bull=true;
      res.type=CAND_SPIN_TOP;
     }
//--- Spinning Top - baixista
   else if(index==6)
     {
      res.bull=false;
      res.type=CAND_SPIN_TOP;
     }
//--- Doji
   else if(index==7)
     {
      res.bull=true;
      res.type=CAND_DOJI;
     }
//--- Marubozu - altista
   else if(index==8)
     {
      res.bull=true;
      res.type=CAND_MARIBOZU;
     }
//--- Marubozu - baixista
   else if(index==9)
     {
      res.bull=false;
      res.type=CAND_MARIBOZU;
     }
//--- Hammer - altista
   else if(index==10)
     {
      res.bull=true;
      res.type=CAND_HAMMER;
     }
//--- Hammer - baixista
   else if(index==11)
     {
      res.bull=false;
      res.type=CAND_HAMMER;
     }
  }

Dependendo do tamanho do padrão, um, dois ou três CANDLE_STRUCTURE são preenchidos: cand1, cand2, cand3. Veja o código abaixo:

if(m_pattern_size==1)
         IndexToPatternType(cand1[i],(int)total_combination[i]);
      else if(m_pattern_size==2)
        {
         IndexToPatternType(cand1[i],(int)elements[0]);
         IndexToPatternType(cand2[i],(int)elements[1]);
        }
      else if(m_pattern_size==3)
        {
         IndexToPatternType(cand1[i],(int)elements[0]);
         IndexToPatternType(cand2[i],(int)elements[1]);
         IndexToPatternType(cand3[i],(int)elements[2]);
        }

Isso permite a análise imediata do número necessário de velas, independentemente do tamanho do padrão encontrado no gráfico. 

Para o padrão de vela única, apenas uma vela será usada para análise e cálculo em cada caso específico, enquanto o conjunto inteiro será usado para padrões de 2 e 3 velas. Por exemplo, com uma amostra na faixa de 2000 velas, para padrões de 1 vela, o conjunto consistirá na 2000º vela. Com duas velas, um conjunto consiste de 2000 e 1999, e assim por diante. 

Em seguida, nós consideramos o seguinte fragmento do método GetPatternType(), que é responsável por encontrar cada um dos padrões gerados no gráfico.

//---
   for(int i=m_range_total2;i>5;i--)
     {
      if(m_pattern_size==1)
        {
         //--- Obtém o tipo de vela atual
         GetCandleType(symbol,cur_cand,m_timeframe2,i);                                         // Vela atual
         //---
         for(int j=0;j<total_patterns;j++)
           {
            if(cur_cand.type==cand1[j].type && cur_cand.bull==cand1[j].bull)
              {
               m_pattern_total[j]++;
               GetCategory(symbol,i-3,ratings[j],m_timeframe2);
              }
           }
        }
      else if(m_pattern_size==2)
        {
         //--- Obtém o tipo de vela atual
         GetCandleType(symbol,prev_cand,m_timeframe2,i);                                        // Vela anterior
         GetCandleType(symbol,cur_cand,m_timeframe2,i-1);                                       // Vela atual
         //---
         for(int j=0;j<total_patterns;j++)
           {
            if(cur_cand.type==cand1[j].type && cur_cand.bull==cand1[j].bull && 
               prev_cand.type==cand2[j].type && prev_cand.bull==cand2[j].bull)
              {
               m_pattern_total[j]++;
               GetCategory(symbol,i-4,ratings[j],m_timeframe2);
              }
           }
        }
      else if(m_pattern_size==3)
        {
         //--- Obtém o tipo de vela atual
         GetCandleType(symbol,prev_cand2,m_timeframe2,i);                                       // Vela anterior
         GetCandleType(symbol,prev_cand,m_timeframe2,i-1);                                      // Vela anterior
         GetCandleType(symbol,cur_cand,m_timeframe2,i-2);                                       // Vela atual
         //---
         for(int j=0;j<total_patterns;j++)
           {
            if(cur_cand.type==cand1[j].type && cur_cand.bull==cand1[j].bull && 
               prev_cand.type==cand2[j].type && prev_cand.bull==cand2[j].bull && 
               prev_cand2.type==cand3[j].type && prev_cand2.bull==cand3[j].bull)
              {
               m_pattern_total[j]++;
               GetCategory(symbol,i-5,ratings[j],m_timeframe2);
              }
           }
        }
     }

Como nós podemos ver no código acima, o loop está logo no começo e ela é encerrada na sexta vela. Por quê? No primeiro artigo, nós mencionamos que, para analisar a eficiência do padrão, nós precisamos determinar para onde o preço vai após a formação do padrão e descobrir o que o padrão prediz, qual é a frequência e a probabilidade de previsão. Para essa avaliação, nós implementamos a análise da movimentação dos preços dentro de três velas seguindo o padrão. A vela zero atual não participa da análise porque ela ainda não está completa. 

Fig.9 Cálculo da avaliação da eficiência do padrão. 

Um padrão de três velas é exibido na Figura 9. Para avaliar sua eficiência, nós precisamos de mais três velas, exceto a zero. Portanto, para preencher essas condições para o padrão atual, a primeira vela dentro do padrão pode ter um índice mínimo igual a 6. 

Depois de procurar por padrões, contar seu número e avaliar por categorias A, B, C, nós precisamos processar os dados recebidos e adicionar os resultados em uma tabela. Esses cálculos são realizados no método CoefCalculation().

//---
   for(int i=0;i<total_patterns;i++)
      CoefCalculation(m_table2,i,ratings[i],m_pattern_total[i]);

 Os argumentos do método são:

  • m_table2 — um link para a tabela na qual os resultados do cálculo de cada padrão gerado serão adicionados.
  • i — linha da tabela.
  • ratings[i] — um array de estruturas RATING_SET, que contém um conjunto de classificações baseadas em categorias para cada padrão
  • m_pattern_total[i] — um array contendo a quantidade de padrões encontrados de cada tipo.

Vamos considerar o método em mais detalhes.

//+------------------------------------------------------------------+
//| Cálculo dos coeficientes de avaliação de eficiência              |
//+------------------------------------------------------------------+
bool CProgram::CoefCalculation(CTable &table,const int row,RATING_SET &rate,int found)
  {
   double p1,p2,k1,k2;
   int sum1=0,sum2=0;
   sum1=rate.a_uptrend+rate.b_uptrend+rate.c_uptrend;
   sum2=rate.a_dntrend+rate.b_dntrend+rate.c_dntrend;
//---
   p1=(found>0)?NormalizeDouble((double)sum1/found*100,2):0;
   p2=(found>0)?NormalizeDouble((double)sum2/found*100,2):0;
   k1=(found>0)?NormalizeDouble((m_k1*rate.a_uptrend+m_k2*rate.b_uptrend+m_k3*rate.c_uptrend)/found,3):0;
   k2=(found>0)?NormalizeDouble((m_k1*rate.a_dntrend+m_k2*rate.b_dntrend+m_k3*rate.c_dntrend)/found,3):0;

   table.SetValue(1,row,(string)found);
   table.SetValue(2,row,(string)((double)found/m_range_total2*100),2);
   table.SetValue(3,row,(string)p1,2);
   table.SetValue(4,row,(string)p2,2);
   table.SetValue(5,row,(string)k1,2);
   table.SetValue(6,row,(string)k2,2);
//--- Atualiza a tabela
   table.Update(true);
   table.GetScrollVPointer().Update(true);
   table.GetScrollHPointer().Update(true);
   return(true);
  }

Como pode ser visto a partir da implementação do método, devido ao uso da estrutura, nós podemos ver claramente como os coeficientes de análise do padrão são calculados.

Demonstração da operação do aplicativo

Como exemplo, nós vamos testar alguns dos padrões gerados com parâmetros diferentes.

Etapa 1. Seleção de modelos simples de velas.

Primeiro, nós precisamos selecionar todos os possíveis modelos de velas simples na seção Used candles, definir "Repeat" e "Number of candles in the pattern" igual a um. O valor do limiar da tendência em pontos será definido como 200. Aqui está a configuração:

Fig.10 A primeira etapa de configuração para a análise de padrões gerados.

Agora, navegue até a guia AutoSearch, digite Major na caixa de busca e clique em Search. Em seguida, defina o período gráfico para Н1 e selecione o par de moedas GBPUSD. Aqui estão os resultados do teste.

Fig.11 Os resultados do teste geraram padrões de uma vela.

Seleciona seis tipos de velas mais frequentes. Estes são 1,2,5,6,3,4 na ordem descendente.

Etapa 2. Testando os padrões de duas velas.

Agora, ele forma padrões de duas velas baseadas em tipos simples. Navegue até a guia Settings e desmarque as caixas de 7 a 11. Desta vez, nós definimos o modo "No Repeat" e definimos o "Number of candles in the pattern" como 2. A configuração acima é exibida na Fig.12.

Fig.12 Testando a configuração para padrões de duas velas geradas.  

Navegue de volta para AutoSearch e clique em GBPUSD. Isso gera várias combinações de velas selecionadas e fornece sua classificação. Mas se o "Valor limiar da tendência" for baixo, especialmente em períodos gráficos mais altos, os resultados do movimento de preço são quase sempre os mesmos. Ao aumentar o valor do limiar, você pode definir condições mais rigorosas para a busca de padrões. Por exemplo, abaixo estão os resultados para o limiar igual a 400:

Fig.13 Resultado do teste com um valor limiar da tendência aumentado.

Entre os resultados obtidos, eu tentei encontrar um grande movimento de preço em uma direção e muito menos movimento na direção oposta. Como pode ser visto na Figura 13, a situação foi encontrada duas vezes: [1,4] e [2,6]. Estes são os padrões Long(bullish)—Short(bearish) and Long(bearish)—Spinning Top(bearish). A segunda variante do padrão é mais preferível, porque a frequência de sua ocorrência é quase duas vezes mais alta.

Agora vamos testar os padrões de três velas. Com todas as possíveis opções de modelos simples, nós temos muitas variantes de padrões, portanto, nós usaremos apenas 4 tipos, que foram frequentemente encontrados em testes anteriores — 1,2,5,6. Uma guia Settings corretamente configurada tem a seguinte aparência:

Fig.14 Testando a configuração para padrões de três velas geradas.

O valor do limiar da tendência ainda é igual a 400. Em seguida, abra a guia AutoSearch e clique novamente em GBPUSD. O mesmo princípio para selecionar os padrões de bom desempenho é usado aqui: um movimento de preço muito maior em uma direção do que na outra oposta. Isso pode ser visto no coeficiente de eficiência. Por exemplo, por duas vezes seguidas eu encontrei resultados muito interessantes com coeficientes e parâmetros de probabilidade muito bons.

Fig.15 Os resultados do teste geraram padrões de três velas.

Estes são os padrões [2,5,2] e o próximo [2,5,5]: Long(bearish)—Spinning Top(bullish)—Long(bearish) e Long(bearish)—Spinning Top(bullish)—Spinning Top(bullish). O primeiro padrão de velas mostrou uma alta probabilidade de uma tendência de alta e um grande coeficiente de eficiência. O segundo tem boa probabilidade em uma direção, mas um coeficiente de eficiência um pouco menor. 

Um grande número de combinações de parâmetros pode fornecer outros resultados interessantes. Ao testar, não é recomendado usar todos os onze tipos de vela de uma só vez, pois o processamento de dados pode levar muito tempo. As combinações máximas possíveis de padrões para análise são iguais a 1463, não levando em consideração o intervalo de amostra, o período de tempo, o valor do limite de tendência e as configurações individuais dos tipos de velas simples. Uma análise tão grande requer muito tempo.

Conclusões

O arquivo anexado abaixo contém todos os arquivos descritos corretamente organizados em pastas. Para o seu bom funcionamento, você só precisa salvar a pasta MQL5 na pasta do terminal.

Programas utilizados no artigo

#
 Nome
Tipo
Descrição
1
PatternAnalyzer.mq5 Interface gráfica
 Barra de ferramentas para analisar padrões de velas
2 MainWindow.mqh Código Base  Biblioteca GUI
3 Program.mqh Código Base  Biblioteca de métodos para a criação de interface e cálculo de elementos

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

Arquivos anexados |
MQL5.zip (431.36 KB)
Últimos Comentários | Ir para discussão (1)
misterzinho
misterzinho | 17 abr 2019 em 00:47

How do i open the pattern analyzer window? I already uploaded the MQL5 folder into the the terminal folder but i don't know how to open the window.


Thanks for your attention.

Criando interfaces gráficas para EAs e indicadores baseados no .Net Framework e C# Criando interfaces gráficas para EAs e indicadores baseados no .Net Framework e C#
Uma maneira simples e rápida de criar janelas gráficas usando o editor do Visual Studio, e integração no código MQL do EA. O artigo é destinado para um vasto público de leitores e não requer conhecimentos de C# e .Net.
Integração da MetaTrader 5 e Python: recebendo e enviando dados Integração da MetaTrader 5 e Python: recebendo e enviando dados
O vasto processamento de dados requer ferramentas extensas e muitas vezes está além do ambiente seguro de um único aplicativo. Linguagens de programação especializadas são usadas para processar e analisar dados, estatísticas e aprendizado de máquina. Uma das principais linguagens de programação para processamento de dados é o Python. O artigo fornece uma descrição de como conectar a MetaTrader 5 e o Python usando sockets, além de como receber cotações por meio da API do terminal.
Colorindo os resultados da otimização de estratégias de negociação Colorindo os resultados da otimização de estratégias de negociação
Neste artigo nós vamos realizar um experimento: nós vamos colorir os resultados da otimização. A cor é determinada por três parâmetros: os níveis de vermelho, verde e azul (RGB). Existem outros métodos de codificação de cores, que também usam três parâmetros. Assim, três parâmetros de teste podem ser convertidos em uma cor, que representa visualmente os valores. Leia este artigo para descobrir se essa representação pode ser útil.
Análise sintática MQL via ferramentas MQL Análise sintática MQL via ferramentas MQL
Este artigo descreve um pré-processador, um leitor e um analisador para examinar códigos fonte em MQL. A implementação em MQL está anexada ao artigo.