English Русский 中文 Deutsch 日本語 Português
Ampliamos la funcionalidad del Constructor de estrategias

Ampliamos la funcionalidad del Constructor de estrategias

MetaTrader 5Trading | 23 enero 2020, 13:42
815 0
Alexander Fedosov
Alexander Fedosov

Contenido

Introducción

En el primer artículo de esta serie, conocimos las figuras técnicas de Merrill y las aplicamos a diferentes conjuntos de datos, como el precio, así como los indicadores del tipo oscilador derivados de él, como ATR, CCI, WPR y los demás. Nuestro objetivo fue crear una herramienta para buscar y estimar la perspectiva del uso de los patrones en los mercados de divisas y los demás. En el segundo artículo, desarrollamos un Constructor de estrategias capaz de modelar unas estrategias simples usando las figuras ya consideradas, y obtener los resultados. En el presente artículo, ampliaremos considerablemente la funcionalidad de creación y simulación de estrategias. Añadiremos la posibilidad de trabajar no sólo con los puntos, sino también con lotes, así como, la posibilidad de visualizar los resultados de la simulación.

Análisis de las novedades y adiciones

Antes de ver las adiciones, me gustaría recordar que en la versión anterior, los resultados de todas las pruebas fueron visualizados en un informe sumario, mientras que el propio resultado fue representado en forma del beneficio o la pérdida en puntos del instrumento investigado. No obstante, eso no nos permitía evaluar el potencial de una estrategia integralmente. Por tanto, el objetivo principal de las adiciones será el aumento de las posibilidades del Simulador de estrategias, y también la extensión de parámetros del informe comercial que se deriva de ello.

Con relación a estos objetivos, el plan de las mejoras será el siguiente:

  • Depósito inicial en la moneda de la cuenta.
  • Posibilidad de escoger el Beneficio: «En puntos» o «En la divisa del depósito».
  • Al seleccionar «En la divisa del depósito», se abren dos campos adicionales: «Tipo del lote» y «Tamaño del lote».
  • A su vez, el «Tipo del lote» puede ser «Constante» o «Del balance».
  • Tamaño del lote. Esta opción está disponible si el «Tipo del lote» es «Constante».

La lista del informe comercial también incluirá las siguientes modificaciones:

  • Beneficio neto. Este parámetro está disponible sólo si el tipo del beneficio es «En la divisa del depósito».
  • Reducción absoluta del balance. Este parámetro está disponible sólo si el tipo del beneficio es «En la divisa del depósito».
  • Reducción máxima del balance en por cientos. Este parámetro está disponible sólo si el tipo del beneficio es «En la divisa del depósito».
  • Ahora el número de las transacciones cortas y largas incluye el parámetro del porcentaje de las ganadoras de uno u otro de los tipos.
  • Factor de rentabilidad de la estrategia. Es la relación entre la ganancia bruta y la pérdida bruta.
  • Factor de recuperación. Este parámetro está disponible sólo si el tipo del beneficio es «En la divisa del depósito».

Para más detalles sobre los parámetros introducidos, consulte el apartado Informe sobre la simulación de la Guía de ayuda de MetaTrader 5. En la fig. 1, se muestra un prototipo de las posibilidades descritas más arriba.

Fig. 1 Prototipo de nuevas herramientas de simulación.

Otra novedad consiste en poder ver visualmente el resultado de la simulación de cualquier estrategia. Se trata del gráfico de los resultados de la simulación. En nuestra aplicación, podemos abrir este gráfico al pulsar el botón «Abrir gráfico» (fíjese en la fig. 1) en el bloque Informe.

Fig. 2. La apariencia del gráfico.

Como podemos observar en la fig. 2, el gráfico permite evaluar visualmente el carácter del movimiento del depósito y los resultados de transacciones. Por motivos de conveniencia, el encabezado incluye el instrumento testeado, su marco temporal y el intervalo de tiempo de la simulación.

Etapas de la implementación de novedades

Igual como en caso de la implementación del Constructor de estrategias desde cero, vamos a definir los principales elementos y los métodos que los implementan. Así, fueron añadidos dos métodos nuevos al método principal de la creación de la interfaz CreateGUI(). Además, analizaremos las adiciones en los métodos ya existentes.

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

Vamos a considerar las modificaciones respecto al método de la creación de la ventana principal CreateWindow(), a saber, la adición de los elementos de la interfaz que implementan nuevas herramientas de la simulación mostradas más arriba, en la fig. 1.

//---- Pestaña CONSTRUCTOR
....
//---
   if(!CreateProfitType(int(0.35*(m_window[0].XSize()-150)-120),50+35*6+ygap))
      return(false);
   if(!CreateLotType(int(0.6*(m_window[0].XSize()-150)-120),50+35*6+ygap))
      return(false);
   if(!CreateBaseLotValue(int(0.85*(m_window[0].XSize()-150)-120),50+35*6+ygap))
      return(false);
   if(!CreateInitialDeposit(int(0.35*(m_window[0].XSize()-150)-120),50+35*7+ygap))
      return(false);
//---
   if(!CreateReportFrame(m_frame[2],int(0.35*(m_window[0].XSize()-150)-120),110+35*7+ygap))
      return(false);
//--- Botón para abrir el Gráfico
   if(!CreateIconButton(int(0.35*(m_window[0].XSize()-150)-50),100+35*7+ygap))
      return(false);
//--- Líneas del informe
   for(int i=0; i<11; i++)
   {
      if(i<5)
         if(!CreateTextLabel(m_report_text[i],int(0.37*(m_window[0].XSize()-150)-120),380+25*i+ygap,"",0))
            return(false);
      if(i>=5 && i<9)
         if(!CreateTextLabel(m_report_text[i],int(0.63*(m_window[0].XSize()-150)-120),380+25*(i-5)+ygap,"",0))
            return(false);
      if(i>=9)
         if(!CreateTextLabel(m_report_text[i],int(0.89*(m_window[0].XSize()-150)-120),380+25*(i-9)+ygap,"",0))
            return(false);
      m_report_text[i].IsCenterText(false);
      m_report_text[i].FontSize(10);
   }
....

Los cambios visuales se refieren sólo a la pestaña Constructor. Para ver la implementación completa de esta pestaña, por favor, consulte los códigos fuente, o bien diríjase al artículo anterior. Aquí, han sido elegidos los métodos que representan la adición. Vamos a examinar cada uno de ellos. 

Método CreateProfitType(). Crea una lista desplegable con el tipo del beneficio durante la simulación: Divisa del depósito o Puntos.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateProfitType(const int x_gap,const int y_gap)
{
//--- Transferir el objeto del panel
   m_profit_type.MainPointer(m_tabs1);
//--- Fijar en la pestaña
   m_tabs1.AddToElementsArray(0,m_profit_type);
//--- Array de valores de los puntos en la lista
   string pattern_names[2]=
   {
      "Pips","Currency"
   };
//--- Establecemos las propiedades antes de la creación
   m_profit_type.XSize(200);
   m_profit_type.YSize(25);
   m_profit_type.ItemsTotal(2);
   m_profit_type.FontSize(12);
   m_profit_type.LabelColor(C'0,100,255');
   m_profit_type.GetButtonPointer().FontSize(10);
   m_profit_type.GetButtonPointer().XGap(80);
   m_profit_type.GetButtonPointer().XSize(100);
   m_profit_type.GetButtonPointer().BackColor(clrAliceBlue);
   m_profit_type.GetListViewPointer().FontSize(10);
   m_profit_type.GetListViewPointer().YSize(44);
//--- Guardamos los valores de los puntos en la lista del cuadro combinado (combobox)
   for(int i=0; i<2; i++)
      m_profit_type.SetValue(i,pattern_names[i]);
//--- Obtenemos el puntero de la lista
   CListView *lv=m_profit_type.GetListViewPointer();
//--- Establecemos las propiedades de la lista
   lv.LightsHover(true);
   m_profit_type.SelectItem(1);
//--- Creamos el control
   if(!m_profit_type.CreateComboBox("Profit Type",x_gap,y_gap))
      return(false);
//--- Añadimos el objeto al array general de los grupos de objetos
   CWndContainer::AddToElementsArray(0,m_profit_type);
   return(true);
}

Método CreateLotType(). Crea una lista desplegable con el tipo del lote: Constante y Del balance.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateLotType(const int x_gap,const int y_gap)
{
//--- Transferir el objeto del panel
   m_lot_type.MainPointer(m_tabs1);
//--- Fijar en la pestaña
   m_tabs1.AddToElementsArray(0,m_lot_type);
//--- Array de valores de los puntos en la lista
   string pattern_names[2]=
   {
      "Balance","Constant"
   };
//--- Establecemos las propiedades antes de la creación
   m_lot_type.XSize(200);
   m_lot_type.YSize(25);
   m_lot_type.ItemsTotal(2);
   m_lot_type.FontSize(12);
   m_lot_type.LabelColor(C'0,100,255');
   m_lot_type.GetButtonPointer().FontSize(10);
   m_lot_type.GetButtonPointer().XGap(65);
   m_lot_type.GetButtonPointer().XSize(100);
   m_lot_type.GetButtonPointer().BackColor(clrAliceBlue);
   m_lot_type.GetListViewPointer().FontSize(10);
   m_lot_type.GetListViewPointer().YSize(44);
//--- Guardamos los valores de los puntos en la lista del cuadro combinado (combobox)
   for(int i=0; i<2; i++)
      m_lot_type.SetValue(i,pattern_names[i]);
//--- Obtenemos el puntero de la lista
   CListView *lv=m_lot_type.GetListViewPointer();
//--- Establecemos las propiedades de la lista
   lv.LightsHover(true);
   m_lot_type.SelectItem(1);
//--- Creamos el control
   if(!m_lot_type.CreateComboBox("Lot Type",x_gap,y_gap))
      return(false);
//--- Añadimos el objeto al array general de los grupos de objetos
   CWndContainer::AddToElementsArray(0,m_lot_type);
   return(true);
}

Метод CreateBaseLotValue(). Crea el campo de edición del valor del lote.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateBaseLotValue(const int x_gap,const int y_gap)
{
//--- Guardamos el puntero al control principal
   m_base_lot.MainPointer(m_tabs1);
//--- Fijar en la pestaña
   m_tabs1.AddToElementsArray(0,m_base_lot);
//--- Propiedades
   m_base_lot.XSize(210);
   m_base_lot.YSize(24);
   m_base_lot.LabelColor(C'0,100,255');
   m_base_lot.FontSize(12);
   m_base_lot.MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX));
   m_base_lot.MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN));
   m_base_lot.StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP));
   m_base_lot.SetDigits(2);
   m_base_lot.SpinEditMode(true);
   m_base_lot.GetTextBoxPointer().AutoSelectionMode(true);
   m_base_lot.GetTextBoxPointer().XGap(100);
//--- Creamos el control
   if(!m_base_lot.CreateTextEdit("Base Lot Size",x_gap,y_gap))
      return(false);
   m_base_lot.SetValue((string)SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN));
//--- Añadimos el objeto al array general de los grupos de objetos
   CWndContainer::AddToElementsArray(0,m_base_lot);
   return(true);
}

Метод CreateInitialDeposit(). Crea el campo de edición del depósito inicial durante la simulación en el modo Beneficio — Divisa.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateInitialDeposit(const int x_gap,const int y_gap)
{
//--- Guardamos el puntero al control principal
   m_init_deposit.MainPointer(m_tabs1);
//--- Fijar en la pestaña
   m_tabs1.AddToElementsArray(0,m_init_deposit);
//--- Propiedades
   m_init_deposit.XSize(210);
   m_init_deposit.YSize(24);
   m_init_deposit.LabelColor(C'0,100,255');
   m_init_deposit.FontSize(12);
   m_init_deposit.MinValue(10);
   m_init_deposit.SetDigits(2);
   m_init_deposit.GetTextBoxPointer().AutoSelectionMode(true);
   m_init_deposit.GetTextBoxPointer().XGap(125);
//--- Creamos el control
   if(!m_init_deposit.CreateTextEdit("Initial Deposit",x_gap,y_gap))
      return(false);
   m_init_deposit.SetValue((string)1000);
//--- Añadimos el objeto al array general de los grupos de objetos
   CWndContainer::AddToElementsArray(0,m_init_deposit);
   return(true);
}

Метод CreateIconButton(). Crea el botón al pulsar el cual se abre la ventana del gráfico.

//+------------------------------------------------------------------+
//| Crea un botón con imagen                                       |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart_gray.bmp"
//---
bool CProgram::CreateIconButton(const int x_gap,const int y_gap)
{
//--- Guardamos el puntero al control principal
   m_graph_button.MainPointer(m_tabs1);
//--- Fijar en la pestaña
   m_tabs1.AddToElementsArray(0,m_graph_button);
//--- Propiedades
   m_graph_button.XSize(150);
   m_graph_button.YSize(22);
   m_graph_button.FontSize(11);
   m_graph_button.IconXGap(3);
   m_graph_button.IconYGap(3);
   m_graph_button.IsHighlighted(false);
   m_graph_button.IsCenterText(true);
   m_graph_button.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart.bmp");
   m_graph_button.IconFileLocked("Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart_gray.bmp");
   m_graph_button.IconFilePressed("Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart.bmp");
   m_graph_button.IconFilePressedLocked("Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart_gray.bmp");
   m_graph_button.BorderColor(C'0,100,255');
   m_graph_button.BackColor(clrAliceBlue);
//--- Creamos el control
   if(!m_graph_button.CreateButton("",x_gap,y_gap))
      return(false);
//--- Añadimos el puntero al control a la base
   CWndContainer::AddToElementsArray(0,m_graph_button);
   return(true);
}

Además, en el método CreateWindow(), fue alterada la estructura de la visualización de las características del propio Informe. Fue completada e implementada como tres columnas, en vez de dos como antes. Pues, éstas son todas las adiciones en el método de la construcción de la ventana principal CreateWindow().

A continuación, pasamos al método que implementa la ventana del gráfico y el propio gráfico, es decir, CreateGraphWindow().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateGraphWindow(void)
{
//--- Añadimos el puntero de la ventana al array de ventanas
   CWndContainer::AddWindow(m_window[2]);
//--- Propiedades
   m_window[2].XSize(750);
   m_window[2].YSize(450);
   m_window[2].FontSize(9);
   m_window[2].WindowType(W_DIALOG);
   m_window[2].IsMovable(true);
//--- Creando el formulario
   if(!m_window[2].CreateWindow(m_chart_id,m_subwin,"",75,75))
      return(false);
   //--- Gráficos
   if(!CreateGraph(22,22))
      return(false);
//---
   return(true);
}

El método de la creación de la ventana en sí no es demasiado grande, pero merece la pena detenernos en el método CreateGraph() incluido dentro de él.

//+------------------------------------------------------------------+
//| Crea el gráfico                                                   |
//+------------------------------------------------------------------+
bool CProgram::CreateGraph(const int x_gap,const int y_gap)
{
//--- Guardamos el puntero al control principal
   m_graph1.MainPointer(m_window[2]);
//--- Propiedades
   m_graph1.AutoXResizeMode(true);
   m_graph1.AutoYResizeMode(true);
   m_graph1.AutoXResizeRightOffset(10);
   m_graph1.AutoYResizeBottomOffset(10);
//--- Creando el control
   if(!m_graph1.CreateGraph(x_gap,y_gap))
      return(false);
//--- Propiedades del gráfico
   CGraphic *graph=m_graph1.GetGraphicPointer();
   graph.BackgroundColor(::ColorToARGB(clrWhiteSmoke));
   graph.XAxis().Min(0);
   graph.BackgroundMainSize(20);
   graph.HistoryNameSize(0);
   graph.HistorySymbolSize(0);
   graph.HistoryNameWidth(0);
//--- Añadimos el puntero al control a la base
   CWndContainer::AddToElementsArray(2,m_graph1);
   return(true);
}

Por ahora, crea un gráfico en blanco, ajustando simplemente sus características visuales. Vamos a llenar el gráfico un poco más tarde.

El último método que forma parte del método principal CreateGUI(), es CreateLoading() que sirve para crear la ventana de descarga. Fue introducido como una referencia informativa durante la carga de la aplicación, cambio de los ajustes del idioma o el testeo y trabajo con datos.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp"
bool CProgram::CreateLoading(void)
{
//--- Añadimos el puntero de la ventana al array de ventanas
   CWndContainer::AddWindow(m_window[3]);
//--- Propiedades
   m_window[3].XSize(100);
   m_window[3].YSize(50);
   m_window[3].LabelYGap(50/2-16/2);
   m_window[3].IconYGap(50/2-16/2);
   m_window[3].FontSize(9);
   m_window[3].WindowType(W_DIALOG);
   m_window[3].IsMovable(false);
   m_window[3].CloseButtonIsUsed(false);
   m_window[3].CaptionColorLocked(C'0,130,225');
   m_window[3].LabelColor(clrWhite);
   m_window[3].LabelColorLocked(clrWhite);
   m_window[3].CaptionHeight(51);
   m_window[3].IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp");
   m_window[3].IconFileLocked("Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp");
   m_window[3].IconFilePressed("Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp");
   m_window[3].IconFilePressedLocked("Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp");
   int x=int(m_window[0].XSize()/2);
   int y=int(m_window[0].YSize()/2);
//--- Creando el formulario
   if(!m_window[3].CreateWindow(m_chart_id,m_subwin,"Working...",x,y))
      return(false);
   return(true);
}

Hemos terminado el trabajo con los elementos visuales del Constructor de estrategias. Ahora hablaremos del propio algoritmo de simulación y participación de nuevos controles y visualización de la información.

Como podemos recordar del artículo anterior, el método GetResult() se encarga de iniciar y procesar la simulación de la estrategia establecida. No obstante, habrá que completarlo debido a la aparición de nuevos datos de entrada.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::GetResult(const string symbol)
{
//--- Obtener 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");
//--- Verificando si las fechas establecidas son correctas
   if(m_start_date>m_end_date || m_end_date>TimeCurrent())
   {
      if(m_lang_index==0)
         MessageBox("Intervalo de fechas inválido","Error",MB_OK);
      else if(m_lang_index==1)
         MessageBox("Incorrect date range selected!","Error",MB_OK);
      return;
   }
//--- Verificando la corrección de patrones definidos
   if(m_combobox1.GetListViewPointer().SelectedItemIndex()==m_combobox2.GetListViewPointer().SelectedItemIndex())
   {
      if(m_lang_index==0)
         MessageBox("¡Los patrones no pueden ser iguales!","Error",MB_OK);
      else if(m_lang_index==1)
         MessageBox("Patterns cannot be the same!","Error",MB_OK);
      return;
   }
//---
   m_window[3].OpenWindow();
//---
   m_counter=0;
   m_all_losses=0;
   m_all_profit=0;
   AddDeal(0,m_counter);
   ZeroMemory(m_report);
   MqlRates rt[];
   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(
         applied1>7 || applied2>7 || applied3>7 ||
         applied4>7 || applied5>7 || applied6>7)
      {
         if(m_custom_path.GetValue()=="")
         {
            if(m_lang_index==0)
               MessageBox("¡La ruta hacia el indicador no está definida!","Error",MB_OK);
            else if(m_lang_index==1)
               MessageBox("The indicator path is not set!","Error",MB_OK);
            break;
         }
         if(m_custom_param.GetValue()=="")
         {
            if(m_lang_index==0)
               MessageBox("¡Los parámetros del indicador no están definidos!","Error",MB_OK);
            else if(m_lang_index==1)
               MessageBox("Indicator parameters not set!","Error",MB_OK);
            break;
         }
      }
      //---
      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;
   }
//--- Visualización del informe
   PrintReport();
//---
   m_window[3].CloseDialogBox();
}

Hablando de la implementación modificada, nos fijaremos en los siguientes momentos:

  • Ha sido añadida la verificación que prohíbe que los patrones seleccionados para la señal de compra y de venta sean iguales.
  • Ha sido introducido el uso de la Ventana de descarga creada anteriormente.
  • Ha sido introducido el método AddDeal(), que se encargará de escribir los resultados de la simulación en el array de datos usados por el gráfico. En este listado, él define un valor inicial para el gráfico. En el modo del tipo del Beneficio «Divisa» es el depósito inicial, «En puntos» es cero.

Ahora analizaremos el propio método para añadir los datos AddDeal():

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::AddDeal(int points,int index)
{
//--- En puntos
   if(m_profit_type.GetListViewPointer().SelectedItemIndex()==0)
   {
      if(index==0)
      {
         ArrayResize(data,index+1);
         data[index]=0;
         return;
      }
      ArrayResize(data,index+1);
      data[index]=data[index-1]+points;
   }
//--- En moneda del depósito
   else if(m_profit_type.GetListViewPointer().SelectedItemIndex()==1)
   {
      if(index==0)
      {
         ArrayResize(data,index+1);
         data[index]=StringToDouble(m_init_deposit.GetValue());
         return;
      }
      ArrayResize(data,index+1);
      //--- Obtenemos el símbolo seleccionado
      string symbol=m_table_symb.GetValue(0,m_table_symb.SelectedItem());
      string basesymbol=AccountInfoString(ACCOUNT_CURRENCY);
      string tf=m_timeframe1.GetListViewPointer().SelectedItemText();
      double lot=StringToDouble(m_base_lot.GetValue());
      if(m_lot_type.GetListViewPointer().SelectedItemIndex()>0)
      {
         lot*=data[index-1];
         lot=GetLotForOpeningPos(symbol,POSITION_TYPE_BUY,lot);
      }

      double pip_price=1;
      int shift=0;
      //--- Par directo
      if(StringSubstr(symbol,3,3)==basesymbol)
      {
         pip_price=NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(symbol,SYMBOL_POINT)*lot,2);
      }
      //--- Par inverso
      else if(StringSubstr(symbol,0,3)==basesymbol)
      {
         shift=iBarShift(symbol,StringToTimeframe(tf),m_start_date,false);
         pip_price=NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(symbol,SYMBOL_POINT)*lot/iOpen(symbol,StringToTimeframe(tf),shift),2);
      }
      else
      {
         //--- Cruzamiento
         StringConcatenate(symbol,StringSubstr(symbol,3,3),basesymbol);
         if(SymbolInfoDouble(symbol,SYMBOL_BID)!=0)
         {
            shift=iBarShift(symbol,StringToTimeframe(tf),m_start_date,false);
            pip_price=NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(symbol,SYMBOL_POINT)*lot/iOpen(symbol,StringToTimeframe(tf),shift),2);
         }
         //---
         StringConcatenate(symbol,basesymbol,StringSubstr(symbol,0,3));
         if(SymbolInfoDouble(symbol,SYMBOL_BID)!=0)
         {
            shift=iBarShift(symbol,StringToTimeframe(tf),m_start_date,false);
            pip_price=NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(symbol,SYMBOL_POINT)*lot*iOpen(symbol,StringToTimeframe(tf),shift),2);
         }
      }
      //---
      if(points>0)
         m_all_profit+=pip_price*points;
      else
         m_all_losses+=pip_price*-points;
      //---
      data[index]=data[index-1]+pip_price*points;
   }
}

