English Русский 中文 Deutsch 日本語 Português
Estudio de técnicas de análisis de velas (Parte II): Búsqueda automática de patrones nuevos

Estudio de técnicas de análisis de velas (Parte II): Búsqueda automática de patrones nuevos

MetaTrader 5Probador | 20 mayo 2019, 11:56
1 516 0
Alexander Fedosov
Alexander Fedosov

Contenido

Introducción

En el artículo anterior, estudiamos los métodos correspondientes de análisis de velas. Y como averiguamos, no resultaron tan unívocos y universales para el uso en cualquier condición. Al contrario, antes de usarlos, el tráder debe realizar comprobaciones adicionales precisamente en el instrumento y marco temporal deseado. Es decir, concretamente donde los patrones van a ser utilizados.

El enfoque desarrollado para ello también permite analizar cada uno de los patrones por separado en el marco de un instrumento financiero o conjunto de instrumentos financieros concretos. Buscar las posibles correlaciones entre cómo los parámetros preestablecidos, tales como el marco temporal o intervalo de la muestra, además de las métricas especialmente introducidas, como la frecuencia de suceso y la probabilidad de movimiento de un patrón concreto en una dirección determinada, después de que este se haya formado.

Sin embargo, en el instrumento para el análisis se han propuesto en total 14 patrones y, como ya sabemos, existen otros modelos de velas. Y, para no sumergirnos en una revisión monótona de la enorme variedad de patrones restantes, hemos decidido tomar otro camino. La peculiaridad clave de todos los patrones vistos anteriormente es su base, o más concretamente, su composición. Entre los modelos de velas presentados, había cuatro que constaban de un tipo de vela, y diez que constaba de dos tipos. En este caso, además, en todos los modelos de velas analizados, se han usado seis tipos de vela de determinados conjuntos o secuencias.

El problema es que los patrones se introdujeron en la vida del tráder ya hace mucho tiempo, mientras los mercados no permanecen estáticos: sus propiedades, métodos de comportamiento y dinámicas cambian constantemente. Y para estar no solo en la tendencia de los mercados, sino también en la tendencia de sus análisis, debemos introducir ciertos cambios en su investigación. En este artículo, vamos a analizar un sistema de búsqueda y prueba de nuevos modelos de velas basados en tipos de vela conocidos. 


Formulando la tarea

Para analizar el algoritmo de generación de nuevos modelos de vela, debemos definir las reglas clave:

  • Los nuevos patrones constarán de uno, dos o tres tipos simples de vela.
  • Los tipos simples de vela serán: vela larga, vela corta, spinning top, Doji, Marubozu y martillo.
  • Los tipos de vela se dividirán según su dirección: alcista o bajista. La excepción será Doji.
  • Los tipos simples de vela. Ejemplo: patrón de dos velas largas bajistas.

En la fig.1 se muestra el esquema general de creación de un nuevo patrón.


Fig.1 Algoritmo de creación de un nuevo patrón.

De esta forma, obtenemos un determinado conjunto (pool) de velas, de las cuales se formarán nuevos patrones de conjuntos de 1-3 velas con repetición o sin ella. La cantidad total en el conjunto será igual a 11 velas básicas. Los modelos de velas formados se analizarán según el mismo principio que en el primer artículo.


Actualizando el prototipo de la interfaz

Para trabajar con los patrones generados, se ha decidido reservar una pestaña aparte e implementar todos los controles que hay en la pestaña Análisis ya existente. La diferencia radicará en que la primera columna se llamará Conjunto, y el tamaño del recuadro no será fijo.

Fig.2 Pestaña para trabajar con los patrones generados.

En el caso de la tercera pestaña, Ajustes, esta se ha modificado sustancialmente. Nos detendremos en ella con más detalle, ya que es importante comprender que los parámetros indicados en esta pestaña deben ajustarse correctamente.

Fig.3 Pestaña Ajustes actualizada.

  1. Añadida la numeración de tipos simples de vela y su nombre, para que exista una mejor correlación entre la representación visual de las velas y la lista de Velas Usadas.
  2. Añadida la selección de idioma de la interfaz. Ruso o inglés.
  3. La lista de Velas Usadas para la generación de patrones. Las casillas de verificación nos permiten elegir aquellos patrones que necesitemos para la simulación.
  4. Un grupo de botones conmutables. Se puede elegir solo una de las posiciones. De esta manera, no se sobrecargará con datos la lista de patrones generados en el recuadro de la pestaña de Búsqueda automática.
  5. Dos botones conmutables (con repeticiones). Esto significa que en el patrón puede haber solo un tipo de vela. Por ejemplo, un patrón compuesto de tres velas Marubozu bajistas. En este caso, además, el patrón Spinning top - Marubozu - Marubozu también puede existir.

Implementando las herramientas

Una vez hemos decidido las principales adiciones de la aplicación que ya tenemos, vamos a proceder a implementarlas por orden en el prototipo. Lo primero que tenemos que hacer es añadir una pestaña adicional a las ya existentes, en este caso, el orden de indexación de las pestañas cambiará. La pestaña Búsqueda automática adoptará el índice 1, que antes pertenecía a la pestaña Ajustes. Aquella, a su vez, pasará a usar el índice número 2. Debemos tener esto en cuenta al vincular los elementos derivados. Por eso, debemos cambiar la vinculación de todos los elementos derivados de la pestaña de ajustes del número 1 al 2.

