English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Indicador para o gráfico de Ponto e Figura

Indicador para o gráfico de Ponto e Figura

MetaTrader 5Exemplos | 25 março 2014, 09:08
9 018 0
Dmitriy Zabudskiy
Dmitriy Zabudskiy

Introdução

Existem vários tipos de gráficos que fornecem informações sobre a situação do mercado atual. Muitos deles, como o Gráfico de Ponto e Figura, são o legado de um passado remoto.

Esse tipo de gráfico é conhecido desde o final do século XIX. Ele foi mencionado pela primeira vez por Charles Dow em seu editorial no Wall Street Journal, escrito em 20 de julho de 1901, que o rotulou de método do "livro". E embora Dow tenha mencionado o método do "livro" já em 1886, ele foi o primeiro a definir oficialmente seu uso até os dias de hoje.

Apesar do fato de que Dow apenas descreveu esse método nos editoriais, você pode encontrar agora muitos livros que fornecem detalhes sobre esse método. Um desses livros que eu recomendaria aos negociantes novatos é um livro de Thomas J. Dorsey intitulado "Point and Figure Charting: The Essential Application for Forecasting and Tracking Market Prices".

 

Descrição

O gráfico de Ponto e Figura é um conjunto de colunas verticais: as colunas de X's são o aumento dos preços e as colunas de O's são a queda dos preços. Ele é único na medida em que é traçado com base no preço da ação, não no tempo. Assim, tendo removido um valor dos dados do gráfico (tempo), obtemos gráficos com linhas de tendência traçadas a um ângulo de 45 graus.

Os gráficos de Ponto e Figura são traçados utilizando dois valores predefinidos:

  • Tamanho da caixa é o montante de movimento do preço necessário para adicionar um X ou um O (originalmente, o valor era expresso como uma quantia de dólares por ação, mas ao longo do tempo desenvolveu-se em pontos que é o que vamos usar em nosso indicador).
  • Quantidade reversa é o montante de reversão do preço expresso em unidades de tamanho de caixa necessário para alterar as colunas de X's para O's ou vice versa (por exemplo, um montante de reversão de 3 e um tamanho de caixa de 10 pontos irão corresponder a 30 pontos).

Então selecionamos um ponto de partida e colocamos um X para um aumento de preço ou um O para uma queda, desde que o preço tenha mudado pelo valor igual ao do tamanho da caixa, multiplicado pelo montante de reversão. Além disso, se o preço continuar a se mover na mesma direção alterando pelo tamanho da caixa, adicionamos um X no topo da coluna de X's para um aumento ou um O na base da coluna O's para uma queda, respectivamente. Se o preço se moveu na direção oposta pelo valor do tamanho da caixa multiplicado pelo montante de reversão, colocamos um X para um aumento no preço ou um O para uma queda no preço, iniciando assim uma nova coluna de X's ou uma nova coluna de O's, respectivamente.

Por conveniência, o gráfico de Ponto e Figura é normalmente traçado no papel quadriculado. Para uma melhor compreensão, vamos revisar um pequeno exemplo do gráfico de Ponto e Figura. Suponhamos que temos os seguintes dados:

Data Preço alto Preço baixo
07.03.2013 12:00 - 07.03.2013 20:00 1,3117 1,2989
07.03.2013 20:00 - 08.03.2013 04:00 1,3118 1,3093
08.03.2013 04:00 - 08.03.2013 12:00 1,3101 1,3080
08.03.2013 12:00 - 08.03.2013 20:00 1,3134 1,2955

Traçaremos o gráfico de Ponto e Figura dado que o tamanho da caixa é igual a 10 e o montante de reversão é igual a 3:

  • Primeiro, vemos um aumento no preço por 28 pontos, de 1.2989 para 1.3117, então desenhamos 12 X's.
  • O preço então cai de 1.3118 to 1.3093 por 25 pontos que é suficiente para a reversão, então deixamos do jeito que está.
  • Mais adiante podemos ver que o preço continua a cair para 1.3080. Dado o valor anterior de 1.3118, ele agora mudou para 38 pontos, então podemos começar uma nova coluna pela adição de dois O's (apesar do movimento do preço ter excedido o valor de três tamanhos de caixa, colocamos apenas dois O's, pois a seguinte coluna de O's sempre inicia em um tamanho de caixa abaixo).
  • O preço então aumenta de 1.3080 para 1.3134 pelos 54 pontos, subsequentemente caindo para 1.2955, que são 179 pontos. Assim, a próxima coluna consiste de quatro X's seguidas por uma coluna de O's constituída de 16 O's.

Vamos ver isso representado abaixo:

Fig. 1. Gráficos de Velas Japonesas (à esquerda) e gráfico de Ponto e Figura (à direita).

Fig. 1. Gráficos de Velas Japonesas (à esquerda) e gráfico de Ponto e Figura (à direita).

O exemplo acima do gráfico de Ponto e Figura é muito grosseiro e foi fornecido aqui para ajudar os iniciantes a compreender melhor o conceito.

 

Princípio do gráfico

Existem muitas técnicas de gráfico de Ponto e Figura, uma delas já foi descrita acima. Essas técnicas de gráficos diferem em dados que eles usam. Por exemplo, podemos utilizar dados diários sem considerar os movimentos intradiários, obtendo assim um traço grosseiro. Ou podemos considerar os dados do movimento do preço intradiário para assim obter um traço mais detalhado e suave.

Para alcançar um gráfico de Ponto e Figura mais suave e mais exato, foi decidido usar os dados por minuto para os cálculos e criação do gráfico já que o movimento do preço durante um minuto não é muito significativo e é normalmente até seis pontos, com dois ou três pontos não sendo raros. Assim, usaremos os dados do preço de abertura em cada barra de minuto.

O princípio do gráfico por si é muito simples:

  • Pegamos um ponto de partida, ou seja, o preço de abertura da primeira barra de minuto.
  • Além disso, se o preço mover a distância igual ao tamanho da caixa multiplicado pelo montante de reversão, ou mais, desenhamos os respectivos símbolos (O's para um movimento para baixo e X's para um movimento para cima). Os dados no último preço do símbolo são armazenados para gráficos posteriores.
  • No caso do preço se mover pelo tamanho da caixa na mesma direção, um símbolo correspondente é desenhado.
  • Ademais, no caso de reversão do preço, o cálculo será baseado no preço do último símbolo, ao invés do preço mais alto do par. Em outras palavras, se o movimento do preço não for maior que 50% do tamanho da caixa, ele é simplesmente ignorado.

Vamos agora determinar o estilo do gráfico de Ponto e Figura. A linguagem MQL5 suporta sete estilos de plotagem do indicador: linha, seção (segmento), histograma, seta (símbolo), área preenchida (canal preenchido), barras e velas japonesas.

Setas (símbolos) seriam perfeitos para a representação visual ideal, mas esse estilo requer um número variado de memórias de armazenamento (buffers) (o que simplesmente não é suportado no MQL5) ou um enorme número dela, já que plotar cada X único ou um O em uma coluna requer uma memória de armazenamento (buffer) do indicador separada. Isso significa que se você decidir usar esse estilo, você deve definir a volatilidade e ter recursos de memória suficientes.

Então decidimos pelas velas japonesas como o estilo do gráfico, precisamente, as velas japonesas coloridas. Cores diferentes devem ser usadas para diferenciar as colunas de X's das colunas de O's. Assim, o indicador precisa apenas de cinco memórias de armazenamento (buffers), permitindo o uso eficiente dos recursos disponíveis.

As colunas são divididas em tamanhos de caixa utilizando linhas horizontais. O resultado que obtemos é bem aceitável:

Fig. 2. Gráfico utilizando o indicador para EURUSD no período diário.

Fig. 2. Gráfico utilizando o indicador para EURUSD no período diário.

 

Algoritmo do indicador

Primeiro, precisamos determinar os parâmetros de entrada do indicador. Desde que o gráfico de Ponto e Figura não leve em conta o tempo e utilizamos os dados para plotagem das barras de minuto, precisamos determinar a quantidade de dados a serem processados, assim como não utilizar desnecessariamente os recursos do sistema. Adicionalmente, não existe razão na plotagem de um gráfico de Ponto e Figura utilizando o histórico inteiro. Então introduzimos os primeiro parâmetro - o Histórico. Isso levará em conta o número das barras de minuto para o cálculo.

Além disso, precisamos determinar o tamanho de caixa e o montante de reversão. Para esse fim, introduziremos as variáveis Cell e CellForChange, respectivamente. Também traremos os parâmetros de cor para o X's, ColorUp e para o O's, ColorDown. E, finalmente, o último parâmetro será a cor da linha - LineColor.

// +++ Program start +++
//+------------------------------------------------------------------+
//|                                                         APFD.mq5 |
//|                                            Aktiniy ICQ:695710750 |
//|                                                    ICQ:695710750 |
//+------------------------------------------------------------------+
#property copyright "Aktiniy ICQ:695710750"
#property link      "ICQ:695710750"
#property version   "1.00"
//--- Indicator plotting in a separate window
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   1
//--- plot Label1
#property indicator_label1  "APFD"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_style1  STYLE_SOLID
#property indicator_color1  clrRed,clrGold
#property indicator_width1  1
//--- Set the input parameters
input int   History=10000;
input int   Cell=5;
input int   CellForChange=3;
input color ColorUp=clrRed;
input color ColorDown=clrGold;
input color LineColor=clrAqua;
//--- Declare indicator buffers
double CandlesBufferOpen[];
double CandlesBufferHigh[];
double CandlesBufferLow[];
double CandlesBufferClose[];
double CandlesBufferColor[];
//--- Array for copying calculation data from the minute bars
double OpenPrice[];
// Variables for calculations
double PriceNow=0;
double PriceBefore=0;
//--- Introduce auxiliary variables
char   Trend=0;      // Direction of the price trend
double BeginPrice=0; // Starting price for the calculation
char   FirstTrend=0; // Direction of the initial market trend
int    Columns=0;    // Variable for the calculation of columns
double InterimOpenPrice=0;
double InterimClosePrice=0;
double NumberCell=0; // Variable for the calculation of cells
double Tick=0;       // Tick size
double OldPrice=0;   // Value of the last calculation price
//--- Create arrays to temporary store data on column opening and closing prices
double InterimOpen[];
double InterimClose[];
// +++ Program start +++

Vamos agora examinar a função OnInit(). Ela ligará as memórias de armazenamento (buffers) aos arranjos unidimensionais. Também configuraremos o valor do indicador sem a renderização de uma exibição mais precisa e

calcularemos o valor da variável auxiliar Tick (tamanho de um ponto) para os cálculos. Adicionalmente, configuraremos o esquema de cor e indexar a ordem nas memórias de armazenamento (buffers) do indicador como séries temporais. Isso é necessário para calcular convenientemente os valores do indicador.

// +++ The OnInit function +++
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,CandlesBufferOpen,INDICATOR_DATA);
   SetIndexBuffer(1,CandlesBufferHigh,INDICATOR_DATA);
   SetIndexBuffer(2,CandlesBufferLow,INDICATOR_DATA);
   SetIndexBuffer(3,CandlesBufferClose,INDICATOR_DATA);
   SetIndexBuffer(4,CandlesBufferColor,INDICATOR_COLOR_INDEX);
//--- Set the value of the indicator without rendering
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);
//--- Calculate the size of one tick
   Tick=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE);
//--- Set the color scheme
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,0,ColorUp);
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,1,ColorDown);
//--- Set the indexing order in arrays as time series
   ArraySetAsSeries(CandlesBufferClose,true);
   ArraySetAsSeries(CandlesBufferColor,true);
   ArraySetAsSeries(CandlesBufferHigh,true);
   ArraySetAsSeries(CandlesBufferLow,true);
   ArraySetAsSeries(CandlesBufferOpen,true);
//--- Check the input parameter for correctness
   if(CellForChange<2)
      Alert("The CellForChange parameter must be more than 1 due to plotting peculiarities");
//---
   return(0);
  }
// +++ The OnInit function +++

Alcançamos o "coração" do indicador, a função OnCalculate() onde os cálculos serão efetuados. Os cálculos dos valores do indicador são divididos em seis funções básicas que serão ativadas a partir da OnCalculate(). Vamos examiná-las:

1.  Função para copiar dados

Esta função copia os dados das barras de minuto para um arranjo para os cálculos. Primeiro, redimensionamos o arranjo de recebimento e então copiamos os preços de abertura para dentro dele utilizando a função CopyOpen().

//+------------------------------------------------------------------+
//| Function for copying data for the calculation                    |
//+------------------------------------------------------------------+
int FuncCopy(int HistoryInt)
  {
//--- Resize the array for copying calculation data
   ArrayResize(OpenPrice,(HistoryInt));
//--- Copy data from the minute bars to the array
   int Open=CopyOpen(Symbol(),PERIOD_M1,0,(HistoryInt),OpenPrice);
//---
   return(Open);
  }

2.  Função para calcular o número de colunas

Esta função calcula o número de colunas para o gráfico de Ponto e Figura.

Os cálculos são feitos em um ciclo iterando o número de barras no período de um minuto que foram copiadas na função acima. O próprio ciclo consiste de três blocos principais para diferentes tipos de tendências:

  •  0 - tendência indefinida.
  •  1 - tendência de alta.
  • -1 - tendência de baixa.

A tendência indefinida será utilizada apenas uma vez para determinar o movimento do preço inicial. A direção do movimento do preço será determinada quando o valor absoluto da diferença entre o mercado atual e o preço inicial exceder o valor do tamanho de caixa multiplicado pelo montante de reversão.

Se existe uma quebra para baixo, a tendência inicial será identificada como tendência de baixa e a entrada correspondente será feita para a variável Trend. Uma tendência de alta é identificada de maneira exatamente oposta. Adicionalmente, o valor da variável para o número de colunas, ColumnsInt, será aumentado.

Uma vez que a tendência atual for identificada, configuramos duas condições para cada direção. Se o preço continuar a se mover na direção da tendência atual pelo tamanho de caixa, o valor da variável ColumnsInt continuará inalterado. Se o preço reverter pelo tamanho de caixa multiplicado pelo montante de reversão, uma nova coluna irá aparecer e a variável ColumnsInt aumentará em um.

E assim por diante até que todas as colunas sejam identificadas. 

Para completar o número de células no ciclo, iremos utilizar a função MathRound() que nos permite arredondar os valores resultantes para os inteiros mais próximos. Opcionalmente, essa função pode ser substituída pela função MathFloor() (arredondando para baixo para os inteiros mais próximos) ou pela função MathCeil() (arredondando para cima para os inteiros mais próximos), dependendo da plotagem necessária.

//+------------------------------------------------------------------+
//| Function for calculating the number of columns                   |
//+------------------------------------------------------------------+
int FuncCalculate(int HistoryInt)
  {
   int ColumnsInt=0;

//--- Zero out auxiliary variables
   Trend=0;                 // Direction of the price trend
   BeginPrice=OpenPrice[0]; // Starting price for the calculation
   FirstTrend=0;            // Direction of the initial market trend
   Columns=0;               // Variable for the calculation of columns
   InterimOpenPrice=0;
   InterimClosePrice=0;
   NumberCell=0;            // Variable for the calculation of cells
//--- Loop for the calculation of the number of main buffers (column opening and closing prices)
   for(int x=0; x<HistoryInt; x++)
     {
      if(Trend==0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
        {
         //--- Downtrend
         if(((BeginPrice-OpenPrice[x])/Tick)>0)
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpenPrice=BeginPrice;
            InterimClosePrice=BeginPrice-(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=-1;
           }
         //--- Uptrend
         if(((BeginPrice-OpenPrice[x])/Tick)<0)
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpenPrice=BeginPrice;
            InterimClosePrice=BeginPrice+(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=1;
           }
         BeginPrice=InterimClosePrice;
         ColumnsInt++;
         FirstTrend=Trend;
        }
      //--- Determine further actions in case of the downtrend
      if(Trend==-1)
        {
         if(((BeginPrice-OpenPrice[x])/Tick)>0 && (Cell)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimClosePrice=BeginPrice-(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=-1;
            BeginPrice=InterimClosePrice;
           }
         if(((BeginPrice-OpenPrice[x])/Tick)<0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            ColumnsInt++;
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpenPrice=BeginPrice+(Cell*Tick);
            InterimClosePrice=BeginPrice+(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=1;
            BeginPrice=InterimClosePrice;
           }
        }
      //--- Determine further actions in case of the uptrend
      if(Trend==1)
        {
         if(((BeginPrice-OpenPrice[x])/Tick)>0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            ColumnsInt++;
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpenPrice=BeginPrice-(Cell*Tick);
            InterimClosePrice=BeginPrice-(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=-1;
            BeginPrice=InterimClosePrice;
           }
         if(((BeginPrice-OpenPrice[x])/Tick)<0 && (Cell)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimClosePrice=BeginPrice+(NumberCell*Cell*Tick);
            InterimClosePrice=NormalizeDouble(InterimClosePrice,Digits());
            Trend=1;
            BeginPrice=InterimClosePrice;
           }
        }
     }
//---
   return(ColumnsInt);
  }

3.  Função para colorir as colunas

Esta função é destinada a colorir as colunas como solicitado utilizando o esquema de cores predefinido. Para esse fim, iremos escrever um ciclo iterando o número de colunas e configurar as cores apropriadas para as colunas pares e ímpares, levando em consideração o valor da tendência inicial (coluna inicial).

//+------------------------------------------------------------------+
//| Function for coloring columns                                    |
//+------------------------------------------------------------------+
int FuncColor(int ColumnsInt)
  {
   int x;
//--- Fill the buffer of colors for drawing
   for(x=0; x<ColumnsInt; x++)
     {
      if(FirstTrend==-1)
        {
         if(x%2==0) CandlesBufferColor[x]=1; // All even buffers of color 1
         if(x%2>0) CandlesBufferColor[x]=0;  // All odd buffers of color 0
        }
      if(FirstTrend==1)
        {
         if(x%2==0) CandlesBufferColor[x]=0; // All odd buffers of color 0
         if(x%2>0) CandlesBufferColor[x]=1;  // All even buffers of color 1
        }
     }
//---
   return(x);
  }

4.  Função para determinar o tamanho da coluna

Uma vez que determinamos o número de colunas a serem utilizadas e configuramos as cores necessárias, precisamos determinar a altura das colunas. Para fazer isso, criaremos os arranjos temporários, InterimOpen[] e InterimClose[], nos quais iremos armazenar os preços de abertura e de fechamento para cada coluna. O tamanho desses arranjos será igual ao número de colunas.

Então, teremos um ciclo que é quase completamente idêntico ao ciclo da função FuncCalculate(), a diferença é que além de todo o exposto acima, ele também armazena os preços de abertura e de fechamento para cada coluna. Essa separação é implementada de modo a conhecer o número de colunas no gráfico com antecedência. Teoricamente, poderíamos inicialmente configurar conscientemente um maior número de colunas para alocação da memória do arranjo e fazer apenas com um único ciclo. Mas, nesse caso, teríamos um maior uso dos recursos da memória.

Vamos agora examinar mais detalhadamente a determinação da altura da coluna. Após o preço ter se movido à distância igual ao número de tamanhos de caixa solicitado, calculamos seu número, arredondando o mesmo para o inteiro mais próximo. Então adicionamos o número total de tamanhos de caixa da coluna atual para a coluna do preço de abertura, obtendo assim a coluna do preço de fechamento que também se torna o último preço utilizado. Isso será incluso em todas as próximas ações.

//+------------------------------------------------------------------+
//| Function for determining the column size                         |
//+------------------------------------------------------------------+
int FuncDraw(int HistoryInt)
  {
//--- Determine the sizes of temporary arrays
   ArrayResize(InterimOpen,Columns);
   ArrayResize(InterimClose,Columns);
//--- Zero out auxiliary variables
   Trend=0;                 // Direction of the price trend
   BeginPrice=OpenPrice[0]; // Starting price for the calculation
   NumberCell=0;            // Variable for the calculation of cells
   int z=0;                 // Variable for indices of temporary arrays
//--- Loop for filling the main buffers (column opening and closing prices)
   for(int x=0; x<HistoryInt; x++)
     {
      if(Trend==0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
        {
         //--- Downtrend
         if(((BeginPrice-OpenPrice[x])/Tick)>0)
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpen[z]=BeginPrice;
            InterimClose[z]=BeginPrice-(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=-1;
           }
         //--- Uptrend
         if(((BeginPrice-OpenPrice[x])/Tick)<0)
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpen[z]=BeginPrice;
            InterimClose[z]=BeginPrice+(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits()); // Normalize the number of decimal places
            Trend=1;
           }
         BeginPrice=InterimClose[z];
        }
      //--- Determine further actions in case of the downtrend
      if(Trend==-1)
        {
         if(((BeginPrice-OpenPrice[x])/Tick)>0 && (Cell)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimClose[z]=BeginPrice-(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=-1;
            BeginPrice=InterimClose[z];
           }
         if(((BeginPrice-OpenPrice[x])/Tick)<0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            z++;
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpen[z]=BeginPrice+(Cell*Tick);
            InterimClose[z]=BeginPrice+(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=1;
            BeginPrice=InterimClose[z];
           }
        }
      //--- Determine further actions in case of the uptrend
      if(Trend==1)
        {
         if(((BeginPrice-OpenPrice[x])/Tick)>0 && (Cell*CellForChange)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            z++;
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimOpen[z]=BeginPrice-(Cell*Tick);
            InterimClose[z]=BeginPrice-(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=-1;
            BeginPrice=InterimClose[z];
           }
         if(((BeginPrice-OpenPrice[x])/Tick)<0 && (Cell)<fabs((BeginPrice-OpenPrice[x])/Tick))
           {
            NumberCell=fabs((BeginPrice-OpenPrice[x])/Tick)/Cell;
            NumberCell=MathRound(NumberCell);
            InterimClose[z]=BeginPrice+(NumberCell*Cell*Tick);
            InterimClose[z]=NormalizeDouble(InterimClose[z],Digits());
            Trend=1;
            BeginPrice=InterimClose[z];
           }
        }
     }
//---
   return(z);
  }

5.  Função para reversão do arranjo

A função reverte os dados do arranjo da coluna obtidos de modo a mostrar posteriormente o gráfico de forma programática da direita para a esquerda. A reversão do arranjo é realizada em um ciclo, com valores Altos e Baixos sendo atribuídos às velas. Isso é feito, pois o indicador é mostrado apenas para as velas para as quais todos os valores da memória de armazenamento (buffer) do indicador não forem iguais a zero.

//+------------------------------------------------------------------+
//| Function for array reversal                                      |
//+------------------------------------------------------------------+
int FuncTurnArray(int ColumnsInt)
  {
//--- Variable for array reversal
   int d=ColumnsInt;
   for(int x=0; x<ColumnsInt; x++)
     {
      d--;
      CandlesBufferOpen[x]=InterimOpen[d];
      CandlesBufferClose[x]=InterimClose[d];
      if(CandlesBufferClose[x]>CandlesBufferOpen[x])
        {
         CandlesBufferHigh[x]=CandlesBufferClose[x];
         CandlesBufferLow[x]=CandlesBufferOpen[x];
        }
      if(CandlesBufferOpen[x]>CandlesBufferClose[x])
        {
         CandlesBufferHigh[x]=CandlesBufferOpen[x];
         CandlesBufferLow[x]=CandlesBufferClose[x];
        }
     }
//---
   return(d);
  }

6.  Função para desenhar linhas horizontais

Esta função faz uma grade de "caixas" utilizando as linhas horizontais (objetos). Ao início da função, determinamos os valores do preço máximo e mínimo a partir do arranjo dos dados de cálculo. Esses valores são utilizados posteriormente para gradualmente traçar linhas acima e abaixo do ponto de partida.

//+------------------------------------------------------------------+
//| Function for drawing horizontal lines                            |
//+------------------------------------------------------------------+
int FuncDrawHorizontal(bool Draw)
  {
   int Horizontal=0;
   if(Draw==true)
     {
      //--- Create horizontal lines (lines for separation of columns)
      ObjectsDeleteAll(0,ChartWindowFind(),OBJ_HLINE); // Delete all old horizontal lines
      int MaxPriceElement=ArrayMaximum(OpenPrice);     // Determine the maximum price level
      int MinPriceElement=ArrayMinimum(OpenPrice);     // Determine the minimum price level
      for(double x=OpenPrice[0]; x<=OpenPrice[MaxPriceElement]+(Cell*Tick); x=x+(Cell*Tick))
        {
         ObjectCreate(0,DoubleToString(x,Digits()),OBJ_HLINE,ChartWindowFind(),0,NormalizeDouble(x,Digits()));
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_COLOR,LineColor);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_STYLE,STYLE_DOT);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_SELECTED,false);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_WIDTH,1);
         Horizontal++;
        }
      for(double x=OpenPrice[0]-(Cell*Tick); x>=OpenPrice[MinPriceElement]; x=x-(Cell*Tick))
        {
         ObjectCreate(0,DoubleToString(x,Digits()),OBJ_HLINE,ChartWindowFind(),0,NormalizeDouble(x,Digits()));
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_COLOR,LineColor);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_STYLE,STYLE_DOT);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_SELECTED,false);
         ObjectSetInteger(0,DoubleToString(x,Digits()),OBJPROP_WIDTH,1);
         Horizontal++;
        }
      ChartRedraw();
     }
//---
   return(Horizontal);
  }

