English Русский 中文 Español Deutsch 日本語
Kit do trader: Indicadores de decoração

Kit do trader: Indicadores de decoração

MetaTrader 4Exemplos | 8 fevereiro 2016, 14:37
953 0
TheXpert
TheXpert

Introdução

O que é um indicador? É uma ferramenta concebida para mostrar um certo tipo de dados. Normalmente, são informações sobre as propriedades da série de preços, exatamente este tipo de indicadores serão considerados adiante.

Cada indicador também tem suas próprias propriedades e características: por exemplo, a faixa de valores, as zonas de sobrecompra/sobrevenda, o cruzamento da linha, os topos e fundos... Eles são muitos e podem ser usados sucessivamente juntos com os principais indicadores de valores. Entretanto, tais propriedades não são sempre vívidas. Os motivos podem ser diferentes - o pequeno tamanho da janela de indicador, a baixa concentração etc.

A finalidade deste artigo é ajudar você a melhorar o valor descritivo e informacional dos indicadores, assim como a automatização parcial e a facilitação do processo de implementação do código. Espero que o código abaixo não cause problemas para os desenvolvedores profissionais e iniciantes.

O artigo é concebido para aqueles que tenham pelo menos o nível iniciante de conhecimento de MQL4 e possam implementar algoritmos e ideias simples em um código, assim como saibam sobre a estrutura de armazenagem de código no terminal e possam usar as bibliotecas (especialistas/bibliotecários) e arquivos de cabeçalho (especialistas/inclusão).


1. Como configurar uma tarefa

Entre todos os indicadores, gostaria de delinear os mais informativos e mais usados:

  • Cruzamento de linha.

  • Nível - não apenas pontos de cruzamento de linha, mas todo o nível será destacado.


  • Topos/fundos em uma interpretação simples.


  • Coloração diferente para as direções ascendente/descendente.


Vamos discuti-los.


2. Noções básicas

Para evitar equívocos, vamos dedicar um tempo para ver a estrutura de indicador.


#property indicator_separate_window

// number of visible buffers of the indicator
#property indicator_buffers 3

// setting the range of indicator values
#property indicator_minimum 0
#property indicator_maximum 100

// setting indicator colors
#property indicator_color1  White
#property indicator_color2  Red
#property indicator_color3  Blue

// external settings
extern int RSIPeriod       = 9;
extern int AppliedPrice    = 0;
extern int MAPeriod        = 5;

// declaring indicator buffers. Here they can be declared in any order.
// Any names can be given to buffers, though better meaningful

double Values[];           // Values
double SmoothedValues[];   // Smoothed values
double Crosses[];          // intersections

// Significant number of digits after a decimal point in indicator values
int DigitsUsed = 5;

// Used empty value. In MQL4 there are two empty values -- EMPTY (-1)
// -- used as an empty parameter when calling functions
// EMPTY_VALUE (0x7FFFFFFF) -- used as an unacceptable value 
// (or default value) of a variable in indicators and function calls. 
// The fact is, most built-in indicators return 0 if there is no value
// Besides, in custom (iCustom) indicators the empty value can be 
// set as any, this must be noted.
int EmptyValueUsed = 0;

// Initialization function.
int init()
{
   // Number of used buffers can be larger than that of displayed ones; some 
   // may contain intermediate calculations and additional information. The total 
   // number of buffers including additional ones is displayed here. 
   // If there are no additional buffers,
   // this line is not needed. Total number must not exceed 8
   // IndicatorBuffers(3);

   // associate buffers. Indexes must go from 0 till the declared number (not including)
   // buffers are drawn in the order of index growing, this is important and can be 
   // used when righting indicators further.
   // It means that a buffer with a larger index is drawn above the buffer with lower one
   SetIndexBuffer(0, Values);
   SetIndexBuffer(1, SmoothedValues);
   SetIndexBuffer(2, Crosses);
   // besides, it is important that additional buffers are located after displayed ones 
   // (i.e. they must have higher index) otherwise problems may occur displaying buffers, 
   // and sometimes the error can be hardly found

   // This function sets an empty value for the buffer with the preset index
   // I do not recommend to use this function in order to avoid possible difficulties
   // Default empty value for buffers -- EMPTY_VALUE. 
   // Empty buffer values are not drawn in a chart (except for DRAW_ZIGZAG)

   // SetIndexEmptyValue(0, EMPTY_VALUE);
   
   // Set parameters for buffers
   SetIndexStyle(0, DRAW_LINE);     // The main signal is a solid line
   SetIndexStyle(1, DRAW_LINE, STYLE_DASH); // Smoothed -- dotted line
   SetIndexStyle(2, DRAW_ARROW, STYLE_SOLID, 2); // Intersections -- crosses of the size 2
   
   SetIndexArrow(2, 251); // cross code in Wingdings
   
   IndicatorDigits(DigitsUsed); // set number of significant digits after point
   
   // Setting the starting plotting point for each indicator. If in terms of the current index 
   // the history depth 
   // is lower than the value written here, the buffer value with this index will not be drawn.
   SetIndexDrawBegin(0, RSIPeriod); 
   SetIndexDrawBegin(1, RSIPeriod + MAPeriod);
   SetIndexDrawBegin(2, RSIPeriod + MAPeriod + 1);

   return(0);
}

