Monitoreo multidivisas de las señales comerciales (Parte 3): Introduciendo los algoritmos para la búsqueda

31 marzo 2020, 09:19
Alexander Fedosov
0
947

Contenido

Introducción

En los artículos anteriores, desarrollamos la estructura de la aplicación para monitorear señales comerciales, así como implementamos la interfaz de la aplicación dotada con posibilidades básicas de la interacción con ella. Ahora, tendremos que llenar la parte visual con un algoritmo de la configuración y de la búsqueda de las señales comerciales. Vamos a basarnos en el proyecto desde el artículo anterior, completándolo paso a paso para la mejor percepción.

Sistema del guardado de un conjunto de símbolos

Antes, durante el primer paso de la configuración de la aplicación, creamos las herramientas para seleccionar símbolos en la Observación del mercado. Aquí, podemos realizar la selección de tres maneras diferentes:

  • Manualmente. Hay que marcar los símbolos necesarios y pulsar en el botón Next (Siguiente).
  • Conjunto predefinido. Al hacer clic en los botones All, Major, Crosses, se selecciona automáticamente un determinado conjunto establecido anteriormente.
  • Conjunto guardado. Se usa un conjunto de símbolos establecido anteriormente con el uso de dos modos anteriores y guardado en un archivo con un determinado nombre.

Fig. 1. Primer paso de la configuración de la aplicación y controles de la configuración del Conjunto guardado.

Si los dos primeros eran bastante simples y fueron implementados antes, en cuanto al tercer modo, pues, tendremos que crearlo. Vamos a aclarar qué es lo que vamos a hacer. Nos centraremos en la interacción entre los controles en el marco rojo de la fig. 1, se compone de lo siguiente:

  • Una vez marcados los símbolos del conjunto que vamos a usar, introducimos el nombre de este conjunto en el campo Template name (Nombre de la plantilla) y pulsamos en el botón Save (Guardar) o hacemos clic en la tecla S. Si la plantilla se ha guardado con éxito, se muestra el mensaje correspondiente.
  • Si queremos acceder a una plantilla configurada y guardada anteriormente, introducimos su nombre en este campo y pulsamos en el botón Load (Cargar) o hacemos clic en la tecla L.

Abrimos nuestro proyecto y añadimos dos métodos al archivo de nuestra clase base CProgram en la sección privada que van a encargarse de cargar y guardar la plantilla de símbolos.

   bool              SaveSymbolSet(string file_name);
   bool              LoadSymbolSet(string file_name);

Ahora implementamos cada uno de los métodos agregados. 

//+------------------------------------------------------------------+
//| Guardando la plantilla en el archivo                             |
//+------------------------------------------------------------------+
bool CProgram::SaveSymbolSet(string file_name)
{
   if(file_name=="")
   {
      MessageBox("Por favor, elija el nombre de la plantilla para el guardado","Signal Monitor");
      return(false);
   }
   int h=FileOpen("Signal Monitor\\"+file_name+".bin",FILE_WRITE|FILE_BIN);
   if(h==INVALID_HANDLE)
   {
      MessageBox("Fallo al crear el archivo de la configuración","Signal Monitor");
      return(false);
   }
   else
      MessageBox("La configuración "+file_name+" ha sido guardada con éxito","Signal Monitor");
//--- Guardamos la selección de los timeframes y patrones
   for(int i=0; i<m_all_symbols; i++)
      m_save.tf[i]=m_checkbox[i].IsPressed();
//---
   FileWriteStruct(h,m_save);
   FileClose(h);
//---
   return(true);
}
//+------------------------------------------------------------------+
//| Cargando los datos en el panel                                   |
//+------------------------------------------------------------------+
bool CProgram::LoadSymbolSet(string file_name)
{
   if(file_name=="")
   {
      MessageBox("Por favor, seleccione el nombre de la plantilla a cargar","Signal Monitor");
      return(false);
   }
   int h=FileOpen("Signal Monitor\\"+file_name+".bin",FILE_READ|FILE_BIN);
   if(h==INVALID_HANDLE)
   {
      MessageBox("La configuración "+file_name+" no ha sido encontrada","Signal Monitor");
      return(false);
   }
   ZeroMemory(m_save);
   FileReadStruct(h,m_save);
//--- Cargando timeframes
   for(int i=0; i<m_all_symbols; i++)
   {
      m_checkbox[i].IsPressed(m_save.tf[i]);
      m_checkbox[i].Update(true);
   }
//---
   FileClose(h);
//---
   return(true);
}

Sin embargo, si compilamos nuestro proyecto ahora, veremos un error relacionado con la variable m_save. Se trata de la estructura que tiene un parámetro tipo bool con el nombre tf. Es necesaria para guardar la selección del usuario en el archivo. Por esta razón, crearemos esta estructura en nuestra clase y añadiremos su instancia a nuestra clase base.

//+------------------------------------------------------------------+
//| Clase para crear la aplicación                                   |
//+------------------------------------------------------------------+
struct SAVE
{
   bool     tf[100];
};
class CProgram : public CWndEvents
{
...
        SAVE            m_save;

Ahora, en el método OnEvent(), entramos en la sección Eventos de la pulsación en el botón, y al final añadimos el código que permitirá implementar nuestra tarea:

         //--- Guardar la plantilla
         if(lparam==m_save_button.Id())
         {
            SaveSymbolSet(m_text_edit.GetValue());
         }
         //--- Cargar la plantilla
         if(lparam==m_load_button.Id())
         {
            LoadSymbolSet(m_text_edit.GetValue());
         }

Además, vamos a implementar el uso de las teclas aceleradoras relacionadas con los botones. Para eso, en el mismo método, añadiremos la verificación del Evento de la pulsación en las teclas del teclado, y escribiremos ahí el código para las teclas que hemos establecido.

//--- Pulsación de la tecla del teclado
   if(id==CHARTEVENT_KEYDOWN)
   {
      if(m_current_step==1)
      {
         short sym=TranslateKey((int)lparam);
         //--- si el símbolo introducido ha sido transformado en Unicode con éxito
         if(sym>0)
         {
            if(ShortToString(sym)=="l" || ShortToString(sym)=="д")
               LoadSymbolSet(m_text_edit.GetValue());
            if(ShortToString(sym)=="s" || ShortToString(sym)=="ы")
               SaveSymbolSet(m_text_edit.GetValue());
         }
      }
   }

Compilamos el proyecto. Si hemos hecho todo correctamente, obtendremos el siguiente resultado.

Fig. 2 Guardar y cargar la plantilla personalizada

Añadir y editar una señal comercial

Ahora pasamos a la parte principal de la aplicación que se encarga de la creación y la edición de las señales comerciales, así como, de su seguimiento posterior en el propio monitoreo. Recordemos qué aspecto tiene la ventana para crear y editar una señal. 

Fig. 3 Ventana para crear y editar una señal

Por ahora, representa un conjunto de los controles de la interfaz que controlan diferentes parámetros, no obstante, estos ajustes no se usan en ninguna parte. Primero, vamos a agregar dos botones en esta interfaz que se utilizan para añadir/guardar una señal comercial, así como, un botón para cancelar la creación/edición. Para eso, entramos en el archivo Program.mqh y añadimos el método de la implementación de estos dos botones en la clase base:

bool              CreateButton3(CButton &button,string text,const int x_gap,const int y_gap);

Además, añadimos dos variables de las instancias de la clase CButton:

   CButton           m_new_signal;
   CButton           m_cancel_button;

Ahora, vamos al archivo SetWindow.mqh e implementamos este método.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateButton3(CButton &button,string text,const int x_gap,const int y_gap)
{
//---
   color baseclr=C'70,180,70';
   color pressed=C'70,170,70';
//--- Guardamos el puntero a la ventana
   button.MainPointer(m_set_window);
//--- Establecemos las propiedades antes de la creación
   button.XSize(60);
   button.YSize(30);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(pressed);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(pressed);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Creamos el control
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Añadimos el puntero al control a la base
   CWndContainer::AddToElementsArray(1,button);
   return(true);
}

Para que estos dos botones aparezcan en la interfaz de la ventana de la adición de la señal comercial, es necesario añadir las siguientes líneas al final del cuerpo del método CreateSetWindow():

//--- Botones para añadir/cancelar
   if(!CreateButton3(m_new_signal,"Add",m_set_window.XSize()-2*(60+10),m_set_window.YSize()-(30+10)))
      return(false);
   if(!CreateButton3(m_cancel_button,"Cancel",m_set_window.XSize()-(60+10),m_set_window.YSize()-(30+10)))
      return(false);

Después de la compilación del proyecto, aparecerán dos botones en la parte inferior de la ventana de la creación de la señal comercial. 

Fig. 4 Añadir botones para crear y cancelar una señal comercial

Ahora, es necesario «darles vida», es decir, agregar eventos que van a ocurrir al pulsarlos. Con el botón Cancel (Cancelar) todo está bien claro (no guarda ningunas acciones ni los ajustes de esta ventana, simplemente la cierra sin agregar la señal). En cuanto a las acciones del clic en el botón Add (Añadir), habrá que hacer una exposición detallada.

Para empezar, vamos a definir el orden de las acciones que tendrá lugar al pulsar en Add añadiendo la señal.

  1. Se realiza el guardado de los parámetros seleccionados en el archivo a través de los controles de la interfaz en la ventana de la creación de la señal comercial.
  2. Si el guardado ha tenido éxito, esta ventana se cierra y en la lista de las señales de la ventana principal aparece la primera entrada con el nombre de la señal y con la posibilidad de editar esta señal al hacer clic en esta entrada.
  3. Al hacer clic en la entrada, desde el archivo, a los controles de la interfaz de la configuración de la señal se les aplicará el conjunto guardado anteriormente, mientras que el botón Add se transformará en Save (Guardar).

Para ejecutar la tarea del guardado de los ajustes en el archivo, es necesario crear un conjunto universal de los ajustes que van a usarse tanto para la visualización en la ventana de la edición, como durante la búsqueda de la señal en el futuro. Para ese propósito, vamos a crear una estructura con el nombre SIGNAL, donde vamos a escribir toda la configuración de los ajustes en la ventana de la creación/edición.

struct SIGNAL
{
   int      ind_type;
   int      ind_period;
   int      app_price;
   int      rule_type;
   double   rule_value;
   int      label_type;
   uchar    label_value[10];
   color    label_color;
   color    back_color;
   color    border_color;
   bool     tooltip;
   uchar    tooltip_text[100];
   bool     image;
   int      img_index;
   bool     timeframes[21];
   TFNAME   tf_name[21];
};

Vamos a considerar cada elemento agregado en ella:

  • ind_type — contiene el tipo del indicador que ha sido elegido como base para buscar la señal. En la interfaz es Indicator Type.
  • ind_period — período del indicador arriba seleccionado.
  • app_price — precio aplicado. Este valor no estará disponible para cada indicador. Por tanto, va a escribirse sólo cuando este parámetro tenga lugar en el indicador actual seleccionado. Por ejemplo, RSI lo tiene, mientras que WPR no dispone de él.
  • rule_type — tipo de la regla que va a usarse durante la búsqueda de la señal. En la interfaz, es el menú desplegable con los signos ==,>=,<= etc.
  • rule_value — valor límite del indicador seleccionado al que va a aplicarse la regla de la búsqueda.
  • label_type — guarda el tipo de la visualización de la etiqueta de texto. Puede ser el valor actual del indicador, o bien una etiqueta personalizada de hasta tres símbolos.
  • label_value — si tenemos seleccionado el segundo tipo de la visualización, aquí se guarda el texto de la etiqueta establecido por el usuario.
  • label_color — guarda el color de la etiqueta de texto.
  • back_color — guarda el color del fondo del bloque de la señal en el monitoreo a condición de que esta opción esté seleccionada.
  • border_color — guarda el color del borde del bloque de la señal en el monitoreo a condición de que esta opción esté seleccionada.
  • tooltip — contiene el indicio del uso de la ayuda emergente.
  • tooltip_text — si el indicio del uso es positivo, incluye su texto.
  • image — indicio del uso de la imagen.
  • img_index — si se usa una imagen, guarda el número de orden de la misma.
  • timeframes — array que contiene la información sobre los ajustes de los timeframes seleccionados en el segundo paso.
  • tf_name — guarda el nombre de los timeframes en los que se busca la señal comercial. 

Ahora, vamos a declarar un array de las estructuras en la clase base para guardar los conjuntos de los ajustes para las señales creadas.

SIGNAL            m_signal_set[5];

Además, crearemos dos métodos en la sección privada de la clase CProgram para guardar el conjunto de los parámetros en el archivo desde la estructura y cargarlo desde el archivo en la estructura.

   bool              SaveSignalSet(int index);
   bool              LoadSignalSet(int index);

Vamos a implementarlos:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::SaveSignalSet(int index)
{
//---
   int h=FileOpen("Signal Monitor\\signal_"+string(index)+".bin",FILE_WRITE|FILE_BIN);
   if(h==INVALID_HANDLE)
   {
      MessageBox("Fallo al crear el archivo de la configuración","Signal Monitor");
      return(false);
   }
//--- Guardar la selección
   //--- Tipo del indicador
   m_signal_set[index].ind_type=m_indicator_type.GetListViewPointer().SelectedItemIndex();
   //--- Período del indicador
   m_signal_set[index].ind_period=(int)m_period_edit.GetValue();
   //--- Tipo del precio aplicado
   m_signal_set[index].app_price=m_applied_price.GetListViewPointer().SelectedItemIndex();
   //--- Tipo de la regla
   m_signal_set[index].rule_type=m_rule_type.GetListViewPointer().SelectedItemIndex();
   //--- Valor de la regla
   m_signal_set[index].rule_value=(double)m_rule_value.GetValue();
   //--- Tipo de la visualización de la etiqueta de texto
   m_signal_set[index].label_type=m_label_button[0].IsPressed()?0:1;
   //--- Guardamos el valor del campo de edición en caso del segundo tipo
   if(m_label_button[1].IsPressed())
      StringToCharArray(StringSubstr(m_text_box.GetValue(),0,3),m_signal_set[index].label_value);
   //--- Color de la etiqueta de texto
   m_signal_set[index].label_color=m_color_button[0].CurrentColor();
   //--- Color del fondo
   if(m_set_param[0].IsPressed())
      m_signal_set[index].back_color=m_color_button[1].CurrentColor();
   else
      m_signal_set[index].back_color=clrNONE;
   //--- Color del borde
   if(m_set_param[1].IsPressed())
      m_signal_set[index].border_color=m_color_button[2].CurrentColor();
   else
      m_signal_set[index].border_color=clrNONE;
   //--- Valor de la ayuda
   m_signal_set[index].tooltip=m_set_param[2].IsPressed();
   if(m_signal_set[index].tooltip)
      StringToCharArray(m_tooltip_text.GetValue(),m_signal_set[index].tooltip_text);
   //--- Imagen seleccionada
   m_signal_set[index].image=m_set_param[3].IsPressed();
   if(m_signal_set[index].image)
      m_signal_set[index].img_index=m_pictures_slider.GetRadioButtonsPointer().SelectedButtonIndex();
   //--- Timeframes seleccionados
   int tf=0;
   for(int i=0; i<21; i++)
   {
      if(!m_tf_button[i].IsLocked() && m_tf_button[i].IsPressed())
      {
         m_signal_set[index].timeframes[i]=true;
         StringToCharArray(m_tf_button[i].LabelText(),m_signal_set[index].tf_name[i].tf);
         tf++;
      }
      else
         m_signal_set[index].timeframes[i]=false;
   }
   //---
   if(tf<1)
   {
      MessageBox(" No hay timeframes seleccionados","Signal Monitor");
      FileClose(h);
      return(false);
   }
//---
   FileWriteStruct(h,m_signal_set[index]);
   FileClose(h);
   Print(" La configuración signal_"+string(index)+" ha sido guardada con éxito");
//---
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::LoadSignalSet(int index)
{
   int h=FileOpen("Signal Monitor\\signal_"+string(index)+".bin",FILE_READ|FILE_BIN);
   if(h==INVALID_HANDLE)
   {
      MessageBox("La configuración no ha sido encontrada","Signal Monitor");
      return(false);
   }
   ZeroMemory(m_signal_set[index]);
   FileReadStruct(h,m_signal_set[index]);
//--- Cargando el tipo del indicador
   m_indicator_type.SelectItem(m_signal_set[index].ind_type);
   RebuildParameters(m_signal_set[index].ind_type);
   m_indicator_type.GetButtonPointer().Update(true);
//--- Cargando el período del indicador
   m_period_edit.SetValue((string)m_signal_set[index].ind_period);
   m_period_edit.GetTextBoxPointer().Update(true);
//--- Cargando el precio aplicado, si hay
   if(!m_applied_price.IsLocked())
   {
      m_applied_price.SelectItem(m_signal_set[index].app_price);
      m_applied_price.GetButtonPointer().Update(true);
   }
//--- Cargando la regla de la señal
   m_rule_type.SelectItem(m_signal_set[index].rule_type);
   m_rule_type.GetButtonPointer().Update(true);
   m_rule_value.SetValue((string)m_signal_set[index].rule_value);
   m_rule_value.GetTextBoxPointer().Update(true);
//--- Cargando la etiqueta de texto
   if(m_signal_set[index].label_type==0)
   {
      m_label_button[0].IsPressed(true);
      m_label_button[0].Update(true);
      m_label_button[1].IsPressed(false);
      m_label_button[1].Update(true);
      m_text_box.IsLocked(true);
   }
   else
   {
      m_label_button[0].IsPressed(false);
      m_label_button[0].Update(true);
      m_label_button[1].IsPressed(true);
      m_label_button[1].Update(true);
      m_text_box.IsLocked(false);
      m_text_box.ClearTextBox();
      m_text_box.AddText(0,CharArrayToString(m_signal_set[index].label_value));
      m_text_box.Update(true);
   }
//--- Cargando el color de la etiqueta de texto
   m_color_button[0].CurrentColor(m_signal_set[index].label_color);
   m_color_button[0].Update(true);
//--- Cargando el color del fondo
   if(m_signal_set[index].back_color==clrNONE)
   {
      m_set_param[0].IsPressed(false);
      m_set_param[0].Update(true);
      m_color_button[1].IsLocked(true);
      m_color_button[1].GetButtonPointer().Update(true);
   }
   else
   {
      m_set_param[0].IsPressed(true);
      m_set_param[0].Update(true);
      m_color_button[1].IsLocked(false);
      m_color_button[1].CurrentColor(m_signal_set[index].back_color);
      m_color_button[1].GetButtonPointer().Update(true);
   }
//--- Cargando el color del borde
   if(m_signal_set[index].border_color==clrNONE)
   {
      m_set_param[1].IsPressed(false);
      m_set_param[1].Update(true);
      m_color_button[2].IsLocked(true);
      m_color_button[2].GetButtonPointer().Update(true);
   }
   else
   {
      m_set_param[1].IsPressed(true);
      m_set_param[1].Update(true);
      m_color_button[2].IsLocked(false);
      m_color_button[2].CurrentColor(m_signal_set[index].border_color);
      m_color_button[2].GetButtonPointer().Update(true);
   }
//--- Cargando el valor de la ayuda
   if(!m_signal_set[index].tooltip)
   {
      m_set_param[2].IsPressed(false);
      m_set_param[2].Update(true);
      m_tooltip_text.IsLocked(true);
      m_tooltip_text.Update(true);
   }
   else
   {
      m_set_param[2].IsPressed(true);
      m_set_param[2].Update(true);
      m_tooltip_text.IsLocked(false);
      m_tooltip_text.ClearTextBox();
      m_tooltip_text.AddText(0,CharArrayToString(m_signal_set[index].tooltip_text));
      m_tooltip_text.Update(true);
   }
//--- Cargando la imagen
   if(!m_signal_set[index].image)
   {
      m_set_param[3].IsPressed(false);
      m_set_param[3].Update(true);
      m_pictures_slider.IsLocked(true);
      m_pictures_slider.GetRadioButtonsPointer().Update(true);
   }
   else
   {
      m_set_param[3].IsPressed(true);
      m_set_param[3].Update(true);
      m_pictures_slider.IsLocked(false);
      m_pictures_slider.GetRadioButtonsPointer().SelectButton(m_signal_set[index].img_index);
      m_pictures_slider.GetRadioButtonsPointer().Update(true);
   }
//--- Cargando timeframes seleccionados
   for(int i=0; i<21; i++)
   {
      if(!m_tf_button[i].IsLocked())
      {
         m_tf_button[i].IsPressed(m_signal_set[index].timeframes[i]);
         m_tf_button[i].Update(true);
      }
   }
//---
   FileClose(h);
   return(true);
}

Pues bien, hemos hecho la primera acción con el algoritmo «guardar/cargar». Ahora, tenemos que diseñar los objetos —que van a representar las entradas sobre las señales creadas— al pulsar los cuales se podrá editar los parámetros de una señal ya creada. Por tanto, vamos a crear un array de las instancias de clase CButton para implementar estos objetos.

CButton           m_signal_editor[5];

Así como un método que va a crearlos.

bool              CreateSignalEditor(CButton &button,string text,const int x_gap,const int y_gap);

Implementamos este método en el archivo StepWindow.mqh ya que estos objetos van a pertenecer a la ventana principal.

//+------------------------------------------------------------------+
//| Crea un botón con imagen                                         |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\settings_light.bmp"

bool CProgram::CreateSignalEditor(CButton &button,string text,const int x_gap,const int y_gap)
{
//---
   color baseclr=C'70,180,70';
   color pressed=C'70,170,70';
//--- Guardamos el puntero a la ventana
   button.MainPointer(m_step_window);
//--- Establecemos las propiedades antes de la creación
   button.XSize(110);
   button.YSize(30);
   button.Font(m_base_font);
   button.FontSize(m_base_font_size);
   button.IconXGap(3);
   button.IconYGap(7);
   button.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\settings_light.bmp");
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(pressed);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(pressed);
   button.LabelColor(clrWhite);
   button.LabelColorPressed(clrWhite);
   button.LabelColorHover(clrWhite);
   button.IsCenterText(true);
//--- Creamos el control
   if(!button.CreateButton(text,x_gap,y_gap))
      return(false);
//--- Añadimos el puntero al control a la base
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}

Usamos este método para añadir 5 objetos al cuerpo de CreateStepWindow(), que serán objetos en la lista de las señales.

//---
   for(int i=0; i<5; i++)
   {
      if(!CreateSignalEditor(m_signal_editor[i],"Signal_"+string(i),10,40*i+90))
         return(false);
   }

Para que estos elementos no se muestren después de su creación durante el inicio de la aplicación, hay que ocultarlos en el método CreateGUI().

//+------------------------------------------------------------------+
//| Crea la interfaz gráfica del programa                            |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
{
//--- Paso 1-3. Ventana para seleccionar símbolos.
   if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols"))
      return(false);
//---
   if(!CreateSetWindow("Signal Monitor Edit Signal"))
      return(false);
//--- Creando el formulario 2 para la paleta de colores
   if(!CreateColorWindow("Color Picker"))
      return(false);
//--- Terminando la creación de GUI
   CWndEvents::CompletedGUI();
   m_back_button.Hide();
   m_add_signal.Hide();
   m_signal_header.Hide();
   m_label_button[1].IsPressed(true);
   m_label_button[1].Update(true);
   for(int i=0; i<5; i++)
      m_signal_editor[i].Hide();
   return(true);
}

Lo siguiente que hay que hacer antes de compilar el proyecto es crear un método que elimine todos los guardados anteriores durante la configuración inicial. Para eso, creamos el método ClearSaves() y lo invocamos en el constructor de la clase CProgram.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::ClearSaves(void)
{
   for(int i=0; i<5; i++)
      FileDelete("Signal Monitor\\signal_"+string(i)+".bin");
   m_total_signals=0;
   return(true);
}

Ahora, completamos el procesamiento del evento de la pulsación en el botón Add Signal (Añadir señal):

//--- Clic en el botón Añadir señal
      if(lparam==m_add_signal.Id())
      {
         m_set_window.OpenWindow();
         m_number_signal=-1;
         RebuildTimeframes();
         m_new_signal.LabelText("Add");
         m_new_signal.Update(true);
      }

Después de compilar el proyecto, el mecanismo de la adición de una nueva señal ya está listo. El siguiente paso será la implementación de la edición de una señal ya creada.

Fig. 5 Adición de una señal nueva.

Vamos a resumir. En este momento (como se muestra en la Fig. 5), se puede añadir señales comerciales pulsando en el botón Add Signal. Además, se puede observar que se añade un botón con el nombre de la señal nueva a la lista Signal List. En este momento, es un nombre definido sin posibilidad de editarlo. No obstante, si hacemos clic en Signal_0, no ocurre nada, y hay que corregir eso. Para que se abra la ventana con los ajustes y que se carguen en la interfaz exactamente las mismas configuraciones que han sido guardadas anteriormente para la señal seleccionada. El segundo detalle consiste en lo siguiente: que haya la posibilidad de modificar las configuraciones cargadas y volver a guardarlas.

Para eso, entramos en el cuerpo del método OnEvent() en la clase base CProgram, buscamos la sección del evento de la pulsación del botón y añadimos dentro el siguiente código:

//---
      for(int i=0; i<5; i++)
      {
         if(lparam==m_signal_editor[i].Id())
         {
            LoadSignalSet(i);
            m_new_signal.LabelText("Save");
            m_new_signal.Update(true);
            m_set_window.OpenWindow();
            m_number_signal=i;
         }
      }

Aquí, definimos el botón de una de las señales creadas que hemos pulsado. Sabiendo eso, cargamos los datos guardados antes en la interfaz de la ventana de configuraciones usando el método LoadSignalSet(), cambiamos el nombre del botón Add (Añadir) por Save (Guardar) y abrimos la ventana de configuraciones.


Una vez desarrollada la herramienta para crear y editar señales comerciales, ahora nos toca vincularla con la parte de la aplicación que se encarga de la visualización y de la búsqueda de señales. Antes, ya hemos creado una base para monitorear las señales. Representa una tabla con los nombres de las filas (símbolos seleccionados en el primer paso de la configuración) y de las columnas (timeframes seleccionados en el segundo paso de la configuración).

Fig. 6 Botón para crear el monitoreo de señales comerciales.

La secuencia de las acciones tras la creación por lo menos de una señal comercial es bastante simple. Cuando se pulsa el botón Create, se forma el monitoreo de las señales comerciales a base del array completo de las configuraciones que han sido definidas anteriormente. Pero antes de comenzar la construcción programática de este sistema, hay que completar el método ToMonitor() que se invoca después de la pulsación en el botón Create (Crear).

//--- Ocultamos el paso 3
   m_add_signal.Hide();

   m_signal_header.Hide();
   m_back_button.Hide();
   m_next_button.Hide();
   for(int i=0; i<5; i++)
      m_signal_editor[i].Hide();

Puesto que ahora disponemos de los objetos-botones que permiten visualizar y editar las señales actuales creadas, pues, al ir a la ventana del monitoreo, habrá que ocultarlos también, igual como los demás controles del Paso 3 antes de eso.

En el primer artículo, durante el desarrollo de la estructura de la aplicación, uno de los elementos del monitoreo era el bloque de indicación mostrado en la Fig. 5 de aquel artículo. Su propósito era mostrar en tiempo real la presencia de una de las señales creadas antes. Por eso, primero, crearemos el objeto que va a usarse como bloque de indicación. Para eso, añadimos el método CreateSignalButton() a la clase CProgram, y lo implementamos. 

bool              CreateSignalButton(CButton &button,const int x_gap,const int y_gap);

Así como el array de las instancias de la clase CButton, que son necesarias para crear un conjunto completo de los bloques de indicación.

CButton           m_signal_button[];

Ahora, entramos en StepWindow.mqh, y añadimos la implementación del método creado al final del archivo:

//+------------------------------------------------------------------+
//| Crea el bloque de indicación                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateSignalButton(CButton &button,const int x_gap,const int y_gap)
{
//---
   color baseclr=C'220,225,235';
//--- Guardamos el puntero a la ventana
   button.MainPointer(m_step_window);
//--- Establecemos las propiedades antes de la creación
   button.TwoState(false);
   button.XSize(40);
   button.YSize(20);
   button.IconXGap(2);
   button.IconYGap(button.YSize()/2-8);
   button.LabelXGap(19);
   button.LabelYGap(2);
   button.FontSize(m_base_font_size);
   button.BackColor(baseclr);
   button.BackColorHover(baseclr);
   button.BackColorPressed(baseclr);
   button.BorderColor(baseclr);
   button.BorderColorHover(baseclr);
   button.BorderColorPressed(baseclr);
   button.LabelColor(clrBlack);
   button.LabelColorPressed(clrSlateGray);
   button.IconFile("");
   button.IconFileLocked("");
   button.IsDoubleBorder(true);
//--- Creamos el control
   if(!button.CreateButton("",x_gap-button.XSize()/2,y_gap))
      return(false);
//--- Añadimos el puntero al control a la base
   CWndContainer::AddToElementsArray(0,button);
   return(true);
}

Y lo aplicamos enseguida en el método de la construcción del monitoreo ToMonitor(). Para eso, buscamos la sección Timeframes en el cuerpo de este método, y completamos el método con el código después de esta sección. Tiene que salir lo siguiente:

//--- Timeframes
   int tf=ArraySize(m_timeframes);
   ArrayResize(m_timeframe_label,tf);
//---
   for(int i=0; i<tf; i++)
   {
      if(!CreateTimeframeLabel(m_timeframe_label[i],110+50*i,m_step_window.CaptionHeight()+3,m_timeframes[i]))
         return;
      m_timeframe_label[i].Update(true);
   }
//-- Bloques de señales
   int k=0;
   ArrayResize(m_signal_button,sy*tf);
   for(int j=0; j<sy; j++)
   {
      for(int i=0; i<tf; i++)
      {
         if(!CreateSignalButton(m_signal_button[k],m_timeframe_label[i].XGap()+m_timeframe_label[i].XSize()/2,m_step_window.CaptionHeight()+25+j*25))
            return;
         m_signal_button[k].Update(true);
         k++;
      }
   }
//--- Cambiamos las dimensiones de la ventana
   AutoResize(m_timeframe_label[tf-1].XGap()+m_timeframe_label[tf-1].XSize()+5,m_symbol_label[sy-1].YGap()+m_symbol_label[sy-1].YSize()+5);

Compilamos el proyecto y obtenemos una maqueta hecha para la futura visualización de señales comerciales.


Fig.7 Maqueta hecha de señales comerciales.

Ahora, hay que recordar qué elementos del bloque de indicación se puede configurar para mostrar una u otra señal dentro de ellos.

  • Color del fondo.
  • Presencia y color del borde del bloque de indicación.
  • Color y valor de la etiqueta de texto.
  • Presencia del icono.
  • Presencia de la ayuda.

Para controlar estas propiedades, vamos a crear los siguientes métodos en la sección privada de nuestra clase base CProgram:

   void              SetBorderColor(int index, color clr);
   void              SetLabel(int index, string text,color clr=clrBlack);
   void              SetIcon(int index,int number);
   void              SetBackground(int index,color clr);
   void              SetTooltip(int index,string text="\n");

Los implementamos ahí mismo.

//+------------------------------------------------------------------+
//| Definir el color del borde                                       |
//+------------------------------------------------------------------+
void CProgram::SetBorderColor(int index, color clr)
{
   m_signal_button[index].BorderColor(clr);
   m_signal_button[index].BorderColorHover(clr);
   m_signal_button[index].BorderColorPressed(clr);
   m_signal_button[index].Update(true);
}
//+------------------------------------------------------------------+
//| Definir el texto de la etiqueta                                           |
//+------------------------------------------------------------------+
void CProgram::SetLabel(int index, string text,color clr=clrBlack)
{
   m_signal_button[index].LabelColor(clr);
   m_signal_button[index].LabelColorHover(clr);
   m_signal_button[index].LabelColorPressed(clr);
   m_signal_button[index].LabelText(text);
   m_signal_button[index].Update(true);
}
//+------------------------------------------------------------------+
//| Definir el fondo                                                 |
//+------------------------------------------------------------------+
void CProgram::SetBackground(int index,color clr)
{
   m_signal_button[index].BackColor(clr);
   m_signal_button[index].BackColorHover(clr);
   m_signal_button[index].Update(true);
}
//+------------------------------------------------------------------+
//| Definir el icono                                                 |
//+------------------------------------------------------------------+
void CProgram::SetIcon(int index,int number)
{
   //---
   string image[]=
   {
      "Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_up.bmp",
      "Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_down.bmp"
   };
   string path=(number>=0)?image[number]:"";
   if(number<0)
      m_signal_button[index].IsCenterText(true);
   else
      m_signal_button[index].IsCenterText(false);
   m_signal_button[index].IconFile(path);
   m_signal_button[index].IconFilePressed(path);
   m_signal_button[index].Update(true);
}
//+------------------------------------------------------------------+
//| Definir la ayuda                                                 |
//+------------------------------------------------------------------+
void CProgram::SetTooltip(int index,string text="\n")
{
   m_signal_button[index].Tooltip(text);
   m_signal_button[index].ShowTooltip(true);
}

A continuación, hace falta crear varios métodos auxiliares que serán necesarios para los cálculos posteriores, una visualización correcta y, lo más importante, para la correspondencia de cada uno de los bloques de indicación creados a su fila (símbolo seleccionado) y su columna (timeframe). Primero, creamos los métodos de la definición de la fila y columna del bloque de indicación según su número (índice) de orden en la tabla.

   int               GetRow(int index,int row_size);
   int               GetCol(int index,int row_size);
//+------------------------------------------------------------------+
//| Definición de la fila según el índice                            |
//+------------------------------------------------------------------+
int CProgram::GetRow(int index,int row_size)
{
   return(int(MathFloor(index/row_size)+1));
}
//+------------------------------------------------------------------+
//| Definición de la columna según el índice                         |
//+------------------------------------------------------------------+
int CProgram::GetCol(int index,int row_size)
{
   return(int(MathMod(index,row_size)+1));
}

Ahora, es necesario aprender a obtener los datos necesarios de la interfaz diseñada. A saber, aprender a convertir la visualización de texto de los timeframes en el tipo de la enumeración del timeframe. Así como, es necesario tener la posibilidad de averiguar del índice del bloque de indicación a qué símbolo o timeframe de este símbolo él pertenece en la tabla.

//+------------------------------------------------------------------+
//| Devuelve el timeframe de la fila                                 |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CProgram::StringToTimeframe(const string timeframe)
{
   if(timeframe=="M1")  return(PERIOD_M1);
   if(timeframe=="M2")  return(PERIOD_M2);
   if(timeframe=="M3")  return(PERIOD_M3);
   if(timeframe=="M4")  return(PERIOD_M4);
   if(timeframe=="M5")  return(PERIOD_M5);
   if(timeframe=="M6")  return(PERIOD_M6);
   if(timeframe=="M10") return(PERIOD_M10);
   if(timeframe=="M12") return(PERIOD_M12);
   if(timeframe=="M15") return(PERIOD_M15);
   if(timeframe=="M20") return(PERIOD_M20);
   if(timeframe=="M30") return(PERIOD_M30);
   if(timeframe=="H1")  return(PERIOD_H1);
   if(timeframe=="H2")  return(PERIOD_H2);
   if(timeframe=="H3")  return(PERIOD_H3);
   if(timeframe=="H4")  return(PERIOD_H4);
   if(timeframe=="H6")  return(PERIOD_H6);
   if(timeframe=="H8")  return(PERIOD_H8);
   if(timeframe=="H12") return(PERIOD_H12);
   if(timeframe=="D1")  return(PERIOD_D1);
   if(timeframe=="W1")  return(PERIOD_W1);
   if(timeframe=="MN")  return(PERIOD_MN1);
//--- Valor por defecto
   return(::Period());
}
//+------------------------------------------------------------------+
//| Definición del timeframe                                         |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CProgram::GetTimeframe(int index)
{
   int tf=ArraySize(m_timeframes);
   return(StringToTimeframe((m_timeframe_label[GetCol(index,tf)-1].LabelText())));
}
//+------------------------------------------------------------------+
//| Definición del símbolo                                           |
//+------------------------------------------------------------------+
string CProgram::GetSymbol(int index)
{
   int tf=ArraySize(m_timeframes);
   return(m_symbol_label[GetRow(index,tf)-1].LabelText());
}

El siguiente método va a estar relacionado directamente con el algoritmo de la búsqueda de la señal porque busca (en el símbolo y tiemframe definidos) el conjunto de los ajustes de la señal creada anteriormente por nosotros que le ha sido traspasado.

bool              GetSignal(string sy,ENUM_TIMEFRAMES tf,SIGNAL &signal_set);

Para traspasar los ajustes, se usa la estructura desde el conjunto de los parámetros SIGNAL

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::GetSignal(string sy,ENUM_TIMEFRAMES tf,SIGNAL &signal_set)
{
//--- Obteniendo handle del indicador
   int h=INVALID_HANDLE;
   ENUM_APPLIED_PRICE app_price;
   switch(signal_set.app_price)
   {
   case  0:
      app_price=PRICE_CLOSE;
      break;
   case  1:
      app_price=PRICE_OPEN;
      break;
   case  2:
      app_price=PRICE_HIGH;
      break;
   case  3:
      app_price=PRICE_LOW;
      break;
   case  4:
      app_price=PRICE_MEDIAN;
      break;
   case  5:
      app_price=PRICE_TYPICAL;
      break;
   case  6:
      app_price=PRICE_WEIGHTED;
      break;
   default:
      app_price=PRICE_CLOSE;
      break;
   }
//---
   switch(signal_set.ind_type)
   {
   case  0:
      h=iATR(sy,tf,signal_set.ind_period);
      break;
   case  1:
      h=iCCI(sy,tf,signal_set.ind_period,app_price);
      break;
   case  2:
      h=iDeMarker(sy,tf,signal_set.ind_period);
      break;
   case  3:
      h=iForce(sy,tf,signal_set.ind_period,MODE_SMA,VOLUME_TICK);
      break;
   case  4:
      h=iWPR(sy,tf,signal_set.ind_period);
      break;
   case  5:
      h=iRSI(sy,tf,signal_set.ind_period,app_price);
      break;
   case  6:
      h=iMomentum(sy,tf,signal_set.ind_period,app_price);
      break;
   default:
      break;
   }
   if(h==INVALID_HANDLE)
   {
      Print(sy+". Failed to get handle");
      Print("Handle = ",h,"  error = ",GetLastError());
      return(false);
   }
   //---
   double arr[1];
   if(CopyBuffer(h,0,
    0,1,arr)!=1)
   {
      Print("sy= ",sy,"tf= ",EnumToString(tf)," Failed to get handle data ",GetLastError());
      return(false);
   }
   IndicatorRelease(h);
//--- Verificando condiciones
double
   double c_value=arr[0];
   m_ind_value=c_value;
   int s=0;
   switch(signal_set.rule_type)
   {
   case  0:
      if(c_value>r_value)
         s=1;
      break;
   case  1:
      if(c_value>=r_value)
         s=1;
      break;
   case  2:
      if(c_value==r_value)
         s=1;
      break;
   case  3:
      if(c_value<r_value)
         s=1;
      break;
   case  4:
      if(c_value<=r_value)
         s=1;
      break;
   default:
      s=0;
      break;
   }
//---
   if(s>0)
      return(true);
   return(false);
}

El método GetSignal() recibe la siguiente información desde la estructura SIGNAL: qué indicador hemos seleccionado entre los propuestos para la búsqueda de la señal comercial, qué ajustes hemos seleccionado para ella, qué regla de búsqueda hemos establecido. Pero no hay que olvidar que la filtración por los timeframes puede realizarse dos veces para cada señal. La primera puede tener lugar en el segundo paso de la configuración, y la segunda, entre los timeframes ya seleccionados en la ventana de la creación de la señal, tal como se muestra en la Fig. 8 más abajo.

Fig. 8 Selección de timeframes para la señal a crear.

Para que nuestro algoritmo lo tenga en cuenta durante la búsqueda de las señales (o para ser más exacto, para que no busque señales donde no hace falta), hay que verificar eso para cada una de las señales creadas. Por tanto, vamos a crear el método CheckTimeframe() que nos servirá de este filtro en la clase base.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CheckTimeframe(ENUM_TIMEFRAMES tf,SIGNAL &signal_set)
{
   for(int i=0; i<21; i++)
   {
      if(StringToTimeframe(CharArrayToString(signal_set.tf_name[i].tf))==tf)
         return(true);
   }
   return(false);
}

En realidad, nos queda crear el propio mecanismo de la búsqueda de las señales comerciales. Para eso, añadimos el método SearchSignal() a la sección pública de nuestra clase CProgram.

bool              SearchSignals(void);

Vamos a analizar más detalladamente su implementación paso a paso, para tener más claro para qué han sido creados algunos métodos auxiliares antes.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::SearchSignals(void)
{
//--- Buscando señales establecidas
   SIGNAL signal_set[];
   int cnt=0;
   for(int i=0; i<5; i++)
   {
      if(FileIsExist("Signal Monitor\\signal_"+string(i)+".bin"))
         cnt++;
   }
//---
   ArrayResize(signal_set,cnt);
   ZeroMemory(signal_set);
//---
   for(int i=0; i<cnt; i++)
   {
      int h=FileOpen("Signal Monitor\\signal_"+string(i)+".bin",FILE_READ|FILE_BIN);
      if(h==INVALID_HANDLE)
      {
         MessageBox("La configuración no ha sido encontrada","Signal Monitor");
         return(false);
      }
      FileReadStruct(h,signal_set[i]);
      FileClose(h);
      for(int j=0; j<ArraySize(m_signal_button); j++)
      {
         //---
         string sy=GetSymbol(j);
         ENUM_TIMEFRAMES tf=GetTimeframe(j);
         //---
         if(!CheckTimeframe(tf,signal_set[i]))
            continue;
         //---
         if(GetSignal(sy,tf,signal_set[i]))
         {
            //---
            if(signal_set[i].label_type==1)
               SetLabel(j,CharArrayToString(signal_set[i].label_value),signal_set[i].label_color);
            else
               SetLabel(j,DoubleToString(m_ind_value,3),signal_set[i].label_color);
            //---
            if(signal_set[i].back_color!=clrNONE)
               SetBackground(j,signal_set[i].back_color);
            //---
            if(signal_set[i].border_color!=clrNONE)
               SetBorderColor(j,signal_set[i].border_color);
            else
               SetBorderColor(j,signal_set[i].back_color);
            //---
            if(signal_set[i].tooltip)
               SetTooltip(j,CharArrayToString(signal_set[i].tooltip_text));
            //---
            if(signal_set[i].image)
               SetIcon(j,signal_set[i].img_index);
            else
               SetIcon(j,-1);
         }
      }
   }
   return(true);
}

Durante la primera fase del trabajo, el método de la búsqueda recopila los datos sobre el número total de las señales creadas y configuradas. Luego, abre cíclicamente el archivo con la información completa sobre los ajustes de la señal y envía esta información a la estructura. A continuación, para cada bloque de indicación, se determina el símbolo y timeframe al que corresponde en forma de la tabla. Teniendo estos datos, comprobamos si hace falta buscar la señal en el timeframe seleccionado. Si el timeframe corresponde, buscamos la señal. Si está presente, coloreamos el bloque de indicación tal como ha sido configurado para esta señal. Por último, aplicamos el método creado. Hay que invocarlo al final del cuerpo del método ToMonitor().

...
//--- Cambiamos las dimensiones de la ventana
   AutoResize(m_timeframe_label[tf-1].XGap()+m_timeframe_label[tf-1].XSize()+5,m_symbol_label[sy-1].YGap()+m_symbol_label[sy-1].YSize()+5);
//---
   SearchSignals();
}

Nos queda elaborar un algoritmo de búsquedas repetidas dentro de un cierto intervalo de tiempo. Para eso, vamos al archivo SignalMonitor.mq5 y creamos una enumeración al principio del archivo:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum UPDATE
{
   MINUTE,        // 1 Minuto
   MINUTE_15,     // 15 Minutos
   MINUTE_30,     // 30 Minutos
   HOUR,          // 1 Hora
   HOUR_4         // 4 Horas
};

Ahora, es fácil agregar la nueva configuración en los parámetros de entrada:

input UPDATE               Update            =  HOUR;                // Intervalo de actualización

Luego, creamos dos variables para los cálculos.

int cnts=0;
datetime update;

Agregamos las siguientes líneas en la inicialización del EA:

//---
   switch(Update)
   {
   case MINUTE:
      cnts=60;
      break;
   case MINUTE_15:
      cnts=60*15;
      break;
   case MINUTE_30:
      cnts=60*30;
      break;
   case HOUR:
      cnts=3600;
      break;
   case HOUR_4:
      cnts=3600*4;
      break;
   default:
      cnts=1;
      break;
   }
   update=TimeLocal()+cnts;

Así, definimos el intervalo de la actualización y establecemos el siguiente momento para actualizar. Ahora, agregamos la verificación por el tiempo en el cuerpo de la función OnTick(), si el intervalo establecido transcurre, buscamos las señales establecidas de nuevo.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   if(TimeLocal()>update)
   {
      program.SearchSignals();
      update=TimeLocal()+cnts;
   }
}

Ahora, compilamos el proyecto y creamos un conjunto de nuestros propios símbolos, así como, añadimos una señal para demostrar el funcionamiento del monitoreo.

 

En el siguiente artículo de esta serie, nos dedicaremos a la extensión de la funcionalidad actual centrándonos en la configuración más flexible de las señales comerciales, también completaremos las posibilidades existentes en este momento.


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 Fig. 9.


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


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

Archivos adjuntos |
MQL5.zip (1706.81 KB)
El enfoque econométrico en la búsqueda de leyes de mercado: autocorrelación, mapas de calor y diagramas de dispersión El enfoque econométrico en la búsqueda de leyes de mercado: autocorrelación, mapas de calor y diagramas de dispersión

Investigación ampliada de características estacionales: autocorrelación, mapas de calor y diagramas de dispersión. El objetivo de este artículo es mostrar que la "memoria del mercado" tiene un carácter estacional que se muestra a través de la maximización de la correlación de los incrementos de orden aleatorio.

Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XXX): Solicitudes comerciales pendientes - Control de los objetos de solicitudes Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XXX): Solicitudes comerciales pendientes - Control de los objetos de solicitudes

En el anterior artículo, creamos las clases de los objetos de solicitudes pendientes que se corresponden con el concepto general de los objetos de la biblioteca. En el presente artículo, nos ocuparemos de la clase que permite controlar los objetos de solicitudes pendientes.

Monitoreo multidivisas de las señales comerciales (Parte 4): Mejorando la funcionalidad y el sistema de búsqueda de las señales Monitoreo multidivisas de las señales comerciales (Parte 4): Mejorando la funcionalidad y el sistema de búsqueda de las señales

En este artículo, vamos a ampliar el sistema de búsqueda y edición de las señales comerciales, introduciremos la posibilidad de usar indicadores personalizados y añadiremos la localización de la aplicación. Antes, creamos el sistema básico de la búsqueda de señales comerciales, pero este sistema se basaba en una reducida gama de indicadores y en una elección lacónica de las reglas para la búsqueda.

Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XXXI): Solicitudes comerciales pendientes - Apertura de posiciones según condiciones Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XXXI): Solicitudes comerciales pendientes - Apertura de posiciones según condiciones

A partir de este artículo, vamos a crear una funcionalidad que permita comerciar con la ayuda de solicitudes comerciales según una cierta condición. Por ejemplo, al llegar o superar una determinada hora, o bien al superar el parámetro de beneficio establecido, o bien al registrarse un evento de cierre de posición por stop loss.