En los argumentos, tiene dos valores:

  1. points — un nuevo valor para el gráfico. Aquí, el valor recibido es el resultado del cierre de la transacción en puntos. Es el valor del Take Profit o de Stop Loss.
  2. index — en realidad, es el número de orden de la transacción.

Luego, dependiendo del modo de visualización del Beneficio seleccionado, se realiza el cálculo y la introducción de datos en el array data. Para el modo del Beneficio «Puntos», cada nuevo elemento del array es la suma del elemento anterior y del resultado de la transacción en puntos. En caso del segundo modo, «Divisa», el resultado de la transacción obtenido en puntos se convierte en la moneda del depósito. Además, se toma en cuenta el tipo del par de divisas testeado: par directo, par inverso y cruzamiento.

Los dos siguientes métodos modificados son CalculateBuyDeals()CalculateSellDeals(). Se encargan de procesar la señal encontrada o abren una posición virtualmente. Vamos a ver las modificaciones de uno de los métodos, ya que los cambios del segundo son los mismos:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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++)
   {
      //--- Disparo del Take Profit
      if((iHigh(symbol,StringToTimeframe(tf),copied-j)-deal_price)/SymbolInfoDouble(symbol,SYMBOL_POINT)>=TP)
      {
         m_counter++;
         AddDeal(TP,m_counter);
         m_report.profit_trades++;
         m_report.profit+=TP;
         m_report.profit_pips+=TP;
         m_report.long_trades++;
         m_report.profit_long++;
         m_report.total_trades++;
         m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j);
         return;
      }
      //--- Disparo de Stop Loss
      else if((deal_price-iLow(symbol,StringToTimeframe(tf),copied-j))/SymbolInfoDouble(symbol,SYMBOL_POINT)>=SL)
      {
         m_counter++;
         AddDeal(-SL,m_counter);
         m_report.loss_trades++;
         m_report.profit-=SL;
         m_report.loss_pips+=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;
}

Los cambios han afectado los lugares del disparo de Take Profit y Stop Loss. Aquí, el método AddDeal() descrito anteriormente procesa el hecho del cierre de la transacción. Además, la estructura REPORT m_report ha sido extendida con los parámetros profit_pips y loss_pips. Son necesarios para calcular nuevas características en el Informe.

El último método que ha sido modificado considerablemente es el procesamiento de datos obtenidos y su visualización en el Informe. Igual como en el artículo anterior, el método PrintReport() se encarga de ello:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::PrintReport(void)
{
   string report_label[11];
   if(m_lang_index==0)
   {
      report_label[0]="Total trades: ";
      report_label[1]="Beneficio neto: ";
      report_label[2]="Beneficio en puntos: ";
      report_label[3]="Reducción absoluta del balance: ";
      report_label[4]="Reducción máxima del balance: ";
      report_label[5]="Trades corrtos/% rentables: ";
      report_label[6]="Trades rentables/% de todos: ";
      report_label[7]="Factor de rentabilidad: ";
      report_label[8]="Factor de recuperación: ";
      report_label[9]="Trades largos/% rentables: ";
      report_label[10]=" Trades no rentables/% de todos: ";
   }
   else
   {
      report_label[0]="Total trades: ";
      report_label[1]="Total profit: ";
      report_label[2]="Total profit(pips): ";
      report_label[3]="Balance Drawdown Abs: ";
      report_label[4]="Balance Drawdown Max: ";
      report_label[5]="Short trades/won %: ";
      report_label[6]="Profit trades/% of all: ";
      report_label[7]="Profit Factor: ";
      report_label[8]="Recovery Factor: ";
      report_label[9]="Long trades/won %: ";
      report_label[10]="Loss trades/% of all: ";
   }
   //---
   m_report_text[0].LabelText(report_label[0]+string(m_report.total_trades));
   //---
   if(m_report.total_trades==0)
      return;
   //---
   if(m_profit_type.GetListViewPointer().SelectedItemIndex()==1)
   {
      double maxprofit=0.0,maxdd=0.0;
      for(int i=1; i<ArraySize(data); i++)
      {
         if(data[i]>maxprofit)
            maxprofit=data[i];
         if(maxdd<maxprofit-data[i])
            maxdd=maxprofit-data[i];
      }
      m_report_text[1].LabelText(report_label[1]+DoubleToString(data[ArraySize(data)-1],2));
      m_report_text[3].LabelText(report_label[3]+string(data[0]-data[ArrayMinimum(data)]));
      m_report_text[4].LabelText(report_label[4]+DoubleToString(maxdd/maxprofit*100,2)+"%");
      m_report_text[7].LabelText(report_label[7]+DoubleToString(m_all_profit/m_all_losses,2));
      m_report_text[8].LabelText(report_label[8]+DoubleToString((data[ArraySize(data)-1]-data[0])/maxdd,2));
   }
   else
   {
      m_report_text[1].LabelText(report_label[1]+"-");
      m_report_text[3].LabelText(report_label[3]+"-");
      m_report_text[4].LabelText(report_label[4]+"-");
      m_report_text[7].LabelText(report_label[7]+DoubleToString(m_report.profit_pips/(double)m_report.loss_pips,2));
   }
   m_report_text[2].LabelText(report_label[2]+string(m_report.profit));
   m_report_text[5].LabelText(report_label[5]+string(m_report.short_trades)+"/"+DoubleToString(m_report.profit_short/(double)m_report.short_trades*100,1)+"%");
   m_report_text[6].LabelText(report_label[6]+string(m_report.profit_trades)+"/"+DoubleToString(m_report.profit_trades/(double)m_report.total_trades*100,1)+"%");
   m_report_text[9].LabelText(report_label[9]+string(m_report.long_trades)+"/"+DoubleToString(m_report.profit_long/(double)m_report.long_trades*100,1)+"%");
   m_report_text[10].LabelText(report_label[10]+string(m_report.loss_trades)+"/"+DoubleToString(m_report.loss_trades/(double)m_report.total_trades*100,1)+"%");
//---
   for(int i=0; i<11; i++)
      m_report_text[i].Update(true);
   m_reported=true;
}

Como ha sido mencionado en el análisis de Novedades, algunos de los parámetros (por ejemplo, Beneficio neto o Factor de recuperación) no están disponibles para el modo de la simulación «Puntos».


Orden de la simulación y demostración del trabajo con el Constructor de estrategias

Puesto que la propia aplicación ha sufrido modificaciones y actualizaciones, habrá que completar la guía de uso.

Paso 1. Definir el idioma de la interfaz. 

Este paso es opcional, y sólo es necesario si el idioma de la interfaz no le conviene.

Paso 2. Definir los parámetros de indicadores.

La aplicación ya dispone los ajustes predefinidos, por eso, no es necesario configurar absolutamente todo, sólo aquellos parámetros con los que se va a trabajar. Si hace falta, siempre podrá volver y modificar los parámetros.

Paso 3. Configurar la tabla de los símbolos.

Por defecto, se realiza la filtración de todos los símbolos disponibles en la pestaña Observación del mercado según la presencia de la parte USD. Para visualizar todos los símbolos disponibles, no es obligatorio quitarlos a través del campo de edición del filtro, basta con desmarcarlos.

Paso 5. El orden de selección del Intervalo temporal de la simulación y del Marco temporal ha quedado sin alterar.

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

El orden de la configuración de señales ha quedado sin alterar. Pero la visualización ha sufrido modificaciones, a saber, al desactivar un tipo de las señales, sus ajustes se ocultan, tal como se muestra en la figura 3.

Fig. 4. Desactivar de las señales de compra o venta.