int start()
{
   // counting number of bars for re-calculation
   int toCount = Bars - IndicatorCounted();  
   
   // Calculating values
   // counting from history start till the current moment
   for (int i = toCount - 1; i >=0; i--)
   {
      // I understood its convenience only when I started to use it
      // I recommend to conduct the normalization of data at once, 
      // so that later comparison could be easily made
      Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed);
   }
      
   // Counting smoothed values
   for (i = toCount - 1; i >=0; i--)
   {
      SmoothedValues[i] = NormalizeDouble(iMAOnArray(Values, 0, MAPeriod, 0, MODE_EMA, i), DigitsUsed);
   }
      
   // ...
   
   return(0);
}


3. Características

Vamos considerar as características em detalhes.


3.1. Cruzamento de linha

Talvez todo o desenvolvedor tenha tentado implementar um algoritmo de negociação usando o cruzamento de duas MAs (médias móveis); ou o mesmo da linha de base MACD e o cruzamento da linha de sinal. Vamos tentar visualizá-lo e torná-lo muito mais evidente exibindo o ponto de cruzamento no indicador.

Como exemplo, por todo o texto, usaremos o Índice de forma relativa, então nosso objetivo é desenvolver um RSI melhorado com algumas novas vantagens.


3.1.1. Formalização de tarefa

É necessário marcar as barras do cruzamento de linha em um buffer separado.


3.1.2. Problemas

Parece que tudo é simples e claro. A tarefa não é difícil e pode ser resolvida com poucas linhas de código.

Precisamos descrever o cruzamento de linha como este:



if ((x1 > y1 && x2 < y2) || (x1 < y1 && x2 > y2))
{
    // line crossing here
}

Ou podemos simplificá-lo:

if ((x1 - y1)*(x2 - y2) < 0)
{
    // line crossing here
}

Mas vamos considerar o seguinte caso:



Observe que os pontos verdes têm os mesmos valores. Em tal caso, não temos cruzamento de linha, apenas um caso de toque de linha.

Mas aqui:



não é tão simples determinar o cruzamento, esse caso é bem possível.

E também é necessário distinguir corretamente um caso de toque do caso de cruzamento, considerando que durante a pesquisa podemos encontrar um valor vazio em um buffer ou fim de história.


3.1.3. Solução

A partir deste lugar na função, init() não será considerado, porque não é importante. O código completo pode ser encontrado na fonte.

Aqui está a solução para os valores simples e suavizados do indicador de índice de forma relativa.


//|                                 RSI_Crosses_Sample.mq4 |

extern int RSIPeriod       = 9;
extern int AppliedPrice    = 0;
extern int MAPeriod        = 5;

// buffers
double Values[];           // Values
double SmoothedValues[];   // Smoothed values
double Crosses[];          // Crosses

int DigitsUsed = 5;
int EmptyValueUsed = 0;

int start()
{
   int toCount = Bars - IndicatorCounted();  
   
   // Reading the values and normalizing them 
   // Mark the crosses
   for (i = toCount - 1; i >=0; i--)
   {
      // i+1 must be greater or equal bars count in the history
      if (i + 1 >= Bars)
      {
         continue;
      }

      // if some of the values are empty, it is not necessary to check
      if (
            Values[i]               == EmptyValueUsed || 
            Values[i + 1]           == EmptyValueUsed ||
            SmoothedValues[i]       == EmptyValueUsed || 
            SmoothedValues[i + 1]   == EmptyValueUsed ||
            Values[i]               == EMPTY_VALUE    || 
            Values[i + 1]           == EMPTY_VALUE    ||
            SmoothedValues[i]       == EMPTY_VALUE    || 
            SmoothedValues[i + 1]   == EMPTY_VALUE
      )
      {
         continue;
      }
      
      // clear the current value
      Crosses[i] = EMPTY_VALUE;
      
      // crossing check (simple case)
      if ((Values[i] - SmoothedValues[i])*(Values[i + 1] - SmoothedValues[i + 1]) < 0)
      {
         Crosses[i] = SmoothedValues[i];
         continue;
      }
      
      // the crossing condition for a complicated case - 
      // when crossing contain several bars with the same values
      // 
      if (Values[i + 1] == SmoothedValues[i + 1] && Values[i] != SmoothedValues[i])
      {
         // there is potential crossing - checking it
         // lets find the second end

         int index = i + 1;
         bool found = false;
         while (
               index < Bars &&    // out of range
               Values[index] != EmptyValueUsed &&   // check for empty
               Values[index] != EMPTY_VALUE &&      // check for empty 
               SmoothedValues[index] != EmptyValueUsed &&  // check for empty
               SmoothedValues[index] != EMPTY_VALUE)       // check for empty
         {
            if (Values[index] != SmoothedValues[index])
            {
               // ok, we have found the second end
               found = true;
               break;
            }
            
            index++;
         }

         if (!found)
         {
            // the case of the end of history or empty value
            // anyway, we mean that there is no crossing
            continue;
         }
         
         // checking the ends for crossing
         if ((Values[i] - SmoothedValues[i])*(Values[index] - SmoothedValues[index]) < 0)
         {
            // crossing found
            Crosses[i] = SmoothedValues[i];
         }  // else we have a touching - do not mark it
            
      }
   }
   
   return(0);
}

3.1.4. Automatização

Nesta seção, consideramos a solução do problema usando a biblioteca Indicator_Painting.

//|                                 RSI_Crosses_Lib_Sample.mq4 |

#include <Indicator_Painting.mqh>

extern int RSIPeriod       = 9;
extern int AppliedPrice    = 0;
extern int MAPeriod        = 5;

// buffers
double Values[];           // Values
double SmoothedValues[];   // Smoothed values
double Crosses[];          // Crosses

int DigitsUsed = 5;
int EmptyValueUsed = 0;

int start()
{
   // Reading values
   // ...
      
   // Mark crosses
   MarkCrosses
   (
      Values,            // the fast buffer with values to check
      SmoothedValues,    // the slow buffer with values to check
      Crosses,           // the crosses buffer
      toCount - 1,       // start check index
      0,                 // final check index
      CROSS_ALL,         // use CROSS_UP for up crosses CROSS_DOWN for down crosses CROSS_ALL for all
      0);                // used empty value
   
   return(0);
}


3.2. Marca de nível

Algumas das oscilações com faixa de conjunto de valores limitada e estrita (RSI, Stochastic Oscillator, DeMarker, Money Flow Index, Williams' Percent Range) frequentemente precisam marcar as zonas e níveis. Por exemplo, as zonas plantas, as zonas de sobrecompra/sobrevenda, as zonas de tendência... Vamos tentar delinear o nível definido usando a pintura com cores diferentes.


3.2.1. Formalização de tarefa

É necessário marcar em um buffer separado as barras com os valores fora do nível definido.


3.2.2. Problemas

Não é simples como parece ser à primeira vista.

O primeiro problema é desenhar as barras, nas quais o nível definido cruza. Aqui está a solução.

//|                                 RSI_Cut_Levels_Sample.mq4 |

extern int RSIPeriod       = 9;
extern int AppliedPrice    = 0;

extern int HigherLevel     = 70;
extern int LowerLevel      = 30;

// buffers
double Higher[];           // Overbought
double Lower[];            // Oversold
double Values[];           // Values

int DigitsUsed = 5;
int EmptyValueUsed = 0;

int start()
{
   int toCount = Bars - IndicatorCounted();  
   
   // Reading values
   // ...
      
   // Mark levels - upper
   for (i = toCount - 1; i >=0; i--)
   {
      // check for empty values
      if (Values[i] == EMPTY_VALUE || Values[i] == EmptyValueUsed)
      {
         continue;
      }
      
      // empty current value
      Higher[i] = EMPTY_VALUE;
   
      // greater than high
      if (Values[i] >= HigherLevel)
      {
         Higher[i] = Values[i];
      }
   }
   
   // for the levels mark - the code is same
   // ...

   return(0);
}

Esse código resolve a tarefa definida, mas há um problema:



É difícil analisá-lo visualmente, porque o desenho do sinal começa do valor, que é maior (mais baixo) que o nível. É por isso que algumas das barras de sinal não podem ser analisadas, por causa das peculiaridades do desenho causadas por alterações rápidas de barras vizinhas.

A solução é marcar não apenas as barras mais altas (mais baixas) que o nível definido, mas também a barra formada antes das já marcadas e a barra próxima a elas. E é necessário marcá-las não com seus próprios valores, mas com os valores do nível.

O segundo problema aparece após a solução do primeiro - o buffer de sinal tem pseudo marcas de rebaixamentos de nível "falsos" como o resultado da compilação do algoritmo.

Isso significa que o preço estava fora do nível durante a formação da barra, entretanto, a barra final tem o valor dentro do nível. Devido a esse fato, podemos ter uma figura como essa:



O problema aparece apenas se usarmos um indicador nas cotas de tempo real. A solução é simples - verificamos duas barras (0 e 1) durante o processamento, as outras são verificadas apenas se necessário.

Depois disso, teremos a seguinte figura para RSI^:




3.2.3. Solução

Então, vamos escrever tudo em um código:

//|                                 RSI_Levels_Sample.mq4 |

extern int RSIPeriod       = 9;
extern int AppliedPrice    = 0;

extern int HigherLevel     = 70;
extern int LowerLevel      = 30;

// buffers
double Higher[];           // Overbought
double Lower[];            // Oversold
double Values[];           // Values

int DigitsUsed = 5;
int EmptyValueUsed = 0;

// looking at least two bars - 0 and 1.
int Depth = 2;

int start()
{
   int toCount = Bars - IndicatorCounted();  
 
   // Reading values
   // ...
   
   toCount = MathMax(toCount, Depth);
      
   // Marking levels - upper
   for (i = toCount - 1; i >=0; i--)
   {
      if (Values[i] == EMPTY_VALUE || Values[i] == EmptyValueUsed) continue;
      
      Higher[i] = EMPTY_VALUE;
   
      // greater than level
      if (Values[i] >= HigherLevel)
      {
         Higher[i] = Values[i];
      
         // if previous is lower
         if (Values[i + 1] < HigherLevel && Values[i + 1] != EmptyValueUsed)
         {
         // mark it also but with the level value
            Higher[i + 1] = HigherLevel;
         }
      }
      // if current lower
      else
      {
         // if previous is greater
         if (Values[i + 1] >= HigherLevel && Values[i + 1] != EMPTY_VALUE)
         {
            // mark it also but with the level value
            Higher[i] = HigherLevel;
         }
      }
   }
   
   // Mark levels - the code is the same
   // ...

   return(0);
}

3.2.4. Automatização

A solução do mesmo problema usando a biblioteca Indicator_Painting.

//|                                 RSI_Levels_Lib_Sample.mq4 |

#include <Indicator_Painting.mqh>

extern int RSIPeriod       = 9;
extern int AppliedPrice    = 0;

extern int HigherLevel     = 70;
extern int LowerLevel      = 30;

// buffers
double Higher[];           // Overbought
double Lower[];            // Oversold
double Values[];           // Values

int DigitsUsed = 5;
int EmptyValueUsed = 0;
int Depth = 2;

int start()
{
   int toCount = Bars - IndicatorCounted();  
   
   // Read values
   for (int i = toCount - 1; i >= 0; i--)
   {
      Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed);
   }
   
   // Mark levels - upper
   MarkLevel(Values, Higher, 0, toCount - 1, HigherLevel, GREATER_THAN, EmptyValueUsed);
   // Mark levels - lower
   MarkLevel(Values, Lower, 0, toCount - 1, LowerLevel, LESS_THAN, EmptyValueUsed);

   return(0);
}


