English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Criando um indicador com buffers de indicador múltiplos para iniciantes

Criando um indicador com buffers de indicador múltiplos para iniciantes

MetaTrader 5Exemplos | 2 janeiro 2014, 07:04
6 512 0
Nikolay Kositsin
Nikolay Kositsin

Introdução

Nos meus artigos anteriores "Indicadores personalizados no MQL5 para iniciantes" e "Implementação prática de filtros digitais no MQL5 para principiantes" foquei nos detalhes sobre a estrutura do indicador com um buffer de indicador.

Obviamente, tal método pode ser largamente aplicado na escrita de indicadores personalizados mas na vida real você dificilmente pode se limitar ao seu uso e assim é hora de abordar métodos mais complexos de construir um código do indicador. Felizmente, as capacidades do MQL5 são realmente inesgotáveis e são limitadas somente pela memória RAM dos seus PCs.


O indicador Aroon como exemplo da duplicação do código

A fórmula deste indicador contém dois componentes: os indicadores de tendência de valorização e e baixa que são diagramados em uma janela de gráfico separada.

BULLS = (1 - (bar - SHIFT(MAX(HIGH(), AroonPeriod)))/AroonPeriod) * 100
BEARS = (1 - (bar - SHIFT(MIN (LOW (), AroonPeriod)))/AroonPeriod) * 100

onde:

  • BULLS - a força do especulador altista;
  • BEARS - a força do especulador baixista;
  • SHIFT() - função de determinação da posição do índice da barra;
  • MAX() - função de busca pelo período máximo de acima do AroonPeriod;
  • MIN() - função de busca pelo período mínimo de acima do AroonPeriod;
  • HIGH() e LOW() - bancos de dados de preços relevantes;

Diretamente a partir das fórmulas do indicador podemos concluir que para construir um indicador, temos que possuir somente dois buffers do indicador, a estrutura do indicador irá diferir muito pouco da estrutura do SMA_1.mq5, considerada no artigo anterior.

Praticamente, é simplesmente o mesmo código duplicado com números diferentes de buffers do indicador. Então vamos abrir o código deste indicador no MetaEditor e salvar como Aroon.mq5. Agora, nas primeiras 11 linhas do código, que relatam o direito autoral e seu número de versão iremos substituir somente o nome do indicador:

//+------------------------------------------------------------------+
//|                                                        Aroon.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//---- copyright
#property copyright "2010, MetaQuotes Software Corp."
//---- link to the author's site
#property link      "http://www.mql5.com"
//---- version number
#property version   "1.00"

A seguir, na 12ª linha do código precisamos modificar a diagramação do indicador da janela de gráfico básica para a janela separada:

//---- plot indicator in the separate window
#property indicator_separate_window

Já que esse indicador tem uma gama completamente diferente de valores a sua diagramação será feita em uma janela separada.

Depois disso, nas próximas 4 linhas do código (as propriedades gerais do indicador) nós mudamos o número de buffers utilizados do indicador para dois:

//---- two buffers are used
#property indicator_buffers 2
//---- two plots are used 
#property indicator_plots   2

As próximas 10 linhas do código são relacionadas à diagramação do indicador a partir de um buffer do indicador específico, o seu rótulo deve ser duplicado, depois disso, devemos substituir todos os índices de 1 a 2. Devemos também modificar todos os rótulos dos buffers do indicador:

//+----------------------------------------------+
//| bullish strength indicator parameters        |
//+----------------------------------------------+
//---- drawing style = line
#property indicator_type1   DRAW_LINE
//---- drawing color = Lime
#property indicator_color1  Lime
//---- line style = solid line
#property indicator_style1  STYLE_SOLID
//---- line width = 1
#property indicator_width1  1
//---- label of the BullsAroon indicator
#property indicator_label1  "BullsAroon"
//+----------------------------------------------+
//|  bearish strength indicator parameters       |
//+----------------------------------------------+
//---- drawing style = line
#property indicator_type2   DRAW_LINE
//---- drawing color = Red
#property indicator_color2  Red
//---- line style = solid line
#property indicator_style2  STYLE_SOLID
//---- line width = 1
#property indicator_width2  1
//---- label of the BearsAroon indicator
#property indicator_label2  "BearsAroon"

Este indicador usa três níveis horizontais com valores de 30, 50 e 70.

Para fazer a diagramação desses níveis precisamos adicionar mais cinco linhas de código ao código do indicador.