Paso 7. La configuración de Take Profit y Stop Loss también ha quedado sin alterar.

Paso 8. Selección del tipo del Beneficio.

  • Al seleccionar Puntos, la simulación y la visualización de los resultados será en puntos del instrumento seleccionado. 
  • Al seleccionar Divisa, se abren los ajustes adicionales como Tipo del lote, Tamaño del lote y Depósito inicial. A su vez, Tipo del lote influye en el cálculo del lote que será aplicado en la simulación. Hay dos opciones: Constante o Del balance.

Paso 9. Una vez ejecutados los pasos 1-8, para iniciar la simulación, seleccione el instrumento a testear en la tabla de los símbolos usando el botón izquierdo del ratón. 

Después de la conclusión de la simulación, sus resultados aparecerán en el bloque Informe. Sólo después de eso, puede pulsar el botón Abrir Gráfico.

El siguiente vídeo muestra un ejemplo del funcionamiento según el orden de simulación expuesto.



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

  • Para un correcto trabajo de la aplicación, es necesario tener cargados los datos históricos para el instrumento financiero seleccionado.
  • Al configurar un indicador personalizado, preste atención en sus parámetros: el número del búfer, el nombre y los parámetros se muestran separados con coma. Hay que añadir que para un indicador personalizado se soportan sólo valores numéricos. El nombre del indicador personalizado es la ruta respecto al directorio raíz de indicadores (MQL5/Indicators/). Si el indicador se ubica en una subcarpeta, por ejemplo, en MQL5/Indicators/Examples, entonces, el nombre tiene que ser correspondiente, a saber «Examples\\nombre_del_indicador" (la indicación de la barra doble inclinada inversa como separador, en vez de una sola, es obligatoria).
  • Los escenarios más comunes que suponen dificultades en el trabajo, han sido dotados con ayudas. Puede tratarse de lo siguiente: los patrones iguales para ambas señales, intento de abrir el gráfico sin realizar ni una sola simulación tras el inicio de la aplicación, fecha del inicio y finalización incorrecta para la simulación.
  • В начале статьи упоминалась ссылка на описания характеристик, используемых в Отчете. Sígala para obtener más información sobre la descripción y los métodos del cálculo de algún parámetro.

Conclusión

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


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


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

Archivos adjuntos |
MQL5.zip (1936.58 KB)
Monitoreo multidivisas de las señales comerciales (Parte 1): Desarrollando la estructura de la aplicación Monitoreo multidivisas de las señales comerciales (Parte 1): Desarrollando la estructura de la aplicación
En este artículo, analizaremos la idea del monitoreo multidivisas de las señales comerciales, desarrollaremos la estructura y el prototipo de la futura aplicación, así como, crearemos su plantilla para el trabajo ulterior. Crearemos paso a paso una aplicación multidivisas ajustada de forma flexible que permite tanto crear las señales comerciales, como ayudar a los traders a buscarlas.
Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XXIV): Clase comercial principal - corrección automática de parámetros erróneos Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XXIV): Clase comercial principal - corrección automática de parámetros erróneos
En el presente artículo, analizaremos el manejador de parámetros erróneos de la orden comercial, mejoraremos la clase comercial básica y también corregiremos el funcionamiento de la clase de eventos comerciales: ahora, todos los eventos comerciales, tanto únicos, como simultáneos en un mismo tick, serán correctamente determinados en los programas.
Trabajando con las funciones de red, o MySQL sin DLL: Parte I - el conector Trabajando con las funciones de red, o MySQL sin DLL: Parte I - el conector
Hace relativamente poco, aparecieron en MetaTrader 5 las funciones de red. Este hecho abre un amplio abanico de posibilidades para los programadores que desarrollan productos para el Mercado, ya que ahora es posible implementar aquello que antes no se podía conseguir sin bibliotecas dinámicas. En este artículo, nos familiarizaremos con ellas usando como ejemplo la escritura de un conector MySQL.
Redes neuronales: así de sencillo Redes neuronales: así de sencillo
Cada vez que hablamos de inteligencia artificial, en nuestra cabeza surgen todo tipo de ideas fantásticas, y nos parece que se trata de algo complicado e inalcanzable. Sin embargo, cada día oímos hablar de la inteligencia artificial en nuestra vida diaria. En las noticias se escribe con cada vez mayor frecuencia sobre los logros en el uso de redes neuronales. En el presente artículo, queremos mostrar al lector lo sencillo que puede resultar para cualquiera crear una red neuronal y usar los logros de la inteligencia artificial en el trading.