English Русский 中文 Español Deutsch 日本語 Türkçe
preview
Abordagem de força bruta para encontrar padrões

Abordagem de força bruta para encontrar padrões

MetaTrader 5Testador | 11 janeiro 2021, 15:01
1 422 0
Evgeniy Ilin
Evgeniy Ilin

Introdução

Neste artigo, procuraremos padrões no mercado, criaremos Expert Advisors com base neles e verificaremos quanto tempo esses padrões permanecem funcionais. Pensei que este artigo iria ser extremamente útil para aqueles que criam sistemas de negociação que se adaptam automaticamente. Gostaria de falar um pouco sobre a força bruta, bem como o que quero dizer com esse conceito no contexto da negociação Forex. Na verdade, por meio da abordagem de força bruta (busca exaustiva) podemos determinar uma sequência de números - para código ou outro propósito - que em última instância permitirá, com a probabilidade máxima ou com a probabilidade máxima disponível, chegar ao resultado desejado. Por exemplo, podemos usá-la para minerar criptomoedas ou hackear uma senha de conta ou de wi-fi. Pode ter muitas aplicações. No caso do Forex, nossa sequência deve dar o máximo de lucro, trabalhando o maior tempo possível. A sequência pode ser de qualquer tipo e comprimento, o principal é que seja útil. Na verdade, o tipo depende basicamente do nosso algoritmo.


Por que decidi cobrir este tópico específico e o que ele tem de especial?

Em geral, procuro expressar meus pensamentos para que tenham o máximo de aplicação prática para outros traders. Por que eu preciso disso? Boa pergunta. Em primeiro lugar, quero compartilhar minha experiência de forma desinteressada. Seria bom se alguém pudesse aplicar minhas ideias e fazer algo interessante e lucrativo, não importa o que eu receba com isso. Ademais, eu certamente entendo que talvez essas não sejam minhas ideias, eu esteja reinventando a roda ou eu queira acreditar em aliens, não importa. Acredito que é necessário compartilhar experiências e colaborar da forma mais produtiva possível. Talvez este seja o segredo do sucesso. Você pode permanecer entre 4 paredes por toda a sua vida e construir castelos no ar em sua cabeça, mas não terá nenhum resultado disso. Na minha opinião, o tema dos padrões e, em geral, como encontrá-los e por que encontrá-los, é de fundamental importância para o entendimento da física do mercado. Acredite em mim, o que estou tentando dizer é muito importante, e sua carreira de negociador pode depender até mesmo de uma compreensão elementar dessas coisas, embora, é claro, a própria frase "carreira de negociador" pareça muito engraçada para mim 🙂. Espero poder trazer algum benefício para as pessoas que ainda estão doentes com esses problemas.


Sobre a abordagem de força bruta e suas diferenças em relação à rede neural

Na realidade, uma rede neural também é um tipo de força bruta. Acontece que seus algoritmos são muito diferentes daqueles dos da força bruta simples. Agora, não vou analisar nenhuma arquitetura de rede neural individual e seus tipos de elementos constituintes, como o perceptron, em vez disso, tentarei cobrir a questão em geral. Na verdade, se estamos vinculados a uma determinada arquitetura, já limitamos antecipadamente as capacidades de nosso algoritmo. Uma arquitetura fixa já é uma limitação irreparável. Uma rede neural é uma espécie de arquitetura, em nosso caso, de uma possível estratégia. Como resultado, a configuração de uma rede neural sempre corresponde a um determinado arquivo com um mapa de rede. Basicamente, são sempre indícios da montagem de uma determinada unidade. É como uma impressora 3D. Defina os parâmetros da peça e ela a montará para você. Em termos mais simples, uma rede neural é um código geral que não faz sentido sem um mapa. É como pegar qualquer linguagem de programação, todos os seus recursos e simplesmente criar um projeto vazio. Como resultado, a linguagem existe, mas com um modelo vazio ela não faz nada. E assim é aqui. Simplesmente, ao contrário da força bruta, uma rede neural pode fornecer uma variabilidade quase ilimitada em nossas estratégias, qualquer número de critérios e maior eficiência. A única desvantagem dessa abordagem é que, se você escrever esse código genérico descuidadamente, sua eficácia pode até mesmo acabar sendo inferior à força bruta. A possível complexidade do sistema aumenta, o que significa que a voracidade do programa aumenta. Como resultado, transformamos nossa estratégia num mapa de rede, que é seu equivalente. Na abordagem de força bruta, fazemos a mesma coisa, só que se transforma numa sequência simples de alguns números. Essa sequência é muito mais simples do que um mapa de rede, é mais fácil de calcular, mas também tem um teto em termos de eficiência. Eis esquematicamente o que disse:


Em outras palavras, no caso de uma abordagem de força bruta, selecionamos uma sequência de números que, ao interagir com nosso código, dará resultados diferentes. Mas, como o algoritmo é fixo, toda a sua flexibilidade está contida na nossa matriz de números. Seu comprimento é fixo e a estrutura é muito simples. No caso de uma rede neural, procuramos um mapa de rede que dê o melhor resultado. Nesses casos, estamos sempre buscando alguma sequência de bytes, alguns dados que são eventualmente convertidos no algoritmo resultante. A única diferença está em suas capacidades e complexidade. 


Meu algoritmo de força bruta e de otimização

Para meu algoritmo, usei a expansão multidimensional de Taylor. A escolha desta abordagem se deve às seguintes considerações. Eu queria fornecer um algoritmo que fosse o mais variável possível com toda a sua simplicidade, realmente não queria estar vinculado a nenhuma fórmula em particular, já que no final qualquer função pode ser expandida numa série de Taylor ou uma série de Fourier. Mas, parece-me que a série de Fourier não é muito adequada para este problema, e não estou familiarizado com o equivalente multidimensional. Por isso, escolhi a primeira variante, que é muito mais fácil de implementar programaticamente. Uma série de Taylor unidimensional é assim:

Y = Cs[0]+Cs[1]*(x-x0)^1 + Cs[2]*(x-x0) ^2 + ... + Cs[i]*(x-x0)^n

Onde os coeficientes na frente das potências desempenham o papel de derivadas da ordem de 0 a n. Isso pode ser convertido para uma forma mais simples, abrindo todos os colchetes:

Y = C[0]+C[1]*x^1 + C[2]*x^2 + ... + C[i]*x^n + ...= Soma(0,+infinito)(C[i]x^i)

Nesse caso, temos apenas uma variável. Esta série pode imitar qualquer função contínua e diferenciável na vizinhança de qualquer ponto x0 escolhido. Quanto mais termos nesta fórmula, mais precisamente ela descreve nossa função. Se seu número for igual ao infinito, então este é o equivalente absoluto de nossa função. Não vou mostrar aqui como expandir qualquer função numa série de Taylor nas proximidades de qualquer ponto. Você encontrará isso em qualquer livro de matemática. No entanto a variante unidimensional não basta, porque queremos usar os dados de várias barras, aumentando a variabilidade da fórmula geral. Por isso, devemos usar a opção multidimensional:

Y = Soma(0,+infinito)( C[i]*Variante do produto(x1^p1*x2^p2...*xN^pN) 

É bastante difícil escrever esta fórmula de outra forma. Sua lógica é a mesma da versão unidimensional. Devemos fornecer todas as derivadas parciais possíveis. Se restringirmos a potência mais alta dos termos, podemos calcular combinatoriamente o número total de tais termos usando combinações e somas, mas não acho que valha a pena apresentar esses cálculos aqui, no nosso caso eles são inúteis. Em nosso algoritmo, usaremos apenas a limitação da mais alta potência, porque não queremos que nosso computador morra 🙂. 

Mas isso ainda não é suficiente para que nossa função de força bruta tenha uma aparência conveniente. É melhor removermos o primeiro termo C[0], pois é melhor que a função tenha simetria máxima com respeito aos valores negativos ou positivos com que iremos alimentá-la. Além disso, é melhor fazer com que, se a função fornecer um número positivo, o interpretemos como um sinal de compra e, se a função for negativa, como um sinal de venda. Um aumento no limite inferior do módulo deste sinal deve idealmente levar a um aumento na expectância e no fator de lucro, mas também levar inevitavelmente a uma diminuição no número de sinais. Quanto mais próxima nossa função estiver de nossos requisitos, melhor. As variáveis que passaremos serão (Close[i]-Open[i]) os valores de um candle em particular.

Tudo o que nos resta é simplesmente gerar variantes aleatoriamente desses coeficientes e verificar como uma ou outra variante se comporta no testador. Mas, por razões óbvias, ninguém no seu perfeito juízo irá classificar esses coeficientes manualmente, portanto, é necessário quer um EA sofisticado que seja capaz de produzir tais variantes e simultaneamente suportar milhares de variantes quer um software de terceiros que implementa parcialmente a funcionalidade do testador de estratégia. Inicialmente, tive que escrever um Expert Advisor em MQL4 e experimentá-lo primeiro, ele será anexado ao artigo junto com as instruções, todos podem usá-lo, talvez até mesmo modificá-lo. Mas vou usar outro aplicativo que desenvolvi em C#. Infelizmente, não vai estar disponível gratuitamente por motivos óbvios. Suas capacidades vão muito além da área de pesquisa. No entanto, vou falar sobre suas capacidades e demonstrar na prática, para que todo mundo que sabe programar, eu acho, possa repetir esse aplicativo, alguém até escreva melhor, se tiver um motivo. Mostrarei capturas de tela abaixo na parte do artigo onde analisaremos seus resultados.

Vou falar sobre suas principais características. A busca por matrizes de coeficientes é realizada em 2 etapas. Na primeira etapa são procuradas as matrizes na cotação carregada, matrizes essas que fornecem a expectância máxima ou o fator de lucro máximo no próximo candle, realizando execuções como no testador de estratégias. Em outras palavras, nela há uma tentativa de encontrar uma fórmula que preveja a direção da próxima barra com o máximo de precisão Vários dos melhores resultados são armazenados na memória e no disco como variantes da matriz. Podemos testar apenas uma parte da cotação carregada, especificando seu valor como uma porcentagem em relação ao arquivo de cotações carregado. Isso é necessário para eliminar resultados aleatórios que não sejam padrões de comportamento no segundo estágio. No segundo estágio, já são simuladas as ordens a mercado e é construída uma curva de equilíbrio, tudo isso feito de forma completa para toda a área carregada. Ao mesmo tempo, é realizado um aumento suave na magnitude do sinal, bem como uma busca por variantes de melhor qualidade. Também há muitos filtros necessários para obter gráficos cada vez mais suaves. Quanto mais uniforme o gráfico, melhor a fórmula que encontramos. Após a conclusão do segundo estágio da pesquisa, permanecerá determinado número das melhores variantes, que ainda podem ser vistas ao navegar pela lista usando os botões. Após selecionar a variante desejada, é possível gerar um robô para MetaTrader 4 e MetaTrader 5 na terceira aba. A geração ocorre de acordo com um modelo pré-compilado, no qual os números são simplesmente preenchidos em determinados locais.


Construindo o modelo mais simples para uma determinada tarefa

O modelo foi originalmente feito em MQL4, depois em MQL5. O código, como no artigo anterior, é adaptado ao máximo para ambas as plataformas. Eu faço isso de propósito para gastar menos tempo na adaptação. Para usar matrizes predefinidas como em MQL4, precisamos adicionar algum código ao Expert Advisor, e o que eu descrevi no último artigo. Nao me vou repetir. Vou escrever presumindo que você sabe como tudo isso é feito. E, para ser honesto, não há nada de complicado nisso, e qualquer desenvolvedor pode implementar isso facilmente, até melhor do que eu, se tiver necessidade. Vamos começar descrevendo as variáveis e matrizes que serão preenchidas automaticamente quando o robô for gerado, e para que servem.

double C1[] = { %%%CVALUES%%% };//массив для коэффициентов
int CNum=%%%CNUMVALUE%%%;//количество свечей в формуле
int DeepBruteX=%%%DEEPVALUE%%%;//глубина формулы
int DatetimeStart=%%%DATETIMESTART%%%;//стартовая точка времени
input bool bInvert=%%%INVERT%%%;//инверт торговли
input double DaysToTrade=%%%DAYS%%%;//дней можно торговать в будущее

Aqui C1 é apenas a matriz de coeficientes na frente das potências que selecionamos. CNum é o número de candles recentes no gráfico de preços que usaremos para calcular o valor do nosso polinômio. Em seguida, vem a profundidade da fórmula, ou, mais simplesmente, a potência máxima de nosso polinômio multidimensional. Costumo usar 1, porque, ao contrário da série de Taylor unidimensional, a multidimensional tem um aumento incrível na complexidade do cálculo com potência crescente devido ao fato de que o número total de coeficientes com potência crescente aumenta como uma avalanche. Precisamos do ponto de tempo inicial para limitar o tempo de operação de nosso EA e, para limitá-lo, precisamos saber a partir de que ponto no tempo contar o próprio tempo. Bem, a inversão é necessária, em primeiro lugar, para garantir que nosso polinômio funcione na direção certa. Afinal, se todos os sinais na frente dos coeficientes de potência forem invertidos, na verdade, o polinômio em si não mudará, senão que simplesmente todos os números que ele produz mudarão de sinal. O mais importante é a proporção entre os coeficientes. Se um valor negativo do polinômio indica uma venda e um valor positivo uma compra, o inverso é false. Caso contrário, é true. Assim, dizemos ao algoritmo "use os valores do polinômio, mas apenas o sinal invertido". Bem, o segundo ponto é melhor fazer essa variável como um valor de entrada, uma vez que podemos precisar da capacidade de inverter a negociação, bem como o número de dias que permitiremos que ela opere no futuro.

Se precisarmos calcular o tamanho de uma matriz com coeficientes, podemos fazer assim:

int NumCAll=0;//размер массива коэффициентов
void DeepN(int Nums,int deepC=1)//промежуточный фрактал
   {
   for ( int i=0; i<Nums; i++ )
      {
      if (deepC > 1)
         {       
         DeepN(Nums,deepC-1);
         } 
      else 
         {
         NumCAll++;
         }
      }   
   }

void CalcDeepN(int Nums,int deepC=1)//для запуска вычислений
   {
   NumCAll=0;
   for ( int i=0; i<deepC; i++ )
      {
      DeepN(Nums,i+1);
      }   
   }

Aqui, uma função chamada fractal intermediário conta o número de termos que têm a mesma potência total de todos os multiplicadores. Isso é feito para simplificar, porque não é tão importante para nós em que ordem somar nossos termos, mas apenas que seja implementado da forma mais simples possível na forma de código. A segunda função simplesmente chama a primeira no loop tantas vezes quanto houver tipos de termos. Por exemplo, se nossa expansão de série multidimensional é limitada a, digamos, o número 4, chamamos a primeira função com todos os números naturais de 1 a 4.

A função que já calculará o valor do próprio polinômio será quase idêntica, só que no nosso caso a matriz é gerada e seu tamanho não precisa ser definido, é assim que ficará:

double ValW;//число куда умножаем все(а потом его прибавляем к ValStart)
uint NumC;//текущее число для коэффициента
double ValStart;//число куда суммируем все
void Deep(double &Ci0[],int Nums,int deepC=1,double Val0=1.0)//вычислим сумму для одной степени
   {
   for ( int i=0; i<Nums; i++ )
      {
      if (deepC > 1)
         {
         ValW=(Close[i+1]-Open[i+1])*Val0;      
         Deep(Ci0,Nums,deepC-1,ValW);
         } 
      else 
         {
         ValStart+=Ci0[NumC]*(Close[i+1]-Open[i+1])*Val0/Point;
         NumC++;
         }
      }   
   }
   
void CalcDeep(double &Ci0[],int Nums,int deepC=1)//вычислим весь полином
   {
   NumC=0;
   ValStart=0.0;
   for ( int i=0; i<deepC; i++ )
      {
      Deep(Ci0,Nums,i+1);
      }   
   }

Tudo o que calcularmos será adicionado ao ValStart, ou seja, o resultado será adicionado a uma variável global. Precisamos também de mais uma variável global ValW, necessária para multiplicar o produto já existente por algum valor. No nosso caso, é o movimento da barra correspondente em pontos. O movimento pode ser tanto para cima como para baixo, o que é levado em consideração pelo sinal. Podemos ver que essas funções têm uma estrutura muito interessante. Elas se chamam a si mesmas internamente, além disso, o número e a estrutura dessas chamadas são sempre diferentes. Obtemos uma espécie de árvore de chamadas. Eu realmente gosto de usar essas funções porque são muito variáveis. No nosso caso, implementamos a série de Taylor multidimensional de uma forma simples e elegante.

Também podemos implementar uma função adicional no caso de usar apenas a versão unidimensional do polinômio. Nesse caso, toda a série é bastante simplificada e se transforma na soma dos coeficientes multiplicada pelo movimento de uma das barras à primeira potência. Seu número se torna idêntico ao número de barras usadas. Isso pode simplificar um pouco os cálculos. Se nossa potência for um, usaremos uma versão simplificada, e, se não, um método mais universal para qualquer potência.

double Val;
double PolinomTrade()//оптимизированный полином
   {
   Val=0;
   if ( DeepBruteX <= 1 )
      {
      for ( int i=0; i<ArraySize(C1); i++ )
         {
         Val+=C1[i]*(Close[i+1]-Open[i+1])/Point;
         }
      return Val;   
      }
   else
      {
      CalcDeep(C1,CNum,DeepBruteX);
      return ValStart;
      }      
   }

No caso de uma variante simples, adicionaremos o resultado a outra variável Val.

Agora vamos escrever o método principal que chamaremos quando aparecer uma nova barra:

void Trade()
   {
   double Value;
   Value=PolinomTrade();
   
   if ( Value > ValueCloseE)
      {
      if ( !bInvert )
         {
         CloseBuyF();
         }      
      else 
         {
         CloseSellF();
         }
      }
      
   if ( Value < -ValueCloseE)
      {
      if ( !bInvert )
         {
         CloseSellF();
         }      
      else 
         {
         CloseBuyF();
         }      
      }   
   
   if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToTrade && Value > ValueOpenE && Value <= ValueOpenEMax )
      {
      if ( !bInvert ) SellF();
      else BuyF();
      }
      
   if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToTrade && Value < -ValueOpenE && Value >= -ValueOpenEMax )
      {
      if ( !bInvert ) BuyF();
      else SellF();
      }

   }

Como você pode ver, a função é a mais simples possível, e tudo o que resta a você é implementar as funções de abertura e fechamento de posições que mais lhe convêm.

Você pode detectar o surgimento de uma barra assim:

void CalcTimer()
   {
   if ( Time[1] > PrevTimeAlpha )
       {
       if ( PrevTimeAlpha > 0 )
          {
          Trade();
          }
       PrevTimeAlpha=Time[1];
       }
   }

Acho que o código é o mais simples e compreensível possível para qualquer pessoa.

Os coeficientes que meu código gera são criados de acordo com os 4 modelos que expliquei acima. Para facilitar a leitura, esses coeficientes estão na faixa [-1,1], uma vez que as proporções do valores também são importantes para nós. A função que gera esses números a partir do protótipo do meu programa MQL5 fica assim:

   void GenerateC()
      {
      double RX;
      if ( DeepBrute > 1 ) CalcDeepN(CandlesE,DeepBrute);
      else NumCAll=CandlesE;
      for ( int j=0; j<VariantsE; j++ )
         {
         ArrayResize(Variants[j].Ci,NumCAll,0);
         Variants[j].CNum=CandlesE;
         Variants[j].ANum=NumCAll;
         Variants[j].DeepBruteX=DeepBrute;
         RX=MathRand()/32767.0;
         for ( int i=0; i<Variants[j].ANum; i++ )
            {
            if ( RE == RANDOM_TYPE_1 ) Variants[j].Ci[i]=double(MathRand())/32767.0;
            if ( RE == RANDOM_TYPE_2 )
               {
               if ( MathRand()/32767.0 >= 0.5  )
                  {
                  Variants[j].Ci[i]=double(MathRand())/32767.0;                  
                  }
               else
                  {
                  Variants[j].Ci[i]=double(-MathRand())/32767.0;                  
                  }
               }
            if ( RE == RANDOM_TYPE_3 )
               {
               if ( MathRand()/32767.0 >= RX  )
                  {
                  if ( MathRand()/32767.0 >= RX+(1.0-RX)/2.0  )
                     {
                     Variants[j].Ci[i]=double(MathRand())/32767.0;
                     ///Print(Variants[j].Ci[i]);
                     }
                  else
                     {
                     Variants[j].Ci[i]=double(-MathRand())/32767.0;                  
                     }        
                  }
               else
                  {
                  Variants[j].Ci[i]=0.0;                  
                  }
               }
            if ( RE == RANDOM_TYPE_4 )
               {
               if ( MathRand()/32767.0 >= RX  )
                  {
                  Variants[j].Ci[i]=double(MathRand())/32767.0;
                  }
               else
                  {
                  Variants[j].Ci[i]=0.0;                  
                  }
               }                              
            }
         }
      }

Um protótipo em MQL4 e MQL5 será anexado ao artigo. Não citei minhas implementações de funções de negociação, pois, além de desnecessárias, não são interessantes. Eu só queria mostrar como minha abordagem é implementada a nível de modelo. Se alguém se interessar em saber como é implementado o resto, pode vê-lo nos EAs anexados ao artigo. Todos os EAs e materiais serão anexados ao artigo. Na verdade, no meu modelo há muitas coisas desnecessárias, incluindo funções desnecessárias, às vezes pode-se otimizar, outras vezes até dá para remover variáveis desnecessárias. Pessoalmente, não me preocupo com isso. Eu elimino o que começa a interferir. O principal é que funciona, e isso basta para mim. Estou constantemente desenvolvendo algo e não tenho tempo para ver cada pormenor. Também não vejo razão para armazenar todos os procedimentos e variáveis em classes, exceto por uma questão de ordem e legibilidade. O modelo é muito simples e acho que não faz sentido querer complicá-lo. Os arquivos com cotações, que serão usados por nosso programa, serão gerados por um Expert Advisor especial, que simplesmente, ao percorrer o histórico, grava os dados da barra num arquivo de texto com uma estrutura que é fácil de ler por nosso programa. Provavelmente não vou dar seu código, para tornar esse EA mesmo fácil.


Usamos nosso programa para encontrar e analisar padrões

Como áreas de mercado a serem analisadas, escolhi 3 trechos de um mês de duração, que se sucedem. Par EURUSD, período M5.

  • A primeira área 2020/01.13 -2020. 02.16
  • Segundo trecho  2020.02.13 - 2020.03.15
  • Terceiro trecho 2020.03.13 - 2020.04.18

As áreas são escolhidas de forma que o último dia do trecho seja sempre sexta-feira. Já na sexta-feira, como se sabe, é o último dia de negociação da semana. Se pegarmos as áreas dessa forma, teremos 2 dias inteiros para pesquisar os padrões, até que a bolsa comece a operar novamente. Provavelmente este é um pequeno truque. É claro que, no nosso caso, isso não terá nenhum efeito, já que ainda verificaremos tudo no testador. Decidi destacar 12 variantes em relação aos padrões encontrados. Destas, 6 opções serão para polinômios com potência máxima de 1. 6 opções com um grau máximo de 2. Acho que isso será o suficiente.

Assim fica a primeira guia do meu programa:

Tem a capacidade de alterar o tipo de geração para nossos números, por exemplo, apenas positivo, positivo e negativo, positivo e zeros; positivo; negativos e zeros. Na segunda ComboBox são configurados os critérios de pesquisa. Existem 2 variantes: expectância em pontos e meu análogo da fórmula do fator de lucro. Somente na minha fórmula, esse valor varia de -1 a +1. Não pode haver casos em que o fator de lucro não possa ser calculado devido à divisão por zero.

P_Factor=(Profit-Loss)/(Profit+Loss).

Bem, então vem o grau máximo do polinômio e quantos núcleos de processador usar para o cálculo. Nas caixas de texto, definimos, respectivamente, o número de barras para nosso polinômio, ou o que é o mesmo, o mesmo número de dimensões, em seguida, também o coeficiente de assimetria de transações, que eu inventei e que é muito semelhante à fórmula anterior.

D_Asymmetry=|(BuyTrades-SellTrades)|/(BuyTrades+SellTrades).

Apenas seus valores estão na faixa de 0 a 1. Este filtro é necessário para poder exigir que uma variante contenha um número semelhante de sinais de compra e venda, a fim de evitar situações em que uma tendência global está acontecendo e todas as negociações estão na mesma direção. A seguir está o número das melhores variantes de entre as encontradas, que armazenamos na memória, e que porcentagem da cotação carregada usamos para força bruta. Este pedaço é contada da última barra até o tempo de abertura, e o pedaço que ficar atrás ficará para otimização. Não vou explicar o resto dos indicadores e a lista com variantes.

A segunda guia fica assim:


A guia corresponde à primeira variante para a primeira área desses robôs que apresentarei. Você pode comparar o gráfico do meu programa com o gráfico do testador. Tudo isso vai estar abaixo. As ordens da parte da cotação em que aplicada força bruta são desenhadas em amarelo, e você pode adivinhar que é desenhado em vermelho. Não vou dar capturas de tela de todas as variantes, para não sobrecarregar o artigo com imagens desnecessárias, elas serão anexadas ao artigo.

Agora vou descrever o que há nesta guia. Os Interval Points são a divisão do intervalo de valores do nosso polinômio. Acontece que quando fazemos força bruta na primeira guia, além dos parâmetros principais da variante, o valor máximo deste polinômio também é calculado de acordo com o módulo para que possamos conhecer a janela de valores do nosso polinômio e podermos tentar detectar sinais mais fortes, detalhando esta janela em partes iguais, aumentando gradativamente valor. Isso é o que a segunda guia faz. Também existe um tipo de pesquisa como na primeira guia e o número de melhores variantes que armazenamos na memória de otimização. Depois, há filtros que nos permitem filtrar opções desnecessárias que não se encaixam em nossa definição de um padrão. O Line Control inclui uma execução adicional para cada variante, na qual é calculado o desvio relativo da linha do gráfico a partir da linha reta que conecta o início e o fim do gráfico. 

Deviation = Max(|Profit[i]-LineProfit[i]|)/EndProfit.

Aqui Profit[i] é o valor da curva de equilíbrio na i-ésima ordem, LineProfit[i] é o mesmo valor, só que de nossa linha reta, EndProfit é o valor do lucro no final do gráfico.

Todas as quantidades são medidas em pontos. Use Percent Control é a porcentagem da parte vermelha do gráfico, mas neste caso não usei este filtro. Também existe um filtro para o número mínimo de ordens.

Assim fica a guia de geração de bots:


Tudo funciona de acordo com o princípio: na guia de otimização, escolhi a opção que gostei, mudei para esta e gerei um EA.

Vamos passar para a verificação dos robôs que gerei no testador de estratégia MetaTrader 4. Eu o escolhi porque podemos definir um spread de 1, praticamente nivelando sua influência na exibição do gráfico. Acontece que a maioria dos robôs encontrados desta forma terão um spread médio ligeiramente superior num par, e isso não nos permitirá analisar visualmente padrões com uma expectância pequena. Escovei e otimizei cada variante por cerca de 2 horas e, naturalmente, isso não é suficiente para encontrar um padrão de alta qualidade. Se for feito da maneira normal, precisaremos de um ou dois dias. Mas, neste caso, bastará assim, porque o objetivo deste artigo não é encontrar um padrão maravilha, senão analisar como esses padrões se comportarão no futuro. Acrescentarei que, se você testar meus robôs no testador, preste atenção à variável DaysToTrade. Por padrão, 3 dias. Portanto, não se surpreenda que quase não haja transações após a área de força bruta.

Primeiro, consideremos os robôs gerados com base num polinômio de primeiro grau.

Primeira área. 01/13/2020 - 2020. 16/02

Bot 1:

                                                              Área de força bruta

                                                              10 dias no futuro 

O primeiro gráfico mostra uma variante da captura de tela da segunda guia do meu programa. Enquanto o segundo gráfico é um teste do mesmo robô no futuro por 10 dias. Acho que 10 dias é o suficiente para ver tudo o que é necessário. Aqui vemos que o padrão continua por algum tempo e então muda abruptamente e segue na direção oposta. À primeira vista, está tudo bem - o padrão funciona o suficiente para lucrar alguns dias. Vamos ver o segundo robô da mesma segmento de mercado:

                                                              Área de força bruta

                                                             10 dias no futuro

Nem tudo é tão bom. Por quê? Parece que o padrão deve funcionar pelo menos um ou dois dias, mas desde os primeiros segundos começa a diminuir, embora a reversão seja bastante suave. Esses provavelmente não são os resultados que muitos esperavam 🙂. Mas tudo isso é muito fácil de explicar. Vou te mostrar como no final.

Passamos para a segunda área de teste. 13/02/2020 -2020.15/03

Vamos para o terceiro robô:

                                                              Área de força bruta

                                                             10 dias no futuro


A primeira imagem das três parece boa. O gráfico é muito plano e muito próximo de uma linha reta. Isso me diz pessoalmente que o padrão é muito estável e é mais provável que continue a se mover durante algum tempo. É assim que aconteceu. Mesmo se observarmos com atenção, veremos que mesmo no futuro esse movimento se assemelha a uma linha, o que significa que muitos parâmetros desse padrão continuaram funcionando.

Vejamos o quarto robô:

                                                              Área de força bruta

                                                             10 dias no futuro

Aqui há um movimento ascendente muito bom e, à primeira vista, tudo é bastante suave e estável, exceto pela forte alta no início do gráfico. Mas é só ele que deve ser notado, porque, na minha opinião, essa assimetria deve nos dizer imediatamente que isso é possível apenas uma coincidência ou um resultado acidental. No futuro, vemos imediatamente desde os primeiros segundos a inversão de todo o padrão na direção oposta, o que, em princípio, não me surpreende.

Passamos para a terceira área de teste. 2020.13/03 - 2020/04/18

Quinto robô:

                                                              Área de força bruta

                                                             10 dias no futuro

Aqui está quase a mesma imagem. A mesma assimetria aparente, certo tipo de onda no início, atenuação no final. Parece um padrão, mas muito discutível para mim pessoalmente. Eu não me atreveria a negociar com base na continuação. Bem, como esperado, uma reversão imediata do gráfico no futuro e uma inversão de toda a fórmula. Não vou mostrar o sexto robô, seu gráfico estará no arquivo. É quase idêntico a este gráfico.

Agora nos voltamos para os robôs baseados no polinômio de 2º grau.

Primeira área. 2020/13/01 - 2020. 16/02

Vejamos o sétimo robô:

                                                              Área de força bruta

                                                             10 dias no futuro

Uma característica marcante desses robôs é, como se pode supor, um melhor trabalho do robô na área onde usada força bruta. Tudo isso parece menos qualitativo na área anterior à área de força bruta. Ou seja, na área em que usamos força bruta é sempre observada uma meia-onda aguda e positiva. Enquanto na área restante há alguma confusão e, na melhor das hipóteses, um leve movimento para cima. No entanto, esses são apenas indicadores de uma fórmula específica, como você pode ver mais adiante, tudo se comporta de forma semelhante. Um pouco de aleatoriedade no início e depois um movimento na direção oposta.

Vejamos a oitava variante:

                                                              Área de força bruta

                                                             10 dias no futuro

Aqui as coisas estão muito piores. não há um padrão global, mas, como na versão anterior, há um movimento ascendente na área onde usada força bruta. Não há nenhuma regularidade global, por isso, como esperado, o gráfico cai imediatamente.

Passamos para a segunda área de teste. 13/02/2020 - 2020.15/03

Nono robô:

                                                              Área de força bruta

                                                             10 dias no futuro

Pessoalmente, eu no primeiro gráfico vejo o início de uma onda e no futuro, seu fim. Não há um padrão global, no entanto, a curva é suave o suficiente para ter tempo até para lucrar alguma coisa - migalhas, mas mesmo assim.

Vejamos 10 robôs:

                                                              Área de força bruta

                                                             10 dias no futuro

Aqui é melhor. Parece mais um padrão. O padrão continua por um ou dois dias no máximo. Mas, para ser honesto, não correria riscos com esse gráfico. O desvio da linha reta é muito pronunciado. 

Passamos para a terceira área de teste. 2020/03/13 - 2020.18/04

Décimo primeiro robô:

                                                               Área de força bruta


                                                              10 dias no futuro

A imagem não é muito bonita, mas mesmo assim dá para ver alguma semelhança com a linha reta. No futuro, o padrão continua, mas na minha opinião isso é mais sorte do que um resultado, muito ruído aleatório. Ou talvez até não sejam ruídos, mas ondas menores, não é claro. 

Décimo segundo robô:

                                                                Área de força bruta

                                                               10 dias no futuro

Um gráfico bastante feio, mas podemos ver o final pronunciado de uma onda e outra enorme a seguir. No futuro, essa enorme onda se desdobra lentamente e em algum ponto é finalmente invertida. Pareceu-me que a reversão da tendência ocorreu de forma mais suave em robôs com um polinômio superior ao 2º grau. Menos surpresas, como dizem. Acho que se passarmos mais tempo e testarmos o 3º grau, vai ser melhor. Em teoria, deveria ser assim, uma vez que a regularidade deveria ser melhor descrita pelo polinômio, que possui a maior potência total dos fatores nos termos.


Conclusões e verdades matemáticas de nossa pesquisa

Agora podemos resumir todos os resultados dos testes de nossos Expert Advisors. À primeira vista, isso é difícil de fazer, já que na área de força bruta e de otimização tudo parece estar subindo, enquanto nas áreas onde usada força bruta basicamente todo está uma confusão. Na verdade não é bem assim:

  • Em cada um dos testes no futuro, sempre há um ponto onde o gráfico se desdobra e a fórmula é invertida.
  • Embora possa ser suave ou instantânea, a reversão sempre estará.
  • Basicamente, a grande maioria dos gráficos irá para baixo no futuro.
  • Às vezes, o padrão continua por um tempo no início.
  • Em geral, para todos os testes futuros, é óbvio que o padrão funciona na direção oposta.
  • Se a curva de equilíbrio se desvia da linha reta, as chances de continuação são muito menores.
  • Na melhor das variantes encontradas, o padrão funciona durante alguns dias.

Agora tentarei explicar todos esses fatos. Vamos começar com o primeiro e mais importante fato, ao qual eu vim há muito tempo, a propósito, quando esses programas nem estavam em minha mente. Esta é uma verdade matemática bastante simples. Abaixo, desenhei um gráfico de equilíbrio de uma estratégia arbitrária. A linha preta com um padrão pequeno, e a linha roxa com um grande, e seu comportamento aproximado, se os negociarmos ao longo do histórico, e não apenas na área onde usada força bruta:

Deliberadamente não insiro cotações, pois em nosso caso a base analisada pode ser o gráfico de equilíbrio. Não devemos nos preocupar como esta cotação é desenhada e o que está nela. Seja como for, não veremos nela mais do que nossa estratégia.

Imagine se testássemos todos os nossos robôs desde o início do histórico e no futuro, mas embora não tenhamos cotações do futuro, ainda podemos supor que o teremos com 100% de precisão. Na sua opinião, absurdo? )). Não, não é um absurdo. Primeiro, vamos imaginar que estamos testando robôs com base em todo o histórico de cotações disponível. O que vamos ver? Algum tipo de confusão para cima e para baixo, depois para cima de novo, depois para baixo. E o que devemos ver nesta bagunça, você perguntará? E eu responderei: ondas. Não importa a forma que tenham, que comprimento de onda e amplitude. E certamente não é uma onda senoidal, mas não nos importamos. É importante apenas que esse processo seja periódico. Se também assumíssemos que a cotação é infinita, então poderíamos dizer, com base na pesquisa matemática do meu último artigo, que, tomando uma estratégia aleatória com uma fórmula aleatória, sua expectância ao longo do histórico tenderá a zero quando a quantidade de dados históricos tende a para infinito. O que obtemos com isso? E o que podemos dizer agora é que qualquer curva de equilíbrio com um número infinito de transações cruzará a linha de equilíbrio inicial um número infinito de vezes. E mesmo que, por algum motivo, o equilíbrio suba ou desça imediatamente e permaneça o tempo todo, então temos o direito de deslocar levemente essa linha para baixo ou para cima e ainda encontrar esse ponto de equilíbrio, próximo ao qual o equilíbrio flutua.

Não levaremos em consideração aquelas fórmulas que levam a um ganho ou perda pronunciado ao longo do histórico, embora essas variantes também possam ser atribuídas a esta categoria, simplesmente é que neste caso caímos na meia onda positiva ou negativa de uma enorme onda maior do que todo nosso histórico. Dentro de nossas suposições, descobri que os padrões que encontrei nada mais são do que as partes encontradas de meias-ondas positivas. Quanto maior a parte encontrada, mais provável é que o ponto de equilíbrio esteja muito abaixo. Com base nisso, como agora existe uma meia-onda positiva, em breve deve aparecer uma negativa. Na linguagem da matemática, a probabilidade de movimento na direção negativa é maior, quanto maior é a meia onda que captamos. E vice-versa, se pegarmos uma meia-onda negativa, quanto maior for essa meia-onda, mais aumenta a probabilidade de que uma meia-onda positiva comece. Pode ser mais simples: se temos uma estratégia com expectância zero ao longo da história, toda esse histórico consiste em segmentos com expectativas negativas e positivas, que se sucedem e se alternam constantemente. Tenho até um EA que implementa esse princípio e funciona para qualquer par de moedas com base em todo o histórico de cotações. Portanto, meus argumentos são apoiados não apenas pela pesquisa que fizemos aqui, mas também pelos Expert Advisors. Na verdade, este princípio não só pode ser ampliado, mas também infinitamente em camadas como uma boneca matryoshka, aumentando a eficiência do sistema. É claro, você pode jogar na continuação da tendência, mas eu o aconselho a fazer isso apenas se o padrão for muito bonito e uniforme, e então, não mais do que 5-10% do padrão encontrado no futuro. Os riscos são muito altos. Além disso, você estará jogando contra a matemática, o que é estúpido. Se fosse possível estimar até mesmo o tempo aproximado de operação restante desse padrão, isso poderia ser feito. Mas, em nosso caso, isso não pode ser feito, uma vez que a natureza do padrão não é clara. Mesmo que a natureza do padrão seja clara, mesmo assim, tal análise é extremamente difícil.


Como determinar o nível em relação ao qual ocorrem as flutuações?

Continuando com o tema das flutuações, tentarei responder ao leitor como definir o nível necessário para determinar as ondas e seu movimento em relação ao próprio nível. A resposta é extremamente simples 🙂. De jeito nenhum 🙂. Mas isso não significa de forma alguma que este nível não exista e que não possa nos ajudar a operar corretamente 🙂. É importante entender que este nível não é fixo, está apenas na nossa cabeça. É ainda mais importante entender o seguinte: quando o tamanho de uma meia onda tende para o infinito, a razão entre o tamanho dessa meia onda e a distância deste nível do equilíbrio inicial tende para o infinito. Ou seja, quanto mais forte for a meia onda que encontramos, menos podemos pensar onde este nível está localizado, pois de qualquer forma, com o aumento do número de transações, esse nível tende para o ponto zero. Tudo o que precisamos é encontrar as meias-ondas mais fortes. Outro facto que testemunha a favor é que quanto maior e mais perfeita for a meia onda, menos provável é que no resto do teste virtual haja uma onda com uma amplitude comparável à nossa 🙂. Vou tentar representar visualmente o que foi dito na imagem:

Por si só, este nível não garante 100% que o padrão vai virar e ir de forma inteligente, mas, mais importante, o próprio fato da existência de um padrão que faz parte da meia-onda nos diz que muito provavelmente haverá mais de uma dessas ondas, e até mesmo possível, estão presentes ao longo do histórico, se testarmos o Expert Advisor ao longo do histórico. Nesse caso, há uma chance bastante grande de que haja um grande movimento de recuo. É isso que você precisa entender. Mesmo quando testei vários Expert Advisors que não funcionavam em escala global, havia muitas áreas locais onde funcionava ou funcionava com uma inversão. Havia ondas claramente pronunciadas e o gráfico parecia um pouco confuso, estava claramente estruturado.


Para completar a imagem

Vou tentar dizer aproximadamente como, em minha opinião, negociar com essas ondas de forma mais eficaz. Deixe-me primeiro explicar algumas coisas:


A primeira variante é negociar com base na inversão do padrão e a segunda é continuar. Se considerarmos a primeira variante, o ideal é que sempre alcancemos um certo nível e paremos o ciclo de negociação aí, e então esperemos pelo próximo. No caso de usar um martingale parcial, haverá uma melhora, mas se soubermos que o gráfico logo se inverterá. Caso contrário, a expectância ainda será "0". Você pode jogar com base na continuação da tendência somente quando o padrão estiver próximo do ideal, e esse for o menor tempo possível. Na minha opinião, na segunda variante, podemos usar o martingale reverso. Na verdade, para ser honesto, todas as estratégias que testei falam de um fato matemático indiscutível: ao negociar um lote fixo, se não sabemos como o preço se comportará no futuro (e quase sempre não sabemos disso), então teremos " 0 ".

Mas ainda existem situações em que acidentalmente pegamos um padrão global que permanece funcional por muito longe, mas na minha opinião, é melhor não esperar por tais situações. É melhor escolher um esquema de negociação e segui-lo. Ainda não tive tempo de testá-lo mesmo em contas de demonstração, pois isso leva de 2 a 3 meses. Um mês têm quatro semanas, e cada fim de semana eu tenho que usar força bruta 2 dias :). Infelizmente, não tenho a oportunidade ainda de testar em algum computador operando 24 horas por dia. Tenho apenas um netbook, que ele está ocupado com outros robôs. Talvez no futuro, quando uma máquina mais poderosa aparecer, eu experimente numa conta demo e faça um sinal separado.


Fim do artigo

Neste artigo, tiramos conclusões simples, mas muito importantes, sobre os padrões e sua física quando aplicados ao mercado, a saber: o mercado não é caótico e tem dentro muitos padrões escondidos em diferentes períodos gráficos, acontece que ele se dispõe em camadas, criando a ilusão do caos. Os padrões de comportamento são um processo periódico que pode ser repetido e invertido. Uma vez que os padrões são repetidos, essas ondas podem ter uma limitação de amplitude, que podemos usar para nossas estratégias. Tentei deixar este artigo o mais claro possível e apresentar um mínimo de matemática para transmitir sua ideia principal. Espero que esta informação ajude alguém no desenvolvimento de seus próprios sistemas de negociação. Alguém pode ver mais nestes resultados, se sim, comente ativamente. Infelizmente, não fui capaz de aplicar força bruta em intervalos de tempo maiores devido ao fato de que tudo leva muito tempo, mesmo usando meu software. Se houver interesse ou necessidade de uma análise mais profunda, estou pronto para continuar este tópico, mas dentro de outro artigo. Este artigo é um artigo introdutório e de demonstração.


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

Arquivos anexados |
results.zip (2075.33 KB)
Otimização Walk Forward contínua (Parte 8): Melhorias e correções do programa Otimização Walk Forward contínua (Parte 8): Melhorias e correções do programa
O programa foi modificado com base nos comentários e solicitações dos usuários e leitores desta série de artigos. Este artigo contém uma nova versão do otimizador automático. Esta versão implementa os recursos solicitados e fornece outras melhorias, que eu descobri ao trabalhar com o programa.
Uma abordagem científica para o desenvolvimento de algoritmos de negociação Uma abordagem científica para o desenvolvimento de algoritmos de negociação
O artigo considera a metodologia para o desenvolvimento de algoritmos de negociação, na qual uma abordagem científica consistente é usada para analisar os possíveis padrões de preços e para construir algoritmos de negociação com base nesses padrões. Os ideais de desenvolvimento são demonstrados por meio de exemplos.
Trabalhando com séries temporais na biblioteca DoEasy (Parte 53): classe do indicador base abstrato Trabalhando com séries temporais na biblioteca DoEasy (Parte 53): classe do indicador base abstrato
Neste artigo, veremos a criação de uma classe de indicador abstrato que será posteriormente usada como uma classe base para a criação de objetos de indicadores padrão e personalizados da biblioteca.
Negociação Forex e sua matemática básica Negociação Forex e sua matemática básica
O objetivo do artigo consiste em descrever as principais características da negociação forex da forma mais simples e rápida possível, compartilhando verdades simples com iniciantes. Aqui tentaremos responder às perguntas mais interessantes no ambiente de negociação, bem como escrever um indicador simples.