Estudio de las figuras técnicas de Merrill

Alexander Fedosov | 16 septiembre, 2019

Contenido

Introducción

El primer intento de crear un sistema de las figuras de precios fue tomado por Robert Levy en 1971. Usaba las figuras de cinco puntas de la oscilación del precio, y luego, testeaba su relevancia. No consiguió resultados significativos, pero dentro de 10 años, su trabajo fue continuado por Arthur Merrill.

Dividió las figuras en dos categorías, que tenían la forma de las letras inglesas M y W. Cada categoría contaba con 16 figuras. Además, cada categoría tenía sus propias subcategorías. Merrill propuso 6 subcategorías:

Nuestra tarea consiste en averiguar hasta qué punto las figuras técnicas de Merrill nos pueden ser útiles en las condiciones actuales. Para eso, ha sido diseñada una aplicación que nos permitirá ejecutar el testeo. Aparte de eso, sería interesante aplicar este modelo a diferentes tipos de datos, tales como el precio de cierre, sus máximos y mínimos y los indicadores del tipo oscilatorio.

Teoría y área de aplicación

Para que esté bien claro de qué manera y para qué datos de entrada vamos a aplicar el modelo de las figuras técnicas de Merrill, es necesario aclarar qué es lo que representan. Hay dos categorías principales: son figuras que tienen la forma de las letras inglesas M y W. Se llaman los patrones de M y los patrones de W. Cada una de estas categorías incluye 16 figuras.

En la figura 1, se muestran 16 patrones M que, de una manera u otra, parecen a la letra M. Como podemos observar, su diferencia consiste en la ubicación mutua de cinco puntos que forman el patrón:  


Fig. 1 Representación visual de los patrones M

La figura 2 muestra 16 patrones W que parecen a la letra W. Vamos a encontrar un conjunto común de estos dos grupos en los gráficos de precios y en los indicadores, además, vamos a analizar, evaluar y buscar posibles regularidades.

Fig. 2 Representación visual de los patrones W

Es que la idea esencial de cualquier patrón consiste en lo siguiente: cuando aparece una u otra formación, esperar, con un determinado grado de probabilidad, la aparición del precio en la dirección deseada y obtener el beneficio.

Vamos a mostrar unos ejemplos para aclarar al máximo en qué campo y cómo vamos a analizar las figuras técnicas de Merrill. En la figura 3, se muestra un gráfico lineal habitual de los precios de USDCAD en el marco temporal (timeframe) H1. Hoy en día, este tipo de representación no se usa muy a menudo, porque las velas japonesas y las barras han encontrado mayor aceptación.

Fig. 3 Gráfico lineal de los precios de cierre de USDCAD, en H1

Aquí, incluso a simple vista, podemos identificar algunas figuras técnicas descritas más arriba. Será el primer campo del estudio, es decir, la aplicación al gráfico lineal con los precios de cierre. Además, comprobaremos los gráficos lineales con los precios de apertura, máximos y mínimos. 

El segundo campo del análisis abarca los indicadores del tipo oscilatorio:

Como método para evaluar las figuras técnicas que van a aplicarse tanto al precio, como a los indicadores oscilatorios descritos, usaremos la técnica propuesta en mi artículo  Estudio de técnicas de análisis de velas (Parte I): comprobando los patrones existentes. Su idea es bastante simple:


Desarrollando la herramientas para el testeo

Antes de empezar a desarrollar las herramientas, tenemos que aclarar qué configuraciones y ajustes debe contener. Va a incluir un panel con dos pestañas: Análisis y Configuraciones. Además, usaremos los ajustes de la ventana de las configuraciones del EA. En total, habrá tres secciones principales que incluirán sus herramientas para trabajar con figuras técnicas. Ahora, describiremos las configuraciones de cada sección.

