DoEasy 函数库中的时间序列(第六十一部分):品种即时报价序列集合

Artyom Trishkin | 24 三月, 2021

内容


概述

在上一篇文章中,我已创建了即时报价数据列表对象类,以便收集和存储指定天数的品种即时报价数据。 鉴于程序在其运行时可能会用到不同的品种,因此应为每个品种创建一个单独的列表。 在本文中,我将把这些列表合并到一个即时报价数据集合。 这将是一个常规列表,基于指向标准库 CObject class 类及其衍生类实例指针的动态数组。 该列表是为了存储为每个品种创建的即时报价数据列表的指针,其对象类我已在上一篇文章中准备好了。

其概念与之前在函数库中构造集合类的概念相同。 这可令我们能够在统计分析中保存、存储、更新、接收和运用函数库数据中存在的任何品种的即时报价数据。


即时报价数据类的集合

在 \MQL5\Include\DoEasy\Collections\ 中,创建一个名为 TickSeriesCollection.mqh 的新即时报价数据集合类文件。

该类是函数库所有对象的基准对象类的衍生类。

我们看一下类主体,并分析其变量和方法:

//+------------------------------------------------------------------+
//|                                         TickSeriesCollection.mqh |
//|                        Copyright 2021, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Objects\Ticks\TickSeries.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
//+------------------------------------------------------------------+
//| Collection of symbol tick series                                 |
//+------------------------------------------------------------------+
class CTickSeriesCollection : public CBaseObj
  {
private:
   CListObj                m_list;                                   // List of used symbol tick series
//--- Return the tick series index by symbol name
   int                     IndexTickSeries(const string symbol);
public:
//--- Return (1) itself and (2) tick series collection list and (3) the number of tick series in the list
   CTickSeriesCollection  *GetObject(void)                              { return &this;               }
   CArrayObj              *GetList(void)                                { return &this.m_list;        }
   int                     DataTotal(void)                        const { return this.m_list.Total(); }
//--- Return the pointer to the tick series object (1) by symbol and (2) by index in the list
   CTickSeries            *GetTickseries(const string symbol);
   CTickSeries            *GetTickseries(const int index);
//--- Create a collection list of symbol tick series
   bool                    CreateCollection(const CArrayObj *list_symbols,const uint required=0);
//--- Set the flag of using the tick series of (1) a specified symbol and (2) all symbols
   void                    SetAvailableTickSeries(const string symbol,const bool flag=true);
   void                    SetAvailableTickSeries(const bool flag=true);
//--- Return the flag of using the tick series of (1) a specified symbol and (2) all symbols
   bool                    IsAvailableTickSeries(const string symbol);
   bool                    IsAvailableTickSeries(void);

//--- Set the number of days of the tick history of (1) a specified symbol and (2) all symbols
   bool                    SetRequiredUsedDays(const string symbol,const uint required=0);
   bool                    SetRequiredUsedDays(const uint required=0);

//--- Return the last tick object of a specified symbol (1) by index, (2) by time and (4) by time in milliseconds
   CDataTick              *GetTick(const string symbol,const int index);
   CDataTick              *GetTick(const string symbol,const datetime tick_time);
   CDataTick              *GetTick(const string symbol,const long tick_time_msc);

//--- Return the new tick flag of a specified symbol
   bool                    IsNewTick(const string symbol);

//--- Create a tick series of (1) a specified symbol and (2) all symbols
   bool                    CreateTickSeries(const string symbol,const uint required=0);
   bool                    CreateTickSeriesAll(const uint required=0);
//--- Update (1) a tick series of a specified symbol and (2) all symbols
   void                    Refresh(const string symbol);
   void                    Refresh(void);

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

类的成员变量 m_list 属于 CListObj 类型 — 它是标准库 CArrayObj 类的衍生类,就像在函数库中创建的许多其他列表一样。 CListObj 类的唯一目的是实现标准库对象基类 CObject 类的 Type() 虚拟方法操作。 该方法应返回类的类型 ID。 在这种情况下,它是数组类型 ID。
在 CListObj 类中实现 Type() 虚拟方法,该类在很久以前已添加到函数库之中:

//+------------------------------------------------------------------+
//|                                                      ListObj.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
//+------------------------------------------------------------------+
//| Class of collection lists                                        |
//+------------------------------------------------------------------+
class CListObj : public CArrayObj
  {
private:
   int               m_type;                    // List type
public:
   void              Type(const int type)       { this.m_type=type;     }
   virtual int       Type(void)           const { return(this.m_type);  }
                     CListObj()                 { this.m_type=0x7778;   }
  };
//+------------------------------------------------------------------+

此处,Type() 方法将传递的数值设置给 m_type 变量,而 Type() 虚方法返回此变量设置的值 。

默认情况下(在类构造函数中),该变量接收数组类型 ID 的值与 CArrayObj 相同 — 0x7778。

所有类方法的目的在代码注释中进行了描述。 我将在下面讲述这些方法的实现。

在类的构造函数中清除列表为其设置已排序列表标志,并为其定义即时报价数据集合列表 ID

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTickSeriesCollection::CTickSeriesCollection()
  {
   this.m_list.Clear();
   this.m_list.Sort();
   this.m_list.Type(COLLECTION_TICKSERIES_ID);
  }
//+------------------------------------------------------------------+

IndexTickSeries() 私密方法按品种名称返回即时报价序列索引:

//+------------------------------------------------------------------+
//| Return the tick series index by symbol name                      |
//+------------------------------------------------------------------+
int CTickSeriesCollection::IndexTickSeries(const string symbol)
  {
   const CTickSeries *obj=new CTickSeries(symbol==NULL || symbol=="" ? ::Symbol() : symbol);
   if(obj==NULL)
      return WRONG_VALUE;
   this.m_list.Sort();
   int index=this.m_list.Search(obj);
   delete obj;
   return index;
  }
//+------------------------------------------------------------------+

该方法接收品种名称,并从列表中返回其即时报价序列索引。
接着,创建一个即时报价序列的临时空对象。 它是传递给方法的品种名称
设置已排序列表标志
,并在列表中搜索对象索引
接着,删除临时对象,然后返回获得的索引。 如果未找到对象,或创建临时对象失败,则该方法返回 NULL

该方法依据品种返回指向即时报价序列对象的指针:

//+------------------------------------------------------------------+
//| Return the object of tick series of a specified symbol           |
//+------------------------------------------------------------------+
CTickSeries *CTickSeriesCollection::GetTickseries(const string symbol)
  {
   int index=this.IndexTickSeries(symbol);
   return this.m_list.At(index);
  }
//+------------------------------------------------------------------+

该方法接收品种名称,并从列表中返回其即时报价序列对象。
我们用刚刚讲述的方法在列表中找到即时报价序列对象的索引依据找到的索引获取指向该对象的指针并返回。 如果找不到索引,则该索引等于 -1,而 CArrayObj 类的 At() 方法返回 NULL

该方法为指定品种的即时报价序列设置标志:

//+------------------------------------------------------------------+
//| Set the flag of using the tick series of a specified symbol      |
//+------------------------------------------------------------------+
void CTickSeriesCollection::SetAvailableTickSeries(const string symbol,const bool flag=true)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return;
   tickseries.SetAvailable(flag);
  }
//+------------------------------------------------------------------+

该方法接收品种名称,其即时报价序列应接收用法标记。<br0 /> 利用上述的 GetTickseries() 方法,从列表中获取指向即时报价序列对象的指针,然后依据传递给该方法的标志设置

该方法为所有品种集合的即时报价序列设置用法标志:

//+------------------------------------------------------------------+
//| Set the flag of using the tick series of all symbols             |
//+------------------------------------------------------------------+
void CTickSeriesCollection::SetAvailableTickSeries(const bool flag=true)
  {
   for(int i=0;i<this.m_list.Total();i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      tickseries.SetAvailable(flag);
     }
  }
//+------------------------------------------------------------------+

按列表中即时报价序列的总数进行循环依据循环索引获取下一个即时报价序列对象,并按传递给该方法的标志设置

该方法返回指定品种的即时报价序列的标志:

//+------------------------------------------------------------------+
//| Return the flag of using the tick series of a specified symbol   |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::IsAvailableTickSeries(const string symbol)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return false;
   return tickseries.IsAvailable();
  }
//+------------------------------------------------------------------+

该方法接收品种名称,并返回其即时报价序列对象的用法标志。
利用 GetTickseries() 方法获取指向所需品种的即时报价序列对象的指针,然后返回为此对象设置的用法标志。 如果从列表中获取对象失败,则该方法返回 false

该方法返回所有品种的即时报价序列用法标志:

//+------------------------------------------------------------------+
//| Return the flag of using tick series of all symbols              |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::IsAvailableTickSeries(void)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      res &=tickseries.IsAvailable();
     }
   return res;
  }
//+------------------------------------------------------------------+

声明 res 变量,并使用 true 值对其进行初始化。
接下来,按列表中对象的总数循环获取指向下一个即时报价序列对象的指针,并添加为当前对象定义的用法标志 res 变量
直至循环完成,返回获得的 res 值。

如果列表中至少有一个对象未能设置用法标志( false ),则 res 在循环完成时其值为false。 因此,该方法能够知道是否已为所有即时报价序列设置了用法标记。 仅当集合中每个即时报价序列对象的用法标志均已设为 true 时,才返回 True

该方法为指定品种的即时报价历史记录设置天数:

//+------------------------------------------------------------------+
//| Set the number of days of the tick history of a specified symbol |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::SetRequiredUsedDays(const string symbol,const uint required=0)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return false;
   tickseries.SetRequiredUsedDays(required);
   return true;
  }
//+------------------------------------------------------------------+

该方法接收品种名称,并为其设置即时报价数据天数。
利用之前研究的方法获取即时报价序列对象的指针,为其设置天数 ,并返回 true
如果从列表中获取即时报价序列对象指针失败,则该方法返回 false

该方法为所有品种的即时报价历史记录设置天数:

//+------------------------------------------------------------------+
//| Set the number of days of the tick history of all symbols        |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::SetRequiredUsedDays(const uint required=0)
  {
   bool res=true;
   for(int i=0;i<this.m_list.Total();i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
        {
         res &=false;
         continue;
        }
      tickseries.SetRequiredUsedDays(required);
     }
   return res;
  }
//+------------------------------------------------------------------+

声明 res 变量,并使用 true 值对其进行初始化。
接着,按列表中对象的总数进行循环获取指向下一个即时报价序列对象的指针,且 如果未能获得指向该对象的指针,则 res 变量将置为 false。 接下来,移至集合列表中的下一个对象
否则,为当前对象的即时报价数据设置天数
直至循环完成,返回获得的 res 值。

如果列表中至少有一个对象未能设置即时报价数据天数,则 res 在循环完成时置为 false。 因此,该方法能够为集合中所有即时报价序列设置天数,且仅当列表中存储的每个即时报价数据对象均设置天数完毕,才返回成功执行。

该方法依据即时报价序列列表中的索引返回指定品种的即时报价对象:

//+------------------------------------------------------------------+
//| Return the tick object of the specified symbol by index          |
//+------------------------------------------------------------------+
CDataTick *CTickSeriesCollection::GetTick(const string symbol,const int index)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return NULL;
   return tickseries.GetTickByListIndex(index);
  }
//+------------------------------------------------------------------+

该方法传递 CTickSeries 类的品种即时报价序列,以及存储在即时报价序列列表中的所需即时报价对象的索引。
利用前面讲述的 GetTickseries() 方法从品种集合中获取指向即时报价序列对象的指针,并利用我在上一篇文章中讨论过的方法 GetTickByListIndex() 从即时报价序列列表中返回指向即时报价对象的指针

如果获取即时报价序列对象失败,则该方法返回 NULL。 CTickSeries 类的 GetTickByListIndex() 方法也可能返回 NULL

该方法从即时报价序列列表中按时间返回指定品种的最后一个即时报价对象:

//+------------------------------------------------------------------+
//| Return the last tick object of the specified symbol by time      |
//+------------------------------------------------------------------+
CDataTick *CTickSeriesCollection::GetTick(const string symbol,const datetime tick_time)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return NULL;
   return tickseries.GetTick(tick_time);
  }
//+------------------------------------------------------------------+

该方法传递 CTickSeries 类的品种即时报价序列,以及存储在即时报价序列列表中的所需即时报价对象的时间。
利用前面讲述的 GetTickseries() 方法从品种集合中获取即时报价序列对象的指针,并利用我在上一篇文章中讨论过的方法 GetTick() 从即时报价序列列表中返回指向即时报价对象的指针

如果获取即时报价序列对象失败,则该方法返回 NULL。 此外,CTickSeries 类的 GetTick() 方法也可能返回 NULL

该方法从即时报价序列列表中按时间(以毫秒为单位)返回指定品种的最后一个即时报价对象:

//+------------------------------------------------------------------+
//| Return the last tick object of the specified symbol              |
//| by time in milliseconds                                          |
//+------------------------------------------------------------------+
CDataTick *CTickSeriesCollection::GetTick(const string symbol,const long tick_time_msc)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return NULL;
   return tickseries.GetTick(tick_time_msc);
  }
//+------------------------------------------------------------------+

该方法接收 CTickSeries 类的品种即时报价序列,以及存储在即时报价序列列表中的所需即时报价对象的时间(以毫秒为单位)。
利用前面讲述的 GetTickseries() 方法从品种集合中获取即时报价序列对象的指针,并利用我在上一篇文章中讨论过的方法 GetTick() 从即时报价序列列表中返回指向即时报价对象的指针

如果获取即时报价序列对象失败,则该方法返回 NULL。 此外,CTickSeries 类的 GetTick() 方法也可能返回 NULL

后两个方法可能会按时间返回多个即时报价,它们的时间相似,因此 CTickSeries 类的 GetTick() 方法只返回它们当中的最后一个(最晚的时间),因为这个相关性最强。

该方法返回指定品种的新即时报价标志:

//+------------------------------------------------------------------+
//| Return the new tick flag of a specified symbol                   |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::IsNewTick(const string symbol)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return false;
   return tickseries.IsNewTick();
  }
//+------------------------------------------------------------------+

该方法接收品种的名称,并返回出现新即时报价的标志。
利用前面讲述的 GetTickseries() 方法从品种集合中获取即时报价序列对象的指针,并利用我在上一篇文章中讨论过的方法 IsNewTick() 返回即时报价序列的新即时报价标志
如果获取即时报价序列对象失败,则该方法返回 false

CTickSeries 类中尚未实现此能力。 这将在后续的文章中完成。

该方法创建指定品种的即时报价序列:

//+------------------------------------------------------------------+
//| Create a tick series of a specified symbol                       |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::CreateTickSeries(const string symbol,const uint required=0)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return false;
   return(tickseries.Create(required)>0);
  }
//+------------------------------------------------------------------+

该方法将接收需为其创建即时报价序列的品种名称,以及即时报价数据天数。
利用前面讲述的 GetTickseries() 方法从品种集合中获取指向即时报价序列对象的指针,并返回 CTickSeries 类的 Create() 方法返回值大于零的指示标志(已创建即时报价对象的数量不为零)。

该方法为所有用到的品种创建即时报价序列:

//+------------------------------------------------------------------+
//| Create tick series of all symbols                                |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::CreateTickSeriesAll(const uint required=0)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      res &=(tickseries.Create(required)>0);
     }
   return res;
  }
//+------------------------------------------------------------------+

该方法接收即时报价数据天数。
声明 res 变量,并以 true 值对其进行初始化。
接下来,按列表中对象的总数进行循环获取指向下一个即时报价序列对象的指针,并 res 变量里设置 CTickSeries 类的 Create() 方法返回值大于零(已创建即时报价序列)的指示标志。
直至循环完毕,返回获得的 res 值。

如果列表中至少有一个对象未能创建即时报价序列,则 res 在循环完成时置为 false。 因此,该方法能够为所有品种创建即时报价序列集合,且仅当列表中存储的每个即时报价数据对象均创建了即时报价序列时,才返回成功执行。

该方法更新指定品种的即时报价序列:

//+------------------------------------------------------------------+
//| Update a tick series of a specified symbol                       |
//+------------------------------------------------------------------+
void CTickSeriesCollection::Refresh(const string symbol)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return;
   tickseries.Refresh();
  }
//+------------------------------------------------------------------+

该方法接收一个需更新其即时报价序列的品种名称。
利用前面讲述的 GetTickseries() 方法从品种集合中获取即时报价序列对象的指针,并利用 CTickSeries 类的 Refresh() 方法更新它

该方法更新所有品种的即时报价序列:

//+------------------------------------------------------------------+
//| Update tick series of all symbols                                |
//+------------------------------------------------------------------+
void CTickSeriesCollection::Refresh(void)
  {
   for(int i=0;i<this.m_list.Total();i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      tickseries.Refresh();
     }
  }
//+------------------------------------------------------------------+

按列表中对象总数进行循环 获取指向下一个即时报价序列对象的指针依据循环索引利用 CTickSeries 类的 Refresh() 方法更新序列

CTickSeries 类中尚未实现更新即时报价序列。 这将在后续的文章中完成。

该方法将完整的集合列表反馈到日志:

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

按列表中的对象总数进行循环依据循环索引获取指向下一个即时报价序列对象的指针,并在日志中显示即时报价序列的完整说明

该方法将集合列表简要反馈到日志:

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

按列表中对象总数进行循环按循环索引获取指向下一个即时报价序列对象的指针,并在日志中显示即时报价序列说明的简述

上面研究的方法旨在操控已创建集合列表指向的即时报价数据对象。 我们的程序也许会用到不同的品种。 以下方法用于创建集合对象本身,以便将所有必需的即时报价序列放置在其中,并从集合列表中获取相应指针,并操控它们。

该方法创建品种的即时报价序列集合列表:

//+------------------------------------------------------------------+
//| Create a collection list of symbol tick series                   |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::CreateCollection(const CArrayObj *list_symbols,const uint required=0)
  {
//--- If an empty list of symbol objects is passed, exit
   if(list_symbols==NULL)
      return false;
//--- Get the number of symbol objects in the passed list
   int total=list_symbols.Total();
//--- Clear the tick series collection list
   this.m_list.Clear();
//--- In a loop by all symbol objects
   for(int i=0;i<total;i++)
     {
      //--- get the next symbol object
      CSymbol *symbol_obj=list_symbols.At(i);
      //--- if failed to get a symbol object, move on to the next one in the list
      if(symbol_obj==NULL)
         continue;
      //--- Create a new empty tick series object
      CTickSeries *tickseries=new CTickSeries();
      //--- If failed to create the tick series object, move on to the next symbol in the list
      if(tickseries==NULL)
         continue;
      //--- Set a symbol name for a tick series object
      tickseries.SetSymbol(symbol_obj.Name());
      //--- Set the sorted list flag for the tick series collection list
      this.m_list.Sort();
      //--- If the object with the same symbol name is already present in the tick series collection list, remove the tick series object
      if(this.m_list.Search(tickseries)>WRONG_VALUE)
         delete tickseries;
      //--- otherwise, there is no object with such a symbol name in the collection yet
      else
        {
         //--- Set the number of tick data days for a tick series object
         tickseries.SetRequiredUsedDays(required);
         //--- if failed to add the tick series object to the collection list, remove the tick series object
         if(!this.m_list.Add(tickseries))
            delete tickseries;
        } 
     }
//--- Return the flag indicating that the created collection list has a size greater than zero
   return this.m_list.Total()>0;
  }
//+------------------------------------------------------------------+

该方法非常简单 — 接收程序用到的品种列表(该列表已经存在很长时间了,并已在创建品种时间序列集合时用过它了)。 接下来,按品种总数进行循环,创建一个新的即时报价序列对象,并依据品种列表当前循环位置的为其设置其品种名称。 如果列表中还没有含有此品种的即时报价序列对象,则设置传递给该方法的即时报价数据天数,然后将该对象添加到集合列表之中。 针对列表中的每个品种均应执行此操作。 在此,我简要概述了该系统。 如果您深入挖掘,将看到成功创建、以及将即时报价序列对象添加到列表中的检查,并在需要时删除不必要的对象。 整个方法的逻辑在其清单中进行了详细描述。 我把它留给您分析。

CEngine 主库类用于将创建的集合与“外部世界”连接。
该类存储在 \MQL5\Include\DoEasy\Engine.mqh 当中。

将新创建的类文件与它相连:

//+------------------------------------------------------------------+
//|                                                       Engine.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"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
#include "Collections\ResourceCollection.mqh"
#include "Collections\TimeSeriesCollection.mqh"
#include "Collections\BuffersCollection.mqh"
#include "Collections\IndicatorsCollection.mqh"
#include "Collections\TickSeriesCollection.mqh"
#include "TradingControl.mqh"
//+------------------------------------------------------------------+

在该类的私密部分中,声明即时报价序列集合类对象

//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Collection of historical orders and deals
   CMarketCollection    m_market;                        // Collection of market orders and deals
   CEventsCollection    m_events;                        // Event collection
   CAccountsCollection  m_accounts;                      // Account collection
   CSymbolsCollection   m_symbols;                       // Symbol collection
   CTimeSeriesCollection m_time_series;                  // Timeseries collection
   CBuffersCollection   m_buffers;                       // Collection of indicator buffers
   CIndicatorsCollection m_indicators;                   // Indicator collection
   CTickSeriesCollection m_tick_series;                  // Collection of tick series
   CResourceCollection  m_resource;                      // Resource list
   CTradingControl      m_trading;                       // Trading management object
   CPause               m_pause;                         // Pause object
   CArrayObj            m_list_counters;                 // List of timer counters

该类拥有 SetUsedSymbols() 方法,允许我们设置要在程序中用到的品种列表。
在函数库中加入应传递的即时报价数据的天数:

//--- Set the list of used symbols in the symbol collection and create the collection of symbol timeseries
   bool                 SetUsedSymbols(const string &array_symbols[],const uint required=0);

默认情况下,将传递零(表示一天),并以 TICKSERIES_DEFAULT_DAYS_COUNT 常量在 \MQL5\Include\DoEasy\Defines.mqh 中进行设置。

在方法实现中,添加即时报价序列集合的创建

//+------------------------------------------------------------------+
//| Set the list of used symbols in the symbol collection            |
//| and create the symbol timeseries collection                      |
//+------------------------------------------------------------------+
bool CEngine::SetUsedSymbols(const string &array_symbols[],const uint required=0)
  {
   bool res=this.m_symbols.SetUsedSymbols(array_symbols);
   CArrayObj *list=this.GetListAllUsedSymbols();
   if(list==NULL)
      return false;
   res&=this.m_time_series.CreateCollection(list);
   res&=this.m_tick_series.CreateCollection(list,required);
   return res;
  }
//+------------------------------------------------------------------+

现在,从程序中调用该方法时,将创建两个集合(时间序列和即时报价序列集合)。

在类的公开部分中,添加从自定义程序里访问即时报价序列集合类的方法

//--- Copy the specified double property of the specified timeseries of the specified symbol to the array
//--- Regardless of the array indexing direction, copying is performed the same way as copying to a timeseries array
   bool                 SeriesCopyToBufferAsSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_BAR_PROP_DOUBLE property,
                                                   double &array[],const double empty=EMPTY_VALUE)
                          { return this.m_time_series.CopyToBufferAsSeries(symbol,timeframe,property,array,empty);}


//--- Return (1) the tick series collection, (2) the list of tick series from the tick series collection
   CTickSeriesCollection *GetTickSeriesCollection(void)                       { return &this.m_tick_series;                                     }
   CArrayObj           *GetListTickSeries(void)                               { return this.m_tick_series.GetList();                            }


//--- Return (1) the buffer collection and (2) the buffer list from the collection 

对于目前,把即时报价序列集合对象本身集合列表返回到程序就足够了。

当前,这就是我们创建即时报价序列集合所作的全部。


测试

为了测试针对程序操作的品种,创建即时报价序列集合,我从上一篇文章中获取 EA,并将其保存在 \MQL5\Experts\TestDoEasy\Part61\ 当中,命名为 TestDoEasyPart61.mq5

由于现在所有即时报价序列都可以从函数库本身获得,所以我们删除程序中包含的相关类文件

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart60.mq5 |
//|                        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"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
#include <DoEasy\Objects\Ticks\TickSeries.mqh>
//--- enums

在程序全局变量区域中,删除 “New tick” 对象变量,和当前品种的即时报价序列数据对象

//--- global variables
CEngine        engine;
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ushort         magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           distance_pending_request;
uint           bars_delay_pending_request;
uint           slippage;
bool           trailing_on;
bool           pressed_pending_buy;
bool           pressed_pending_buy_limit;
bool           pressed_pending_buy_stop;
bool           pressed_pending_buy_stoplimit;
bool           pressed_pending_close_buy;
bool           pressed_pending_close_buy2;
bool           pressed_pending_close_buy_by_sell;
bool           pressed_pending_sell;
bool           pressed_pending_sell_limit;
bool           pressed_pending_sell_stop;
bool           pressed_pending_sell_stoplimit;
bool           pressed_pending_close_sell;
bool           pressed_pending_close_sell2;
bool           pressed_pending_close_sell_by_buy;
bool           pressed_pending_delete_all;
bool           pressed_pending_close_all;
bool           pressed_pending_sl;
bool           pressed_pending_tp;
double         trailing_stop;
double         trailing_step;
uint           trailing_start;
uint           stoploss_to_modify;
uint           takeprofit_to_modify;
int            used_symbols_mode;
string         array_used_symbols[];
string         array_used_periods[];
bool           testing;
uchar          group1;
uchar          group2;
double         g_point;
int            g_digits;

//--- "New tick" object
CNewTickObj    check_tick;
//--- Object of the current symbol tick series data
CTickSeries    tick_series;
//+------------------------------------------------------------------+

在 OnInit() 处理程序的末尾,删除在 “New tick” 对象设置当前品种

//--- Wait for 600 milliseconds
   engine.Pause(600);
   engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","Falling coin 2"));

//--- Set the current symbol for "New tick" object
   check_tick.SetSymbol(Symbol());
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

从 OnInitDoEasy() 函数中删除检查当前品种即时报价序列创建的代码块

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

//--- Code block for checking the tick list creation and working with it
   Print("");
//--- Since the tick series object is created with the default constructor,
//--- set a symbol, usage flag and the number of days (the default is 1) to copy the ticks
//--- Create the tick series and printed data in the journal
   tick_series.SetSymbol(Symbol());
   tick_series.SetAvailable(true);
   tick_series.SetRequiredUsedDays();
   tick_series.Create();
   tick_series.Print();
   
   Print("");
//--- Get and display in the journal the data of an object with the highest Ask price in the daily price range
   int index_max=CSelect::FindTickDataMax(tick_series.GetList(),TICK_PROP_ASK);
   CDataTick *tick_max=tick_series.GetList().At(index_max);
   if(tick_max!=NULL)
      tick_max.Print();
//--- Get and display in the journal the data of an object with the lowest Bid price in the daily price range
   int index_min=CSelect::FindTickDataMin(tick_series.GetList(),TICK_PROP_BID);
   CDataTick *tick_min=tick_series.GetList().At(index_min);
   if(tick_min!=NULL)
      tick_min.Print();

//--- Create resource text files

现在,我们需要在此处为即时报价数据集合的所有品种创建即时报价序列

//--- 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 tick series of all used symbols
   engine.GetTickSeriesCollection().CreateTickSeriesAll();
//--- Check created tick series - display descriptions of all created tick series in the journal
   engine.GetTickSeriesCollection().Print();

//--- Create resource text files

在 OnTick() 处理程序中,在新的即时报价到达时,尝试在即时报价数据列表中为每个品种找到含有最高要价(Ask)和最低出价(bid)的即时报价对象,并在日志中显示每个检测到的即时报价对象的参数

//+------------------------------------------------------------------+
//| 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
     }
     
//--- Check created tick data on the first tick
//--- Get and display in the journal the data of an object with the highest Ask price and the lowest Bid price in the daily price range
   static bool check=false;
   if(!check)
     {
      Print("");
      //--- Get the pointer to the list of tick data of all symbols from the tick collection
      CArrayObj* list=engine.GetTickSeriesCollection().GetList();
      int total=engine.GetTickSeriesCollection().DataTotal();
      //--- In the loop by the number of tick series in the collection
      for(int i=0;i<list.Type();i++)
        {
         //--- Get the next tick series from the collection by index
         CTickSeries *tick_series=engine.GetTickSeriesCollection().GetTickseries(i);
         if(tick_series!=NULL)
           {
            //--- In the obtained tick series, find the indices of tick objects with the highest Ask and the lowest Bid
            int index_max=CSelect::FindTickDataMax(tick_series.GetList(),TICK_PROP_ASK);
            int index_min=CSelect::FindTickDataMin(tick_series.GetList(),TICK_PROP_BID);
            //--- Display the data of the tick objects obtained from the tick series in the journal
            engine.GetTickSeriesCollection().GetTick(tick_series.Symbol(),index_max).Print();
            engine.GetTickSeriesCollection().GetTick(tick_series.Symbol(),index_min).Print();
           }
        }
      check=true;
     }
  }
//+------------------------------------------------------------------+

编译 EA,并在任何品种的图表上启动它。 在此之前,请在预定义列表中确保启用当前时间帧和品种,在整个建议的品种当中,仅保留了前两个品种:


在 OnInit() 处理程序中,经历了短时间,为两个所用品种创建即时报价数据之后,日志会收到有关程序参数、创建的时间序列、和所创建即时报价的数据。 直至新的即时价格到达后,日志会收到四个检测到的即时价格变化的数据,其中含有两个品种的最高要价(Ask)和最低出价(Bid)

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10426.13 USD, 1:100, Hedge, MetaTrader 5 demo
--- Initializing "DoEasy" library ---
Working with predefined symbol list. The number of used symbols: 2
"AUDUSD" "EURUSD"
Working with the current timeframe only: H1
AUDUSD symbol timeseries: 
- Timeseries "AUDUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 6194
EURUSD symbol timeseries: 
- Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5675
Tick series "AUDUSD": Requested number of days: 1, Historical data created: 142712
Tick series "EURUSD": Requested number of days: 1, Historical data created: 113985
Library initialization time: 00:00:06.156
 
============= Beginning of parameter list (Tick "AUDUSD" 2021.01.19 10:06:53.387) =============
Last price update time in milliseconds: 2021.01.19 10:06:53.387
Last price update time: 2021.01.19 10:06:53
Volume for the current Last price: 0
Flags: 6
Changed data on the tick:
 - Ask price change
 - Bid price change
------
Bid price: 0.77252
Ask price: 0.77256
Last price: 0.00000
Volume for the current Last price with greater accuracy: 0.00
Spread: 0.00004
------
Symbol: "AUDUSD"
============= End of parameter list (Tick "AUDUSD" 2021.01.19 10:06:53.387) =============
 
============= Beginning of parameter list (Tick "AUDUSD" 2021.01.18 11:51:48.662) =============
Last price update time in milliseconds: 2021.01.18 11:51:48.662
Last price update time: 2021.01.18 11:51:48
Volume for the current Last price: 0
Flags: 130
Changed data on the tick:
 - Bid price change
------
Bid price: 0.76589
Ask price: 0.76593
Last price: 0.00000
Volume for the current Last price with greater accuracy: 0.00
Spread: 0.00004
------
Symbol: "AUDUSD"
============= End of parameter list (Tick "AUDUSD" 2021.01.18 11:51:48.662) =============
 
============= Beginning of parameter list (Tick "EURUSD" 2021.01.19 10:05:07.246) =============
Last price update time in milliseconds: 2021.01.19 10:05:07.246
Last price update time: 2021.01.19 10:05:07
Volume for the current Last price: 0
Flags: 6
Changed data on the tick:
 - Ask price change
 - Bid price change
------
Bid price: 1.21189
Ask price: 1.21189
Last price: 0.00000
Volume for the current Last price with greater accuracy: 0.00
Spread: 0.00000
------
Symbol: "EURUSD"
============= End of parameter list (Tick "EURUSD" 2021.01.19 10:05:07.246) =============

============= Beginning of parameter list (Tick "EURUSD" 2021.01.18 14:57:53.847) =============
Last price update time in milliseconds: 2021.01.18 14:57:53.847
Last price update time: 2021.01.18 14:57:53
Volume for the current Last price: 0
Flags: 134
Changed data on the tick:
 - Ask price change
 - Bid price change
------
Bid price: 1.20536
Ask price: 1.20536
Last price: 0.00000
Volume for the current Last price with greater accuracy: 0.00
Spread: 0.00000
------
Symbol: "EURUSD"
============= End of parameter list (Tick "EURUSD" 2021.01.18 14:57:53.847) =============

根据日志,函数库的初始化和即时报价数据列表的创建花费了 16 秒。 直至新的即时报价出现,我们发现两个即时报价 — 携带最高要加(Ask)和最低出价(Bid)— 当日 每个用到的品种之一。

下一步是什么?

在下一篇文章中,我将开始在今天所创建即时报价集合基础上,创建实时更新和数据事件控制的功能。

以下是该函数库当前版本的所有文件,以及 MQL5 的测试 EA 文件,供您测试和下载。
即时报价序列集合类正在开发之中,因此,在现阶段强烈建议不要在自定义程序中使用它。
请您在评论中留下问题和建议。

返回内容目录

该系列中的先前文章:

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