DoEasy 函数库中的时间序列(第五十五部分):指标集合类

Artyom Trishkin | 16 二月, 2021

内容目录


概述

在本文中,我们将完成在上一篇文章中开始开发的抽象指标基准对象的衍生类。
遵循函数库对象构造的一般概念,如此指标对象的组织与其他函数库对象没有不同,因此必须在指标对象中添加其说明。 沿用此方式,改善这些对象在其集合中的存储,即消除我们在上一篇文章中创建指标集合,并将对象添加到集合时所犯的瑕疵。

请注意,抽象指标基准对象和该对象后代的指标对象是独立的主题,不会与我们之前创建的多品种、多周期指标交叉,我们在利用函数库创建自定义指标时会才会用到这些。

抽象指标对象及其后代都是指标对象,我们必须将它们用于指标 EA,以及搜索数据的各种组合,和各种指标值的状态。


改进库类

通常,首次开发函数库对象类时,需为对象添加显示其描述时所需的文本消息。 对于指标对象,需要所有标准指标所有可能参数的显示消息。
往文件 \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 the list
   
  };
//+------------------------------------------------------------------+

和与新添加的索引对应的文本消息:

   {"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 period"},
   {"Kijun-sen period"},
   {"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"},
   {"Price type or handle"},
   {"Number of standard deviations"},
   {"Deviation of boundaries from the midline"},
   {"Price increment step - acceleration factor"},
   {"Maximum value of step"},
   {"K-period (number of bars for calculations)"},
   {"D-period (period of first smoothing)"},
   {"Final smoothing"},
   {"Stochastic calculation method"},
   {"Chande Momentum period"},
   {"Smoothing factor period"},
   
   {"Error. Failed to add indicator object to list"},
   
  };
//+---------------------------------------------------------------------+

为了显示某些指标参数,例如平均方法、价格类型和计算的交易量,等等,在 \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);
  }
//+------------------------------------------------------------------+

这很简单:从枚举值的文本表示形式中提取所需位置的子字符串,最终获得指标名称、计算方法或交易量和价格的类型。

每个指标都拥有其确定的参数集。 可以借助指标参数结构 MqlParam 数组为指标设置这些参数。 这就是我们在创建每个指标对象期间所做的事情。 对于每个指标,这些结构数组的所有值都可以显示在日志当中。 若指标不同,则只有那些指标类型固有的参数值数据才可保存在数组单元之中。 但若几个指标类型相同,且其属性与目的相等,而只是数值不同,则可在数组单元里指定。
因此,可为每个指标编写一个在日志中显示一组指标参数,并设置数值的方法。 这仅适用于标准指标,因为对于它们来说,我们肯定了解每个特定指标的一组参数。

而这只是一个虚拟方法。 在 \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) {;}

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

在该类中,此方法不执行任何操作。 在日志中显示指标数据的方法将在衍生对象中完成实现。
每个衍生类将拥有自己的方法,因为每个指标都有它自己的固有参数集。

在封闭的参数型构造函数中,替换获取指标类型描述的代码

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

通过上述新服务汉书来获得描述

//+------------------------------------------------------------------+
//| 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 for the object
   this.m_type=COLLECTION_INDICATORS_ID;
//--- Write description of indicator type
   this.m_ind_type_description=IndicatorTypeDescription(ind_type);
//--- If parameter array size 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;
  }
//+------------------------------------------------------------------+

在日志中显示指标属性的方法中,在显示了所有指标对象属性之后,在清单末尾添加调用在日记中显示指标参数集并为其设置数值的方法

//+------------------------------------------------------------------+
//| 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");
  }
//+------------------------------------------------------------------+

现在,如果抽象指标基准对象的衍生类中存在 PrintPatameters() 方法,则当调用 Print() 方法时,会从衍生类中调用 PrintPatameters() 虚方法,故应在该类中实现在日志中显示指标参数。

鉴于每种指标类型都有其自己的参数集,因此在每个衍生类中,我们必须实现相应的 PrintPatameters() 方法。
已经为每个指标对象编写了此类方法。 它们在逻辑上都属于同一类型,但内涵不同。 应为所有指标均编写这些方法,除了一个 - 自定义指标。因为针对该指标的方法实现会有所不同,因其无法与其它标准指标形成对比,故无法提前知道指标参数集。
我们来分析每个衍生对象的这些方法。

指标对象加速振荡器(Accelerator Oscillator)的类:

//+------------------------------------------------------------------+
//|                                                        IndAC.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\\IndicatorDE.mqh"
//+------------------------------------------------------------------+
//| Standard indicator Accelerator Oscillator                        |
//+------------------------------------------------------------------+
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 indicator supports a passed                     |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CIndAC::SupportProperty(ENUM_INDICATOR_PROP_INTEGER property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Return 'true' if indicator supports a 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(),"]");
  }
