O show deve continuar, ou Mais uma vez sobre o ZigZag

Rider | 5 fevereiro, 2016


Introdução

Uma grande quantidade de versões do ZigZag prova a existência de um interesse constante e persistente nesse indicador. Um interesse que vale muito à pena. Talvez seja o único indicador que diretamente afeta as emoções do trader forçando ações imediatas com sua representação gráfica brilhante e vívida dos principais movimentos de mercado. Aparentemente, essa pode ser a única explicação do fenômeno, com o entendimento detalhado da maioria que esse indicador não pretende produzir sinais de trading direto, não obstante, ele tenta continuamente aproximá-los ao máximo da situação de mercado atual. Observar a reelaboração infinita da última fenda de previsão é uma vantagem duvidosa, especialmente ao perceber isso.

Vamos discursar um pouco. Acredite ou não, mas esse é o começo de tudo.

Letras

Todos nós que viemos e FICAMOS nesse mercado, mais cedo ou mais entendemos que ele não é tão simples como pensamos no início. Assim que percebemos isso, começamos a ler (aqueles que conseguem, claro). Mas, de alguma forma, percebemos o que lemos de forma muito peculiar. Resumindo, escolhemos as coisas mais simples e não as certas. Correspondentemente, adicionamos ao nosso arsenal somente as coisas que ficam superficiais, que são óbvias para o entendimento e podem ser facilmente (rapidamente) traduzidas em uma linguagem de algoritmo. Existem muitos exemplos disso. Aqui está um deles, talvez não seja o melhor, mas vamos usá-lo da forma que é.

Todos sabem e se lembram da tese básica de TA que ainda não foi desmentida, mas também ainda não foi estritamente comprovada, entretanto:

O preço é tudo para nós!

Levando isso ao pé da letra e de cabeça erguida, começamos a buscar não os pontos de controle do mercado, mas, com todos os meios, por seus máximos e mínimos - isso é mais fácil e mais claro. Elaboramos níveis de suporte/resistência com uma régua, contagem de nível Fibo deles, contagem de ciclos em barras, etc. Do ruim para o pior - começamos a selecionar a partir do sistema de trading de uma pessoa - somente as coisas que são acessíveis para nosso entendimento, sem prestar atenção aos avisos ou para qual mercado ele está e o tempo de criação daquele sistema... O que é pior, começamos a simplificar... simplificar Gann (!!!), que, como eu penso, encontrou (ou estava perto disso) a solução do problema preço/tempo... delírio... como é possível simplificar algo que ainda não foi totalmente entendido por ninguém?

E então percebemos que tudo funciona de forma errada de alguma forma, um pouco melhor do que meio a meio, começamos a gritar que os Gurus estão mentindo, seus métodos não funcionam, os livros são escritos somente para obter dinheiro, a seção golden não existe... etc. E esse é o resultado do nosso esquecimento que não é o sinônimo do minimáximo quando olhamos para um extremo, mas é realmente um máximo ou um mínimo, apenas outra exibição da sua essência interna que ainda precisamos entender. Então, com base nessa vista sombria, tentamos prever o futuro até mesmo tentando não analisar o que fez esse ponto ser o que é exatamente nesse lugar exatamente nesse momento.

Por mais que ganhar dinheiro seja nosso desejo, não percebemos que às vezes precisamos parar e pensar um pouco. É útil, como se sabe. Você começa a perceber claramente a certeza de que o "homem é a única criatura viva que faz a mesma coisa várias vezes esperando resultados diferentes". Não estamos cortando madeira, certo? :)

Estou muito entusiasmado. Talvez porque se trata de mim também. É hora de terminar. Vamos apenas entender que nem todos os gurus são Gurus e os que são nunca irão lhe dizer tudo... ainda mais quando eles sabem que algo dado de mão beijada não é bom e nem ajuda muito.

De volta ao assunto.

Multiframe Fractal ZigZag


Precisei de um indicador que não representasse apenas os mínimos e os máximos, mas os pontos extremos de mercado
logicamente racionalizados pelo movimento de preço (LREP - talvez essa abreviação possa se tornar natural?) e, se possível, confirmada. O problema em torná-lo um gerador de sinais de indicador não foi declarado inicialmente, Em primeiro lugar, tentei usar o ZigZag padrão (não o que está embutido no MT4, mas o "padrão" de forma geral), mas algo me colocou em alerta e me forçou a recusar essa ideia:

Como se refere a mim pessoalmente, a última é a mais inaceitável. Não é atrativo para mim. Nada em particular.

Lembro de Demark com seus pontos de TD e Williams com seus "fractais" (não sei quem pegou emprestado de quem, mas eles são farinha do mesmo saco). Parecia ser “o único” - pelo menos foi racionalizado com os movimentos de preço anteriores e subsequentes. Talvez, nem todo mundo irá gostar desse tipo de abordagem à escolha desses pontos. Entretanto, ninguém inventou nada mais preciso e apropriado para sua identificação inicial ainda, exceto, claro, por termos como "extremo valioso", "máximo local", etc.

As coisas próprias talvez não sejam as melhores, mas certamente são as que tem mais "parentesco". É por isso que não procurei por um código emprestado e escrevi meu próprio indicador fractal simples, mais preciso, sua semelhança: as regras da escolha dos pontos diferem superficialmente das regras padrão. Tentei solicitá-lo com timeframes diferentes usando iCustom, mas então entendi que seria mais racional calcular tudo no timeframe (TF) atual (em funcionamento). Mais adiante, a lógica de programação solicitou tudo sozinha: e sobre a construção de módulo que sugere a si próprio no futuro e sobre o TF subpadrão. Aqui está o resultado do que aconteceu.

Os quadros mostram sua aparição com os parâmetros de TF 1140, 360, 60. O gráfico H1 é escolhido, por sua demonstrabilidade, para fazer você ver que a linha preta (60) não toma todos os pontos fractais e rejeita alguns deles. O primeiro quadro é para ver a ponta e o segundo é simplesmente tirado do meio do gráfico.

O esquema de cor não é o melhor, a imagem em anexo mostra minha visão disso.

 

Código


Vamos ver como é implementado - não deveríamos atuar em show de comédia? ;)
O indicador forma
no timeframe (TF) atual em funcionamento uma sequência de nós de ZigZag que são calculados nos três maiores TFs emulados. Funciona em todos e com todos os TFs, incluindo os subpadrão com as seguintes restrições implementadas no código:
- o TF maior deve ser divisível pelo que está em funcionamento;
- o TF em funcionamento não é maior do que o menor TF dentre os maiores;
- os períodos nos parâmetros são especificados em minutos e devem ser configurados em ordem decrescente;
- o período do maior TF não excede 43.200 (um mês) - esse não é o limite, um maior é possível;
A característica é que somente um buffer é usado para cada TF. não há necessidade de usar dois deles, pois na combinação racional de TFs a probabilidade de
aparição de dois extremos diferentemente diretos em uma única barra do TF em funcionamento é pequena demais.

Aqui está esse fragmento:

//-----------------------------------------------------------------------
// MF_Fractal_ZZ_3in1.mq4
//-----------------------------------------------------------------------
#property copyright "Copyright © 2008, BiViSi Corp."
#property link      "riderfin@bk.ru"
#property link      "ICQ 499949112"

#property indicator_chart_window    
#property indicator_buffers 3
//---- style of the indicator line
#property indicator_color1 Blue 
#property indicator_color2 Red
#property indicator_color3 Yellow        
#property indicator_style1 0
#property indicator_style2 0
#property indicator_style3 0
#property indicator_width1 5
#property indicator_width2 3
#property indicator_width3 1
//---- INOUT PARAMETERS OF THE INDICATOR
extern int VolExt=50; // VolExt+1" calculation of the last control points
extern int TFLarge=1440;
extern int TFMidle=240;
extern int TFSmall=60;
//---- Variables 
double Large[],Midle[],Small[];  // control points (indicator bufers)
datetime PrevTimePer[4];         // the time of the last calculation of every TF
datetime PrevTimeCalc=0; 
double P60,CP60;
int CurPeriod, ErrorTF=0, NumberExt, Per,  largelast=0, midlelast=0, smalllast=0;
//-----------------------------------------------------------------------
int init() 
{
   // initialization
   IndicatorBuffers(3); // for perspective" entry :)
   SetIndexBuffer(0,Large); SetIndexStyle(0,DRAW_SECTION);
   SetIndexEmptyValue(0,0.0);
   SetIndexBuffer(1,Midle); SetIndexStyle(1,DRAW_SECTION);
   SetIndexEmptyValue(1,0.0); 
   SetIndexBuffer(2,Small); SetIndexStyle(2,DRAW_SECTION);
   SetIndexEmptyValue(2,0.0);
   ArrayInitialize(PrevTimePer,0);
   CurPeriod=Period(); CP60=CurPeriod*60;
   // restrictions:
   // control of TF and inputted parameters
   if (MathCeil(TFSmall/CurPeriod) != TFSmall/CurPeriod) 
      TFSmall=MathCeil(TFSmall/CurPeriod)*CurPeriod;
   if (MathCeil(TFMidle/CurPeriod) != TFMidle/CurPeriod)
      TFMidle=MathCeil(TFMidle/CurPeriod)*CurPeriod;
   if (MathCeil(TFLarge/CurPeriod) != TFLarge/CurPeriod)
       TFLarge=MathCeil(TFLarge/CurPeriod)*CurPeriod;
   if (CurPeriod > TFSmall) 
      {Alert ("The chart period must be less than or equal to ", TFSmall," min.");
       ErrorTF=1;return;}
   if (TFSmall >= TFMidle || TFMidle >= TFLarge || TFLarge>43200)
      {Alert ("Incorrect choice of timeframes for calulation!!!"); ErrorTF=1;return;}
   return;              
}
//--------------------------------------------------------------------