Agora que descrevemos todas as funções básicas, vamos ver a ordem em que elas são ativadas na OnCalculate():

  • Inicie a função para copiar os dados para os cálculos (desde que ainda não existam barras calculadas).
  • Ative a função para calcular o número de colunas.
  • Determine as cores das colunas.
  • Determine o tamanho das colunas.
  • Ative a função para reversão dos dados nos arranjos.
  • Ative a função para traçar as linhas horizontais que irão dividir as colunas em "caixas".
// +++ Main calculations and plotting +++
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- Reverse the array to conveniently get the last price value
   ArraySetAsSeries(close,true);
//---
   if(prev_calculated==0)
     {
      //--- Start the function for copying data for the calculation
      int ErrorCopy=FuncCopy(History);
      //--- In case of error, print the message
      if(ErrorCopy==-1)
        {
         Alert("Failed to copy. Data is still loading.");
         return(0);
        }
      //--- Call the function for calculating the number of columns
      Columns=FuncCalculate(History);
      //--- Call the function for coloring columns
      int ColorCalculate=FuncColor(Columns);
      //--- Call the function for determining column sizes
      int z=FuncDraw(History);
      //--- Start the function for array reversal
      int Turn=FuncTurnArray(Columns);
      //--- Start the function for drawing horizontal lines
      int Horizontal=FuncDrawHorizontal(true);
      //--- Store the value of the last closing price in the variable
      OldPrice=close[0];
     }