//+------------------------------------------------------------------+

此处,仅声明了显示参数的虚方法,且该方法为空,因为 AC 指标没有输入项。 由于此处缺少衍生类中的虚方法,故可在此处省略,直接调用父类的虚方法。 但我们在这里编写它,是为了在衍生类里拥有相同的方法结构。
在指标简述的显示方法中,添加显示为此对象所创建的指标句柄。 在此方法中进行的此类秀嘎i,对所有指标对象都其起作用。 我们不会进一步分析它们。 代之,我们仅分析指标参数描述的显示方法。

指标对象建仓/派发的类,及其显示指标参数说明的方法

//+------------------------------------------------------------------+
//|                                                        IndAD.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\\IndicatorDE.mqh"
//+------------------------------------------------------------------+
//| Standard indicator Accumulation/Distribution                     |
//+------------------------------------------------------------------+
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 indicator supports a passed                     |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CIndAD::SupportProperty(ENUM_INDICATOR_PROP_INTEGER property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| Return 'true' if indicator supports a 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));
  }
//+------------------------------------------------------------------+

建仓/派发指标仅有一个输入 - 交易量计算类型。 因此,输入结构的数组或许仅有一个存储该参数的单元。 它们分别将我们从结构的整数型数据中按索引 0 从数组中提取的数据显示在日志当中,并利用上述服务函数将其发送到日志。 在显示参数值之前,需用来自先前编写的函数库文本消息对其进行描述

此外,仅分析显示指标输入之说明描述的方法。

显示“平均方向指数”指标对象的类参数说明的方法:

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

ADX 指标拥有一个输入项,即计算周期。 因此,仅需以相同的方式显示从结构的整数型数据中索引为 0 的一个数组单元的描述

显示鳄嘴指标对象类参数说明的方法:

//+------------------------------------------------------------------+
//| 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)
          );
  }
//+------------------------------------------------------------------+

鳄嘴指标拥有八个输入项,因此在创建指标时按照其顺序依次显示它们:

因此,所有数据都是整数类型,因此可显示来自相应数组单元的整数型结构数据说明。
最后一个参数可以存储构建指标所依据的价格类型,或在其上构建鳄嘴数据的指标句柄。 因此,首先检查数组单元中的值。 如果该值小于 10 ,则表示该指标建立在一种可能的价格类型上,并显示价格类型的描述。 如果该值等于或大于 10,则表示该指标是建立在另一个指标的数据上的,该指标的句柄已写入数组 - 显示该指标的句柄值

显示轨迹线指标对象的类指标参数说明的方法:

//+------------------------------------------------------------------+
//| 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));
   
  }
//+------------------------------------------------------------------+

轨迹线指标具有五个输入项。 最后一个参数 “Deviation of channel borders from the central line” 为实数型。 因此,为了显示此参数描述,从输入结构的实数型数据中取值

指标对象类的所有剩余方法都重复上述研究的逻辑,故我们不再分析其方法。 您可在本文所附的文件中找到并研究它们。

将所有创建的指标对象放置到指标集合列表当中。 在终端里,当创建任意数量拥有相同参数的指标时,实际上只会创建一个指标,并且所有调用都会指向它。 因此,若要创建指标对象并将其放置到集合列表中,我们必须控制集合列表是否已含有我们要放入列表中的指标,其类型、品种/时间帧和参数均相同。 这很简单,因为绝对相同的指标对象,在创建指标时它们的句柄相同。 这意味着这是一个相同的指标对象。

在文件 \MQL5\Include\DoEasy\Collections\IndicatorsCollection.mqh 里的指标对象集合类中加入必要的修改,在上一篇文章中我们已开始实现它。 为了搜索和比较两个指标对象,我们会利用类的 Search() 方法,而这些类应取自指向来自函数库标准发行包的 CObject 类和其衍生类实例的指针的动态数组。 但这个方法不能绝对判定包含结构的两个对象是否相等。 此方法的目的是比较两个相同类型对象的一个指定属性。 在指标对象中利用 MqlParam 指标参数结构数组,数组需逐元素比较结构的每个属性。 幸运的是,在所有函数库对象中,我们都有一个默认方法 IsEqual(),可精确比较两个相同类型的对象。 为了比较两个相同类型的对象是否相等,会调用该方法。

在类的私密部分声明返回集合列表中指标对象索引的方法:

//+------------------------------------------------------------------+
//| Indicator collection                                             |
//+------------------------------------------------------------------+
class CIndicatorsCollection : public CObject
  {
private:
   CListObj                m_list;                       // Indicator object list
   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:

在类主体清单的最后,声明两个公开方法 - 显示指标对象的完整和简短说明

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

//--- Constructor
                           CIndicatorsCollection();

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

在类主体之外实现所声明的方法。

在日志中显示完整集合说明的方法:

//+------------------------------------------------------------------+
//| Display full collection description in 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();
     }
  }
//+------------------------------------------------------------------+

In a loop by collection list get another indicator object and display its full description in the journal.

The method displaying the short collection description in the journal:

//+------------------------------------------------------------------+
//| 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();
     }
  }
//+------------------------------------------------------------------+

循环遍历集合列表获取另一个指标对象,并且在日志中显示其简述

返回集合列表中指标对象索引的方法
:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

循环遍历集合列表 获取另一个指标对象与指标对象进行比较传递给方法的指针如果对象相等,则返回循环索引。 循环完成后(如果所有对象都不相等)返回 -1

在创建新指标对象、和将对象置于集合列表中的所有方法中,都进行了相同的修改。 这样做是为了排除在对象创建失败,或列表放置失败时可能发生的内存泄漏。

我们以加速振荡器指标创建方法为例:

//+------------------------------------------------------------------+
//| 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;
  }
//+------------------------------------------------------------------+

Check availability of indicator object in the list by its index.
如果索引超出 -1
,则表示它在列表中,且新创建的对象必须被删除
如果列表中不存在这样的指标,且出于某种原因我们未能将其添加到集合列表中
,则删除新创建的对象
如果没有将新对象成功放置到集合列表中,这将排除内存泄漏的情况。

在创建指标对象的所有方法中均进行了此类修改,故我们不再对其进行分析。 您可在本文所附的文件中找到并研究它们。

为了在集合列表中搜索指标对象,并从该列表中获取指向它的指针,我们需要返回指向所需对象指针的方法。 为了该方法传递必要指标的类型,包括其品种、时间帧和参数(对于每个指标,其参数都应与指标类型相对应)。 在最后,必须能得到在列表中发现的指向指标对象的指针。

在上一篇文章中,我编写了一个方法来获取指向加速器振荡器指标的指针。 这是最简单的一种,因为 AC 指标没有输入项,我们只需要通过品种和时间帧即可找到所需的对象

//+------------------------------------------------------------------+
//| Return pointer to indicator object Accelerator Oscillator        |
//+------------------------------------------------------------------+
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));
  }
//+------------------------------------------------------------------+

为了搜索指标可能的输入项,我将创建含有设置参数的临时指标对象,并在集合列表中搜索匹配项

//+------------------------------------------------------------------+
//| Return pointer to indicator object                               |
//| Accumulation/Distribution                                        |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

如果在列表中找到该对象,则返回其索引。 如果没有 - 则返回 -1。

返回列表中指标对象指针的其余方法与上述方法相同,但它们含有创建指标对象的其他参数。 例如,若要返回指向鳄嘴指标对象的指针,创建由八个参数组成的数组:

//+------------------------------------------------------------------+
//| Return pointer to indicator object Alligator                     |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

其余所有内容与上述研究的返回指向建仓/派发指标对象的方法相同。
在每种方法中,都必须删除临时指标对象。 它只是作为引用,对应于在集合列表中搜索到的匹配项。

我不再分析其余类似的方法。 它们与刚才我们研究的两种方法相同。

在本文框架内,类的改进至此完毕。


测试 EA

为了在 EA 中执行创建指标的测试,取用来自第三十九篇文章中的测试 EA
并将其保存在新文件夹 \MQL5\Experts\TestDoEasy\Part55\,新命名为 TestDoEasyPart55.mq5

主要是,改进很轻微。 在之前的一篇文章中,我在测试器里将 EventsHandling() 中的事件处理函数移到了函数库中 - 文件 Engine.mqh。 因此,从 EA 代码里删除此函数,并在处理程序 OnTick() 中替换调用它的代码从 EA 文件

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

从函数库调用:

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

从处理程序 OnTick() 中删除代码模块,该处理程序将在当前柱线图表上显示数据注释:

//--- Get the zero bar of the current timeseries
   CBar *bar=engine.SeriesGetBar(NULL,PERIOD_CURRENT,0);
   if(bar==NULL)
      return;
//--- Create parameters string 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);

因此,“新的即时报价”事件处理程序将如下所示:

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

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

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

在函数库初始化函数 OnInitDoEasy() 中,显示已用品种列表的模块里替换品种数量按数值设置

//--- 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   

宏替换中指定的品种数量

//--- 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   

鉴于在 MetaTrader 5 中,从版本 2430 开始修改了操作品种的总数,且函数库会检查该数字,并自动设置在文件 \MQL5\Include\DoEasy\Defines.mqh 中声明的宏替换 SYMBOLS_COMMON_TOTAL。

