Indicador para la representación del gráfico "ruptura en tres líneas"

Dmitriy Zabudskiy | 12 agosto, 2014

Introducción

En artículos anteriores ya estudiamos los gráficos: "Punto y Figura", "Kagi", "Renko". Continuando con la serie de artículos sobre gráficos del siglo XIX, esta vez vamos a hablar del gráfico "ruptura en tres líneas" (Three Line Break), y más concretamente sobre su implementación con ayuda de código de programa. La información sobre la procedencia de este gráfico es muy escasa, pero puedo suponer que originariamente apareció en Japón. En EE.UU y Europa sólo se llegó a conocer después de la publicación en 1994 del libro de Steve Nison «Más allá de las velas japonesas» (Beyond Candlesticks).

Como sucede en los gráficos mencionados más arriba, el gráfico "Ruptura en tres líneas" ignora la escala temporal, basando su construcción en los precios de cierre de un determinado marco temporal que se forman de nuevo, lo que permite filtrar las oscilaciones de precio poco significativas con respecto al movimiento anterior.

En el libro de Steve Nison «Más allá de las velas japonesas» se dan once reglas para construir un gráfico (pag. 185). Yo voy a intentar fundirlas en tres.

Echemos un vistazo más detallado al ejemplo de una representación clásica de la historia (en la fig. 1).

Fig.1 Ejemplo de representación del gráfico "Ruptura en tres líneas" (EURUSD H1 27.06.2014)

Fig.1 Ejemplo de representación del gráfico "Ruptura en tres líneas" (EURUSD, H1, 27.06.2014)

En el dib. 1 a la izquierda, tenemos el gráfico "Velas japonesas", a la derecha se muestra el gráfico "Ruptura en tres líneas". El gráfico se representa sobre la pareja EURUSD, marco temporal H1. El comienzo de la representación del gráfico es el 27.06.2014 sobre el precio 1.3613 (el precio de cierre de la vela 00:00), después de la vela (01:00) se cierra en 1.3614, formando con ello la primera línea ascendente del gráfico "ruptura en tres líneas", después va la vela del "oso" de dirección (02:00), así se forma la línea descendente con el cierre en 1.3612 (precio de cierre inferior al mínimo anterior).

A continuación, los "toros" salen de nuevo, desplazando el precio hacia la marca 1.3619 (03:00) formando un nuevo máximo y línea. A la vela en 04:00 le resulta imposible romper el mínimo, es decir, esto no queda reflejado en la representación de ninguna manera. Acto seguido, la vela en 05:00 se cierra en 1.3623, formando ahora un nuevo máximo (una nueva línea ascendente).

Ahora, para representar la línea descendente es necesario superar dos mínimos (1.3613), pero los "toros" no ceden su posición y forman un nuevo máximo 1.3626 (06:00). Después los "osos" durante dos horas intentan bajar el mercado, pero la tendencia continúa y se alcanza un nuevo máximo 1.3634 (09:00), los "toros" lideran de nuevo. Ahora, para representar la línea descendente ya es necesario superar tres mínimos (1.3626; 1.3623 y 1.3619).

Las siguinetes tres horas observamos cómo los "osos" dominan el mercado, haciéndolo bajar hasta la marca de los 1.3612 (12:00), y formando con ello una línea descendente. Pero las cinco horas siguientes, los "toros", toman el mercado con sus "cuernos" y lo hacen subir hasta la marca de los 1.3641, superando el máximo anterior en 1.3626 y formando una nueva línea ascendente en 17:00. Los "Osos" a las 18:00 no pueden superar el mínimo anterior, y las cinco horas restantes, los "toros" elevan el mercado hasta la marca de los 1.3649, formando así cada hora una nueva línea ascendente.


Fundamentos de representación del gráfico

Hablemos ahora un poco sobre el propio indicador, ¿en qué se diferencia de los demás y por qué? A la pregunta de "por qué", la respuesta es obvia, para facilitar el análisis del mercado y la búsqueda de nuevas estrategias. ¿Y qué es lo que tiene de nuevo? Pues la verdad es que podríamos decir que bastante. En el indicador se contempla la sustitución de precios para el cálculo (abarca los cuatro precios estándar de las barras). En la variante clásica se contempla la representación de sólo uno de los precios, y el modo modernizado permite utilizar los cuatro tipos de precios (open, high, low y close), lo que cambia el aspecto clásico de representación, añadiendo "sombra" a las líneas, haciendo de ellas "velas japonesas", lo que amplia la comprensión visual del gráfico.

En la variante modernizada existen ajustes con respecto a la sincronización de los datos de los precios según el tiempo, y se sustituyen por precios prioritarios si se perciben precios que no alcanzan.

El aspecto modernizado de la representación del gráfico se muestra en la fig. 2:

Fig.2 Representación modificada del gráfico en base a los cuatro precios

Fig.2 Representación modificada del gráfico en base a los cuatro precios

Dado que la representación modernizada unifica los cuatro gráficos de "ruptura en tres líneas" según diferentes aspectos de precio, entonces lo lógico es topar con problemas de divergencia de precios. Para que esto no suceda, es necesario llevar a cabo la sincronización de datos conforme al tiempo. La sincronización de precios se realiza en dos variantes, la total (fig. 2 a la derecha) y la parcial (fig. 2 a la izquierda). La sincronización total constituye una sincronización parcial filtrada, en la cual se organizan todos los datos, y los datos que no alcanzan se sustituyen por los precios prioritarios,  indicados en los ajustes. En el primer modo de sincronización, los datos que no alcanzan simplemente se deshechan, y y sólo se representan aquellas "velas" para las que existe un conjunto completo de datos.

Otra de las novedades es la designación de los separadores de los periodos, para mayor comodidad al separar las señales. Como ya se sabe, los separadores de periodos se activan en los ajustes del gráfico, en el indicador se cambian también, dependiendo del marco temporal indicado en los ajustes. A diferencia de los gráficos en MetaTrader 5, donde la separación se lleva a cabo en base a las líneas punteadas verticales, en el indicador esto se realiza con el cambio de color de la línea (de la "vela", fig. 3):

Fig.3 Designación de los separadores de periodos en el indicador

Fig.3 Designación de los separadores de periodos en el indicador

Otra pequeña adición ha sido la implantación del indicador técnico iMA, que se se construía con los precios del gráfico principal, pero en lo que respecta al tiempo se sincroniza con los datos del indicador, produciendo con ello mismo la filtración de los datos de la media móvil (fig. 4):

Fig.4 Media móvil interna

Fig.4 Media móvil interna

Asímismo, en el indicador se ha agregado una opción por la que se puede establecer el movimiento mínimo en puntos para la formación de la línea, y la cantidad de líneas que se necesitan para formar el viraje, lo cual actúa como un pequeño filtro.


Código del indicador

El algoritmo del indicador es bastante sencillo e incluye tres estadios: la copia de datos, el cálculo en base a los datos copiados y el llenado de los búffers del indicador (representación en el gráfico de los datos obtenidos). El código al completo se divide en funciones que están estrechamente entrelazadas entre sí, o bien con datos de entrada. Veamos con más detalle el código completo.

1. Parámetros de entrada del indicador

En el preámbulo del indicador se incluye la explicación de las construcciones gráficas, hay dos de ellas en el indicador: la construcción principal, el gráfico "ABCTB" (DRAW_COLOR_CANDLES) y la media móvil adicional "LINE_TLB" (DRAW_LINE), correspondiente a los búffers 6. A continuación van los datos de la enumeración enum (para mejorar el interfaz de ajustes) y los ajustes propiamente dichos:

A continuación se anuncian las matrices de los búffers, las variables y las estructuras utilizadas para los cálculos.

//+------------------------------------------------------------------+
//|                                                        ABCTB.mq5 |
//|                                 "Azotskiy Aktiniy ICQ:695710750" |
//|                        "https://www.mql5.com/ru/users/Aktiniy" |
//+------------------------------------------------------------------+
// ABCTB - Auto Build Chart Three Line Break
#property copyright "Azotskiy Aktiniy ICQ:695710750"
#property link      "https://www.mql5.com/ru/users/Aktiniy"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 6
#property indicator_plots   2
//--- plot ABCTB
#property indicator_label1  "ABCTB"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrBlue,clrRed,clrGreenYellow
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot LINE_TLB
#property indicator_label2  "LINE_TLB"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- tipo de precio para el cálculo
enum type_price
  {
   close=0, // Close
   open=1,  // Open
   high=2,  // Hight
   low=3,   // Low
  };
//--- tipo de representación
enum type_build
  {
   classic=0,  // Clásica
   modified=1, // Modificada
  };
//--- nivel de prioridad
enum priority
  {
   highest_t=4, // Máximo
   high_t=3,    // Alto
   medium_t=2,  // Medio
   low_t=1,     // Bajo
  };
//--- input parameters
input long               magic_numb=65758473787389;                // Número mágico
input ENUM_TIMEFRAMES    time_frame=PERIOD_CURRENT;                // Periodo de cálculo
input ENUM_TIMEFRAMES    time_redraw=PERIOD_M1;                    // Periodo de actualización del gráfico
input datetime           first_date_start=D'2013.03.13 00:00:00';  // Fecha incial
input type_price         chart_price=close;                        // Tipo de precio para el cálculo (0-Close, 1-Open, 2-High, 3-Low)
input int                step_min_f=4;                             // Salto mínimo para la nueva columna (>0)
input int                line_to_back_f=3;                         // Cantidad de líneas para el viraje (>0)
input type_build         chart_type=classic;                       // Tipo de representación (0-clásico, 1-modificado)
input bool               chart_color_period=true;                  // Cambio de color en el paso a otro periodo
input bool               chart_synchronization=true;               // Representación del gráfico solo con sincronización completa
input priority           chart_priority_close=highest_t;           // Nivel de prioridad del precio de cierre
input priority           chart_priority_open=highest_t;            // Nivel de prioridad del precio de apertura
input priority           chart_priority_high=highest_t;            // Nivel de prioridad del precio máximo
input priority           chart_priority_low=highest_t;             // Nivel de prioridad del precio mínimo
input bool               ma_draw=true;                             // Dibujar la media
input ENUM_APPLIED_PRICE ma_price=PRICE_CLOSE;                     // Tipo de precio de representación de la media
input ENUM_MA_METHOD     ma_method=MODE_EMA;                       // Tipo de representación
input int                ma_period=14;                             // Periodo de promediación
//--- indicator buffers
//--- búffers del gráfico
double         ABCTBBuffer1[];
double         ABCTBBuffer2[];
double         ABCTBBuffer3[];
double         ABCTBBuffer4[];
double         ABCTBColors[];
//--- búffer de la media
double         LINE_TLBBuffer[];
//--- variables
MqlRates rates_array[];// matriz de todos los datos según las barras para el análisis
datetime date_stop;    // fecha actual
datetime date_start;   // variable de la fecha inicial para los cálculos
//+------------------------------------------------------------------+
//| Struct Line Price                                                |
//+------------------------------------------------------------------+
struct line_price// estructura de guardado de información sobre las líneas pasadas
  {
   double            up;  // valor del precio superior
   double            down;// valor del precio inferior
  };
//+------------------------------------------------------------------+
//| Struct Line Information                                          |
//+------------------------------------------------------------------+
struct line_info// estructura de guardado de información sobre las líneas para el uso común
  {
   double            up;
   double            down;
   char              type;
   datetime          time;
  };
line_info line_main_open[];  // datos sobre el gráfico según los precios de apertura
line_info line_main_high[];  // datos sobre el gráfico según los precios máximos
line_info line_main_low[];   // datos sobre el gráfico según los precios mínimos
line_info line_main_close[]; // datos sobre el gráfico según los precios de cierre
//+------------------------------------------------------------------+
//| Struct Buffer Info                                               |
//+------------------------------------------------------------------+
struct buffer_info// estructura de guardado de datos para el llenado de búffer
  {
   double            open;
   double            high;
   double            low;
   double            close;
   char              type;
   datetime          time;
  };
buffer_info data_for_buffer[];// datos para el llenado del búffer de representación modificada
datetime array_datetime[];    // matriz para el guardado de información sobre la hora de cada línea
int time_array[3];            // matriz para la función func_date_color
datetime time_variable;       // variable para la función func_date_color
bool latch=false;             // variable activadora de la función func_date_color
int handle;                   // handle del indicador iMA
int step_min;                 // variable del salto mínimo
int line_to_back;             // variable de la cantidad de líneas para el viraje

2. Función OnInit 

En la función OnInit se anuncian todos los búffers de indicador y se establece la indicación de las matrices como en las series temporales.

A continuación establecemos qué valores del indicador no serán dibujados, establecemos el nombre, elegimos la precisión de las indicaciones y quitamos los valores actuales para la representación (estos atascan el gráfico). En este mismo lugar establecemos el handle del indicador iMA, y comprobamos que los datos introducidos sean correctos (su correcta condición). En caso de error, dará el mensaje correspondiente y el valor se cambiará al mínimo permisible.

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
//--- búffers para el gráfico
   SetIndexBuffer(0,ABCTBBuffer1,INDICATOR_DATA);
   ArraySetAsSeries(ABCTBBuffer1,true);
   SetIndexBuffer(1,ABCTBBuffer2,INDICATOR_DATA);
   ArraySetAsSeries(ABCTBBuffer2,true);
   SetIndexBuffer(2,ABCTBBuffer3,INDICATOR_DATA);
   ArraySetAsSeries(ABCTBBuffer3,true);
   SetIndexBuffer(3,ABCTBBuffer4,INDICATOR_DATA);
   ArraySetAsSeries(ABCTBBuffer4,true);
   SetIndexBuffer(4,ABCTBColors,INDICATOR_COLOR_INDEX);
   ArraySetAsSeries(ABCTBColors,true);
//--- búffer para la representación de la media
   SetIndexBuffer(5,LINE_TLBBuffer,INDICATOR_DATA);
   ArraySetAsSeries(LINE_TLBBuffer,true);
//--- establecemos qué valores no van a ser dibujados
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0); // para el gráfico
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0); // para la media
//--- establecemos el aspecto externo del indicador
   IndicatorSetString(INDICATOR_SHORTNAME,"ABCTB "+IntegerToString(magic_numb)); // nombre del indicador
//--- precisión de la representación
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
//--- prohibimos la muestra de resultados de los valores actuales para el indicador
   PlotIndexSetInteger(0,PLOT_SHOW_DATA,false);
   PlotIndexSetInteger(1,PLOT_SHOW_DATA,false);
//---
   handle=iMA(_Symbol,time_frame,ma_period,0,ma_method,ma_price);
   if(step_min_f<1)
     {
      step_min=1;
      Alert("El salto mínimo para la nueva columna debe ser superior a cero");
     }
   else step_min=step_min_f;
//---
   if(line_to_back_f<1)
     {
      line_to_back=1;
      Alert("La cantidad de líneas para la muestra del viraje debe ser superior a cero");
     }
   else line_to_back=line_to_back_f;
//---
   return(INIT_SUCCEEDED);
  }

3. Función de copia de datos

Dado que el funcionamiento del indicador está previsto con los cuatro tipos de precio, es necesario llevar a cabo la copia de todos los datos, incluyendo la hora. En el lenguaje MQL5 existe una estructura para guardar la información sobre la hora de inicio de la sesión, los precios, los volúmenes y el spread, se llama MqlRates.

Los parámetros de entrada de la función son: fecha de inicio y fin, marco temporal y matriz de recepción del tipo MqlRates. Si se copia con éxito, la función retorna true. La copia de datos se efectúa a través de una matriz intermedia, donde se copia la cantidad de datos calculados que no alcanzan, más una sesión (los últimos datos se actualizan constantemente). Si la copia de la matriz intermedia ha tenido éxito, los datos se copian en la matriz transmitida para el funcionamiento de la función.

//+------------------------------------------------------------------+
//| Func All Copy                                                    |
//+------------------------------------------------------------------+
bool func_all_copy(MqlRates &result_array[],// matriz de la respuesta
                   ENUM_TIMEFRAMES period,  // marco temporal
                   datetime data_start,     // fecha de inicio
                   datetime data_stop)      // fecha del final
  {
//--- anuncio de las variables auxiliares
   bool x=false;       // variable para la respuesta de la función
   int result_copy=-1; // cantidad de datos copiados
//--- añadido de variables y matrices para el cálculo
   static MqlRates interim_array[]; // matriz temporal dinámica para el guardado de los datos copiados
   static int bars_to_copy;         // cantidad de barras para copiar
   static int bars_copied;          // cantidad de barras ya copiadas desde la fecha de inicio
//--- conocemos la cantidad actual de barras en el intervalo temporal
   bars_to_copy=Bars(_Symbol,period,data_start,data_stop);
//--- calculamos la cantidad de barras que hay que copiar
   bars_to_copy-=bars_copied;
//---  si los datos se copian, pero no por primera vez
   if(bars_copied>0)
     {
      bars_copied--;
      bars_to_copy++;
     }
//--- cambiamos el tamaño de la matriz receptora
   ArrayResize(interim_array,bars_to_copy);
//--- copiamos los datos en la matriz temporal
   result_copy=CopyRates(_Symbol,period,0,bars_to_copy,interim_array);
//--- comprobamos el resultado de la copia de datos
   if(result_copy!=-1) // si la copia de datos en la matriz intermedia ha tenido éxito
     {
      ArrayCopy(result_array,interim_array,bars_copied,0,WHOLE_ARRAY); // copiamos los datos de la matriz temporal a la básica
      x=true;                   // asignamos la respuesta afirmativa a la función
      bars_copied+=result_copy; // aumentamos el valor de los datos copiados
     }
//---
   return(x);
  }

4. Función de cálculo de datos

La función es un prototipo de cálculo para la representación clásica del gráfico "Ruptura en tres líneas". Repetiré de nuevo que la función sólo calcula los datos y los dispone en una matriz especial de tipo estructura line_info, anunciada al inicio del código del indicador.

Dentro de la función también entran dos funciones más, func_regrouping (reagrupación) y func_insert (pegado), vamos primero a echarles un vistazo:

4.1. Función de reagrupación

Esta función tiene como cometido reagrupar la información sobre las líneas consecutivas en una dirección. Su acción se ve limitada por las dimensiones de la matriz que se le transmite, y para ser más exactos, por el parámetro de los ajustes del indicador line_to_back_f (cantidad de líneas para la muestra del viraje). Es decir, cada vez que el mando se transmite a la función, todos los datos recibidos sobre líneas solitarias se desplazan en un índice hacia final, y el índice cero lo llena un nuevo valor.

De esta forma se efectúa el guardado de la información sobre las líneas, imprescindibles para su ruptura (en el caso clásico, de tres líneas).

//+------------------------------------------------------------------+
// Func Regrouping                                                   |
//+------------------------------------------------------------------+
void func_regrouping(line_price &input_array[],// matriz para la reagrupación
                     double new_price,         // nuevo valor de precio
                     char type)                // tipo de movimiento
  {
   int x=ArraySize(input_array);// conocemos el tamaño de la reagrupación
   for(x--; x>0; x--)           // ciclo de reagrupación
     {
      input_array[x].up=input_array[x-1].up;
      input_array[x].down=input_array[x-1].down;
     }
   if(type==1)
     {
      input_array[0].up=new_price;
      input_array[0].down=input_array[1].up;
     }
   if(type==-1)
     {
      input_array[0].down=new_price;
      input_array[0].up=input_array[1].down;
     }
  }

 4.2. Función de pegado

 La función lleva a cabo el pegado de los valores en la matriz de respuesta, el código es sencillo y no requiere explicación. 

//+------------------------------------------------------------------+
// Func Insert                                                       |
//+------------------------------------------------------------------+
void func_insert(line_info &line_m[],  // matriz receptora
                 line_price &line_i[], // matriz fuente
                 int index,            // elemento a pegar de la matriz
                 char type,            // tipo de columna recibida
                 datetime time)        // fecha
  {
   line_m[index].up=line_i[0].up;
   line_m[index].down=line_i[0].down;
   line_m[index].type=type;
   line_m[index].time=time;
  }