3.3. Topos e fundos


Os pontos extremos (extremos) do indicador podem ser usados como sinais. Neste artigo, o termo "extremo" é usado em seu significado mais simples - se a barra tiver um valor maior (mais baixo) que os valores vizinhos, é considerada extrema.


3.3.1. Formalização de tarefa

É necessário marcar as barras com valores extremos em um buffer separado.



3.3.2. Problemas

Vamos considerar alguns exemplos:



Aqui, valores extremos evidentes são marcados com linhas vermelhas:


if ((x1 > x2 && x3 > x2) || (x1 < x2 && x3 < x2))
{
    // x2 is extremal
}

Ou podemos simplificá-lo:


if ((x1 - x2)*(x2 - x3) < 0)
{
    // x2 is extremal
}

Mas vamos considerar o caso:



Os pontos marcados têm os mesmos valores. O ponto azul é um extremo. Não será fácil defini-lo. E no seguinte caso:



não há extremo, assumimos que há uma dobra.

A solução de tais casos é encontrar a segunda extremidade, como nos casos de cruzamento.



3.3.3. Solução

Aqui está o código:

//|                                 RSI_Extremums_Sample.mq4 |

extern int RSIPeriod       = 9;
extern int AppliedPrice    = 0;

// buffers
double Values[];           // Values
double Extremums[];        // Extremums

int DigitsUsed = 5;
int EmptyValueUsed = 0;

int start()
{
   int toCount = Bars - IndicatorCounted();  
   for (int i = toCount - 1; i >=0; i--)
   {
      Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed);
   }
      
   for (i = toCount - 1; i >=0; i--)
   {
      // check the values relative to the current index.
      if (i + 2 >= Bars)
      {
         continue;
      }

      // check for empty values, if there are, it is not necessary to check
      if (
            Values[i]      == EmptyValueUsed || 
            Values[i + 1]  == EmptyValueUsed ||
            Values[i + 2]  == EmptyValueUsed
      )
      {
         continue;
      }
      
      // fill the current value of the mark buffer
      Extremums[i + 1] = EMPTY_VALUE;
      
      // cross condition - the simple case
      if ((Values[i] - Values[i + 1])*(Values[i + 1] - Values[i + 2]) < 0)
      {
         // we have found the cross
         Extremums[i + 1] = Values[i + 1];
         continue;
      }
      
      // the cross condition in a complicated case - 
      // when top contain several bars with the same value
      if (Values[i + 1] == Values[i + 2] && Values[i] != Values[i + 1])
      {
         // there is possible extremum - to check it
         // we have to find the second end

         int index = i + 2;
         bool found = false;
         while (index < Bars && Values[index] != EmptyValueUsed && Values[index] != EMPTY_VALUE)
         {
            if (Values[i + 2] != Values[index])
            {
               // ok, we have found the second end
               found = true;
               break;
            }
            
            index++;
         }

         if (!found)
         {
            // we are at the end of the history or have an empty value
            // for the both cases we assume that there is no extremum
            continue;
         }
         
         // checking the ends for a cross
         if ((Values[i] - Values[i + 1])*(Values[i + 1] - Values[index]) < 0)
         {
            // there is a cross
            Extremums[i + 1] = Values[i + 1];
         }  // else -- there is a bend point, do not mark it
      }
   }
   
   return(0);
}

3.3.4. Automatização

A mesma solução da tarefa usando a biblioteca Indicator_Painting.

//|                                 RSI_Extremums_Lib_Sample.mq4 |

#include <Indicator_Painting.mqh>

extern int RSIPeriod       = 9;
extern int AppliedPrice    = 0;

// buffers
double Values[];           // Values
double Extremums[];        // Extremal points

int DigitsUsed = 5;
int EmptyValueUsed = 0;

int start()
{
   int toCount = Bars - IndicatorCounted();  

   for (int i = toCount - 1; i >=0; i--)
   {
      Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed);
   }
   
   MarkExtremums(Values, Extremums, toCount - 1, 0, DIR_ALL, EmptyValueUsed);
   
   return(0);
}


3.4. Como colorir por direção

O método de visualização é usado em alguns indicadores padrão e também pode ser útil.



