A contribuição de Thomas Demark para a análise técnica

Ivan Morozov | 10 outubro, 2016


Introdução

Acredita-se que a análise técnica é uma ciência e uma arte. A razão para esta dualidade é a individualidade dos pontos de vista que têm os diferentes traders e analistas. Por exemplo, a mesma linha de tendência pode ser traçada de maneiras completamente diferentes. Esta incerteza não é bem-vinda na prática, pois a negociação de bolsa requer sobre tudo precisão. Este problema tem sido enfrentado por todos os traders que alguma vez tentaram construir uma linha de tendência e descobriram que pode ser feito de várias maneiras. Esta confusão faz com que seja difícil criar sistemas de negociação exatos que analisem os mercados de acordo com as linhas de tendência. Além disso, há uma série de problemas causados pela multiplicidade: as discrepâncias ao procurar extremos locais, divergência e convergência devidas a uma linha de tendência construída impropriamente e problemas semelhantes.

Nem todos estão dispostos a aceitar uma abordagem tão frouxa na análise técnica. Por exemplo, Thomas Demark conseguiu tanto realizar uma abordagem analítica para este problema, quanto sugerir maneiras de solucioná-lo. Em seu livro "A nova ciência da análise técnica" descreveu os seus métodos para uma análise mais precisa da situação atual dos preços. Neste artigo vou falar sobre duas invenções, nomeadamente, sobre os pontos TD e as linhas TD. Claro, no livro descreve-se não apenas estes métodos: nele, Demark também escreve sobre a periodicidade dos mercados, ondas de Elliott e muito mais.

Além disso, no artigo será apresentado e explicado o processo de criação de três indicadores e dois EAs, com base nas ideias de Thomas Demark. Tenho certeza que será de interesse para um amplo círculo de traders, principalmente para iniciantes em Forex.


1. Pontos TD

A primeira invenção de Thomas Demark simplifica a descoberta dos extremos de preço para construir as linhas de tendência. Ele decidiu encontrar esse tipo de velas num gráfico diário, cujos preços máximos fossem superiores aos máximos do dia anterior e os subsequentes a partir de um definido (eu vou usar essa palavra para me referir a velas que irão determinar a presença de pontos TD). Se esta condição for atendida, no gráfico será possível construir um ponto TD de acordo com o preço máximo de uma vela definida. Nesse sentido, se o mínimo de um dia particular for inferior ao mínimo do dia anterior e posterior, será possível construir um ponto TD em conformidade com o preço mínimo de uma vela.


Fig. 1. Pontos TD em alta e baixa

Na primeira ilustração acima é mostrado um ponto TD de nível 1 (ele é marcado com um círculo vermelho). Como você pode ver, o máximo da vela definida é superior ao máximo anterior e próximo. Na figura, os valores máximos são exibidos na forma de linhas horizontais de cor cinza. Na segunda figura, é mostrado um evento similar, mas com um ponto TD em baixa. As regras são respeitadas da mesma maneira: o mínimo da vela definida é inferior ao mínimo anterior e próximo.

Acima foram considerados apenas os pontos TD do nível 1. Isto significa que se compara o preço da vela determinada apenas com uma anterior e ela com uma outra posterior. Se for necessário construir um ponto TD de nível 2, será preciso comparar o preço máximo da vela determinada com os dois anteriores e posteriores. Tudo acontece da mesma forma para os preços mínimos.


Fig. 2. Exemplo ponto de nível 2

A imagem acima mostra um ponto TD de nível 2. É possível notar que o preço máximo da vela definida é superior aos dois preços máximos das velas anteriores e as duas posteriores. 


Fig. 3. Ponto TD de nível 40.

O nível dos pontos TD pode ser superior a 2 dependendo do número de valores máximos e mínimos, com os quais seja necessário comparar vela definida. Ao fazer isso, é lógico que o ponto TD, por exemplo, do nível 3, seja simultaneamente ponto dos níveis inferiores, isto é, primeiro e segundo. Thomas Demark em seu livro descreve os pontos até o nível 3.

É interessante notar que existe há muito tempo um indicador que opera com base num princípio semelhante. Com efeito, os fractais Bill Williams são outra coisa senão pontos TD de nível 2. Deixe-me lembrá-lo, eles são criados, se pelo menos 2 velas até à definida e 2 após ela terem valores máximos inferiores ou mínimos superiores, o que repete a definição dos pontos TD de nível 2.


2. Linhas TD

Em si mesmos, os pontos TD só são extremos. Precisamos de dois pontos (dois máximos e dois mínimos) para construir a linha de tendência. Ao mesmo tempo, Thomas Demark utilizou apenas os dois últimos pontos como os mais importantes.

Linha TD de nível 1Linha TD de nível 2

Fig. 4. Linhas TD de níveis 1 e 2

Na imagem à esquerda, foram construídas duas linhas TD de nível 3 (a linha azul indica os mínimos e a verde, os máximos). Nível de linha indica o nível dos pontos em que esta linha foi construída. Na imagem à direita foi construída a mesmas linhas TD, mas de nível 3.


3. Criação de indicadores