//+----------------------------------------------+
//| Horizontal levels                            |
//+----------------------------------------------+
#property indicator_level1 70.0
#property indicator_level2 50.0
#property indicator_level3 30.0
#property indicator_levelcolor Gray
#property indicator_levelstyle STYLE_DASHDOTDOT

Para os parâmetros de entrada do indicador, em comparação com o indicador anterior, tudo permanece igual, a não ser por pequenas modificações nos títulos.

//+----------------------------------------------+
//| Indicator input parameters                   |
//+----------------------------------------------+
input int AroonPeriod = 9; // Period 
input int AroonShift = 0;  // Horizontal shift of the indicator in bars 

Agora, entretanto, haverá dois bancos de dados, que serão usados como buffers do indicador e eles terão nomes apropriados:

//--- declare the dynamic arrays used further as indicator buffers
double BullsAroonBuffer[];
double BearsAroonBuffer[]; 

Continuamos de uma maneira completamente igual com o código da função OnInit().

Primeiramente, modificamos as linhas de código do buffer zeroth:

//--- set BullsAroonBuffer dynamic array as indicator buffer 
SetIndexBuffer(0, BullsAroonBuffer, INDICATOR_DATA);
//--- horizontal shift (AroonShift) of the indicator 1
PlotIndexSetInteger(0, PLOT_SHIFT, AroonShift);
//--- plot draw begin (AroonPeriod) of the indicator 1
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, AroonPeriod);
//--- label shown in DataWindow
PlotIndexSetString(0, PLOT_LABEL, "BearsAroon"); 

Depois disso, copie o código inteiro da área de transferência do Windows e cole logo depois do mesmo código.

Então, no código colado, modificamos o número do buffer do indicador de 0 a 1, modificamos o nome do banco de dados do indicador e rótulo do indicador:

//--- set BearsAroonBuffer dynamic array as indicator buffer 
SetIndexBuffer(1, BearsAroonBuffer, INDICATOR_DATA); 
//--- horizontal shift (AroonShift) of the indicator 2 
PlotIndexSetInteger(1, PLOT_SHIFT, AroonShift); 
//--- plot draw begin (AroonPeriod) of the indicator 2 
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, AroonPeriod); 
//--- label shown in DataWindow 
PlotIndexSetString(1, PLOT_LABEL, "BullsAroon");  

O apelido do indicador também passou por pequenas modificações:

//--- initialization of the variable for a short indicator name
string shortname;
StringConcatenate(shortname, "Aroon(", AroonPeriod, ", ", AroonShift, ")"); 

Agora, vamos considerar a precisão da diagramação do indicador. O alcance real do indicador é de 0 a 100 e este alcance é mostrado o tempo todo.

Nesta situação, é bem possível usar somente os valores de números inteiros do indicador, diagramados no gráfico. Por essa razão usamos 0 para os números depois do ponto decimal para a diagramação do indicador:

//--- set accuracy of drawing of indicator values
IndicatorSetInteger(INDICATOR_DIGITS, 0);

No indicador SMA_1.mq5 usamos a primeira forma de chamado da função OnCalculate().

Ela não é adequada para o indicador Aroon, devido à sua falta de bancos de dados de preços alto[] e baixo[]. Estes bancos de dados estão disponíveis na segunda forma de chamado desta função. E, então, é necessário modificar o cabeçalho da função:

int OnCalculate( 
                const int rates_total,    // total bars on the current tick
                const int prev_calculated,// total bars on the previous tick
                const datetime& time[],
                const double& open[],    
                const double& high[],     // price array of the maximum prices for the indicator calculations
                const double& low[],      // price array of the minimum prices for the indicator calculations
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[]
              )

Depois desta modificação o uso do parâmetro de início perdeu todo o sentido então ele deve ser removido do código!

O código para calcular os limites de modificações de variáveis do ciclo de operação, a verificação de dados para a suficiência do cálculo permaneceram praticamente sem modificação.

//--- checking the number of bars
if (rates_total < AroonPeriod - 1) return(0);
   
//--- declare the local variables 
int first, bar;
double BULLS, BEARS; 

//--- calculation of the first (staring index) for the main loop
if (prev_calculated == 0)          // checking for the first call of the OnCalculate function
    first = AroonPeriod - 1;       // starting index for calculating all of the bars 
else first = prev_calculated - 1;  // starting index for calculating new bars

Entretanto, certos problemas aparecem com os algoritmos para o cálculo dos valores do indicador. O problema é que o MQL5 não tem as funções integradas para determinar os índices do máximo e do mínimo, para o período a partir da barra atual na direção do índice decrescente.