3.4.1. Formalização de tarefa

É necessário pintar alguns conjuntos de valores de indicadores (por exemplo, os conjuntos enquanto ascendem e descendem) com cores diferentes. A direção quer dizer o caso mais simples - se o valor atual é maior que o anterior, temos a direção ascendente, caso contrário, assumimos uma direção descendente.



3.4.2. Problemas

Vamos começar pelo recurso. Assume-se que temos um buffer de dados base e esse buffer é plotado. Caso contrário, faremos isso, porque para a pintura direcional personalizada, precisamos de pelo menos 2 cores, e pelo menos dois buffers. Agora, o recurso. Se desenharmos uma das direções sobre o buffer base, não será necessário pintar a outra direção - a veremos nas peças não pintadas do buffer base.

O buffer base:


Aqui está o buffer base com a direção ascendente plotada:

É por isso que depois consideraremos um único gráfico de direção, ascendeste, por exemplo. Vamos considerar os problemas que podem ocorrer.

A implementação ingênua do recurso é a seguinte:


//|                                 RSI_Simple_Directions_Sample.mq4 |

extern int RSIPeriod       = 9;
extern int AppliedPrice    = 0;

// buffers
double Values[];           // Values
double Growing[];          // Growing buffer

int DigitsUsed = 5;
int EmptyValueUsed = 0;

int start()
{
   int toCount = Bars - IndicatorCounted();

   // Reading values
   for (int i = toCount - 1; i >=0; i--)
   {
      Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed);
   }
   
   // Mark the growing levels - we will get the falling levels as a result
   for (i = toCount - 1; i >=0; i--)
   {

      // check for empty value, if there are, the further check is not necessary
      // ...
      
      // filling the current values with empty values
      Growing[i] = EMPTY_VALUE;
      
      // if growing
      if (Values[i] > Values[i + 1])
      {
         Growing[i] = Values[i];
         Growing[i + 1] = Values[i + 1];
      }
   }

   return(0);
}

Compilar, anexar aos gráficos e... ver o resultado de execução do código:



Há alguns problemas, eles estão marcados com pontos. Vamos considerar por que ele está assim. Ao pintar uma direção, temos um efeito que deixa valores vazios (EMPTY_VALUE) em outras partes.

Vamos considerar o seguinte caso:


Os dados de buffer adicional que devem ter valores não vazios são marcados por pontos pretos. Para evitar a plotagem de linha reta entre os pontos (usando o estilo DRAW_LINE) é necessário ter ao menos um valor não vazio entre eles. Toda a faixa plotada não tem valores vazios, e é por isso que o buffer base é plotado somente em pedaços de "dente de serra".


A solução deste problema não é evidente - por exemplo, suavizando ou usando algumas das condições adicionais o torna muito mais complicado. Como resultado, podemos ter várias barras pintadas novamente ou outra coisa difícil.



A solução é usar dois buffers adicionais para isso. Depois, é possível alternar os pedaços pintados - assim, teremos os valores vazios necessários em cada um dos buffers.

Vamos atribuir as cores diferentes aos buffers adicionais e ver o resultado:


O problema principal é resolvido, mas há outro problema pequeno com a barra zero. Ela é desenhada novamente em todas as vezes e, em alguns casos, é necessário excluir o gráfico de direção ascendente, se a direção foi alterada.


Vamos considerar a implementação.

3.4.3. Solução


//|                                 RSI_Directions_Sample.mq4 |

extern int RSIPeriod       = 9;
extern int AppliedPrice    = 0;

// buffers
double Values[];           // Values
double Growing1[];         // First growing buffer
double Growing2[];         // Second growing buffer

int DigitsUsed = 5;
int EmptyValueUsed = 0;