Convencionalmente, la función de cálculo se divide en tres partes. La primera parte es el copiado de los datos investigados en la matriz intermedia, con ayuda del operador switch, se copia sólo el precio investigado. La segunda parte realiza el ensayo preliminar para el cálculo del lugar necesario en la matriz de datos. A continuación se cambia la matriz de datos line_main_array[], que había sido transmitida en un principio a la función para la respuesta . Y la tercera parte rellena la matriz de datos, ya medida.

//+------------------------------------------------------------------+
//| Func Build Three Line Break                                      |
//+------------------------------------------------------------------+
void func_build_three_line_break(MqlRates &input_array[],      // matriz para el análisis
                                 char price_type,              // tipo de precio analizado (0-Close, 1-Open, 2-High, 3-Low)
                                 int min_step,                 // salto mínimo para formar la línea
                                 int line_back,                // cantidad de líneas para el viraje
                                 line_info &line_main_array[]) // matriz de retorno (respuestas) de la función
  {
//--- calculamos el tamaño de la matriz a analizar
   int array_size=ArraySize(input_array);
//--- extraemos los datos necesarios para calcular en la matriz intermedia
   double interim_array[];// matriz intermedia
   ArrayResize(interim_array,array_size);// adaptamos la matriz intermedia al tamaño de los datos
   switch(price_type)
     {
      case 0: // Close
        {
         for(int x=0; x<array_size; x++)
           {
            interim_array[x]=input_array[x].close;
           }
        }
      break;
      case 1: // Open
        {
         for(int x=0; x<array_size; x++)
           {
            interim_array[x]=input_array[x].open;
           }
        }
      break;
      case 2: // High
        {
         for(int x=0; x<array_size; x++)
           {
            interim_array[x]=input_array[x].high;
           }
        }
      break;
      case 3: // Low
        {
         for(int x=0; x<array_size; x++)
           {
            interim_array[x]=input_array[x].low;
           }
        }
      break;
     }
//--- introducimos las variables para guardar los datos sobre la posición actual
   line_price passed_line[];// matriz de guardado de datos sobre los últimos precios de las líneas (tipo de estructura line_price)
   ArrayResize(passed_line,line_back+1);
   int line_calc=0;// cantidad de líneas
   int line_up=0;// cantidad de últimas líneas ascendentes
   int line_down=0;// cantidad de últimas líneas descendentes
   double limit_up=0;// límite superior necesario para la determinación
   double limit_down=0;// límite inferior necesario para la determinación
/* Llenamos las variables informativas sobre la posicion actual con los primeros valores */
   passed_line[0].up=interim_array[0];
   passed_line[0].down=interim_array[0];
//--- ponemos en marcha el primer ciclo para el cálculo de los datos obtenidos para el llenado de los búffers de dibujado
   for(int x=0; x<array_size; x++)
     {
      if(line_calc==0)// no se ha formado aún ni una sola línea
        {
         limit_up=passed_line[0].up;
         limit_down=passed_line[0].down;
         if(interim_array[x]>=limit_up+min_step*_Point)// se ha superado el límite superior
           {
            func_regrouping(passed_line,interim_array[x],1);// realizamos la reagrupación
            line_calc++;// actualizamos el contador de líneas
            line_up++;
           }
         if(interim_array[x]<=limit_down-min_step*_Point)// se ha superado el límite inferior
           {
            func_regrouping(passed_line,interim_array[x],-1);// realizamos la reagrupación
            line_calc++;// actualizamos el contador de líneas
            line_down++;
           }
        }
      if(line_up>line_down)// la/s última/s línea/s es/son ascendente/s
        {
         limit_up=passed_line[0].up;
         limit_down=passed_line[(int)MathMin(line_up,line_back-1)].down;
         if(interim_array[x]>=limit_up+min_step*_Point)// se ha superado el límite superior
           {
            func_regrouping(passed_line,interim_array[x],1);// realizamos la reagrupación
            line_calc++;// actualizamos el contador de lineas
            line_up++;
           }
         if(interim_array[x]<limit_down)// se ha superado el límite inferior
           {
            func_regrouping(passed_line,interim_array[x],-1);// realizamos la reagrupación
            line_calc++;// actualizamos el contador de líneas
            line_up=0;
            line_down++;
           }
        }
      if(line_down>line_up)// la/s última/s línea/s es/son descendente/s
        {
         limit_up=passed_line[(int)MathMin(line_down,line_back-1)].up;
         limit_down=passed_line[0].down;
         if(interim_array[x]>limit_up)// se ha superado el límite superior
           {
            func_regrouping(passed_line,interim_array[x],1);// realizamos la reagrupación
            line_calc++;// actualizamos el contador de líneas
            line_down=0;
            line_up++;
           }
         if(interim_array[x]<=limit_down-min_step*_Point)// se ha superado el límite inferior
           {
            func_regrouping(passed_line,interim_array[x],-1);// realizamos la reagrupación
            line_calc++;// actualizamos el contador de líneas
            line_down++;
           }
        }
     }
   ArrayResize(line_main_array,line_calc);// cambiamos el tamaño de la matriz receptora
//--- ponemos a cero las variables y las llenamos con los valores iniciales
   line_calc=0;
   line_up=0;
   line_down=0;
   passed_line[0].up=interim_array[0];
   passed_line[0].down=interim_array[0];
//--- ponemos en marcha el segundo ciclo para el llenado de buffers de dibujado
   for(int x=0; x<array_size; x++)
     {
      if(line_calc==0)// no se ha formado aún ni una sola línea
        {
         limit_up=passed_line[0].up;
         limit_down=passed_line[0].down;
         if(interim_array[x]>=limit_up+min_step*_Point)// se ha superado el límite superior
           {
            func_regrouping(passed_line,interim_array[x],1);// realizamos la reagrupación
            func_insert(line_main_array,passed_line,line_calc,1,input_array[x].time);
            line_calc++;// actualizamos el contador de líneas
            line_up++;
           }
         if(interim_array[x]<=limit_down-min_step*_Point)// se ha superado el límite inferior
           {
            func_regrouping(passed_line,interim_array[x],-1);// realizamos la reagrupación
            func_insert(line_main_array,passed_line,line_calc,-1,input_array[x].time);
            line_calc++;// actualizamos el contador de líneas
            line_down++;
           }
        }
      if(line_up>line_down)// la/s última/s línea/s es/son ascendente/s
        {
         limit_up=passed_line[0].up;
         limit_down=passed_line[(int)MathMin(line_up,line_back-1)].down;
         if(interim_array[x]>=limit_up+min_step*_Point)// se ha superado el límite superior
           {
            func_regrouping(passed_line,interim_array[x],1);// realizamos la reagrupación
            func_insert(line_main_array,passed_line,line_calc,1,input_array[x].time);
            line_calc++;// actualizamos el contador de líneas
            line_up++;
           }
         if(interim_array[x]<limit_down)// se ha superado el límite inferior
           {
            func_regrouping(passed_line,interim_array[x],-1);// realizamos la reagrupación
            func_insert(line_main_array,passed_line,line_calc,-1,input_array[x].time);
            line_calc++;// actualizamos el contador de líneas
            line_up=0;
            line_down++;
           }
        }
      if(line_down>line_up)// la/s última/s línea/s es/son descendente/s
        {
         limit_up=passed_line[(int)MathMin(line_down,line_back-1)].up;
         limit_down=passed_line[0].down;
         if(interim_array[x]>limit_up)// se ha superado el límite superior
           {
            func_regrouping(passed_line,interim_array[x],1);// realizamos la reagrupación
            func_insert(line_main_array,passed_line,line_calc,1,input_array[x].time);
            line_calc++;// actualizamos el contador de líneas
            line_down=0;
            line_up++;
           }
         if(interim_array[x]<=limit_down-min_step*_Point)// se ha superado el límite inferior
           {
            func_regrouping(passed_line,interim_array[x],-1);// realizamos la reagrupación
            func_insert(line_main_array,passed_line,line_calc,-1,input_array[x].time);
            line_calc++;// actualizamos el contador de líneas
            line_down++;
           }
        }
     }
  }

5. Función de representación del gráfico

La misión de esta función es calcular el gráfico, dependiendo del parámetro de representación elegido (clásico o modificado) y rellenar los búffers del indicador con los datos para la muestra. Al igual que la función precedente, cuenta con tres funciones adicionales (de color, de sincronización y de media móvil). Las vamos a analizar más en profundidad.

5.1. Función de color

La función sólo dispone de un parámetro de entrada, la hora. La respuesta es una variable lógica, si la fecha transmitida es el límite del periodo, entonces la función retornará true. Dado que los periodos dependen del marco temporal elegido, en la función se ha dividido en periodos con el operador if. Después de elegir el periodo, tiene lugar la comprobación sobre si ha llegado el nuevo periodo o no, eso se hace con ayuda de la transformación de la fecha en estructura MqlDateTime y la comparación. Para un marco temporal hasta H2 inclusive, de los cambios de día da fe el cambio de periodo de H12 hasta D1 inclusive se comprueba el cambio de mes, en W1 y MN se comprueba el cambio de año.

Por desgracia, la estructura MqlDateTime no tiene información sobre qué semana es la actual. Esta cuestión se resolvió creando un punto de partida del recuento, en forma de variable time_variable. En lo sucesivo, cada vez se resta a esta fecha la cantidad de segundos que hay en una semana.

//+------------------------------------------------------------------+
// Func Date Color                                                   |
//+------------------------------------------------------------------+
bool func_date_color(datetime date_time) // fecha de entrada
  {
   bool x=false;// variable para la respuesta
   int seconds=PeriodSeconds(time_frame);// conocemos el periodo de cálculo
   MqlDateTime date;
   TimeToStruct(date_time,date);// transformamos los datos
   if(latch==false) // comprobando el estado del pestillo
     {
      MqlDateTime date_0;
      date_0=date;
      date_0.hour=0;
      date_0.min=0;
      date_0.sec=0;
      int difference=date_0.day_of_week-1;
      datetime date_d=StructToTime(date_0);
      date_d=date_d-86400*difference;
      time_variable=date_d;
      latch=true;// cerramos el pestillo
     }
   if(seconds<=7200)// periodo inferior o igual a H2
     {
      if(time_array[0]!=date.day)
        {
         x=true;
         time_array[0]=date.day;
        }
     }
   if(seconds>7200 && seconds<=43200)// periodo superior a H2 pero inferior o igual a H12
     {
      if(time_variable>=date_time)
        {
         x=true;
         time_variable=time_variable-604800;
        }
     }
   if(seconds>43200 && seconds<=86400)// periodo superior a H12 pero inferior o igual a D1
     {
      if(time_array[1]!=date.mon)
        {
         x=true;
         time_array[1]=date.mon;
        }
     }
   if(seconds>86400)// periodo W1 o MN
     {
      if(time_array[2]!=date.year)
        {
         x=true;
         time_array[2]=date.year;
        }
     }
   return(x);
  }

5.2. Función de sincronización

La función de sincronización tiene seis parámetros de entrada, cuatro de los cuales son niveles de prioridad de los precios (cuanto más alto sea el valor, más alta será la prioridad), el parámetro lógico de sincronización total o parcial y la propia matriz para el análisis. De forma convencional, la función está dividida en dos partes: condiciones de las sincronizaciones total y parcial.

La sincronización total se lleva a cabo en 3 pasadas:

  1. Cálculo de los elementos de la matriz que cumplan la condición de presencia de datos conforme a los cuatro tipos de precio,
  2. Copiado de elementos en la matriz intermedia, conforme a la misma condición,
  3. Copia desde la matriz intermedia a la matriz transmitida por los parámetros.

La sincronización parcial es algo más complicada.

La matriz unidimensional de estructuras transmitida se transforma en bidimensional, el primer índice denota el orden, y el segundo, el tipo de precio. A continuación se introduce la matriz unidimensional con los cuatro elementos, en la que se copian los niveles de prioridad de los precios, después en esta matriz se produce una clasificación para esclarecer el orden de prioridad. Acto seguido, con la ayuda del ciclo for y del operador if, realizamos la distribución según prioridades. Además, si las prioridades son iguales, entonces la secuencia de precios será la que sigue: close, open, high, low. En cuanto el operador if encuentra el primer valor según la prioridad, con ayuda del ciclo for, en la matriz bidimensional que se había creado con anterioridad se cambian todos los valores cero por prioritarios, etc.

