Constructor de estrategias basado en las figuras técnicas de Merrill

4 noviembre 2019, 09:49
Alexander Fedosov
0
315

Contenido

Introducción

En el artículo anterior, analizamos un modelo de aplicación de las figuras técnicas de Merrill a diferentes datos, tales como el valor del precio en el gráfico de un instrumento de divisa y los valores de los diferentes indicadores del paquete estándar del terminal MetaTrader 5: ATR, WPR, CCI, RSI y otros. Asimismo, desarrollamos una interfaz gráfica para investigar esta idea. Sin embargo, la interfaz tiene un carácter experimental, ya que su objetivo es comprobar y encontrar los métodos de aplicación de las figuras técnicas. A su vez, deberemos construir en forma de estrategia comercial los métodos de uso de las configuraciones exitosas encontradas, y también ponerlas a prueba. En esta artículo, desarrollaremos el instrumental básico para construir y simular estrategias comerciales.


Formulando la tarea y el prototipo de la aplicación

Antes de desarrollar la aplicación, merece la pena definir la lista de posibilidades y elementos de la interfaz que necesitaremos para el control. Comenzaremos por dos apartados:

  • La pestaña Constructor
  • La pestaña Ajustes

En la pestaña Constructor se encontrará el conjunto de elementos de la interfaz imprescindibles para formar la estrategia comercial. Veamos todos los elementos básicos:

  • El recuadro de símbolos, que consta de la lista completa disponible en la pestaña del terminal de la Observación de Mercado.
  • Un instrumento de filtrado para el recuadro de símbolos, que hará más cómoda la búsqueda de grupos aparte de instrumentos comerciales.
  • Dos apartados iguales para la formación de la señal: para la compra y para la venta. Ambos apartados son iguales en cuanto a la composición de los elementos, por eso describiremos más abajo solo uno de ellos.
  • El intervalo de fechas para la simulación.
  • La selección del marco temporal actual.
  • El último de los apartado básicos de la pestaña Constructor es el bloque con los resultados de la comprobación de la estrategia comercial establecida.

Vamos a ver ahora de qué consta el Apartado para la formación de una señal:

  1. El patrón elegido del conjunto de figuras técnicas de Merrill.
  2. Un conjunto de tres señales al que se puedan aplicar las figuras técnicas. Para cada una de las señales existirá la posibilidad de desactivación. Es decir, al seleccionar solo una, se dará la entrada en el mercado solo según esta. Si seleccionamos más de una señal, la entrada se realizará según una de ellas. La aplicación de figuras técnicas se realizará con respecto a una lista de diferentes indicadores, e incluso con respecto al propio precio. 
  3. Estableciendo el Take-profit y el Stop-loss.

En la siguiente pestaña de Ajustes, se encontrarán los parámetros para los indicadores estándar, así como la posibilidad de cargar nuestro propio indicador.

Usando como base las posibilidades deseadas, establecidas más arriba, crearemos el prototipo de la futura aplicación. En la fig.1, más abajo, mostramos el esquema y el conjunto de los elementos de la interfaz para la primera pestaña Constructor:


Fig.1 Prototipo y Elementos de la interfaz para la pestaña Constructor.

De la misma forma, crearemos un prototipo y colocaremos los elementos para la pestaña Ajustes. 


Fig.2 Prototipo y Elementos de la interfaz para la pestaña Ajustes

Aquí tenemos que prestar atención al segundo subapartado de Ajustes de los indicadores personalizados y el campo de edición. En función de cómo se corresponden con la sintaxis de la función iCustom la introducción de los valores correctos y el funcionamiento de un indicador de terceros. En primer lugar, esto se relaciona con la correcta introducción de la ruta hasta el indicador:

name

[in]  Nombre del indicador de usuario que contiene la ruta respecto al directorio raíz de los indicadores(MQL5/Indicators/). Si el indicador se encuentra en un subdirectorio, por ejemplo, en MQL5/Indicators/Examples, el nombre deberá tener el aspecto correspondiente, para ser más exactos: Examples\\nombre_del_indicador" (indicando necesariamente como separador la barra oblicua inversa doble en lugar de la barra oblicua inversa simple).

Para aplicar un indicador de terceros a una de las señales, es necesario aplicar a cualquiera de las señales en la lista desplegable el punto Custom como se muestra en la fig.3, más abajo:

Fig.3 Selección del uso de un indicador personalizado.

La secuencia completa de acciones para el ajuste y el uso del Constructor de estrategias se mostrará más tarde en el artículo, después de la implementación programática de la aplicación.

Implementando el constructor para la simulación

Antes de crear la interfaz gráfica de nuestra aplicación, vamos a definir los elementos básicos que usaremos para construir y desarrollar los demás en una cadena lógica.

En la aplicación, como podemos ver en la fig.1, tenemos dos ventanas: la primera es la ventana princial de la aplicación, y la segunda, la ventana de diálogo de Ajuste del intervalo de fechas. En este caso, además, la primera tiene dos pestañas, Constructor y Ajustes. Por eso, el método principal de creación de la interfaz  CreateGUI() combinará dos métodos para crear las ventanas: 

  • CreateWindow() crea la ventana principal.
  • CreateDateSetting() crea la ventana de Ajuste del intervalo de fechas.
//+------------------------------------------------------------------+
//| Creando la interfaz gráfica del programa                         |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
{
//--- Creando el panel
   if(!CreateWindow("Merrill Constructor"))
      return(false);
//--- Creando la ventana de diálogo
   if(!CreateDateSetting())
      return(false);
//--- Finalizando la creación de GUI
   CWndEvents::CompletedGUI();
   return(true);
}
//+------------------------------------------------------------------+

Vamos a analizar de qué consta cada una de ellas a su vez. Comenzaremos por el método CreateDateSetting(), puesto que es más sencillo en cuanto a su implementación y la composición de los elementos que incluye. En la fig.4 se representa aparte el elemento de la interfaz que se implementa con este método:

Fig.4 Ventana de Ajuste del intervalo de fechas.

La ventana consta de la propia ventana de diálogo, así como de 2 elementos de calendario y dos elementos para el ajuste de la fecha inicial y final. Tras determinar con exactitud de qué consta este elemento, lo implementamos dentro del método CreateDate Setting().

//+------------------------------------------------------------------+
//| Crea la ventana de diálogo de selección del intervalo            |
//+------------------------------------------------------------------+
bool CProgram::CreateDateSetting(void)
{
//--- Añadimos el puntero de la ventana a la matriz de ventanas
   CWndContainer::AddWindow(m_window[1]);
//--- Coordenadas
   int x=m_date_range.X();
   int y=m_date_range.Y()+m_date_range.YSize();
//--- Propiedades
   m_window[1].XSize(372);
   m_window[1].YSize(230);
   m_window[1].WindowType(W_DIALOG);
   m_window[1].IsMovable(true);
//--- Creando el formulario
   if(!m_window[1].CreateWindow(m_chart_id,m_subwin,"",x,y))
      return(false);
//---
   if(!CreateCalendar(m_calendar1,m_window[1],10,25,D'01.01.2019',1))
      return(false);
   if(!CreateCalendar(m_calendar2,m_window[1],201,25,m_calendar2.Today(),1))
      return(false);
//---
   if(!CreateTimeEdit(m_time_edit1,m_window[1],10,200,"Время",1))
      return(false);
   if(!CreateTimeEdit(m_time_edit2,m_window[1],200,200,"Время",1))
      return(false);
//---
   return(true);
}

Ahora, pasamos al método CreateWindow(), que implementa la ventana principal de la aplicación. Este método es bastante amplio en cuanto a su estructura, por eso solo vamos a analizar los momentos clave de los que consta. El primer momento clave es la creación de la propia ventana y su estructura básica, concretamente, las dos pestañas Constructor y Ajustes. 

//+------------------------------------------------------------------+
//| Crea el formulario para los elementos de control                 |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
{
#define VERSION " 1.0"
   color caption=C'0,130,255';
   int ygap=30;
//--- Añadimos el puntero de la ventana a la matriz de ventanas
   CWndContainer::AddWindow(m_window[0]);
//--- Propiedades
   m_window[0].XSize(900);
   m_window[0].YSize(600);
   m_window[0].FontSize(9);
   m_window[0].CloseButtonIsUsed(true);
   m_window[0].CollapseButtonIsUsed(true);
   m_window[0].CaptionColor(caption);
   m_window[0].CaptionColorHover(caption);
   m_window[0].CaptionColorLocked(caption);
//--- Creando el formulario
   if(!m_window[0].CreateWindow(m_chart_id,m_subwin,caption_text+VERSION,10,20))
      return(false);
//--- Pestañas
   if(!CreateTabs(150,20))
      return(false);

Esta parte del código crea la propia ventana de diálogo, mientras que el método CreateTabs() se encarga de añadir las dos pestañas descritas más arriba:

//+------------------------------------------------------------------+
//| Crea el grupo con las pestañas                                   |
//+------------------------------------------------------------------+
bool CProgram::CreateTabs(const int x_gap,const int y_gap)
{
//--- Guardamos el puntero al elemento principal
   m_tabs1.MainPointer(m_window[0]);
//--- Propiedades
   m_tabs1.IsCenterText(true);
   m_tabs1.PositionMode(TABS_LEFT);
   m_tabs1.AutoXResizeMode(true);
   m_tabs1.AutoYResizeMode(true);
   m_tabs1.AutoYResizeBottomOffset(25);
   m_tabs1.TabsYSize(40);   
//--- Añadimos las pestañas con las propiedades indicadas
   for(int i=0; i<ArraySize(m_tabs_names); i++)
      m_tabs1.AddTab(m_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);
}

Antes, en el prototipo se determinaban los componentes para cada una de las pestañas. Para la pestaña Constructor (fig.1) y para la pestaña Ajustes (fig.2). Por eso, ahora vamos a analizar la implementación de los elementos incluidos en cada una de ellas. La primera pestaña, Constructor, contiene bastantes tipos repetidos de elementos, por eso vamos a analizar solo los subapartados básicos y la lista de métodos usados para implementar estos elementos.

//---- Pestaña Constructor
//--- Filtro de instrumentos
   if(!CreateSymbolsFilter(10,10))
      return(false);
   if(!CreateSymbolsTable(10,45))
      return(false);
//--- Marco temporal de trabajo
   if(!CreateTextLabel(m_text_labels1[2],290,10,"Timeframe",0))
      return(false);
   if(!CreateTimeframe1(440,10))
      return(false);
//--- Intervalo de fechas
   if(!CreateButton(m_date_range,240,10))
      return(false);
//--- Etiquetas de texto
   if(!CreateTextLabel(m_text_labels1[0],int(0.35*(m_window[0].XSize()-150)-100),10+ygap,"BUY—Signal",0))
      return(false);
   if(!CreateTextLabel(m_text_labels1[1],int(0.75*(m_window[0].XSize()-150)-100),10+ygap,"SELL—Signal",0))
      return(false);
//--- Seleccionando el patrón
   if(!PatternType1(int(0.35*(m_window[0].XSize()-150)-100),40+ygap,0))
      return(false);
   if(!CreateCheckBox(m_checkbox[0],int(0.35*(m_window[0].XSize()-150)-120),45+ygap,"Паттерн"))
      return(false);
   if(!PatternType2(int(0.75*(m_window[0].XSize()-150)-100),40+ygap,0))
      return(false);
   if(!CreateCheckBox(m_checkbox[1],int(0.75*(m_window[0].XSize()-150)-120),45+ygap,"Паттерн"))
      return(false);
//--- Seleccionando la aplicación de patrones
   if(!AppliedType1(int(0.35*(m_window[0].XSize()-150)-100),80+ygap))
      return(false);
   if(!AppliedType2(int(0.35*(m_window[0].XSize()-150)-100),50+33*2+ygap))
      return(false);
   if(!AppliedType3(int(0.35*(m_window[0].XSize()-150)-100),50+33*3+ygap))
      return(false);
   if(!AppliedType4(int(0.75*(m_window[0].XSize()-150)-100),80+ygap))
      return(false);
   if(!AppliedType5(int(0.75*(m_window[0].XSize()-150)-100),50+33*2+ygap))
      return(false);
   if(!AppliedType6(int(0.75*(m_window[0].XSize()-150)-100),50+33*3+ygap))
      return(false);
//--- Casillas de verificación de las señales
   for(int i=2; i<8; i++)
   {
      if(i<5)
         if(!CreateCheckBox(m_checkbox[i],int(0.35*(m_window[0].XSize()-150)-120),50+35*(i-1)+ygap,"Сигнал "+string(i-1)))
            return(false);
      if(i>=5)
         if(!CreateCheckBox(m_checkbox[i],int(0.75*(m_window[0].XSize()-150)-120),50+35*(i-4)+ygap,"Сигнал "+string(i-4)))
            return(false);
   }
//--- Ajustes para establecer el Take-profit y el Stop-loss
   if(!CreateEditValue(m_takeprofit1,int(0.35*(m_window[0].XSize()-150)-120),50+35*4+ygap,"Тейк Профит",500,0))
      return(false);
   if(!CreateEditValue(m_stoploss1,int(0.35*(m_window[0].XSize()-150)-120),50+35*5+ygap,"Стоп Лосс",500,0))
      return(false);
   if(!CreateEditValue(m_takeprofit2,int(0.75*(m_window[0].XSize()-150)-120),50+35*4+ygap,"Тейк Профит",500,0))
      return(false);
   if(!CreateEditValue(m_stoploss2,int(0.75*(m_window[0].XSize()-150)-120),50+35*5+ygap,"Стоп Лосс",500,0))
      return(false);
//--- Informe
   if(!CreateReportFrame(m_frame[2],"",int(0.35*(m_window[0].XSize()-150)-120),60+35*6+ygap))
      return(false);
   for(int i=0; i<6; i++)
   {
      if(i<3)
         if(!CreateTextLabel(m_report_text[i],int(0.4*(m_window[0].XSize()-150)-120),60+35*(7+i)+ygap,"",0))
            return(false);
      if(i>=3)
         if(!CreateTextLabel(m_report_text[i],int(0.75*(m_window[0].XSize()-150)-120),60+35*(7+i-3)+ygap,"",0))
            return(false);
      m_report_text[i].IsCenterText(false);
   }

Vamos a ahora a analizar las partes básicas de la interfaz, en concreto, qué es lo que implementan y de qué métodos constan.

1. Filtro de instrumentos.

Consta de los métodos CreateSymbolsFilter() CreateSymbolsTable(), que implementan el siguiente elemento:

Fig.5 Filtro de instrumentos.

CreateSymbolsFilter()  implementa un campo de edición con una casilla de verificación y un botón de Búsqueda.

//+--------------------------------------------------------------------------+
//| Crea la casilla de verificación con el campo de edición "Symbols filter" |
//+--------------------------------------------------------------------------+
bool CProgram::CreateSymbolsFilter(const int x_gap,const int y_gap)
{
//--- Guardamos el puntero al elemento principal
   m_symb_filter.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(0,m_symb_filter);
//--- Propiedades
   m_symb_filter.CheckBoxMode(true);
   m_symb_filter.YSize(25);
   m_symb_filter.FontSize(11);
   m_symb_filter.XSize(200);
   m_symb_filter.GetTextBoxPointer().XGap(20);
   m_symb_filter.GetTextBoxPointer().XSize(100);
   m_symb_filter.GetTextBoxPointer().YSize(25);
   m_symb_filter.GetTextBoxPointer().AutoSelectionMode(true);
   m_symb_filter.SetValue("USD"); // "EUR,USD" "EURUSD,GBPUSD" "EURUSD,GBPUSD,AUDUSD,NZDUSD,USDCHF"
//--- Creamos el elemento de control
   if(!m_symb_filter.CreateTextEdit("",x_gap,y_gap))
      return(false);
//--- Activamos la casilla de verificación
   m_symb_filter.IsPressed(true);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_symb_filter);
//---
   if(!CreateRequest(x_gap+125,y_gap))
      return(false);
   return(true);
}