//--- If the price is one box size different from the previous one, 
//--- the indicator is recalculated
   if(fabs((OldPrice-close[0])/Tick)>Cell)
      return(0);
//--- return value of prev_calculated for next call
   return(rates_total);
  }
// +++ Main calculations and plotting +++

Esse é o final do código principal do indicador. Mas como o indicador possui suas desvantagens de conter arranjos complexos, ele às vezes precisa ser recarregado.

Para implementar isso, utilizaremos a função OnChartEvent() que lida com os eventos de pressionar a tecla "С" - limpar e a tecla "R" - redesenhar. Para limpar, o valor zero é atribuído a uma das memórias de armazenamento (buffers) do indicador. A função para redesenhar o gráfico representa a repetição dos cálculos anteriores e a atribuição dos valores para as memórias de armazenamento (buffers) do indicador.

// +++ Secondary actions for the "С" key - clear and the "R" key - redraw +++
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   if(id==CHARTEVENT_KEYDOWN)
     {
      //--- 67 - The "C" key code clears the indicator buffer
      if(lparam==67)
        {
         for(int x=0; x<Bars(Symbol(),PERIOD_CURRENT); x++)
            CandlesBufferOpen[x]=0;
         ChartRedraw();
        }
      // 82 - The "R" key code redraws the indicator
      if(lparam==82)
        {
         //--- Start the copying function
         int ErrorCopy=FuncCopy(History);
         //--- In case of error, print the message
         if(ErrorCopy==-1)
            Alert("Failed to copy data.");
         //--- Call the function for calculating the number of columns
         Columns=FuncCalculate(History);
         //--- Call the function for coloring columns
         int ColorCalculate=FuncColor(Columns);
         //--- Call the function for determining column sizes
         int z=FuncDraw(History);
         //--- Start the function for array reversal
         int Turn=FuncTurnArray(Columns);
         //--- Start the function for drawing horizontal lines
         int Horizontal=FuncDrawHorizontal(true);
        }
     }
  }
//+------------------------------------------------------------------+
// +++ Secondary actions for the "С" key - clear and the "R" key - redraw +++

Podemos agora respirar fundo enquanto finalizamos a descrição do algoritmo e o código do indicador, e podemos continuar a examinar alguns padrões de gráficos de Ponto e Figura que geram sinais para a execução das negociações.

 

Sinais padrão

Existem duas abordagens de negociação e análise dos gráficos de Ponto e Figura: uma com base nos padrões e outra com base nas linhas de suporte e resistência. A última é peculiar na medida em que as linhas de suporte e resistência são traçadas a um ângulo de 45 graus (não é sempre assim em um indicador planejado, pois ele é traçado utilizando as velas japonesas, as quais os tamanhos mudam dependendo do tamanho do gráfico, o que pode levar a distorção do ângulo).

Vamos agora considerar os padrões como:

  1. o "Topo Duplo" e o "Fundo Duplo".

    O "Topo Duplo" ocorre quando o preço aumenta, depois cai formando uma certa coluna de O's e sobe novamente excedendo a coluna anterior de X's pelo tamanho de caixa. O padrão é um sinal de compra.

    O "Fundo Duplo" é oposto exato do padrão "Topo Duplo". Existe uma certa queda no preço (uma coluna de O's) seguida por uma coluna de X's e uma coluna de O's que cai uma caixa abaixo da coluna anterior de O's, formando assim um sinal de venda.

    Fig. 3. Padrões do

    Fig. 3. Padrões do "Topo Duplo" e "Fundo Duplo".

  2. Padrões do "Topo Triplo" e "Fundo Triplo".

    Estes padrões são menos frequentes, mas representam sinais muito fortes. Essencialmente, eles são similares aos padrões do "Topo Duplo" e "Fundo Duplo", sendo sua continuação. Antes de transmitirem um sinal, eles repetem os movimentos dos dois padrões acima.

    Um "Topo Triplo" ocorre quando o preço alcança o mesmo nível de preço duas vezes e então rompe acima daquele nível, representando um sinal de compra.

    O padrão do "Fundo Triplo" é o oposto do "Topo Triplo" e ocorre quando o preço cai ao mesmo nível duas vezes e então rompe abaixo daquele nível, transmitindo assim um sinal de venda.

    Fig. 4. Padrões do

    Fig. 4. Padrões do "Topo Triplo" e "Fundo Triplo".

  3. Padrões do "Rompimento do Triângulo Simétrico": parte de cima e parte de baixo.

    Todos nós nos lembramos dos padrões de análise técnica. O padrão do "Rompimento do Triângulo Simétrico" é similar ao "Triângulo Simétrico" na análise técnica. Um rompimento da parte de cima (como mostrado na figura à esquerda) é um sinal de compra. Inversamente, um rompimento da parte de baixo é um sinal de venda (figura à direita).

    Fig. 5.

    Fig. 5. "Rompimento do Triângulo Simétrico": parte de cima e parte de baixo.

  4. Padrões da "Catapulta Ascendente" (Bullish Catapult) e "Catapulta Descendente" (Bearish Catapult).

    As "Catapultas" são de certo modo similares aos padrões do "Triângulo ascendente" e "Triângulo descendente" da análise técnica. Seus sinais são essencialmente parecidos - assim que o preço rompe acima ou abaixo do lado paralelo do triângulo, um sinal de compra ou venda ocorre, respectivamente. No caso da "Catapulta Ascendente", o preço rompe acima, o que é um sinal de compra (figura à esquerda) enquanto no caso da "Catapulta Descendente" o preço rompe abaixo, o que é um sinal de venda (figura à direita).

    Fig. 6. Padrões da

    Fig. 6. Padrões da "Catapulta Ascendente" e "Catapulta Descendente".

  5. Padrão da "Linha de Tendência de 45 graus".

    O padrão da "Linha de Tendência de 45 graus" cria um suporte ou uma linha de resistência. Se existe um rompimento daquela linha, obteremos um sinal de venda (como mostrado na figura à direita) ou um sinal de compra (como mostrado na figura á esquerda).

    Fig. 7. Padrão da

    Fig. 7. Padrão da "Linha de Tendência de 45 graus".

Revisamos os padrões e sinais básicos do gráfico de Ponto e Figura. Vamos agora encontrar alguns deles no gráfico do indicador fornecido no início do artigo:

Fig. 8. Identificando os padrões no gráfico de Ponto e Figura.

Fig. 8. Identificando os padrões no gráfico de Ponto e Figura.

 

Conclusão

Chegamos ao estágio final do artigo. Gostaria de dizer aqui que o gráfico de Ponto e Figura não está perdido no tempo e ainda pode ser utilizado ativamente, o que mais uma vez prova seu valor.

O indicador desenvolvido, embora não destituído de desvantagens como o gráfico de bloco ao invés dos usuais X's e O's e a incapacidade de ser testado (ou melhor, a operação incorreta) no Examinador de Teste, produz resultados gráficos bastante precisos.

Também quero pontuar que esse algoritmo, em uma versão ligeiramente modificada, pode potencialmente ser utilizado para o gráfico Renko, assim como para a integração de ambos os tipos de gráficos em um único código com opções de menu, permitindo que você selecione, conforme apropriado. Não excluo a possibilidade de traçar o gráfico diretamente na janela principal que novamente solicitará uma ligeira modificação do código.

No geral, o objetivo deste artigo era compartilhar minhas ideias com relação ao desenvolvimento do indicador. Como este é o meu primeiro artigo, agradecerei qualquer comentário ou opinião. Obrigado pelo seu interesse no meu artigo!

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

Arquivos anexados |
apfd.mq5 (18.85 KB)
Rede em nuvem do MQL5: Você ainda está calculando? Rede em nuvem do MQL5: Você ainda está calculando?
Logo fará um ano e meio desde que a Rede em nuvem do MQL5 foi inaugurada. Esse evento inovador nos conduziu para uma era de negócios algorítmicos - agora com poucos cliques, negociadores podem ter centenas e milhares de núcleos de computação a sua disposição para a otimização de suas estratégias de negócios.
Depuração dos programas do MQL5 Depuração dos programas do MQL5
Este artigo é destinado principalmente aos programadores que já tenham aprendido a linguagem, mas ainda não tenham dominado completamente o desenvolvimento do programa. Ele revela algumas técnicas de depuração e apresenta uma experiência combinada do autor e muitos outros programadores.
Como instalar e utilizar o OpenCL para realizar cálculos Como instalar e utilizar o OpenCL para realizar cálculos
Já se passou mais de um ano desde que o MQL5 começou a fornecer suporte nativo para OpenCL. Porém, não muitos usuários viram o verdadeiro valor do uso de uma computação paralela em seus Expert Advisors, indicadores e scripts. Este artigo tem o propósito de ajudá-lo a instalar e configurar OpenCL no seu computador de modo que pode tentar usar esta tecnologia no terminal de negócio do MetaTrader 5.
Guia prático do MQL5: Reduzindo o efeito de sobreajuste e lidando com a falta de cotações Guia prático do MQL5: Reduzindo o efeito de sobreajuste e lidando com a falta de cotações
Seja qual for a estratégia de negociação que você usa, sempre haverá uma questão de quais parâmetros escolher para garantir lucros futuros. Este artigo fornece um exemplo de um Expert Advisor com a possibilidade de otimizar vários parâmetros símbolos ao mesmo tempo. Esse método destina-se a reduzir o efeito dos parâmetros de sobreajuste e a lidar com situações em que os dados a partir de um único símbolo não são suficientes para o estudo.