int start()
{
   int toCount = Bars - IndicatorCounted();

   // Reading values
   for (int i = toCount - 1; i >=0; i--)
   {
      Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed);
   }
   
   // Mark for the growing levels - we will get the falling levels as a resut
   for (i = toCount - 1; i >=0; i--)
   {
      // check of an empty values
      // ...
      
      // assume that the current values are empty
      Growing1[i] = EMPTY_VALUE;
      Growing2[i] = EMPTY_VALUE;
      
      // if it growing
      if (Values[i] > Values[i + 1])
      {
         // if it growing on the previous bar
         if (Values[i + 1] > Values[i + 2])
         {
            // writing to the current growing buffer
            if (Growing1[i + 1] != EMPTY_VALUE) Growing1[i] = Values[i];
            else                                Growing2[i] = Values[i];
         }
         // if the previous bar was not increasing
         else
         {
            // write to the buffer which it was not used the last 2 bars
            // we must have at least one such bar

            if (Growing2[i + 2] == EMPTY_VALUE) 
            {
               Growing2[i] = Values[i];
               Growing2[i + 1] = Values[i + 1];
            }
            else
            {
               Growing1[i] = Values[i];
               Growing1[i + 1] = Values[i + 1];
            }
         }
      }
      // if the last value does not grow, remove it
      else if (i == 0)
      {
         if (Growing1[i + 1] != EMPTY_VALUE && Growing1[i + 2] == EMPTY_VALUE)
         {
            Growing1[i + 1] = EMPTY_VALUE;
         }

         if (Growing2[i + 1] != EMPTY_VALUE && Growing2[i + 2] == EMPTY_VALUE)
         {
            Growing2[i + 1] = EMPTY_VALUE;
         }
      }
   }

   return(0);
}


3.4.4. Automatização

A mesma solução da tarefa usando a biblioteca Indicator_Painting.

Na biblioteca, também há uma implementação parecida para a direção descendente.


//|                                 RSI_Directions_Lib_Sample.mq4 |

#include <Indicator_Painting.mqh>

extern int RSIPeriod       = 9;
extern int AppliedPrice    = 0;

// buffers
double Values[];           // Values
double Growing1[];         // First growing buffer
double Growing2[];         // Second growing buffer

int DigitsUsed = 5;
int EmptyValueUsed = 0;

int start()
{
   int toCount = Bars - IndicatorCounted();

   // Reading values
   for (int i = toCount - 1; i >=0; i--)
   {
      Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed);
   }
   
   // Mark the growing levels - we will get the falling levels automatically
   MarkGrowing(Values, Growing1, Growing2, toCount - 1, 0, EmptyValueUsed);
   
   return(0);
}


4. A biblioteca Indicator_Painting

Como resultado de todo o trabalho concluído, há a biblioteca Indicator_Painting.

Ela foi projetada especialmente para a automatização das operações descritas, com algumas adições.

Aqui vai uma lista das funções disponíveis:


// ====================================================
// Mark for tops and bottoms
// ====================================================
void MarkExtremums( 
      double values[],        // Indicator values
      double& extremums[],    // Buffer for extremums
      int startIndex,         // Start index for check (it included) 
      int endIndex,           // End index for check (it included)
      int direction,          // DIR_TOP for tops, DIR_BOTTOM for bottoms, DIR_ALL for tops an bottoms
      double emptyValueUsed); // The value used for "empty" mark
      
      
// ====================================================
// Mark for crosses
// ====================================================
void MarkCrosses( 
      double values1[],       // Values of the first indicator
      double values2[],       // Values of the second indicator
      double& crosses[],      // Buffer for their crosses
      int startIndex,         // Start index for check (it included)
      int endIndex,           // End index for check (it included)
      int direction,          // CROSS_UP for up crosses,CROSS_DOWN for down crosses, CROSS_ALL for all crosses
      double emptyValueUsed); // The value used for "empty" mark
      
      
// ====================================================
// Mark for level crosses
// ====================================================
void MarkLevelCrosses( 
      double values[],        // Values of the indicator
      double level,           // Level value for a cross check
      double& crosses[],      // Buffer for the crosses
      int startIndex,         // Start index for check (it included)
      int endIndex,           // End index for check (it included)
      int direction,          // CROSS_UP for up crosses,CROSS_DOWN for down crosses, CROSS_ALL for all crosses
      double emptyValueUsed); // The value used for "empty" mark
      
      
// ====================================================
// Mark for levels
// ====================================================
void MarkLevel( 
      double values[],        // Values of the indicator
      double& level[],        // Buffer for the crosses
      int startIndex,         // Start index for check (it included)
      int endIndex,           // End index for check (it included)
      double levelValue,      // Level value
      int condition,          // Mark condition (LESS_THAN = -1, GREATER_THAN = 1)
      double emptyValueUsed); // The value used for "empty" mark
      