//+------------------------------------------------------------------+
// Func Synchronization                                              |
//+------------------------------------------------------------------+
void func_synchronization(buffer_info &info[],
                          bool synchronization,
                          char close,
                          char open,
                          char high,
                          char low)
  {
   if(synchronization==true)// realizamos la sincronización completa
     {
      int calc=0;// variable de cálculo
      for(int x=0; x<ArraySize(info); x++)// calculamos la cantidad de datos completos
        {
         if(info[x].close!=0 && info[x].high!=0 && info[x].low!=0 && info[x].open!=0)calc++;
        }
      buffer_info i_info[];    //introducimos la matriz temporal para el copiado
      ArrayResize(i_info,calc);// cambiamos el tamaño de la matriz temporal
      calc=0;
      for(int x=0; x<ArraySize(info); x++)// copiamos los datos en la matriz temporal
        {
         if(info[x].close!=0 && info[x].high!=0 && info[x].low!=0 && info[x].open!=0)
           {
            i_info[calc]=info[x];
            calc++;
           }
        }
      ZeroMemory(info);        // limpiamos la matriz receptora
      ArrayResize(info,calc);  // cambiamos el tamaño de la matriz principal
      for(int x=0; x<calc; x++)// copiamos los datos de la matriz temporal en la principal
        {
         info[x]=i_info[x];
        }
     }
   if(synchronization==false)  // cambiamos los valores cero por los prioritarios
     {
      int size=ArraySize(info); // medimos el tamaño de la matriz
      double buffer[][4];       // creamos una matriz temporal para el cálculo
      ArrayResize(buffer,size); // cambiamos el tamaño de la matriz temporal
      for(int x=0; x<size; x++) // copiamos los datos en la matriz temporal
        {
         buffer[x][0]=info[x].close;
         buffer[x][1]=info[x].open;
         buffer[x][2]=info[x].high;
         buffer[x][3]=info[x].low;
        }
      char p[4];// introducimos la matriz para la clasificación por orden
      p[0]=close; p[1]=open; p[2]=high; p[3]=low;// asignamos las variables para la posterior clasificación
      ArraySort(p); // clasificamos
      int z=0,v=0;  // incialización de las variables utilizadas con frecuencia
      for(int x=0; x<4; x++)// teniendo en cuenta la clasificación, repasamos las variables y las sustituimos según prioridad
        {
         if(p[x]==close)// prioridad sobre precios de cierre
           {
            for(z=0; z<size; z++)
              {
               for(v=1; v<4; v++)
                 {
                  if(buffer[z][v]==0)buffer[z][v]=buffer[z][0];
                 }
              }
           }
         if(p[x]==open)// prioridad sobre precios de apertura
           {
            for(z=0; z<size; z++)
              {
               for(v=0; v<4; v++)
                 {
                  if(v!=1 && buffer[z][v]==0)buffer[z][v]=buffer[z][1];
                 }
              }
           }
         if(p[x]==high)// prioridad sobre precios máximos
           {
            for(z=0; z<size; z++)
              {
               for(v=0; v<4; v++)
                 {
                  if(v!=2 && buffer[z][v]==0)buffer[z][v]=buffer[z][2];
                 }
              }
           }
         if(p[x]==low)// prioridad sobre precios mínimos
           {
            for(z=0; z<size; z++)
              {
               for(v=0; v<3; v++)
                 {
                  if(buffer[z][v]==0)buffer[z][v]=buffer[z][3];
                 }
              }
           }
        }
      for(int x=0; x<size; x++)// copiamos de vuelta lo datos de la matriz temporal
        {
         info[x].close=buffer[x][0];
         info[x].open=buffer[x][1];
         info[x].high=buffer[x][2];
         info[x].low=buffer[x][3];
        }
     }
  }

5.3. Función de la media móvil

Es la función más sencilla, según el handle del indicador obtenido en la función OnInit, se lleva a cabo el copiado del valor según la fecha transmitada en los parámetros de la función, y luego se transmite en respuesta a la función.

//+------------------------------------------------------------------+
// Func MA                                                           |
//+------------------------------------------------------------------+
double func_ma(datetime date)
  {
   double x[1];
   CopyBuffer(handle,0,date,1,x);
   return(x[0]);
  }

De forma convencional, la función de construcción del gráfico se divide en dos partes: la construcción clásica y la modificada. La función disponde de dos parámetros de entrada: el tipo de precio para la construcción (que se ignora en la construcción modificada) y el tipo de construcción (clásico o modificado).

Justo al comienzo se lleva a cabo la limpieza de los búffers del indicador, después, dependiendo del tipo de construcción se divide en dos partes. La primera parte (construcción modificada) comienza con la llamada de la función de cálculo de datos según los cuatro tipos de precios, a continuación creamos la matriz común de datos, donde se copian todas las fechas utilizadas, obtenidas durante la llamada de las funciones de cálculo de datos. Después, la matriz de fechas obtenida se clasifica y limpia de datos presentes por partida doble. Tras ello, en base a las fechas consecutivas, se rellena la matriz data_for_buffer[], anunciada a nivel global y se realiza la sincronización de datos. La etapa definitiva es el llenado de los búffers de indicador.

La segunda parte (construcción clásica) es mucho más sencilla: primero se llama la función de cálculo, después se llenan los búffers del indicador.