仅为了检查指标对象的创建,临时创建两个类型相同,但参数不同的指标。 在我们的程序中尚未实现正常创建指标之前,简单地在函数库初始化函数中创建它们

//+------------------------------------------------------------------+
//| 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="\nThe number of symbols on server "+(string)total+".\nMaximal 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("--- 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(". Number of used symbols: ",". 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("The sound of a falling coin 1"),sound_array_coin_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_02",TextByLanguage("Sound fallen coins"),sound_array_coin_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_03",TextByLanguage("Sound of coins"),sound_array_coin_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_coin_04",TextByLanguage("The sound of a falling coin 2"),sound_array_coin_04);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_01",TextByLanguage("Click on the button sound 1"),sound_array_click_01);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_02",TextByLanguage("Click on the button sound 1"),sound_array_click_02);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_click_03",TextByLanguage("Click on the button sound 1"),sound_array_click_03);
   engine.CreateFile(FILE_TYPE_WAV,"sound_array_cash_machine_01",TextByLanguage("The sound of the 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 (set 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 to 100 points
         symbol.SetControlBidInc(100000*symbol.Point());
        //--- Set control of the symbol price decrease to 100 points
         symbol.SetControlBidDec(100000*symbol.Point());
        //--- Set control of the symbol spread increase to 40 points
         symbol.SetControlSpreadInc(400);
        //--- Set control of the symbol spread decrease to 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));
  }
//+------------------------------------------------------------------+

此处,在当前品种和时间帧上我们创建了两个“自适应移动平均线”指标,但是拥有不同的输入值

编译 EA 并在终端图表上启动它。
初始化之后,“智能系统”日志显示有关函数库的初始化消息。 两个创建的指标的参数,它们当中分别拥有完整简洁参数列表:

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

尽管指标类型只有一种 - AMA,但由于创建时指标参数不同,因此创建了该指标的两个句柄。 因此,这是两个不同的指标 - 每个都有其句柄。 两个指标对象分别创建,并将其放置在指标集合当中。

期间,我们只能以不同的参数创建不同指标。 但若要在 EA 中应用它们,必须准备存储其数据的区域。 从该区域,可以接收任何所需参数组合的数据,并将其用于程序中,以便制定决策,或获取统计数据。 自下一篇文章开始,我将进行所有这些事情。

下一步是什么?

在下一篇文章中,将开始实现在 EA 中存储数据,并从指标对象接收数据。

函数库当前版本的所有文件与 MQL5 的测试 EA 文件一并附于文后。 您可以下载它们,并测试所有内容。
请注意,目前指标集合类正在开发当中,因此,强烈建议不要在程序中使用它。
请在文章的评论中留下您的评论、问题和建议。

返回内容目录

该系列中的先前文章:

DoEasy 函数库中的时间序列(第三十五部分):柱线对象和品种时间序列列表
DoEasy 函数库中的时间序列(第三十六部分):所有用到的品种周期的时间序列对象
DoEasy 函数库中的时间序列(第三十七部分):时间序列集合 - 按品种和周期的时间序列数据库
DoEasy 函数库中的时间序列(第三十八部分):时间序列集合 - 实时更新以及从程序访问数据
DoEasy 函数库中的时间序列(第三十九部分):基于函数库的指标 - 准备数据和时间序列事件
DoEasy 函数库中的时间序列(第四十部分):基于函数库的指标 - 实时刷新数据
DoEasy 函数库中的时间序列(第四十一部分):多品种多周期指标样品
DoEasy 函数库中的时间序列(第四十二部分):抽象指标缓冲区对象类
DoEasy 函数库中的时间序列(第四十三部分):指标缓冲区对象类
DoEasy 函数库中的时间序列(第四十四部分):指标缓冲区对象集合类
DoEasy 函数库中的时间序列(第四十五部分):多周期指标缓冲区
DoEasy 函数库中的时间序列(第四十六部分):多周期、多品种指标缓冲区
DoEasy 函数库中的时间序列(第四十七部分):多周期、多品种标准指标
DoEasy 函数库中的时间序列(第四十八部分):在单一子窗口里基于一个缓冲区的多周期、多品种指标
DoEasy 函数库中的时间序列(第四十九部分):多周期、多品种、多缓冲区标准指标
DoEasy 函数库中的时间序列(第五十部分):多周期、多品种带位移的标准指标
DoEasy 函数库中的时间序列(第五十一部分):复合多周期、多品种标准指标
DoEasy 函数库中的时间序列(第五十二部分):多周期、多品种单缓冲区标准指标的跨平台性质
DoEasy 函数库中的时间序列(第五十三部分):抽象基准指标类
DoEasy 函数库中的时间序列(第五十四部分):抽象基准指标类的衍生类