Descargar MetaTrader 5

Indicador para la representación del gráfico "Renko"

16 abril 2014, 14:52
Dmitriy Zabudskiy
0
2 362

Introducción

En los artículos Indicador para la representación del gráfico "Cruces - Círculos" e Indicador para la representación del gráfico "Kagi" se vieron los métodos de representación de los indicadores de los gráficos: "Cruces - Círculos" y "Kagi". Continuando con esta serie de artículos, esta vez vamos a ver uno de los tipos de representación de programa "Renko".

El gráfico "Renko" recibió su nombre de la palabra japonesa "renga" - ladrillo, lo cual no es sorprendente, teniendo en cuenta que constituye una serie de "ladrillos", dispuestos de uno en uno, en una superficie vertical. Conforme va creciendo el precio, los "ladrillos" van subiendo más y más arriba, o al contrario, van bajando con la disminución del precio. Si traducimos del japonés, la palabra "Renko" significa "paso silencioso". El gráfico "Renko" apareció en Japón probablemente sobre el siglo XIX, pero en EE.UU y Europa sólo se llegó a conocer después de la publicación del libro «Más allá de las velas japonesas», de Steve Nison, en 1994 (en inglés Beyond Candlesticks).

El gráfico "Renko", al igual que los gráficos mencionados con anterioridad, ignora la escala temporal y se orienta sólo por el movimiento de los precios. A diferencia del gráfico "Cruces-Círculos", el gráfico "Renko" representa cada "ladrillo" con una nueva columna (en una nueva superficie vertical), en lo demás, el análisis de su construcción tiene mucho en común: existe un tamaño fijo del "ladrillo" (de la "cruz", y del "círculo"), el precio se analiza y las figuaras de construyen de manera análoga.

Así pues, el gráfico "Renko" constituye un conjunto de rectángulos verticales ("ladrillos"). El "ladrillo" creciente normalmente es de color blanco, y el decreciente de color negro. La representación es regulada por el movimiento del precio. El precio actual del periodo examinado se compara con el mínimo y el máximo del "ladrillo" precedente (blanco o negro). Si el precio actual es mayor que el máximo del "ladrillo" precedente en un tamaño equivalente o superior al de un "ladrillo", entonces en la nueva columna del máximo del "ladrillo" precedente se forma el siguiente "ladrillo" (de color blanco), o varios. Y al revés, si el precio actual es inferior al mínimo del "ladrillo" precedente en un tamaño equivalente o superior al de un "ladrillo", entonces se forma un "ladrillo" descendente de color negro.

El primer "ladrillo" del cálculo de la representación del gráfico se forma dependiendo del movimiento del precio en el periodo tratado, teniendo en cuenta el precio del mínimo o el máximo del ladrillo anterior, se toma el precio de apertura de la barra.

Podemos ver un ejemplo del aspecto que tiene el gráfico clásico "Renko" en el dibujo 1:

Dib. 1. Ejemplo de representación del gráfico clásico "Renko"

Dib. 1. Ejemplo de representación del gráfico clásico "Renko"

1. Ejemplo de representación

El aspecto clásico de representación del gráfico "Renko" se suele formar en base a los precios de cierre. Primero hay que elegir el time frame y luego la magnitud del "salto" (umbral) que le corresponda.

En este ejemplo se usa la pareja EURUSD (time frame H4), con un "salto" de 30 puntos. El resultado de la representación del gráfico "Renko" durante el periodo de 03.01.2014 a 31.01.2014 (un mes aproximadamente), se muestra en el dibujo 2, a la izquierda se muestra la representación teniendo en cuenta el intervalo temporal (aquí podemos ver una prolongación horizontal de "ladrillos"), a la derecha se muestra el resultado de la representación del gráfico Renko:


Dib.2. Resultado de la representación del gráfico "Renko" en EURUSD (H4, salto 30 puntos) 

Dib.2. Resultado de la representación del gráfico "Renko" en EURUSD (H4, salto 30 puntos)

Veamos con más detalle el principio de construcción del gráfico. En rojo, en el dib. 2 están representadas las líneas horizontales que indican el tamaño del precio de cada "ladrillo" (30 puntos), y en color azul está representadas las fechas más interesantes (para orientarse).

Como se puede ver por el gráfico, al final del día 03.01.2014, la vela se cierra por debajo de los 1.3591 diapasones de precio del gráfico anteriormente marcados (líneas horizontales rojas) en 1.3589 (marcados con una señal de precio), lo que da como resultado un "ladrillo" descendente en el gráfico.

Después el precio sigue plano (no se cierra por debajo de la marca de los 1.3561, pero tampoco se cierra por encima de la marca de los 1.3651) hasta las 20:00 10.01.2014 (cierre de la vela, formada desde las 16:00), donde se cierra (por encima de la marca de precio de los 1.3651) en 1.3663 (marcado con una señal de precio). Después se puede ver que el precio vuelve a progresar en plano, hasta las 20:00 14.01.2014 (cierre de la vela formada a las 16:00), donde supera el diapasón de precio, formando con ello un nuevo "ladrillo", y se cierra en la marca de los 1.3684.

Además, podemos ver la dirección del movimiento del precio, que se dirige hacia abajo y traspasa en cuatro ocasiones los diapasones de precio, descendiendo más y más en el gráfico de precios. A las 12:00 del 23.01.2014 (cierre de la vela formada a las 08:00), observamos cómo traspasa en dirección ascendente dos diapasones de precio y forma a su vez dos "ladrillos", cerrándose en los 1.3639. El primer "ladrillo" se ve perfectamente, y el segundo se estira en una larga línea vertical (debido a la aparición simultánea con respecto al primero). Después la representación continúa conforme a las mismas reglas básicas.


2. Principio de construcción del indicador "Renko"

Al desarrollar este indicador todas las funciones fueron realizadas de la manera más independiente posible. Otra de las tareas básicas fue aumentar al máximo las posibilidades del indicador, para una mayor comodidad a la hora de analizar el mercado.

Se decidió separar los cálculos del time frame en curso, es decir, se elige un time frame en la configuración e, independientemente del time frame en el que se inicie el indicador, se nos mostrarán los datos según el time frame que indicamos en la configuración. Esto se logra gracias a la copia de los datos del periodo examinado en matrices de búfer por separado, efectuando con ellos posteriormente los cálculos y el llenado de los búfers de salida del indicador.

La representación clásica del gráfico "Renko" presupone la construcción del gráfico según los precios de cierre(Close), pero para ampliar las posibilidades del análisis se añadió la opción de usar los precios Open, High, Low.

Dado que los "ladrillos" en el gráfico "Renko" tienen el mismo tamaño, cuando los precios se muevan bruscamente (un tamaño equivalente a varios "ladrillos") nos sería más cómodo saber en qué lugar se ha mostrado el mercado más enérgico. Para esto existe una indicación (desconectable) en forma de "sombra" vertical pequeña (como sucede con el gráfico de velas japonesas) desde el "ladrillo" que asciende o desciende hasta el nivel del "ladrillo" formado del movimiento de una barra del time frame seleccionado.

La última y más notable peculiaridad de este indicador es la posibilidad de generar una representación de tipo "ZigZag" en el gráfico principal, lo que permitirá ampliar las posibilidades del análisis gráfico.

En el dib. 3 se nos muestra el aspecto exterior del indicador con su funcionalidad completa:

Dib.3. Aspecto exterior del indicador en el gráfico EURUSD (Daily, salto de 25 puntos)

Dib.3. Aspecto exterior del indicador en el gráfico EURUSD (Daily, salto de 25 puntos)


3. Código y algoritmo del indicador

El código del indicador es bastante grande, incluye en su realización cerca de 900 líneas. Como ya se dijo antes, las funciones están separadas al máximo las unas de las otras, lo que puede complicar la comprensión del algoritmo. Se han tomado como base algunas de las funciones del artículo anterior. En caso de que algunos aspectos resulten incomprensibles, se puede recurrir al artículo antes mencionado Indicador para la representación del gráfico Kagi o enviarme un mensaje personal con las cuestiones a aclarar.

En el artículo aclararemos el código por funciones, incluyendo ciertas explicaciones conectoras en algunos lugares. Las funciones se irán describiendo conforme se vayan usando, para facilitar la comprensión del código.


3.1. Parámetros de entrada del indicador

Como ya hemos dicho con anterioridad, el gráfico "Renko" constituye un conjunto de "ladrillos" ascendentes y descendentes de diferentes colores. Para semejante representación sólo son necesarios cinco búfers, unidos en una sólo representación gráfica del tipo "Velas de colores". Los cuatro búfers restantes se usan para guardar los datos necesarios para los cálculos del indicador.

Veamos los parámetros de entrada (son 25), dividiéndolos por grupos.

  • step - salto o tamaño del "ladrillo";
  • type_step - aspecto del salto, puede ser tanto por puntos, como porcentual (el último se calcula dependiendo del precio actual);
  • magic_numb - el número mágico, es necesario para distinguir los objetos gráficos del indicador y se usa para borrarlos del gráfico;
  • levels_number - cantidad de niveles (0-sin nivel), para indicar la división de los "ladrillos" en la ventana del indicador;
  • levels_color - el color de los niveles en la ventana del indicador;
  • time_frame - establece el periodo para la representación del gráfico (del periodo analizado);
  • time_redraw - periodo de actualización del gráfico (un intervalo de tiempo, transcurrido el cual, se actualiza la representación del gráfico);
  • first_date_start - fecha de inicio (la fecha desde la que se comienza la representación del gráfico);
  • type_price - tipo de precio para la representación (establece el precio, en total existen cuatro tipos: Close - representación según los precios de cierre, el método clásico; Open - precios de apertura; High - precios máximos Low - precios mínimos);
  • shadow_print - mostrar o no la sombra (si ponemos este parámetro en la posición true, se usan las sombras que surgen de los "ladrillos" para indicar el precio máximo o mínimo usado para la apertura de varios "ladrillos" de golpe);
  • filter_number - el valor de la cantidad de "ladrillos" para el viraje (un parámetro adicional que no se utiliza en la representación clásica, responsable de la cantidad de "ladrillos" necesaria para el viraje del gráfico en otra dirección);
  • zig_zag - dibujar una línea en ZigZag en el gráfico principal (representación adicional en el gráfico principal, que pretende simplificar el análisis o modernizarlo, en cierta manera);
  • zig_zag_shadow - dibujar una línea en ZigZag según los precios mínimos y máximos (utiliza los precios más cercanos de los máximos y los mínimos para la representación del ZigZag según los puntos extremos);
  • zig_zag_width - grosor de la línea en ZigZag;
  • zig_zag_color_up - color de línea en ZigZag ascendente;
  • zig_zag_color_down - color de línea en ZigZag descendente;
  • square_draw - dibujar o no los "ladrillos" en el gráfico principal (en este régimen se puede ver el movimiento de los precios conforme a los que se formaron los "ladrillos");
  • square_color_up - color del "ladrillo" en el gráfico hacia arriba;
  • square_color_down - color del "ladrillo" en el gráfico hacia abajo;
  • square_fill - relleno del "ladrillo" en el gráfico principal;
  • square_width - grosor de línea del "ladrillo" en el gráfico principal;
  • frame_draw - dibujar o no el marco del "ladrillo" (se trata de un ribete para los "ladrillos" representados, es un parámetro gráfico adicional, cuyo uso se presume muy poco frecuente);
  • frame_width - grosor de la línea del marco del "ladrillo";
  • frame_color_up - color de los marcos de los "ladrillos" hacia arriba;
  • frame_color_down - color de los marcos de los "ladrillos" hacia abajo.

A continuación, en el código, tenemos la declaración de los búfers, los cinco búfers pricipales se usan para la representación gráfica y cuatro para el almacenamiento de los datos para la representación y los cálculos. Price[] - es el búfer para el almacenamiento de los precios copiados usados para la representación, Date[] - es el búfer para el almacenamiento de las fechas y se usa para la representación en el gráfico principal, los búfers Price_high[] y Price_low[] - se usan para el almacenamiento de los precios copiados de los máximos y los mínimos, se utilizan para la representación del tipo "ZigZag" en el gráfico principal.

Después viene la declaración de las matrices de los búfers de cálculo y de las variables auxiliares para las funciones: func_draw_renko, func_draw_zig_zag, func_draw_renko_main_chart, sobre las que hablaremos más tarde.

//+------------------------------------------------------------------+
//|                                                         ABCR.mq5 |
//|                                   Azotskiy Aktiniy ICQ:695710750 |
//|                          https://www.mql5.com/ru/users/Aktiniy |
//+------------------------------------------------------------------+
//--- Auto Build Chart Renko
#property copyright "Azotskiy Aktiniy ICQ:695710750"
#property link      "https://www.mql5.com/ru/users/Aktiniy"
#property version   "1.00"
#property description "Auto Build Chart Renko"
#property description "   "
#property description "This indicator makes drawing a chart Renko as a matter of indicator window, and in the main chart window"
#property indicator_separate_window
#property indicator_buffers 9
#property indicator_plots   1
//--- plot RENKO
#property indicator_label1  "RENKO"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrRed,clrBlue,C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0'
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- вид построения
enum type_step_renko
  {
   point=0,   // Punto
   percent=1, // Porcentaje
  };
//--- tipo de precio para la representación
enum type_price_renko
  {
   close=0, // Close
   open=1,  // Open
   high=2,  // High
   low=3,   // Low
  };
//--- input parameters
input double           step=10;                                 // Salto
input type_step_renko  type_step=point;                         // Tipo de salto
input long             magic_numb=65758473787389;               // Número mágico
input int              levels_number=1000;                      // Cantidad de niveles (0-sin niveles)
input color            levels_color=clrLavender;                // Color de los niveles
input ENUM_TIMEFRAMES  time_frame=PERIOD_CURRENT;               // Periodo del cálculo
input ENUM_TIMEFRAMES  time_redraw=PERIOD_M1;                   // Periodo de actualización del gráfico
input datetime         first_date_start=D'2013.09.13 00:00:00'; // Fecha de inicio
input type_price_renko type_price=close;                        // Tipo de precio para la representación
input bool             shadow_print=true;                       // mostrar o no la sombra (precios que crean de golpe varios ladrillos)
input int              filter_number=0;                         // Valor de la cantidad de ladrillos para el viraje
input bool             zig_zag=true;                            // Dibujar ZigZag en el gráfico principal
input bool             zig_zag_shadow=true;                     // Dibujar ZigZag conforme a los precios mínimos y máximos
input int              zig_zag_width=2;                         // Grosor de la línea ZigZag
input color            zig_zag_color_up=clrBlue;                // Color de la línea ZigZag ascendente
input color            zig_zag_color_down=clrRed;               // Color de la línea ZigZag descendente
input bool             square_draw=true;                        // Dibujar o no un ladrillo en el gráfico principal
input color            square_color_up=clrBlue;                 // Color del ladrillo en el gráfico principal hacia arriba
input color            square_color_down=clrRed;                // Color del ladrillo en el gráfico principal hacia arriba
input bool             square_fill=true;                        // Relleno del ladrillo en el gráfico principal
input int              square_width=2;                          // Grosor de la línea del ladrillo en el gráfico principal
input bool             frame_draw=true;                         // Dibujar o no el marco del ladrillo
input int              frame_width=2;                           // Grosor de la línea del marco del ladrillo
input color            frame_color_up=clrBlue;                  // Color del marco del ladrillo hacia arriba
input color            frame_color_down=clrRed;                 // Color del marco del ladrillo hacia abajo
//--- indicator buffers
double         RENKO_open[];
double         RENKO_high[];
double         RENKO_low[];
double         RENKO_close[];
double         RENKO_color[];

double         Price[];      // Búfer para el almacenamiento de los precios copiados
double         Date[];       // Búfer para el almacenamiento de las fechas copiadas
double         Price_high[]; // Búfer para el almacenamiento de los precios copiados de los máximos
double         Price_low[];  // Búfer para el almacenamiento de los precios copiados de los mínimos
//--- matrices de los búfers de calculación
double         up_price[];    // precio superior del ladrillo
double         down_price[];  // precio inferior del ladrillo
char           type_box[];    // tipo de ladrillo (hacia arriba, hacia abajo)
datetime       time_box[];    // hora de cerrado del ladrillo
double         shadow_up[];   // máximo superior del precio
double         shadow_down[]; // mínimo inferior del precio
int            number_id[];   // índice de las matrices Price_high y Price_low
//--- variables globales para los cálculos
int obj=0;           // variable para el almacenamiento de la cantidad de objetos gráficos
int a=0;             // variable para el recuento de ladrillos
int bars;            // cantidad de barras
datetime date_stop;  // fecha actual
datetime date_start; // variable de la fecha inicial, para los cálculos
bool date_change;    // variable de almacenamiento de información sobre los cambios de hora (fecha)


3.2. Función de inicialización del indicador

En esta función se realiza la conexión de los búfers de indicador con las matrices dinámicas unidimensionales, para los búfers del tipo INDICATOR_DATA y INDICATOR_COLOR_INDEX, asímismo, se establece el direccionamiento como en las series temporales. Las matrices dinámicas restantes (Price[], Date[], Price_high[], Price_low[]), permanecen sin cambios de dirección de direccionamiento, ya que no les es necesario y sirven sólo para el almacenamiento de datos.

A continuación se establecen los valores que serán representados en el gráfico, se introduce el nombre del indicador, su precisión de representación y se prohíbe la muestra de los valores actuales del indicador en la ventana del mismo.

Después se produce la asignación del valor de la variable date_start (la fecha desde la que se debe comenzar el cálculo). Se procede precisamente a la asignación de la variable, y no al uso de un valor de entrada, ya que el gráfico puede resultar bastante mayor de lo que puede caber en el búfer de indicador, así que se lleva a cabo una cierta corrección de la fecha inicial, advirtiendo al usuario sobre ello. De la corrección de la hora se encarga la función de cálculo de la fecha del comienzo del análisis o "func_calc_date_start".

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,RENKO_open,INDICATOR_DATA);
   ArraySetAsSeries(RENKO_open,true);
   SetIndexBuffer(1,RENKO_high,INDICATOR_DATA);
   ArraySetAsSeries(RENKO_high,true);
   SetIndexBuffer(2,RENKO_low,INDICATOR_DATA);
   ArraySetAsSeries(RENKO_low,true);
   SetIndexBuffer(3,RENKO_close,INDICATOR_DATA);
   ArraySetAsSeries(RENKO_close,true);
   SetIndexBuffer(4,RENKO_color,INDICATOR_COLOR_INDEX);
   ArraySetAsSeries(RENKO_color,true);
//---
   SetIndexBuffer(5,Price,INDICATOR_CALCULATIONS);      // inicializamos el búfer de precios
   SetIndexBuffer(6,Date,INDICATOR_CALCULATIONS);       // inicializamos el búfer de fechas
   SetIndexBuffer(7,Price_high,INDICATOR_CALCULATIONS); // inicializamos el búfer de precios de máximos
   SetIndexBuffer(8,Price_low,INDICATOR_CALCULATIONS);  // inicializamos el búfer de precios de mínimos
//--- establecemos los valores que no se representarán
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);
//--- establecemos el aspecto exterior del indicador
   IndicatorSetString(INDICATOR_SHORTNAME,"ABCR "+IntegerToString(magic_numb)); // nombre del indicador
//--- precisión de representación
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
//--- prohíbimos la muestra de los resultados de los valores actuales en el indicador
   PlotIndexSetInteger(0,PLOT_SHOW_DATA,false);
//--- asignación del valor de la variable de la fecha inicial
   date_start=first_date_start;
//---
   return(INIT_SUCCEEDED);
  }

3.3. Función de cálculo de la fecha del comienzo del análisis

La función en sí misma no es grande de tamaño y consta básicamente de un ciclo. En total hay dos parámetros de entrada, la fecha de comienzo establecida inicialmente y la fecha de finalización del cálculo (fecha actual), la primera de las cuales se cambia dentro de la misma función y se emite como respuesta.

El cuerpo de la función comienza con la medición del tamaño de la matriz del búfer receptor (se puede tomar cualquiera de las matrices de búfer para la medición del tamaño, ya que su tamaño es el mismo y es igual a la cantidad de barras del time frame elegido). Después se mide la cantidad de barras en el intervalo de tiempo elegido.

En la condición del ciclo se compara la cantidad de barras en el intervalo de tiempo elegido y el tamaño de la matriz del búfer.  Si hay más barras, es decir, si no caben todas en la matriz del búfer, entonces el intervalo de tiempo elegido se acorta por diez días, es decir, a la fecha de comienzo del análisis se le añaden diez días. Así se continúa hasta que la matriz del búfer no pueda incluir dentro de sí todos los datos sobre las barras. La función retorna la fecha calculada.

//+------------------------------------------------------------------+
//| Func Calculate Date Start                                        |
//+------------------------------------------------------------------+
datetime func_calc_date_start(datetime input_data_start,// fecha de comienzo establecida inicialmente
                              datetime data_stop)       // fecha de finalización del cálculo (fecha actual)
//---
  {
   int Array_Size=ArraySize(Price);
   int Bars_Size=Bars(_Symbol,time_frame,input_data_start,data_stop);
   for(;Bars_Size>Array_Size;input_data_start+=864000) // 864000 = 10 días
     {
      Bars_Size=Bars(_Symbol,time_frame,input_data_start,data_stop);
     }
   return(input_data_start);
//---
  }

3.4. Funciones de copiado de datos

Para trabajar con los datos, primero hay que copiarlos. Precisamente para ello existen las funciones de copiado de datos (func_copy_price y func_copy_date), que ahora veremos.

Empecemos con la función de copiado del precio o func_copy_price, esta función permite copiar en la matriz designada los precios Open, Close, High y Low de un time frame y periodo definidos, transmitidos en los parámetros de la función. En caso de que el copiado se haya llevado a cabo con éxito, la función retornará "true".

Al principio de la llamada de la función, se inicializa el valor retornado false, a continuación tiene lugar la inicialización de la variable del resultado de los datos copiados y se le asigna un valor negativo. Se declara la matriz común bars_to_copy para almacenar temporalmente los datos copiados sobre los precios, y la variable bars_to_copy, para prevenir la copia de los datos ya copiados.

Después, la función convierte en cero las variables declaradas de almacenamiento de la cantidad de datos copiados, cuenta la cantidad de barras en el intervalo temporal y, dependiendo del tipo de precio copiado elegido (0-Close, 1-Open, 2-High y 3-Low) con ayuda del interruptor-operador switch se asigna el valor de la cantidad de los datos sobre los precios copiados con anterioridad de la variable común bars_copied. A continuación se calcula la cantidad de datos que hay que copiar. Si los datos no se copian por primera vez, se procede a la eliminación de la información de la última barra copiada, porque ha podido cambiar mientras el gráfico se movía.

De nuevo con la ayuda del interruptor-operador, se copia el tipo necesario de datos sobre los precios en la matriz de tiempo price_interim[]. Después se comprueba el resultado de la copia y de nuevo con la ayuda del interruptor-operador switch, se procede a llenar las variables sobre la cantidad de datos copiados.

//+------------------------------------------------------------------+
//| Func Copy Price                                                  |
//+------------------------------------------------------------------+
bool func_copy_price(double &result_array[],
                     ENUM_TIMEFRAMES period,// Time frame
                     datetime data_start,
                     datetime data_stop,
                     char price_type) // 0-Close, 1-Open, 2-High, 3-Low
  {
//---
   int x=false;        // variable para la respuesta
   int result_copy=-1; // cantidad de datos copiados
//---
   static double price_interim[]; // Variable dinámica temporal para el almacenamiento de los datos copiados
   static int bars_to_copy;       // cantidad de barras para el copiado
   static int bars_copied_0;      // cantidad de barras ya copiadas desde la fecha inicial Close
   static int bars_copied_1;      // cantidad de barras ya copiadas desde la fecha inicial Open
   static int bars_copied_2;      // cantidad de barras ya copiadas desde la fecha inicial High
   static int bars_copied_3;      // cantidad de barras ya copiadas desde la fecha inicial Low
   static int bars_copied;        // cantidad de barras ya copiadas desde la fecha inicial variable común
//--- puesta a cero de las variables como forma de cambio de la fecha inicial
   if(date_change==true)
     {
      ZeroMemory(price_interim);
      ZeroMemory(bars_to_copy);
      ZeroMemory(bars_copied_0);
      ZeroMemory(bars_copied_1);
      ZeroMemory(bars_copied_2);
      ZeroMemory(bars_copied_3);
      ZeroMemory(bars_copied);
     }
//--- averiguamos la cantidad actual de barras en el intervalo temporal
   bars_to_copy=Bars(_Symbol,period,data_start,data_stop);
//--- asignación a la variable común del valor de una de las variables copiadas
   switch(price_type)
     {
      case 0:
         //--- Close
         bars_copied=bars_copied_0;
         break;
      case 1:
         //--- Open
         bars_copied=bars_copied_1;
         break;
      case 2:
         //--- High
         bars_copied=bars_copied_2;
         break;
      case 3:
         //--- Low
         bars_copied=bars_copied_3;
         break;
     }
//--- calculamos la cantidad de barras que es necesario copiar
   bars_to_copy-=bars_copied; 
//--- si los datos no son copiados por primera vez
   if(bars_copied!=0) 
     {
      bars_copied--;
      bars_to_copy++;
     }
//--- cambiamos el tamaño de la matriz receptora
   ArrayResize(price_interim,bars_to_copy); 
//--- copiamos los datos en la matriz temporal
   switch(price_type)
     {
      case 0:
         //--- Close
        {
         result_copy=CopyClose(_Symbol,period,0,bars_to_copy,price_interim);
        }
      break;
      case 1:
         //--- Open
        {
         result_copy=CopyOpen(_Symbol,period,0,bars_to_copy,price_interim);
        }
      break;
      case 2:
         //--- High
        {
         result_copy=CopyHigh(_Symbol,period,0,bars_to_copy,price_interim);
        }
      break;
      case 3:
         //--- Low
        {
         result_copy=CopyLow(_Symbol,period,0,bars_to_copy,price_interim);
        }
      break;
     }
//--- comprobamos el resultado del copiado de datos
   if(result_copy!=-1) // si la copia de datos a la matriz intermedia ha tenido éxito
     {
      ArrayCopy(result_array,price_interim,bars_copied,0,WHOLE_ARRAY); // copiamos los datos de la matriz temporal a la matriz básica
      x=true;                   // asignamos la respuesta afirmativa de la función
      bars_copied+=result_copy; // aumentamos el valor de los datos copiados
     }
//--- retorno de información sobre los datos copiados de una de las variables copiadas
   switch(price_type)
     {
      case 0:
         //--- Close
         bars_copied_0=bars_copied;
         break;
      case 1:
         //--- Open
         bars_copied_1=bars_copied;
         break;
      case 2:
         //--- High
         bars_copied_2=bars_copied;
         break;
      case 3:
         //--- Low
         bars_copied_3=bars_copied;
         break;
     }
//---
   return(x);
  }

 La siguiente función es la de copiado de fecha o "func_copy_date". El código de la función es muy parecido al del bloque ya examinado, la única diferencia es el tipo de datos copiados, así que no se harán comentarios.

//+------------------------------------------------------------------+
//| Func Copy Date                                                   |
//+------------------------------------------------------------------+
bool func_copy_date(double &result_array[],
                    ENUM_TIMEFRAMES period,// time frame
                    datetime data_start,
                    datetime data_stop)
  {
//---
   int x=false;                    // variable para la respuesta
   int result_copy=-1;             // cantidad de datos copiados
   static datetime time_interim[]; // Variable dinámica temporal para el almacenamiento de los datos copiados
   static int bars_to_copy;        // cantidad de barras para el copiado
   static int bars_copied;         // cantidad de barras ya copiadas desde la fecha inicial
//--- puesta a cero de las variables como forma de cambio de la fecha inicial
   if(date_change==true)
     {
      ZeroMemory(time_interim);
      ZeroMemory(bars_to_copy);
      ZeroMemory(bars_copied);
     }
//---
   bars_to_copy=Bars(_Symbol,period,data_start,data_stop); // averiguamos la cantidad actual de barras en el intervalo temporal
   bars_to_copy-=bars_copied; // calculamos la cantidad de barras que es necesario copiar
//---
   if(bars_copied!=0) // si los datos no son copiados por primera vez
     {
      bars_copied--;
      bars_to_copy++;
     }
//---
   ArrayResize(time_interim,bars_to_copy); // cambiamos el tamaño de la matriz receptora
   result_copy=CopyTime(_Symbol,period,0,bars_to_copy,time_interim);
//---
   if(result_copy!=-1) // si la copia de datos a la matriz intermedia ha tenido éxito
     {
      ArrayCopy(result_array,time_interim,bars_copied,0,WHOLE_ARRAY); // copiamos los datos de la matriz temporal en la matriz principal de datos
      x=true; // asignamos la respuesta afirmativa de la función
      bars_copied+=result_copy; // aumentamos el valor de los datos copiados
     }
//---
   return(x);
  }

3.5. Función de cálculo de "Ladrillos"

Como podemos ver por los parámetros del indicador, es posible establecer el tamaño del ladrillo no sólo en puntos, sino también en un tanto por ciento del precio actual. Con los puntos todo es sencillo, se trata de una magnitud fijada, pero ¿cómo proceder con un tanto por ciento? Para este menester existe la función de cálculo de "ladrillos" o "func_calc_dorstep".

La función tiene tres parámetros de entrada: el precio actual (el precio a partir del cual se calculará el tanto por ciento, en el caso de que se elija el tamaño del "ladrillo" en tanto por ciento), tipo elegido de cálculo (por puntos o por tanto por ciento), y tamaño del salto (se establece una magnitud que se calculará como tanto por ciento o puntos, dependiendo del tipo de cálculo elegido).

Al principio de la función se declara la variable para la respuesta del tipo double y, dependiendo del tipo de cálculo elegido, comprobado por el operador convencional, la variable de la respuesta recibirá un valor en puntos. A continuación, la variable de la respuesta se transforma en tipo int para que el valor retornado sea siempre entero, incluso si en el transcurso del cálculo en forma de tanto por ciento se obtuviese un resultado fraccionado.

//+------------------------------------------------------------------+
//| Func Calculate Doorstep                                          |
//+------------------------------------------------------------------+
int func_calc_dorstep(double price,      // precio
                      char type_doorstep,// tipo de salto
                      double doorstep)   // salto
  {
   double x=0;          // variable para la respuesta

   if(type_doorstep==0) // si el cálculo se debe efectuar en puntos
     {
      x=doorstep;
     }

   if(type_doorstep==1) // si el cálculo se debe efectuar en tanto por ciento
     {
      x=price/_Point*doorstep/100;
     }

   return((int)x);
  }

3.6. Función principal - representación del gráfico "Renko"

La función más importante para la representación del gráfico "Renko" es "func_draw_renko". Esta función es responsable del llenado de los búfers gráficos (búfers de indicador) y de las matrices de los búfers de cálculo. Los búfers de cálculo son necesarios para almacenar la información sobre cada "ladrillo", suponen una fuente de información correcta, es decir, recopilada y calculada en base a los datos iniciales.

Los parámetros de entrada de la función son las matrices de datos de los precios y fechas de formación de las barras, así como la información sobre el tipo de salto y su parámetro, el filtro de viraje y el dibujado de las sombras.

De manera convencional, podemos dividir la función en dos partes: la parte responsable del cálculo de la cantidad de "ladrillos" y la parte responsable del llenado de los búfers de gráficos y de cálculo.

En el principio mismo de la función tiene lugar la puesta a cero de los búfers gráficos, para evitar la representación de celdas no llenadas, sin valor alguno. Después se introducen las variables auxiliares para los cálculos: "doorstep_now" se utiliza para el salto (básicamente se necesita en el cálculo de su tamaño, en caso de elegirse el tipo de salto por tanto por ciento), la variable "point_go" almacena la información sobre distancia superada desde la formación del último "ladrillo", la variable "a" se usa al contar los "ladrillos", "up_price_calc" y "down_price_calc" son los últimos precios altos y bajos analizados, "type_box_calc" es el aspecto del último "ladrillo" analizado (hacia arriba o hacia abajo).

La primera parte de la función, al igual que la segunda, consta de un ciclo, pero la segunda representa un primer ciclo completado. Analicémoslo todo por orden.

El primer ciclo pasa por todos los valores copiados, la cantidad de datos copiados es determinada por la variable "bars" (se calcula en la función "func_concolidation", que estudiaremos más tarde). Después, en el ciclo tiene lugar la llamada de la función del cálculo del tamaño del "ladrillo". Dado que cada barra tiene un precio de cierre distinto, propiamente, si usamos la medición del salto en tanto por ciento, hay que calcularlo para cada barra por separado.

A continuación, con ayuda de los operadores convencionales if se comprueba el movimiento del precio, pero el precio habrá debido recorrer una distancia de un salto o superior. Después de determinar la dirección del movimiento del precio, se lleva a cabo la comprobación de la condición del movimiento anterior, es decir, de qué manera se construyó el último "ladrillo" (hacia arriba o hacia abajo). Esto se hace así en vista de que en los parámetros del indicador hay un parámetro de filtro (el valor de la cantidad de "ladrillos" para el viraje). Tras la comprobación de todas estas condiciones, se pone en marcha el ciclo, que tendrá lugar exactamente tantas veces como "ladrillos" pueda formar el movimiento actual del precio.

Después del ciclo de cálculo de "ladrillos" va el ciclo principal (llenado de búfers gráficos y de cálculo), pero antes de ello tendrá lugar el cálculo de la cantidad de barras para la representación; a continuación tiene lugar el cambio de las dimensiones de las matrices de los búfers de cálculo con la representación máxima de barras, así como su puesta a cero. Acto seguido, a los primeros miembros de ciertas matrices de cálculo (básicas, usadas en la primera comparación) se les asignan los valores iniciales.

En el caso de que su cantidad máxima de barras representadas sea menor que la cantidad posible de "ladrillos" para la representación correcta del gráfico, tiene lugar el cálculo de los "ladrillos" de sobra, y se emite el mensaje correspondiente sobre el bajo valor del salto.

Después se produce la puesta a cero de la variable de cálculo de los "ladrillos" y comienza el ciclo principal. La diferencia entre el ciclo principal y el anterior reside en que en él, además de los cálculos indicados arriba, tiene lugar el llenado de las matrices de los búfers de cálculo y la puesta a cero del contador de "ladrillos", según los parámetros calculados previamente, antes del comienzo del ciclo.

Al final de la función se produce el llenado de los búfers gráficos.

//+------------------------------------------------------------------+
//| Func Draw Renko                                                  |
//+------------------------------------------------------------------+
void func_draw_renko(double &price[],   // matriz de precios
                     double &date[],    // matriz de fechas
                     int number_filter, // cantidad de ladrillos para el viraje
                     bool draw_shadow,  // dibujar sombra
                     char type_doorstep,// tipo de salto
                     double doorstep)   // salto
  {
//--- puesta a cero de las matrices
//--- matrices de los búfers de dibujado
   ZeroMemory(RENKO_close);
   ZeroMemory(RENKO_color);
   ZeroMemory(RENKO_high);
   ZeroMemory(RENKO_low);
   ZeroMemory(RENKO_open);
//--- variables auxiliares para los cálculos
   int doorstep_now; // salto actual
   int point_go;     // puntos recorridos
//--- variables auxiliares para el cálculo de la cantidad de ladrillos
   a=0;
   double up_price_calc=price[0];
   double down_price_calc=price[0];
   char type_box_calc=0;

   for(int z=0; z<bars; z++) //---> ciclo de cálculo de ladrillos
     {
      //--- calculamos el tamaño del salto, teniendo en cuenta el precio actual examinado
      doorstep_now=func_calc_dorstep(price[z],type_doorstep,doorstep);
      //--- si el precio va hacia arriba
      if((price[z]-up_price_calc)/_Point>=doorstep_now)
        {
         //--- calculamos la cantidad de puntos recorrida
         point_go=int((price[z]-up_price_calc)/_Point);
         //--- antes el precio iba hacia arriba o la dirección del precio era desconocida
         if(type_box_calc==1 || type_box_calc==0)
           {
            for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
              {
               //--- añadimos el siguiente ladrillo
               a++;
               //--- establecemos el valor del precio inferior del ladrillo siguiente
               down_price_calc=up_price_calc;
               //--- establecemos el valor del precio superior del ladrillo siguiente
               up_price_calc=down_price_calc+(doorstep_now*_Point);
               //--- estableciendo el tipo de ladrillo (hacia arriba)
               type_box_calc=1;
              }
           }
         //--- hasta entonces el precio estaba descendiendo
         if(type_box_calc==-1)
           {
            if((point_go/doorstep_now)>=number_filter)
              {
               for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
                 {
                  //--- añadimos el siguiente ladrillo
                  a++;
                  //--- establecemos el valor del precio inferior del ladrillo siguiente
                  down_price_calc=up_price_calc;
                  //--- establecemos el valor del precio superior del ladrillo siguiente
                  up_price_calc=down_price_calc+(doorstep_now*_Point);
                  //--- estableciendo el tipo de ladrillo (hacia arriba)
                  type_box_calc=1;
                 }
              }
           }
        }
      //--- si el precio está descendiendo
      if((down_price_calc-price[z])/_Point>=doorstep_now)
        {
         //--- calculamos la cantidad de puntos recorrida
         point_go=int((down_price_calc-price[z])/_Point);
         //--- antes el precio iba hacia abajo o la dirección del precio era desconocida
         if(type_box_calc==-1 || type_box_calc==0)
           {
            for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
              {
               //--- añadimos el siguiente ladrillo
               a++;
               //--- establecemos el valor del precio inferior del ladrillo siguiente
               up_price_calc=down_price_calc;
               //--- establecemos el valor del precio superior del ladrillo siguiente
               down_price_calc=up_price_calc-(doorstep_now*_Point);
               //--- estableciendo el tipo de ladrillo (hacia arriba)
               type_box_calc=-1;
              }
           }
         //--- hasta entonces el precio estaba ascendiendo
         if(type_box_calc==1)
           {
            if((point_go/doorstep_now)>=number_filter)
              {
               for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
                 {
                  //--- añadimos el siguiente ladrillo
                  a++;
                  //--- establecemos el valor del precio inferior del ladrillo siguiente
                  up_price_calc=down_price_calc;
                  //--- establecemos el valor del precio superior del ladrillo siguiente
                  down_price_calc=up_price_calc-(doorstep_now*_Point);
                  //--- estableciendo el tipo de ladrillo (hacia arriba)
                  type_box_calc=-1;
                 }
              }
           }
        }
     } //---< ciclo de cálculo de ladrillos
//--- calculamos la cantidad de barras representadas
   int b=Bars(_Symbol,PERIOD_CURRENT);
//--- cambiamos las dimensiones de las matrices
   ArrayResize(up_price,b);
   ArrayResize(down_price,b);
   ArrayResize(type_box,b);
   ArrayResize(time_box,b);
   ArrayResize(shadow_up,b);
   ArrayResize(shadow_down,b);
   ArrayResize(number_id,b);
//--- ponemos a cero las matrices de los búfers de calculación
   ZeroMemory(up_price);
   ZeroMemory(down_price);
   ZeroMemory(type_box);
   ZeroMemory(time_box);
   ZeroMemory(shadow_up);
   ZeroMemory(shadow_down);
   ZeroMemory(number_id);
//--- llenamos los valores iniciales de las matrices
   up_price[0]=price[0];
   down_price[0]=price[0];
   type_box[0]=0;
//--- calculamos la cantidad de ladrillos sobrantes
   int l=a-b;
   int turn_cycle=l/(b-1);
   int turn_rest=(int)MathMod(l,(b-1))+2;
   int turn_var=0;
//--- emitimos el mensaje sobre la representación parcial de los ladrillos
   if(a>b)Alert("Hay más ladrillos de los que caben en el gráfico, posiblemente el salto sea pequeño");

   a=0; //--- ponemos a cero la variable de cálculo de ladrillos
   for(int z=0; z<bars; z++) //---> Ciclo principal
     {
      //--- calculamos el tamaño del salto, teniendo en cuenta el precio actual examinado
      doorstep_now=func_calc_dorstep(price[z],type_doorstep,doorstep);
      //--- si el precio está ascendiendo
      if((price[z]-up_price[a])/_Point>=doorstep_now)
        {
         //--- calculamos la cantidad de puntos recorrida
         point_go=int((price[z]-up_price[a])/_Point);
         //--- antes el precio iba hacia arriba o la dirección del precio era desconocida
         if(type_box[a]==1 || type_box[a]==0)
           {
            for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
              {
               a++; //--- añadimos el siguiente ladrillo
               if((a==b && turn_var<turn_cycle) || (turn_var==turn_cycle && turn_rest==a))
                 {
                  up_price[0]=up_price[a-1];
                  a=1;        // puesta a cero del contador de ladrillos
                  turn_var++; // contador de ciclos de puesta a cero
                 }
               //--- establecemos el valor del precio inferior del ladrillo siguiente
               down_price[a]=up_price[a-1];
               //--- establecemos el valor del precio superior del ladrillo siguiente
               up_price[a]=down_price[a]+(doorstep_now*_Point);

               //--- estableciendo el valor de la sombra superior
               if(shadow_print==true) shadow_up[a]=price[z]; // al nivel del último precio más alto 
               else shadow_up[a]=up_price[a];                // al nivel del precio superior del ladrillo 

               //--- estableciendo los valores del precio inferior (al nivel del precio del ladrillo)
               shadow_down[a]=down_price[a];
               //--- estableciendo los valores de hora de cierre del ladrillo
               time_box[a]=(datetime)Date[z];
               //--- estableciendo el tipo de ladrillo (hacia arriba)
               type_box[a]=1;
               //--- estableciendo índice
               number_id[a]=z;
              }
           }
         //--- hasta entonces el precio estaba descendiendo
         if(type_box[a]==-1)
           {
            if((point_go/doorstep_now)>=number_filter)
              {
               for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
                 {
                  a++; //--- añadimos el siguiente ladrillo

                  if((a==b && turn_var<turn_cycle) || (turn_var==turn_cycle && turn_rest==a))
                    {
                     up_price[0]=up_price[a-1];
                     a=1;        // puesta a cero del contador de ladrillos
                     turn_var++; // contador de ciclos de puesta a cero
                    }
                  //--- establecemos el valor del precio inferior del ladrillo siguiente
                  down_price[a]=up_price[a-1];
                  //--- establecemos el valor del precio superior del ladrillo siguiente
                  up_price[a]=down_price[a]+(doorstep_now*_Point);

                  //--- estableciendo el valor de la sombra superior
                  if(shadow_print==true) shadow_up[a]=price[z]; // al nivel del último precio más alto
                  else shadow_up[a]=up_price[a];                // al nivel del precio superior del ladrillo

                  //--- estableciendo el valor del precio inferior (al nivel del precio del ladrillo)
                  shadow_down[a]=down_price[a];
                  //--- estableciendo el valor de hora de cierre del ladrillo
                  time_box[a]=(datetime)Date[z];
                  //--- estableciendo el tipo de ladrillo (ascendente)
                  type_box[a]=1;
                  //--- estableciendo índice
                  number_id[a]=z;
                 }
              }
           }
        }

      //--- si el precio desciende
      if((down_price[a]-price[z])/_Point>=doorstep_now)
        {
         //--- calculamos la cantidad de puntos recorrida
         point_go=int((down_price[a]-price[z])/_Point);
         //--- antes el precio iba hacia abajo o la dirección del precio era desconocida
         if(type_box[a]==-1 || type_box[a]==0)
           {
            for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
              {
               a++; //--- añadimos el siguiente ladrillo
               if((a==b && turn_var<turn_cycle) || (turn_var==turn_cycle && turn_rest==a))
                 {
                  down_price[0]=down_price[a-1];
                  a=1;        // puesta a cero del contador de ladrillos
                  turn_var++; // contador de ciclos de puesta a cero
                 }
               //--- establecemos el valor del precio inferior del ladrillo siguiente
               up_price[a]=down_price[a-1];
               //--- establecemos el valor del precio superior del ladrillo siguiente
               down_price[a]=up_price[a]-(doorstep_now*_Point);

               //--- estableciendo el valor de la sombra inferior 
               if(shadow_print==true) shadow_down[a]=price[z]; //--- al nivel del último precio más bajo
               else shadow_down[a]=down_price[a];              //--- al nivel del precio inferior del ladrillo

               //--- estableciendo el valor del precio superior (al nivel del precio del ladrillo)
               shadow_up[a]=up_price[a];
               //--- estableciendo el valor de hora de cierre del ladrillo
               time_box[a]=(datetime)Date[z];
               //--- estableciendo el tipo de ladrillo (descendente)
               type_box[a]=-1;
               //--- estableciendo índice
               number_id[a]=z;
              }
           }
         //--- hasta entonces el precio estaba ascendiendo
         if(type_box[a]==1)
           {
            if((point_go/doorstep_now)>=number_filter)
              {
               for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
                 {
                  a++; //--- añadimos el siguiente ladrillo
                  if((a==b && turn_var<turn_cycle) || (turn_var==turn_cycle && turn_rest==a))
                    {
                     down_price[0]=down_price[a-1];
                     a=1;        // puesta a cero del contador de ladrillos
                     turn_var++; // contador de ciclos de puesta a cero
                    }

                  up_price[a]=down_price[a-1]; //--- establecemos el valor del precio inferior del ladrillo siguiente
                  down_price[a]=up_price[a]-(doorstep_now*_Point); //--- establecemos el valor del precio superior del ladrillo siguiente

                  //--- estableciendo el valor de la sombra inferior 
                  if(shadow_print==true) shadow_down[a]=price[z]; // al nivel del último precio más bajo
                  else shadow_down[a]=down_price[a];              // al nivel del precio inferior del ladrillo

                  //--- estableciendo el valor del precio superior (al nivel del precio del ladrillo)
                  shadow_up[a]=up_price[a];
                  //--- estableciendo el valor de hora de cierre del ladrillo
                  time_box[a]=(datetime)Date[z];
                  //--- estableciendo el tipo de ladrillo (descendente)
                  type_box[a]=-1;
                  //--- estableciendo índice
                  number_id[a]=z;
                 }
              }
           }
        }
     } //---< Ciclo principal

//--- llenamos los búfers de dibujado
   int y=a;
   for(int z=0; z<a; z++)
     {
      if(type_box[y]==1)RENKO_color[z]=0;
      else RENKO_color[z]=1;
      RENKO_open[z]=down_price[y];
      RENKO_close[z]=up_price[y];
      RENKO_high[z]=shadow_up[y];
      RENKO_low[z]=shadow_down[y];
      y--;
     }
  }


3.7. Función de creación de los objetos gráficos "línea de tendencia" y "rectángulo"

La función de creación de un objeto gráfico del tipo "línea de tendencia" o "func_create_trend_line" y la función de creación de un objeto gráfico del tipo "rectángulo" o "func_create_square_or_rectangle" están creadas según los materiales indicados en la guía de objetos OBJ_RECTANGLE y OBJ_TREND. Sirven para la creación de objetos gráficos en la función de construcción del gráfico "Renko" y para la representación de gráficos del tipo "ZigZag" en el gráfico principal.

//+------------------------------------------------------------------+
//| Func Create Trend Line                                           |
//+------------------------------------------------------------------+
void func_create_trend_line(string name,
                            double price1,
                            double price2,
                            datetime time1,
                            datetime time2,
                            int width,
                            color color_line)
  {
   ObjectCreate(0,name,OBJ_TREND,0,time1,price1,time2,price2);
//--- establecemos el color de la línea
   ObjectSetInteger(0,name,OBJPROP_COLOR,color_line);
//--- establecemos el estilo de representacion de línea
   ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
//--- establecemos el grosor de la línea
   ObjectSetInteger(0,name,OBJPROP_WIDTH,width);
//--- representamos en el plano anterior (false) o posterior (true)
   ObjectSetInteger(0,name,OBJPROP_BACK,false);
//--- conectamos (true) o desconectamos (false) el modo de continuación de la representación de la línea hacia la izquierda
   ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,false);
//--- conectamos (true) o desconectamos (false) el modo de continuación de la representación de la línea hacia la derecha
   ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,false);
  }
//+------------------------------------------------------------------+
//| Func Create Square or Rectangle                                  |
//+------------------------------------------------------------------+
void func_create_square_or_rectangle(string name,
                                     double price1,
                                     double price2,
                                     datetime time1,
                                     datetime time2,
                                     int width,
                                     color color_square,
                                     bool fill)
  {
//--- creamos un rectángulo según las coordenadas establecidas
   ObjectCreate(0,name,OBJ_RECTANGLE,0,time1,price1,time2,price2);
//--- establecemos el color del rectángulo
   ObjectSetInteger(0,name,OBJPROP_COLOR,color_square);
//--- establecemos el estilo de la línea del rectángulo
   ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
//--- establecemos el grosor de la línea del rectángulo
   ObjectSetInteger(0,name,OBJPROP_WIDTH,width);
//--- conectamos (true) o desconectamos (false) el modo de rellenado del rectángulo
   ObjectSetInteger(0,name,OBJPROP_FILL,fill);
//--- representamos en el plano anterior (false) o posterior (true)
   ObjectSetInteger(0,name,OBJPROP_BACK,false);
  }


3.8. Representación del gráfico "Renko" en el gráfico principal

Teniendo en cuenta el uso de las matrices comunes de los búfers de calculación, la función de representación del gráfico "Renko" en el gráfico principal o "func_draw_renko_main_chart" ha salido de forma bastante compacta.

Como parámetros de entrada actúan: el color del "ladrillo" ascendente o descendente, así como su marco, dos tipos de grosor de marcos (el primero para el "ladrillo" y el segundo para su marco), y tres parámetros de representación (el de los "ladrillos", el de su relleno y el de su marco).

Para comenzar se declaran las variables que contienen los nombres de los objetos, después se abre un ciclo en el que se realiza la generación del nombre de cada objeto y, dependiendo del tipo del "ladrillo" precedente, tiene lugar la llamada de las funciones de construcción de los objetos gráficos de los tipos "línea de tendencia" y "rectángulo", con la transmisión correspondiente a los mismos de los parámetros de las matrices de los búfers de calculación.

//+------------------------------------------------------------------+
//| Func Draw Renko Main Chart                                       |
//+------------------------------------------------------------------+
void func_draw_renko_main_chart(color color_square_up,
                                color color_square_down,
                                color color_frame_up,
                                color color_frame_down,
                                int width_square,
                                int width_frame,
                                bool square,
                                bool fill,
                                bool frame)
  {
   string name_square;
   string name_frame;

   for(int z=2; z<=a; z++)
     {
      name_square=IntegerToString(magic_numb)+"_Square_"+IntegerToString(z);
      name_frame=IntegerToString(magic_numb)+"_Frame_"+IntegerToString(z);
      if(type_box[z]==1)
        {
         if(square==true)func_create_square_or_rectangle(name_square,up_price[z],down_price[z],time_box[z-1],time_box[z],width_square,color_square_up,fill);
         if(frame==true)func_create_square_or_rectangle(name_frame,up_price[z],down_price[z],time_box[z-1],time_box[z],width_frame,color_frame_up,false);
        }
      if(type_box[z]==-1)
        {
         if(square==true)func_create_square_or_rectangle(name_square,up_price[z],down_price[z],time_box[z-1],time_box[z],width_square,color_square_down,fill);
         if(frame==true)func_create_square_or_rectangle(name_frame,up_price[z],down_price[z],time_box[z-1],time_box[z],width_frame,color_frame_down,false);
        }
     }
  }

3.9. Representación de un gráfico del tipo "ZigZag" en el gráfico principal

La siguiente función, que por cierto es un complemento bastanete peculiar al indicador, es una función de representación del gráfico en "ZigZag" o "func_draw_zig_zag".

Los parámetros de entrada son: el tipo de dibujado (según los precios máximos o mínimos, o bien según los puntos de representación del gráfico), el gosor de la línea y el color de la línea ascendente y descendente.

Un ejemplo del cambio del parámetro "zig_zag_shadow" se puede observar en el dibujo 4, aquí se ve que, al poner la posición "true", el indicador dibuja una línea en "ZigZag" sobre los límites de las sombras (es decir, según los precios máximos o mínimos), y en la posición "false", la línea "ZigZag" se dibuja según los máximos y mínimos del gráfico "Renko".


Dib.4. Ejemplo de la influencia del parámetro "zig_zag_shadow" en EURUSD, H1, 10 puntos.

Dib.4. Ejemplo de la influencia del parámetro "zig_zag_shadow" en EURUSD, H1, 10 puntos. 

Dado que para la construcción del objeto "línea de tendencia" es necesario que existan dos puntos (inicial y final), introducimos dos variables, que serán responsables del parámetro del precio, y dos variables que serán responsables del parámetro de la fecha. Establecemos el primer punto, dependiendo del tipo del "ladrillo" inicial, con ayuda de los operadores convencionales.

Después viene el ciclo con ayuda del cual se lleva a cabo la construcción de todos los objetos. Como se puede ver, el ciclo comienza con el análisis del segundo "ladrillo", dado que el primer punto ya ha sido establecido. Acto seguido, se comprueba con ayuda del operador convencional si ha cambiado el tipo de "ladrillo" (la dirección del movimiento del precio). Después se rellena la variable encargada de poner nombre al objeto, y dependiendo del tipo del cambio de movimiento, con ayuda de los operadores convencionales, se lleva a cabo la bifurcación del ciclo. A su vez, la bifurcación se divide en dos variantes, dependiendo del parámetro del tipo de dibujado.

Si se usa la representación según los precios máximos y mínimos, entonces se implican las matrices dinámicas de datos anteriormente descritas Price_high[] y Price_low[]. Con su ayuda se lleva a cabo la búsqueda de los puntos máximos y mínimos aproximados, dicha búsqueda se ve limitada por las barras contiguas.

Si la construcción se efectúa mediante los puntos de representación del gráfico, entonces todo es muy sencillo, pues se asignan los datos de las matrices de los búfers de cálculo.

Después de determinar con dos puntos de representación, se llama ya la función conocida de construcción del objeto gráfico "línea de tendencia". Con ello la función finaliza el análisis y la construcción del gráfico "ZigZag".

//+------------------------------------------------------------------+
//| Func Draw Zig Zag                                                |
//+------------------------------------------------------------------+
void func_draw_zig_zag(bool price_shadow,
                       int line_width,
                       color line_color_up,
                       color line_color_down)
  {
   double price_1=0;
   double price_2=0;
   datetime date_1=0;
   datetime date_2=0;

   if(type_box[1]==1)price_1=down_price[1];
   if(type_box[1]==-1)price_1=up_price[1];
   date_1=time_box[1];
   int id=0; // variable de almacenamiento del identificador de la matriz Low & High
   int n=0;  // variable para la formación del nombre

   string name_line; //--- variable responsable del nombre del objeto "línea de tendencia"

   for(int z=2; z<=a; z++)
     {
      if(type_box[z]!=type_box[z-1])
        {
         n++;
         name_line=IntegerToString(magic_numb)+"_Line_"+IntegerToString(n);
         if(type_box[z]==1)
           {
            if(price_shadow==true)
              {
               id=number_id[z-1];
               if((id-1)>0 && Price_low[id-1]<Price_low[id])id--;
               if(Price_low[id+1]<Price_low[id])id++;
               price_2=Price_low[id];
               date_2=(datetime)Date[id];
              }
            else
              {
               price_2=down_price[z-1];
               date_2=time_box[z-1];
              }
            func_create_trend_line(name_line,price_1,price_2,date_1,date_2,line_width,line_color_down);
            price_1=price_2;
            date_1=date_2;
           }
         if(type_box[z]==-1)
           {
            if(price_shadow==true)
              {
               id=number_id[z-1];
               if((id-1)>0 && Price_high[id-1]>Price_high[id])id--;
               if(Price_high[id+1]>Price_high[id])id++;
               price_2=Price_high[id];
               date_2=(datetime)Date[id];
              }
            else
              {
               price_2=up_price[z-1];
               date_2=time_box[z-1];
              }
            func_create_trend_line(name_line,price_1,price_2,date_1,date_2,line_width,line_color_up);
            price_1=price_2;
            date_1=date_2;
           }
        }
     }
  }

3.10. Función para la eliminación de objetos gráficos

Con anterioridad se inventó el uso del número "mágico" para determinar la pertenencia de los objetos al indicador, esto se hacía para que fuese más cómodo iniciar varios indicadores en un solo gráfico y para que fuese más cómodo eliminar los objetos.

La siguiente función es la función de eliminación de objetos o "func_delete_objects". Aquí sólo hay dos parámetros de entrada: el nombre (se determina dependiendo del tipo de objeto: línea de tendencia o rectángulo) y el número de objetos. La función va revisando por turno todos los objetos, y si los objetos con el nombre asignado existen, entonces la función procede a eliminarlos.

//+------------------------------------------------------------------+
//| Func Delete Objects                                              |
//+------------------------------------------------------------------+
void func_delete_objects(string name,
                         int number)
  {
   string name_del;
   for(int x=0; x<=number; x++)
     {
      name_del=name+IntegerToString(x);
      ObjectDelete(0,name_del);
     }
  }

Para mayor comodidad ha sido creada una función que consolida dentro de sí las funciones de borrado de todos los objetos creados por el indicador.

//+------------------------------------------------------------------+
//| Func All Delete                                                  |
//+------------------------------------------------------------------+
void func_all_delete()
  {
//--- cálculo de los objetos gráficos
   obj=ObjectsTotal(0,-1,-1);
//--- eliminación de todos los objetos gráficos pertenecientes al indicador
   func_delete_objects(IntegerToString(magic_numb)+"_Line_",obj);
   func_delete_objects(IntegerToString(magic_numb)+"_Square_",obj);
   func_delete_objects(IntegerToString(magic_numb)+"_Frame_",obj);
//--- redibujado del gráfico
   ChartRedraw(0);
  }


3.11. Función para la creación de niveles

Otra función no muy voluminosa, creada para una representación más cómoda del gráfico en la ventana del indicador, es la función de creación de niveles o "func_create_levels". Sólo tiene dos parámetros de entrada: la cantidad de niveles creados y el color de los niveles.

En el cuerpo principal de la función se establece la cantidad de niveles representados con la ayuda de la función IndicatorSetInteger, después, por turnos, en el ciclo se determina un nivel de precio y un color para cada nivel.

//+------------------------------------------------------------------+
//| Func Create Levels                                               |
//+------------------------------------------------------------------+
void func_create_levels(int level_number,
                        color level_color)
  {
//--- establecer cantidad de niveles en la ventana del indicador
   IndicatorSetInteger(INDICATOR_LEVELS,level_number);
//--- desde qué ladrillo se comienza el dibujado de los niveles
   int k=0;
   if(a>level_number)k=a-level_number;
//--- designación de los precios de los niveles
   for(int z=0;(z<=level_number && k<=a); z++,k++)
     {
      IndicatorSetDouble(INDICATOR_LEVELVALUE,z,up_price[k]);
      IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,level_color);
     }
  }

3.12. Función de consolidación

Para unir todas las funciones estudiadas más arriba se creó una función de consolidación o "func_concolidation".

La función sólo tiene una tarea: llamar todas las funciones ejecutoras, dependiendo de los parámetros configurados.

//+------------------------------------------------------------------+
//| Func Consolidation                                               |
//+------------------------------------------------------------------+
void func_concolidation()
  {
//--- borrado de todos los objetos gráficos pertenecientes al indicador
   func_all_delete();
//--- obtención de la fecha actual
   date_stop=TimeCurrent();
//--- cambio de la fecha inicial en vista de las posibilidades limitadas del tamaño del búfer
   if((bars=Bars(_Symbol,time_frame,date_start,date_stop))>ArraySize(Price))
     {
      date_start=func_calc_date_start(date_start,date_stop);
      Alert("La fecha inicial fue cambiada por el sistema, debido a la falta de espacio en el gráfico");
      date_change=true;
      //--- cálculo de las barras en el intervalo de tiempo para el cálculo
      bars=Bars(_Symbol,time_frame,date_start,date_stop);
     }
//---
   bool result_copy_price=func_copy_price(Price,time_frame,date_start,date_stop,type_price);
   bool result_copy_date=func_copy_date(Date,time_frame,date_start,date_stop);
//--- cambiamos el parámetro de cambio de fecha
   if(result_copy_price=true && result_copy_date==true)date_change=false;
//---
   if(zig_zag_shadow==true)
     {
      func_copy_price(Price_high,time_frame,date_start,date_stop,2);
      func_copy_price(Price_low,time_frame,date_start,date_stop,3);
     }
//---
   func_draw_renko(Price,Date,filter_number,shadow_print,type_step,step);
   if(zig_zag==true)func_draw_zig_zag(zig_zag_shadow,zig_zag_width,zig_zag_color_up,zig_zag_color_down);
//---
   func_draw_renko_main_chart(square_color_up,square_color_down,frame_color_up,frame_color_down,square_width,frame_width,square_draw,square_fill,frame_draw);
   func_create_levels(levels_number,levels_color);
//--- redibujado del gráfico
   ChartRedraw(0);
  }

3.13. Funciones OnCalculate() y OnChartEvent()

Antes de pasar a la función OnCalculate(), veamos aún una pequeña función para el análisis de la aparición de una nueva barra o "func_new_bar".

La función constituye un tipo simplificado de la función descrita en IsNewBar.

//+------------------------------------------------------------------+
//| Func New Bar                                                     |
//+------------------------------------------------------------------+
bool func_new_bar(ENUM_TIMEFRAMES period_time)
  {
//---
   static datetime old_times; // variable de almacenamiento de valores antiguos
   bool res=false;            // variable del resultado del análisis  
   datetime new_time[1];      // hora de la nueva barra
//---
   int copied=CopyTime(_Symbol,period_time,0,1,new_time); // copiamos la hora de la nueva barra en la celda new_time  
//---
   if(copied>0) // todo está en orden. los datos han sido copiados
     {
      if(old_times!=new_time[0])    // si la hora de la barra antigua no es igual a la de la nueva
        {
         if(old_times!=0) res=true; // si no es el primer inicio, entonces verdadero = nueva barra
         old_times=new_time[0];     // recordamos la hora de la barra
        }
     }
//---
   return(res);
  }

La función OnCalculate() es responsable del inicio de la función de consolidación en el caso de que aparezca una nueva barra en el periodo de actualización del gráfico, es decir, durante el periodo que se haya establecido en los parámetros del indicador. 

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---
   if(func_new_bar(time_redraw)==true)
     {
      func_concolidation();
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

La función OnChartEvent() tiene dos acciones: limpiar el gráfico en caso de que se pulse el botón "C" (tiene lugar la llamada de la función de borrado de objetos gráficos) y el redibujado del gráfico (llamada de la función de consolidación) en caso de que se pulse el botón "R".

//+------------------------------------------------------------------+
//| OnChartEvent                                                     |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // identificador de acontecimiento  
                  const long& lparam,   // parámetro de acontecimiento de tipo long
                  const double& dparam, // parámetro de acontecimiento de tipo double
                  const string& sparam) // parámetro de acontecimiento de tipo string
  {
//--- acontecimiento de pulsación del botón en el teclado
   if(id==CHARTEVENT_KEYDOWN)
     {
      if(lparam==82) //--- se pulsó el botón "R"
        {
         //--- llamada de la función de consolidación
         func_concolidation();
        }
      if(lparam==67) //--- se pulsó el botón "C"
        {
         //--- limpiar el gráfico de objetos construidos del indicador
         func_all_delete();
        }
     }
  }


3.14. Función OnDeinit()

Y, al fin, la función de salida o OnDeinit(). La función llama sólo la función de borrado de todos los objetos gráficos creados por el indicador.

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+ 
void OnDeinit(const int reason)
  {
//--- limpiar el gráfico de objetos construidos del indicador
   func_all_delete();
  }


4. Uso práctico del gráfico "Renko"

El gráfico "Renko" está orientado, básicamente, a la determinación de la tendencia principal de movimiento.

Un ejemplo del funcionamiento clásico en el gráfico sería - "vende cuando el "ladrillo" ascendente se vuelva descendente, y compra cuando el "ladrillo" descendente se vuelva ascendente".

Una ilustración de este método clásico se puede observar en el dib. 5:


Dib.5. Ejemplo de funcionamiento clásico en el gráfico "Renko" en EURUSD (H4, 20 puntos)

Dib.5. Ejemplo de funcionamiento clásico en el gráfico "Renko" en EURUSD (H4, 20 puntos)

En el dibujo 5 se han marcado los seis puntos posibles (A,B,C,D,E,F) para la entrada en el mercado.

En el punto "A" se puede ver que después del "ladrillo" ascendente se ha formado un "ladrillo" descendente.

La entrada se realiza directamente, aquí, como en los otros puntos (B,C,D), el primer "ladrillo" formado en dirección contraria se forma con un movimiento, lo cual no se puede decir del punto "E", dado que en ese caso se formaron dos "ladrillos" de golpe con un solo movimiento. Esto se puede ver por la formación de las sombras que acaban a un mismo nivel hacia abajo.

En este caso es posible la entrada entre los puntos "E" y "F", como se puede ver por el gráfico no es una jugada del todo acertada, ya que el precio va en dirección contraria, en el punto "F" la situación es análoga. Ahí, de igual forma, se forman dos "ladrillos" a causa de un solo movimiento. Esto se puede ver por las sombras hacia arriba, que acaban a un mismo nivel. Aunque se ha tratado de un movimiento fuerte, el precio ha mantenido su dirección.

Podemos extraer la conclusión de que cuando el mercado está más tranquilo, y un movimiento forma sólo un "ladrillo" (podemos deducirlo por las sombras) en la dirección contraria, entonces es el momento más adecuado para entrar en el mercado, pero si vemos la formación de varios "ladrillos" de golpe, enotonces la entrada podría no ser segura.

La representación de tipo "ZigZag" en este gráfico se puede usar para el análisis gráfico, en el dib. 6 se muestran varios ejemplos así: la determinación de la línea de "apoyo" y "resistencia", así como los modelos "Cabeza y hombros".


 Dib.6. Ejemplo del uso del análisis gráfico en GBPUSD (H4, 20 puntos)

Dib.6. Ejemplo del uso del análisis gráfico en GBPUSD (H4, 20 puntos)

Otro ejemplo más del análisis gráfico es el "Canal equidistante", mostrado en el dib. 7.

Para una construcción más precisa, el indicador se construyó sobre el análisis del time frame de horas, y la conclusión de la construcción se hizo sobre el time frame de cuatro horas.

Estas configuraciones permiten estudiar las señales en varios time frames a la vez, es decir, en uno se puede aplicar un cierto indicador, y en el segundo se puede aplicar otro.

Dib.7. Ejemplo del análisis gráfico "Canal equidistante" USDCHF, H4, configuración H1, 20 puntos. 

Dib.7. Ejemplo del análisis gráfico "Canal equidistante" USDCHF, H4, configuración H1, 20 puntos

En el dib. 8 tenemos otro ejemplo de cómo obtener señales de distintos time frames en un solo gráfico .

Podemos ver que el gráfico por horas muestra los posibles virajes más cercanos, el gráfico de cuatro horas los alisa (filtra las señales sobrantes), y el gráfico diurno confirma las direcciones de larga duración de la tendencia.

Dib.8. Ejemplo de utilización del indicador en GBPUSD, H1, H4 y D1 

Dib.8. Ejemplo de utilización del indicador en GBPUSD, H1, H4 y D1

Otro ejemplo más, orientado precisamente al indicador mostrado en el dib. 9. La norma dicta: "construye la línea ascendente entre los dos "ladrillos" rojos más cercanos que tengan al menos un "ladrillo" azul entre ellos; posteriormente, al formarse un "ladrillo", bajo la línea, vende".

Y al contrario: "construye la línea descendente entre los dos "ladrillos" azules más cercanos que tengan al menos un "ladrillo" rojo entre ellos; posteriormente, al formarse un "ladrillo", bajo la línea, vende".

Los colores de la norma se muestran de acuerdo con el dib. 9. En el dib. 9, con flechitas azules y rojas se indican los lugares que sirven de apoyo para la representación de la línea, y con con flechas más grandes se indican los lugares que sirven de señales para la compra o la venta.

Dib.9. Ejemplo de utilización del indicador en GBPUSD, H4, 25 puntos 

Dib.9. Ejemplo de utilización del indicador en GBPUSD, H4, 25 puntos

Conclusión

El gráfico "Renko" será interesante tanto para los principiantes, como para los profesionales del mercado. Tras muchos años en circulación, no se ha quedado atrás y hasta el día de hoy encuentra aplicación en el mercado.

En este artículo he pretendido atraer la atención sobre este gráfico y modernizar el análisis de gráficos "Ренко". He tratado de hablar con más detalle sobre los principios de representación del gráfico "Renko", sobre el principio de funcionamiento del indicador y sus peculiaridades.

Estaré encantado de recibir vuestros comentarios y propuestas, así como cualquier modificación y ampliación que propongáis. En el artículo se muestran varias vertientes del uso del indicador. También podéis encontrar vuestras propias variantes en la utilización del mismo.

Muchas gracias a todos por el interés mostrado por el artículo. Os deseo suerte en el comercio y que encontréis nuevas estrategias comerciales.


Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/792

Archivos adjuntos |
abcr.mq5 (77.52 KB)
Fundamentos de programación en MQL5 - Arrays Fundamentos de programación en MQL5 - Arrays

Junto con las variables y las funciones, los arrays forman prácticamente una parte integrante de cualquier lenguaje de programación. Este artículo puede ser interesante en primer lugar para los principiantes que se han puesto a estudiar la programación en MQL5. Mientras que los programadores experimentados tienen una buena oportunidad de generalizar y sistematizar sus conocimientos.

El reproductor de trading basado en el historial de las transacciones El reproductor de trading basado en el historial de las transacciones

El reproductor de trading. Solo son cuatro palabras, y no requieren ninguna explicación. Le viene a la mente un pequeño dispositivo con botones. Empieza a reproducir al presionar un botón y cambia la velocidad de reproducción al mover la palanca. En realidad, es muy parecido. En este artículo, quiero mostrar el reproductor que he desarrollado y que reproduce el historial de las operaciones como si fuera en tiempo real. El artículo trata algunos matices de la programación orientada a objetos, el trabajo con indicadores y la gestión de los gráficos.

Cómo trabajar con el módem GSM de un experto de MQL5 Cómo trabajar con el módem GSM de un experto de MQL5

En la actualidad existen medios suficientes para monitorizar a distancia una cuenta comercial, con toda comodidad: con la ayuda de los terminales móviles, las notificaciones push y el trabajo con ICQ. Pero para todo ello se debe tener conexión a internet. Este artículo describe la creación un experto que les permitirá mantenerse en contacto con su terminal comercial, incluso en el caso de que el internet móvil no está disponible, más concretamente con ayuda de llamadas y mensajes SMS.

Pegado de contrato de futuros en MetaTrader 5 Pegado de contrato de futuros en MetaTrader 5

El análisis técnico de los contratos de futuros (futuros, en lo sucesivo) se ve dificultado por la breve duración de su circulación. En gráficos relativamente cortos resulta difícil llevar a cabo el análisis técnico, por ejemplo, la cantidad de barras en el gráfico diurno de futuros en el índice de la bolsa ucraniana UX-9.13 es de algo más de 100. Por eso al trader le surge la cuestión sobre la construcción de instrumentos sintéticos sobre los futuros. En el artículo veremos el tema del pegado de la historia de los contratos de futuros con diferentes fechas de duración en el terminal MetaTrader 5.