Criando um indicador com buffers de indicador múltiplos para iniciantes
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
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso