Prices in DoEasy library (part 63): Depth of Market and its abstract request class

Artyom Trishkin | 30 March, 2021

Contents


Concept

In this article, I will start implementing the functionality for working with the Depth of Market (DOM). Conceptually, classes for working with DOM will not differ from all previously implemented library classes. At the same time, we will have a mold of DOM featuring data about orders stored in DOM. The data is obtained by the MarketBookGet() function when the OnBookEvent() handler is activated. In case of any change in DOM, an event is activated for each of the symbols in the handler having the active subscription to DOM events.

Thus, the DOM class structure is to be as follows:

  1. DOM order object class — the object describing data of one order out of multiple orders obtained from DOM when OnBookEvent() handler is triggered for one symbol;
  2. DOM mold object class — the object describing data on all orders obtained from DOM simultaneously at a single activation of the OnBookEvent() handler for a single symbol — p1 set of objects making up the current DOM mold;
  3. Timeseries class consisting of the p2 object sequence entered into the timeseries list at each OnBookEvent() activation for a single symbol;
  4. Timeseries collection class of DOM data of all used symbols with enabled subscription to DOM events.

Today I will implement the order object class (1) and test obtaining DOM data when OnBookEvent() is activated for the current symbol.

The properties of each order are set in the MqlBookInfo structure providing data in DOM:

  • order type from the ENUM_BOOK_TYPE enumeration
  • order price
  • order volume
  • extended accuracy order volume

DOM may feature four order types (from the ENUM_BOOK_TYPE enumeration):

  • Sell order
  • Sell order by Market
  • Buy order
  • Buy order by Market

As we can see, there are four order types — two Buy and two Sell ones. To divide all types of orders into two sides, we should add one more property to the already existing ones — order status indicating its direction — Buy or Sell order. This will allow us to quickly divide all orders into their sides — supply and demand.

The object of a single DOM request will be made similar to order objects (as well as many other library objects) — we will have a basic object of the DOM abstract order and four descendant objects with the order type specification. The concept of constructing such objects was considered at the very beginning of the library development in the first and second articles.

    Before implementing the classes for working with DOM, add new library messages and slightly improve the tick data object classes. Add new message indices in \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
    
      };
    //+------------------------------------------------------------------+
    
    

    and message texts corresponding to newly added indices:

       {"В окно \"Обзор рынка\" добавлен символ","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"},
       
      };
    //+---------------------------------------------------------------------+
    
    

    Add displaying messages about the error when subscribing to DOM in \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh file of the symbol object class:

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

    and do the same when unsubscribing from it:

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

    From the tick series update method of the tick series class in \MQL5\Include\DoEasy\Objects\Ticks\TickSeries.mqh, remove displaying debugging comments on the symbol chart we left for tests in the previous article:

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

    The last tick time is now immediately set in the m_last_time variable meant for this purpose because I needed to display verification data as a symbol chart comment featuring the previous and current tick times in the previous article. Now we do not need it and the time is immediately saved in the variable:

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


    Class of the abstract order object in the Depth of Market

    Like with all library objects having enumeration sets for defining object property constants, we need to create enumerations of integer, real and string object properties for DOM orders as well.

    Add enumerations of DOM order object properties and parameters in \MQL5\Include\DoEasy\Defines.mqh. Since I am not going to implement an event model of working with each of orders in DOM (at one moment in time the order book displays the current state of all orders, and their change leads to its next state and is processed at the next activation of OnBookEvent()), simply add the constant specifying the code of the next event after the last code of the DOM event simply to maintain the identity of the constants of all objects to bring them to the same form:

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

    Define the enumeration featuring two possible states of a single DOM order — Buy or Sell side:

    //+------------------------------------------------------------------+
    //| Abstract DOM type (status)                                       |
    //+------------------------------------------------------------------+
    enum ENUM_MBOOK_ORD_STATUS
      {
       MBOOK_ORD_STATUS_BUY,                              // Buy side
       MBOOK_ORD_STATUS_SELL,                             // Sell side
      };
    //+------------------------------------------------------------------+
    
    

    Sorting out all orders in DOM by these properties allows us to quickly select all orders in DOM belonging either to demand or to supply by these properties.

    Next, add enumerations of the integer, real and string properties of DOM order object properties:

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

    Let's implement the enumeration of possible criteria of sorting orders in DOM according to created properties:

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

    Now it is possible to create the abstract order object class in DOM.

    In \MQL5\Include\DoEasy\Objects\, create the new Book\ folder containing the MarketBookOrd.mqh file of the CMarketBookOrd class inherited from the basic object of all CBaseObj library objects:

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

    The composition of the class is absolutely identical to other classes of library objects. I mentioned them quite often. You can find the detailed descriptions in the first and subsequent articles.

    Let's have a look at the implementation of the class methods.

    In the closed class parametric constructor, set all object properties from the order structure passed from DOM to the constructor:

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

    The constructor also receives the order status specified in descendant objects of the class when creating a new DOM order object.

    The method of comparing two CMarketBookOrd objects by a specified property for defining the equality of the specified properties of two objects:

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

    The method receives the object whose property should be compared with the same property of the current object. If the specified property value of the compared object is lower than the current one, -1 is returned, if larger — +1, if the properties are equal — 0.

    The method for comparing two CMarketBookOrd objects by all properties. It allows determining the complete identity of two compared objects:

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

    Each subsequent property of two objects is compared one by one here. If the objects are not equal, false is returned. After checking the equality of all properties of two objects is complete and no false is obtained, return true — both objects are completely identical.

    The method displaying all object properties in the journal:

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

    String descriptions of each subsequent property are displayed in three loops by integer, real and string object properties.

    The methods returning the descriptions of the specified integer, real and string object properties:

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

    Each of the methods receives the property whose description should be returned. Depending on the property passed to the method, a string to be eventually returned from the method is created.

    The method returning a short object name:

    //+------------------------------------------------------------------+
    //| Return the object short name                                     |
    //+------------------------------------------------------------------+
    string CMarketBookOrd::Header(void)
      {
       return this.TypeDescription()+" \""+this.Symbol()+"\"";
      }
    //+------------------------------------------------------------------+
    
    

    The method returns the string consisting of the description of an order type and its symbol.

    The method displaying the short object description in the journal:

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

    The method simply displays the string created by the previous method in the journal.

    The method returning the order status description in 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)  :
          ""
         );
      }
    //+------------------------------------------------------------------+
    
    

    Depending on the order "status", the string with the status description is returned.

    This is the entire order object class in DOM.

    Now we need to create four classes that inherit from this abstract order object. The descendant classes will be used to create new order objects from DOM. The status of the created order object will be specified in the initialization list of the descendant class constructor depending on the order type.


    Descendant classes of the abstract order object

    In \MQL5\Include\DoEasy\Objects\Book\, create the MarketBookBuy.mqh file of the CMarketBookBuy class. The newly created CMarketBookOrd abstract order class is to be a parent class:

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

    When creating a new DOM order object, set the "Buy side" in the parent class constructor.

    In the virtual methods returning the flag of supporting the integer and real properties, return true — each property is supported by the object.

    In the virtual method returning the short name of the DOM order object, return the string in the following format

    Type "Symbol": Price [VolumeReal]

    For example:

    "EURUSD" buy order: 1.20123 [10.00]

    In the virtual method returning the description of the DOM order object type, return the "Buy order" string.

    The remaining three classes inherited from the DOM abstract order base class are identical to the considered one except for the order status. Each class constructor features its status corresponding to the described order object and its virtual methods returning the strings corresponding to the type of the DOM order described by each of the objects. All these classes are located in the same folder as the one described above. I will show their listings here allowing you to analyze and compare their virtual methods.

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

    This is all I wanted to do in the current article.


    Test

    To perform the test, I will use the EA from the previous article and save it in \MQL5\Experts\TestDoEasy\Part63\ as TestDoEasyPart63.mq5.

    After launching the EA, we subscribe to DOMs of symbols specified in the settings for work. All events with DOMs are registered in the OnBookEvent() handler. Accordingly, in this handler, we make sure that the event has occurred on the current symbol. We also get the DOM snapshot and save all existing orders to the list sorted by price values. Next, display the very first and last orders from the list in the chart comments. Thus, we will display two extreme DOM orders — sell and buy ones. In the journal, display the list of all obtained DOM orders at the very first OnBookEvent() activation.

    To enable the EA to see the newly created classes, include them to the EA file (currently, they cannot be accessed from the CEngine library main object):

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

    Now we need to create the OnBookEvent() handler in the EA and implement handling a DOM event in it:

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

    The code comments contain all the details. If you have any questions, feel free to ask them in the comments.

    Compile the EA and launch it on a symbol chart having preliminarily defined in the settings to use the two specified symbols and the current timeframe.


    After the EA is launched and the first DOM change event arrives, the parameters of the current DOM snapshot list are displayed in the chart comments together with two orders — the highest Buy and the lowest Sell one:


    The journal displays the list of all orders of the current DOM snapshot:

    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]
    
    


    What's next?

    In the next article, we will continue creating the functionality for working with DOM.

    All files of the current version of the library are attached below together with the test EA file for MQL5 for you to test and download.
    The classes for working with DOM are under development, therefore their use in custom programs at this stage is strongly not recommended.
    Leave your questions and suggestions in the comments.

    Back to contents

    *Previous articles within the series:

    Prices in DoEasy library (part 59): Object to store data of one tick
    Prices in DoEasy library (part 60): Series list of symbol tick data
    Prices in DoEasy library (part 61): Collection of symbol tick series
    Prices in DoEasy library (part 62): Updating tick series in real time, preparation for working with Depth of Market