English Русский 中文 Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Crear un Indicador con Buffers de Indicador Múltiples para Principiantes

Crear un Indicador con Buffers de Indicador Múltiples para Principiantes

MetaTrader 5Ejemplos | 7 febrero 2014, 13:41
3 353 0
Nikolay Kositsin
Nikolay Kositsin

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

Archivos adjuntos |
sma_1.mq5 (3.92 KB)
aroon.mq5 (8.04 KB)
aroons.mq5 (6.28 KB)
zigzag.mq5 (9.17 KB)
MQL para "Dummies": Cómo Diseñar y Construir Clases de Objetos MQL para "Dummies": Cómo Diseñar y Construir Clases de Objetos
Creando un modelo de programa de diseño visual demostramos cómo diseñar y construir clases en MQL5. Este artículo está escrito para programadores principiantes que están trabajando con aplicaciones MT5. Propondremos una tecnología sencilla y fácil de entender para crear clases sin necesidad de entrar muy a fondo en la teoría de programación orientada al objeto.
Widgets de las Señales Comerciales MetaTrader 4 y MetaTrader 5 Widgets de las Señales Comerciales MetaTrader 4 y MetaTrader 5
Desde hace poco, cada usuario MetaTrader 4 y MetaTrader 5 tiene la posibilidad de convertirse en suministrador de señales comerciales y obtener beneficios adicionales. Ahora, con la ayuda de los nuevos widgets puede hablar de sus éxitos en su propia página web, en un blog o en una página de la red social. Las ventajas del uso de los widgets son obvias: el aumento de la popularidad del suministrador, de su reputación como trader exitoso y la captación de nuevos suscriptores. Todo esto pueden obtener los traders que coloquen widgets en otras páginas de internet.
Algoritmos Genéticos: ¡Es fácil! Algoritmos Genéticos: ¡Es fácil!
En este artículo, el autor habla sobre cálculos evolucionarios con el uso de un algoritmo genético personalmente desarrollado. Demuestra el funcionamiento de un algoritmo, usando ejemplos, y facilita recomendaciones prácticas para su uso.
MetaQuotes ID en el terminal móvil MetaTrader MetaQuotes ID en el terminal móvil MetaTrader
Los teléfonos con base Android e iOS nos proponen un sinfín de posibilidades, muchas de las cuales ni siquiera imaginamos o no usamos en todo su potencial. Una de esas novedades es la tecnología de notificaciones Push, que permite recibir mensajes personales a través de internet, independientemente del número de teléfono móvil o el operador de telefonía móvil. En el terminal móvil MetaTrader ya ha fructificado la posibilidad de recibir este tipo de mensajes directamente desde su robot comercial. Para ello, basta con conocer el identificador MetaQuotes ID de su aparato, más de 9 000 000 terminales móviles lo han recibido ya.