Uma saída para essa situação é escrever estas funções nós mesmos. Felizmente, tais funções já existem no indicador ZIGZAG.mq5 nos indicadores personalizados localizados na pasta "MetaTrader5\MQL5\Indicators\Examples".

A saída mais fácil é selecionar o código dessas funções no indicador ZIGZAG.mq5, copiá-las para a área de transferência do Windows e as colar em nosso código, por exemplo, logo depois da descrição da função OnInit() no nível global:

//+------------------------------------------------------------------+
//|  searching index of the highest bar                              |
//+------------------------------------------------------------------+
int iHighest(const double &array[], // array for searching for the index of the maximum element
             int count,            // number of the elements in the array (in the decreasing order), 
             int startPos          // starting index
             )                     
  {
//---+
   int index = startPos;
   
   //---- checking the starting index
   if (startPos < 0)
     {
      Print("Incorrect value in the function iHighest, startPos = ", startPos);
      return (0);
     } 
   //---- checking the startPos values
   if (startPos - count < 0) count = startPos;
    
   double max = array[startPos];
   
   //---- index search
   for(int i = startPos; i > startPos - count; i--)
     {
      if(array[i] > max)
        {
         index = i;
         max = array[i];
        }
     }
//---+ return of the index of the largest bar
   return(index);
  }
//+------------------------------------------------------------------+
//|  searching index of the lowest bar                               |
//+------------------------------------------------------------------+
int iLowest(
            const double &array[], // array for searching for the index of the maximum element
            int count,            // number of the elements in the array (in the decreasing order),
            int startPos          // starting index
            ) 
{
//---+
   int index = startPos;
   
   //--- checking the stating index
   if (startPos < 0)
     {
      Print("Incorrect value in the iLowest function, startPos = ",startPos);
      return(0);
     }
     
   //--- checking the startPos value
   if (startPos - count < 0) count = startPos;
    
   double min = array[startPos];
   
   //--- index search
   for(int i = startPos; i > startPos - count; i--)
     {
      if (array[i] < min)
        {
         index = i;
         min = array[i];
        }
     }
//---+ return of the index of the smallest bar
   return(index);
  }

Depois disso, o código da função OnCalculate() se parecerá desta maneira:

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate( const int rates_total,    // total number of bars on the current tick
               const int prev_calculated,// number of calculated bars on the previous tick
               const datetime& time[],
               const double& open[],    
               const double& high[],     // price array for the maximum price for the indicator calculation
               const double& low[],      // price array for the minimum price for the indicator calculation
               const double& close[],
               const long& tick_volume[],
               const long& volume[],
               const int& spread[]
             )
  {
//---+   
   //--- checking the number of bars
   if (rates_total < AroonPeriod - 1)
    return(0);
   
   //--- declare the local variables 
   int first, bar;
   double BULLS, BEARS;
   
   //--- calculation of the starting bar number
   if (prev_calculated == 0)  // checking for the first start of the indicator calculation
     first = AroonPeriod - 1; // starting number for the calculation of all of the bars

   else first = prev_calculated - 1; // starting number for the calculation of new bars

   //--- main loop
   for(bar = first; bar < rates_total; bar++)
    {
     //--- calculation of values
     BULLS = 100 - (bar - iHighest(high, AroonPeriod, bar) + 0.5) * 100 / AroonPeriod;
     BEARS = 100 - (bar - iLowest (low,  AroonPeriod, bar) + 0.5) * 100 / AroonPeriod;

     //--- filling the indicator buffers with the calculated values 
     BullsAroonBuffer[bar] = BULLS;
     BearsAroonBuffer[bar] = BEARS;
    }
//---+     
   return(rates_total);
  }
//+------------------------------------------------------------------+

Para a simetria dos eixos, eu corrigi levemente o código, adicionando o movimento vertical do indicador comparado com o original usando o valor de 0,5.

Aqui estão os resultados do trabalho deste indicador no gráfico:

Para achar a posição do elemento com os valores máximo ou mínimo em uma distância não maior que o AroonPeriod a partir da barra atual podemos usar as funções integradas ArrayMaximum() e ArrayMinimum() do MQL5, que também buscam pelos extremos, mas essas funções executam a busca usando a ordem crescente.

Entretanto, a busca deve ser feita na ordem decrescente dos índices. Para este caso a solução mais simples é modificar a direção de indexação no indicador e buffers de preços usando a função ArraySetAsSeries().

