Trabajando con las series temporales en la biblioteca DoEasy (Parte 55): Clase de colección de indicadores

25 enero 2021, 10:59
Artyom Trishkin
0
112

Contenido


Concepto

Hoy llevaremos a término lógico las clases herederas del objeto básico del indicador abstracto que comenzamos a hacer en el artículo anterior.
Con el fin de observar el concepto general de la construcción de los objetos de la biblioteca y para que la organización de los objetos de indicadores no se diferencie de otros objetos de la biblioteca, tenemos que añadir las descripciones a los objetos de indicador. Al mismo tiempo, vamos a mejorar el almacenamiento de estos objetos en su colección, a saber: corregiremos los errores cometidos en el artículo anterior a la hora de crear la colección de los indicadores y añadir los objetos a la colección.

Cabe notar que el objeto básico del indicador abstracto y los objetos de indicador que representan a los herederos de este objeto son unas esencias independientes y no tienen nada que ver con los indicadores de período y símbolo múltiples que fueron creados antes para la creación de nuestros indicadores personalizado a través de la biblioteca.

El objeto del indicador abstracto y sus herederos son objetos de indicador que habrá que usar para los Asesores Expertos de indicador y para la búsqueda de diferentes combinaciones de datos y estados de valores de diferentes indicadores.


Mejorando las clases de la biblioteca

Al desarrollar las clases de los objetos de la biblioteca, en primer lugar, añadimos tradicionalmente los mensajes de texto necesarios que se usan por los objetos para mostrar sus descripciones. Para los objetos de indicador, necesitamos los mensajes para mostrar todos los posibles parámetros de todos los indicadores estándar.
Añadimos los índices de mensajes nuevos al archivo\MQL5\Include\DoEasy\Data.mqh:

   MSG_LIB_TEXT_IND_TEXT_EMPTY_VALUE,                 // Empty value for plotting where nothing will be drawn
   MSG_LIB_TEXT_IND_TEXT_SYMBOL,                      // Indicator symbol
   MSG_LIB_TEXT_IND_TEXT_NAME,                        // Indicator name
   MSG_LIB_TEXT_IND_TEXT_SHORTNAME,                   // Indicator short name
   
   MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS,              // Indicator parameters
   MSG_LIB_TEXT_IND_TEXT_APPLIED_VOLUME,              // Volume type for calculation
   MSG_LIB_TEXT_IND_TEXT_PERIOD,                      // Averaging period
   MSG_LIB_TEXT_IND_TEXT_FAST_PERIOD,                 // Fast MA period
   MSG_LIB_TEXT_IND_TEXT_SLOW_PERIOD,                 // Slow MA period
   MSG_LIB_TEXT_IND_TEXT_SIGNAL,                      // Difference averaging period
   MSG_LIB_TEXT_IND_TEXT_TENKAN_PERIOD,               // Tenkan-sen period
   MSG_LIB_TEXT_IND_TEXT_KIJUN_PERIOD,                // Kijun-sen period
   MSG_LIB_TEXT_IND_TEXT_SPANB_PERIOD,                // Senkou Span B period
   MSG_LIB_TEXT_IND_TEXT_JAW_PERIOD,                  // Period for jaw line calculation
   MSG_LIB_TEXT_IND_TEXT_TEETH_PERIOD,                // Period for teeth line calculation
   MSG_LIB_TEXT_IND_TEXT_LIPS_PERIOD,                 // Period for lips line calculation
   MSG_LIB_TEXT_IND_TEXT_JAW_SHIFT,                   // Horizontal shift of jaws line
   MSG_LIB_TEXT_IND_TEXT_TEETH_SHIFT,                 // Horizontal shift of teeth line
   MSG_LIB_TEXT_IND_TEXT_LIPS_SHIFT,                  // Horizontal shift of lips line
   MSG_LIB_TEXT_IND_TEXT_SHIFT,                       // Horizontal shift of the indicator
   MSG_LIB_TEXT_IND_TEXT_MA_METHOD,                   // Smoothing type
   MSG_LIB_TEXT_IND_TEXT_APPLIED_PRICE,               // Price type or handle
   MSG_LIB_TEXT_IND_TEXT_STD_DEVIATION,               // Number of standard deviations
   MSG_LIB_TEXT_IND_TEXT_DEVIATION,                   // Deviation of channel borders from the central line
   MSG_LIB_TEXT_IND_TEXT_STEP,                        // Price change step — acceleration factor
   MSG_LIB_TEXT_IND_TEXT_MAXIMUM,                     // Maximum step
   MSG_LIB_TEXT_IND_TEXT_KPERIOD,                     // K period (number of bars for calculation)
   MSG_LIB_TEXT_IND_TEXT_DPERIOD,                     // D period (primary smoothing period)
   MSG_LIB_TEXT_IND_TEXT_SLOWING,                     // Final smoothing
   MSG_LIB_TEXT_IND_TEXT_PRICE_FIELD,                 // Stochastic calculation method
   MSG_LIB_TEXT_IND_TEXT_CMO_PERIOD,                  // Chande Momentum period
   MSG_LIB_TEXT_IND_TEXT_SMOOTHING_PERIOD,            // Smoothing factor period
   
//--- CIndicatorsCollection
   MSG_LIB_SYS_FAILED_ADD_IND_TO_LIST,                // Error. Failed to add indicator object to list
   
  };
//+------------------------------------------------------------------+

Añadimos los mensajes de texto correspondientes a los índices nuevamente añadidos:

   {"Пустое значение для построения, для которого нет отрисовки","Empty value for plotting, for which there is no drawing"},
   {"Символ индикатора","Indicator symbol"},
   {"Имя индикатора","Indicator name"},
   {"Короткое имя индикатора","Indicator shortname"},
   
   {"Параметры индикатора","Indicator parameters"},
   {"Тип объема для расчета","Volume type for calculation"},
   {"Период усреднения","Averaging period"},
   {"Период быстрой скользящей","Fast MA period"},
   {"Период медленной скользящей","Slow MA period"},
   {"Период усреднения разности","Averaging period for their difference"},
   {"Период Tenkan-sen","Tenkan-sen period"},
   {"Период Kijun-sen","Kijun-sen period"},
   {"Период Senkou Span B","Senkou Span B period"},
   {"Период для расчета челюстей","Period for the calculation of jaws"},
   {"Период для расчета зубов","Period for the calculation of teeth"},
   {"Период для расчета губ","Period for the calculation of lips"},
   {"Смещение челюстей по горизонтали","Horizontal shift of jaws"},
   {"Смещение зубов по горизонтали","Horizontal shift of teeth"},
   {"Смещение губ по горизонтали","Horizontal shift of lips"},
   {"Смещение индикатора по горизонтали","Horizontal shift of the indicator"},
   {"Тип сглаживания","Smoothing type"},
   {"Тип цены или handle","Price type or handle"},
   {"Количество стандартных отклонений","Number of standard deviations"},
   {"Отклонение границ от средней линии","Deviation of boundaries from the midline"},
   {"Шаг изменения цены - коэффициент ускорения","Price increment step - acceleration factor"},
   {"Максимальный шаг","Maximum value of step"},
   {"K-период (количество баров для расчетов)","K-period (number of bars for calculations)"},
   {"D-период (период первичного сглаживания)","D-period (period of first smoothing)"},
   {"Окончательное сглаживание","Final smoothing"},
   {"Способ расчета стохастика","Stochastic calculation method"},
   {"Период Chande Momentum","Chande Momentum period"},
   {"Период фактора сглаживания","Smoothing factor period"},
   
   {"Ошибка. Не удалось добавить объект-индикатор в список","Error. Failed to add indicator object to list"},
   
  };
//+---------------------------------------------------------------------+

Para mostrar algunos parámetros de los indicadores, como el método de promediación, tipo del precio y volumen para el cálculo, etc., escribiremos funciones separadas en el archivo de las funciones de servicio de la biblioteca en \MQL5\Include\DoEasy\Services\DELib.mqh:

//+------------------------------------------------------------------+
//| Return timeframe description                                     |
//+------------------------------------------------------------------+
string TimeframeDescription(const ENUM_TIMEFRAMES timeframe)
  {
   return StringSubstr(EnumToString((timeframe>PERIOD_CURRENT ? timeframe : (ENUM_TIMEFRAMES)Period())),7);
  }
//+------------------------------------------------------------------+
//| Return volume description for calculation                        |
//+------------------------------------------------------------------+
string AppliedVolumeDescription(const ENUM_APPLIED_VOLUME volume)
  {
   return StringSubstr(EnumToString(volume),7);
  }
//+------------------------------------------------------------------+
//| Return indicator type description                                |
//+------------------------------------------------------------------+
string IndicatorTypeDescription(const ENUM_INDICATOR indicator)
  {
   return StringSubstr(EnumToString(indicator),4);
  }
//+------------------------------------------------------------------+
//| Return averaging method description                              |
//+------------------------------------------------------------------+
string AveragingMethodDescription(const ENUM_MA_METHOD method)
  {
   return StringSubstr(EnumToString(method),5);
  }
//+------------------------------------------------------------------+
//| Return applied price description                                 |
//+------------------------------------------------------------------+
string AppliedPriceDescription(const ENUM_APPLIED_PRICE price)
  {
   return StringSubstr(EnumToString(price),6);
  }
//+------------------------------------------------------------------+
//| Return stochastic price calculation description                  |
//+------------------------------------------------------------------+
string StochPriceDescription(const ENUM_STO_PRICE price)
  {
   return StringSubstr(EnumToString(price),4);
  }
//+------------------------------------------------------------------+

Como podemos ver, aquí todo es muy simple: recuperamos la subcadena de la posición necesaria desde la representación textual del valor de la enumeración y obtenemos al final el nombre del indicador, método del cálculo o el tipo del volumen o del precio.

Cada indicador tiene su determinado conjunto de parámetros. Se puede establecer estos parámetros para el indicador usando la matriz de estructuras de los parámetros del indicador MqlParam. Es lo que estamos haciendo durante la creación de cada objeto de indicador. Por tanto, podemos imprimir en el registro todos los valores de la matriz de estas estructuras para cada indicador. Para indicadores diferentes, cada celda de la matriz va a contener los datos sobre el valor de los parámetros propios solamente de este tipo del indicador. Pero para varios indicadores del mismo tipo, en cada celda de la matriz se indican las propiedades iguales según su propósito, diferenciándose sólo en su valor.
De esta manera, para cada indicador se puede escribir un método que va a mostrar en el registro un conjunto de parámetros del indicador con valores establecidos para él. Eso es justo sólo para los indicadores estándar, ya que para ellos conocemos exactamente el conjunto de parámetros de cada indicador especificado.

Será un método virtual. Lo escribimos en la clase del objeto del indicador abstracto en el archivo \MQL5\Include\DoEasy\Objects\Indicators\IndicatorDE.mqh.

//--- Display the description of indicator object properties in the journal (full_prop=true - all properties, false - supported ones only)
   void              Print(const bool full_prop=false);
//--- Display (1) a short description, (2) description of indicator object parameters in the journal (implementation in the descendants)
   virtual void      PrintShort(void) {;}
   virtual void      PrintParameters(void) {;}

  };
//+------------------------------------------------------------------+

En esta clase, este método no hace nada. El método de la visualización de los datos del indicador en el registro se implementa en los objetos herederos.
Cada clase heredera va a tener su propio método porque cada indicador tiene su propio conjunto de parámetros.

En el constructor paramétrico cerrado reemplazamos la cadena de la obtención de la descripción del tipo del indicador

   this.m_ind_type=::StringSubstr(::EnumToString(ind_type),4);

por la obtención de la descripción a través de la nueva función de servicio que hemos escrito antes:

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
CIndicatorDE::CIndicatorDE(ENUM_INDICATOR ind_type,
                           string symbol,
                           ENUM_TIMEFRAMES timeframe,
                           ENUM_INDICATOR_STATUS status,
                           ENUM_INDICATOR_GROUP group,
                           string name,
                           string shortname,
                           MqlParam &mql_params[])
  {
//--- Set collection ID to the object
   this.m_type=COLLECTION_INDICATORS_ID;
//--- Write description of indicator type
   this.m_ind_type_description=IndicatorTypeDescription(ind_type);
//--- If parameter array value passed to constructor is more than zero
//--- fill in the array of object parameters with data from the array passed to constructor
   int count=::ArrayResize(this.m_mql_param,::ArraySize(mql_params));
   for(int i=0;i<count;i++)
     {
      this.m_mql_param[i].type         = mql_params[i].type;
      this.m_mql_param[i].double_value = mql_params[i].double_value;
      this.m_mql_param[i].integer_value= mql_params[i].integer_value;
      this.m_mql_param[i].string_value = mql_params[i].string_value;
     }
//--- Create indicator handle
   int handle=::IndicatorCreate(symbol,timeframe,ind_type,count,this.m_mql_param);
   
//--- Save integer properties
   this.m_long_prop[INDICATOR_PROP_STATUS]                     = status;
   this.m_long_prop[INDICATOR_PROP_TYPE]                       = ind_type;
   this.m_long_prop[INDICATOR_PROP_GROUP]                      = group;
   this.m_long_prop[INDICATOR_PROP_TIMEFRAME]                  = timeframe;
   this.m_long_prop[INDICATOR_PROP_HANDLE]                     = handle;
   
//--- Save real properties
   this.m_double_prop[this.IndexProp(INDICATOR_PROP_EMPTY_VALUE)]=EMPTY_VALUE;
//--- Save string properties
   this.m_string_prop[this.IndexProp(INDICATOR_PROP_SYMBOL)]   = (symbol==NULL || symbol=="" ? ::Symbol() : symbol);
   this.m_string_prop[this.IndexProp(INDICATOR_PROP_NAME)]     = name;
   this.m_string_prop[this.IndexProp(INDICATOR_PROP_SHORTNAME)]= shortname;
  }
//+------------------------------------------------------------------+

Al final del listado del método que muestra las propiedades del indicador en el registro (después de mostrar todas las propiedades del objeto de indicador), vamos a añadir la llamada al método que muestra un conjunto de parámetros del indicador en el registro, con valores establecidos para ellos:

//+------------------------------------------------------------------+
//| Display indicator properties in the journal                      |
//+------------------------------------------------------------------+
void CIndicatorDE::Print(const bool full_prop=false)
  {
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG),": \"",this.GetStatusDescription(),"\" =============");
   int beg=0, end=INDICATOR_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_INDICATOR_PROP_INTEGER prop=(ENUM_INDICATOR_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=INDICATOR_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_INDICATOR_PROP_DOUBLE prop=(ENUM_INDICATOR_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=INDICATOR_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_INDICATOR_PROP_STRING prop=(ENUM_INDICATOR_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   this.PrintParameters();
   ::Print("================== ",CMessage::Text(MSG_LIB_PARAMS_LIST_END),": \"",this.GetStatusDescription(),"\" ==================\n");
  }
//+------------------------------------------------------------------+

Ahora, si el método PrintPatameters() ya existe en la clase heredera del objeto básico del indicador abstracto, al llamar al método Print(), también será invocado el método virtual PrintPatameters() desde la clase heredera, donde será implementada la visualización de los parámetros del indicador en el registro.

Tendremos que implementar su propio método PrintPatameters() en cada clase heredera, ya que cada tipo de indicadores tiene su propio conjunto de parámetros.
Estos métodos ya fueron escritos para cada objeto de indicador. Todos ellos son del mismo tipo según su lógica pero son diferentes en su contenido. Los métodos están escritos para todos los indicadores, excepto uno, es decir, del tipo personalizado. La implementación de su método será diferente porque no podemos saber de antemano el conjunto de parámetros del indicador, como podemos saberlo para todos los indicadores estándar.
Vamos a considerar estos métodos para cada uno de los objetos herederos.

Clase del objeto de indicador Accelerator Oscillator:

//+------------------------------------------------------------------+
//|                                                        IndAC.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/es/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/es/users/artmedia70"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\\IndicatorDE.mqh"
//+------------------------------------------------------------------+
//| Accelerator Oscillator standard indicator                        |
//+------------------------------------------------------------------+
class CIndAC : public CIndicatorDE
  {
private:

public:
   //--- Constructor
                     CIndAC(const string symbol,const ENUM_TIMEFRAMES timeframe,MqlParam &mql_param[]) : 
                        CIndicatorDE(IND_AC,symbol,timeframe,
                                     INDICATOR_STATUS_STANDART,
                                     INDICATOR_GROUP_OSCILLATOR,
                                     "Accelerator Oscillator",
                                     "AC("+symbol+","+TimeframeDescription(timeframe)+")",mql_param) {}
   //--- Supported indicator properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_INDICATOR_PROP_INTEGER property);

//--- Display (1) a short description, (2) description of indicator object parameters in the journal
   virtual void      PrintShort(void);
   virtual void      PrintParameters(void) {;}
  };
//+------------------------------------------------------------------+
//| Return 'true' if the indicator supports the passed               |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CIndAC::SupportProperty(ENUM_INDICATOR_PROP_INTEGER property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Return 'true' if the indicator supports the passed               |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CIndAC::SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Display a short description of indicator object in the journal   |
//+------------------------------------------------------------------+
void CIndAC::PrintShort(void)
  {
   ::Print(GetStatusDescription()," ",this.Name()," ",this.Symbol()," ",TimeframeDescription(this.Timeframe())," [",this.Handle(),"]");
  }
//+------------------------------------------------------------------+

Aquí, sólo tenemos declarado el método virtual para mostrar parámetros, y él está vacío porque el indicador AC no tiene parámetros de entrada. Podíamos no escribirlo en absoluto, ya que si la clase heredera no tiene método virtual, se invoca el método virtual de la clase padre. Lo hemos escrito aquí sólo para que todas las clases herederas tengan la misma estructura de sus métodos.
En el método de la visualización de la descripción breve del indicador, vamos a añadir la visualización del manejador del indicador creado para este objeto. Estas modificaciones en este método han sido hechas para todos los objetos de indicador, y no vamos a considerarlas más adelante, nos limitaremos sólo con los métodos de la visualización de la descripción de los parámetros del indicador.

Clase del objeto de indicador Accumulation/Distribution y su método para visualizar la descripción de los parámetros del indicador:

//+------------------------------------------------------------------+
//|                                                        IndAD.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/es/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/es/users/artmedia70"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\\IndicatorDE.mqh"
//+------------------------------------------------------------------+
//| Accumulation/Distribution standard indicator                     |
//+------------------------------------------------------------------+
class CIndAD : public CIndicatorDE
  {
private:

public:
   //--- Constructor
                     CIndAD(const string symbol,const ENUM_TIMEFRAMES timeframe,MqlParam &mql_param[]) :
                        CIndicatorDE(IND_AD,symbol,timeframe,
                                     INDICATOR_STATUS_STANDART,
                                     INDICATOR_GROUP_VOLUMES,
                                     "Accumulation/Distribution",
                                     "AD("+symbol+","+TimeframeDescription(timeframe)+")",mql_param) {}
   //--- Supported indicator properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_INDICATOR_PROP_INTEGER property);
   
//--- Display (1) a short description, (2) description of indicator object parameters in the journal
   virtual void      PrintShort(void);
   virtual void      PrintParameters(void);
  };
//+------------------------------------------------------------------+
//| Return 'true' if the indicator supports the passed               |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CIndAD::SupportProperty(ENUM_INDICATOR_PROP_INTEGER property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Return 'true' if the indicator supports the passed               |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CIndAD::SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Display a short description of indicator object in the journal   |
//+------------------------------------------------------------------+
void CIndAD::PrintShort(void)
  {
   ::Print(GetStatusDescription()," ",this.Name()," ",this.Symbol()," ",TimeframeDescription(this.Timeframe())," [",this.Handle(),"]");
  }
//+------------------------------------------------------------------+
//| Display parameter description of indicator object in the journal |
//+------------------------------------------------------------------+
void CIndAD::PrintParameters(void)
  {
   ::Print(" --- ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS)," --- ");
   //--- applied_volume
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_APPLIED_VOLUME),": ",AppliedVolumeDescription((ENUM_APPLIED_VOLUME)m_mql_param[0].integer_value));
  }
//+------------------------------------------------------------------+

El indicador Accumulation/Distribution tiene sólo un parámetro de entrada (tipo del volumen para el cálculo). Por eso, la matriz de las estructuras de los parámetros de entrada tiene solamente una celda que almacena este parámetro. Por tanto, para visualizarlo en el registro, lo obtenemos de la matriz según el índice 0 a partir de los datos enteros de la estructura, y lo imprimimos en el registro usando la función de servicio escrita entes. La visualización del valor del parámetro es precedida por su descripción desde los mensajes de texto escritos anteriormente.

En adelante, vamos a considerar sólo los métodos de la visualización de la descripción de los parámetros de entrada del indicador.

Método para visualizar la descripción de los parámetros del indicador de la clase del objeto de indicador Average Directional Index:

//+------------------------------------------------------------------+
//| Display parameter description of indicator object in the journal |
//+------------------------------------------------------------------+
void CIndADX::PrintParameters(void)
  {
   ::Print(" --- ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS)," --- ");
   //--- adx_period
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_PERIOD),": ",(string)m_mql_param[0].integer_value);
  }
//+------------------------------------------------------------------+

El indicador ADX tiene un parámetro de entrada (período del cálculo), por eso, visualizamos de la misma manera la descripción sólo de una celda de la matriz por el índice 0 desde los datos enteros de la estructura.

Método para visualizar la descripción de los parámetros del indicador de la clase del objeto de indicador Alligator:

//+------------------------------------------------------------------+
//| Display parameter description of indicator object in the journal |
//+------------------------------------------------------------------+
void CIndAlligator::PrintParameters(void)
  {
   ::Print(" --- ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS)," --- ");
   //--- jaw_period
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_JAW_PERIOD),": ",(string)m_mql_param[0].integer_value);
   //--- jaw_shift
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_JAW_SHIFT),": ",(string)m_mql_param[1].integer_value);
   //--- teeth_period
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TEETH_PERIOD),": ",(string)m_mql_param[2].integer_value);
   //--- teeth_shift
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TEETH_SHIFT),": ",(string)m_mql_param[3].integer_value);
   //--- lips_period
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_LIPS_PERIOD),": ",(string)m_mql_param[4].integer_value);
   //--- lips_shift
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_LIPS_SHIFT),": ",(string)m_mql_param[5].integer_value);
   //--- ma_method
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_MA_METHOD),": ",AveragingMethodDescription((ENUM_MA_METHOD)m_mql_param[6].integer_value));
   //--- applied_price
   ::Print(
           " - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_APPLIED_PRICE),": ",
           (m_mql_param[7].integer_value<10 ? AppliedPriceDescription((ENUM_APPLIED_PRICE)m_mql_param[7].integer_value) : (string)m_mql_param[7].integer_value)
          );
  }
//+------------------------------------------------------------------+

El indicador Alligator tiene ocho parámetros de entrada, por eso, los visualizamos uno por uno de acuerdo con el orden de su secuencia durante la creación del indicador:

  • Período del cálculo de la línea «Mandíbulas» — índice 0 en la matriz
  • Desplazamiento por la línea horizontal «Mandíbulas» — índice 1 en la matriz
  • Período del cálculo de la línea «Dientes» — índice 2 en la matriz
  • Desplazamiento por la línea horizontal «Dientes» — índice 3 en la matriz
  • Período del cálculo de la línea «Labios» — índice 4 en la matriz
  • Desplazamiento por la línea horizontal «Labios» — índice 5 en la matriz
  • Tipo de suavizado — índice 6 en la matriz
  • Tipo de precio o manejador del indicador — índice 7 en la matriz

Todos los datos tienen el tipo entero, por eso, visualizamos la descripción de datos enteros de la estructura desde las celdas correspondientes de la matriz.
El último parámetro puede almacenar el tipo de precio según el cual se construye el indicador, o bien el manejador del indicador cuyos datos se usan para construir Alligator. Por eso, primero, verificamos el valor en la celda de la matriz, y si es menor que 10, eso significa que el indicador está construido a base de uno de los tipos posibles de precio y mostramos la descripción del tipo de precio. Si el valor es 10 o mayor, eso quiere decir que el indicador se basa en los datos de otro indicador cuyo manejador está escrito en la matriz. Mostramos el valor del manejador de este indicador.

Método para visualizar la descripción de los parámetros del indicador de la clase del objeto de indicador Envelopes:

//+------------------------------------------------------------------+
//| Display parameter description of indicator object in the journal |
//+------------------------------------------------------------------+
void CIndEnvelopes::PrintParameters(void)
  {
   ::Print(" --- ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_IND_PARAMETERS)," --- ");
   //--- ma_period
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_PERIOD),": ",(string)m_mql_param[0].integer_value);
   //--- ma_shift
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_SHIFT),": ",(string)m_mql_param[1].integer_value);
   //--- ma_method
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_MA_METHOD),": ",AveragingMethodDescription((ENUM_MA_METHOD)m_mql_param[2].integer_value));
   //--- applied_price
   ::Print(
           " - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_APPLIED_PRICE),": ",
           (m_mql_param[3].integer_value<10 ? AppliedPriceDescription((ENUM_APPLIED_PRICE)m_mql_param[3].integer_value) : (string)m_mql_param[3].integer_value)
          );
   //--- deviation
   ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_IND_TEXT_DEVIATION),": ",::DoubleToString(m_mql_param[4].double_value,3));
   
  }
//+------------------------------------------------------------------+

El indicador Envelopes tiene cinco parámetros de entrada. El último parámetro «Desviación de los límites de la línea central» tiene el tipo real. Por tanto, para mostrar la descripción de este parámetro, usamos el valor de los datos reales de la estructura de los parámetros de entrada.

Todos los demás métodos de las clases de los objetos de indicador de una forma o de otra repiten la lógica anterior, y no vamos a considerar sus métodos. Se puede estudiarlos personalmente accediendo a los archivos adjuntos.

Colocamos todos los objetos-indicadores creados en la lista de colección de los indicadores. En el terminal, al crear cualquier cantidad de indicadores con parámetros iguales, en realidad creamos sólo un indicador y todas las llamadas se dirigen a él. Por eso, para crear objetos de indicador y colocarlos en la lista de colección, tenemos que controlar en la lista de colección la presencia del mismo indicador con el mismo tipo, símbolo/timeframe y parámetros que tiene el indicador a colocar en la lista. Simplemente, como los objetos de indicador absolutamente idénticos tienen el mismo manejador para el indicador creado, eso significa que se trata del mismo objeto de indicador.

Vamos a hacer algunos cambios en la clase de colección de los objetos de indicador en el archivo \MQL5\Include\DoEasy\Collections\IndicatorsCollection.mqh que empezamos a hacer en el artículo anterior. Para buscar y comparar dos objetos de indicador, usamos el método Search() de la clase de la matriz dinámica de punteros a las instancias de la clase CObject y sus herederos que vienen en la biblioteca estándar. Pero este método no puede determinar con precisión si dos objetos que contienen estructuras son idénticos. Este método sirve para comparar una propiedad especificada de dos objetos del mismo tipo. En los objetos de indicador, usamos activamente la matriz de estructuras de los parámetros del indicador MqlParam donde es necesarios comparar elemento por elemento cada propiedad de la estructura en la matriz. Afortunadamente, todos los objetos de la biblioteca disponen del método predefinido IsEqual() para una comparación más exacta de dos objetos del mismo tipo. Usaremos este método para comparar la igualdad de dos objetos del mismo tipo.

En la sección privada de la clase, declaramos el método que retorna el índice del objeto de indicador en la lista de colección:

//+------------------------------------------------------------------+
//| Indicator collection                                             |
//+------------------------------------------------------------------+
class CIndicatorsCollection : public CObject
  {
private:
   CListObj                m_list;                       // List of indicator objects
   MqlParam                m_mql_param[];                // Array of indicator parameters

//--- Create a new indicator object
   CIndicatorDE           *CreateIndicator(const ENUM_INDICATOR ind_type,MqlParam &mql_param[],const string symbol_name=NULL,const ENUM_TIMEFRAMES period=PERIOD_CURRENT);
//--- Return the indicator index in the list
   int                     Index(CIndicatorDE *compared_obj);

public:

Al final del listado del cuerpo de la clase, declaramos dos métodos públicos, para mostrar las descripciones completas y breves de los objetos de indicador que se encuentran en la lista de colección:

//--- Display (1) the complete and (2) short collection description in the journal
   void                    Print(void);
   void                    PrintShort(void);

//--- Constructor
                           CIndicatorsCollection();

  };
//+------------------------------------------------------------------+

Escribimos la implementación de métodos declarados fuera del cuerpo de la clase.

Método que retorna al diario la descripción completa de la colección:

//+------------------------------------------------------------------+
//| Display complete collection description to the journal           |
//+------------------------------------------------------------------+
void CIndicatorsCollection::Print(void)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CIndicatorDE *ind=m_list.At(i);
      if(ind==NULL)
         continue;
      ind.Print();
     }
  }
//+------------------------------------------------------------------+

Recorriendo en el ciclo la lista de colección, obtenemos el próximo objeto de indicador y mostramos en el registro su descripción completa.

Método que muestra en el diario la descripción breve de la colección:

//+------------------------------------------------------------------+
//| Display the short collection description in the journal          |
//+------------------------------------------------------------------+
void CIndicatorsCollection::PrintShort(void)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CIndicatorDE *ind=m_list.At(i);
      if(ind==NULL)
         continue;
      ind.PrintShort();
     }
  }
//+------------------------------------------------------------------+

Recorriendo en el ciclo la lista de colección, obtenemos el próximo objeto de indicador y mostramos en el registro su descripción breve
.

El método que retorna el índice del objeto de indicador en la lista de colección
:

//+------------------------------------------------------------------+
//| Return the indicator index in the list                           |
//+------------------------------------------------------------------+
int CIndicatorsCollection::Index(CIndicatorDE *compared_obj)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CIndicatorDE *indicator=m_list.At(i);
      if(indicator==NULL)
         continue;
      if(indicator.IsEqual(compared_obj))
         return i;
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

Recorremos en el ciclo la lista de colección y obtenemos el próximo objeto de indicador, lo comparamos con el objeto de indicador, cuyo puntero ha sido pasado al método y, si los objetos son iguales, devolvemos el índice del ciclo. Una vez finalizado el ciclo (si todos los objetos no son iguales), devolvemos -1.

En todos los métodos para crear nuevos objetos de indicador y colocarlos en la lista de colección, fueron hechas correcciones idénticas para cada método con el fin de evitar posibles fugas de memoria en caso del fallo de la creación del objeto o su colocación en la lista.

Vamos a ver un ejemplo del método de creación del indicador Accelerator Oscillator:

//+------------------------------------------------------------------+
//| Create a new indicator object Accelerator Oscillator             |
//| and place it to the collection list                              |
//+------------------------------------------------------------------+
int CIndicatorsCollection::CreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
//--- AC indicator possesses no parameters - resize the array of parameter structures
   ::ArrayResize(this.m_mql_param,0);
//--- Create indicator object
   CIndicatorDE *indicator=this.CreateIndicator(IND_AC,this.m_mql_param,symbol,timeframe);
   if(indicator==NULL)
      return INVALID_HANDLE;
//--- If such indicator is already in the list
   int index=this.Index(indicator);
   if(index!=WRONG_VALUE)
     {
      //--- Remove created object, get indicator object from the list and return indicator handle
      delete indicator;
      indicator=this.m_list.At(index);
      return indicator.Handle();
     }
//--- If such indicator is not in the list
   else
     {
      //--- If failed to add indicator object to the list
      //--- display the appropriate message, remove object and return INVALID_HANDLE
      if(!this.m_list.Add(indicator))
        {
         ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_ADD_IND_TO_LIST));
         delete indicator;
         return INVALID_HANDLE;
        }
      //--- Return the handle of a new indicator added to the list
      return indicator.Handle();
     }
//--- Return INVALID_HANDLE
   return INVALID_HANDLE;
  }
//+------------------------------------------------------------------+

Comprobamos la presencia de un objeto de indicador en la lista según su índice.
Si el índice es mayor que -1
, entonces se encuentra en la lista y tenemos que eliminar el objeto recién creado.
Si este indicador no está en la lista y por alguna razón no podemos añadirlo a la lista de colección
, también hay que eliminar el objeto recién creado.
Eso nos librará de las fugas de memoria en caso de una colocación fallada del nuevo objeto en la lista de colección.

Estas correcciones han sido introducidas en todos los métodos de creación de los objetos de indicador, y no vamos a considerarlos. Se puede estudiarlos personalmente accediendo a los archivos adjuntos.

Para buscar un objeto de indicador en la lista de colección y obtener un puntero a este objeto de esta lista, necesitamos los métodos que devuelven los punteros al objeto necesario. Transmitimos al método el tipo del indicador buscado, su símbolo, marco temporal y parámetros (para cada indicador sus parámetros corresponden al tipo del indicador), en la salida tenemos que obtener el puntero al objeto de indicador encontrado en la lista.

En el artículo anterior, escribimos uno de estos métodos para obtener el puntero al indicador Accelerator Oscillator. Es el más simple, ya que el indicador AC no tiene parámetros de entrada. Lo único que hay que hacer es encontrar el objeto necesario según el símbolo o marco temporal:

//+------------------------------------------------------------------+
//| Return the pointer to Accelerator Oscillator indicator object    |
//+------------------------------------------------------------------+
CIndicatorDE *CIndicatorsCollection::GetIndAC(const string symbol,const ENUM_TIMEFRAMES timeframe)
  {
   CArrayObj *list=GetListAC(symbol,timeframe);
   return(list==NULL || list.Total()==0 ? NULL : list.At(0));
  }
//+------------------------------------------------------------------+

Para buscar indicadores con parámetros de entrada, tendremos que crear un objeto de indicador temporal con parámetros establecidos y buscar una coincidencia en la lista de colección:

//+------------------------------------------------------------------+
//| Return the pointer to                                            |
//| Accumulation/Distribution indicator object                       |
//+------------------------------------------------------------------+
CIndicatorDE *CIndicatorsCollection::GetIndAD(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume)
  {
   MqlParam param[1];
   param[0].type=TYPE_INT;
   param[0].integer_value=applied_volume;
   CIndicatorDE *tmp=this.CreateIndicator(IND_AD,param,symbol,timeframe);
   if(tmp==NULL)
      return NULL;
   int index=this.Index(tmp);
   delete tmp;
   return(index>WRONG_VALUE ? this.m_list.At(index) : NULL);
  }
//+------------------------------------------------------------------+

Si este objeto ha sido encontrado en la lista, se devuelve su índice; de lo contrario, se devuelve -1.

Los demás métodos para devolver punteros a objetos de indicador en la lista son idénticos al considerado más arriba, pero tienen otros parámetros para crear el objeto de indicador. Por ejemplo, para devolver el puntero al objeto de indicador Alligator, necesitamos crear una matriz compuesta de ocho parámetros:

//+------------------------------------------------------------------+
//| Return the pointer to Alligator indicator object                 |
//+------------------------------------------------------------------+
CIndicatorDE *CIndicatorsCollection::GetIndAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe,
                                                     const int jaw_period,
                                                     const int jaw_shift,
                                                     const int teeth_period,
                                                     const int teeth_shift,
                                                     const int lips_period,
                                                     const int lips_shift,
                                                     const ENUM_MA_METHOD ma_method,
                                                     const ENUM_APPLIED_PRICE applied_price)
  {
   MqlParam param[8];
   param[0].type=TYPE_INT;
   param[0].integer_value=jaw_period;
   param[1].type=TYPE_INT;
   param[1].integer_value=jaw_shift;
   param[2].type=TYPE_INT;
   param[2].integer_value=teeth_period;
   param[3].type=TYPE_INT;
   param[3].integer_value=teeth_shift;
   param[4].type=TYPE_INT;
   param[4].integer_value=lips_period;
   param[5].type=TYPE_INT;
   param[5].integer_value=lips_shift;
   param[6].type=TYPE_INT;
   param[6].integer_value=ma_method;
   param[7].type=TYPE_INT;
   param[7].integer_value=applied_price;
   CIndicatorDE *tmp=this.CreateIndicator(IND_ALLIGATOR,param,symbol,timeframe);
   if(tmp==NULL)
      return NULL;
   int index=this.Index(tmp);
   delete tmp;
   return(index>WRONG_VALUE ? this.m_list.At(index) : NULL);
  }
//+------------------------------------------------------------------+

Todo lo demás es idéntico al método anterior que devuelve el puntero al objeto de indicador Accumulation/Distribution.
En cada uno de los métodos hay que eliminar el objeto de indicador temporal que sirve de referencia para buscar coincidencias en la lista de colección.

No vamos a considerar aquí el resto de los métodos semejantes, son iguales que los dos métodos que acabamos de analizar.

Pues, damos por finalizada la mejora de las clases en el marco del presente artículo.


Asesor Experto de prueba

Para simular la creación de los indicadores, vamos a usar el el EA de prueba del artículo 39
y lo guardaremos en la carpeta nueva \MQL5\Experts\TestDoEasy\Part55\ con el nombre nuevo TestDoEasyPart55.mq5.

La mayoría de las mejoras serán «cosméticas». En uno de los artículos anteriores, trasladamos la función para trabajar con eventos en el Simulador de Estrategias EventsHandling() a la biblioteca —al archivo Engine.mqh. Por eso, eliminamos esta función del código en el manejador OnTick() y sustituimos su llamada desde el archivo del EA

//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer(rates_data);   // Working in the timer
      PressButtonsControl();        // Button pressing control
      EventsHandling();             // Working with events
     }

por la llamada desde la biblioteca:

//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer(rates_data);   // Working in the timer
      PressButtonsControl();        // Button pressing control
      engine.EventsHandling();      // Working with events
     }

Eliminamos el bloque del código del manejador OnTick(), que visualiza en el gráfico un comentario con datos en la barra actual:

//--- Bet the zero bar of the current timeseries
   CBar *bar=engine.SeriesGetBar(NULL,PERIOD_CURRENT,0);
   if(bar==NULL)
      return;
//--- Create a string of parameters of the current bar similar to the one
//--- displayed by the bar object description:
//--- bar.Header()+": "+bar.ParameterDescription()
   string parameters=
     (TextByLanguage("Бар \"","Bar \"")+Symbol()+"\" "+TimeframeDescription((ENUM_TIMEFRAMES)Period())+"[0]: "+TimeToString(bar.Time(),TIME_DATE|TIME_MINUTES|TIME_SECONDS)+
      ", O: "+DoubleToString(engine.SeriesOpen(NULL,PERIOD_CURRENT,0),Digits())+
      ", H: "+DoubleToString(engine.SeriesHigh(NULL,PERIOD_CURRENT,0),Digits())+
      ", L: "+DoubleToString(engine.SeriesLow(NULL,PERIOD_CURRENT,0),Digits())+
      ", C: "+DoubleToString(engine.SeriesClose(NULL,PERIOD_CURRENT,0),Digits())+
      ", V: "+(string)engine.SeriesTickVolume(NULL,PERIOD_CURRENT,0)+
      ", Real: "+(string)engine.SeriesRealVolume(NULL,PERIOD_CURRENT,0)+
      ", Spread: "+(string)engine.SeriesSpread(NULL,PERIOD_CURRENT,0)
     );
//--- Display the data received from the bar object in the first line of the chart comment,
//--- while the second line contains the methods of receiving timeseries price data
   Comment(bar.Header(),": ",bar.ParameterDescription(),"\n",parameters);

De esta manera, el manejador del evento «nuevo tick» será así:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Handle the NewTick event in the library
   engine.OnTick(rates_data);

//--- If working in the tester
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer(rates_data);   // Working in the timer
      PressButtonsControl();        // Button pressing control
      engine.EventsHandling();      // Working with events
     }

//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();          // Trailing positions
      TrailingOrders();             // Trailing pending orders
     }
  }
//+------------------------------------------------------------------+

En la función de la incialización de la biblioteca OnInitDoEasy() en el bloque de la visualización de la lista de símbolos usados, sustituimos la cantidad de los símbolos establecida por un número

//--- Implement displaying the list of used symbols only for MQL5 - MQL4 has no ArrayPrint() function
#ifdef __MQL5__
   if(InpModeUsedSymbols!=SYMBOLS_MODE_CURRENT)
     {
      string array_symbols[];
      CArrayObj* list_symbols=engine.GetListAllUsedSymbols();
      for(int i=0;i<list_symbols.Total();i++)
        {
         CSymbol *symbol=list_symbols.At(i);
         if(symbol==NULL)
            continue;
         ArrayResize(array_symbols,ArraySize(array_symbols)+1,1000);
         array_symbols[ArraySize(array_symbols)-1]=symbol.Name();
        }
      ArrayPrint(array_symbols);
     }
#endif   

por la cantidad de símbolos especificada en macroinstrucción:

//--- Implement displaying the list of used symbols only for MQL5 - MQL4 has no ArrayPrint() function
#ifdef __MQL5__
   if(InpModeUsedSymbols!=SYMBOLS_MODE_CURRENT)
     {
      string array_symbols[];
      CArrayObj* list_symbols=engine.GetListAllUsedSymbols();
      for(int i=0;i<list_symbols.Total();i++)
        {
         CSymbol *symbol=list_symbols.At(i);
         if(symbol==NULL)
            continue;
         ArrayResize(array_symbols,ArraySize(array_symbols)+1,SYMBOLS_COMMON_TOTAL);
         array_symbols[ArraySize(array_symbols)-1]=symbol.Name();
        }
      ArrayPrint(array_symbols);
     }
#endif   

ya que en MetaTrader 5, a partir de la versión 2430, la cantidad total de símbolos de trabajo se ha cambiado y esta cantidad se verifica por la biblioteca y se establece automáticamente en macroinstrucción SYMBOLS_COMMON_TOTAL declarada en \MQL5\Include\DoEasy\Defines.mqh.

Vamos a crear temporalmente (y sólo para verificar la creación de objetos de indicador) dos indicadores del mismo tipo pero con parámetros diferentes. Hasta que no haya una creación normal de indicadores en nuestros programas, simplemente los crearemos en la función de inicialización de la biblioteca:

//+------------------------------------------------------------------+
//| Initializing DoEasy library                                      |
//+------------------------------------------------------------------+
void OnInitDoEasy()
  {
//--- Check if working with the full list is selected
   used_symbols_mode=InpModeUsedSymbols;
   if((ENUM_SYMBOLS_MODE)used_symbols_mode==SYMBOLS_MODE_ALL)
     {
      int total=SymbolsTotal(false);
      string ru_n="\nКоличество символов на сервере "+(string)total+".\nМаксимальное количество: "+(string)SYMBOLS_COMMON_TOTAL+" символов.";
      string en_n="\nNumber of symbols on server "+(string)total+".\nMaximum number: "+(string)SYMBOLS_COMMON_TOTAL+" symbols.";
      string caption=TextByLanguage("Внимание!","Attention!");
      string ru="Выбран режим работы с полным списком.\nВ этом режиме первичная подготовка списков коллекций символов и таймсерий может занять длительное время."+ru_n+"\nПродолжить?\n\"Нет\" - работа с текущим символом \""+Symbol()+"\"";
      string en="Full list mode selected.\nIn this mode, the initial preparation of lists of symbol collections and timeseries can take a long time."+en_n+"\nContinue?\n\"No\" - working with the current symbol \""+Symbol()+"\"";
      string message=TextByLanguage(ru,en);
      int flags=(MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2);
      int mb_res=MessageBox(message,caption,flags);
      switch(mb_res)
        {
         case IDNO : 
           used_symbols_mode=SYMBOLS_MODE_CURRENT; 
           break;
         default:
           break;
        }
     }
//--- Set the counter start point to measure the approximate library initialization time
   ulong begin=GetTickCount();
   Print(TextByLanguage("--- Инициализация библиотеки \"DoEasy\" ---","--- Initializing the \"DoEasy\" library ---"));
//--- Fill in the array of used symbols
   CreateUsedSymbolsArray((ENUM_SYMBOLS_MODE)used_symbols_mode,InpUsedSymbols,array_used_symbols);
//--- Set the type of the used symbol list in the symbol collection and fill in the list of symbol timeseries
   engine.SetUsedSymbols(array_used_symbols);
//--- Displaying the selected mode of working with the symbol object collection in the journal
   string num=
     (
      used_symbols_mode==SYMBOLS_MODE_CURRENT ? ": \""+Symbol()+"\"" : 
      TextByLanguage(". Количество используемых символов: ",". The number of symbols used: ")+(string)engine.GetSymbolsCollectionTotal()
     );
   Print(engine.ModeSymbolsListDescription(),num);
//--- Implement displaying the list of used symbols only for MQL5 - MQL4 has no ArrayPrint() function
#ifdef __MQL5__
   if(InpModeUsedSymbols!=SYMBOLS_MODE_CURRENT)
     {
      string array_symbols[];
      CArrayObj* list_symbols=engine.GetListAllUsedSymbols();
      for(int i=0;i<list_symbols.Total();i++)
        {
         CSymbol *symbol=list_symbols.At(i);
         if(symbol==NULL)
            continue;
         ArrayResize(array_symbols,ArraySize(array_symbols)+1,SYMBOLS_COMMON_TOTAL);
         array_symbols[ArraySize(array_symbols)-1]=symbol.Name();
        }
      ArrayPrint(array_symbols);
     }
#endif   
//--- Set used timeframes
   CreateUsedTimeframesArray(InpModeUsedTFs,InpUsedTFs,array_used_periods);
//--- Display the selected mode of working with the timeseries object collection
   string mode=
     (
      InpModeUsedTFs==TIMEFRAMES_MODE_CURRENT   ? 
         TextByLanguage("Работа только с текущим таймфреймом: ","Work only with the current Period: ")+TimeframeDescription((ENUM_TIMEFRAMES)Period())   :
      InpModeUsedTFs==TIMEFRAMES_MODE_LIST      ? 
         TextByLanguage("Работа с заданным списком таймфреймов:","Work with a predefined list of Periods:")                                              :
      TextByLanguage("Работа с полным списком таймфреймов:","Work with the full list of all Periods:")
     );
   Print(mode);
//--- Implement displaying the list of used timeframes only for MQL5 - MQL4 has no ArrayPrint() function
#ifdef __MQL5__
   if(InpModeUsedTFs!=TIMEFRAMES_MODE_CURRENT)
      ArrayPrint(array_used_periods);
#endif 
//--- Create timeseries of all used symbols
   engine.SeriesCreateAll(array_used_periods);
//--- Check created timeseries - display descriptions of all created timeseries in the journal
//--- (true - only created ones, false - created and declared ones)
   engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions
   //engine.GetTimeSeriesCollection().Print(true);      // Full descriptions

//--- Create indicators
   engine.GetIndicatorsCollection().CreateAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE);
   engine.GetIndicatorsCollection().CreateAMA(Symbol(),Period(),10,3,32,5,PRICE_CLOSE);
   engine.GetIndicatorsCollection().Print();
   engine.GetIndicatorsCollection().PrintShort();

//--- Create resource text files
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_01",TextByLanguage("Звук упавшей монетки 1","Falling coin 1"),sound_array_coin_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Звук упавших монеток","Falling coins"),sound_array_coin_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Звук монеток","Coins"),sound_array_coin_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("Звук упавшей монетки 2","Falling coin 2"),sound_array_coin_04);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Звук щелчка по кнопке 1","Button click 1"),sound_array_click_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Звук щелчка по кнопке 2","Button click 2"),sound_array_click_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Звук щелчка по кнопке 3","Button click 3"),sound_array_click_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("Звук кассового аппарата","Cash machine"),sound_array_cash_machine_01);
   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_green",TextByLanguage("Изображение \"Зелёный светодиод\"","Image \"Green Spot lamp\""),img_array_spot_green);
   engine.CreateFile(FILE_TYPE_BMP,"img_array_spot_red",TextByLanguage("Изображение \"Красный светодиод\"","Image \"Red Spot lamp\""),img_array_spot_red);

//--- Pass all existing collections to the main library class
   engine.CollectionOnInit();

//--- Set the default magic number for all used symbols
   engine.TradingSetMagic(engine.SetCompositeMagicNumber(magic_number));
//--- Set synchronous passing of orders for all used symbols
   engine.TradingSetAsyncMode(false);
//--- Set the number of trading attempts in case of an error
   engine.TradingSetTotalTry(InpTotalAttempts);
//--- Set correct order expiration and filling types to all trading objects
   engine.TradingSetCorrectTypeExpiration();
   engine.TradingSetCorrectTypeFilling();

//--- Set standard sounds for trading objects of all used symbols
   engine.SetSoundsStandart();
//--- Set the general flag of using sounds
   engine.SetUseSounds(InpUseSounds);
//--- Set the spread multiplier for symbol trading objects in the symbol collection
   engine.SetSpreadMultiplier(InpSpreadMultiplier);
      
//--- Set controlled values for symbols
   //--- Get the list of all collection symbols
   CArrayObj *list=engine.GetListAllUsedSymbols();
   if(list!=NULL && list.Total()!=0)
     {
      //--- In a loop by the list, set the necessary values for tracked symbol properties
      //--- By default, the LONG_MAX value is set to all properties, which means "Do not track this property" 
      //--- It can be enabled or disabled (by setting the value less than LONG_MAX or vice versa - set the LONG_MAX value) at any time and anywhere in the program
      /*
      for(int i=0;i<list.Total();i++)
        {
         CSymbol* symbol=list.At(i);
         if(symbol==NULL)
            continue;
         //--- Set control of the symbol price increase by 100 points
         symbol.SetControlBidInc(100000*symbol.Point());
         //--- Set control of the symbol price decrease by 100 points
         symbol.SetControlBidDec(100000*symbol.Point());
         //--- Set control of the symbol spread increase by 40 points
         symbol.SetControlSpreadInc(400);
         //--- Set control of the symbol spread decrease by 40 points
         symbol.SetControlSpreadDec(400);
         //--- Set control of the current spread by the value of 40 points
         symbol.SetControlSpreadLevel(400);
        }
      */
     }
//--- Set controlled values for the current account
   CAccount* account=engine.GetAccountCurrent();
   if(account!=NULL)
     {
      //--- Set control of the profit increase to 10
      account.SetControlledValueINC(ACCOUNT_PROP_PROFIT,10.0);
      //--- Set control of the funds increase to 15
      account.SetControlledValueINC(ACCOUNT_PROP_EQUITY,15.0);
      //--- Set profit control level to 20
      account.SetControlledValueLEVEL(ACCOUNT_PROP_PROFIT,20.0);
     }
//--- Get the end of the library initialization time counting and display it in the journal
   ulong end=GetTickCount();
   Print(TextByLanguage("Время инициализации библиотеки: ","Library initialization time: "),TimeMSCtoString(end-begin,TIME_MINUTES|TIME_SECONDS));
  }
//+------------------------------------------------------------------+

Aquí, hemos creado dos indicadores Adaptive Moving Average que se calculan a base del símbolo y marco temporal actuales, pero con diferentes valores de parámetros de entrada.

Compilamos el asesor y lo ejecutamos en el gráfico del terminal. Después de su inicialización, los mensajes de la biblioteca sobre la inicialización se mostrarán en el registro «Expertos», incluyendo la impresión completa y breve de los parámetros de dos indicadores creados:

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Demo account MetaTrader 5
--- Initializing the "DoEasy" library ---
Work only with the current symbol: "EURUSD"
Work with a predefined list of Periods:
"H1" "H4"
Symbol time series EURUSD: 
- Timeseries "EURUSD" H1: Required: 1000, Actual: 1000, Created: 1000, On server: 6350
- Timeseries "EURUSD" H4: Required: 1000, Actual: 1000, Created: 1000, On server: 6255
============= The beginning of the event parameter list: "Standard indicator" =============
Indicator status: Standard indicator
Indicator type: AMA
Indicator timeframe: H1
Indicator handle: 10
Indicator group: Trend indicator
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
--- Indicator parameters --- 
- Averaging period: 9
- Fast MA period: 2
- Slow MA period: 30
- Horizontal shift of the indicator: 0
- Price type or handle: CLOSE
================== End of the parameter list: "Standard indicator" ==================
 
============= The beginning of the event parameter list: "Standard indicator" =============
Indicator status: Standard indicator
Indicator type: AMA
Indicator timeframe: H1
Indicator handle: 11
Indicator group: Trend indicator
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
--- Indicator parameters --- 
- Averaging period: 10
- Fast MA period: 3
- Slow MA period: 32
- Horizontal shift of the indicator: 5
- Price type or handle: CLOSE
================== End of the parameter list: "Standard indicator" ==================
 
Standard indicator Adaptive Moving Average EURUSD H1 [10]
Standard indicator Adaptive Moving Average EURUSD H1 [11]
Library initialization time: 00:00:00.000

Aunque hay sólo un tipo del indicador (es AMA), pero fueron creados dos manejadores de este indicador, ya que los parámetros de los indicadores creados eran diferentes. Por tanto, se trata de dos indicadores diferentes (cada uno tiene su propio manejador). Por consiguiente, fueron creados dos objetos de indicador y los colocamos en la colección de indicadores.

Por ahora, solamente podemos crear diferentes indicadores con diferentes parámetros, pero para usarlos en los EAs, necesitamos preparar un sitio para almacenar sus datos, del cual podremos obtener los datos en cualquier combinación necesaria y usarlos en nuestros programas para tomar decisiones o obtener datos estadísticos. Vamos a tratar de todo eso a partir del artículo siguiente.

¿Qué es lo próximo?

En el siguiente artículo, comenzaremos a implementar el almacenamiento y la obtención de datos de parte de los objetos de indicador en los asesores expertos.

Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y el archivo del asesor de prueba para MQL5. Puede descargarlo todo y ponerlo a prueba por sí mismo.
Nótese que, por el momento, la clase de colección de los indicadores se encuentra en el proceso del desarrollo. Por tanto, no se recomienda estrictamente usarla en sus programas.
Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios al artículo.

Volver al contenido

Artículos de esta serie:

Trabajando con las series temporales en la biblioteca DoEasy (Parte 35): El objeto "Barra" y la lista de serie temporal del símbolo
Trabajando con las series temporales en la biblioteca DoEasy (Parte 36): El objeto de series temporales de todos los periodos utilizados del símbolo
Trabajando con las series temporales en la biblioteca DoEasy (Parte 37): Colección de series temporales - Base de datos de series temporales según el símbolo y el periodo
Trabajando con las series temporales en la biblioteca DoEasy (Parte 38): Colección de series temporales - Actualización en tiempo real y acceso a los datos desde el programa
Trabajando con las series temporales en la biblioteca DoEasy (Parte 39): Indicadores basados en la biblioteca - Preparación de datos y eventos de la series temporales
Trabajando con las series temporales en la biblioteca DoEasy (Parte 40): Indicadores basados en la biblioteca - actualización de datos en tiempo real
Trabajando con las series temporales en la biblioteca DoEasy (Parte 41): Ejemplo de indicador de símbolo y periodo múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 42): La clase del objeto de búfer de indicador abstracto
Trabajando con las series temporales en la biblioteca DoEasy (Parte 43): Las clases de los objetos de búferes de indicador
Trabajando con las series temporales en la biblioteca DoEasy (Parte 44): Las clases de colección de los objetos de búferes de indicador
Trabajando con las series temporales en la biblioteca DoEasy (Parte 45): Búferes de indicador de periodo múltiple
Trabajando con las series temporales en la biblioteca DoEasy (Parte 46): Búferes de indicador de periodo y símbolos múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 47): Indicadores estándar de periodo y símbolo múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 48): Indicadores de periodo y símbolo múltiples en un búfer en una subventana
Trabajando con las series temporales en la biblioteca DoEasy (Parte 49): Indicadores estándar de periodo, símbolo y búfer múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 50): Indicadores estándar de periodo y símbolo múltiples con desplazamiento
Trabajando con las series temporales en la biblioteca DoEasy (Parte 51): Indicadores estándar compuestos de período y símbolo múltiples
Trabajando con las series temporales en la biblioteca DoEasy (Parte 52): Concepto multiplataforma de indicadores estándar de periodo y símbolo múltiples de búfer único
Trabajando con las series temporales en la biblioteca DoEasy (Parte 53): Clase del indicador abstracto básico
Trabajando con las series temporales en la biblioteca DoEasy (Parte 54): Clases herederas del indicador abstracto básico

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

Archivos adjuntos |
MQL5.zip (3856.21 KB)
Redes neuronales: así de sencillo (Parte 5): Cálculos multihilo en OpenCL Redes neuronales: así de sencillo (Parte 5): Cálculos multihilo en OpenCL

Ya hemos analizado algunos tipos de implementación de redes neuronales. Podemos ver con facilidad que se repiten las mismas operaciones para cada neurona de la red. Y aquí sentimos el legítimo deseo de aprovechar las posibilidades que ofrece la computación multihilo de la tecnología moderna para acelerar el proceso de aprendizaje de una red neuronal. En el presente artículo, analizaremos una de las opciones para tal implementación.

¿Cómo ganar $1 000 000 en el trading algorítmico? ¡En los servicios de MQL5.com! ¿Cómo ganar $1 000 000 en el trading algorítmico? ¡En los servicios de MQL5.com!

Todo tráder llega al mercado con el objetivo de ganar su primer millón de dólares. ¿Cómo podemos conseguirlo sin grandes riesgos y sin capital inicial? Los servicios MQL5 ofrecen estas posibilidades a los desarrolladores y tráders en cualquier país del mundo.

Trabajando con las series temporales en la biblioteca DoEasy (Parte 56): Objeto del indicador personalizado, obtención de datos de parte de los objetos de indicador en la colección Trabajando con las series temporales en la biblioteca DoEasy (Parte 56): Objeto del indicador personalizado, obtención de datos de parte de los objetos de indicador en la colección

En el presente artículo, vamos a considerar la creación de un objeto del indicador personalizado para usarlo en los asesores expertos. Mejoraremos un poco las clases de la biblioteca y escribiremos los métodos para obtener los datos de parte de los objetos de indicador en los expertos.

El comercio en fórex y sus matemáticas básicas El comercio en fórex y sus matemáticas básicas

El artículo pretende describir las principales características del comercio de divisas de la forma más rápida y simple posible, para compartir verdades sencillas con los lectores principiantes. También intentaremos responder a las preguntas más interesantes en el entorno comercial, así como escribir un indicador simple.