//+------------------------------------------------------------------+
//| Func Chart Build                                                 |
//+------------------------------------------------------------------+
void func_chart_build(char price, // tipo de los precios para la representación
                      char type)  // tipo de representación
  {
//--- ponemos a cero los búffers
   ZeroMemory(ABCTBBuffer1);
   ZeroMemory(ABCTBBuffer2);
   ZeroMemory(ABCTBBuffer3);
   ZeroMemory(ABCTBBuffer4);
   ZeroMemory(ABCTBColors);
   ZeroMemory(LINE_TLBBuffer);
   if(type==1)// construimos una representación modíficada (respecto a todos los tipos de precio)
     {
      func_build_three_line_break(rates_array,0,step_min,line_to_back,line_main_close);// datos sobre los precios de cierre
      func_build_three_line_break(rates_array,1,step_min,line_to_back,line_main_open);// datos sobre los precios de apertura
      func_build_three_line_break(rates_array,2,step_min,line_to_back,line_main_high);// datos sobre los precios máximos
      func_build_three_line_break(rates_array,3,step_min,line_to_back,line_main_low);// datos sobre los precios mínimos
      //--- calculamos las matrices de los datos
      int line_main_calc[4];
      line_main_calc[0]=ArraySize(line_main_close);
      line_main_calc[1]=ArraySize(line_main_open);
      line_main_calc[2]=ArraySize(line_main_high);
      line_main_calc[3]=ArraySize(line_main_low);
      //--- reunimos las matrices de las fechas
      int all_elements=line_main_calc[0]+line_main_calc[1]+line_main_calc[2]+line_main_calc[3];// conocemos la cantidad de todos los elementos
      datetime datetime_array[];// introducimos matriz para el copiado
      ArrayResize(datetime_array,all_elements);
      int y[4];
      ZeroMemory(y);
      for(int x=0;x<ArraySize(datetime_array);x++)// copiamos los datos en la matriz
        {
         if(x<line_main_calc[0])
           {
            datetime_array[x]=line_main_close[y[0]].time;
            y[0]++;
           }
         if(x<line_main_calc[0]+line_main_calc[1] && x>=line_main_calc[0])
           {
            datetime_array[x]=line_main_open[y[1]].time;
            y[1]++;
           }
         if(x<line_main_calc[0]+line_main_calc[1]+line_main_calc[2] && x>=line_main_calc[0]+line_main_calc[1])
           {
            datetime_array[x]=line_main_high[y[2]].time;
            y[2]++;
           }
         if(x>=line_main_calc[0]+line_main_calc[1]+line_main_calc[2])
           {
            datetime_array[x]=line_main_low[y[3]].time;
            y[3]++;
           }
        }
      ArraySort(datetime_array);// clasificamos las matrices
      //--- llevamos a cabo el proceso de limpieza de la matriz de datos presentes por partida doble
      int good_info=1;
      for(int x=1;x<ArraySize(datetime_array);x++)// contamos la cantidad de información necesaria
        {
         if(datetime_array[x-1]!=datetime_array[x])good_info++;
        }
      ArrayResize(array_datetime,good_info);
      array_datetime[0]=datetime_array[0];// copiamos el primer elemento (dado que es una muestra al inicio de la comparación)
      good_info=1;
      for(int x=1;x<ArraySize(datetime_array);x++)// llenamos la nueva matriz con los datos necesarios
        {
         if(datetime_array[x-1]!=datetime_array[x])
           {
            array_datetime[good_info]=datetime_array[x];
            good_info++;
           }
        }
      //--- rellenamos el búffer de dibujado (velas de colores)
      int end_of_calc[4];// variables de guardado de información sobre la última comparación
      ZeroMemory(end_of_calc);
      ZeroMemory(data_for_buffer);
      ArrayResize(data_for_buffer,ArraySize(array_datetime));// cambiamos el tamaño de la matriz anunciada globalmente para el guardado de los datos antes de ser transmitidos al búffer
      for(int x=0; x<ArraySize(array_datetime); x++)
        {
         data_for_buffer[x].time=array_datetime[x];
         for(int s=end_of_calc[0]; s<line_main_calc[0]; s++)
           {
            if(array_datetime[x]==line_main_close[s].time)
              {
               end_of_calc[0]=s;
               if(line_main_close[s].type==1)data_for_buffer[x].close=line_main_close[s].up;
               else data_for_buffer[x].close=line_main_close[s].down;
               break;
              }
           }
         for(int s=end_of_calc[1]; s<line_main_calc[1]; s++)
           {
            if(array_datetime[x]==line_main_open[s].time)
              {
               end_of_calc[1]=s;
               if(line_main_open[s].type==1)data_for_buffer[x].open=line_main_open[s].down;
               else data_for_buffer[x].open=line_main_open[s].up;
               break;
              }
           }
         for(int s=end_of_calc[2]; s<line_main_calc[2]; s++)
           {
            if(array_datetime[x]==line_main_high[s].time)
              {
               end_of_calc[2]=s;
               data_for_buffer[x].high=line_main_high[s].up;
               break;
              }
           }
         for(int s=end_of_calc[3]; s<line_main_calc[3]; s++)
           {
            if(array_datetime[x]==line_main_low[s].time)
              {
               end_of_calc[3]=s;
               data_for_buffer[x].low=line_main_low[s].down;
               break;
              }
           }
        }
      //--- ponemos en marcha la función de sincronización de datos
      func_synchronization(data_for_buffer,chart_synchronization,chart_priority_close,chart_priority_open,chart_priority_high,chart_priority_low);
      //--- acciones preparatorias para el inicio de la función func_date_color
      ZeroMemory(time_array);
      time_variable=0;
      latch=false;
      //--- llenamos el búffer de dibujado de velas
      for(int x=ArraySize(data_for_buffer)-1,z=0; x>=0; x--)
        {
         ABCTBBuffer1[z]=data_for_buffer[x].open;
         ABCTBBuffer2[z]=data_for_buffer[x].high;
         ABCTBBuffer3[z]=data_for_buffer[x].low;
         ABCTBBuffer4[z]=data_for_buffer[x].close;
         if(ABCTBBuffer1[z]<=ABCTBBuffer4[z])ABCTBColors[z]=0;
         if(ABCTBBuffer1[z]>=ABCTBBuffer4[z])ABCTBColors[z]=1;
         if(func_date_color(data_for_buffer[x].time)==true && chart_color_period==true)ABCTBColors[z]=2;
         if(ma_draw==true)LINE_TLBBuffer[z]=func_ma(data_for_buffer[x].time);
         z++;
        }
     }
   else// construimos la representación clásica (según un tipo de precio)
     {
      func_build_three_line_break(rates_array,price,step_min,line_to_back,line_main_close);// encontramos los datos de los precios seleccionados
      ArrayResize(array_datetime,ArraySize(line_main_close));
      //--- acciones preparatorias para el inicio de la función func_date_color
      ZeroMemory(time_array);
      time_variable=0;
      latch=false;
      //--- llenamos el búffer de dibujado de velas
      for(int x=ArraySize(line_main_close)-1,z=0; x>=0; x--)
        {
         ABCTBBuffer1[z]=line_main_close[x].up;
         ABCTBBuffer2[z]=line_main_close[x].up;
         ABCTBBuffer3[z]=line_main_close[x].down;
         ABCTBBuffer4[z]=line_main_close[x].down;
         if(line_main_close[x].type==1)ABCTBColors[z]=0;
         else ABCTBColors[z]=1;
         if(func_date_color(line_main_close[x].time)==true && chart_color_period==true)ABCTBColors[z]=2;
         if(ma_draw==true)LINE_TLBBuffer[z]=func_ma(line_main_close[x].time);
         z++;
        }
     }
  }

6. Función de consolidación

En esta función se reúnen todos los elemntos de gestión del indicador. Primero se determina la fecha actual, después se llama la función de copiado de datos y la función de construcción del gráfico.

//+------------------------------------------------------------------+
//| Func Consolidation                                               |
//+------------------------------------------------------------------+
void func_consolidation()
  {
//--- determinamos fecha actual
   date_stop=TimeCurrent();
//--- copiado de datos para el análisis
   func_all_copy(rates_array,time_frame,first_date_start,date_stop);
//--- representación principal del gráfico
   func_chart_build(chart_price,chart_type);
   ChartRedraw();
  }

7. Función de gestión de la representación mediante botones y de manera automática

Los datos de la función están destinados a dibujar el indicador con ayuda del botón "R" (OnChartEvent) en el teclado, y también de manera automática a través de un intervalo de tiempo indicado (OnCalculate), la nueva barra analizada con ayuda de la función (func_new_bar) constituye un aspecto simplificado de la función descrita en IsNewBar.

//+------------------------------------------------------------------+
//| 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_consolidation();
     };
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- evento de pulsación de la tecla en el teclado
   if(id==CHARTEVENT_KEYDOWN)
     {
      if(lparam==82) //--- se ha pulsado la tecla "R"
        {
         func_consolidation();
        }
     }
  }
//+------------------------------------------------------------------+
//| Func New Bar                                                     |
//+------------------------------------------------------------------+
bool func_new_bar(ENUM_TIMEFRAMES period_time)
  {
//---
   static datetime old_times; // variable de guardado de valores antiguos
   bool res=false;            // variable de 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 última barra en la celda new_time  
//---
   if(copied>0) // todo está bien. 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 verdad = nueva barra
         old_times=new_time[0];     // guardamos la hora de la barra
        }
     }
//---
   return(res);
  }

Y con esto terminamos la descripción del código del indicador, ahora veamos cómo se puede usar.


Ejemplos de uso del indicador y estrategia comercial

Empecemos con las estrategias de análisis básicas, fundamentadas en la representación clásica del gráifico.

1. Líneas blancas y negras como señales de compra y venta

En general, se puede hablar de dos reglas:

  1. Regla №1: Compra cuando haya tres líneas ascendentes consecutivas, vende cuando haya tres líneas descendentes consecutivas. Tres líneas consecutivas sirven como confirmación de la aparición de una tendencia.
  2. Regla №2: Vende cuando se forme la caída de la línea de viraje más abajo de las tres líneas ascendentes consecutivas, compra cuando la elevación de la línea de viraje sea superior a las tres líneas descendentes consecutivas.

Veamos la fig.6, en la que se muestra la representación clásica de EURUSD H1 desde principios del año 2013 (el periodo de análisis propiamente dicho se muestra en la fig.5).

Fig.5 Periodo EURUSD H1 investigado

Fig.5 Periodo EURUSD H1 investigado

Fig.6 Rerpresentación clásica del gráfico "Ruptura en tres líneas", EURUSD H1, comienzos del año 2013, precios de cierre

Fig.6 Rerpresentación clásica del gráfico "Ruptura en tres líneas", EURUSD H1, comienzos del año 2013, precios de cierre

En el gráfico (fig. 6) se ve claramente la señal (regla №1) entre los puntos 1 y 2, lo que sirve de punto de arranque para la compra. Como se puede ver, la ganancia en este caso es de más de 200 puntos en los cuatro símbolos. El punto siguiente 4 ya es de compra (según la regla №2). Al cerrar en el punto 5 el beneficio es de 40 puntos, con el cierre en el punto 6 tendremos cero pérdidas.

En el punto 6 tenemos señal de venta (regla №2), si se cierra en el punto 7 obtendremos 10 puntos de beneficio, cerrando en el punto 8 tendremos cero pérdidas. Los puntos 8 y 9 no se pueden considerar señal, ya que ni el primero ni el segundo se ajustan a lo especificado por la regla. En el punto 10 se puede comprar (regla №1), si se cierra en el punto 11 obtendremos 20 puntos de beneficio (o cero pérdidas en el punto 12). Todas las cifras han sido redondeadas.

Con esta estrategia, en el mejor de los casos obtendremos un beneficio de 270 puntos, lo cual, estará de acuerdo, no es poco. Pero al mismo ptiempo, en el periodo estudiado tenemos un fuerte movimiento, lo cual influye en el beneficio. En el peor de los casos tendremos cero pérdidas, lo que tampoco es mal resultado.

Hay que señalar también que después de aparecer el modelo según la regla primera o segunda, se debe esperar aún la confirmación del viraje de tendencia en forma de una línea en la dirección de la tendencia.

2. Canal equidistante, líneas de apoyo y resistencia

Otra estrategia comercial es incluir el análisis técnico en el gráfico "Ruptura en tres líneas", veamos la fig. 7:

 Fig.7 Canal equidistante y líneas de apoyo y resistencia, GBPUSD H1, periodo desde el 01.03.2014 hasta el 01.05.2014

Fig. 7 Canal equidistante y líneas de apoyo y resistencia, GBPUSD H1, periodo desde el 01.03.2014 hasta el 01.05.2014

En el dib. 7 se muestra con líneas rojas el canal equidistante descendente, el canal ascendente se representa con líneas azules, las líneas de apoyo y resistencia se muestran en color negro. Se puede ver cómo la primera línea de resistencia cambia a línea de apoyo.

3. Modelos de velas japonesas

El gráfico en la representación modificada (ruptura en dos líneas) en el marco temporal M30 con la pareja USDCAD a principios del año 2013 ha dado un resultado bastante interesante.

Se ven claramente los modelos de velas japonesas, que justificaron el trabajo de sus señales (fig. 8).

Fig.8 Gráfico modificado "ruptura en tres líneas", USDCAD M30, comienzos de 2013, ruptura en dos líneas

Fig. 8 Gráfico modificado "ruptura en tres líneas", USDCAD M30, comienzos de 2013, ruptura en dos líneas

Al comienzo del gráfico con el número 1 se muestra el modelo de viraje "absorción", que consta de dos velas: una roja y la anterior, azul. Después de la tendencia ascendente el mercado desciende hasta la cifra 2, lo que representa el modelo de viraje de una sola vela "martillo", y el mercado cambia su dirección. Una situación análoga se da con el modelo número 3 ("Lobito"). El siguiente modelo de viraje es "Harami" (número 4) que forma una vela con el número 4 y la siguiente vela grande ascendente ante ella. El modelo con el número 6 también consta de dos velas (modelo "Absorción"), pero a diferencia del primero, este vira el mercado en la otra dirección.

De aquí podemos sacar la conclusión de que el uso del indicador en esta forma de análisis es aplicable, pero tiene sus defectos (las señales aparecen con bastante poca frecuencia y es posible que se de una pequeña pérdida). Hay que finalizar el desarrollo de dicha estrategia

4. Media móvil

Una modificación peculiar del indicador (añadir una media móvil del gráfico principal sólo en las líneas representadas) da nuevas posibilidades al análisis.

Veamos la fig. 9:

Fig.9 Análisis según la media móvil, EURUSD H4, gráfico "ruptura en tres líneas", representación clásica, desde el 01.01.2014 hasta el 01.07.2014

Fig.9 Análisis según la media móvil, EURUSD H4, gráfico "ruptura en tres líneas", representación clásica, desde el 01.01.2014 hasta el 01.07.2014

En la parte superior de la fig. 9 se muestra la representación clásica según los precios más altos (high) con la media móvil (periodo de promediación de 90, tipo de precio low, promediación suavizada). En la parte inferior se muestra la representación clásica según los precios más bajos (low) con la media móvil (periodo de promediación de 90, tipo de precio high, promediación suavizada).

En la parte superior de la fig. 9 se puede ver la media móvil como una línea de apoyo, y en la parte inferior, al contrario, como una línea de resistencia. Si el precio en ambos gráficos se sale de la media hacia abajo, significa que en el mercado existe una tendencia descendente, por lo que hay que vender. Si el precio se sale de la media hacia arriba, hay que comprar. La desventaja de esta estrategia es que presupone un tipo de comercio a largo plazo.


Conclusión

A modo de conclusión puedo decir que el gráfico "ruptura en tres líneas" da de manera estable señales con beneficio, o en el peor de los casos conlleva cero pérdidas. He acabado por tener la idea de que el comercio con él es bueno sólo a largo plazo, así que no recomiendo utilizar este gráfico en el comercio a corto plazo. Si a alguien le surge alguna idea de tipo comercial sobre esta estrategia, será un placer charlar sobre ello.

Como siempre, he intentado explorarla con detalle, pero si alguien tiene ideas que puedan complementar, ampliar u optimizar el código, por favor escríbanme.