La pestaña Análisis incluye lo siguiente:

  1. Dos conjuntos de botones para seleccionar los tipos de las figuras técnicas a testear. Además, incluye los botones All M y All W para seleccionar/quitar rápidamente los grupos de figuras tipo M y W.
  2. Conjunto de botones para seleccionar los timeframes a testear y el botón ALL para seleccionar/quitar el grupo entero de botones.
  3. Campo de entrada «Valor límite de tendencia en puntos». Es un valor del beneficio en puntos que debe ser alcanzado por el precio máximo a lo largo de tres velas tras la identificación de la figura de Merrill analizada.
  4. Botón que abre la ventana de diálogo para seleccionar la fecha Inicial y Final y la hora del testeo. 
  5. Campo de entrada con casilla de verificación (checkbox) y un botón representa un filtro para buscar instrumentos financieros necesarios. Tiene un preset — Major. Muestra los pares de divisas mayores. El checkbox desactiva el filtro y muestra todos los instrumentos disponibles.
  6. Los símbolos seleccionados en la tabla a través del filtro, al seleccionar los cuales, se realiza el análisis de las figuras técnicas.
  7. Tablas de los resultados compuesta de siete columnas: 
    • Nombre de la figura. En la columna, se muestra el nombre de la figura analizada según Merrill. Por ejemplo, M10 o W 12.
    • Encontrado. Número de figuras encontradas del tipo establecido en la muestra predeterminada.
    • Timeframe. Muestra el timeframe en el que ha sido realizado el análisis de la figura especificada.
    • P, Uptrend. Es la probabilidad del movimiento del precio hasta el valor «Tendencia límite en puntos» después de la aparición de una figura técnica en la dirección alcista.
    • P, Dntrend. Es la probabilidad del movimiento del precio hasta el valor «Tendencia límite en puntos» después de la aparición de una figura técnica en la dirección bajista.
    • K, UpTrend/K, DnTrend. Es el coeficiente descrito en mi artículo Estudio de técnicas de análisis de velas (Parte I): comprobando los patrones existentes. Su idea consiste en estimar con qué rapidez el precio alcance el profit establecido después de la aparición de la figura técnica analizada en la tendencia alcista y bajista.

La figura 4 muestra la representación visual de todas las herramientas y parámetros descritos.

Fig. 4 Implementación de la pestaña Análisis

Ahora, hablaremos de la segunda pestaña, Configuraciones:

  1. Indicador utilizado. Permite seleccionar un indicador al que va a aplicarse la búsqueda y el análisis de las figuras técnicas de Merrill.
  2. Coeficientes de pesos. Se usan en el cálculo de los coeficientes descritos antes: K, UpTrend/DnTrend. 
  3. Idioma de la interfaz. Lista desplegable para seleccionar el idioma de la interfaz: ruso o inglés.

Puede ver la pestaña con ajustes en la imagen de abajo:

Fig. 5 Implementación de la pestaña Configuraciones

La última sección de las configuraciones utiliza la ventana «Propiedades del EA» (atajo de teclado F7) donde se establecen los ajustes del indicadores aplicados que figuran debajo del encabezado Indicador usado. La figura 6 muestra la ventana de la última sección de las configuraciones.

Fig. 6 Ventana de ajustes de indicadores usados

A la hora de definir los ajustes en esta ventana, hay que tomar en cuenta las siguientes particularidades:

Vamos a considerar ahora la implementación informática de la interfaz de la aplicación descrita anteriormente, así como, la técnica de la búsqueda y del análisis de las figuras técnicas de Merrill.

Para crear la interfaz gráfica, se usa el método CreateGUI(), que se compone de los métodos que crean la ventana principal de la interfaz CreateWindow() y la ventana de diálogo CreateWindowSetting1() para seleccionar el intervalo temporal del análisis.

//+------------------------------------------------------------------+
//| Crea la interfaz gráfica del programa                            |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
  {
//--- Creación del panel
   if(!CreateWindow("Merrill Patterns"))
      return(false);
//--- Creando la ventana de diálogo
   if(!CreateWindowSetting1("Ajuste del intervalo de fechas"))
      return(false);
//--- Terminar la creación de GUI
   CWndEvents::CompletedGUI();
   return(true);
  }

Ahora, veremos de qué se compone cada uno de estos métodos. El primero será la ventana principal de la interfaz. Se compone de la implementación de la pestaña Análisis que incluye los controles descritos en la figura 4.

//+------------------------------------------------------------------+
//| Pestaña Analyze                                                  |
//+------------------------------------------------------------------+
//--- Creando los botones del conjunto de patrones
   if(!CreatePatternSet(m_patterns,10,10))
      return(false);
//--- Encabezado de timeframes
   if(!CreateTFLabel(m_text_labels[1],10,105,0))
      return(false);
//--- Creando los botones del conjunto de timeframes
   if(!CreateTimeframeSet(m_timeframes,10,125,0))
      return(false);
//--- Campo de búsqueda del filtro de símbolos 
   if(!CreateSymbolsFilter(m_symb_filter1,m_request1,10,180,0))
      return(false);
//--- Creando el botón de la selección del intervalo de fechas
   if(!CreateDateRange(m_request3,280,180,0))
      return(false);
//--- Creando el campo de entrada del valor límite del beneficio
   if(!CreateThresholdValue(m_threshold1,400,180,100,0))
      return(false);
//--- Creando la tabla de símbolos
   if(!CreateSymbTable(m_symb_table1,10,225,0))
      return(false);
//--- Creando la tabla de resultados
   if(!CreateTable1(m_table1,120,225,0))
      return(false);

Y también, de la pestaña Configuraciones descrita en la figura 5.

//+------------------------------------------------------------------+
//| Pestaña Settings                                                 |
//+------------------------------------------------------------------+
//---
   if(!CreateButtonsGroup1(10,50))
      return(false);
//--- Etiquetas de texto
   if(!CreateTextLabel(m_text_labels[0],10,100))
      return(false);
   if(!CreateTextLabel(m_text_labels[3],10,10))
      return(false);
//--- Campos de edición
   if(!CreateCoef(m_coef1,10,140,"K1",1))
      return(false);
   if(!CreateCoef(m_coef2,100,140,"K2",0.5))
      return(false);
   if(!CreateCoef(m_coef3,200,140,"K3",0.25))
      return(false);
   if(!CreateLanguageSetting(m_lang_setting,10,180,1))
      return(false);
//--- Barra de estado
   if(!CreateStatusBar(1,26))
      return(false);
//---
   return(true);
  }

Por favor, consulte los códigos fuente adjuntos al artículo para ver la implementación más detallada de los métodos aplicados que implementan los controles de la interfaz.

El método que implementa la ventana de diálogo para seleccionar los intervalos temporales es el siguiente:

//+------------------------------------------------------------------+
//| Crea la ventana de diálogo para seleccionar el intervalo de      |
//| fechas en la pestaña Análisis                                    |
//+------------------------------------------------------------------+
bool CProgram::CreateWindowSetting1(const string caption_text)
  {
//--- Añadimos el puntero de la ventana en el array de ventanas
   CWndContainer::AddWindow(m_window[2]);
//--- Coordenadas
   int x=m_request3.X();
   int y=m_request3.Y()+m_request3.YSize();
//--- Propiedades
   m_window[2].XSize(372);
   m_window[2].YSize(230);
   m_window[2].WindowType(W_DIALOG);

//--- Creando formulario
   if(!m_window[2].CreateWindow(m_chart_id,m_subwin,caption_text,x,y))
      return(false);
//---
   if(!CreateCalendar(m_calendar1,m_window[2],10,25,D'01.01.2019',1))
      return(false);
   if(!CreateCalendar(m_calendar2,m_window[2],201,25,m_calendar2.Today(),1))
      return(false);
//---
   if(!CreateTimeEdit(m_time_edit1,m_window[2],10,200,"Время",1))
      return(false);
   if(!CreateTimeEdit(m_time_edit2,m_window[2],200,200,"Время",1))
      return(false);
//---
   return(true);
  }

Ahora, estudiaremos más detalladamente las técnicas del estudio de las figuras técnicas, su búsqueda y estimación. Para eso, rastrearemos toda la secuencia de acciones de este algoritmo. Primero, examinaremos el archivo MerrillPatterns.mq5 en el que se inicializa este algoritmo.

//--- Incluir la clase de la aplicación
#include "Program.mqh"
CProgram program;
//+------------------------------------------------------------------+
//| Parámetros de entrada del EA                                     |
//+------------------------------------------------------------------+
input ENUM_APPLIED_PRICE   Inp_Price1              =  PRICE_CLOSE;   // Applied price
input int                  Inp_ATR_Peroid          =  5;             // ATR Period
input int                  Inp_CCI_Peroid          =  5;             // CCI Period
input int                  Inp_DeM_Peroid          =  5;             // DeMarker Period
input int                  Inp_ForcePeriod         =  13;            // ForceIndex Period
input ENUM_MA_METHOD       Inp_ForceMAMethod       =  MODE_SMA;      // ForceIndex MA method
input ENUM_APPLIED_PRICE   Inp_ForceAppliedPrice   =  PRICE_CLOSE;   // ForceIndex Applied price
input ENUM_APPLIED_VOLUME  Inp_ForceAppliedVolume  =  VOLUME_TICK;   // ForceIndex Volumes
input int                  Inp_WPR_Period          =  5;             // WPR Period
input int                  Inp_RSI_Period          =  5;             // RSI Period
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(void)
  {
//---
   program.OnInitEvent();
//--- Definimos el panel del trading
   if(!program.CreateGUI())
     {
      ::Print(__FUNCTION__," > Fallo al crear la interfaz gráfica");
      return(INIT_FAILED);
     }
//---
   program.InitializePrice(Inp_Price1);
   program.InitializeATR(Inp_ATR_Peroid);
   program.InitializeCCI(Inp_CCI_Peroid);
   program.InitializeDeM(Inp_DeM_Peroid);
   program.InitializeForce(Inp_ForcePeriod,Inp_ForceMAMethod,Inp_ForceAppliedPrice,Inp_ForceAppliedVolume);
   program.InitializeWPR(Inp_WPR_Period);
   program.InitializeRSI(Inp_RSI_Period);
   return(INIT_SUCCEEDED);
  }

Aparte de los parámetros de entrada de los indicadores, en la sección OnInit(), se crea un envoltorio gráfico, y luego, se realiza la inicialización de los datos definidos en la ventana de Propiedades.Todos los métodos pasan los ajustes externos en las variables internas.

//---
   void              InitializePrice(ENUM_APPLIED_PRICE price)    { m_applied_price=price;        }
   void              InitializeATR(int period)                    { m_atr_period=period;          }
   void              InitializeCCI(int period)                    { m_cci_period=period;          }
   void              InitializeDeM(int period)                    { m_dem_period=period;          }
   void              InitializeWPR(int period)                    { m_wpr_period=period;          }
   void              InitializeRSI(int period)                    { m_rsi_period=period;          }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::InitializeForce(int period,ENUM_MA_METHOD ma_method,ENUM_APPLIED_PRICE price,ENUM_APPLIED_VOLUME volume)
  {
   m_force_period=period;
   m_force_ma_method=ma_method;
   m_force_applied_price=price;
   m_force_applied_volume=volume;
  }
//+-----------------------------------------------------------------

Después de eso, la aplicación está lista para el uso y todas las demás configuraciones se pasan para la interfaz gráfica diseñada. Antes hemos dicho que el inicio del cálculo se realiza de acuerdo con la selección del instrumento financiero de la tabla de los símbolos (punto 6, fig. 4), lo mismo ocurre después de terminar la entrada en «Valor límite de la tendencia en puntos» (punto 3, fig. 4). Ambos eventos inicializan el mismo método ChangeSymbol1() para empezar a recopilar los datos definidos, con el fin de prepararlos para el análisis.

//+------------------------------------------------------------------+
//| Selección del símbolo en la pestaña Análisis                     |
//+------------------------------------------------------------------+
bool CProgram::ChangeSymbol1(const long id)
  {
//--- Comprobando el identificador del elemento
   if(id!=m_symb_table1.Id())
      return(false);
//--- Salir si la línea no ha sido encontrada
   if(m_symb_table1.SelectedItem()==WRONG_VALUE)
     {
      //--- Mostrar la descripción completa del símbolo en la barra de estado
      m_status_bar.SetValue(0,"Símbolo para el análisis no seleccionado");
      m_status_bar.GetItemPointer(0).Update(true);
      return(false);
     }
//--- Obtenemos el símbolo seleccionado
   string symbol=m_symb_table1.GetValue(0,m_symb_table1.SelectedItem());
//--- Mostrar la descripción completa del símbolo en la barra de estado
   string val=(m_lang_index==0)?"Símbolo seleccionado: ":"Selected symbol: ";
   m_status_bar.SetValue(0,val+::SymbolInfoString(symbol,SYMBOL_DESCRIPTION));
   m_status_bar.GetItemPointer(0).Update(true);
//---
   GetResult(symbol);
   return(true);
  }

El sentido de su trabajo consiste en determinar el instrumento comercial seleccionado de la tabla de los símbolos y pasar su valor en la barra de estado y en el método GetResult(). Vamos a considerarlo con más detalle, ya que el trabajo principal se realiza precisamente en este método.

//+------------------------------------------------------------------+
//| Procesamiento de los resultados de la búsqueda de los patrones   |
//+------------------------------------------------------------------+
bool CProgram::GetResult(const string symbol)
  {
//--- Estructura para estimar la eficacia de las figuras
   RATING_SET m_coef[];
//--- Tipos de figuras
   PATTERN_TYPE pattern_types[];
//---
   ArrayResize(pattern_types,33);
   for(int i=0;i<33;i++)
     {
      if(i==16)
         pattern_types[i]=-1;
      if(i<16)
         pattern_types[i]=PATTERN_TYPE(i);
      if(i>16)
         pattern_types[i]=PATTERN_TYPE(i-1);
     }
//--- Definimos los timeframes seleccionados
   GetTimeframes(m_timeframes,m_cur_timeframes);
   int total=ArraySize(m_cur_timeframes);
//--- Comprobación de un timeframe como mínimo
   if(total<1)
     {
      if(m_lang_index==0)
         MessageBox("No ha seleccionado el timeframe de trabajo","Error",MB_OK);
      else if(m_lang_index==1)
         MessageBox("You have not selected a working timeframe!","Error",MB_OK);
      return(false);
     }
   int count=0;
   m_total_row=0;
//--- Eliminar todas las líneas
   m_table1.DeleteAllRows();
//--- Obtener intervalo de fechas
   datetime start=StringToTime(TimeToString(m_calendar1.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit1.GetHours()+":"+(string)m_time_edit1.GetMinutes()+":00");
   datetime end=StringToTime(TimeToString(m_calendar2.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit2.GetHours()+":"+(string)m_time_edit2.GetMinutes()+":00");
//--- Verificando si las fechas establecidas son correctas
   if(start>end || end>TimeCurrent())
     {
      if(m_lang_index==0)
         MessageBox("Intervalo de fechas inválido","Error",MB_OK);
      else if(m_lang_index==1)
         MessageBox("Incorrect date range selected!","Error",MB_OK);
      return(false);
     }
//--- 
   for(int k=0;k<33;k++)
     {
      if(k==16)
         continue;
      //--- Obtener figuras seleccionadas para el análisis
      if(m_patterns[k].IsPressed())
        {
         ArrayResize(m_m_total,total);
         ArrayResize(m_coef,total);
         ZeroMemory(m_m_total);
         ZeroMemory(m_coef);
         count++;
         //--- Cálculo según timeframes
         for(int j=0;j<total;j++)
           {
            double arr[];
            //--- Obtener datos para el análisis
            int copied=GetData(m_buttons_group1.SelectedButtonIndex(),symbol,m_cur_timeframes[j],start,end,arr);
            //---
            if(copied<9)
               MessageBox("Faltan datos para el análisis","Error",MB_OK);
            for(int i=0;i<copied;i++)
              {
               if(i>copied-9)
                  continue;
               //--- Condición para la búsqueda del patrón
               double A=arr[i];
               double B=arr[i+1];
               double C=arr[i+2];
               double D=arr[i+3];
               double E=arr[i+4];
               if(GetPatternType(A,B,C,D,E)==pattern_types[k])
                 {
                  m_m_total[j]++;
                  GetCategory(symbol,i+5,m_coef[j],m_cur_timeframes[j],m_threshold_value1);
                 }
              }
            //--- Añadir resultado a la tabla
            AddRow(m_table1,m_patterns[k].LabelText(),m_coef[j],m_m_total[j],m_cur_timeframes[j]);
           }
        }
     }
//---
   if(count>0)
     {
      //---
      m_table1.DeleteRow(m_total_row);
      //--- Actualizar la tabla
      m_table1.Update(true);
      m_table1.GetScrollVPointer().Update(true);
     }
   else
     {
      if(m_lang_index==0)
         MessageBox("Patrón no seleccionado","Error",MB_OK);
      else if(m_lang_index==1)
         MessageBox("You have not chosen a pattern!","Error",MB_OK);
     }
   return(true);
  }

Para empezar, hay que explicar los tipos de las variables introducidas el principio del método.La primera de ellas representa la estructura RATING_SET.

struct RATING_SET
  {
   int               a_uptrend;
   int               b_uptrend;
   int               c_uptrend;
   int               a_dntrend;
   int               b_dntrend;
   int               c_dntrend;
  };

Incluye 6 variables tipo int y es necesaria para escribir en ellas los resultados respecto a la frecuencia con la que el precio iba en la dirección establecida tras la identificación de la figura, y con qué rapidez la alcanzaba. Por ejemplo, para una tendencia alcista y el valor límite de la tendencia establecido en 100 puntos en el dígito 5, al encontrar una figura y superar el precio de este valor con una vela, la variable a_uptrend recibe un uno; si el precio ha alcanzado 100 puntos con 2 velas, un uno se escribe en b_uptrend. En nuestro método, vamos a usar el array de estas estructuras m_coef[].

El segundo tipo de la variable es PATTERN_TYPE. Es una enumeración en la que se reúnen todos tipos de las figuras técnicas de Merrill.

//+------------------------------------------------------------------+
//| Tipo de la figura                                                |
//+------------------------------------------------------------------+
enum PATTERN_TYPE
  {
   M1,M2,M3,M4,M5,M6,M7,M8,
   M9,M10,M11,M12,M13,M14,M15,M16,
   W1,W2,W3,W4,W5,W6,W7,W8,
   W9,W10,W11,W12,W13,W14,W15,W16
  };

En el método, se usa el array de enumeraciones pattern_types[]. Luego, se verifica qué timeframes han sido seleccionados en la aplicación para el trabajo. Esta información se procesa por el método GetTimeframes().

//+------------------------------------------------------------------+
//| Obtener array de timeframes seleccionados                        |
//+------------------------------------------------------------------+
void  CProgram::GetTimeframes(CButton &buttons[],ENUM_TIMEFRAMES &timeframe[])
  {
   string tf[22]=
     {
      "M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30",
      "H1","H2","H3","H4","H6","H8","H12","D1","W1","MN"
     };
   int j=0;
   ArrayResize(timeframe,22);
   for(int i=0;i<22;i++)
     {
      if(buttons[i].IsPressed())
        {
         timeframe[j]=StringToTimeframe(tf[i]);
         j++;
        }
     }
   ArrayResize(timeframe,j);
  }

Y escribe eso en el array de timeframes m_cur_timeframes[] definido anteriormente. Luego, obtenemos el intervalo temporal para el trabajo.

En el primer ciclo, empezamos a comprobar la pulsación de botones seleccionados, que responden de los tipos de la figuras y que definen el conjunto de las figuras analizadas. En el siguiente ciclo, cada una de las figuras se analiza en los timeframes seleccionados antes. Pues, aquí hemos llegado a la pregunta, ¿a qué datos van a aplicarse los ajustes de las figuras y timeframes seleccionados antes? El método GetData() se encarga de eso. El determina los ajustes establecidos en la ventana de las propiedades del EA, así como, el Indicador usado (punto 1, fig. 5), en la ventana de la configuración de la aplicación.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CProgram::GetData(int index,string symb,ENUM_TIMEFRAMES tf,datetime start,datetime end,double &arr[])
  {
//---
   int Handle=INVALID_HANDLE,copied;
//--- Precio de cierre
   if(index==0)
     {
      MqlRates rt[];
      ZeroMemory(rt);
      copied=CopyRates(symb,tf,start,end,rt);
      ArrayResize(arr,copied);
      for(int i=0;i<copied;i++)
        {
         arr[i]=rt[i].close;
         if(m_applied_price==PRICE_OPEN)
            arr[i]=rt[i].open;
         else if(m_applied_price==PRICE_CLOSE)
            arr[i]=rt[i].close;
         else if(m_applied_price==PRICE_HIGH)
            arr[i]=rt[i].high;
         else if(m_applied_price==PRICE_LOW)
            arr[i]=rt[i].low;
        }
      return(copied);
     }
//--- ATR
   if(index==1)
      Handle=iATR(symb,tf,m_atr_period,m_applied_price);
//--- CCI
   if(index==2)
      Handle=iCCI(symb,tf,m_cci_period,m_applied_price);
//--- DeMarker
   if(index==3)
      Handle=iDeMarker(symb,tf,m_dem_period);
//--- Force Index
   if(index==4)
      Handle=iForce(symb,tf,m_force_period,m_force_ma_method,m_force_applied_volume);
//--- WPR
   if(index==5)
      Handle=iWPR(symb,tf,m_wpr_period);
//--- RSI
   if(index==6)
      Handle=iRSI(symb,tf,m_rsi_period,m_applied_price);
//---
   if(Handle==INVALID_HANDLE)
     {
      Print("Fallo al recibir el handle del indicador");
      return(-1);
     }
   copied=CopyBuffer(Handle,0,start,end,arr);
   return(copied);
  }

Después de que los datos para el análisis hayan sido obtenidos, el algoritmo pasa al método GetPatternType(), a través del cual se realiza la búsqueda de todos los patrones establecidos anteriormente en los timeframes seleccionados. 

//+------------------------------------------------------------------+
//| Identificación de patrones                                       |
//+------------------------------------------------------------------+
PATTERN_TYPE CProgram::GetPatternType(double A,double B,double C,double D,double E)
  {
//--- M1
   if(B>A && A>D && D>C && C>E)
      return(M1);
//--- M2
   if(B>A && A>D && D>E && E>C)
      return(M2);
//--- M3
   if(B>D && D>A && A>C && C>E)
      return(M3);
//--- M4
   if(B>D && D>A && A>E && E>C)
      return(M4);
//--- M5
   if(D>B && B>A && A>C && C>E)
      return(M5);
//--- M6
   if(D>B && B>A && A>E && E>C)
      return(M6);
//--- M7
   if(B>D && D>C && C>A && A>E)
      return(M7);
//--- M8
   if(B>D && D>E && E>A && A>C)
      return(M8);
//--- M9
   if(D>B && B>C && C>A && A>E)
      return(M9);
//--- M10
   if(D>B && B>E && E>A && A>C)
      return(M10);
//--- M11
   if(D>E && E>B && B>A && A>C)
      return(M11);
//--- M12
   if(B>D && D>C && C>E && E>A)
      return(M12);
//--- M13
   if(B>D && D>E && E>C && C>A)
      return(M13);
//--- M14
   if(D>B && B>C && C>E && E>A)
      return(M14);
//--- M15
   if(D>B && B>E && E>C && C>A)
      return(M15);
//--- M16
   if(D>E && E>B && B>C && C>A)
      return(M16);
//--- W1
   if(A>C && C>B && B>E && E>D)
      return(W1);
//--- W2
   if(A>C && C>E && E>B && B>D)
      return(W2);
//--- W3
   if(A>E && E>C && C>B && B>D)
      return(W3);
//--- W4
   if(A>C && C>E && E>D && D>B)
      return(W4);
//--- W5
   if(A>E && E>C && C>D && D>B)
      return(W5);
//--- W6
   if(C>A && A>B && B>E && E>D)
      return(W6);
//--- W7
   if(C>A && A>E && E>B && B>D)
      return(W7);
//--- W8
   if(E>A && A>C && C>B && B>D)
      return(W8);
//--- W9
   if(C>A && A>E && E>D && D>B)
      return(W9);
//--- W10
   if(E>A && A>C && C>D && D>B)
      return(W10);
//--- W11
   if(C>E && E>A && A>B && B>D)
      return(W11);
//--- W12
   if(E>C && C>A && A>B && B>D)
      return(W12);
//--- W13
   if(C>E && E>A && A>D && D>B)
      return(W13);
//--- W14
   if(E>C && C>A && A>D && D>B)
      return(W14);
//--- W15
   if(C>E && E>D && D>A && A>B)
      return(W15);
//--- W16
   if(E>C && C>D && D>A && A>B)
      return(W16);
   return(-1);
  }

Al encontrar una figura técnica, se realiza su estimación a través del método GetCategory(). Pues, precisamente aquí, se usa el array de estructuras tipo RATING_SET definido anteriormente.

//+------------------------------------------------------------------+
//| Definir categorías del beneficio                                 |
//+------------------------------------------------------------------+
bool CProgram::GetCategory(const string symbol,const int shift,RATING_SET &rate,ENUM_TIMEFRAMES timeframe,int threshold)
  {
   MqlRates rt[];
   datetime start=StringToTime(TimeToString(m_calendar1.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit1.GetHours()+":"+(string)m_time_edit1.GetMinutes()+":00");
   start+=PeriodSeconds(timeframe)*shift;
   int copied=CopyRates(symbol,timeframe,start,4,rt);
//--- Obtenemos datos de las velas anteriores
   if(copied<4)
      return(false);
   double high1,high2,high3,low1,low2,low3,close0,point;
   close0=rt[0].close;
   high1=rt[1].high;
   high2=rt[2].high;
   high3=rt[3].high;
   low1=rt[1].low;
   low2=rt[2].low;
   low3=rt[3].low;
   if(!SymbolInfoDouble(symbol,SYMBOL_POINT,point))
      return(false);

//--- Comprobación en Uptrend
   if((int)((high1-close0)/point)>=threshold)
     {
      rate.a_uptrend++;
     }
   else if((int)((high2-close0)/point)>=threshold)
     {
      rate.b_uptrend++;
     }
   else if((int)((high3-close0)/point)>=threshold)
     {
      rate.c_uptrend++;
     }

//--- Comprobación en Downtrend
   if((int)((close0-low1)/point)>=threshold)
     {
      rate.a_dntrend++;
     }
   else if((int)((close0-low2)/point)>=threshold)
     {
      rate.b_dntrend++;
     }
   else if((int)((close0-low3)/point)>=threshold)
     {
      rate.c_dntrend++;
     }
   return(true);
  }

Los datos de la estimación procesados se transmiten en el método AddRow(), que calcula los valores de las probabilidades y coeficientes de la eficacia y los introduce en la tabla de resultados.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::AddRow(CTable &table,string pattern_name,RATING_SET &rate,int found,ENUM_TIMEFRAMES timeframe)
  {
   int row=m_total_row;
   double p1,p2,k1,k2;
   int sum1=0,sum2=0;
   sum1=rate.a_uptrend+rate.b_uptrend+rate.c_uptrend;
   sum2=rate.a_dntrend+rate.b_dntrend+rate.c_dntrend;
//---
   p1=(found>0)?(double)sum1/found*100:0;
   p2=(found>0)?(double)sum2/found*100:0;
   k1=(found>0)?(m_k1*rate.a_uptrend+m_k2*rate.b_uptrend+m_k3*rate.c_uptrend)/found:0;
   k2=(found>0)?(m_k1*rate.a_dntrend+m_k2*rate.b_dntrend+m_k3*rate.c_dntrend)/found:0;
//---
   table.AddRow(row);
   table.SetValue(0,row,pattern_name);
   table.SetValue(1,row,(string)found);
   table.SetValue(2,row,TimeframeToString(timeframe));
   table.SetValue(3,row,DoubleToString(p1,2),2);
   table.SetValue(4,row,DoubleToString(p2,2),2);
   table.SetValue(5,row,DoubleToString(k1,2),2);
   table.SetValue(6,row,DoubleToString(k2,2),2);
   ZeroMemory(rate);
   m_total_row++;
  }

Para despejar posibles dudas respecto al uso de la aplicación, por favor, vea el vídeo de abajo que contiene ejemplos del cálculo con diferentes ajustes.


Recomendaciones para el testeo de las figuras técnicas de Merrill:

Conclusiones

Al final del articulo se adjunta el archivo comprimido con todos los ficheros mencionados, ordenados por carpetas. Por eso, para un trabajo correcto basta con colocar la  carpeta MQL5  en la raíz del terminal. Para abrir el directorio raíz del terminal que contiene la carpeta MQL5, pulse en la combinación  Ctrl+Shift+D o utilice el menú contextual, tal como se muestra en la imagen 7.


Fig. 7 Abrir la carpeta MQL5 en el directorio raíz del terminal MetaTrader 5