在本文中,我将着手实现操控市场深度的功能。 从概念上讲,操控 DOM 的类与以前实现的所有函数库类都没啥区别。 与此同时,我们将拥有一个 DOM 特征数据的模型,其中包含 DOM 中存储的有关订单数据信息。激活 OnBookEvent() 处理程序时,可由 MarketBookGet() 函数获取数据。 在 DOM 发生任何变化的情况下,处理程序中会为订阅 DOM 事件的每个品种激活一个事件。
故此,DOM 类结构如下:
今天,我将实现订单对象类(1),并测试当前品种激活 OnBookEvent() 时获取 DOM 数据。
每个订单的属性都在 MqlBookInfo 结构中设置,提供 DOM 所含的数据:
DOM 可能有四种订单类型(来自 ENUM_BOOK_TYPE 枚举):
正如我们所见,有四种订单类型 — 两种买入,和两种卖出。 为了将所有类型的订单分为两部分,我们应该在现有的属性上再添加一个属性 — 指示其方向的订单状态 — 买单或卖单。 这能令我们迅速将所有订单划分为两部分 — 供给和需求。
该对象将令单个 DOM 请求类似于订单对象(以及许多其他函数库对象)— 我们会有一个基准 DOM 抽象订单对象,和四个含有订单类型规则的衍生对象。 此类对象的构造概念,已在函数库开发之初的第一篇和第二篇文章里就研究过了。
在实现操控 DOM 的类之前,需添加新的函数库消息,并略微改进即时报价数据对象类。 并在 \MQL5\Include\DoEasy\Data.mqh 里加入新消息的索引:
MSG_SYM_EVENT_SYMBOL_ADD, // Added symbol to Market Watch window MSG_SYM_EVENT_SYMBOL_DEL, // Symbol removed from Market Watch window MSG_SYM_EVENT_SYMBOL_SORT, // Changed location of symbols in Market Watch window MSG_SYM_SYMBOLS_MODE_CURRENT, // Work with current symbol only MSG_SYM_SYMBOLS_MODE_DEFINES, // Work with predefined symbol list MSG_SYM_SYMBOLS_MODE_MARKET_WATCH, // Work with Market Watch window symbols MSG_SYM_SYMBOLS_MODE_ALL, // Work with full list of all available symbols MSG_SYM_SYMBOLS_BOOK_ADD, // Subscribed to Depth of Market MSG_SYM_SYMBOLS_BOOK_DEL, // Unsubscribed from Depth of Market MSG_SYM_SYMBOLS_MODE_BOOK, // Subscription to Depth of Market MSG_SYM_SYMBOLS_ERR_BOOK_ADD, // Error subscribing to DOM MSG_SYM_SYMBOLS_ERR_BOOK_DEL, // Error unsubscribing from DOM //--- CAccount
...
//--- CTickSeries MSG_TICKSERIES_TEXT_TICKSERIES, // Tick series MSG_TICKSERIES_ERR_GET_TICK_DATA, // Failed to get tick data MSG_TICKSERIES_FAILED_CREATE_TICK_DATA_OBJ, // Failed to create tick data object MSG_TICKSERIES_FAILED_ADD_TO_LIST, // Failed to add tick data object to list MSG_TICKSERIES_TEXT_IS_NOT_USE, // Tick series not used. Set the flag using SetAvailable() MSG_TICKSERIES_REQUIRED_HISTORY_DAYS, // Requested number of days //--- CMarketBookOrd MSG_MBOOK_ORD_TEXT_MBOOK_ORD, // Order in DOM MSG_MBOOK_ORD_VOLUME, // Volume MSG_MBOOK_ORD_VOLUME_REAL, // Extended accuracy volume MSG_MBOOK_ORD_STATUS_BUY, // Buy side MSG_MBOOK_ORD_STATUS_SELL, // Sell side MSG_MBOOK_ORD_TYPE_SELL, // Sell order MSG_MBOOK_ORD_TYPE_BUY, // Buy order MSG_MBOOK_ORD_TYPE_SELL_MARKET, // Sell order by Market MSG_MBOOK_ORD_TYPE_BUY_MARKET, // Buy order by Market }; //+------------------------------------------------------------------+
以及与新添加索引相对应的消息文本:
{"В окно \"Обзор рынка\" добавлен символ","Added symbol to \"Market Watch\" window"}, {"Из окна \"Обзор рынка\" удалён символ","Removed from \"Market Watch\" window"}, {"Изменено расположение символов в окне \"Обзор рынка\"","Changed arrangement of symbols in \"Market Watch\" window"}, {"Работа только с текущим символом","Work only with the current symbol"}, {"Работа с предопределённым списком символов","Work with predefined list of symbols"}, {"Работа с символами из окна \"Обзор рынка\"","Working with symbols from \"Market Watch\" window"}, {"Работа с полным списком всех доступных символов","Work with full list of all available symbols"}, {"Осуществлена подписка на стакан цен ","Subscribed to Depth of Market"}, {"Осуществлена отписка от стакан цен ","Unsubscribed from Depth of Market"}, {"Подписка на стакан цен","Subscription to Depth of Market"}, {"Ошибка при подписке на стакан цен",""}, {"Ошибка при отписке от стакан цен",""}, //--- CAccount
...
//--- CMarketBookOrd {"Заявка в стакане цен","Order in Depth of Market"}, {"Объем","Volume"}, {"Объем c повышенной точностью","Volume Real"}, {"Сторона Buy","Buy side"}, {"Сторона Sell","Sell side"}, {"Заявка на продажу","Sell order"}, {"Заявка на покупку","Buy order"}, {"Заявка на продажу по рыночной цене","Sell order at market price"}, {"Заявка на покупку по рыночной цене","Buy order at market price"}, }; //+---------------------------------------------------------------------+
在品种对象类的 \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh 文件中添加与订阅 DOM 有关的错误显示消息:
//+------------------------------------------------------------------+ //| Subscribe to the Depth of Market | //+------------------------------------------------------------------+ bool CSymbol::BookAdd(void) { this.m_book_subscribed=(#ifdef __MQL5__ ::MarketBookAdd(this.m_name) #else false #endif); this.m_long_prop[SYMBOL_PROP_BOOKDEPTH_STATE]=this.m_book_subscribed; if(this.m_book_subscribed) ::Print(CMessage::Text(MSG_SYM_SYMBOLS_BOOK_ADD)+" "+this.m_name); else ::Print(CMessage::Text(MSG_SYM_SYMBOLS_ERR_BOOK_ADD)+": "+CMessage::Text(::GetLastError())); return this.m_book_subscribed; } //+------------------------------------------------------------------+
对于取消订阅时执行相同的操作:
//+------------------------------------------------------------------+ //| Close the market depth | //+------------------------------------------------------------------+ bool CSymbol::BookClose(void) { //--- If the DOM subscription flag is off, subscription is disabled (or not enabled yet). Return 'true' if(!this.m_book_subscribed) return true; //--- Save the result of unsubscribing from the DOM bool res=( #ifdef __MQL5__ ::MarketBookRelease(this.m_name) #else true #endif ); //--- If unsubscribed successfully, reset the DOM subscription flag and write the status to the object property if(res) { this.m_long_prop[SYMBOL_PROP_BOOKDEPTH_STATE]=this.m_book_subscribed=false; ::Print(CMessage::Text(MSG_SYM_SYMBOLS_BOOK_DEL)+" "+this.m_name); } else { this.m_long_prop[SYMBOL_PROP_BOOKDEPTH_STATE]=this.m_book_subscribed=true; ::Print(CMessage::Text(MSG_SYM_SYMBOLS_ERR_BOOK_DEL)+": "+CMessage::Text(::GetLastError())); } //--- Return the result of unsubscribing from DOM return res; } //+------------------------------------------------------------------+
来自 \MQL5\Include\DoEasy\Objects\Ticks\TickSeries.mqh 中即时报价序列类的更新方法,从其中删除在品种图表上显示调试注释,我们在上一篇文章留作测试:
//+------------------------------------------------------------------+ //| Update the tick series list | //+------------------------------------------------------------------+ void CTickSeries::Refresh(void) { MqlTick ticks_array[]; if(IsNewTick()) { //--- Copy ticks from m_last_time time+1 ms to the end of history int err=ERR_SUCCESS; int total=::CopyTicksRange(this.Symbol(),ticks_array,COPY_TICKS_ALL,this.m_last_time+1,0); //--- If the ticks have been copied, create new tick data objects and add them to the list in the loop by their number if(total>0) { for(int i=0;i<total;i++) { //--- Create the tick object and add it to the list CDataTick *tick_obj=this.CreateNewTickObj(ticks_array[i]); if(tick_obj==NULL) break; //--- Write the last tick time for subsequent copying of newly arrived ticks long end_time=ticks_array[::ArraySize(ticks_array)-1].time_msc; if(this.Symbol()=="AUDUSD") Comment(DFUN,this.Symbol(),", copied=",total,", m_last_time=",TimeMSCtoString(m_last_time),", end_time=",TimeMSCtoString(end_time),", total=",DataTotal()); this.m_last_time=end_time; } //--- If the number of ticks in the list exceeds the default maximum number, //--- remove the calculated number of tick objects from the end of the list if(this.DataTotal()>TICKSERIES_MAX_DATA_TOTAL) { int total_del=m_list_ticks.Total()-TICKSERIES_MAX_DATA_TOTAL; for(int j=0;j<total_del;j++) this.m_list_ticks.Delete(j); } } } } //+------------------------------------------------------------------+
最后的即时报价时间立即设置在 m_last_time 变量中,如此这般目的在于在上一篇文章里,我需要将验证数据显示在品种图表注释当中,其中包含的前一次和当前的即时报价时间。 现在我们不需要它了,时间会被立即保存在变量中:
//+------------------------------------------------------------------+ //| Update the tick series list | //+------------------------------------------------------------------+ void CTickSeries::Refresh(void) { MqlTick ticks_array[]; if(IsNewTick()) { //--- Copy ticks from m_last_time time+1 ms to the end of history int err=ERR_SUCCESS; int total=::CopyTicksRange(this.Symbol(),ticks_array,COPY_TICKS_ALL,this.m_last_time+1,0); //--- If the ticks have been copied, create new tick data objects and add them to the list in the loop by their number if(total>0) { for(int i=0;i<total;i++) { //--- Create the tick object and add it to the list CDataTick *tick_obj=this.CreateNewTickObj(ticks_array[i]); if(tick_obj==NULL) break; //--- Write the last tick time for subsequent copying of newly arrived ticks this.m_last_time=ticks_array[::ArraySize(ticks_array)-1].time_msc; } //--- If the number of ticks in the list exceeds the default maximum number, //--- remove the calculated number of tick objects from the end of the list if(this.DataTotal()>TICKSERIES_MAX_DATA_TOTAL) { int total_del=m_list_ticks.Total()-TICKSERIES_MAX_DATA_TOTAL; for(int j=0;j<total_del;j++) this.m_list_ticks.Delete(j); } } } } //+------------------------------------------------------------------+
与所有函数库对象一样,定义对象属性常量均有相应的枚举集合,我们也需要为 DOM 订单创建整数型、实数型和字符串型对象属性的枚举。
在 \MQL5\Include\DoEasy\Defines.mqh 以下位置添加 DOM 订单对象属性和参数的枚举。 鉴于我不打算实现处理每个 DOM 订单的事件模型(在某时刻,订单簿会显示所有订单的当前状态,它们的变化会引发下一个状态,并在下次激活 OnBookEvent() 时处理,只需在 DOM 事件的最后一个代码之后添加指定下一个事件的代码常量即可,如此只需维护所有对象的常量标识,令它们具有相同的形式即可:
//+------------------------------------------------------------------+ //| Data for working with DOM | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| List of possible DOM events | //+------------------------------------------------------------------+ #define MBOOK_ORD_EVENTS_NEXT_CODE (SERIES_EVENTS_NEXT_CODE+1) // The code of the next event after the last DOM event code //+------------------------------------------------------------------+
定义枚举指定单个 DOM 订单的两种可能状态 — 买方或卖方:
//+------------------------------------------------------------------+ //| Abstract DOM type (status) | //+------------------------------------------------------------------+ enum ENUM_MBOOK_ORD_STATUS { MBOOK_ORD_STATUS_BUY, // Buy side MBOOK_ORD_STATUS_SELL, // Sell side }; //+------------------------------------------------------------------+
依据这些属性针对 DOM 的所有订单进行分类,可令我们快速选择属于需求方或供应方的所有 DOM 订单。
接下来,添加 DOM 订单对象的整数型、实数型和字符串型属性的枚举:
//+------------------------------------------------------------------+ //| Integer properties of DOM order | //+------------------------------------------------------------------+ enum ENUM_MBOOK_ORD_PROP_INTEGER { MBOOK_ORD_PROP_STATUS = 0, // Order status MBOOK_ORD_PROP_TYPE, // Order type MBOOK_ORD_PROP_VOLUME, // Order volume }; #define MBOOK_ORD_PROP_INTEGER_TOTAL (3) // Total number of integer properties #define MBOOK_ORD_PROP_INTEGER_SKIP (0) // Number of integer DOM properties not used in sorting //+------------------------------------------------------------------+ //| Real properties of DOM order | //+------------------------------------------------------------------+ enum ENUM_MBOOK_ORD_PROP_DOUBLE { MBOOK_ORD_PROP_PRICE = MBOOK_ORD_PROP_INTEGER_TOTAL, // Order price MBOOK_ORD_PROP_VOLUME_REAL, // Extended accuracy order volume }; #define MBOOK_ORD_PROP_DOUBLE_TOTAL (2) // Total number of real properties #define MBOOK_ORD_PROP_DOUBLE_SKIP (0) // Number of real properties not used in sorting //+------------------------------------------------------------------+ //| String properties of DOM order | //+------------------------------------------------------------------+ enum ENUM_MBOOK_ORD_PROP_STRING { MBOOK_ORD_PROP_SYMBOL = (MBOOK_ORD_PROP_INTEGER_TOTAL+MBOOK_ORD_PROP_DOUBLE_TOTAL), // Order symbol name }; #define MBOOK_ORD_PROP_STRING_TOTAL (1) // Total number of string properties //+------------------------------------------------------------------+
我们根据创建的属性实现 DOM 订单的可能排序标准的枚举:
//+------------------------------------------------------------------+ //| Possible sorting criteria of DOM orders | //+------------------------------------------------------------------+ #define FIRST_MB_DBL_PROP (MBOOK_ORD_PROP_INTEGER_TOTAL-MBOOK_ORD_PROP_INTEGER_SKIP) #define FIRST_MB_STR_PROP (MBOOK_ORD_PROP_INTEGER_TOTAL-MBOOK_ORD_PROP_INTEGER_SKIP+MBOOK_ORD_PROP_DOUBLE_TOTAL-MBOOK_ORD_PROP_DOUBLE_SKIP) enum ENUM_SORT_MBOOK_ORD_MODE { //--- Sort by integer properties SORT_BY_MBOOK_ORD_STATUS = 0, // Sort by order status SORT_BY_MBOOK_ORD_TYPE, // Sort by order type SORT_BY_MBOOK_ORD_VOLUME, // Sort by order volume //--- Sort by real properties SORT_BY_MBOOK_ORD_PRICE = FIRST_MB_DBL_PROP, // Sort by order price SORT_BY_MBOOK_ORD_VOLUME_REAL, // Sort by extended accuracy order volume //--- Sort by string properties SORT_BY_MBOOK_ORD_SYMBOL = FIRST_MB_STR_PROP, // Sort by symbol name }; //+------------------------------------------------------------------+
现在可创建 DOM 抽象订单对象类。
在 \MQL5\Include\DoEasy\Objects\ 里,创建一个新的 Book\ 文件夹,其下的 MarketBookOrd.mqh 文件包含 CMarketBookOrd 类,继承自所有 CBaseObj 函数库对象的基准对象:
//+------------------------------------------------------------------+ //| MarketBookOrd.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" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\..\Services\DELib.mqh" #include "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| DOM abstract order class | //+------------------------------------------------------------------+ class CMarketBookOrd : public CBaseObj { private: int m_digits; // Number of decimal places long m_long_prop[MBOOK_ORD_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[MBOOK_ORD_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[MBOOK_ORD_PROP_STRING_TOTAL]; // String properties //--- Return the index of the array the (1) double and (2) string properties are actually located at int IndexProp(ENUM_MBOOK_ORD_PROP_DOUBLE property) const { return(int)property-MBOOK_ORD_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_MBOOK_ORD_PROP_STRING property) const { return(int)property-MBOOK_ORD_PROP_INTEGER_TOTAL-MBOOK_ORD_PROP_DOUBLE_TOTAL; } public: //--- Set object's (1) integer, (2) real and (3) string properties void SetProperty(ENUM_MBOOK_ORD_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_MBOOK_ORD_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Return object’s (1) integer, (2) real and (3) string property from the properties array long GetProperty(ENUM_MBOOK_ORD_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_MBOOK_ORD_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Return itself CMarketBookOrd *GetObject(void) { return &this;} //--- Return the flag of the object supporting this property virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_STRING property) { return true; } //--- Get description of (1) integer, (2) real and (3) string properties string GetPropertyDescription(ENUM_MBOOK_ORD_PROP_INTEGER property); string GetPropertyDescription(ENUM_MBOOK_ORD_PROP_DOUBLE property); string GetPropertyDescription(ENUM_MBOOK_ORD_PROP_STRING property); //--- Display the description of object 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 object in the journal virtual void PrintShort(void); //--- Return the object short name virtual string Header(void); //--- Compare CMarketBookOrd objects by all possible properties (to sort the lists by a specified order object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CMarketBookOrd objects by all properties (to search for equal request objects) bool IsEqual(CMarketBookOrd* compared_req) const; //--- Default constructor CMarketBookOrd(){;} protected: //--- Protected parametric constructor CMarketBookOrd(const ENUM_MBOOK_ORD_STATUS status,const MqlBookInfo &book_info,const string symbol); public: //+-------------------------------------------------------------------+ //|Methods of a simplified access to the DOM request object properties| //+-------------------------------------------------------------------+ //--- Return order (1) status, (2) type and (3) order volume ENUM_MBOOK_ORD_STATUS Status(void) const { return (ENUM_MBOOK_ORD_STATUS)this.GetProperty(MBOOK_ORD_PROP_STATUS); } ENUM_BOOK_TYPE TypeOrd(void) const { return (ENUM_BOOK_TYPE)this.GetProperty(MBOOK_ORD_PROP_TYPE); } long Volume(void) const { return this.GetProperty(MBOOK_ORD_PROP_VOLUME); } //--- Return (1) the price and (2) extended accuracy order volume double Price(void) const { return this.GetProperty(MBOOK_ORD_PROP_PRICE); } double VolumeReal(void) const { return this.GetProperty(MBOOK_ORD_PROP_VOLUME_REAL); } //--- Return (1) order symbol and (2) symbol's Digits string Symbol(void) const { return this.GetProperty(MBOOK_ORD_PROP_SYMBOL); } int Digits() const { return this.m_digits; } //--- Return the description of order (1) type (ENUM_BOOK_TYPE) and (2) status (ENUM_MBOOK_ORD_STATUS) virtual string TypeDescription(void) const { return this.StatusDescription(); } string StatusDescription(void) const; }; //+------------------------------------------------------------------+
该类的组成与函数库对象的其他类绝对相同。 我经常提到它们。 您可以在第一篇及后续文章中找到详细解说。
我们来看一下类方法的实现。
在类的封闭参数构造函数中,所有对象属性的设置值均来自 DOM 传递给构造函数的订单结构参数:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CMarketBookOrd::CMarketBookOrd(const ENUM_MBOOK_ORD_STATUS status,const MqlBookInfo &book_info,const string symbol) { //--- Save symbol’s Digits this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS); //--- Save integer object properties this.SetProperty(MBOOK_ORD_PROP_STATUS,status); this.SetProperty(MBOOK_ORD_PROP_TYPE,book_info.type); this.SetProperty(MBOOK_ORD_PROP_VOLUME,book_info.volume); //--- Save real object properties this.SetProperty(MBOOK_ORD_PROP_PRICE,book_info.price); this.SetProperty(MBOOK_ORD_PROP_VOLUME_REAL,book_info.volume_real); //--- Save additional object properties this.SetProperty(MBOOK_ORD_PROP_SYMBOL,(symbol==NULL || symbol=="" ? ::Symbol() : symbol)); } //+------------------------------------------------------------------+
创建新的 DOM 订单对象时,构造函数还会接收该类衍生对象中指定的订单状态。
该方法依据指定属性比较两个 CMarketBookOrd 对象,可定义两个对象指定属性的相等性:
//+------------------------------------------------------------------+ //| Compare CMarketBookOrd objects | //| by a specified property | //+------------------------------------------------------------------+ int CMarketBookOrd::Compare(const CObject *node,const int mode=0) const { const CMarketBookOrd *obj_compared=node; //--- compare integer properties of two objects if(mode<MBOOK_ORD_PROP_INTEGER_TOTAL) { long value_compared=obj_compared.GetProperty((ENUM_MBOOK_ORD_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_MBOOK_ORD_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare real properties of two objects else if(mode<MBOOK_ORD_PROP_DOUBLE_TOTAL+MBOOK_ORD_PROP_INTEGER_TOTAL) { double value_compared=obj_compared.GetProperty((ENUM_MBOOK_ORD_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_MBOOK_ORD_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare string properties of two objects else if(mode<MBOOK_ORD_PROP_DOUBLE_TOTAL+MBOOK_ORD_PROP_INTEGER_TOTAL+MBOOK_ORD_PROP_STRING_TOTAL) { string value_compared=obj_compared.GetProperty((ENUM_MBOOK_ORD_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_MBOOK_ORD_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+
该方法接收对象,并与当前对象的相同属性进行比较。 如果所比较对象的指定属性值小于当前对象的属性值,则返回 -1;如果较大,则返回 — +1,如果属性相等,则返回 0。
该方法比较两个 CMarketBookOrd 对象的所有属性。 它能够判断两个所比较对象是否完全相等:
//+------------------------------------------------------------------+ //| Compare CMarketBookOrd objects by all properties | //+------------------------------------------------------------------+ bool CMarketBookOrd::IsEqual(CMarketBookOrd *compared_obj) const { int beg=0, end=MBOOK_ORD_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_MBOOK_ORD_PROP_INTEGER prop=(ENUM_MBOOK_ORD_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=MBOOK_ORD_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_MBOOK_ORD_PROP_DOUBLE prop=(ENUM_MBOOK_ORD_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=MBOOK_ORD_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_MBOOK_ORD_PROP_STRING prop=(ENUM_MBOOK_ORD_PROP_STRING)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
在此,按顺序逐一比较两个对象的每个属性。 如果对象不等,则返回 false。 在两个对象所有属性的相等性检查完成后,若结果并非 false,则返回 true — 两个对象完全相同。
该方法在日志中显示所有对象属性:
//+------------------------------------------------------------------+ //| Display object properties in the journal | //+------------------------------------------------------------------+ void CMarketBookOrd::Print(const bool full_prop=false) { ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") ============="); int beg=0, end=MBOOK_ORD_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_MBOOK_ORD_PROP_INTEGER prop=(ENUM_MBOOK_ORD_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=MBOOK_ORD_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_MBOOK_ORD_PROP_DOUBLE prop=(ENUM_MBOOK_ORD_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=MBOOK_ORD_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_MBOOK_ORD_PROP_STRING prop=(ENUM_MBOOK_ORD_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"); } //+------------------------------------------------------------------+
在三个循环中显示后续每个属性的字符串说明按整数型、实数型和字符串型对象属性。
该方法返回指定的整数型、实数型和字符串型对象属性说明的方法:
//+------------------------------------------------------------------+ //| Return description of object's integer property | //+------------------------------------------------------------------+ string CMarketBookOrd::GetPropertyDescription(ENUM_MBOOK_ORD_PROP_INTEGER property) { return ( property==MBOOK_ORD_PROP_STATUS ? CMessage::Text(MSG_ORD_STATUS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.StatusDescription() ) : property==MBOOK_ORD_PROP_TYPE ? CMessage::Text(MSG_ORD_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.TypeDescription() ) : property==MBOOK_ORD_PROP_VOLUME ? CMessage::Text(MSG_MBOOK_ORD_VOLUME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : "" ); } //+------------------------------------------------------------------+ //| Return description of object's real property | //+------------------------------------------------------------------+ string CMarketBookOrd::GetPropertyDescription(ENUM_MBOOK_ORD_PROP_DOUBLE property) { int dg=(this.m_digits>0 ? this.m_digits : 1); return ( property==MBOOK_ORD_PROP_PRICE ? CMessage::Text(MSG_LIB_TEXT_REQUEST_PRICE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==MBOOK_ORD_PROP_VOLUME_REAL ? CMessage::Text(MSG_MBOOK_ORD_VOLUME_REAL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : "" ); } //+------------------------------------------------------------------+ //| Return description of object's string property | //+------------------------------------------------------------------+ string CMarketBookOrd::GetPropertyDescription(ENUM_MBOOK_ORD_PROP_STRING property) { return(property==MBOOK_ORD_PROP_SYMBOL ? CMessage::Text(MSG_LIB_PROP_SYMBOL)+": \""+this.GetProperty(property)+"\"" : ""); } //+------------------------------------------------------------------+
每个方法都接收需返回其描述的属性。 取决于传递给该方法的属性,创建并最终返回说明字符串。
该方法返回一个对象名称的简述:
//+------------------------------------------------------------------+ //| Return the object short name | //+------------------------------------------------------------------+ string CMarketBookOrd::Header(void) { return this.TypeDescription()+" \""+this.Symbol()+"\""; } //+------------------------------------------------------------------+
该方法返回由订单类型的描述、极其品种组成的字符串。
该方法在日志中显示对象简述:
//+------------------------------------------------------------------+ //| Display a short description of the object in the journal | //+------------------------------------------------------------------+ void CMarketBookOrd::PrintShort(void) { ::Print(this.Header()); } //+------------------------------------------------------------------+
该方法在日志中简单地显示由先前方法创建的字符串。
该方法返回 DOM 中的订单状态描述:
//+------------------------------------------------------------------+ //| Return the order status description in DOM | //+------------------------------------------------------------------+ string CMarketBookOrd::StatusDescription(void) const { return ( Status()==MBOOK_ORD_STATUS_SELL ? CMessage::Text(MSG_MBOOK_ORD_STATUS_SELL) : Status()==MBOOK_ORD_STATUS_BUY ? CMessage::Text(MSG_MBOOK_ORD_STATUS_BUY) : "" ); } //+------------------------------------------------------------------+
根据订单“状态”,返回含有状态描述的字符串。
这就是 DOM 的完整订单对象类。
现在,我们需要创建四个类,继承自该抽象订单对象。 衍生类将用于创建来自 DOM 的新订单对象。根据订单类型,将在衍生类构造函数的初始化列表中指定需创建的订单对象状态。
在 \MQL5\Include\DoEasy\Objects\Book\ 里,创建包含 CMarketBookBuy 类的 MarketBookBuy.mqh 文件。 新创建的 CMarketBookOrd 抽象订单类将成为父类:
//+------------------------------------------------------------------+ //| MarketBookBuy.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 "MarketBookOrd.mqh" //+------------------------------------------------------------------+ //| Buy order in DOM | //+------------------------------------------------------------------+ class CMarketBookBuy : public CMarketBookOrd { private: public: //--- Constructor CMarketBookBuy(const string symbol,const MqlBookInfo &book_info) : CMarketBookOrd(MBOOK_ORD_STATUS_BUY,book_info,symbol) {} //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property); //--- Return the object short name virtual string Header(void); //--- Return the description of order type (ENUM_BOOK_TYPE) virtual string TypeDescription(void); }; //+------------------------------------------------------------------+ //| Return 'true' if an order supports a passed | //| integer property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CMarketBookBuy::SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property) { return true; } //+------------------------------------------------------------------+ //| Return 'true' if an order supports a passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CMarketBookBuy::SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property) { return true; } //+------------------------------------------------------------------+ //| Return the object short name | //+------------------------------------------------------------------+ string CMarketBookBuy::Header(void) { return CMessage::Text(MSG_MBOOK_ORD_TYPE_BUY)+" \""+this.Symbol()+ "\": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]"; } //+------------------------------------------------------------------+ //| Return the description of order type | //+------------------------------------------------------------------+ string CMarketBookBuy::TypeDescription(void) { return CMessage::Text(MSG_MBOOK_ORD_TYPE_BUY); } //+------------------------------------------------------------------+
创建新的 DOM 订单对象时,在父类构造函数中设置“买方”。
在虚拟方法中返回支持整数型和实数型属性的标志,返回 true — 对象支持每个属性。
在虚拟方法里返回 DOM 订单对象的简称,返回的字符串格式如下
Type "Symbol": Price [VolumeReal]
例如:
"EURUSD" buy order: 1.20123 [10.00]
在虚拟方法里返回 DOM 订单对象类型的描述,返回 “Buy order” 字符串。
除了订单状态,继承自 DOM 抽象订单基类的其余三个类与已研究过的相同。 每个类的构造函数特征是与所描述订单对象的状态对应,其虚拟方法返回与每个对象所描述的 DOM 订单类型相对应的字符串。 所有这些类都与上述类位于同一文件夹当中。 我会在此展示它们的清单,让您分析和比较它们的虚拟方法。
MarketBookBuyMarket.mqh:
//+------------------------------------------------------------------+ //| MarketBookBuyMarket.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 "MarketBookOrd.mqh" //+------------------------------------------------------------------+ //| Buy order by Market in DOM | //+------------------------------------------------------------------+ class CMarketBookBuyMarket : public CMarketBookOrd { private: public: //--- Constructor CMarketBookBuyMarket(const string symbol,const MqlBookInfo &book_info) : CMarketBookOrd(MBOOK_ORD_STATUS_BUY,book_info,symbol) {} //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property); //--- Return the object short name virtual string Header(void); //--- Return the description of order type (ENUM_BOOK_TYPE) virtual string TypeDescription(void); }; //+------------------------------------------------------------------+ //| Return 'true' if an order supports a passed | //| integer property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CMarketBookBuyMarket::SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property) { return true; } //+------------------------------------------------------------------+ //| Return 'true' if an order supports a passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CMarketBookBuyMarket::SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property) { return true; } //+------------------------------------------------------------------+ //| Return the object short name | //+------------------------------------------------------------------+ string CMarketBookBuyMarket::Header(void) { return CMessage::Text(MSG_MBOOK_ORD_TYPE_BUY_MARKET)+" \""+this.Symbol()+ "\": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]"; } //+------------------------------------------------------------------+ //| Return the description of order type | //+------------------------------------------------------------------+ string CMarketBookBuyMarket::TypeDescription(void) { return CMessage::Text(MSG_MBOOK_ORD_TYPE_BUY_MARKET); } //+------------------------------------------------------------------+
MarketBookSell.mqh:
//+------------------------------------------------------------------+ //| MarketBookSell.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 "MarketBookOrd.mqh" //+------------------------------------------------------------------+ //| Sell order in DOM | //+------------------------------------------------------------------+ class CMarketBookSell : public CMarketBookOrd { private: public: //--- Constructor CMarketBookSell(const string symbol,const MqlBookInfo &book_info) : CMarketBookOrd(MBOOK_ORD_STATUS_SELL,book_info,symbol) {} //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property); //--- Return the object short name virtual string Header(void); //--- Return the description of order type (ENUM_BOOK_TYPE) virtual string TypeDescription(void); }; //+------------------------------------------------------------------+ //| Return 'true' if an order supports a passed | //| integer property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CMarketBookSell::SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property) { return true; } //+------------------------------------------------------------------+ //| Return 'true' if an order supports a passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CMarketBookSell::SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property) { return true; } //+------------------------------------------------------------------+ //| Return the object short name | //+------------------------------------------------------------------+ string CMarketBookSell::Header(void) { return CMessage::Text(MSG_MBOOK_ORD_TYPE_SELL)+" \""+this.Symbol()+ "\": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]"; } //+------------------------------------------------------------------+ //| Return the description of order type | //+------------------------------------------------------------------+ string CMarketBookSell::TypeDescription(void) { return CMessage::Text(MSG_MBOOK_ORD_TYPE_SELL); } //+------------------------------------------------------------------+
MarketBookSellMarket.mqh:
//+------------------------------------------------------------------+ //| MarketBookSellMarket.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 "MarketBookOrd.mqh" //+------------------------------------------------------------------+ //| Sell order by Market in DOM | //+------------------------------------------------------------------+ class CMarketBookSellMarket : public CMarketBookOrd { private: public: //--- Constructor CMarketBookSellMarket(const string symbol,const MqlBookInfo &book_info) : CMarketBookOrd(MBOOK_ORD_STATUS_SELL,book_info,symbol) {} //--- Supported order properties (1) real, (2) integer virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property); //--- Return the object short name virtual string Header(void); //--- Return the description of order type (ENUM_BOOK_TYPE) virtual string TypeDescription(void); }; //+------------------------------------------------------------------+ //| Return 'true' if an order supports a passed | //| integer property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CMarketBookSellMarket::SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property) { return true; } //+------------------------------------------------------------------+ //| Return 'true' if an order supports a passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CMarketBookSellMarket::SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property) { return true; } //+------------------------------------------------------------------+ //| Return the object short name | //+------------------------------------------------------------------+ string CMarketBookSellMarket::Header(void) { return CMessage::Text(MSG_MBOOK_ORD_TYPE_SELL_MARKET)+" \""+this.Symbol()+ "\": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]"; } //+------------------------------------------------------------------+ //| Return the description of order type | //+------------------------------------------------------------------+ string CMarketBookSellMarket::TypeDescription(void) { return CMessage::Text(MSG_MBOOK_ORD_TYPE_SELL_MARKET); } //+------------------------------------------------------------------+
这些就是我在本文中想要做的所有事情。
为了执行测试,我们借用上一篇文章中的 EA,并将其保存在 \MQL5\Experts\TestDoEasy\Part63\,TestDoEasyPart63.mq5。
启动 EA 之后,按照设置中指定的操作品种,我们订阅 DOM。 所有 DOM 事件都在 OnBookEvent() 处理程序中注册。 相应地,在此处理程序中,我们确保事件已在当前品种上发生。 我们还得到 DOM 快照,并将所有现有订单保存到按价格值排序的列表当中。 接下来,在图表注释中显示列表中的第一笔和最后一笔订单。 因此,我们将显示两端的 DOM 订单 — 买单,卖单各一。 在日志中,首次激活 OnBookEvent() 时显示所有得到的 DOM 订单的列表。
为了令 EA 能够查看新创建的类,将它们包括在 EA 文件里(当前,无法从 CEngine 函数库主对象访问它们):
//+------------------------------------------------------------------+ //| TestDoEasyPart63.mq5 | //| 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" //--- includes #include <DoEasy\Engine.mqh> #include <DoEasy\Objects\Book\MarketBookBuy.mqh> #include <DoEasy\Objects\Book\MarketBookSell.mqh> #include <DoEasy\Objects\Book\MarketBookBuyMarket.mqh> #include <DoEasy\Objects\Book\MarketBookSellMarket.mqh> //--- enums
现在,我们需要在 EA 中创建 OnBookEvent() 处理程序,并在其中实现 DOM 事件的处理:
//+------------------------------------------------------------------+ //| OnBookEvent function | //+------------------------------------------------------------------+ void OnBookEvent(const string& symbol) { static bool first=true; //--- Get a symbol object CSymbol *sym=engine.GetSymbolCurrent(); //--- If failed to get a symbol object or it is not subscribed to DOM, exit if(sym==NULL || !sym.BookdepthSubscription()) return; //--- create the list for storing DOM order objects CArrayObj *list=new CArrayObj(); if(list==NULL) return; //--- Work by the current symbol if(symbol==sym.Name()) { //--- Declare the DOM structure array MqlBookInfo book_array[]; //--- Get DOM entries to the structure array if(!MarketBookGet(sym.Name(),book_array)) return; //--- clear the list list.Clear(); //--- In the loop by the structure array int total=ArraySize(book_array); for(int i=0;i<total;i++) { //--- Create order objects of the current DOM snapshot depending on the order type CMarketBookOrd *mbook_ord=NULL; switch(book_array[i].type) { case BOOK_TYPE_BUY : mbook_ord=new CMarketBookBuy(sym.Name(),book_array[i]); break; case BOOK_TYPE_SELL : mbook_ord=new CMarketBookSell(sym.Name(),book_array[i]); break; case BOOK_TYPE_BUY_MARKET : mbook_ord=new CMarketBookBuyMarket(sym.Name(),book_array[i]); break; case BOOK_TYPE_SELL_MARKET : mbook_ord=new CMarketBookSellMarket(sym.Name(),book_array[i]); break; default: break; } if(mbook_ord==NULL) continue; //--- Set the sorted list flag for the list (by the price value) and add the current order object to it list.Sort(SORT_BY_MBOOK_ORD_PRICE); if(!list.InsertSort(mbook_ord)) delete mbook_ord; } //--- Get the very first and last DOM order objects from the list CMarketBookOrd *ord_0=list.At(0); CMarketBookOrd *ord_N=list.At(list.Total()-1); if(ord_0==NULL || ord_N==NULL) return; //--- Display the size of the current DOM snapshot in the chart comment, //--- the maximum number of displayed orders in DOM for a symbol and //--- the highest and lowest orders of the current DOM snapshot Comment ( DFUN,sym.Name(),": ",TimeMSCtoString(sym.Time()),", array total=",total,", book size=",sym.TicksBookdepth(),", list.Total: ",list.Total(),"\n", "Max: ",ord_N.Header(),"\nMin: ",ord_0.Header() ); //--- Display the first DOM snapshot in the journal if(first) { for(int i=list.Total()-1;i>WRONG_VALUE;i--) { CMarketBookOrd *ord=list.At(i); ord.PrintShort(); } first=false; } } //--- Delete the created list delete list; } //+------------------------------------------------------------------+
代码注释包含所有详细信息。 如果您有任何疑问,请随时在评论中提问。
编译 EA,在设置中初步定义的品种图表上启动它,它会使用两个指定品种和当前时间帧。
启动 EA,且第一个 DOM 变化事件到达后,当前 DOM 快照列表的参数将与两笔订单一起显示在图表注释中,最高的出价(Buy)和最低的要价(Ask):
日志显示当前 DOM 快照的所有订单的列表:
Subscribed to Depth of Market AUDUSD Subscribed to Depth of Market EURUSD Library initialization time: 00:00:11.391 "EURUSD" sell order: 1.20250 [250.00] "EURUSD" sell order: 1.20245 [100.00] "EURUSD" sell order: 1.20244 [50.00] "EURUSD" sell order: 1.20242 [36.00] "EURUSD" buy order: 1.20240 [16.00] "EURUSD" buy order: 1.20239 [20.00] "EURUSD" buy order: 1.20238 [50.00] "EURUSD" buy order: 1.20236 [100.00] "EURUSD" buy order: 1.20232 [250.00]
在下一篇文章中,我们将继续创建操控 DOM 的功能。
以下是该函数库当前版本的所有文件,以及 MQL5 的测试 EA 文件,供您测试和下载。
操控 DOM 的类正在开发当中,因此,在现阶段强烈建议不要在自定义程序中使用它们。
请您在评论中留下问题和建议。
返回内容目录
*该系列的前几篇文章:
DoEasy 函数库中的价格(第五十九部分):存储一个即时报价数据的对象
DoEasy 函数库中的价格(第六十部分):品种即时报价数据的序列列表
DoEasy 函数库中的价格(第六十一部分):品种即时报价序列集合
DoEasy 函数库中的价格(第六十二部分):实时更新即时报价序列,为操控市场深度做准备
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程