
Crear un Indicador con Buffers de Indicador Múltiples para Principiantes
Introducción
En mis anteriores artículos "Custom Indicators in MQL5 for Newbies" ("Indicadores MQL5 Personalizados para Principiantes") y "Practical Implementation of Digital Filers in MQL5 for Beginners" ("Implementación Práctica de Archivadores Digitales en MQL5 para Principiantes") me centré en detalles de la estructura del indicador con un buffer del indicador.
Evidentemente, un método así se puede aplicar ampliamente para escribir indicadores personalizados, pero en la vida real apenas nos podemos limitar a su uso, y por ello es el momento de ver métodos más complejos de construcción de códigos de indicador. Afortunadamente, la capacidad del MQL5 es realmente inagotable, y solo está limitada por la memoria RAM de nuestros ordenadores.
El Indicador Aroon como ejemplo de doblaje de código
La fórmula de este indicador contiene dos componentes: los indicadores "bullish" y "bearish", que están representados en una ventana de gráfico separada:
BULLS = (1 - (bar - SHIFT(MAX(HIGH(), AroonPeriod)))/AroonPeriod) * 100
BEARS = (1 - (bar - SHIFT(MIN (LOW (), AroonPeriod)))/AroonPeriod) * 100
donde:
- BULLS - la fuerza del Bull;
- BEARS - la fuerza del Bear;
- SHIFT() - función para determinar la posición de índice de la barra;
- MAX() - función de búsqueda del máximo sobre el período AroonPeriod;
- MIN() - función de búsqueda del mínimo sobre el período AroonPeriod;
- HIGH() y LOW() - los arrays de precio relevantes;
Ya desde las fórmulas del indicador, podemos concluir que para construir un indicador, debemos tener solo dos buffers de indicador. La estructura del indicador será muy diferente de la estructura del SMA_1.mq5, que se trató en el artículo anterior.
Prácticamente se trata del mismo código duplicado, con números diferentes de indicadores de buffer. De modo que abramos el código de este indicador en el MetaEditor, y guardémoslo como Aroon.mq5. Ahora, en las primeras 11 líneas del código, que se refieren al copyright y a su número de versión, sustituiremos solo el nombre del 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 continuación, en la línea 12 de código, debemos cambiar la representación del indicador de la ventana de gráfico básica a la ventana separada:
//---- plot indicator in the separate window #property indicator_separate_window
Puesto que este indicador tiene una serie de valores completamente diferente, su representación se hará en una ventana separada.
A continuación, en las siguientes 4 líneas de código (las propiedades generales del indicador), cambiaremos el número de los buffers de indicador usados a dos:
//---- two buffers are used #property indicator_buffers 2 //---- two plots are used #property indicator_plots 2
Las siguientes 10 líneas de código se refieren a la representación del indicador desde un buffer del indicador específico; su etiqueta debe duplicarse, tras lo cual sustituiremos todos los índices de 1 a 2. Asimismo, debemos cambiar todas las etiquetas de los buffers de 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 tres niveles horizontales con los valores de 30, 50 y 70.
Para representar estos niveles, debemos añadir cinco líneas de código adicionales al código del 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 los parámetros de entrada del indicador, en comparación con el indicador anterior, todo sigue igual, a excepción de cambios menores en los títulos:
//+----------------------------------------------+ //| Indicator input parameters | //+----------------------------------------------+ input int AroonPeriod = 9; // Period input int AroonShift = 0; // Horizontal shift of the indicator in bars
Sin embargo, ahora habrá dos arrays, que se usarán como buffers de indicador y tendrán sus nombres correspondientes:
//--- declare the dynamic arrays used further as indicator buffers double BullsAroonBuffer[]; double BearsAroonBuffer[];
Procederemos exactamente igual con el código de la función OnInit().
Primero, modificaremos las líneas de código del buffer cero:
//--- 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");
Después, copiaremos el código entero del bloc de notas de Windows y lo pegaremos tras el mismo código.
A continuación, en el código pegado, cambiamos el número del buffer del indicador de 0 a 1, cambiamos el nombre del array del indicador y la etiqueta del 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");
El nombre corto del indicador también ha sufrido algunos cambios menores:
//--- initialization of the variable for a short indicator name string shortname; StringConcatenate(shortname, "Aroon(", AroonPeriod, ", ", AroonShift, ")");
Ahora consideremos la precisión de la representación del indicador. El alcance real del indicador es de 0 a 100, y este alcance se muestra en todo momento.
En esta situación, es posible usar solo los valores íntegros del indicador representados en el gráfico. Por este motivo, usamos 0 para los números después de la coma decimal para la representación del indicador:
//--- set accuracy of drawing of indicator values IndicatorSetInteger(INDICATOR_DIGITS, 0);
En el indicador SMA_1.mq5, usamos la primera forma de llamada de función OnCalculate().
No es conveniente para el Indicador Aroon a causa de su falta de arrays de precio high[] y low[]. Estos arrays están disponibles en la segunda forma de llamada de esta función. Por tanto, es necesario cambiar el título de la función:
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[] )
Tras este cambio, el uso del parámetro begin ha perdido todo su sentido, ¡de modo que se debe eliminar del código!
El código para calcular los límites de cambios de variables del ciclo de operaciones, la verificación de datos para la suficiencia de cálculo, apenas ha sufrido cambio alguno.
//--- 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
Sin embargo, aparecen determinados problemas con los algoritmos para calcular los valores de indicador. El problema es que MQL5 no tiene funciones incorporadas para determinar los índices del máximo y el mínimo para el período de la barra actual en la dirección del índice decreciente.
Una forma de solucionar esto es escribir nosotros mismos estas funciones. Afortunadamente, estas funciones ya existen en el indicador ZigZag.mq5 en los indicadores personalizados, que se encuentran en la carpeta "MetaTrader5\MQL5\Indicators\Examples".
La forma más fácil de solucionar esto es seleccionar el código de estas funciones en el indicador ZigZag.mq5, copiarlo en el bloc de notas de Windows y después pegarlo en nuestro código, por ejemplo tras describir la función OnInit() a nivel 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); }
Después de esto, el código de la función OnCalculate() tendrá el siguiente aspecto:
//+------------------------------------------------------------------+ //| 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 la simetría de los ejes, corregí ligeramente el código añadiendo la variación vertical del indicador, en comparación con el original, usando el valor de 0,5.
Aquí están los resultados del trabajo de este indicador en el gráfico:
Para encontrar la posición del elemento con el valor máximo o mínimo en una distancia no superior a AroonPeriod desde la barra actual, podemos usar las funciones incorporadas ArrayMaximum() y ArrayMinimum() de MQL5, que también buscan los extremos, pero estas funciones realizan la búsqueda usando el orden de aumento.
No obstante, la búsqueda se debería hacer en orden decreciente de índices. Para este caso, la solución más sencilla es cambiar la dirección de los índices en el indicador y en los buffers de precio usando la función ArraySetAsSeries().
Pero también debemos cambiar la dirección del orden de la barra en el bucle de cálculo y cambiar el algoritmo del primer cálculo variable.
En este caso, la función OnCalculate() resultante tendrá este aspecto:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Cambié el nombre de la variable "first" a "limit", que es más apropiado en este caso.
En este c aso, el código del bucle principal es similar a la forma en que se hacía en MQL4. De modo que este estilo de escritura de la función OnCalculate() se puede usar para la conversión de indicadores de MQL4 a MQL5 con cambios mínimos en el código.
Conclusión
¡Pues ya está hecho! Escribimos el indicador, incluso en dos versiones.
Para ser un caso de solución correcta, conservadora e inteligente de este tipo de problemas, la solución acabó siendo apenas más complicada que la construcción de un juguete usando piezas de Lego.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/48





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso