DoEasy 函数库中的时间序列(第五十七·部分):存储一次即时报价数据的对象

9 三月 2021, 11:32
Artyom Trishkin
0
410

内容目录


概述

从本文开始,着手开发操控价格数据的函数库功能。

存储和使用即时报价数据的概念与时间序列数据的存储概念相似,其中最小数据单位为柱线。 柱线对象存储在属于相应时间帧的列表中,并存储在品种所属的列表中。

在即时报价数据存储的概念中,数据量的最小单位应为一次即时报价的价格数值结构。 这些数值可由品种 MqlTick 存储的最后价格结构来定义。 存储此类数值的对象还拥有其他属性:点差 - 要价(Ask)和出价(Bid)的差额,以及定义该对象一次即时报价数据对应的品种。

为了更便捷地构建列表,应为每个品种创建自己的对象列表,其内含有即时报价数据。 当然,即时报价数据对象的每个列表都会自动更新,新传入的即时报价新数据依次加入其中。 同时,函数库用户确定j的体量应支持列表大小。

函数库中数据存储的概念提供了按列表中存储的任何对象属性进行搜索和排序的功能,从而可以接收所需的数据,并供后续使用或分析研究。 故此,即时报价数据将具有相同的功能。 这将令用户能够快速分析即时报价数据流,以便跟踪;例如,它们的发生性质产生任何变化,或搜索设置的形态,等等。


准备数据

与任何函数库对象一样,为显示其参数的描述,必须添加文本消息,并创建对象属性的枚举,从而可以在列表中进行搜索,或按这些属性进行对象排序。

在文件 \MQL5\Include\DoEasy\Data.mqh 里加入新的消息索引:

//--- CSeriesDataInd
   MSG_LIB_TEXT_METHOD_NOT_FOR_INDICATORS,            // The method is not intended to work with indicator programs
   MSG_LIB_TEXT_IND_DATA_FAILED_GET_SERIES_DATA,      // Failed to get indicator data timeseries
   MSG_LIB_TEXT_IND_DATA_FAILED_GET_CURRENT_DATA,     // Failed to get current data of indicator buffer
   MSG_LIB_SYS_FAILED_CREATE_IND_DATA_OBJ,            // Failed to create indicator data object
   MSG_LIB_TEXT_IND_DATA_FAILED_ADD_TO_LIST,          // Failed to add indicator data object to list
   
//--- CTick
   MSG_TICK_TEXT_TICK,                                // Tick
   MSG_TICK_TIME_MSC,                                 // Time of the last update of prices in milliseconds
   MSG_TICK_TIME,                                     // Time of the last update of prices
   MSG_TICK_VOLUME,                                   // Volume for the current Last price
   MSG_TICK_FLAGS,                                    // Flags
   MSG_TICK_VOLUME_REAL,                              // Volume for the current Last price with greater accuracy
   MSG_TICK_SPREAD,                                   // Spread
   MSG_LIB_TEXT_TICK_CHANGED_DATA,                    // Changed data on tick:
   MSG_LIB_TEXT_TICK_FLAG_BID,                        // Bid price change
   MSG_LIB_TEXT_TICK_FLAG_ASK,                        // Ask price change
   MSG_LIB_TEXT_TICK_FLAG_LAST,                       // Last deal price change
   MSG_LIB_TEXT_TICK_FLAG_VOLUME,                     // Volume change

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

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

//--- CSeriesDataInd
   {"The method is not intended for working with indicator programs"},
   {"Failed to get indicator data timeseries"},
   {"Failed to get the current data of the indicator buffer"},
   {"Failed to create indicator data object"},
   {"Failed to add indicator data object to the list"},
   
//--- CTick
   {"Tick"},
   {"Last price update time in milliseconds"},
   {"Last price update time"},
   {"Volume for the current Last price"},
   {"Flags"},
   {"Volume for the current \"Last\" price with increased accuracy"},
   {"Spread"},
   {"Changed data on a tick:"},
   {"Bid price change"},
   {"Ask price change"},
   {"Last price change"},
   {"Volume change"},
   
  };
//+---------------------------------------------------------------------+


在 \MQL5\Include\DoEasy\Defines.mqh 文件里加入指定即时报价数据对象整数型实数型字符串型属性的枚举:

//+------------------------------------------------------------------+
//| Data for working with tick data                                  |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Tick integer properties                                          |
//+------------------------------------------------------------------+
enum ENUM_TICK_PROP_INTEGER
  {
   TICK_PROP_TIME_MSC = 0,                                  // Time of the last price update in milliseconds
   TICK_PROP_TIME,                                          // Time of the last update
   TICK_PROP_VOLUME,                                        // Volume for the current Last price
   TICK_PROP_FLAGS,                                         // Tick flags
  }; 
#define TICK_PROP_INTEGER_TOTAL (4)                         // Total number of tick integer properties
#define TICK_PROP_INTEGER_SKIP  (0)                         // Number of tick properties not used in sorting
//+------------------------------------------------------------------+
//| Real tick properties                                             |
//+------------------------------------------------------------------+
enum ENUM_TICK_PROP_DOUBLE
  {
   TICK_PROP_BID = TICK_PROP_INTEGER_TOTAL,                 // Tick Bid price
   TICK_PROP_ASK,                                           // Tick Ask price
   TICK_PROP_LAST,                                          // Current price of the last trade (Last)
   TICK_PROP_VOLUME_REAL,                                   // Volume for the current Last price with greater accuracy
   TICK_PROP_SPREAD,                                        // Tick spread (Ask - Bid)
  }; 
#define TICK_PROP_DOUBLE_TOTAL  (5)                         // Total number of real tick properties
#define TICK_PROP_DOUBLE_SKIP   (0)                         // Number of tick properties not used in sorting
//+------------------------------------------------------------------+
//| String tick properties                                           |
//+------------------------------------------------------------------+
enum ENUM_TICK_PROP_STRING
  {
   TICK_PROP_SYMBOL = (TICK_PROP_INTEGER_TOTAL+TICK_PROP_DOUBLE_TOTAL), // Tick symbol
  };
#define TICK_PROP_STRING_TOTAL  (1)                         // Total number of string tick properties
//+------------------------------------------------------------------+

为每个枚举指定在排序时用到未用到的属性总数。

为了可按上述指定的属性启用搜索和排序,需添加另一个枚举,在其中指定即时报价数据排序的可能标准:

//+------------------------------------------------------------------+
//| Possible tick sorting criteria                                   |
//+------------------------------------------------------------------+
#define FIRST_TICK_DBL_PROP          (TICK_PROP_INTEGER_TOTAL-TICK_PROP_INTEGER_SKIP)
#define FIRST_TICK_STR_PROP          (TICK_PROP_INTEGER_TOTAL-TICK_PROP_INTEGER_SKIP+TICK_PROP_DOUBLE_TOTAL-TICK_PROP_DOUBLE_SKIP)
enum ENUM_SORT_TICK_MODE
  {
//--- Sort by integer properties
   SORT_BY_TICK_TIME_MSC = 0,                               // Sort by the time of the last price update in milliseconds
   SORT_BY_TICK_TIM,                                        // Sort by the time of the last price update
   SORT_BY_TICK_VOLUME,                                     // Sort by volume for the current Last price
   SORT_BY_TICK_FLAGS,                                      // Sort by tick flags
//--- Sort by real properties
   SORT_BY_TICK_BID = FIRST_TICK_DBL_PROP,                  // Sort by tick Bid price
   SORT_BY_TICK_ASK,                                        // Sort by tick Ask price
   SORT_BY_TICK_LAST,                                       // Sort by current price of the last trade (Last)
   SORT_BY_TICK_VOLUME_REAL,                                // Sort by volume for the current Last price with greater accuracy
   SORT_BY_TICK_SPREAD,                                     // Sort by tick spread
//--- Sort by string properties
   SORT_BY_TICK_SYMBOL = FIRST_TICK_STR_PROP,               // Sort by tick symbol
  };
//+------------------------------------------------------------------+


早前,我们已经在第三十八篇文章里创建了 “New tick” 对象,该对象能够跟踪指定品种上新到达的即时报价。
该对象位于 \MQL5\Include\DoEasy\Objects\Ticks\ 文件夹目录的 NewTickObj.mqh 文件里。
改进对象类,如此即可将 NULL 值(或空字符串)传递给为对象指定当前品种的设置方法:

//--- Set a symbol
   void              SetSymbol(const string symbol)   { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol);  }

并在类构造函数的初始化清单中,为 m_new_tick 变量设置 false ,该变量存储新的即时报价标志:

//+------------------------------------------------------------------+
//| Parametric constructor CNewTickObj                               |
//+------------------------------------------------------------------+
CNewTickObj::CNewTickObj(const string symbol) : m_symbol(symbol),m_new_tick(false)
  {
//--- Reset the structures of the new and previous ticks
   ::ZeroMemory(this.m_tick);
   ::ZeroMemory(this.m_tick_prev);
//--- If managed to get the current prices to the tick structure -
//--- copy data of the obtained tick to the previous tick data and reset the first launch flag
  if(::SymbolInfoTick(this.m_symbol,this.m_tick))
     { 
      this.m_tick_prev=this.m_tick;
      this.m_first_start=false;
     }
  }
//+------------------------------------------------------------------+

在此之前,此变量未在任何地方以初始值进行初始化。 这是不正确的。


即时报价数据对象类

在 “New tick” 对象类所在的 \MQL5\Include\DoEasy\Objects\Ticks\ 文件夹中,创建一个即时报价数据对象类的新文件 DataTick.mqh

该对象对于函数库类而言并非什么新事物。 一切都是标准的。 我们来分析一下类主体:

//+------------------------------------------------------------------+
//|                                                     DataTick.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\BaseObj.mqh"
#include "..\..\Services\DELib.mqh"
//+------------------------------------------------------------------+
//| “Tick” class                                                     |
//+------------------------------------------------------------------+
class CDataTick : public CBaseObj
  {
private:
   MqlTick           m_tick;                                      // Structure for obtaining current prices
   int               m_digits;                                    // Symbol's digits value
   long              m_long_prop[TICK_PROP_INTEGER_TOTAL];        // Integer properties
   double            m_double_prop[TICK_PROP_DOUBLE_TOTAL];       // Real properties
   string            m_string_prop[TICK_PROP_STRING_TOTAL];       // String properties

//--- Return the index of the array the tick’s (1) double and (2) string properties are actually located at
   int               IndexProp(ENUM_TICK_PROP_DOUBLE property)       const { return(int)property-TICK_PROP_INTEGER_TOTAL;                       }
   int               IndexProp(ENUM_TICK_PROP_STRING property)       const { return(int)property-TICK_PROP_INTEGER_TOTAL-TICK_PROP_DOUBLE_TOTAL;}

public:
//--- Set tick’s (1) integer, (2) real and (3) string property
   void              SetProperty(ENUM_TICK_PROP_INTEGER property,long value)  { this.m_long_prop[property]=value;                               }
   void              SetProperty(ENUM_TICK_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value;             }
   void              SetProperty(ENUM_TICK_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value;             }
//--- Return tick’s (1) integer, (2) real and (3) string property from the properties array
   long              GetProperty(ENUM_TICK_PROP_INTEGER property)    const { return this.m_long_prop[property];                                 }
   double            GetProperty(ENUM_TICK_PROP_DOUBLE property)     const { return this.m_double_prop[this.IndexProp(property)];               }
   string            GetProperty(ENUM_TICK_PROP_STRING property)     const { return this.m_string_prop[this.IndexProp(property)];               }

//--- Return the flag of the tick supporting this property
   virtual bool      SupportProperty(ENUM_TICK_PROP_INTEGER property)      { return true; }
   virtual bool      SupportProperty(ENUM_TICK_PROP_DOUBLE property)       { return true; }
   virtual bool      SupportProperty(ENUM_TICK_PROP_STRING property)       { return true; }
//--- Return itself
   CDataTick        *GetObject(void)                                       { return &this;}

//--- Compare CDataTick objects with each other by the specified property (for sorting the lists by a specified object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CDataTick objects with each other by all properties (to search equal objects)
   bool              IsEqual(CDataTick* compared_obj) const;
//--- Constructors
                     CDataTick(){;}
                     CDataTick(const string symbol,const MqlTick &tick);
        
//+------------------------------------------------------------------+
//| Descriptions of object tick data properties                      |
//+------------------------------------------------------------------+
//--- Return description of tick's (1) integer, (2) real and (3) string property
   string            GetPropertyDescription(ENUM_TICK_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_TICK_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_TICK_PROP_STRING property);

//--- Display the description of tick properties in the journal (full_prop=true - all properties, false - supported ones only)
   void              Print(const bool full_prop=false);
//--- Display a short description of the tick in the journal
   virtual void      PrintShort(void);
//---  Return the (1) short name and (2) description of tick data object flags
   virtual string    Header(void);
   string            FlagsDescription(void);
   
//+------------------------------------------------------------------+ 
//| Methods of simplified access to tick data object properties      |
//+------------------------------------------------------------------+
//--- Return tick’s (1) Time, (2) time in milliseconds, (3) volume, (4) flags
   datetime          Time(void)                                         const { return (datetime)this.GetProperty(TICK_PROP_TIME);           }
   long              TimeMSC(void)                                      const { return this.GetProperty(TICK_PROP_TIME_MSC);                 }
   long              Volume(void)                                       const { return this.GetProperty(TICK_PROP_VOLUME);                   }
   uint              Flags(void)                                        const { return (uint)this.GetProperty(TICK_PROP_FLAGS);              }
   
//--- Return tick’s (1) Bid, (2) Ask, (3) Last price, (4) volume with greater accuracy, (5) spread of the tick
//--- size of the (9) candle upper, (10) lower wick
   double            Bid(void)                                          const { return this.GetProperty(TICK_PROP_BID);                      }
   double            Ask(void)                                          const { return this.GetProperty(TICK_PROP_ASK);                      }
   double            Last(void)                                         const { return this.GetProperty(TICK_PROP_LAST);                     }
   double            VolumeReal(void)                                   const { return this.GetProperty(TICK_PROP_VOLUME_REAL);              }
   double            Spread(void)                                       const { return this.GetProperty(TICK_PROP_SPREAD);                   }
   
//--- Return tick symbol
   string            Symbol(void)                                       const { return this.GetProperty(TICK_PROP_SYMBOL);                   }

//--- Return bar (1) time, (2) index on the specified timeframe the tick time falls into
   datetime          TimeBar(const ENUM_TIMEFRAMES timeframe)const
                       { return ::iTime(this.Symbol(),timeframe,this.Index(timeframe));                                                      }
   int               Index(const ENUM_TIMEFRAMES timeframe)  const
                       { return ::iBarShift(this.Symbol(),(timeframe==PERIOD_CURRENT ? ::Period() : timeframe),this.Time());                 }  

//--- Return the flag of (1) Bid, (2) Ask, (3) Last price, (4) volume change; (5) buy and (6) sell trades
   bool              IsChangeBid()                                      const { return((this.Flags() & TICK_FLAG_BID)==TICK_FLAG_BID);       }
   bool              IsChangeAsk()                                      const { return((this.Flags() & TICK_FLAG_ASK)==TICK_FLAG_ASK);       }
   bool              IsChangeLast()                                     const { return((this.Flags() & TICK_FLAG_LAST)==TICK_FLAG_LAST);     }
   bool              IsChangeVolume()                                   const { return((this.Flags() & TICK_FLAG_VOLUME)==TICK_FLAG_VOLUME); }
   bool              IsChangeBuy()                                      const { return((this.Flags() & TICK_FLAG_BUY)==TICK_FLAG_BUY);       }
   bool              IsChangeSell()                                     const { return((this.Flags( )& TICK_FLAG_SELL)==TICK_FLAG_SELL);     }
//---
  };
//+------------------------------------------------------------------+

最开始的第一篇文章中曾详细讨论过函数库对象的组成及其创建。 现在,我们遍历主要的变量和方法,并进一步分析其在类中的实现。

在类的私密部分包含辅助变量,存储对象整数型、实数型和字符串型的属性值数组;并返回属性在数组所处的实际索引的方法。

在类的公开部分包含设置指定属性的值,并将其返回给相应对象属性数组的方法;返回对象支持某个属性的标志的方法;对象搜索、比较和排序的方法;以及类构造函数。

显示对象属性说明的方法访问对象属性的简化方法也放在此处。

现在,我们来看一下类方法的实现。

品种和已填充当前即时报价数据的结构也应传递给参数类构造函数:

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CDataTick::CDataTick(const string symbol,const MqlTick &tick)
  {
//--- Save symbol’s Digits
   this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS);
//--- Save integer tick properties
   this.SetProperty(TICK_PROP_TIME,tick.time);
   this.SetProperty(TICK_PROP_TIME_MSC,tick.time_msc);
   this.SetProperty(TICK_PROP_VOLUME,tick.volume);
   this.SetProperty(TICK_PROP_FLAGS,tick.flags);
//--- Save real tick properties
   this.SetProperty(TICK_PROP_BID,tick.bid);
   this.SetProperty(TICK_PROP_ASK,tick.ask);
   this.SetProperty(TICK_PROP_LAST,tick.last);
   this.SetProperty(TICK_PROP_VOLUME_REAL,tick.volume_real);
//--- Save additional tick properties
   this.SetProperty(TICK_PROP_SPREAD,tick.ask-tick.bid);
   this.SetProperty(TICK_PROP_SYMBOL,(symbol==NULL || symbol=="" ? ::Symbol() : symbol));
  }
//+------------------------------------------------------------------+

在构造函数中,简单地在对象属性中填充来自即时报价结构的相应数值,以及从中即时报价数据里获取的品种即可。

依据指定属性比较对象两个参数的方法:

//+----------------------------------------------------------------------+
//| Compare CDataTick objects with each other by the specified property  |
//+----------------------------------------------------------------------+
int CDataTick::Compare(const CObject *node,const int mode=0) const
  {
   const CDataTick *obj_compared=node;
//--- compare integer properties of two objects
   if(mode<TICK_PROP_INTEGER_TOTAL)
     {
      long value_compared=obj_compared.GetProperty((ENUM_TICK_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_TICK_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare real properties of two objects
   else if(mode<TICK_PROP_DOUBLE_TOTAL+TICK_PROP_INTEGER_TOTAL)
     {
      double value_compared=obj_compared.GetProperty((ENUM_TICK_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_TICK_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare string properties of two objects
   else if(mode<TICK_PROP_DOUBLE_TOTAL+TICK_PROP_INTEGER_TOTAL+TICK_PROP_STRING_TOTAL)
     {
      string value_compared=obj_compared.GetProperty((ENUM_TICK_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_TICK_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

传递给该方法的参数包括,指向所需比较的两个对象的指针,两个对象所欲比较的属性类型。

取决于所传递的当前对象比较模式,比较两个对象的这些属性,如果当前对象的属性值分别大于/小于或等于比较对象的属性值,则返回 1/-1/0。

比较两个对象标识的方法:

//+------------------------------------------------------------------+
//| Compare CDataTick objects with each other by all properties      |
//+------------------------------------------------------------------+
bool CDataTick::IsEqual(CDataTick *compared_obj) const
  {
   int beg=0, end=TICK_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_TICK_PROP_INTEGER prop=(ENUM_TICK_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=TICK_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_TICK_PROP_DOUBLE prop=(ENUM_TICK_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=TICK_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_TICK_PROP_STRING prop=(ENUM_TICK_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+

此处:指向欲比较对象的指针,传递给该方法。 然后,每个属性组在三重循环中,逐一比较两个对象的连续属性。 如果至少一个属性与所比较对象的相同属性不匹配,则返回 false。 直至所有三个循环完毕,则返回 true — 两个对象的所有属性都相等。 这意味着对象是相同的。

在日志中显示所有对象属性的方法:

//+------------------------------------------------------------------+
//| Display tick properties in the journal                           |
//+------------------------------------------------------------------+
void CDataTick::Print(const bool full_prop=false)
  {
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") =============");
   int beg=0, end=TICK_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_TICK_PROP_INTEGER prop=(ENUM_TICK_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=TICK_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_TICK_PROP_DOUBLE prop=(ENUM_TICK_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=TICK_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_TICK_PROP_STRING prop=(ENUM_TICK_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n");
  }
//+------------------------------------------------------------------+

此处:每个对象属性组在三重循环中,利用相应的方法 GetPropertyDescription() 在日志中逐一显示每个连续属性的说明。 如果在方法参数中将 false 值传递给 full_prop 变量,则仅显示对象支持的属性。 对于不支持的属性,在日志里显示不支持该属性,尽管此对象支持所有属性。 但这可以在类的衍生对象中更改。

返回指定对象的整数型实数型字符串型属性说明的方法:

//+------------------------------------------------------------------+
//| Return description of tick's integer property                    |
//+------------------------------------------------------------------+
string CDataTick::GetPropertyDescription(ENUM_TICK_PROP_INTEGER property)
  {
   return
     (
      property==TICK_PROP_TIME_MSC           ?  CMessage::Text(MSG_TICK_TIME_MSC)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+TimeMSCtoString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==TICK_PROP_TIME               ?  CMessage::Text(MSG_TICK_TIME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==TICK_PROP_VOLUME             ?  CMessage::Text(MSG_TICK_VOLUME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==TICK_PROP_FLAGS              ?  CMessage::Text(MSG_TICK_FLAGS)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)+"\n"+CMessage::Text(MSG_LIB_TEXT_TICK_CHANGED_DATA)+this.FlagsDescription()
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return description of tick's real property                       |
//+------------------------------------------------------------------+
string CDataTick::GetPropertyDescription(ENUM_TICK_PROP_DOUBLE property)
  {
   int dg=(this.m_digits>0 ? this.m_digits : 1);
   return
     (
      property==TICK_PROP_BID                ?  CMessage::Text(MSG_LIB_PROP_BID)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==TICK_PROP_ASK                ?  CMessage::Text(MSG_LIB_PROP_ASK)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==TICK_PROP_LAST               ?  CMessage::Text(MSG_LIB_PROP_LAST)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==TICK_PROP_VOLUME_REAL        ?  CMessage::Text(MSG_TICK_VOLUME_REAL)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==TICK_PROP_SPREAD             ?  CMessage::Text(MSG_TICK_SPREAD)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Return description of tick's string property                     |
//+------------------------------------------------------------------+
string CDataTick::GetPropertyDescription(ENUM_TICK_PROP_STRING property)
  {
   return(property==TICK_PROP_SYMBOL ? CMessage::Text(MSG_LIB_PROP_SYMBOL)+": \""+this.GetProperty(property)+"\"" : "");
  }
//+------------------------------------------------------------------+

此处:取决于传递给方法的属性,返回其字符串说明。

返回即时报价所有标志的说明文字的方法:

//+------------------------------------------------------------------+
//| Display the description of flags                                 |
//+------------------------------------------------------------------+
string CDataTick::FlagsDescription(void)
  {
   string flags=
     (
      (this.IsChangeAsk()     ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_ASK)     : "")+
      (this.IsChangeBid()     ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_BID)     : "")+
      (this.IsChangeLast()    ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_LAST)    : "")+
      (this.IsChangeVolume()  ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_VOLUME)  : "")+
      (this.IsChangeBuy()     ? "\n - "+CMessage::Text(MSG_DEAL_TO_BUY)                : "")+
      (this.IsChangeSell()    ? "\n - "+CMessage::Text(MSG_DEAL_TO_SELL)               : "")
     );
  return flags; 
  }
//+------------------------------------------------------------------+

在其属性中,每个即时报价都有一个包含一组标志的变量。 这些标志定义了引发该即时报价的事件:

来自 MqlTick 用户指南

为了找出当前即时报价里哪些特定数据被更改,分析其标志:

  • TICK_FLAG_BID — 即时报价出价(Bid)改变
  • TICK_FLAG_ASK — 即时报价要价(Ask)改变
  • TICK_FLAG_LAST — 即时报价最后成交价改变
  • TICK_FLAG_VOLUME — 即时报价交易量改变
  • TICK_FLAG_BUY — 即时报价结果来自买入交易
  • TICK_FLAG_SELL — 即时报价结果来自卖出交易

我们有六个方法(按标志的数量)返回每个标志变量的状态标志:

//--- Return the flag of (1) Bid, (2) Ask, (3) Last price, (4) volume change; (5) buy and (6) sell trades
   bool IsChangeBid()    const { return((this.Flags() & TICK_FLAG_BID)==TICK_FLAG_BID);       }
   bool IsChangeAsk()    const { return((this.Flags() & TICK_FLAG_ASK)==TICK_FLAG_ASK);       }
   bool IsChangeLast()   const { return((this.Flags() & TICK_FLAG_LAST)==TICK_FLAG_LAST);     }
   bool IsChangeVolume() const { return((this.Flags() & TICK_FLAG_VOLUME)==TICK_FLAG_VOLUME); }
   bool IsChangeBuy()    const { return((this.Flags() & TICK_FLAG_BUY)==TICK_FLAG_BUY);       }
   bool IsChangeSell()   const { return((this.Flags() & TICK_FLAG_SELL)==TICK_FLAG_SELL);     }

在所讲述的方法中,首先检查变量中存在标志。 取决于是否这种变化可行与否,结果文本应添加换行符+标志说明或空字符串。 在检查完所有标志之后,最终我们得到已编译文本,其中包含变量中存在标志的说明。 如果有若干个标志可用,则每个标志的说明在日志中会以新行开始。

在日志中显示报价数据对象简述的方法:

//+------------------------------------------------------------------+
//| Display a short description of the tick in the journal           |
//+------------------------------------------------------------------+
void CDataTick::PrintShort(void)
  {
   ::Print(this.Header());
  }
//+------------------------------------------------------------------+

该方法简单地在日志中显示其自身的简称,该名称由以下方法返回:

//+------------------------------------------------------------------+
//| Return a short name of tick data object                          |
//+------------------------------------------------------------------+
string CDataTick::Header(void)
  {
   return
     (
      CMessage::Text(MSG_TICK_TEXT_TICK)+" \""+this.Symbol()+"\" "+TimeMSCtoString(TimeMSC())
     );
  }
//+------------------------------------------------------------------+

在此阶段,即时报价数据对象的创建完成。


测试即时报价数据对象

为了执行测试,我们借用上一篇文章中的 EA,并将其保存在新文件夹 \MQL5\Experts\TestDoEasy\Part59\ 之下,并命名为新名称 TestDoEasyPart59.mq5

在此 EA 中,不再需要指标及其时间序列数据。 此处,只需创建即时报价数据对象,在日志中显示其完整描述,并在图表的注释中显示其简述。 伴随每次即时报价,其在图表上的说明也会变化;而在日志中,显示 EA 启动后出现的第一次即时报价。

由于尚未将此 EA 与新函数库对象相连接,因此我将把该类的文件连接到 EA

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart59.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
#include <DoEasy\Objects\Ticks\DataTick.mqh>

In the area of EA’s global variables instead of custom indicator parameter arrays

//--- Arrays of custom indicator parameters
MqlParam       param_ma1[];
MqlParam       param_ma2[];
//+------------------------------------------------------------------+

声明一个 “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;
//+------------------------------------------------------------------+

OnInit() 处理程序里删除指标创建模块:

//--- Create indicators
   ArrayResize(param_ma1,4);
   //--- Name of indicator 1
   param_ma1[0].type=TYPE_STRING;
   param_ma1[0].string_value="Examples\\Custom Moving Average.ex5";
   //--- Calculation period
   param_ma1[1].type=TYPE_INT;
   param_ma1[1].integer_value=13;
   //--- Horizontal shift
   param_ma1[2].type=TYPE_INT;
   param_ma1[2].integer_value=0;
   //--- Smoothing method
   param_ma1[3].type=TYPE_INT;
   param_ma1[3].integer_value=MODE_SMA;
   //--- Create indicator 1
   engine.GetIndicatorsCollection().CreateCustom(NULL,PERIOD_CURRENT,MA1,1,INDICATOR_GROUP_TREND,param_ma1);
   
   ArrayResize(param_ma2,5);
   //--- Name of indicator 2
   param_ma2[0].type=TYPE_STRING;
   param_ma2[0].string_value="Examples\\Custom Moving Average.ex5";
   //--- Calculation period
   param_ma2[1].type=TYPE_INT;
   param_ma2[1].integer_value=13;
   //--- Horizontal shift
   param_ma2[2].type=TYPE_INT;
   param_ma2[2].integer_value=0;
   //--- Smoothing method
   param_ma2[3].type=TYPE_INT;
   param_ma2[3].integer_value=MODE_SMA;
   //--- Calculation price
   param_ma2[4].type=TYPE_INT;
   param_ma2[4].integer_value=PRICE_OPEN;
   //--- Create indicator 2
   engine.GetIndicatorsCollection().CreateCustom(NULL,PERIOD_CURRENT,MA2,1,INDICATOR_GROUP_TREND,param_ma2);
   
   //--- Create indicator 3
   engine.GetIndicatorsCollection().CreateAMA(NULL,PERIOD_CURRENT,AMA1);
   //--- Create indicator 4
   engine.GetIndicatorsCollection().CreateAMA(NULL,PERIOD_CURRENT,AMA2,14);
   
   //--- Display descriptions of created indicators
   engine.GetIndicatorsCollection().Print();
   engine.GetIndicatorsCollection().PrintShort();

OnInit() 的最后为 “New tick” 对象设置当前品种作为操作品种:

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

在 EA 的 OnTick() 处理程序中,添加一个代码块,判断新的即时报价(作为未来即时报价集合类模拟操作),并在图表和日志里显示新创建的即时报价数据对象的说明

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

//--- Create a temporary list for storing “Tick data” objects,
//--- a variable for obtaining tick data and
//--- a variable for calculating incoming ticks
   static int tick_count=0;
   CArrayObj list;
   MqlTick tick_struct;
//--- Check a new tick on the current symbol
   if(check_tick.IsNewTick())
     {
      //--- If failed to get the price - exit
      if(!SymbolInfoTick(Symbol(),tick_struct))
         return;
      //--- Create a new tick data object
      CDataTick *tick_obj=new CDataTick(Symbol(),tick_struct);
      if(tick_obj==NULL)
         return;
      //--- Increase tick counter (simply to display on the screen, no other purpose is provided)
      tick_count++;
      //--- Limit the number of ticks in the counting as one hundred thousand (again, no purpose is provided)
      if(tick_count>100000) tick_count=1;
      //--- In the comment on the chart display the tick number and its short description
      Comment("--- №",IntegerToString(tick_count,5,'0'),": ",tick_obj.Header());
      //--- If this is the first tick (which follows the first launch of EA) display its full description in the journal
      if(tick_count==1)
         tick_obj.Print();
      //--- Remove if failed to put the created tick data object in the list
      if(!list.Add(tick_obj))
         delete tick_obj;
     }
   
//--- If the trailing flag is set
   if(trailing_on)
     {
      TrailingPositions();          // Trailing positions
      TrailingOrders();             // Trailing of pending orders
     }
  }
//+------------------------------------------------------------------+

在清单的注释中详细注明了逻辑。我相信这是明晰而简单的。

编译 EA,并在已预先设置好当前品种和时间帧的图表上启动它。 启动之后,当有新即时报价到达,对象(到达的即时报价)的说明将显示在日志当中:

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10426.13 USD, 1:100, Hedge, Demo account MetaTrader 5
--- Initialize "DoEasy" library ---
Work with the current symbol only: "EURUSD"
Work with the current timeframe only: H1
EURUSD symbol timeseries: 
- "EURUSD" H1 timeseries: Requested: 1000, Actually: 1000, Created: 1000, On the server: 5153
Library initialize time: 00:00:00.000
============= Beginning of parameter list (Tick "EURUSD" 2020.12.16 13:22:32.822) =============
Last price update time in milliseconds: 2020.12.16 13:22:32.822
Last price update time: 2020.12.16 13:22:32
Volume for the current Last price: 0
Flags: 6
Changed data on the tick:
 - Ask price change
 - Bid price change
------
Bid price: 1.21927
Ask price: 1.21929
Last price: 0.00000
Volume for the current Last price with greater accuracy: 0.00
Spread: 0.00002
------
Symbol: "EURUSD"
============= End of parameter list (Tick "EURUSD" 2020.12.16 13:22:32.822) =============

且伴随每个新即时报价数据,在图表上将显示带有其简述的注释:



下一步是什么?

在下一篇文章中,我们将开始为单一品种创建即时报价数据集合。

下面附有当前函数库版本的所有文件,以及 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 函数库中的时间序列(第五十八部分):指标缓冲区数据的时间序列


本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/8818

附加的文件 |
MQL5.zip (3868.71 KB)
直推和主动机器学习中的梯度提升 直推和主动机器学习中的梯度提升
在本文中,我们将探讨利用真实数据的主动机器学习方法,并讨论它们的优缺点。也许你会发现这些方法很有用,并将它们包含在你的机器学习模型库中。直推是由支持向量机(SVM)的共同发明者弗拉基米尔·瓦普尼克(Vladimir Vapnik)提出的。
开发自适应算法 (第二部分): 提高效率 开发自适应算法 (第二部分): 提高效率
在本文中,我将通过改进先前创建的算法的灵活性来继续本主题的开发。随着分析窗口中烛形数量的增加,或烛形超额阈值百分比的增加,算法变得更加稳定。我不得不做出妥协,并设置一个更大的样本量进行分析或更大的烛形超额百分比。
利用 CatBoost 算法寻找外汇市场的季节性模式 利用 CatBoost 算法寻找外汇市场的季节性模式
本文探索了用时间过滤器建立机器学习模型,并讨论了这种方法的有效性。现在,只要简单地指示模型在一周中某一天的某个时间进行交易,就可以消除人为因素。模式搜索可以由单独的算法提供。
DoEasy 函数库中的时间序列(第五十八部分):指标缓冲区数据的时间序列 DoEasy 函数库中的时间序列(第五十八部分):指标缓冲区数据的时间序列
关于操控时间序列的主题总结,诸如组织存储、针对存储在指标缓冲区中的数据进行搜索和分类,如此即可在程序里利用函数库创建指标值,并进一步据其执行分析。 函数库的所有集合类的一般概念,能够轻松地在相应的集合中找到必要的数据。 在今天创建的类中,也可分别完成同样功能。