CreateSymbolsTable() implementa el recuadro que muestra los instrumentos de divisa filtrados desde la Observación de mercado.

//+------------------------------------------------------------------+
//| Crea el recuadro de símbolos                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateSymbolsTable(const int x_gap,const int y_gap)
{
#define ROWS1_TOTAL    1
//--- Guardamos el puntero al elemento principal
   m_table_symb.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(0,m_table_symb);
//--- Matriz de anchura de las columnas
   int width[1]= {119};
//--- Matriz de alineamiento del texto en columnas
   ENUM_ALIGN_MODE align[1]= {ALIGN_CENTER};
//--- Matriz de sangría del texto en las columnas según el eje X
   int text_x_offset[1]= {5};
//--- Propiedades
   m_table_symb.XSize(120);
   m_table_symb.TableSize(1,ROWS1_TOTAL);
   m_table_symb.ColumnsWidth(width);
   m_table_symb.TextAlign(align);
   m_table_symb.FontSize(10);
   m_table_symb.TextXOffset(text_x_offset);
   m_table_symb.ShowHeaders(true);
   m_table_symb.SelectableRow(true);
   m_table_symb.IsWithoutDeselect(true);
   m_table_symb.IsZebraFormatRows(clrWhiteSmoke);
   m_table_symb.AutoYResizeMode(true);
   m_table_symb.AutoYResizeBottomOffset(3);
   m_table_symb.HeadersColor(C'0,130,255');
   m_table_symb.HeadersColorHover(clrCornflowerBlue);
   m_table_symb.HeadersTextColor(clrWhite);
   m_table_symb.BorderColor(C'0,100,255');
//--- Creamos el elemento de control
   if(!m_table_symb.CreateTable(x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_table_symb);
   return(true);
}

2. El marco temporal de trabajo y el botón del Intervalo de fechas.

Estos elementos implementan la selección del marco temporal de trabajo para la simulación, mientras que el papel del botón del Intervalo de fechas consiste en abrir la ventana de diálogo homónima, que hemos analizado anteriormente. El método CreateButton() implementa el botón, mientras que los métodos  CreateTextLabel()CreateTimeframe1() implementan el rótulo y la lista desplegable, respectivamente. Debemos añadir que los métodos CreateButton() y CreateTextLabel() son métodos universales que se usarán más adelante, por eso analizaremos aquí su listado una sola vez. En la fig.6 se muestran estos elementos aparte:

Fig.6 Botón de Intervalo de fechas y selección del marco temporal de trabajo.

//+------------------------------------------------------------------+
//| Crea una etiqueta de texto en la primera pestaña                 |
//+------------------------------------------------------------------+
bool CProgram::CreateTextLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text,int tab)
{
//--- Guardamos el puntero a la ventana
   text_label.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(tab,text_label);
//---
   text_label.Font("Trebuchet");
   text_label.FontSize(11);
   text_label.XSize(200);
   text_label.LabelColor(C'0,100,255');
   text_label.IsCenterText(true);
//--- Creando el botón
   if(!text_label.CreateTextLabel(label_text,x_gap,y_gap))
      return(false);
//--- Añadimos el puntero al elemento en la base
   CWndContainer::AddToElementsArray(0,text_label);
   return(true);
}
//+--------------------------------------------------------------------------------+
//| Crea el botón para representar la ventana de selección del intervalo de fechas |
//+--------------------------------------------------------------------------------+
bool CProgram::CreateButton(CButton &button,const int x_gap,const int y_gap)
{
//--- Guardar el puntero al elemento principal
   button.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(0,button);
//--- Propiedades
   button.XSize(100);
   button.YSize(25);
   button.FontSize(11);
   button.IsHighlighted(false);
   button.IsCenterText(true);
   button.BorderColor(C'0,100,255');
   button.BackColor(clrAliceBlue);
//--- Creamos el elemento de control
   if(!button.CreateButton("Диапазон дат",x_gap,y_gap))
      return(false);
//--- Añadimos el puntero al elemento en la base
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}