Mas também precisamos modificar a direção da ordem da barra no loop de cálculo e modificar o algoritmo do cálculo da primeira variável.

Neste caso a função resultante OnCalculate() será assim:

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(
                const int rates_total,    // total number of bars on the current tick
                const int prev_calculated,// number of calculated bars on the previous tick
                const datetime& time[],
                const double& open[],    
                const double& high[],     // price array for the maximum price for the indicator calculation
                const double& low[],      // price array for the minimum price for the indicator calculation
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[]
              )
  {
//---+   
   //--- checking the number of bars
   if (rates_total < AroonPeriod - 1)
    return(0);
    
   //--- set indexation as timeseries
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low,  true);
   ArraySetAsSeries(BullsAroonBuffer, true);
   ArraySetAsSeries(BearsAroonBuffer, true);
   
   //--- declare the local variables 
   int limit, bar;
   double BULLS, BEARS;
   
   //--- calculation of the starting bar index
   if (prev_calculated == 0)                      // check for the first call of OnCalculate function
       limit = rates_total - AroonPeriod - 1;  // starting index for the calculation of all of the bars
   else limit = rates_total - prev_calculated; // starting index for the calculation of new bars
   
   //--- main loop
   for(bar = limit; bar >= 0; bar--)
    {
     //--- calculation of the indicator values
     BULLS = 100 + (bar - ArrayMaximum(high, bar, AroonPeriod) - 0.5) * 100 / AroonPeriod;
     BEARS = 100 + (bar - ArrayMinimum(low,  bar, AroonPeriod) - 0.5) * 100 / AroonPeriod;

     //--- filling the indicator buffers with the calculated values 
     BullsAroonBuffer[bar] = BULLS;
     BearsAroonBuffer[bar] = BEARS;
    }
//----+     
   return(rates_total);
  }
//+------------------------------------------------------------------+

Eu modifiquei o nome da variável "primeiro" para "limite" que é mais apropriado neste caso.

Neste caso o código do loop principal é similar à maneira com que era feito no MQL4. Então, este estilo de escrita da função OnCalculate() pode ser usado para a conversão dos indicadores do MQL4 para o MQL5 com modificações mínimas no código.


Conclusão

Então, está pronto! O indicador está escrito e ainda em duas versões.

Para um caso de justiça, uma maneira conservadora e esperta em resolver tais problemas as soluções se tornam um pouco mais complicadas que a construção de algum brinquedo usando o construtor lego para crianças.

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

Arquivos anexados |
aroon.mq5 (8.04 KB)
aroons.mq5 (6.28 KB)
sma_1.mq5 (3.92 KB)
zigzag.mq5 (9.17 KB)
MQL para "Principiantes": como projetar e construir classes de objeto MQL para "Principiantes": como projetar e construir classes de objeto
Criando um programa de amostra de design visual, demonstramos como projetar e construir classes no MQL5. O artigo é escrito para programadores iniciantes, que estejam trabalhando em aplicações MT5. Propomos uma tecnologia simples e de fácil compreensão para criação de classes, sem a necessidade de se aprofundar na teoria de programação orientada a objeto.
Interação entre o MetaTrader 5 e MATLAB Interação entre o MetaTrader 5 e MATLAB
Este artigo cobre os detalhes da interação entre o MetaTrader 5 e o pacote matemático MatLab. Ele mostra o mecanismo da conversão de dados, o processo de desenvolvimento de uma biblioteca universal para interagir com o desktop MatLab. Ele também cobre o uso do DLL gerado pelo ambiente MatLab. Este artigo é destinado a leitores experientes que conhecem C++ e MQL5.
Algoritmos genéticos - é fácil! Algoritmos genéticos - é fácil!
Neste artigo o autor fala sobre cálculos evolutivos com o uso de um algoritmo genético desenvolvido pessoalmente. Ele demonstra o funcionamento do algoritmo, usando exemplos e fornece recomendações práticas para seu uso.
Criando um indicador com opções de controle gráfico Criando um indicador com opções de controle gráfico
Aqueles que são familiares com os sentimentos do mercado, conhecem o indicador MACD (seu nome completo é convergência/divergência de média móvel) - a poderosa ferramenta para analisar o movimento de preço, usada por negociantes desde os primeiros momentos do aparecimento dos métodos de análise computacionais. Neste artigo, consideraremos possíveis modificações do MACD e o implementaremos no indicador com a possibilidade de mudar graficamente entre as modificações.