//+------------------------------------------------------------------+
//| Crea el grupo con pestañas                                       |
//+------------------------------------------------------------------+
bool CProgram::CreateTabs(const int x_gap,const int y_gap)
  {
#define TABS1_TOTAL 3
//--- Guardamos el puntero al elemento principal
   m_tabs1.MainPointer(m_window1);
//--- Propiedades
   m_tabs1.IsCenterText(true);
   m_tabs1.PositionMode(TABS_TOP);
   m_tabs1.AutoXResizeMode(true);
   m_tabs1.AutoYResizeMode(true);
   m_tabs1.AutoXResizeRightOffset(3);
   m_tabs1.AutoYResizeBottomOffset(25);

//--- Añadimos las pestañas con las propiedades indicadas
   string tabs_names[TABS1_TOTAL]={"Análisis","Búsqueda automática","Ajustes"};
   for(int i=0; i<TABS1_TOTAL; i++)
      m_tabs1.AddTab(tabs_names[i],150);
//--- Creamos el elemento de control
   if(!m_tabs1.CreateTabs(x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_tabs1);
   return(true);
  }

Los elementos derivados de la clase Búsqueda automática son idénticos a los mismos de la pestaña Análisis. La única diferencia es el nombre de la primera columna y el esquema de color. Esto se ha hecho para distinguir visualmente .

//+------------------------------------------------------------------+
//| Crea el formulario para los elementos de control                 |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
  {
//--- Añadimos el puntero de la ventana a la matriz de ventanas
   CWndContainer::AddWindow(m_window1);
//--- Propiedades
   m_window1.XSize(750);
   m_window1.YSize(500);
   m_window1.FontSize(9);
   m_window1.IsMovable(true);
   m_window1.CloseButtonIsUsed(true);
   m_window1.CollapseButtonIsUsed(true);
   m_window1.FullscreenButtonIsUsed(true);
   m_window1.TooltipsButtonIsUsed(true);
//--- Creando el formulario
   if(!m_window1.CreateWindow(m_chart_id,m_subwin,caption_text,5,5))
      return(false);
//--- Pestañas
   if(!CreateTabs(3,43))
      return(false);
//--- Pestaña Analyze
//--- Campo de edición
   if(!CreateSymbolsFilter(m_symb_filter1,10,10,"Símbolos",0))
      return(false);
   if(!CreateRequest(m_request1,250,10,"Búsqueda",0))
      return(false);
   if(!CreateRange(m_range1,485,10,"Intervalo",0))
      return(false);
//--- Cuadros combinados
   if(!CreateComboBoxTF(m_timeframes1,350,10,"Marco temporal",0))
      return(false);
//--- Creando el recuadro de símbolos
   if(!CreateSymbTable(m_symb_table1,10,50,0))
      return(false);
//--- Creando el recuadro de resultados
   if(!CreateTable1(m_table1,120,50,0))
      return(false);

//--- Pestaña AutoSearch
//--- Campo de edición
   if(!CreateSymbolsFilter(m_symb_filter2,10,10,"Símbolos",1))
      return(false);
   if(!CreateRequest(m_request2,250,10,"Búsqueda",1))
      return(false);
   if(!CreateRange(m_range2,485,10,"Intervalo",1))
      return(false);
//--- Cuadros combinados
   if(!CreateComboBoxTF(m_timeframes2,350,10,"Marco temporal",1))
      return(false);
//--- Creando el recuadro de símbolos
   if(!CreateSymbTable(m_symb_table2,10,50,1))
      return(false);
//--- Creando el recuadro de resultados
   if(!CreateTable2(m_table2,120,50,1))
      return(false);

Como podemos ver por la lista, la diferencia reside solo en dos métodos CreateTable1() y CreateTable2(). A continuación, pasamos a la pestaña de ajustes. La primera modificación que debemos realizar es el cambio de los recursos gráficos. Asimismo, tenemos que añadir una marca de texto al método de creación del elemento de ajuste de los parámetros de la vela. De ello se encargará el método CreateNameCandle().

//+------------------------------------------------------------------+
//| Creando el elemento de ajuste de la vela                         |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\settings_dark.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\long.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\short.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\doji.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\spin.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\maribozu.bmp"
#resource "\\Images\\EasyAndFastGUI\\Candles\\hammer.bmp"
//---
bool CProgram::CreateCandle(CPicture &pic,CButton &button,CTextLabel &candlelabel,const string candlename,const int x_gap,const int y_gap,string path)
  {
//--- Guardamos el puntero al elemento principal
   pic.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(2,pic);
//--- Propiedades
   pic.XSize(64);
   pic.YSize(64);
   pic.IconFile(path);
//--- Creando el botón
   if(!pic.CreatePicture(x_gap,y_gap))
      return(false);
//--- Añadimos el puntero al elemento en la base
   CWndContainer::AddToElementsArray(0,pic);
   CreateButtonPic(pic,button,"Images\\EasyAndFastGUI\\Icons\\bmp16\\settings_dark.bmp");
   CreateNameCandle(candlelabel,x_gap,y_gap+pic.YSize(),candlename);
   return(true);
  }

Con esto, finalizan los cambios de los controles ya creados. Vamos a pasar a los nuevos. En primer lugar, analizaremos la opción que permite cambiar el idioma de la interfaz. En esta aplicación se pueden elegir dos idiomas: ruso e inglés. En fig.4 se muestra claramente cómo tiene lugar el proceso.

Fig.4 Cambio de idioma de la interfaz.

Del cambio de idioma de la interfaz se encarga el método ChangeLanguage(). Su lógica consiste en el cambio de los componentes textuales de un idioma por los de otro. La lista completa del código de este método es simple y monótona, por eso, solo vamos a mostrar su aplicación al procesamiento de eventos:

//--- Evento de selección en el cuadro combinado
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
     {
      //--- Cambio de marco temporal
      if(ChangePeriod1(lparam))
         Update(true);
      //--- Cambiando el idioma de la interfaz
      if(ChangeLanguage(lparam))
         Update(true);
     }

Como podemos ver por el código, al darse el evento de selección del punto en el cuadro combinado, el método de cambio de idioma determina cuál de los puntos del menú desplegable está seleccionado, estableciendo con ello el índice del idioma elegido:

//+------------------------------------------------------------------+
//| Cambiando el idioma de la interfaz                               |       
//+------------------------------------------------------------------+
bool CProgram::ChangeLanguage(const long id)
  {
//--- Comprobando el identificador del elemento
   if(id!=m_lang_setting.Id())
      return(false);
   m_lang_index=m_lang_setting.GetListViewPointer().SelectedItemIndex();
//---
   if(m_lang_index==0)
     ....

El siguiente apartado que vamos a analizar es el responsable de elegir los tipos de las velas simples con las que se generarán los patrones para la simulación. Son 11 tipos en total. El control se ha hecho en forma de lista de nombres de los tipos usados de vela, así como de casillas de verificación, que son una señal de selección; además, ha sido implementado con el método CreateListView():

//+------------------------------------------------------------------+
//| Creando la lista                                                 |
//+------------------------------------------------------------------+
bool CProgram::CreateListView(const int x_gap,const int y_gap)
  {
//--- Tamaño de la lista
#define CANDLE_TOTAL 11
//--- Guardamos el puntero al elemento principal
   m_listview1.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(2,m_listview1);
//--- Propiedades
   m_listview1.XSize(175);
   m_listview1.YSize(250);
   m_listview1.ItemYSize(19);
   m_listview1.LabelXGap(25);
   m_listview1.LightsHover(true);
   m_listview1.CheckBoxMode(true);
   m_listview1.ListSize(CANDLE_TOTAL);
   m_listview1.AutoYResizeMode(true);
   m_listview1.AutoYResizeBottomOffset(10);
   m_listview1.FontSize(10);
//--- Rellenando la lista con datos
   string cand_name[CANDLE_TOTAL]=
     {
      "Larga — alcista",
      "Larga — bajista",
      "Corta — alcista",
      "Corta — bajista",
      "Spinnig top — alcista",
      "Spinnig top — bajista",
      "Doji",
      "Marubozu — alcista",
      "Marubozu — bajista",
      "Martillo — alcista",
      "Martillo — bajista"
     };
   for(int r=0; r<CANDLE_TOTAL; r++)
     {
      m_listview1.SetValue(r,(string)(r+1)+". "+cand_name[r]);
     }
//--- Creando la lista
   if(!m_listview1.CreateListView(x_gap,y_gap))
      return(false);
//--- Añadimos el puntero al elemento en la base
   CWndContainer::AddToElementsArray(0,m_listview1);
   return(true);
  }

Los dos próximos controles están directamente relacionados con la lista de tipos simples de vela. El primero de ellos es un conmutador que configura la posibilidad de repetición. Como ya mencionamos en el apartado "Formulando la tarea", consideraremos repetición aquel patrón que conste solo de un tipo de vela, independientemente de su dimensión. 

Fig.5 Selección de los tipos de vela investigados y el modo de repetición.

El método responsable de la creación del conmutador de Repetición se llama CreateDualButton():

//+------------------------------------------------------------------+
//| Creando el conmutador de Repetición                              |
//+------------------------------------------------------------------+
bool CProgram::CreateDualButton(CButton &lbutton,CButton &rbutton,const int x_gap,const int y_gap,const string ltext,const string rtext)
  {
   CreateButton(lbutton,x_gap,y_gap,ltext);
   CreateButton(rbutton,x_gap+99,y_gap,rtext);
   return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateButton(CButton &button,const int x_gap,const int y_gap,const string text)
  {
//--- Guardar el puntero al elemento principal
   button.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(2,button);
//--- Propiedades
   button.XSize(100);
   button.YSize(30);
   button.Font("Trebuchet");
   button.FontSize(10);
   button.IsCenterText(true);
   button.BorderColor(C'0,100,255');
   button.BackColor(clrAliceBlue);
   button.BackColorLocked(C'50,180,75');
   button.BorderColorLocked(C'50,180,75');
   button.LabelColorLocked(clrWhite);
//--- Creamos el elemento de control
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Añadimos el puntero al elemento en la base
   CWndContainer::AddToElementsArray(0,button);
   return(true);
  }

El ajuste del funcionamiento del control se monitorea en el evento de pulsación del botón izquierdo del ratón:

   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      ....
      //--- Si hemos pulsado el botón
      if(lparam==m_button7.Id())
        {
         m_button7.IsLocked(true);
         m_button8.IsLocked(false);
        }
      else if(lparam==m_button8.Id())
        {
         m_button7.IsLocked(false);
         m_button8.IsLocked(true);
        }

Tras analizar la parte visual de la aplicación, vamos a pasar a la parte de cálculos. Pero antes, vamos a definir los ajustes mínimos, la secuencia de acciones y los métodos de procesamiento de estas acciones:

Paso 1. Estableciendo los datos de entrada.

Antes de comenzar a trabajar con la aplicación, debemos elegir métodos sencillos de velas, a partir de los cuales se generarán los patrones para su investigación. A continuación, elegiremos si habrá repeticiones entre los patrones generados. Consideramos repetición el uso de un solo tipo de vela al construir un patrón de cualquier dimensión. Después, seleccionamos el número de vales en un solo patrón. Podemos elegir un patrón de una vela, dos o tres. En este caso, además, debemos comprender que al formar un patrón con unas dimensiones de dos o tres velas, el número de velas simples seleccionadas deberá ser no inferior a dos. Si intentamos generar patrones seleccionando menos, la aplicación dará un error como el mostrado en el ejemplo de la figura. 6.

Fig.6 Muestra del error al seleccionar un tipo de vela y unas dimensiones de dos.

Paso 2. Trabajando con la pestaña de Búsqueda Automática.

Después de establecer correctamente los parámetros de entrada, pasamos a la pestaña de Búsqueda Automática y elegimos la herramienta necesaria para la investigación. Vamos a analizar con mayor detalle las posibilidades de las herramientas mostradas en esta pestaña.

  • Selección y búsqueda de los instrumentos de divisas. Aquí, en el campo de edición, podemos teclear el nombre del instrumento, o bien introducir los necesarios usando comas y pulsar el botón de Búsqueda. Asimsmo, existe la frase predeterminada Major, que se encarga de mostrar las principales parejas de divisas. Para ver todos los instrumentos mostrados en la Observación del Mercado, debemos quitar la marca en la esquina superior izquierda. Haciendo esto, también conseguiremos desactivar cualquier filtro de la ventana de búsqueda.
  • A continuación, elegimos de la lista desplegable el marco temporal necesario y el intervalo de muestra del número de velas del marco temporal seleccionado.
  • Después de seleccionar el instrumento o lista necesario, clique en actual para iniciar el análisis. Acto seguido, se formarán los patrones construidos en el paso 1, se realizarán los cálculos y se mostrarán en el recuadro. Debemos decir que tras obtener los datos en el recuadro, podremos cambiar el marco temporal; los datos se recalcularán en tiempo real teniendo en cuenta nuestro cambios.

Vamos a echar un vistazo más detallado a los datos obtenidos en el recuadro. En lo sucesivo, estos nos ayudará a comprender mejor el principio de cálculo y funcionamiento de los algoritmos que analizaremos un poco más tarde.


Fig.7 Ejemplo de cálculo y obtención de datos en la pareja de divisa EURUSD.  

Como podemos ver en la fig.7, en el recuadro Símbolo se ha seleccionado la línea con la pareja de divisas investigada. Arriba y a la derecha se ha establecido el marco temporal М15 y un intervalo de muestra de 8000 velas de 15 minutos. En el recuadro de resultados podemos ver seis columnas. Aquí vamos a analizar solo la primera, sobre el resto se puede leer en el primer artículo, en el apartado Desarrollando el prototipo de la interfaz.

La primera columna se llama Conjunto. En las líneas vemos los datos mostrados en el formato [1,1,2]. Tres números entre corchetes indican que se usa un patrón con tres velas. En este caso, además, en él se usan velas simples con los números 1 y 2, precisamente en el orden indicado entre corchetes. Los números de velas utilizadas se puede averiguar en la pestaña Ajustes, en el apartado Velas utilizadas.

Fig.8 Lista y número de velas utilizadas al construir el patrón.  

Ahora que comprendemos cómo configurar e iniciar la aplicación, podemos proceder a analizar la lógica interna de su funcionamiento. Bien, después de configurar los datos de entrada, tiene lugar la pulsación sobre el instrumento de divisa investigado del recuadro Símbolo. El procesamiento del evento de pulsación sobre el punto de la lista del recuadro genera la llamada del método ChangeSymbol2():

//--- Evento de pulsación sobre un punto de la lista o recuadro
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
     {
      //--- Cambio de símbolo
      if(ChangeSymbol1(lparam))
         Update(true);
      if(ChangeSymbol2(lparam))
         m_table2.Update(true);
     }

En el método llamado, aparte de la definición de los valores de información en la barra de estado, encontramos dos métodos más.

//+------------------------------------------------------------------+
//| Cambio de símbolo                                                |
//+------------------------------------------------------------------+
bool CProgram::ChangeSymbol2(const long id)
  {
//--- Comprobando el identificador del elemento
   if(id!=m_symb_table2.Id())
      return(false);
//--- Salir, si la línea no está seleccionada
   if(m_symb_table2.SelectedItem()==WRONG_VALUE)
     {
      //--- Mostrando la descripción completa del símbolo en la barra de estado
      m_status_bar.SetValue(0,"No se ha seleccionado un símbolo para el análisis");
      m_status_bar.GetItemPointer(0).Update(true);
      return(false);
     }
//--- Obtenemos el símbolo
   string symbol=m_symb_table2.GetValue(0,m_symb_table2.SelectedItem());
//--- Mostrando 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);
   if(!BuildingAutoSearchTable())
      return(false);
   GetPatternType(symbol,m_total_combination);
   return(true);
  }

El primer método es BuildingAutoSearchTable(), encargado de crear los patrones de la lista de tipos simples de vela elegidos en la pestaña Ajustes del apartado Velas usadas(fig.8) y mostrarlos en el recuadro de resultados en la primera columna.

//+------------------------------------------------------------------+
//| Reconfigura el recuadro de búsqueda automática de patrones       |
//+------------------------------------------------------------------+
bool CProgram::BuildingAutoSearchTable(void)
  {
//---
   if(!GetCandleCombitaion())
     {
      if(m_lang_index==0)
         MessageBox("¡El número de velas seleccionadas es menor al tamaño del patrón investigado!","Error");
      else if(m_lang_index==1)
         MessageBox("The number of selected candles is less than the size of the studied pattern!","Error");
      return(false);
     }
//--- Eliminar todas las líneas
   m_table2.DeleteAllRows();
//--- Establecemos el número de líneas según el número de símbolos
   for(int i=0; i<ArraySize(m_total_combination); i++)
     {
      m_table2.AddRow(i);
      m_table2.SetValue(0,i,m_total_combination[i]);
     }
   m_table2.DeleteRow(ArraySize(m_total_combination));
//--- Actualizar el recuadro
   m_table2.Update(true);
   m_table2.GetScrollVPointer().Update(true);
   m_table2.GetScrollHPointer().Update(true);
   return(true);
  }
//+------------------------------------------------------------------+
//| Generando los patrones de velas simples                          |
//+------------------------------------------------------------------+
bool CProgram::GetCandleCombitaion(void)
  {
   string candlenumber[];
   int selected_candles=0,n;
   ArrayResize(candlenumber,m_total_candles);
//---
   for(int i=0;i<m_total_candles;i++)
     {
      if(m_listview1.GetState(i))
        {
         candlenumber[selected_candles]=(string)(i+1);
         selected_candles++;
        }
     }

   if((m_pattern_size==2 && selected_candles<2) || (m_pattern_size==3 && selected_candles<2) || selected_candles<1)
      return(false);
//--- Calculando el número de combinaciones
   if(m_pattern_size>1)
      n=(m_button7.IsLocked())?(int)MathPow(selected_candles,m_pattern_size):(int)MathPow(selected_candles,m_pattern_size)-selected_candles;
   else
      n=selected_candles;
   ArrayResize(m_total_combination,n);

   n=0;
//--- Conjunto de una vela
   if(m_pattern_size==1)
     {
      for(int i=0;i<selected_candles;i++)
         m_total_combination[i]="["+candlenumber[i]+"]";
     }
//--- Conjunto de dos velas
   else if(m_pattern_size==2)
     {
      //--- Con repeticiones
      if(m_button7.IsLocked())
        {
         for(int i=0;i<selected_candles;i++)
           {
            for(int j=0;j<selected_candles;j++)
              {
               m_total_combination[n]="["+candlenumber[i]+","+candlenumber[j]+"]";
               n++;
              }
           }
        }
      //--- Sin repeticiones
      else if(m_button8.IsLocked())
        {
         for(int i=0;i<selected_candles;i++)
           {
            for(int j=0;j<selected_candles;j++)
              {
               if(j!=i)
                 {
                  m_total_combination[n]="["+candlenumber[i]+","+candlenumber[j]+"]";
                  n++;
                 }
              }
           }
        }
     }
//--- Conjunto de tres velas
   else if(m_pattern_size==3)
     {
      //--- Con repeticiones
      if(m_button7.IsLocked())
        {
         for(int i=0;i<selected_candles;i++)
           {
            for(int j=0;j<selected_candles;j++)
              {
               for(int k=0;k<selected_candles;k++)
                 {
                  m_total_combination[n]="["+candlenumber[i]+","+candlenumber[j]+","+candlenumber[k]+"]";
                  n++;
                 }
              }
           }
        }
      //--- Sin repeticiones
      else if(m_button8.IsLocked())
        {
         for(int i=0;i<selected_candles;i++)
           {
            for(int j=0;j<selected_candles;j++)
               for(int k=0;k<selected_candles;k++)
                 {
                  if(i==j && i==k)
                     continue;
                  m_total_combination[n]="["+candlenumber[i]+","+candlenumber[j]+","+candlenumber[k]+"]";
                  n++;
                 }
           }
        }
     }
   return(true);
  }

A su vez, el método GetCandleCombination() es el encargado de crear los patrones del conjunto de velas. Su tarea consiste en mostrar todas las combinaciones posibles de velas simples, partiendo de los números ordinales de las velas usadas elegidas, con rpeticiones y sin ellas, teniendo también en cuenta las dimensiones establecidas en el apartado Número de Velas en el Patrón. 

Todas las combinaciones se registran en la matriz de línea m_total_combitaion[], y ya en el método BuildingAutoSearchTable() se añade al recuadro de resultados.

El segundo método, llamado tras BuildingAutoSearchTable(), es responsable del cálculo y la muestra de los demás datos usando la lista obtenida de patrones generados. Vamos a analizar el método GetPatternType() con mayor detalle, tanto más que se ha usado en el primer artículo para calcular los patrones preestablecidos, si bien se ha sometido a mejoras sustanciales encaminadas a reducir y simplificar su implementación. La tarea de este método consiste en reconocer en el gráfico los patrones que hemos establecido y analizarlos posteriormente. Puesto que este método se utiliza tanto para la búsqueda de patrones preestablecidos existentes, como para encontrar aquellos generados por nostros, hemos decidido hacer la sobrecarga de este método:

   //--- Detectando los patrones
   bool              GetPatternType(const string symbol);
   bool              GetPatternType(const string symbol,string &total_combination[]);

Como podemos ver, en la segunda variante del método se usará la matriz de línea de datos ya creada por nosotros con los patrones generados. La tarea de este método consiste en identificar en el gráfico cualquiera de nuestro patrones generados, valorar estos y calcular su efectividad. En el apartado Formulando la tarea del primer artículo se muestra la descripción de la idea y los algoritmos. Por eso, no nos vamos a detener en ello.

Para valorar la efectividad del patrón encontrado, antes se usaba una matriz a la que se añadían valoraciones según las categorías A,B,C para la tendencia ascendente y descendente. Ahora, hemos decidido convertir la matriz en una estructura. La ventaja es que el código se reduce y es más visual. La estructura ha resultado como sigue:

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

 Para nuestra tarea, necesitaremos un conjunto de valoraciones así para cada patrón generado. Por eso, al inicio del método se ha declarado la matriz de estructuras RATING_SET. Las dimensiones de la matriz se corresponden con el número de patrones generados o el tamaño de la matriz de línea m_total_combitaion[].

   RATING_SET ratings[];

//---
   total_patterns=ArraySize(total_combination);
...
   ArrayResize(ratings,total_patterns);

Recordemos que en esta matriz se guardan todos los patrones generados, sin embargo, estos se presentan en forma de línea a mostrar en la primera columna del recuadro de resultados. Por eso, en la siguiente etapa, debemos extraer en cada línea los índices de las velas simples usadas y convertirlos en el tipo de vela CANDLE_STRUCTURE.

struct CANDLE_STRUCTURE
  {
   double            open,high,low,close;       // OHLC
   TYPE_TREND        trend;                     //Tendencia
   bool              bull;                      //Vela alcista
   double            bodysize;                  //Tamaño del cuerpo
   TYPE_CANDLESTICK  type;                      //Tipo de vela
  };

Para ello, realizamos las siguientes transformaciones, analizando, asimismo, un método adicional que ayuda a convertir los índices de las velas en su tipo.

//---
   for(int i=0;i<total_patterns;i++)
     {
      StringReplace(total_combination[i],"[","");
      StringReplace(total_combination[i],"]","");
      if(m_pattern_size>1)
        {
         ushort sep=StringGetCharacter(",",0);
         StringSplit(total_combination[i],sep,elements);
        }
      ZeroMemory(ratings[i]);
      m_pattern_total[i]=0;
      if(m_pattern_size==1)
         IndexToPatternType(cand1[i],(int)total_combination[i]);
      else if(m_pattern_size==2)
        {
         IndexToPatternType(cand1[i],(int)elements[0]);
         IndexToPatternType(cand2[i],(int)elements[1]);
        }
      else if(m_pattern_size==3)
        {
         IndexToPatternType(cand1[i],(int)elements[0]);
         IndexToPatternType(cand2[i],(int)elements[1]);
         IndexToPatternType(cand3[i],(int)elements[2]);
        }
     }

Iteramos en el ciclo cada valor de línea de los patrones generados, quitamos los corchetes y, con ayuda de StringSplit(), usando el separador "," añadimos la información a la matriz elements[]. Además, debemos tener en cuenta que este proceso es necesario solo para los patrones constan de más de una vela, ya que, cuando solo hay una, la línea no tendrá coma, y bastará con quitarle los corchetes. Ahora, vamos a analizar el método IndexToPatternType(), que recibe en la entrada CANDLE_STRUCTURE para rellenar y procesar los datos de la matriz de patrones generados.

void CProgram::IndexToPatternType(CANDLE_STRUCTURE &res,const int index)
  {
//--- Larga - alcista
   if(index==1)
     {
      res.bull=true;
      res.type=CAND_LONG;
     }
//--- Larga - bajista
   else if(index==2)
     {
      res.bull=false;
      res.type=CAND_LONG;
     }
//--- Corta - alcista
   else if(index==3)
     {
      res.bull=true;
      res.type=CAND_SHORT;
     }
//--- Corta - bajista
   else if(index==4)
     {
      res.bull=false;
      res.type=CAND_SHORT;
     }
//--- Spinning top - alcista
   else if(index==5)
     {
      res.bull=true;
      res.type=CAND_SPIN_TOP;
     }
//--- Spinning top - bajista
   else if(index==6)
     {
      res.bull=false;
      res.type=CAND_SPIN_TOP;
     }
//--- Doji
   else if(index==7)
     {
      res.bull=true;
      res.type=CAND_DOJI;
     }
//--- Marubozu - alcista
   else if(index==8)
     {
      res.bull=true;
      res.type=CAND_MARIBOZU;
     }
//--- Marubozu - bajista
   else if(index==9)
     {
      res.bull=false;
      res.type=CAND_MARIBOZU;
     }
//--- Martillo - alcista
   else if(index==10)
     {
      res.bull=true;
      res.type=CAND_HAMMER;
     }
//--- Martillo - bajista
   else if(index==11)
     {
      res.bull=false;
      res.type=CAND_HAMMER;
     }
  }

Dependiendo de las dimensiones del patrón, se rellenan una, dos o tres estructuras CANDLE_STRUCTURE: cand1, cand2, cand3. Aquí podemos verlo:

if(m_pattern_size==1)
         IndexToPatternType(cand1[i],(int)total_combination[i]);
      else if(m_pattern_size==2)
        {
         IndexToPatternType(cand1[i],(int)elements[0]);
         IndexToPatternType(cand2[i],(int)elements[1]);
        }
      else if(m_pattern_size==3)
        {
         IndexToPatternType(cand1[i],(int)elements[0]);
         IndexToPatternType(cand2[i],(int)elements[1]);
         IndexToPatternType(cand3[i],(int)elements[2]);
        }

Esto es necesario para que, al buscar en el gráfico el patrón que necesitamos, dependiendo de las dimensiones, podamos analizar directamente el número de velas necesario. 

Para un patrón de una vela, para realizar el análisis y los cálculos tomaremos en cada caso concreto solo una vela, y para dos o tres velas, tomaremos todo el conjunto. Por ejemplo, con un intervalo de muestra de 2000 velas, para padrones de una vela, al realizar el análisis completo, el conjunto constará de una 2000º vela. Para padrones de dos velas, el conjunto constará de las velas 2000º y 1999º, etcétera. 

A continuación, vamos a analizar el siguiente fragmento del método GetPatternType(), encargado de buscar en el gráfico cada uno de los patrones generados.

//---
   for(int i=m_range_total2;i>5;i--)
     {
      if(m_pattern_size==1)
        {
         //--- Obteniendo el tipo de la vela actual
         GetCandleType(symbol,cur_cand,m_timeframe2,i);                                         // vela actual
         //---
         for(int j=0;j<total_patterns;j++)
           {
            if(cur_cand.type==cand1[j].type && cur_cand.bull==cand1[j].bull)
              {
               m_pattern_total[j]++;
               GetCategory(symbol,i-3,ratings[j],m_timeframe2);
              }
           }
        }
      else if(m_pattern_size==2)
        {
         //--- Obteniendo el tipo de la vela actual
         GetCandleType(symbol,prev_cand,m_timeframe2,i);                                        // vela anterior
         GetCandleType(symbol,cur_cand,m_timeframe2,i-1);                                       // vela actual
         //---
         for(int j=0;j<total_patterns;j++)
           {
            if(cur_cand.type==cand1[j].type && cur_cand.bull==cand1[j].bull && 
               prev_cand.type==cand2[j].type && prev_cand.bull==cand2[j].bull)
              {
               m_pattern_total[j]++;
               GetCategory(symbol,i-4,ratings[j],m_timeframe2);
              }
           }
        }
      else if(m_pattern_size==3)
        {
         //--- Obteniendo el tipo de la vela actual
         GetCandleType(symbol,prev_cand2,m_timeframe2,i);                                       // vela anterior
         GetCandleType(symbol,prev_cand,m_timeframe2,i-1);                                      // vela anterior
         GetCandleType(symbol,cur_cand,m_timeframe2,i-2);                                       // vela actual
         //---
         for(int j=0;j<total_patterns;j++)
           {
            if(cur_cand.type==cand1[j].type && cur_cand.bull==cand1[j].bull && 
               prev_cand.type==cand2[j].type && prev_cand.bull==cand2[j].bull && 
               prev_cand2.type==cand3[j].type && prev_cand2.bull==cand3[j].bull)
              {
               m_pattern_total[j]++;
               GetCategory(symbol,i-5,ratings[j],m_timeframe2);
              }
           }
        }
     }

Como podemos ver por la lista de arriba, el ciclo se encuentra al inicio, pero finaliza en la sexta vela. ¿Por qué? Ya en el primer artículo, hablamos de que para analizar la efectividad de un patrón - una vez identificado - debemos monitorear en qué dirección irá el precio, aclarando al mismo tiempo, con la ayuda de estas observaciones, qué indica la aparición de este patrón en el futuro, y con qué frecuencia y probabilidad. Para realizar esta valoración, hemos introducido el análisis del movimiento del precio en las tres velas que siguen al patrón. En este caso, además, la vela cero se excluye del análisis, puesto que no aún no está completa. 

Fig.9 Calculando la valoración de la efectividad del patrón. 

En la fig.9 se analiza un patrón de tres velas. Para valorar su efectividad, necesitamos otras tres velas, pero no incluiremos la vela cero. Por eso, para cumplir estas condiciones para el patrón actual, la primera vela dentro del patrón puede tener un índice mínimo igual a 6. 

Bien, después de buscar los patrones, calcular su número y realizar la valoración según las categorías A, B, C, debemos procesar los datos obtenidos e introducir los resultados en el recuadro. Estos cálculos se realizan en el método CoefCalculation().

//---
   for(int i=0;i<total_patterns;i++)
      CoefCalculation(m_table2,i,ratings[i],m_pattern_total[i]);

 Como podemos ver, los argumentos para este método son:

  • m_table2 — enlace al recuadro en el que se introducirán los resultados del cálculo para cada patrón generado.
  • i — línea del recuadro.
  • ratings[i] — matriz de estructuras RATING_SET, que contiene un conjunto de valoraciones según las categorías para cada patrón.
  • m_pattern_total[i] — matriz que contiene el número de patrones encontrados de cada tipo.

Vamos a analizar el método con un poco más de detalle.

//+------------------------------------------------------------------+
//| Calculando los coeficientes de valoración de la efectividad      |
//+------------------------------------------------------------------+
bool CProgram::CoefCalculation(CTable &table,const int row,RATING_SET &rate,int found)
  {
   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)?NormalizeDouble((double)sum1/found*100,2):0;
   p2=(found>0)?NormalizeDouble((double)sum2/found*100,2):0;
   k1=(found>0)?NormalizeDouble((m_k1*rate.a_uptrend+m_k2*rate.b_uptrend+m_k3*rate.c_uptrend)/found,3):0;
   k2=(found>0)?NormalizeDouble((m_k1*rate.a_dntrend+m_k2*rate.b_dntrend+m_k3*rate.c_dntrend)/found,3):0;

   table.SetValue(1,row,(string)found);
   table.SetValue(2,row,(string)((double)found/m_range_total2*100),2);
   table.SetValue(3,row,(string)p1,2);
   table.SetValue(4,row,(string)p2,2);
   table.SetValue(5,row,(string)k1,2);
   table.SetValue(6,row,(string)k2,2);
//--- Actualizar el recuadro
   table.Update(true);
   table.GetScrollVPointer().Update(true);
   table.GetScrollHPointer().Update(true);
   return(true);
  }

Como podemos ver por la implementación del método, al usar la estructura se ha hecho más comprensible de qué forma se calculan los coeficientes para analizar los modelos de velas.

Demostrando el funcionamiento de la aplicación

Como ejemplo de investigación de los patrones generados, vamos a simular varios de ellos con distintos parámetros.

Etapa 1. Selección de modelos simples de velas.

Para comenzar, vamos a elegir todos los modelos simples de velas en el apartado Velas Usadas, indicando los parámetros "Con repetición" y "Número de velas en el patrón" igual a uno. Asimismo, el "Valor umbral de la tendencia (puntos)" lo estableceremos en 200. Deberá resultar de la forma siguiente:

Fig.10 Primera etapa de ajuste del análisis de patrones generados.

Ahora, pasamos a la pestaña de Búsqueda automática, tecleamos en el campo de búsqueda Major y pulsamos Buscar. Después, establecemos el marco temporal actual en Н1, y elegimos la pareja de divisas GBPUSD. Hemos obtenido el siguiente resultado de simulación.

Fig.11 Resultados de simulación de los patrones de una vela generados.

Vamos a seleccionar los seis tipos de vela más frecuentes. Son 1,2,5,6,3,4, de menor a mayor.

Etapa 2. Simulando los patrones de dos velas.

A partir de estos tipos simples de vela, componemos ahora patrones de dos velas. Para ello, vamos a pasar a la pestaña Ajustes y desactivar las casillas de la 7 a la 11. Esta vez, elegiremos el modo "Sin Repeticiones", y también pondremos el "Número de velas en el patrón" en dos. Deberemos ver un resultado como en la fig.12, un poco más abajo.

Fig.12 Ajustes de simulación de los patrones de dos velas generados.  

Ahora, pasamos de nuevo a la pestaña de Búsqueda automática y pulsamos en GBPUSD. Obtenemos diferentes combinaciones de las velas seleccionadas por nosotros, así como los resultados de su valoración. Sin embargo, si el valor "Valor umbral de la tendencia" es bajo, especialmente en los marcos temporales mayores, los resultados del movimiento del precio serán más o menos iguales con mucha frecuencia. Aumentando paulatinamente del valor, usted podrá establecer unas condiciones de selección más rigurosas, reduciendo con ello el marco de búsqueda de modelos de velas interesantes. Por ejemplo, aumentando el valor hasta 400, hemos obtenido estos resultados:

Fig.13 Resultados de la simulación al aumentar el Valor umbral de la tendencia.

En los resultados obtenidos hemos buscado un valor alto de movimiento de precio en una dirección, y varias veces menor en la otra. Como podemos ver por la fig.13, aquí tenemos dos variantes así: [1,4] y [2,6]. Estos se corresponde con los patrones Larga(alcista)—Corta(bajista) y Larga(bajista)—Spinning top(bajista). La segunda variante del patrón es preferible, ya que su Frecuencia es de casi el doble.

En último lugar, hemos simulado los modelos de velas que constan de tres velas. Puesto que al elegir todas las posibles variantes de modelos simples de velas hemos obtenido un volumen demasiado alto de datos para el estudio, hemos elegido los 4 tipos más frecuentes de las anteriores muestras: 1,2,5,6. Si hemos configurado todo correctamente, la pestaña de Ajustes tendrá el siguiente aspecto:

Fig.14 Ajustes de simulación de los patrones de tres velas generados.

Como podemos ver, el "Valor umbral de la tendencia" se ha dejado en 400. A continuación, pasamos a la pestaña de Búsqueda automática y pulsamos de nuevo en GBPUSD. La selección de modelos de vela prometedores se realiza según el mismo principio de antes: un valor alto de movimiento de precio en una dirección, y varias veces menor en la otra. Esto se ve perfectamente por el coeficiente de efectividad. Por ejemplo, hemos encontrado dos veces seguidas resultados destacados con coeficientes y parámetros de probabilidad muy buenos.

Fig.15 Resultados de simulación de los patrones de tres velas generados.

Se trata de los patrones [2,5,2] y los siguientes [2,5,5], los cuales se corresponden con los patrones Larga(bajista)—Spinning top(alcista)Larga(bajista) y Larga(bajista)—Spinning top(alcista)—Spinning top(bajista). El primer modelo de velas tiene un valor alto de tendencia alcista, al tiempo que un alto coeficiente de efectividad con respecto a otros. El segundo, tiene una buena probabilidad en una dirección, pero su efectividad es menor. 

Un gran número de combinaciones de ajustes puede dar otros resultados y reglas interesantes. Una pequeña recomendación: al realizar la simulación, no se aconseja usar los once tipos de vela, de lo contrario, el procesamiento puede durar mucho. Tanto más que el número máximo de posibles combinaciones de patrones para el estudio es igual a 1463, eso sin tener en cuenta el intervalo de muestra, el marco temporal, el valor umbral de la tendencia y los ajustes individuales de los tipos simples de vela. El lector comprenderá que semejante volumen de datos requerirá bastante tiempo.

Conclusión

Al final del artículo se adjunta un fichero con todos los archivos enumerados, clasificados por carpetas. Por eso, para que funcione correctamente, basta con colocar la carpeta MQL5 en la carpeta raíz del terminal.

Programas usados en el artículo:

#
 Nombre
Tipo
Descripción
1
PatternAnalyzer.mq5 Interfaz gráfica
 Panel de instrumentos para analizar el modelo de velas.
2 MainWindow.mqh Biblioteca  Biblioteca para la construcción de la interfaz gráfica
3 Program.mqh Biblioteca  Biblioteca de métodos para la creación de los elementos de la interfaz y los cálculos

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

Archivos adjuntos |
MQL5.zip (431.36 KB)
Usando los recursos computacionales de MATLAB 2018 en MetaTrader 5 Usando los recursos computacionales de MATLAB 2018 en MetaTrader 5
Tras la modernización del paquete MATLAB en 2015, es necesario analizar el método moderno de creación de bibliotecas DLL. Usando como ejemplo un indicador de pronóstico, en el artículo se ilustran las peculiaridades de la vinculación de MetaTrader 5 y MATLAB al utilizar las versiones modernas de 64 bits de la plataforma. El análisis de todas las posibilidades de conexión de MATLAB permitirá al desarrollador de MQL5 crear más rápido aplicaciones con recursos computacionales ampliados, evitando tropezones indeseables.
Análisis sintáctico de MQL usando las herramientas de MQL Análisis sintáctico de MQL usando las herramientas de MQL
El presente artículo describe el preprocesador, escáner y el parser (analizador sintáctico) para el análisis sintáctico de los códigos fuente en el lenguaje MQL. La implementación en MQL se adjunta.
Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte II): Colección de órdenes y transacciones históricas Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte II): Colección de órdenes y transacciones históricas
En el primer artículo, comenzamos a crear una gran biblioteca multiplataforma, cuyo cometido es facilitar la creación de programas para las plataformas MetaTrader 5 y MetaTrader 4. Creamos el objeto abstracto COrder, que es el objeto básico para guardar los datos de las órdenes y transacciones históricas, así como de las órdenes y posiciones de mercado. Ahora, vamos a crear todos los objetos necesarios para guardar los datos de la historia de la cuenta en colecciones.
Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte I): Concepto, organización de datos y primeros resultados Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte I): Concepto, organización de datos y primeros resultados
Tras analizar una ingente cantidad de estrategias comerciales, ejecutar multitud de encargos de preparación de programas para los terminales MT5 y MT4, y visitar distintos sitios web de MetaTrader, hemos llegado a la conclusión de que una mayoría aplastante de esta diversidad se construye en la práctica sobre un número fijo de funciones elementales, acciones y valores que se repiten de un programa a otro. El resultado de semejante trabajo es la biblioteca multiplataforma "DoEasy", que permite crear fácil y rápidamente programas para МetaТrader 5 y МetaТrader 4