El método CreateTimeframe1() supone una lista desplegable con todos los marcos temporales disponibles.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateTimeframe1(const int x_gap,const int y_gap)
{
//--- Transmitir el objeto del panel
   m_timeframe1.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(0,m_timeframe1);
//--- Matriz con los valores de los puntos en la lista
   string timeframe_names[21]=
   {
      "M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30",
      "H1","H2","H3","H4","H6","H8","H12","D1","W1","MN"
   };
//--- Estableciendo las propiedades antes de la creación
   m_timeframe1.XSize(50);
   m_timeframe1.YSize(25);
   m_timeframe1.ItemsTotal(21);
   m_timeframe1.FontSize(12);
   m_timeframe1.LabelColor(C'0,100,255');
   CButton *but=m_timeframe1.GetButtonPointer();
   but.FontSize(10);
   but.XSize(50);
   but.BackColor(clrAliceBlue);
   but.XGap(1);
   m_timeframe1.GetListViewPointer().FontSize(10);
//--- Guardamos los valores de los puntos en la lista del cuadro combinado
   for(int i=0; i<21; i++)
      m_timeframe1.SetValue(i,timeframe_names[i]);
//--- Obtenemos el puntero de la lista
   CListView *lv=m_timeframe1.GetListViewPointer();
//--- Establecemos las propiedades de la lista
   lv.LightsHover(true);
   m_timeframe1.SelectItem(0);
//--- Creamos el elemento de control
   if(!m_timeframe1.CreateComboBox("",x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_timeframe1);
   return(true);
}

3. Etiquetas de texto de los apartados y los elementos de selección del patrón para las señales de compra y venta.

Para crear las etiquetas de texto, se usa el mismo método CreateTextLabel() (por eso no lo vamos a analizar), así como dos métodos que implementan las casillas de verificación y el menú desplegable de selección de la figura técnica de Merrill para la simulación.

Fig.7 Etiquetas de texto de los apartados y selección de la figura técnica.

El método CreateCheckBox() crea las casillas de verificación con el encabezado Patrón.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateCheckBox(CCheckBox &checkbox,const int x_gap,const int y_gap,const string text)
{
//--- Guardamos el puntero al elemento principal
   checkbox.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(0,checkbox);
//--- Propiedades
   checkbox.YSize(25);
   checkbox.GreenCheckBox(true);
   checkbox.IsPressed(true);
   checkbox.FontSize(12);
   checkbox.LabelColor(C'0,100,255');
   checkbox.LabelColorPressed(C'0,100,255');
//--- Creamos el elemento de control
   if(!checkbox.CreateCheckBox(text,x_gap,y_gap))
      return(false);
//--- Añadimos el puntero al elemento en la base
   CWndContainer::AddToElementsArray(0,checkbox);
   return(true);
}

Los métodos PatternType1() y PatternType2() son idénticos.

//+------------------------------------------------------------------+
//| Crea el cuadro combinado 1                                       |
//+------------------------------------------------------------------+
bool CProgram::PatternType1(const int x_gap,const int y_gap,const int tab)
{
//--- Número total de puntos en la lista
#define ITEMS_TOTAL1 32
//--- Transmitir el objeto del panel
   m_combobox1.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(tab,m_combobox1);
//--- Matriz con los valores de los puntos en la lista
   string pattern_names[ITEMS_TOTAL1]=
   {
      "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"
   };
//--- Estableciendo las propiedades antes de la creación
   m_combobox1.XSize(200);
   m_combobox1.YSize(25);
   m_combobox1.ItemsTotal(ITEMS_TOTAL1);
   m_combobox1.GetButtonPointer().FontSize(10);
   m_combobox1.GetButtonPointer().BackColor(clrAliceBlue);
   m_combobox1.GetListViewPointer().FontSize(10);
//--- Guardamos los valores de los puntos en la lista del cuadro combinado
   for(int i=0; i<ITEMS_TOTAL1; i++)
      m_combobox1.SetValue(i,pattern_names[i]);
//--- Obtenemos el puntero de la lista
   CListView *lv=m_combobox1.GetListViewPointer();
//--- Establecemos las propiedades de la lista
   lv.LightsHover(true);
   m_combobox1.SelectItem(0);
//--- Creamos el elemento de control
   if(!m_combobox1.CreateComboBox("",x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_combobox1);
   return(true);
}

4. Selección de la aplicación de patrones y casillas de verificación de las señales.

Este bloque de la interfaz consta de un conjunto de señales para el ajuste de las señales de Compra y Venta. Cada bloque consta de una selección opcional de una a tres señales que pueden ser condiciones de entrada en la transacción. Consta de los métodos CreateCheckBox() y AppliedTypeN().


Fig.8 Aplicación de patrones y casillas de verificación. 

Los métodos AppliedType1()—AppliedType6() son idénticos en cuanto a su estructura, y suponen una lista desplegable con selección de la matriz de datos en la que se buscarán las señales en forma de figuras técnicas.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::AppliedType1(const int x_gap,const int y_gap)
{
//--- Transmitir el objeto del panel
   m_applied1.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(0,m_applied1);
//--- Matriz con los valores de los puntos en la lista
   string pattern_names[9]=
   {
      "Price","ATR","CCI","DeMarker","Force Ind","WPR","RSI","Momentum","Custom"
   };
//--- Estableciendo las propiedades antes de la creación
   m_applied1.XSize(200);
   m_applied1.YSize(25);
   m_applied1.ItemsTotal(9);
   m_applied1.GetButtonPointer().FontSize(10);
   m_applied1.GetButtonPointer().BackColor(clrAliceBlue);
   m_applied1.GetListViewPointer().FontSize(10);
//--- Guardamos los valores de los puntos en la lista del cuadro combinado
   for(int i=0; i<9; i++)
      m_applied1.SetValue(i,pattern_names[i]);
//--- Obtenemos el puntero de la lista
   CListView *lv=m_applied1.GetListViewPointer();
//--- Establecemos las propiedades de la lista
   lv.LightsHover(true);
   m_applied1.SelectItem(0);
//--- Creamos el elemento de control
   if(!m_applied1.CreateComboBox("",x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_applied1);
   return(true);
}

5. Ajustes para establecer el Take-profit y el Stop-loss.

Apartado de la interfaz que permite ajustar por separado el Take-profit y el Stop-loss tanto para la señal de compra, como para la señal de venta. El ajuste se realiza en puntos. 

Fig.9 Campo de edición para el Take-profit y el Stop-loss.

Para implementar estos campos de edición, se usa el método universal CreateEditValue().

//+------------------------------------------------------------------+
//| Creando campo de edición                                         |
//+------------------------------------------------------------------+
bool CProgram::CreateEditValue(CTextEdit &text_edit,const int x_gap,const int y_gap,const string label_text,const int value,const int tab)
{
//--- Guardamos el puntero al elemento principal
   text_edit.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(tab,text_edit);
//--- Propiedades
   text_edit.XSize(210);
   text_edit.YSize(24);
   text_edit.LabelColor(C'0,100,255');
   text_edit.FontSize(12);
   text_edit.MaxValue(1000);
   text_edit.MinValue(10);
   text_edit.SpinEditMode(true);
   text_edit.SetValue((string)value);
   text_edit.GetTextBoxPointer().AutoSelectionMode(true);
   text_edit.GetTextBoxPointer().XGap(100);
//--- Creamos el elemento de control
   if(!text_edit.CreateTextEdit(label_text,x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}

6. Resultados de la simulación e informe.

Este bloque consta de los resultados de la simulación. Para implementarlo, se usa el método CreateTextLabel(), que ya hemos analizado. 

Fig.10 Bloque del informe.

Con esto, podemos dar por finalizada la implementación de la pestaña Constructor. A continuación, vamos a analizar la pestaña Ajustes.

1. Ajustes de los parámetros de los indicadores estándar.

En este apartado entran todos los ajustes de los indicadores que se ofrecen para la simulación y el análisis.

Fig.11 Bloque de ajustes de los indicadores estándar.

Para implementar este bloque, se usan tres métodos CreateFrame() para crear un apartado visual con contorno, así como el método universal de campo de edición para crear los parámetros de los indicadores CreateIndSetting() y el conjunto de métodos IndicatorSetting1()IndicatorSetting4() para las listas desplegables de los parámetros Ma Method, Volumes, Price.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateFrame(CFrame &frame,const string text,const int x_gap,const int y_gap)
{
//--- Guardamos el puntero al elemento principal
   frame.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(1,frame);
//---
   frame.XSize(350);
   frame.YSize(500);
   frame.LabelColor(C'0,100,255');
   frame.BorderColor(C'0,100,255');
   frame.FontSize(11);
   frame.AutoYResizeMode(true);
   frame.AutoYResizeBottomOffset(100);
   frame.GetTextLabelPointer().XSize(250);
//--- Creamos el elemento de control
   if(!frame.CreateFrame(text,x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,frame);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::IndicatorSetting1(const int x_gap,const int y_gap,const string text)
{
//--- Transmitir el objeto del panel
   m_ind_set1.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(1,m_ind_set1);
//--- Matriz con los valores de los puntos en la lista
   string pattern_names[4]=
   {
      "Simple","Exponential","Smoothed","Linear weighted"
   };
//--- Estableciendo las propiedades antes de la creación
   m_ind_set1.XSize(200);
   m_ind_set1.YSize(25);
   m_ind_set1.ItemsTotal(4);
   m_ind_set1.FontSize(12);
   m_ind_set1.LabelColor(C'0,100,255');
   CButton *but=m_ind_set1.GetButtonPointer();
   but.FontSize(10);
   but.XSize(100);
   but.BackColor(clrAliceBlue);
   m_ind_set1.GetListViewPointer().FontSize(10);
//--- Guardamos los valores de los puntos en la lista del cuadro combinado
   for(int i=0; i<4; i++)
      m_ind_set1.SetValue(i,pattern_names[i]);
//--- Obtenemos el puntero de la lista
   CListView *lv=m_ind_set1.GetListViewPointer();
//--- Establecemos las propiedades de la lista
   lv.LightsHover(true);
   m_ind_set1.SelectItem(0);
//--- Creamos el elemento de control
   if(!m_ind_set1.CreateComboBox(text,x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_ind_set1);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::IndicatorSetting3(const int x_gap,const int y_gap,const string text)
{
//--- Transmitir el objeto del panel
   m_ind_set3.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(1,m_ind_set3);
//--- Matriz con los valores de los puntos en la lista
   string pattern_names[2]=
   {
      "Tick volume","Real Volume"
   };
//--- Estableciendo las propiedades antes de la creación
   m_ind_set3.XSize(200);
   m_ind_set3.YSize(25);
   m_ind_set3.ItemsTotal(2);
   m_ind_set3.FontSize(12);
   m_ind_set3.LabelColor(C'0,100,255');
   CButton *but=m_ind_set3.GetButtonPointer();
   but.FontSize(10);
   but.XSize(100);
   but.BackColor(clrAliceBlue);
   m_ind_set3.GetListViewPointer().FontSize(10);
//--- Guardamos los valores de los puntos en la lista del cuadro combinado
   for(int i=0; i<2; i++)
      m_ind_set3.SetValue(i,pattern_names[i]);
//--- Obtenemos el puntero de la lista
   CListView *lv=m_ind_set3.GetListViewPointer();
//--- Establecemos las propiedades de la lista
   lv.LightsHover(true);
   lv.ItemYSize(20);
   lv.YSize(42);
   m_ind_set3.SelectItem(0);
//--- Creamos el elemento de control
   if(!m_ind_set3.CreateComboBox(text,x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_ind_set3);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::IndicatorSetting4(const int x_gap,const int y_gap,const string text)
{
//--- Transmitir el objeto del panel
   m_ind_set4.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(1,m_ind_set4);
//--- Matriz con los valores de los puntos en la lista
   string pattern_names[4]=
   {
      "Open","Close","High","Low"
   };
//--- Estableciendo las propiedades antes de la creación
   m_ind_set4.XSize(200);
   m_ind_set4.YSize(25);
   m_ind_set4.ItemsTotal(4);
   m_ind_set4.FontSize(12);
   m_ind_set4.LabelColor(C'0,100,255');
   CButton *but=m_ind_set4.GetButtonPointer();
   but.FontSize(10);
   but.XSize(100);
   but.BackColor(clrAliceBlue);
   m_ind_set4.GetListViewPointer().FontSize(10);
//--- Guardamos los valores de los puntos en la lista del cuadro combinado
   for(int i=0; i<4; i++)
      m_ind_set4.SetValue(i,pattern_names[i]);
//--- Obtenemos el puntero de la lista
   CListView *lv=m_ind_set4.GetListViewPointer();
//--- Establecemos las propiedades de la lista
   lv.LightsHover(true);
   lv.ItemYSize(20);
   lv.YSize(82);
   m_ind_set4.SelectItem(1);
//--- Creamos el elemento de control
   if(!m_ind_set4.CreateComboBox(text,x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_ind_set4);
   return(true);
}

 2. Idioma de la interfaz.

Elemento de la interfaz Idioma de la interfaz en forma de lista desplegable con selección de Inglés o Ruso para la interfaz. El método  LanguageSetting() es el encargado de crear este elemento:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::LanguageSetting(const int x_gap,const int y_gap,const string text)
{
//--- Transmitir el objeto del panel
   m_language_set.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(1,m_language_set);
//--- Matriz con los valores de los puntos en la lista
   string pattern_names[2]=
   {
      "Russian","English"
   };
//--- Estableciendo las propiedades antes de la creación
   m_language_set.XSize(200);
   m_language_set.YSize(25);
   m_language_set.ItemsTotal(2);
   m_language_set.FontSize(12);
   m_language_set.LabelColor(C'0,100,255');
   CButton *but=m_language_set.GetButtonPointer();
   but.FontSize(10);
   but.XSize(100);
   but.BackColor(clrAliceBlue);
   but.XGap(140);
   m_language_set.GetListViewPointer().FontSize(10);
//--- Guardamos los valores de los puntos en la lista del cuadro combinado
   for(int i=0; i<2; i++)
      m_language_set.SetValue(i,pattern_names[i]);
//--- Obtenemos el puntero de la lista
   CListView *lv=m_language_set.GetListViewPointer();
//--- Establecemos las propiedades de la lista
   lv.LightsHover(true);
   lv.ItemYSize(20);
   lv.YSize(42);
   m_language_set.SelectItem(1);
//--- Creamos el elemento de control
   if(!m_language_set.CreateComboBox(text,x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,m_language_set);
   return(true);
}

3. Ajuste de los parámetros de los indicadores personalizados.

Consta de un apartado visual con encabezado y contorno, creado con ayuda del método CreateFrame(), que hemos analizado antes, así como un campo de edición para el búfer de indicador mediante CreateIndSetting() y el nuevo método CreateCustomEdit() para introducir el nombre del indicador y sus parámetros separados con una coma.

Fig.12 Ajuste de los parámetros de los indicadores personalizados.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateCustomEdit(CTextEdit &text_edit,const int x_gap,const int y_gap,const string default_text)
{
//--- Guardamos el puntero al elemento principal
   text_edit.MainPointer(m_tabs1);
//--- Fijar a la pestaña
   m_tabs1.AddToElementsArray(1,text_edit);
//--- Propiedades
   text_edit.XSize(100);
   text_edit.YSize(24);
   text_edit.LabelColor(C'0,100,255');
   CTextBox *box=text_edit.GetTextBoxPointer();
   box.AutoSelectionMode(true);
   box.XSize(325);
   box.XGap(1);
   box.DefaultTextColor(clrSilver);
   box.DefaultText(default_text);
//--- Creamos el elemento de control
   if(!text_edit.CreateTextEdit("",x_gap,y_gap))
      return(false);
//--- Añadimos el objeto a la matriz general de grupos de objetos
   CWndContainer::AddToElementsArray(0,text_edit);
   return(true);
}

Con esto, podemos dar por terminada la creación de la parte visual. Ahora, tenemos que describir cómo tiene lugar el algoritmo de simulación de las estretagias comerciales incorporadas.

Para describir de la forma más comprensible posible el algoritmo de funcionamiento de la simulación con la ayuda de esta aplicación, deberemos definir las instrucciones o la secuencia de acciones necesarias para iniciar el test y obtener los resultados de la forma correcta. Una secuencia de acciones adecuadamente compuesta nos permitirá aclarar el principio de funcionamiento de cada interacción con la interfaz de la aplicación.

Paso 1. Selección del idioma de la interfaz

De acuerdo con nuestra implementación, esta opción se encuentra en la pestaña Ajustes (Settings), en la lista desplegable. Ahora, vamos a describir el algoritmo de funcionamiento del cambio de idioma de la interfaz. El encargado de ello es el evento de usuario de selección del punto en el cuadro combinado que llama el método ChangeLanguage().

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

Ahora, vamos a ver los propios métodos para cambiar el idioma de la interfaz de la aplicación. A pesar de su amplitud, su idea es bastante sencilla.

//+------------------------------------------------------------------+
//| Cambiando el idioma de la interfaz                               |
//+------------------------------------------------------------------+
bool CProgram::ChangeLanguage(const long id)
{
//--- Comprobando el identificador del elemento
   if(id!=m_language_set.Id())
      return(false);
   m_lang_index=m_language_set.GetListViewPointer().SelectedItemIndex();
//---
   if(m_lang_index==0)
   {
      //--- Pestaña Constructor
      m_tabs1.Text(0,"Constructor");
      m_tabs1.Text(1,"Ajustes");
      m_table_symb.SetHeaderText(0,"Símbolo");
      m_request.LabelText("Búsqueda");
      m_date_range.LabelText("Intervalo de fechas");
      m_timeframe1.LabelText("Marco temporal");
      for(int i=0; i<8; i++)
      {
         if(i<2)
            m_checkbox[i].LabelText("Patrón");
         else if(i>=2 && i<5)
            m_checkbox[i].LabelText("Señal "+string(i-1));
         else if(i>=5)
            m_checkbox[i].LabelText("Señal "+string(i-4));
      }
      m_takeprofit1.LabelText("Take Profit");
      m_takeprofit2.LabelText("Take Profit");
      m_stoploss1.LabelText("Stop Loss");
      m_stoploss2.LabelText("Stop Loss");
      m_frame[2].GetTextLabelPointer().LabelText("Informe");
      string report_label[6]=
      {
         "Transacciones totales: ","Transacciones cortas: ","Transacciones rentables: ",
         "Beneficio en puntos: ","Transacciones largas: ","Transacciones con pérdidas: "
      };
      for(int i=0; i<6; i++)
         m_report_text[i].LabelText(report_label[i]+"-");
      //--- Pestaña Ajustes
      m_frame[0].GetTextLabelPointer().LabelText("Ajustes de los indicadores estándar");
      m_frame[1].GetTextLabelPointer().LabelText("Ajustes de los indicadores personalizados");
      m_custom_buffer.LabelText("Número de búfer");
      m_custom_path.GetTextBoxPointer().DefaultText("Introduzca la dirección del indicador");
      m_custom_param.GetTextBoxPointer().DefaultText("Introduzca los parámetros del indicador separados por comas");
      m_language_set.LabelText("Idioma de la interfaz");
      //--- Ventana de Intervalo de fechas
      m_window[1].LabelText("Ajustes del intervalo de fechas");
      m_time_edit1.LabelText("Hora");
      m_time_edit2.LabelText("Hora");
      m_time_edit3.LabelText("Hora");
      m_time_edit4.LabelText("Hora");
      m_status_bar.SetValue(0,"No se ha seleccionado un símbolo para el análisis");
   }
   else
   {
      //--- Pestaña Constructor
      m_tabs1.Text(0,"Constructor");
      m_tabs1.Text(1,"Settings");
      m_table_symb.SetHeaderText(0,"Symbol");
      m_request.LabelText("Search");
      m_date_range.LabelText("Date range");
      m_timeframe1.LabelText("Timeframe");
      for(int i=0; i<8; i++)
      {
         if(i<2)
            m_checkbox[i].LabelText("Pattern");
         else if(i>=2 && i<5)
            m_checkbox[i].LabelText("Signal "+string(i-1));
         else if(i>=5)
            m_checkbox[i].LabelText("Signal "+string(i-4));
      }
      m_takeprofit1.LabelText("Take Profit");
      m_takeprofit2.LabelText("Take Profit");
      m_stoploss1.LabelText("Stop Loss");
      m_stoploss2.LabelText("Stop Loss");
      m_frame[2].GetTextLabelPointer().LabelText("Report");
      string report_label[6]=
      {
         "Total trades: ","Short Trades: ","Profit Trades: ",
         "Profit in points: ","Long Trades: ","Loss Trades: "
      };
      for(int i=0; i<6; i++)
         m_report_text[i].LabelText(report_label[i]+"-");
      //--- Pestaña Ajustes
      m_frame[0].GetTextLabelPointer().LabelText("Standard Indicator Settings");
      m_frame[1].GetTextLabelPointer().LabelText("Custom Indicator Settings");
      m_custom_buffer.LabelText("Buffer number");
      m_custom_path.GetTextBoxPointer().DefaultText("Enter the indicator path");
      m_custom_param.GetTextBoxPointer().DefaultText("Enter indicator parameters separated by commas");
      m_language_set.LabelText("Interface language");
      //--- Ventana de Intervalo de fechas
      m_window[1].LabelText("Date Range Settings");
      m_time_edit1.LabelText("Time");
      m_time_edit2.LabelText("Time");
      m_time_edit3.LabelText("Time");
      m_time_edit4.LabelText("Time");
      m_status_bar.SetValue(0,"No symbol selected for analysis");
   }
   return(true);
}

Paso 2. Ajuste de los parámetros de los indicadores

En la propia pestaña de ajustes, establecemos los valores de los parámetros de los indicadores en el caso de que simulemos determinados indicadores. O bien ajustamos los parámetros del indicador personalizado: el número de búfer, el nombre y los parámetros separados por comas. Debemos añadir que, para los indicadores personalizados, se soportan solo valores numéricos.

Paso 3. Ajuste del recuadro de símbolos.

Vamos a pasar a la pestaña Constructor, ajustando en la parte superior izquierda los instrumentos necesarios disponibles en la Observación de mercado. El encargado de ello será el método RequestData(), mientras que el evento de llamada de este método será la pulsación sobre el botón de Búsqueda.

   //--- Evento de pulsación del botón
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
   {
      //--- Solicitando datos
      RequestData(lparam);
....
//+------------------------------------------------------------------+
//| Mostrando los instrumentos en el recuadro de símbolos            |
//+------------------------------------------------------------------+
bool CProgram::RequestData(const long id)
{
//--- Comprobando el identificador del elemento
//---
   if(id==m_request.Id())
   {
      //--- Ocultar recuadro
      m_table_symb.Hide();
      //--- Inicializando el recuadro
      GetSymbols(m_symb_filter);
      RebuildingTables(m_table_symb);
      //--- Mostrar recuadro
      m_table_symb.Show();
   }
   return(true);
}

Paso 4. Selección del intervalo temporal de la simulación

Se trata también de un evento de pulsación sobre el botón de Intervalo de fechas. La lógica es sencilla: se abre la ventana de diálogo de Ajustes del intervalo de fechas.

//--- Evento de pulsación del botón
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
   {
...
      //---
      if(lparam==m_date_range.Id())
      {
         int x=m_date_range.X();
         int y=m_date_range.Y()+m_date_range.YSize();
         m_window[1].X(x);
         m_window[1].Y(y);
         m_window[1].OpenWindow();
         string val=(m_lang_index==0)?"Настройки диапазона дат":"Date Range Settings";
         m_window[1].LabelText(val);
      }
...

A la hora de seleccionar las fechas, debemos estar atentos, de lo contrario, si las fechas son incorrectas, se mostrarán mensajes sobre la incorrección de su indicación. Los errores pueden ser: la fecha final es mayor a la actual del terminal, o la fecha inicial es mayor a la final.

Paso 5. Establecimiento del marco temporal.

El marco temporal de trabajo es aplicable a los seis canales que se pueden ajustar en el constructor.

Paso 6. Activación de las señales de compra/venta y selección de la figura técnica para la simulación.

Por defecto, la simulación se realiza en ambas direcciones: tanto de compra, como de venta. Sin embargo, podemos desactivar uno de estos métodos, como se muestra en la fig. 13, más abajo.

Fig.13 Desactivando las señales de compra o venta.

Asimismo, a la izquierda de la etiqueta de texto de patrón, seleccionamos el tipo de figura técnica de Merrill para su posterior simulación. En el anterior artículo sobre este tema, ya describimos con detalle el aspecto que tienen.

Paso 7. Selección de señales para la simulación y colocación de Take Profit y Stop Loss

Vamos a echar un vistazo de nuevo a la fig.13, en la que se ve que para cada uno de los tipos de entrada en el mercado se pueden colocar hasta tres señales a la vez. Estas funcionan según el principio del O lógico. Es decir, si se colocan las tres señales de compra, al realizar la simulación se registrará la entrada en el mercado cuando aparezca cualquiera de las tres. Lo mismo sucede con las ventas. A la derecha de las etiquetas de texto Señal, en la lista desplegable se puede sТакже слева от текстовой метки паттернeleccionar el tipo de datos al que se aplicará la figura técnica elegida anteriormente.

Paso 8. Inicio de la simulación

Después de ejecutar los pasos 1-7, para iniciar la prueba será necesario seleccionar el instrumento simulado con el botón izquierdo en el recuadro de símbolos. El inicio del algoritmo de simulación se activa según el evento de usuario de pulsación sobre un punto de la lista o recuadro.

//--- Evento de pulsación sobre un punto de la lista o recuadro
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
   {
      //--- Selección del símbolo para el trabajo posterior
      //--- Comprobando el identificador del elemento
      if(lparam==m_table_symb.Id())
      {
         //--- Salir, si la línea no está seleccionada
         if(m_table_symb.SelectedItem()==WRONG_VALUE)
         {
            //--- Mostrando la descripción completa del símbolo en la línea 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);
         }
         //--- Obteniendo el símbolo seleccionado
         string symbol=m_table_symb.GetValue(0,m_table_symb.SelectedItem());
         //--- Mostrando la descripción completa del símbolo en la línea de estado
         m_status_bar.SetValue(0,"Selected symbol: "+::SymbolInfoString(symbol,SYMBOL_DESCRIPTION));
         m_status_bar.GetItemPointer(0).Update(true);
         GetResult(symbol);
      }
   }

El método GetResult() se encarga de la simulación. Vamos a analizarlo con mayor detalle.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::GetResult(const string symbol)
{
//--- Obteniendo el intervalo de fechas
   m_start_date=StringToTime(TimeToString(m_calendar1.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit1.GetHours()+":"+(string)m_time_edit1.GetMinutes()+":00");
   m_end_date=StringToTime(TimeToString(m_calendar2.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit2.GetHours()+":"+(string)m_time_edit2.GetMinutes()+":00");
//--- Comprobando si las fechas establecidas son correctas
   if(m_start_date>m_end_date || m_end_date>TimeCurrent())
   {
      if(m_lang_index==0)
         MessageBox("¡El intervalo de fechas seleccionado es incorrecto!","Error",MB_OK);
      else if(m_lang_index==1)
         MessageBox("Incorrect date range selected!","Error",MB_OK);
      return;
   }
//--- Comprobando la selección de los patrones
   int buy_pat=m_combobox1.GetListViewPointer().SelectedItemIndex();
   int sell_pat=m_combobox2.GetListViewPointer().SelectedItemIndex();
   if(buy_pat==sell_pat)
   {
      if(m_lang_index==0)
         MessageBox("¡El patrón de compra y el de venta no pueden ser iguales!","Error",MB_OK);
      else if(m_lang_index==1)
         MessageBox("The pattern for buying and selling cannot be the same!","Error",MB_OK);
      return;
   }
//---
   ZeroMemory(m_report);
   datetime cur_date=m_start_date;
   string tf=m_timeframe1.GetListViewPointer().SelectedItemText();
   int applied1=m_applied1.GetListViewPointer().SelectedItemIndex();
   int applied2=m_applied2.GetListViewPointer().SelectedItemIndex();
   int applied3=m_applied3.GetListViewPointer().SelectedItemIndex();
   int applied4=m_applied4.GetListViewPointer().SelectedItemIndex();
   int applied5=m_applied5.GetListViewPointer().SelectedItemIndex();
   int applied6=m_applied6.GetListViewPointer().SelectedItemIndex();
//---
   while(cur_date<m_end_date)
   {
      if(
         BuySignal(symbol,m_start_date,applied1,1) ||
         BuySignal(symbol,m_start_date,applied2,2) ||
         BuySignal(symbol,m_start_date,applied3,3))
      {
         CalculateBuyDeals(symbol,m_start_date);
         cur_date=m_start_date;
         continue;
      }
      if(
         SellSignal(symbol,m_start_date,applied4,1) ||
         SellSignal(symbol,m_start_date,applied5,2) ||
         SellSignal(symbol,m_start_date,applied6,3))
      {

         CalculateSellDeals(symbol,m_start_date);
         cur_date=m_start_date;
         continue;
      }
      m_start_date+=PeriodSeconds(StringToTimeframe(tf));
      cur_date=m_start_date;
   }
//--- Mostrando el informe
   PrintReport();
}

Este método incluye dos comprobaciones sobre la corrección del intervalo de fechas introducido, y también sobre el caso en el que el usuario establace por casualidad patrones iguales para simular tanto la señal de compra, como la de venta. El método GetResult() incluye tres métodos para trabajar con los datos establecidos en los ajustes.

1. Los métodos de búsqueda de señales BuySignal() y SellSignal(). Como son similares, solo vamos a ver una.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::BuySignal(const string symbol,datetime start,int applied,int signal)
{
//--- Salimos si la señal de compra está activada
   if(!m_checkbox[0].IsPressed())
      return(false);
//---
   int Handle=INVALID_HANDLE;
   string tf=m_timeframe1.GetListViewPointer().SelectedItemText();
//--- Preparando los datos
   if(m_checkbox[signal+1].IsPressed())
   {
      //--- Precio
      if(applied==0)
      {
         MqlRates rt[];
         int sl=0,tp=0;
         POINTS pat;
         double arr[];
         int copied=CopyRates(symbol,StringToTimeframe(tf),m_start_date,5,rt);
         int app_price=m_ind_set4.GetListViewPointer().SelectedItemIndex();
         ArrayResize(arr,copied);
         //Print(m_start_date+": "+copied);
         if(copied<5)
            return(false);
         //---
         for(int i=0; i<copied; i++)
         {
            if(app_price==0)
               arr[i]=rt[i].open;
            else if(app_price==1)
               arr[i]=rt[i].close;
            else if(app_price==2)
               arr[i]=rt[i].high;
            else if(app_price==3)
               arr[i]=rt[i].low;
         }
         //--- Buscando el patrón
         pat.A=arr[0];
         pat.B=arr[1];
         pat.C=arr[2];
         pat.D=arr[3];
         pat.E=arr[4];
         //--- Si el patrón no se encuentra, comprobamos la señal
         if(GetPatternType(pat)==m_combobox1.GetListViewPointer().SelectedItemIndex())
         {
            m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),5);
            return(true);
         }
         return(false);
      }
      //--- ATR
      if(applied==1)
         Handle=iATR(symbol,StringToTimeframe(tf),int(m_ind_setting[0].GetValue()));
      //--- CCI
      if(applied==2)
      {
         int app_price;
         switch(m_ind_set4.GetListViewPointer().SelectedItemIndex())
         {
         case  0:
            app_price=PRICE_OPEN;
            break;
         case  1:
            app_price=PRICE_CLOSE;
            break;
         case  2:
            app_price=PRICE_HIGH;
            break;
         case  3:
            app_price=PRICE_LOW;
            break;
         default:
            app_price=PRICE_CLOSE;
            break;
         }
         Handle=iCCI(symbol,StringToTimeframe(tf),int(m_ind_setting[1].GetValue()),app_price);
      }
      //--- DeMarker
      if(applied==3)
         Handle=iDeMarker(symbol,StringToTimeframe(tf),int(m_ind_setting[2].GetValue()));
      //--- Force Index
      if(applied==4)
      {
         int force_period=int(m_ind_setting[3].GetValue());
         ENUM_MA_METHOD force_ma_method;
         ENUM_APPLIED_VOLUME force_applied_volume;
         switch(m_ind_set1.GetListViewPointer().SelectedItemIndex())
         {
         case  0:
            force_ma_method=MODE_SMA;
            break;
         case  1:
            force_ma_method=MODE_EMA;
            break;
         case  2:
            force_ma_method=MODE_SMMA;
            break;
         case  3:
            force_ma_method=MODE_LWMA;
            break;
         default:
            force_ma_method=MODE_SMA;
            break;
         }
         switch(m_ind_set3.GetListViewPointer().SelectedItemIndex())
         {
         case  0:
            force_applied_volume=VOLUME_TICK;
            break;
         case  1:
            force_applied_volume=VOLUME_REAL;
            break;
         default:
            force_applied_volume=VOLUME_TICK;
            break;
         }
         Handle=iForce(symbol,StringToTimeframe(tf),force_period,force_ma_method,force_applied_volume);
      }
      //--- WPR
      if(applied==5)
         Handle=iWPR(symbol,StringToTimeframe(tf),int(m_ind_setting[5].GetValue()));
      //--- RSI
      if(applied==6)
         Handle=iRSI(symbol,StringToTimeframe(tf),int(m_ind_setting[4].GetValue()),PRICE_CLOSE);
      //--- Momentum
      if(applied==7)
         Handle=iMomentum(symbol,StringToTimeframe(tf),int(m_ind_setting[6].GetValue()),PRICE_CLOSE);
      //--- Custom
      if(applied==8)
      {
         string str[];
         double arr[];
         string parameters=m_custom_param.GetValue();
         StringSplit(parameters,',',str);
         if(ArraySize(str)>20)
         {
            if(m_lang_index==0)
               MessageBox("¡El número de parámetros no deberá ser superior a 20!","Error",MB_OK);
            else if(m_lang_index==1)
               MessageBox("The number of parameters should not be more than 20!","Error",MB_OK);
         }
         ArrayResize(arr,ArraySize(str));
         for(int i=0; i<ArraySize(str); i++)
            arr[i]=StringToDouble(str[i]);
         string name=m_custom_path.GetValue();
         Handle=GetCustomValue(StringToTimeframe(tf),name,arr);
      }
      //---
      if(applied>0)
      {
         if(Handle==INVALID_HANDLE)
         {
            if(m_lang_index==0)
               MessageBox("¡No se ha logrado encontrar el manejador del indicador!","Error",MB_OK);
            else if(m_lang_index==1)
               MessageBox("Failed to get handle indicator!","Error",MB_OK);
         }
         double arr[];
         int buffer=(applied==8)?int(m_custom_buffer.GetValue()):0;
         int copied=CopyBuffer(Handle,buffer,m_start_date,5,arr);
         //---
         int sl=0,tp=0;
         POINTS pat;
         if(copied<5)
            return(false);
         //--- Condición de búsqueda del patrón
         pat.A=arr[0];
         pat.B=arr[1];
         pat.C=arr[2];
         pat.D=arr[3];
         pat.E=arr[4];
         //--- Si el patrón no se encuentra, comprobamos la señal
         if(GetPatternType(pat)==m_combobox1.GetListViewPointer().SelectedItemIndex())
         {
            m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),5);
            return(true);
         }
         return(false);
      }
      return(false);
   }
   return(false);
}

La esencia de este método reside en la secuencia de acciones establecida:

  • Se comprueba si está permitida cualquier señal de compra, así como la establecida en concreto.
  • Se comprueba a qué matriz de datos se aplicarán las figuras técnicas.
  • Se preparan los datos para la búsqueda y se busca el patrón establecido con la ayuda del método GetPatternType().

2. Métodos de procesamiento de la señal encontrada CalculateBuyDeals()CalculateSellDeals().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::CalculateBuyDeals(const string symbol,datetime start)
{
   MqlRates rt[];
   int TP=int(m_takeprofit1.GetValue());
   int SL=int(m_stoploss1.GetValue());
   string tf=m_timeframe1.GetListViewPointer().SelectedItemText();
   int copied=CopyRates(symbol,StringToTimeframe(tf),m_start_date,m_end_date,rt);
   double deal_price=iOpen(symbol,StringToTimeframe(tf),copied);
   for(int j=0; j<copied; j++)
   {
      if((iHigh(symbol,StringToTimeframe(tf),copied-j)-deal_price)/SymbolInfoDouble(symbol,SYMBOL_POINT)>=TP)
      {
         m_report.profit_trades++;
         m_report.profit+=TP;
         m_report.long_trades++;
         m_report.total_trades++;
         m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j);
         return;
      }
      else if((deal_price-iLow(symbol,StringToTimeframe(tf),copied-j))/SymbolInfoDouble(symbol,SYMBOL_POINT)>=SL)
      {
         m_report.loss_trades++;
         m_report.profit-=SL;
         m_report.long_trades++;
         m_report.total_trades++;
         m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j);
         return;
      }
   }
   m_start_date=m_end_date;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::CalculateSellDeals(const string symbol,datetime start)
{
   MqlRates rt[];
   int TP=int(m_takeprofit2.GetValue());
   int SL=int(m_stoploss2.GetValue());
   string tf=m_timeframe1.GetListViewPointer().SelectedItemText();
   int copied=CopyRates(symbol,StringToTimeframe(tf),m_start_date,m_end_date,rt);
   double deal_price=iOpen(symbol,StringToTimeframe(tf),copied);
   for(int j=0; j<copied; j++)
   {
      if((deal_price-iLow(symbol,StringToTimeframe(tf),copied-j))/SymbolInfoDouble(symbol,SYMBOL_POINT)>=TP)
      {
         m_report.profit_trades++;
         m_report.profit+=TP;
         m_report.short_trades++;
         m_report.total_trades++;
         m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j);
         return;
      }
      else if((iHigh(symbol,StringToTimeframe(tf),copied-j)-deal_price)/SymbolInfoDouble(symbol,SYMBOL_POINT)>=SL)
      {
         m_report.loss_trades++;
         m_report.profit-=SL;
         m_report.short_trades++;
         m_report.total_trades++;
         m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j);
         return;
      }
   }
   m_start_date=m_end_date;
}

Su objetivo es procesar la señal encontrada y registrar las estadísticas para su posterior muestra en el Informe.

3. Método de muestra de los resultados de la simulación PrintReport().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::PrintReport(void)
{
   if(m_lang_index==0)
   {
      string report_label[6]=
      {
         "Transacciones totales: ","Transacciones cortas: ","Transacciones rentables: ",
         "Beneficio en puntos: ","Transacciones largas: ","Transacciones con pérdidas: "
      };
      //---
      m_report_text[0].LabelText(report_label[0]+string(m_report.total_trades));
      m_report_text[1].LabelText(report_label[1]+string(m_report.short_trades));
      m_report_text[2].LabelText(report_label[2]+string(m_report.profit_trades));
      m_report_text[3].LabelText(report_label[3]+string(m_report.profit));
      m_report_text[4].LabelText(report_label[4]+string(m_report.long_trades));
      m_report_text[5].LabelText(report_label[5]+string(m_report.loss_trades));
   }
   else
   {
      string report_label[6]=
      {
         "Total trades: ","Short Trades: ","Profit Trades: ",
         "Profit in points: ","Long Trades: ","Loss Trades: "
      };
      //---
      m_report_text[0].LabelText(report_label[0]+string(m_report.total_trades));
      m_report_text[1].LabelText(report_label[1]+string(m_report.short_trades));
      m_report_text[2].LabelText(report_label[2]+string(m_report.profit_trades));
      m_report_text[3].LabelText(report_label[3]+string(m_report.profit));
      m_report_text[4].LabelText(report_label[4]+string(m_report.long_trades));
      m_report_text[5].LabelText(report_label[5]+string(m_report.loss_trades));
   }
   Update(true);
}

Muestra en la aplicación los datos sobre la simulación. Con esto, finaliza el algoritmo de funcionamiento de la aplicación.

Demostración y ejemplo de trabajo con el constructor

Como ejemplo, hemos decidido grabar un pequeño vídeo sobre el trabajo con el constructor de estrategias.


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. Para encontrar la carpeta raíz del terminal en la que se encuentra la carpeta MQL5, debemos pulsar en MetaTarder5 la combinación de teclas   Ctrl+Shift+D o usar el menú contextual como se muestra en la fig.9, más abajo.


Fig.14 Búsqueda de la carpeta MQL5 en la carpeta raíz de MetaTrader5


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

Archivos adjuntos |
MQL5.zip (506.48 KB)
Creando un EA gradador multiplataforma (última parte): la diversificación como método para aumentar la rentabilidad Creando un EA gradador multiplataforma (última parte): la diversificación como método para aumentar la rentabilidad

En los anteriores artículos de la serie, hemos intentado crear de formas distintas un asesor gradador más o menos rentable. En esta ocasión, vamos a tratar de aumentar la rentabilidad del asesor comercial con la ayuda de la diversificación. Nuestro objetivo es el 100% de beneficio anual anhelado por todos, con un 20% de reducción máxima del balance.

Recetas MQL5 – Prueba de estrés de una estrategia comercial con ayuda de símbolos personalizados Recetas MQL5 – Prueba de estrés de una estrategia comercial con ayuda de símbolos personalizados

En el artículo se analiza un enfoque sobre la prueba de estrés de estrategias comerciales con ayuda de símbolos personalizados Para este objetivo se crea una clase de símbolo de usuario. Con su ayuda, se obtendrán los datos de ticks desde fuentes de terceros y se cambiarán las propiedades del símbolo. Según los resultados del trabajo realizado, se ofrecerán variantes de cambio de las condiciones comerciales con respecto a las cuales se simula la estrategia comercial.

Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XVI): Eventos de la colección de símbolos. Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XVI): Eventos de la colección de símbolos.

En este artículo, vamos a crear la clase básica para todos los objetos de la biblioteca, encargada de añadir la funcionalidad de los eventos a todos sus herederos; asimismo, crearemos una clase para monitorear los eventos de la colección de símbolos basada en la nueva clase básica. Además, modificaremos las clases de la cuenta y los eventos de la cuenta para trabajar con la nueva funcionalidad del objeto básico.

Desarrollando el Oscilador de Promedio de Pivote (PMO): un nuevo indicador para la Media Móvil Acumulativa Desarrollando el Oscilador de Promedio de Pivote (PMO): un nuevo indicador para la Media Móvil Acumulativa

En este artículo, presentamos el Pivot Mean Oscillator (PMO) o Oscilador de Promedio de Pivote, una implementación de la Media Móvil Acumulativa (CMA) como indicador comercial para las plataformas MetaTrader. En particular, primero presentaremos el Pivot Mean (PM) o Promedio de Pivote, como un índice de normalización para las series temporales que calcula la fracción entre cualquier punto de datos y la CMA. Entonces, construimos el PMO como la diferencia entre las medias móviles aplicadas a las dos señales de PM. También hemos realizado algunos experimentos preliminares con el símbolo EURUSD para probar la eficacia del indicador presentado, dejando un amplio espacio para otras consideraciones y mejoras.