3.1. iTDDots

Seria tedioso construir manualmente novos pontos e linhas para cada nova vela. Eu acredito que se algo pode ser automatizado sem perda de qualidade, é necessário fazê-lo. Abaixo vou falar sobre o processo de criação de um indicador que constrói pontos TD. Neste exemplo, estamos trabalhando com linguagem MQL4.

Inicialmente, foi desenvolvido um plano do indicador. Sua operação não leva em conta a vela atual, desde que seus altos e baixos podem variar, e isso implica uma compilação incorreta dos pontos. Assim, são tratadas apenas as velas anteriores.

Plano de trabalho do indicador:

  • Ao instalá-lo no gráfico, é preciso construir todos os pontos TD no histórico existente. Seu nível é definido pelo usuário.
  • A cada nova vela é necessário verificar se existe a possibilidade de construir um novo ponto e, no caso de sua ocorrência, teremos de construí-lo.
A primeira tarefa do indicador é definir a vela cujo extremo é superior aos extremos de n velas vizinhas. Para este efeito, eu sugiro uma função que irá determinar os preços máximos e mínimos das velas na faixa necessária para mim. Para determinar quantas velas eu preciso verificar a cada vez, é necessário multiplicar o nível dos pontos por 2 e adicionar 1.


Fig. 5. Velas usadas no indicador

 A imagem acima mostra um exemplo de como são enumeradas as velas, calculadas pelo indicador. Graças a ela, o método para calcular sua quantidade também é mais claro.

Agora eu vou mostrar como o código foi desenvolvido.

O indicador deve ter dois buffers para exibir pontos, pois uma vela pode ser simultaneamente um ponto TD construído de acordo com um máximo e um ponto TD, de acordo com um mínimo. Portanto, o início do programa tem a seguinte aparência:

#property indicator_chart_window        //Para exibição do indicador na janela do gráfico
#property indicator_buffers 2           //Usamos 2 buffers
#property indicator_plots   2           //Dois buffers serão exibidas
#property indicator_color1 clrGreen     //Cor padrão do primeiro buffer
#property indicator_type1   DRAW_ARROW  //Tipo de desenho do primeiro buffer
#property indicator_width1 2            //Espessura padrão da linha de exibição do primeiro buffer
#property indicator_color2 clrBlue      //Cor padrão do primeiro buffer
#property indicator_type2   DRAW_ARROW  //Tipo de desenho do primeiro buffer
#property indicator_width2 2            //Espessura padrão da linha de exibição do segundo buffer

A seguir, ao instalá-lo no gráfico, o usuário deve determinar para o indicador o nível de construção dos pontos de TD:

input int Level = 1;

Para a operação do indicador, é usando as seguintes variáveis que são declaradas como globais:

bool new_candle = true;         
double  pdH,                    //Para o preço máximo da vela definida
        pdL,                    //Para preço mínimo da vela definida
        pricesH[],              //Matriz para armazenar os preços máximos
        pricesL[];              //Matriz para armazenar os preços mínimos
bool    DOTH,                   //Para exibição do ponto (segundo o máximo)
        DOTL;                   //Para exibição do ponto (segundo o mínimo)
double  UpDot[],                //Matriz do buffer para os valores dos pontos construídos segundo os máximos
        DownDot[];              //Matriz do buffer para os valores dos pontos construídos segundo os mínimos

Abaixo está a função Init():

int OnInit()
  {
   ChartRedraw(0);                              //Isto é necessário, ao mudar de timeframe, não haja problemas com a exibição
   SetIndexBuffer(0, UpDot);
   SetIndexBuffer(1, DownDot);
   SetIndexEmptyValue(0,0.0);
   SetIndexEmptyValue(1,0.0);
   SetIndexArrow(0,159);                        //Definimos o número do símbolo a partir da fonte Wingdings
   SetIndexArrow(1,159);
   SetIndexLabel(0, "TD " + Level + " High");   //Estes valores são exibidos na janela de dados
   SetIndexLabel(1, "TD " + Level + " Low");
   return(INIT_SUCCEEDED);
  }

Para obter os preços das vela que preciso, eu criei uma função com a qual podemos nos familiarizar abaixo:

void GetVariables(int start_candle, int level)
  {
   /*Nesta função, o indicador coleta informações a partir das velas para construir os pontos. Todas as variáveis usadas aqui já são declaradas como globais*/
   pdH = iHigh(NULL, 0, start_candle + 1 + level);      //High da vela definida
   pdL = iLow(NULL, 0, start_candle + 1 + level);       //Low da vela definida
   
   ArrayResize(pricesH, level * 2 + 1);                 //Definimos o tamanho da matriz
   ArrayResize(pricesL, level * 2 + 1);                 //
   
   for (int i = level * 2; i >= 0; i--){                //Coletamos todos os preços (máximos e mínimos) que preciso nas matrizes
      pricesH[i] = iHigh(NULL, 0, start_candle + i + 1);
      pricesL[i] = iLow(NULL, 0, start_candle + i + 1);
  }

E, finalmente, o mais interessante é o código de função Start():

int start()
  {
   int i = Bars - IndicatorCounted();                   //Isto é necessário para não contar a mesma coisa muitas vezes ao aparecer uma nova vela
   for (; i >= 0; i--)
     {                                                  //Aqui acontece tudo para construção dos pontos TD
      DOTH = true;
      DOTL = true;
      GetVariables(i, Level);                           //Obtenção dos valores atuais de preços
      
      for(int ii = 0; ii < ArraySize(pricesH); ii++)
        {                                              //Define se existe neste intervalo u ponto TD
         if (pdH < pricesH[ii]) DOTH = false;
         if (pdL > pricesL[ii]) DOTL = false;
        }   
         
      if(DOTH) UpDot[i + Level + 1] = pdH;              //Se sem, sua construção
      if(DOTL) DownDot[i + Level + 1] = pdL;
      
      if(UpDot[i + Level + 1] ==  UpDot[i + Level + 2]) UpDot[i + Level + 2] = 0;       //Aqui eu cobro o caso em que há duas velas sucessivamente
      if(DownDot[i + Level + 1] ==  DownDot[i + Level + 2]) DownDot[i + Level + 2] = 0; //têm os mesmos preços mínimos e máximos, e traço o ponto TD
     }                                                                                  //na última vela no par
   return(0);
  }

Então, você já conseguiu se relacionar com o processo de criação do indicador para construir pontos TD. Abaixo são mostrados dois gráficos com o indicador estabelecido TDDots. No primeiro valor da variável Level = 1, enquanto no segundo, Level = 10. Isso significa que, no primeiro gráfico, todos os pontos TD são cercados, pelo menos, por uma vela com valores máximos mais baixos ou mínimos mais altos, enquanto, no segundo, existem 10 dessas velas. Estes gráficos são fornecidos para demostrar como funciona o indicador.


Fig. 6. Amostra de trabalho do indicador: pontos TD dos níveis 1 e 10.


3.2. iTDLines

Como eu já escrevi, Thomas Demark usava apenas os dois últimos pontos para construir a linha TD. Em meu indicador, automatizo esta tática. A tarefa do indicador é construir duas linhas retas através dos pontos definidos. Mas traçá-las? Certamente isso pode ser feito usando uma função linear da forma: y = kx + b. Para fazer isso, é preciso encontrar os coeficientes (k) e (b) para que o a linha reta passe estritamente através de pontos especificados.

Para construir uma linha reta através de dois pontos, é preciso saber as coordenadas. Usando as fórmulas abaixo, encontramos (k) e (b) para a função linear. Além disso, tomaremos x como os números das velas a partir do limite direito do gráfico, enquanto y será o preço.

k = (y2 - y1) / (x2 - x1),
b = (x2 * y1 - x1 * y2) / (x2 - x1),
Onde: x1 - número da vela com o primeiro ponto,
      x2 - número de vela com o segundo ponto,
      y1 - preço do primeiro ponto,
      y2 - preço do segundo ponto.

Conhecendo (k) e (b), basta resolver a equação linear em cada vela para obter o preço do ponto na linhas TD. 

Abaixo está o código do indicador:

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2

#property indicator_color1 clrGreen
#property indicator_color2 clrBlue

input int Level = 1;

double LU[], LD[];

//Essa variável será usada para reduzir o cálculo.
datetime LastCount;

No indicador, usa-se uma variável, isto é, o valor do nível dos pontos de acordo com os quais serão construídas as linhas. Ela é definida pelo usuário, como em TDDots. A seguir, temos duas matrizes que conterão os valores dos preços de todos os pontos das linhas. A variável LastCount será usada para que o indicador realize os cálculos somente uma vez numa vela.

Além disso é exibida a função que encontra instantaneamente os valores k e b para cada uma das linhas. Ela será descrita por partes:

void GetBK(double &Ub, double &Uk, double &Db, double &Dk, int &EndUN, int &EndDN)
  {
   double TDU[];
   double TDD[];
   int TDU_n[];
   int TDD_n[];
   ArrayResize(TDU, 2, 2);
   ArrayResize(TDD, 2, 2);
   ArrayResize(TDU_n, 2, 2);
   ArrayResize(TDD_n, 2, 2);

A função retorna seis valores. Se o valor das quatro primeiras variáveis é compreensível, quanto às duas últimas é mais complicado. Elas serão úteis para exibir linhas. Cada uma delas indica o comprimento da linha em velas, a partir da atual.

//Obtemos os valores dos preços dos pontos e os números de velas a partir do início
   int Ui = 0;
   int Di = 0;
   for(int i = 0;; i++)
     {
      double current_bar_U = iCustom(NULL, 0, "TDDots", Level, 0, i);
      double current_bar_D = iCustom(NULL, 0, "TDDots", Level, 1, i);
      
      if(current_bar_U > 0 && Ui < 2)
        {
         TDU[Ui] = current_bar_U;   //Preço
         TDU_n[Ui] = i;             //Número
         Ui++;
        }
      if(current_bar_D > 0 && Di < 2)
        {
         TDD[Di] = current_bar_D;
         TDD_n[Di] = i;
         Di++;
        }
      if(Ui == 2 && Di == 2) break;
     }

Esta parte do código obtém os valores dos preços dos dois últimos pontos TD. Os preços são armazenados em matrizes para trabalhar com eles mais tarde.

   Ub = ( (TDU_n[0] * TDU[1]) - (TDU[0] * TDU_n[1]) ) / ( TDU_n[0] - TDU_n[1] );
   Uk = (TDU[0] - TDU[1]) / (TDU_n[0] - TDU_n[1]);
   
   Db = ( (TDD_n[0] * TDD[1]) - (TDD_n[1] * TDD[0]) ) / ( TDD_n[0] - TDD_n[1] );
   Dk = (TDD[0] - TDD[1]) / (TDD_n[0] - TDD_n[1]);   
   
   EndUN = TDU_n[1];
   EndDN = TDD_n[1];
  }

Como os números de velas têm uma direção de numeração como nas séries temporais (a conta é conduzida da direita para esquerda), é conveniente usar os valores opostos dos pontos. Em outras palavras, nas fórmulas acima, é necessário substituir x2 por x1, enquanto x1, por x2 e assim por diante. Desta forma, elas terão a seguinte aparência:

b = (x1 * y2 - x2 * y1) / (x1 - x2),
k = (y1 - y2) / (x1 - x2),
Onde: x1 - número da vela com o primeiro ponto,
      x2 - número de vela com o segundo ponto,
      y1 - preço do primeiro ponto,
      y2 - preço do segundo ponto.

Isto é seguido pela função (Init):

int OnInit()
  {
   SetIndexBuffer(0, LU);
   SetIndexLabel(0, "TDLU");
   SetIndexBuffer(1, LD);
   SetIndexLabel(1, "TDLD");
      
   SetIndexEmptyValue(0, 0);
   SetIndexEmptyValue(1, 0);
   
   LastCount = iTime(NULL, 0, 1);
   
   return(INIT_SUCCEEDED);
  }

Nela, os buffers são inicializados e atribuídos nomes. Também para que, com o primeiro tick de novos dados, o indicador calcule a localização das linhas, na variável LastCount é armazenada a data da vela anterior. Em princípio, nela é possível gravar qualquer data que não seja a atual.

Em seguida, escrevemos a função start():

int start()
  {
   //Se nova vela ou primeira execução
   if(iTime(NULL, 0, 0) != LastCount)
     {
      double Ub, Uk, Db, Dk;
      int eUp, eDp;
      
      GetBK(Ub, Uk, Db, Dk, eUp, eDp);
      
      //Excluímos os valores antigos
      for(int i = 0; i < IndicatorCounted(); i++)
        {
         LU[i] = 0;      
         LD[i] = 0;
        }
         
      //Construímos novos valores
      for(i = 0; i <= eUp; i++)
        {
         LU[i] = Uk * i + Ub;
        }
         
      for(i = 0; i <= eDp; i++)
        {
         LD[i] = Dk * i + Db;
        }
         
      LastCount = iTime(NULL, 0, 0);
     }
      
   return 0;
  }

Antes de traçar os novos valores, é necessário excluir os valores velhos do gráfico. Para fazer isso, no início da função é implementado um ciclo separado. Agora na equação acima são construídos dois linhas retas e à variável LastCount é atribuída a data atual, para não realizar essas ações novamente na vela atual.

Como resultado, o indicador funciona da seguinte maneira:

Amostra do indicador

Fig. 7. Amostra de trabalho do indicador: construção das linhas TD do nível 5.

Como você pode ver a fig. 7 mostra o o trabalho do indicador com o valor da variável Level = 5.


3.3 Indicador dos níveis horizontais

Certamente, existem muitas maneiras de determinar o preço dos níveis horizontais no gráfico. Sugiro uma maneira simples de construir dois níveis em relação ao preço atual. No trabalho do indicador será utilizado o método iTDDots que já temos descrito (ver. p. 3.1).

A essência é simples:

  1. O indicador obtém os valores de um número n de pontos TD de um nível determinado, estabelecido pelo usuário
  2. Calcula-se o valor médio de preço destes pontos
  3. Segundo esse valore, uma linha reta horizontal é exibida no gráfico

No entanto, há casos, quando os pontos TD estão longe em relação ao último ponto, isso move fortemente o nível horizontal do preço atual. Para resolver esse problema, tem sido introduzida uma variável incluída pelo usuário, ela limita a distância, em pontos, entre esses pontos. Ou seja, o indicador encontra o valor médio entre os últimos n extremos, que estão separados uns dos outros por não mais do que um certo número de pontos.

Vamos dar uma olhada no código do indicador.

São usados 2 buffers e 3 variáveis cujos valores devem ser digitados pelo usuário:

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2
#property indicator_color1 clrGreen
#property indicator_type1   DRAW_LINE
#property indicator_width1 2
#property indicator_color2 clrBlue
#property indicator_type2   DRAW_LINE
#property indicator_width2 2

input int TDLevel = 1;       //Nível de pontos
input int NumberOfDots = 3;  //Quantidade de pontos
input double Delta = 0.001;  //Distância máxima entre dois pontos

double TDLU[], TDLD[];

Este indicador cria dois nível horizontais. Como eles podem ser valiosos durante uma análise mais longa do que as linhas de tendência no indicador TDLines, eu decidi usar para sua construção objetos gráficos, isto é: linhas retas horizontais. No entanto, usar este indicador no futuro será muito mais fácil se os valores dos níveis horizontais forem armazenados nos buffers do indicador. Eu decidi armazenar esses valores na vela atual com índice 0, para sempre obter o valor do preço do nível de maneira mais fácil, usando iCustom a partir de outro programa.

int OnInit()
  {
   SetIndexBuffer(0,TDLU);
   SetIndexBuffer(1,TDLD);
   SetIndexEmptyValue(0,0.0);
   SetIndexEmptyValue(1,0.0);
   SetIndexLabel(0, "ГУ U");
   SetIndexLabel(1, "ГУ D");
   ObjectCreate(0, "Nível horizontal U", OBJ_HLINE, 0, iTime(NULL, 0, 0), 0);
   ObjectCreate(0, "Nível horizontal D", OBJ_HLINE, 0, iTime(NULL, 0, 0), 0);
   return(INIT_SUCCEEDED);
  }

Abaixo está uma função que calcula o preço de um determinado nível horizontal.

double GetLevelPrice(int ud,int n,double delta,int level)
  {
   /* ud - indicador do tipo de linha. 0 - U, outro valor - D.
   n - número de pontos.
   delta - distância máxima entre eles
   level - nível de pontos.*/

   //São armazenadas matrizes para armazenamento dos preços dos pontos
   double TDU[];
   double TDD[];
   ArrayResize(TDU,n,n);
   ArrayResize(TDD,n,n);
   ArrayInitialize(TDU,0);
   ArrayInitialize(TDD,0);
 
   //O ciclo funciona duas vezes, porque apenas existem 2 buffers de dados
   for(int Buffer=0; Buffer<2; Buffer++)
     {
      int N=0;
      int Fails=0;
      bool r=false;
      for(int i=0; r==false; i++)
        {
         double d=iCustom(NULL,0,"TDDots",level,Buffer,i);
         if(d>0)
           {
            if(N>0)
              {
               if(Buffer==0) double cp=TDU[N-1];
               else cp=TDD[N-1];
               if(MathAbs(d-cp)<=delta)
                 {
                  if(Buffer == 0)
                     TDU[N] = d;
                  else TDD[N]=d;
                  N++;
                 }
               //Se a distância for muito grande, será adicionado 1 ao erro
               else
                 {
                  Fails++;
                 }
              }
            else
              {
               if(Buffer == 0)
                  TDU[N] = d;
               else TDD[N]=d;

               N++;
              }
           }
         //Se forem coletados muitos erros, o ciclo será concluído
         if(Fails>2 || N>n) r=true;
        }
     }
   
   //Obtenção do valor médio
   double ATDU = 0;
   double ATDD = 0;
   N=0;
   for(i=0; i<ArraySize(TDU); i++)
     {
      ATDU=ATDU+TDU[i];
      if(TDU[i]==0)
        {
         i=ArraySize(TDU);
        }
      else
        {
         N++;
        }
     }
   ATDU=ATDU/N;
   N=0;
   for(i=0; i<ArraySize(TDD); i++)
     {
      ATDD=ATDD+TDD[i];
      if(TDD[i]==0)
        {
         i=ArraySize(TDD);
        }
      else
        {
         N++;
        }
     }
   ATDD=ATDD/N;

   //A função retorna o valor
   if(ud == 0) return ATDU;
   else return ATDD;
  }

 Na função, que é chamada em cada ticke novo, só resta receber valores de preço e atribuí-los a objetos já criados:

void start()
  { 
   //Excluo valores de buffers do indicador na vela anterior
   TDLD[1] = 0;
   TDLU[1] = 0;

   TDLD[0] = GetLevelPrice(1, TDLevel, Delta, NumberOfDots);
   TDLU[0] = GetLevelPrice(0, TDLevel, Delta, NumberOfDots);
   
   //Se, de alguma forma, faltam objetos
   if(ObjectFind("Nível horizontal U") < 0)
     {
      ObjectCreate(0, "Nível horizontal U", OBJ_HLINE, 0, iTime(NULL, 0, 0), 0);
     }
   if(ObjectFind("Nível horizontal D") < 0)
     {
      ObjectCreate(0, "Nível horizontal D", OBJ_HLINE, 0, iTime(NULL, 0, 0), 0);
     }
   
   ObjectSetDouble(0, "Nível horizontal U", OBJPROP_PRICE, TDLU[0]);
   ObjectSetDouble(0, "Nível horizontal D", OBJPROP_PRICE, TDLD[0]);
  }

Para sua conveniência, depois que você remover o indicador devem ser removidos os objetos:

void OnDeinit(const int reason)
  {
   if(!ObjectDelete("Nível horizontal U")) Print(GetLastError());
   if(!ObjectDelete("Nível horizontal D")) Print(GetLastError());
  }

No final, o indicador criado permite construir automaticamente os níveis horizontais de acordo com os parâmetros especificados pelo usuário.

Amostra do indicador

Fig. 8. Amostra do indicador. 



4. Expert Advisor para negociação segundo um indicador de níveis horizontais

Qualquer indicador deve ser usado com fins lucrativos. E se houver uma possibilidade, também para tirar lucro automaticamente. Certamente, o Expert Advisor descrito neste artigo não pode trazer lucro sempre e em qualquer mercado, no entanto seu propósito é diferente. Foi escrito para demonstrar as possibilidades dos indicadores e mostrar de forma geral a estrutura do trabalho do Expert Advisor.

Agora, falando de forma mais concreta. Usando indicador de níveis horizontais, é possível escrever um Expert Advisor que vai negociar segundo os sinais seguintes.

Condições de compra:

  • O preço ultrapassou o nível horizontal superior
  • O preço subiu n pontos a partir do valor de preço do nível horizontal superior

Condições de venda opostas:

  • O preço ultrapassou o nível horizontal inferior
  • Preço diminuiu n pontos a partir do valor de preço do nível horizontal inferior

De outro modo, ele pode ser representado também:

Sinal de compraSinal de venda

Fig. 9. Condições de compra e venda

O Expert Advisor abrira apenas uma ordem e irá acompanhá-la com um Trailing Stop, quer dizer, moverá o Stop Loss para o número de pontos especificados pelo usuário ao iniciá-lo.

A saída do mercado ocorrerá exclusivamente de acordo com o stop.

Examinemos o código do Expert Advisor desenvolvido em MQL4.
Primeiro, vamos descrever as variáveis cujos valores são definidos pelo usuário antes de executar o programa.

input int MagicNumber = 88341;      //Com esse número mágico será aberta a ordem
input int GL_TDLevel = 1;           //Nível de pontos TD que será usado pelo indicador de níveis horizontais
input int GL_NumberOfDots = 3;      //Número de pontos que usará o indicador de níveis horizontais
input double S_ExtraPoints = 0.0001;//Número de pontos adicionais que são aditivos L das ilustrações acima
input double GL_Delta = 0.001;      //Distância na qual o valor dos pontos TD são levado em atençáo pelo indicador de níveis horizontais
input int StopLoss = 50;            //Nível stop
input double Lot = 0.01;            //Tamanho do lote

A função a seguir verifica se foi cruzada a linha TD. Para não receber um sinal para abertura de posição, quando o preço atual cruza a linha, construída pelo indicador iglevels, é verificada a condição que exige que o preço mínimo da vela anterior seja inferior ao preço dessa linha para penetração da baixo para cima, ou para o preço máximo seja superior à linha para penetração de cima para baixo. Assim, é alcançada a possibilidade de receber um sinal unicamente numa vela. A seguir está o código para essa função:

int GetSignal(string symbol,int TF,int TDLevel,int NumberOfDots,int Delta,double ExtraPoints)
  {
//Valor do preço de nível construído sobre os pontos TD
   double UL=iCustom(symbol,TF,"iglevels",GL_TDLevel,GL_NumberOfDots,GL_Delta,0,0)+ExtraPoints;
//...segundo a parte inferior
   double DL=iCustom(symbol,TF,"iglevels",GL_TDLevel,GL_NumberOfDots,GL_Delta,1,0)-ExtraPoints;

   if(Bid<DL && iLow(symbol,TF,1)>DL)
     {
      return 1;
     }
   else
     {
      if(Ask>UL && iHigh(symbol,TF,1)<UL)
        {
         return 0;
        }
      else
        {
         return -1;
        }
     }
  }

Então para o trabalho do Expert Advisor é necessário declarar globalmente as variáveis a seguir:

int Signal = -1;          //Sinal atual
datetime LastOrder;       //Data da última transação realizada. É necessária para evitar casos de abertura de várias ordens numa vela.

A função Init() deve atribuir à variável LastOrder qualquer data passada para poder abrir uma nova ordem.

int OnInit()
  {
   LastOrder = iTime(NULL, 0, 1);
   return(INIT_SUCCEEDED);
  }

Na função OnTick acontece o mais importante:

void OnTick()
  {
   bool order_is_open=false;
//Acontece a procura da ordem aberta
   for(int i=0; i<OrdersTotal(); i++)
     {
      if(!OrderSelect(i,SELECT_BY_POS)) Print(GetLastError());

      if(OrderMagicNumber()==MagicNumber)
        {
         order_is_open=true;
         break;
        }
     }

//Obtenção do sinal atual
   Signal=GetSignal(Symbol(),0,GL_TDLevel,GL_NumberOfDots,GL_Delta,S_ExtraPoints);

//Cálculo do tamanho do stop
   double tsl=NormalizeDouble(StopLoss*MathPow(10,-Digits),Digits);

   if(order_is_open==true)
     {
      //Cálculo do preço do stop
      double p=NormalizeDouble(Ask-tsl,Digits);
      if(OrderType()==1) p=NormalizeDouble(Ask+tsl,Digits);

      if(OrderType()==0 && OrderStopLoss()<p)
        {
         if(!OrderModify(OrderTicket(),OrderOpenPrice(),p,0,0)) Print(GetLastError());
        }
      if(OrderType()==1 && OrderStopLoss()>p)
        {
         if(!OrderModify(OrderTicket(),OrderOpenPrice(),p,0,0)) Print(GetLastError());
        }
     }
//Se não houver nenhuma ordem
   if(order_is_open==false)
     {
      //Se, na vela atual, ainda não foi aberta a ordem
      if(iTime(NULL,0,0)!=LastOrder)
        {
         //Compra
         if(Signal==0)
           {
            if(!OrderSend(NULL,0,Lot,Ask,5,Ask-tsl,0,NULL,MagicNumber)) Print(GetLastError());
            LastOrder=iTime(NULL,0,0);
           }
         //Venda
         if(Signal==1)
           {
            if(!OrderSend(NULL,1,Lot,Bid,5,Ask+tsl,0,NULL,MagicNumber)) Print(GetLastError());
            LastOrder=iTime(NULL,0,0);
           }
        }
     }
  }

Nela, é verificada a presença de uma ordem aberta, se ela existir, o Expert Advisor verificará a possibilidade de mover o stop loss. Se ainda não existir ordem, ele agirá de acordo com o sinal recebido pela função GetSignal. Importante: se você está pensando, em seu programa, comparar dois números reais, então certifique-se usar a função NormalizeDouble, antes disso, caso contrário, o resultado da comparação pode ser totalmente ilógico.

O Expert Advisor funciona da seguinte maneira:

Exemplo de trabalho do Expert Advisor

Fig. 10. Amostra do trabalho do Expert Advisor no testador de estratégias

Na figura 10, é possível ver que ele dá tanto posições rentáveis quanto lucrativas. Além disso, o tamanho dos lucros excede o tamanho das perdas, desde que as perdas são limitadas aos critérios especificados, enquanto as transações rentáveis são acompanhadas pelo Trailing Stop. O Expert Advisor pode mostrar tanto bons resultados e como ruins. No entanto, lembramos que ele não foi criado para negociação real, mas para demonstrar na prática as possibilidade da implementação dos pontos TD e indicadores criados em sua base. Nosso Expert Advisor pode lidar com esta tarefa muito bem.



Fig. 11. Resultado do teste

Este Expert Advisor pode ser melhorado se você adicionar um fechamento parcial de posição ao atingir um determinado lucro, para não sofrer perdas por causa da correção da tendência, como às vezes acontece. Neste caso, a posição seria fechada sem perda nem lucro, o que permite a preservação do capital. Além disso, é lógico observar que o tamanho do stop deve variar dependendo da atividade do movimento do preço.


5. Expert Advisor para negociação usando linhas TD

Este EA é um exemplo do uso das linhas TD ao criar sistemas de negociação. Em geral, as linhas TD são linhas de tendência. Isto significa que se o preço cruzar a linha TD e continuar o movimento na mesma direção, a tendência poderá mudar.

O EA opera apenas com dois sinais:

Sinal de venda      Sinal de compra

Fig. 12. Sinais nos quais etdlines opera

A posição de compra abre, se, por um lado, a linha TD em alta for ultrapassada de baixo para cima e, por outro, o preço passar um certo número de pontos (definido pelo usuário) na mesma direção. Além disso, a linha TD deve ser dirigida para baixo, ou seja, a vela mais posterior -segundo a data- deve coincidir com o valor mais baixo de preço da linha.

A posição de venda terá o aspecto de inverso: se a linha TD em baixa for ultrapassada de cima para baixo e o preço diminuir ainda mais uma certa quantidade de pontos. Linha TD para cima.

As posições são acompanhadas por um Trailing Stop de tamanho fixo. O fechamento de posição é realizado somente por stop.

Abaixo está o código que descreve as variáveis introduzidas pelo usuário:

input int Level=1;                 //Nível de linhas TD
input double Lot=0.01;             //Tamanho do lote
input int AddPips=3;               //Número adicionado de pontos
input int Magic=88342;             //Número mágico de ordens
input int Stop=50;                 //Tamanho do stop em pontos
input int Bars_To_Open_New_Order=2;//Intervalo nas barras até a abertura da nova ordem

datetime LastBar;
datetime Trade_is_allowed;

Neste Expert Advisor, usa-se um atraso entre o fechamento da ordem velha e a apertura de uma nova. Isso é feito para assegurar que as posições não são abertas muitas vezes.

Na função OnInit, o Expert Advisor atribui os valores para as variáveis LastBar e Trade_is_allowed:

int OnInit()
  {
   LastBar=iTime(NULL,0,1);
   Trade_is_allowed=iTime(NULL,0,0);

   return(INIT_SUCCEEDED);
  }

A seguinte função, ou seja, a GetSignal retorna o sinal de negociação:

int GetSignal()
  {
//Se uma nova vela
   if(LastBar!=iTime(NULL,0,0))
     {
      double DU = iCustom(NULL, 0, "itdlines", Level, 0, 0);
      double DD = iCustom(NULL, 0, "itdlines", Level, 1, 0);
      double DU1 = iCustom(NULL, 0, "itdlines", Level, 0, 1);
      double DD1 = iCustom(NULL, 0, "itdlines", Level, 1, 1);
     }

   double add_pips=NormalizeDouble(AddPips*MathPow(10,-Digits),Digits);

//Пробитие линии U --> покупка
   if(Ask>DU+add_pips && iLow(NULL,0,0)<Ask && DU<DU1)
     {
      return 0;
     }
   else
     {
      //Penetração da linha D --> venda
      if(Bid<DD-add_pips && iHigh(NULL,0,0)>Bid && DD>DD1)
        {
         return 1;
        }
      //Sem penetração --> ausência de sinal
      else
        {
         return -1;
        }
     }

   return -1;
  }

A variável LastBar é usada para definir a nova vela. Isso é necessário para não contar os valores dos dois últimos pontos da linha TD com a chegada de cada novo tick, uma vez que eles são calculados somente uma vez, quando aparece uma vela nova. A função retorna: 0 — compra, 1 — venda, -1 — sem sinal.

Finalmente, a função OnTick, na qual acontecem todas as operações com ordens:

void OnTick()
  {
   int signal=GetSignal();
   bool order_is_open=false;

//Busca da ordem aberta
   for(int i=0; i<OrdersTotal(); i++)
     {
      if(!OrderSelect(i,SELECT_BY_POS)) Print(GetLastError());

      //Segundo o valor do número mágico
      if(OrderMagicNumber()==Magic)
        {
         order_is_open=true;
         i=OrdersTotal();
        }
     }

//Tamanho do stop
   double stop=Stop*MathPow(10,-Digits);
//Se a ordem já está aberta
   if(order_is_open==true)
     {
      //Verificação da possibilidade de mover o stop

      //Valores do preço stop da ordem
      double order_stop=NormalizeDouble(OrderStopLoss(),Digits);

      //Если ордер на покупку
      if(OrderType()==0)
        {
         if(order_stop<NormalizeDouble(Ask-stop,Digits))
           {
            if(!OrderModify(OrderTicket(),OrderOpenPrice(),NormalizeDouble(Ask-stop,Digits),0,0))Print(GetLastError());
           }
        }
      //Se a ordem é de venda
      if(OrderType()==1)
        {
         if(order_stop>NormalizeDouble(Bid+stop,Digits))
           {
            if(!OrderModify(OrderTicket(),OrderOpenPrice(),NormalizeDouble(Bid+stop,Digits),0,0))Print(GetLastError());
           }
        }
      Trade_is_allowed=iTime(NULL,0,0)+ChartPeriod(0)*60*Bars_To_Open_New_Order;
     }
//Se não há ordem aberta
   else
     {
      if(signal>=0 && iTime(NULL,0,0)>Trade_is_allowed)
        {
         if(signal==0)
           {
            if(!OrderSend(NULL,signal,Lot,Ask,5,Ask-stop,0,NULL,Magic)) Print(GetLastError());
           }
         if(signal==1)
           {
            if(!OrderSend(NULL,signal,Lot,Bid,5,Bid+stop,0,NULL,Magic)) Print(GetLastError());
           }

        }
     }
  }

No início, calcula-se o sinal, o ciclo for a procura a ordem, cujo número mágico é igual ao especificado pelo usuário (considerado que não haverá correspondências com outros sistemas). Em seguida, se já existir uma ordem, será verificada a possibilidade de mover o stop e, se possível, é deslocado. Se não houver nenhuma ordem, então, de acordo com o sinal, poderá ser aberta uma nova ordem, mas somente se a data da vela atual for superior à data da variável Trade_is_allowed (ela, por sua vez, se você tem uma ordem aberta, altera seu valor com a chegada de cada tick).

Esse EA opera como segue:

Exemplo de trabalho do testador

Fig. 13. Exemplo de trabalho do Expert Advisor etdlines

Pode ser visto claramente que o Expert Advisor opera bem os movimentos de preço longos, no entanto, as alterações bruscas do preço representam uma série de sinais falsos, o que leva a transações desfavoráveis. A seguir, o resultado do teste do Expert Advisor:


Fig. 14. Resultado do teste do etdlines



Conclusão

Meu objetivo era descrever os pontos TD e linhas TD de Thomas Demark e mostrar a implementação de seus desenvolvimentos na linguagem MQL4. Este artigo descreve um exemplo de como criar 3 indicadores e 2 Expert Advisors. Como você pode ver, as ideias de Demark encaixam de forma lógica nos sistemas de negociação e fornecem oportunidades muito boas de uso. 


Para utilizar os indicadores e Expert Advisors abaixo, você deve primeiro instalar itddots.mq4, uma vez que este indicador é usado em todos os outros programas. Para que o etdlines.mq4 funcione, também é necessário instalar itdlines.mq4, e de maneira semelhante acontece com eglevels.mq4: é preciso primeiro instalar iglevels.mq4. Isto é extremamente importante, porque se não forem instalados os indicadores necessários, o programa não funcionará e pode causar erros de parada.