O próximo bloco é responsável pela busca de pontos de controle, cálculo de nós ZigZag e escrita deles nos arranjos de buffer do indicador.

A recusa em usar as construções do tipo int IC=IndicatorCounted(); e outras depende do próprio algoritmo de cálculo que fornece a velocidade suficiente sem ele (veja Force #1-3 no código), também pode ser aumentado a propósito, um pouco antes disso

//--------------------------------------------------------------------
int start()
{
   if ( ErrorTF==1 ) return; // incorrect timeframe
   FractalCalc(); 
   return;
}
//======================================================================
// Searching of the 5 bar fractals and zigzag nodes claculation
// on the emulation of the larger TF, deleting of the odd ones and representation on the current TF
//======================================================================
void FractalCalc ()
{   
   // Force  №1 - caculation only on the fully formed bar of the working TF
   if (PrevTimeCalc == Time[0]) return; else PrevTimeCalc=Time[0];
   int y, x, k, i, j, extr=0; 
   // the time of the last bar of the current TF, that closes bar №1-5 of the larger TF
   int t1, t2, t3, t4, t5;                     
   // the number of the last bar of the cur. TF, that closes bar №1-5 of the larger TF
   int limit1, limit2, limit3, limit4, limit5; 
   // the numver of bars of the cur. TF with the peaks and bases that correspond with the bars 1-5 of the larger TF
   int up1,up2,up3,up4,up5,dn1,dn2,dn3,dn4,dn5;
      
   for (y=1; y<=3; y++) // cycle of the calculated TF
      {
      if (y==1) Per=TFLarge; if (y==2) Per=TFMidle; if (y==3) Per=TFSmall;
      P60=Per*60;
      // Force №2 - calculate the fissures only with the forming of the bar of the larger TF
      if (PrevTimePer[y] !=0)
         { 
         if (Per<43200 && (Time[0] - PrevTimePer[y])<P60 )continue;
         if (Per==43200 && Month()==TimeMonth(PrevTimePer[y]))continue;
         }
      // Processing of bars absence
      // If linearly PrevTimePer[y]=Time[0], then in case of bar absence
      // the shift of whole calculation chain by the absence size will take place on the working TF
      PrevTimePer[y]=MathCeil(Time[0]/Per/60)*P60; 
      
      NumberExt=0;  extr=0;
      k=Per/CurPeriod;
      // limitation of cycle depending on currently calculated TF
      // and on the last fractal
      i=MathCeil(Bars/k)-5;
      // Force #3 - calculation starting with the last fissure
      if(y==1 && largelast !=0) i=largelast+k;
      if(y==2 && midlelast !=0) i=midlelast+k;
      if(y==3 && smalllast !=0) i=smalllast+k;
      for (x=1; x<=i; x++) 
         {
         // find peaks and bases
         // the time of the beginning of the las bar of the cur. TF, that closes bar №1 of the larger TF
         if (PrevTimePer[y] !=0) t1=PrevTimePer[y]-x*P60+(k-1)*CP60;
         else t1=MathCeil(Time[0]/Per/60)*P60-x*P60+(k-1)*CP60;
         t2=t1-P60; t3=t2-P60; t4=t3-P60; t5=t4-P60;
         limit1=iBarShift(NULL,0,t1, false); limit2=iBarShift(NULL,0,t2, false);
         limit3=iBarShift(NULL,0,t3, false); limit4=iBarShift(NULL,0,t4, false);
         limit5=iBarShift(NULL,0,t5, false);         
         up1=iHighest(NULL,0,MODE_HIGH,k,limit1); up2=iHighest(NULL,0,MODE_HIGH,k,limit2);
         up3=iHighest(NULL,0,MODE_HIGH,k,limit3); up4=iHighest(NULL,0,MODE_HIGH,k,limit4);
         up5=iHighest(NULL,0,MODE_HIGH,k,limit5);
         dn1=iLowest(NULL,0,MODE_LOW,k,limit1); dn2=iLowest(NULL,0,MODE_LOW,k,limit2);
         dn3=iLowest(NULL,0,MODE_LOW,k,limit3); dn4=iLowest(NULL,0,MODE_LOW,k,limit4);
         dn5=iLowest(NULL,0,MODE_LOW,k,limit5);

         // searching for control points
         if(High[up3]>High[up2] && High[up3]>High[up1] && High[up3]>=High[up4] && High[up3]>=High[up5])
            {
            if (y==1){Large[up3]=High[up3];largelast=up3;}
            if (y==2){Midle[up3]=High[up3];midlelast=up3;}
            if (y==3){Small[up3]=High[up3];smalllast=up3;}
            NumberExt++;  extr++;
            }
         if(Low[dn3]<Low[dn2] && Low[dn3]<Low[dn1] && Low[dn3]<=Low[dn4] && Low[dn3]<=Low[dn5])
            {
            if (y==1){Large[dn3]=Low[dn3];largelast=dn3;}
            if (y==2){Midle[dn3]=Low[dn3];midlelast=dn3;}
            if (y==3){Small[dn3]=Low[dn3];smalllast=dn3;}
            NumberExt++; extr++;
            }
         if (NumberExt>VolExt) break;   
         } 
      }

Como você vê, a maioria do código se volta para a otimização dos cálculos e para a proteção contra uma possível situação inesperada - conexão com o servidor perdida e barras perdidas no histórico.

O próximo bloco realiza um tipo de "casamento": a eliminação de extremos ímpares (quando várias bases aparecem entre dois picos e vice versa) para formar um ZigZag correto: os máximos e mínimos são escolhidos a partir de 2 ou mais picos/bases vindos em sucessão e, no caso de igualdade, o de número menor é escolhido...os ímpares são configurados a zero. O algoritmo fornece espaço para argumento, claro, e alternativas são possíveis, mas decidi fazer assim por enquanto.
O código contém entradas comentadas que permitem que você se livre de desacordos em picos/bases que aparecem em TFs diferentes, mas é necessário observar mais uma vez, não funciona corretamente na forma atual. Suspendi essa ideia temporariamente, mas você pode fazer se quiser.
O mesmo fragmento tem outra possibilidade de aceleração do seu trabalho se a construção if (NumberExt>VolExt) break; do desvio previamente calculado
for usado, mas também suspendi essa ideia por enquanto.
E, uma vez que começamos a falar sobre a velocidade, o método mais fácil e óbvio é diminuir o VolExt nas entradas do indicador; quase ninguém precisa de mais do que 10 a 15 deles para trading nem para análises, exceto para verdadeiros comilões :).

 

if (extr==0) return;
   for (y=1; y<=3; y++)
      {
      if (y==1) j=ArraySize(Large);if (y==2)j=ArraySize(Midle);
      if (y==3)j=ArraySize(Small);      
      int min=0, max=0, extmin=0, extmax=0;
      NumberExt=0;      
      for (x=1;x<=j;x++)
         {
         if (y==1)
            {
            if (Large[x] == 0.0 ) continue;
            if (Large[x] == High[x])
               {
               NumberExt++; extmax++; extmin=0;
               if (extmax==2)
                  {
                  if (Large[max]>=Large[x])Large[x]=0.0;
                  else {Large[max]=0.0;max=x;}
                  extmax--;  
                  }
               else max=x;
               }
            if (Large[x] == Low[x])
               {
               NumberExt++; extmax=0; extmin++;
               if (extmin==2)
                  {
                  if (Large[min]<=Large[x])Large[x]=0.0;
                  else {Large[min]=0.0;min=x;}
                  extmin--;  
                  }
               else min=x;
               }
            }         
         if (y==2)
            {
            if (Midle[x] == 0.0 ) continue;
            if (Midle[x] == High[x])
               {
               NumberExt++; extmax++; extmin=0;
               if (extmax==2)
                  {
                  if (Midle[max]>=Midle[x])Midle[x]=0.0;
                  else {Midle[max]=0.0;max=x;}
                  extmax--;  
                  // it can be easier as the above one, but.... remove disagreements
                  /*
                  if (Midle[max]>Midle[x])Midle[x]=0.0; 
                  if (Midle[max]==Midle[x])
                     {
                     if (Large[x] == High[x]) {Midle[max]=0.0;max=x;}
                     else Midle[x]=0.0; 
                     }
                  if (Midle[max]<Midle[x]){Midle[max]=0.0;max=x;}
                  */
                  }
               else max=x;
               }
            if (Midle[x] == Low[x])
               {
               NumberExt++; extmax=0; extmin++;
               if (extmin==2)
                  {
                  if (Midle[min]<=Midle[x])Midle[x]=0.0;
                  else {Midle[min]=0.0;min=x;}
                  extmin--;  
                  // it can be easier as the above one, but.... remove disagreements
                  /*
                  if (Midle[min]<Midle[x])Midle[x]=0.0; 
                  if (Midle[min]==Midle[x])
                     {
                     if (Large[x] == Low[x]) {Midle[min]=0.0;min=x;}
                     else Midle[x]=0.0; 
                     }
                  if (Midle[min]>Midle[x]){Midle[min]=0.0;min=x;}
                  */
                  }
               else min=x;
               }
            }         
         if (y==3)
            {
            if (Small[x] == 0.0 ) continue;
            if (Small[x] == High[x])
               {
               NumberExt++; extmax++; extmin=0;
               if (extmax==2)
                  {
                  if (Small[max]>=Small[x])Small[x]=0.0;
                  else {Small[max]=0.0;max=x;}
                  extmax--;  
                  // it can be easier as the above one, but.... remove disagreements
                  /*
                  if (Small[max]>Small[x])Small[x]=0.0; 
                  if (Small[max]==Small[x])
                     {
                     if (Midle[x] == High[x]) {Small[max]=0.0;max=x;}
                     else Small[x]=0.0; 
                     }
                  if (Small[max]<Small[x]){Small[max]=0.0;max=x;}
                  */
                  }
               else max=x;
               }
            if (Small[x] == Low[x])
               {
               NumberExt++; extmax=0; extmin++;
               if (extmin==2)
                  {
                  if (Small[min]<=Small[x])Small[x]=0.0;
                  else {Small[min]=0.0;min=x;}
                  extmin--;  
                  // it can be easier as the above one, but.... remove diagreements
                  /*
                  if (Small[min]<Small[x])Small[x]=0.0; 
                  if (Small[min]==Small[x])
                     {
                     if (Midle[x] == Low[x]) {Small[min]=0.0;min=x;}
                     else Small[x]=0.0; 
                     }
                  if (Small[min]>Small[x]){Small[min]=0.0;max=x;}
                  */
                  }
               else min=x;
               }
            }         
         if (NumberExt>VolExt) break;
         }
      }         

}

Conclusão

Como resultado, obtivemos um indicador com um algoritmo que talvez seja óbvio, mas nunca usado por ninguém antes, proveniente de algumas desvantagens do ZigZag padrão e tendo as seguintes vantagens:

Você pode me perguntar por que eu não deveria fazer essa correção e de uma vez. O que obtive até o momento é suficiente para mim...suficiente POR ENQUANTO. :)
E sei que um código eficiente finalizado pode ser aprimorado infinitamente. É como apontar um lápis...você pode esquecer que você queria desenhar com ele. Gostaria de começar a desenhar. Quero muito! :)

Qual será o próximo passo e o que devemos fazer com ele? Não existe uma solução pronta. É somente uma ferramenta e o que deve ser feito com ela, um Pinóquio ou um banquinho, cada Gepeto irá decidir sozinho.
Só posso dizer que o resíduo seco contém 5 buffers de indicador livres, a possibilidade de adicionar alguns módulos facilmente e uma resposta bastante adequada à solicitação através de iCustom().... Fantasie!

A última coisa legal é CONHECIMENTO para:

ANG3110 - para a coisa mais valiosa - as ideias que, propriamente, me levaram a escrever esse indicador;
Korey
- por seu apoio e ajuda no diagnóstico de TimeCurrent e otimização de cálculos;
Diretores
, que, quer queira quer não, conscientemente ou não, mas às vezes expressam suas ideias que eu quero pensar a respeito.