// ====================================================
// Mark for dynamic levels
// ====================================================
void MarkDynamicLevel( 
      double values[],        // Values of the indicator
      double dynamicLevel[],  // Dynamical level values for check
      double& level[],        // Buffer for the crosses
      int startIndex,         // Start index for check (it included)
      int endIndex,           // End index for check (it included)
      int condition,          // меньше (LESS_THAN = -1) или больше уровня (GREATER_THAN = 1)
      double emptyValueUsed); // The value used for "empty" mark
      
// ====================================================
// Mark for direction (upward)
// ====================================================
void MarkGrowing( 
      double values[],        // Values of the indicator
      double& growing1[],     // The first buffer to mark the direction
      double& growing2[],     // The second buffer to mark the direction
      int startIndex,         // Start index for check (it included)
      int endIndex,           // End index for check (it included)
      double emptyValueUsed); // The value used for "empty" mark

// ====================================================
// Mark for direction (downward)
// ====================================================
void MarkReducing( 
      double values[],        // Values of the indicator
      double& reducing1[],    // The first buffer to mark the direction
      double& reducing2[],    // The second buffer to mark the direction
      int startIndex,         // Start index for check (it included)
      int endIndex,           // End index for check (it included)
      double emptyValueUsed); // The value used for "empty" mark

Aqui estão alguns exemplos do uso da biblioteca:





Para usar a biblioteca, faça o seguinte:

1. Copie o arquivo "Indicator_Painting.mq4" para a pasta "experts/libraries".

2. Copie o arquivo "Indicator_Painting.mqh" para a pasta "experts/include".

3. Adicione a seguinte string ao código de indicador:

#include <Indicator_Painting.mqh>

Agora é possível usar todas as funções da biblioteca. Veja o arquivo "Indicator_Painting.mqh" para saber mais detalhes.

É possível encontrar os exemplos nos arquivos anexados ao artigo.


Conclusão

O autor espera que este artigo ajuda e simplifique o trabalho de algumas pessoas. O autor considera que o objetivo foi alcançado.


Agradecimentos

O autor gostaria de agradecer ao sr. Viktor Rustamov (granit77) pela sugestão da tarefa, ajuda e pelos comentários para melhorar o artigo.

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

Sobreposição e interferência de valores mobiliários financeiros Sobreposição e interferência de valores mobiliários financeiros
Quanto mais fatores influenciarem o comportamento de um par de moedas, mais difícil será avaliar seu comportamento e criar previsões futuras. Assim, se conseguirmos extrair componentes de um par de moeda, valores de uma moeda nacional que mudam com o tempo, poderemos delimitar consideravelmente a liberdade do movimento da moeda nacional em comparação ao par de moeda com esta moeda, assim como os fatores que influenciam seu comportamento. Como resultado, aumentaríamos a precisão da estimativa do seu comportamento e de previsões futuras. Como podemos fazer isso?
Alerta e comentários para indicadores externos Alerta e comentários para indicadores externos
No trabalho prático, um trader pode enfrentar a seguinte situação: é necessário ficar "alerta" ou uma mensagem de texto aparecerá em uma tela (em uma janela de gráfico) indicando um sinal de um indicador. O artigo contém um exemplo de exibição de informações sobre objetos gráficos criados por um indicador externo.
Ação de preço. Como automatizar a estratégia de negociação de padrão de engolfo Ação de preço. Como automatizar a estratégia de negociação de padrão de engolfo
Esse artigo descreve um processo de criação de um Expert Advisor para MetaTrader 4 baseado no padrão de engolfo, assim como no princípio de reconhecimento de padrão, nas regras de definição de ordens pendentes e ordens de parada. Os resultados de teste e otimização são fornecidos para sua informação.
Interação entre o MеtaTrader 4 e a máquina MATLAB (Máquina virtual MATLAB) Interação entre o MеtaTrader 4 e a máquina MATLAB (Máquina virtual MATLAB)
O artigo contém considerações sobre a criação da biblioteca DLL - empacotador que permitirá a interação do pacote matemático da área de trabalho MetaTrader 4 e do MATLAB. Ele descreve as "armadilhas" e formas de superá-las. O artigo destina-se a programadores de C/C++ preparados que usem o compilador Borland C++ Builder 6.