
DoEasy 函数库中的时间序列(第五十七部分):存储一次即时报价数据的对象
内容目录
概述
从本文开始,着手开发操控价格数据的函数库功能。
存储和使用即时报价数据的概念与时间序列数据的存储概念相似,其中最小数据单位为柱线。 柱线对象存储在属于相应时间帧的列表中,并存储在品种所属的列表中。
在即时报价数据存储的概念中,数据量的最小单位应为一次即时报价的价格数值结构。 这些数值可由品种的 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 Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/8818



