English Русский 中文 Español Deutsch 日本語
Trabalhando com preços na biblioteca DoEasy (Parte 64): livro de ofertas, classes do objeto-instantâneo e objeto-série de instantâneos do livro de ofertas

Trabalhando com preços na biblioteca DoEasy (Parte 64): livro de ofertas, classes do objeto-instantâneo e objeto-série de instantâneos do livro de ofertas

MetaTrader 5Exemplos | 4 maio 2021, 10:55
531 0
Artyom Trishkin
Artyom Trishkin

Sumário


Ideia

No último artigo criamos uma classe de objeto de ordem abstrata do livro de ofertas e classes herdeiras desse objeto. Tal universo de objetos constitui um instantâneo do livro de ofertas, obtido numa chamada da função MarketBookGet() no momento em que o manipulador OnBookEvent() é ativado. Os dados recebidos pela função MarketBookGet() são gravados numa série de estruturas MqlBookInfo, e com base nos dados recebidos, podemos criar um objeto-instantâneo do livro de ofertas, que armazenará todas as ordens, recebidas na matriz de estruturas MqlBookInfo. Em outras palavras, os dados na matriz sonora de estruturas serão um instantâneo do livro de ofertas, em que cada membro da estrutura será representado por um objeto-ordem do livro de ofertas. Cada acionamento do manipulador OnBookEvent() desencadeará a criação de outro instantâneo do livro de ofertas, que, por sua vez, inseriremos no objeto de classe de uma série de instantâneos do livro de ofertas.
A lista para armazenamento de objetos-instantâneos do livro de ofertas será uma classe de matriz dinâmica de ponteiros para instâncias da classe CObject e seus herdeiros da biblioteca padrão. Limitaremos o tamanho desta lista ao número especificado de objetos. Por padrão, o tamanho desta lista não excederá 200 000 objetos-instantâneos do livro de ofertas, o que deve abranger aproximadamente um ou dois dias de negociação. Essas listas-séries de instantâneos do livro de ofertas serão criadas para cada símbolo usado no programa. Como resultado, cada uma dessas listas será armazenada na coleção de dados do livro de ofertas.

Hoje vamos criar duas classes - a classe de objeto-instantâneo do livro de ofertas de um símbolo e a classe-série de instantâneos do livro de ofertas de um símbolo. No próximo artigo, criaremos e testaremos uma classe-coleção dessas séries-instantâneos.


Aprimorando as classes da biblioteca

No arquivo \MQL5\Include\DoEasy\Data.mqh inserimos os índices das novas mensagens da biblioteca:

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

//--- CMarketBookSnapshot
   MSG_MBOOK_SNAP_TEXT_SNAPSHOT,                      // DOM snapshot
   
//--- CMBookSeries
   MSG_MBOOK_SERIES_TEXT_MBOOKSERIES,                 // DOM snapshot series
   
  };
//+------------------------------------------------------------------+

e as mensagens de texto correspondentes aos índices adicionados recentemente:

//--- 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"},
   
//--- CMarketBookSnapshot
   {"Снимок стакана цен","Depth of Market Snapshot"},
   
//--- CMBookSeries
   {"Серия снимков стакана цен","Series of shots of the Depth of Market"},
   
  };
//+---------------------------------------------------------------------+

Para que possamos especificar o critério de classificação por tempo ao pesquisar os instantâneos do livro de ofertas exigidos na lista-série, precisaremos adicionar uma nova propriedade ao objeto-ordem de livro de ofertas - o tempo de recebimento do instantâneo em milissegundos. A ordem em si não tem essa propriedade, mas podemos rastrear o tempo de recebimento do instantâneo do livro de ofertas. Além disso, para não introduzir novas enumerações para a listas-série de instantâneos com apenas uma propriedade inteira (o tempo do instantâneo em milissegundos), vamos adicionar esta propriedade ao objeto-ordem. Cada ordem num objeto-instantâneo terá a hora de recebimento deste instantâneo e usaremos essa constante recém-adicionada para pesquisar e classificar na lista-série.

No arquivo \MQL5\Include\DoEasy\Defines.mqh vamos escrever os parâmetros das séries de instantâneos do livro de ofertas, precisamos deles para indicar o número de dias de dados e o número máximo possível de instantâneos na lista:

//--- Tick series parameters
#define TICKSERIES_DEFAULT_DAYS_COUNT  (1)                        // Required number of days for tick data in default series
#define TICKSERIES_MAX_DATA_TOTAL      (200000)                   // Maximum number of stored tick data of a single symbol
//--- Parameters of the DOM snapshot series
#define MBOOKSERIES_DEFAULT_DAYS_COUNT (1)                        // The default required number of days for DOM snapshots in the series
#define MBOOKSERIES_MAX_DATA_TOTAL     (200000)                   // Maximum number of stored DOM snapshots of a single symbol
//+------------------------------------------------------------------+

Não usaremos o primeiro parâmetro (o número de dias) - no futuro, tentaremos vincular esses dados ao número de dias de dados de ticks, e agora usaremos apenas o segundo parâmetro - o número máximo possível de dados do livro de ofertas.

No mesmo arquivo escrevemos uma nova propriedade inteira do objeto-ordem de livro de ofertas - tempo em milissegundos,
e aumentamos o número de propriedades inteiras do objeto para 4:

//+------------------------------------------------------------------+
//| 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
   MBOOK_ORD_PROP_TIME_MSC,                           // Time of making a DOM snapshot in milliseconds
  }; 
#define MBOOK_ORD_PROP_INTEGER_TOTAL (4)              // Total number of integer properties
#define MBOOK_ORD_PROP_INTEGER_SKIP  (0)              // Number of integer DOM properties not used in sorting
//+------------------------------------------------------------------+

Uma vez que adicionamos uma nova propriedade inteira, precisamos adicionar um novo critério de classificação para propriedades inteiras:

//+------------------------------------------------------------------+
//| 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_MBOOK_ORD_TIME_MSC,                        // Sort by time of making a DOM snapshot in milliseconds
//--- 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
  };
//+------------------------------------------------------------------+

É esta constante que indicaremos como o parâmetro necessário para ordenar os objetos-instantâneos de livro de ofertas na nova classe de objetos-série de instantâneos que está sendo criada agora.

Aqui precisamos criar métodos para pesquisar e classificar objetos-ordens do livro de ofertas no arquivo da classe CSelect armazenada em \MQL5\Include\DoEasy\Services\Select.mqh. Uma descrição detalhada do que escrevemos pode ser encontrada no terceiro artigo. Agora vamos simplesmente descrever todas as modificações necessárias desta classe para gerar a pesquisa e classificação por propriedades dos objetos-ordens do livro de ofertas.

Vamos anexar ao arquivo a classe de objeto-ordem abstrata do livro de ofertas:

//+------------------------------------------------------------------+
//|                                                       Select.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\Event.mqh"
#include "..\Objects\Accounts\Account.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\PendRequest\PendRequest.mqh"
#include "..\Objects\Series\SeriesDE.mqh"
#include "..\Objects\Indicators\Buffer.mqh"
#include "..\Objects\Indicators\IndicatorDE.mqh"
#include "..\Objects\Indicators\DataInd.mqh"
#include "..\Objects\Ticks\DataTick.mqh"
#include "..\Objects\Book\MarketBookOrd.mqh"
//+------------------------------------------------------------------+

No final do corpo da classe declaramos todos os métodos necessários:

//+------------------------------------------------------------------+
//| Methods of working with DOM data                                 |
//+------------------------------------------------------------------+
   //--- Return the list of DOM data with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByMBookProperty(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByMBookProperty(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByMBookProperty(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the DOM data index in the list with the maximum value of (1) integer, (2) real and (3) string property of data
   static int        FindMBookMax(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_INTEGER property);
   static int        FindMBookMax(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_DOUBLE property);
   static int        FindMBookMax(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_STRING property);
   //--- Return the DOM data index in the list with the minimum value of (1) integer, (2) real and (3) string property of data
   static int        FindMBookMin(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_INTEGER property);
   static int        FindMBookMin(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_DOUBLE property);
   static int        FindMBookMin(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_STRING property);
//---
  };
//+------------------------------------------------------------------+

Fora do corpo da classe, escrevemos sua implementação:

//+------------------------------------------------------------------+
//| Methods of working with DOM data                                 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the list of DOM data with one of integer                  |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByMBookProperty(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      CMarketBookOrd *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      long obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of DOM data with one of real                     |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByMBookProperty(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CMarketBookOrd *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      double obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of DOM data with one of string                   |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByMBookProperty(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CMarketBookOrd *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      string obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the DOM data index in the list                            |
//| with the maximum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindMBookMax(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CMarketBookOrd *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CMarketBookOrd *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      long obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the DOM data index in the list                            |
//| with the maximum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindMBookMax(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CMarketBookOrd *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CMarketBookOrd *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      double obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the DOM data index in the list                            |
//| with the maximum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindMBookMax(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CMarketBookOrd *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CMarketBookOrd *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      string obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the DOM data index in the list                            |
//| with the minimum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindMBookMin(CArrayObj* list_source,ENUM_MBOOK_ORD_PROP_INTEGER property)
  {
   int index=0;
   CMarketBookOrd *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CMarketBookOrd *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      long obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the DOM data index in the list                            |
//| with the minimum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindMBookMin(CArrayObj* list_source,ENUM_MBOOK_ORD_PROP_DOUBLE property)
  {
   int index=0;
   CMarketBookOrd *min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CMarketBookOrd *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      double obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the DOM data index in the list                            |
//| with the minimum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindMBookMin(CArrayObj* list_source,ENUM_MBOOK_ORD_PROP_STRING property)
  {
   int index=0;
   CMarketBookOrd *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CMarketBookOrd *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      string obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+

A lógica dos métodos é idêntica à lógica da operação de outros semelhantes desta classe que criamos anteriormente para outros objetos da biblioteca. Por isso, em vez de nos deter na descrição do trabalho dos métodos, vamos apontar que sempre podemos rever o material já visto repetidamente acessando uma descrição de como esses métodos funcionam.

Como agora apresentaremos um novo parâmetro - o momento em que o instantâneo do livro de ofertas é recebido, este tempo será escrito no objeto da ordem abstrata da livro de ofertas. Na estrutura MqlBookInfo, que descreve a ordem do livro de ofertas, não há parâmetro de tempo, portanto, teremos que inserir por conta própria o tempo de recebimento do instantâneo para cada objeto-ordem do livro de ofertas.

Para fazer isso, no arquivo de classe de ordem abstrata do livro de ofertas \MQL5\Include\DoEasy\Objects\Book\MarketBookOrd.mqh inserimos o novo método público:

public:
//+-------------------------------------------------------------------+ 
//|Methods of a simplified access to the DOM request object properties|
//+-------------------------------------------------------------------+
//--- Set a snapshot time - all orders of a single DOM snapshot have the same name
   void              SetTime(const long time_msc)  { this.SetProperty(MBOOK_ORD_PROP_TIME_MSC,time_msc);                      }
   
//--- Return order (1) status, (2) type and (3) order volume

O método simplesmente define o valor do tempo passado em milissegundos para a nova propriedade do objeto.

No construtor paramétrico da classe abstrata do livro de ofertas inicializamos o tempo do instantâneo como zero:

//+------------------------------------------------------------------+
//| 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));
//--- Order time is not present in the parameters and is considered in the DOM snapshot class. Reset the time
   this.SetProperty(MBOOK_ORD_PROP_TIME_MSC,0);
  }
//+------------------------------------------------------------------+

Vamos definir o tempo para cada uma das ordens presentes num instantâneo do livro de ofertas no momento em que este último é recebido.

Também no arquivo de classe de ordem abstrata do livro de ofertas MarketBookOrd.mqh e nos arquivos de classes de seus herdeiros MarketBookBuy.mqh, MarketBookBuyMarket.mqh, MarketBookSell.mqh e MarketBookSellMarket.mqh fizemos pequenas alterações nos métodos virtuais para descrever objetos:

//--- Display a short description of the object in the journal
   virtual void      PrintShort(const bool symbol=false);
//--- Return the object short name
   virtual string    Header(const bool symbol=false);

A cada um dos métodos foram adicionados sinalizadores indicando a necessidade de exibir o nome do símbolo na descrição do objeto. Por padrão, o símbolo não é exibido na descrição do objeto-ordem. Isso acontece porque o objeto-ordem não é, por assim dizer, um objeto independente, mas, em vez disso, faz parte do instantâneo do livro de ofertas, cuja classe será feita hoje. E ao exibir a descrição do objeto-instantâneo do livro de ofertas, onde, além da descrição do objeto, são exibidas as descrições de todas as ordens incluídas nele, a exibição do símbolo para cada ordem parece redundante, uma vez que o símbolo já é exibido no título da descrição do objeto-instantâneo do livro de ofertas.

O aprimoramento desses métodos parece o mesmo para todas as classes acima.

Para a classe CMarketBookOrd:

//+------------------------------------------------------------------+
//| Return the object short name                                     |
//+------------------------------------------------------------------+
string CMarketBookOrd::Header(const bool symbol=false)
  {
   return this.TypeDescription()+(symbol ? " \""+this.Symbol()+"\"" : "");
  }
//+------------------------------------------------------------------+
//| Display a short description of the object in the journal         |
//+------------------------------------------------------------------+
void CMarketBookOrd::PrintShort(const bool symbol=false)
  {
   ::Print(this.Header(symbol));
  }
//+------------------------------------------------------------------+

Para a classe CMarketBookBuy, CMarketBookBuyMarket, CMarketBookSell e CMarketBookSellMarket:

//+------------------------------------------------------------------+
//| Return the object short name                                     |
//+------------------------------------------------------------------+
string CMarketBookBuy::Header(const bool symbol=false)
  {
   return CMessage::Text(MSG_MBOOK_ORD_TYPE_BUY)+(symbol ? " \""+this.Symbol() : "")+
          ": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]";
  }
//+------------------------------------------------------------------+

Para a classe CMarketBookBuyMarket:

//+------------------------------------------------------------------+
//| Return the object short name                                     |
//+------------------------------------------------------------------+
string CMarketBookBuyMarket::Header(const bool symbol=false)
  {
   return CMessage::Text(MSG_MBOOK_ORD_TYPE_BUY_MARKET)+(symbol ? " \""+this.Symbol() : "")+
          ": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]";
  }
//+------------------------------------------------------------------+

Para a classe CMarketBookSell:

//+------------------------------------------------------------------+
//| Return the object short name                                     |
//+------------------------------------------------------------------+
string CMarketBookSell::Header(const bool symbol=false)
  {
   return CMessage::Text(MSG_MBOOK_ORD_TYPE_SELL)+(symbol ? " \""+this.Symbol() : "")+
          ": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]";
  }
//+------------------------------------------------------------------+

Para a classe CMarketBookSellMarket:

//+------------------------------------------------------------------+
//| Return the object short name                                     |
//+------------------------------------------------------------------+
string CMarketBookSellMarket::Header(const bool symbol=false)
  {
   return CMessage::Text(MSG_MBOOK_ORD_TYPE_SELL_MARKET)+(symbol ? " \""+this.Symbol() : "")+
          ": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]";
  }
//+------------------------------------------------------------------+

Assim, na declaração desses métodos em todas as classes herdadas foram adicionados sinalizadores:

//--- Return the object short name
   virtual string    Header(const bool symbol=false);

Por padrão, o símbolo não será exibido na descrição do objeto.


Classe de objeto-instantâneo de livro de ofertas

Desse modo, estamos prontos para criar uma classe do objeto-instantâneo do livro de ofertas. Na verdade, é uma lista de ordens do livro de ofertas recebidas na matriz de estruturas MqlBookInfo quando o manipulador OnBookEvent() é acionado. Apenas cada uma das ordens na matriz, nesta classe, será representado por um objeto da classe CMarketBookOrd - seus herdeiros. Todos eles serão adicionados à lista CArrayObj, sendo a classe de uma matriz dinâmica de ponteiros para instâncias da classe CObject e seus herdeiros da biblioteca padrão. Além da lista na qual os objetos-ordem do livro de ofertas serão armazenados, a classe fornecerá a capacidade padrão de trabalhar com esses objetos e suas listas para todos os objetos da biblioteca - pesquisa e classificação por propriedades - para coleta conveniente de quaisquer dados estatísticos ao trabalhar com o livro de ofertas.

Na pasta da biblioteca \MQL5\Include\DoEasy\Objects\Book\ criamos o novo arquivo MarketBookSnapshot.mqh da classe CMBookSnapshot.
A classe base deve ser a classe do objeto base de todos os objetos da biblioteca CBaseObj
.
Arquivos de classes de objetos-herdeiros do objeto da ordem abstrata do livro de ofertas devem ser anexados ao arquivo
.

Considere a listagem da classe e a implementação de métodos:

//+------------------------------------------------------------------+
//|                                           MarketBookSnapshot.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\Select.mqh"
#include "MarketBookBuy.mqh"
#include "MarketBookSell.mqh"
#include "MarketBookBuyMarket.mqh"
#include "MarketBookSellMarket.mqh"
//+------------------------------------------------------------------+
//| "DOM snapshot" class                                             |
//+------------------------------------------------------------------+
class CMBookSnapshot : public CBaseObj
  {
private:
   string            m_symbol;                  // Symbol
   long              m_time;                    // Snapshot time
   int               m_digits;                  // Symbol's Digits
   CArrayObj         m_list;                    // List of DOM order objects
public:
//--- Return (1) itself and (2) the list of DOM order objects
   CMBookSnapshot   *GetObject(void)                                    { return &this;   }
   CArrayObj        *GetList(void)                                      { return &m_list; }

//--- Return the list of DOM order objects by selected (1) double, (2) integer and (3) string property satisfying the compared condition
   CArrayObj        *GetList(ENUM_MBOOK_ORD_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByMBookProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_MBOOK_ORD_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMBookProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_MBOOK_ORD_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByMBookProperty(this.GetList(),property,value,mode); }
//--- (1) Return the DOM order object by index in the list and (2) the order list size
   CMarketBookOrd   *GetMBookByListIndex(const uint index)              { return this.m_list.At(index);  }
   int               DataTotal(void)                              const { return this.m_list.Total();    }

//--- The comparison method for searching and sorting DOM snapshot objects by time
   virtual int       Compare(const CObject *node,const int mode=0) const 
                       {   
                        const CMBookSnapshot *compared_obj=node;
                        return(this.Time()<compared_obj.Time() ? -1 : this.Time()>compared_obj.Time() ? 1 : 0);
                       } 

//--- Return the DOM snapshot change
   string            Header(void);
//--- Display (1) description and (2) short description of a DOM snapshot
   void              Print(void);
   void              PrintShort(void);

//--- Constructors
                     CMBookSnapshot(){;}
                     CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]);
//+----------------------------------------------------------------------+ 
//|Methods of a simplified access to the DOM snapshot object properties  |
//+----------------------------------------------------------------------+
//--- Set (1) a symbol, (2) a DOM snapshot time and (3) the specified time for all DOM orders
   void              SetSymbol(const string symbol)   { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); }
   void              SetTime(const long time_msc)     { this.m_time=time_msc; }
   void              SetTimeToOrders(const long time_msc);
//--- Return (1) a DOM symbol, (2) symbol's Digits and (3) a snapshot time
   string            Symbol(void)               const { return this.m_symbol; }
   int               Digits(void)               const { return this.m_digits; }
   long              Time(void)                 const { return this.m_time;   }
  };
//+------------------------------------------------------------------+

Aqui vemos a organização usual da classe, como em todos os objetos da biblioteca: as variáveis dos membros da classe são declaradas na seção privada, na seção pública - métodos para retornar listas pelas propriedades especificadas de objetos-ordens, padrão para objetos de biblioteca, um método para comparar dois objetos-instantâneos do livro de ofertas para pesquisar e classificar esses objetos na lista onde os colocaremos (na lista do objeto-série de instantâneos do livro de ofertas), métodos para descrever o objeto - instantâneo do livro de ofertas, dois construtores - o construtor padrão e o construtor paramétrico (usaremos o construtor paramétrico ao criar novos objetos-instantâneos do livro de ofertas, cujas propriedades são conhecidas, e por padrão, servirão para crie rapidamente um novo objeto e adicionar a propriedade necessária para pesquisar objetos na lista com o valor especificado). Os métodos de acesso fácil para propriedades de objeto são usados para definir e retornar algumas das propriedades de objeto que precisaremos no futuro.

Consideremos a implementação de métodos de classe.

Num construtor de classe paramétrica examinamos a matriz de estruturas MqlBookInfo passadas a ele e, de acordo com o tipo de ordens, criamos os tipos correspondentes de objetos-ordens do livro de ofertas e os adicionamos à lista.

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CMBookSnapshot::CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]) : m_time(time)
  {
   //--- Set a symbol
   this.SetSymbol(symbol);
   //--- Clear the list
   this.m_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(this.m_symbol,book_array[i]);         break;
         case BOOK_TYPE_SELL        : mbook_ord=new CMarketBookSell(this.m_symbol,book_array[i]);        break;
         case BOOK_TYPE_BUY_MARKET  : mbook_ord=new CMarketBookBuyMarket(this.m_symbol,book_array[i]);   break;
         case BOOK_TYPE_SELL_MARKET : mbook_ord=new CMarketBookSellMarket(this.m_symbol,book_array[i]);  break;
         default: break;
        }
      if(mbook_ord==NULL)
         continue;
      //--- Set the DOM snapshot time for the order
      mbook_ord.SetTime(this.m_time);
      //--- Set the sorted list flag for the list (by the price value) and add the current order object to it
      this.m_list.Sort(SORT_BY_MBOOK_ORD_PRICE);
      if(!this.m_list.InsertSort(mbook_ord))
         delete mbook_ord;
     }
  }
//+------------------------------------------------------------------+

Método que retorna o nome abreviado do objeto-instantâneo do livro de ofertas:

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

Ele simplesmente cria uma string a partir de uma mensagem de texto que descreve o objeto e o símbolo, assim:

EURUSD DOM snapshot

Método que exibe uma breve descrição do objeto-instantâneo da livro de ofertas no log:

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

O método imprime no log uma linha que consiste no nome do objeto mais o tempo gasto para captura do livro de ofertas em milissegundos, exemplo:

"EURUSD" DOM snapshot (2021.02.09 22:16:24.557)

Método que exibe as propriedades do objeto-instantâneo do livro de ofertas para o log:

//+------------------------------------------------------------------+
//| Display object properties in the journal                         |
//+------------------------------------------------------------------+
void CMBookSnapshot::Print(void)
  {
   ::Print(this.Header()," ("+TimeMSCtoString(this.m_time),"):");
   this.m_list.Sort(SORT_BY_MBOOK_ORD_PRICE);
   for(int i=this.m_list.Total()-1;i>WRONG_VALUE;i--)
     {
      CMarketBookOrd *ord=this.m_list.At(i);
      if(ord==NULL)
         continue;
      ::Print(" - ",ord.Header());
     }
  }
//+------------------------------------------------------------------+

Primeiro, o título com a descrição do instantâneo é exibido e, em seguida, no ciclo através da lista de todos os objetos-ordens do livro de ofertas são mostradas suas descrições.

Visto que os objetos-ordens que estão na lista do objeto-instantâneo não têm sua própria capacidade de obter seu tempo, nesta classe iremos escrever um método que define para todos os objetos-ordens na lista o tempo em milissegundos registrado neste objeto:

//+------------------------------------------------------------------+
//| Set the specified time to all DOM orders                         |
//+------------------------------------------------------------------+
void CMBookSnapshot::SetTimeToOrders(const long time_msc)
  {
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CMarketBookOrd *ord=this.m_list.At(i);
      if(ord==NULL)
         continue;
      ord.SetTime(time_msc);
     }
  }
//+------------------------------------------------------------------+

No loop através da lista de todos os objetos-ordens do livro de ofertas, obtemos o próximo objeto-ordem e o configuramos a hora registrada nas propriedades do objeto-instantâneo do livro de ofertas. Assim, todos os objetos-ordens na lista de um determinado instantâneo do livro de ofertas têm o mesmo tempo de recebimento, o que é lógico - nós os recebemos quando o manipulador OnBookEvent() é acionado e anotamos o tempo de ativação para o objeto-instantâneo do livro de ofertas e todas as suas ordens.

O objeto-instantâneo do livro de ofertas está pronto. Agora precisamos colocar esses objetos na lista - afinal, toda vez que o manipulador OnBookEvent() for acionado, receberemos um novo instantâneo do livro de ofertas e criaremos o objeto correspondente.
Todos esses objetos serão armazenados na classe-série de objetos do livro de ofertas.

Classe de objeto-série de instantâneos de livro de ofertas

A ideia por trás da classe de uma série de instantâneos do livro de ofertas é semelhante à das classes de séries temporais de símbolos ou dados de ticks. Em contraste com essas classes, nas quais os dados podem ser obtidos do ambiente, na classe-lista dos instantâneos do livro de ofertas não podemos obter dados históricos - eles terão que ser acumulados em tempo real. Portanto, a classe não terá um método para criar a lista, mas, sim, apenas um método para atualizá-la.

Na pasta da biblioteca \MQL5\Include\DoEasy\Objects\Book\ criamos o novo arquivo MBookSeries.mqh da classe CMBookSeries.
A classe base deve ser a classe do objeto base de todos os objetos da biblioteca CBaseObj
.
O arquivo deve incluir o arquivo de classe do objeto-instantâneo do livro de ofertas
.

Vamos dar uma olhada na listagem da classe e, em seguida, analisaremos seus métodos:

//+------------------------------------------------------------------+
//|                                                  MBookSeries.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 "MarketBookSnapshot.mqh"
//+------------------------------------------------------------------+
//| "DOM snapshot series" class                                      |
//+------------------------------------------------------------------+
class CMBookSeries : public CBaseObj
  {
private:
   string            m_symbol;                                          // Symbol
   uint              m_amount;                                          // Number of used DOM snapshots in the series
   uint              m_required;                                        // Required number of days for DOM snapshot series
   CArrayObj         m_list;                                            // DOM snapshot series list
   MqlBookInfo       m_book_info[];                                     // DOM structure array
public:
//--- Return (1) itself and (2) the series list
   CMBookSeries     *GetObject(void)                                    { return &this;   }
   CArrayObj        *GetList(void)                                      { return &m_list; }

//--- Return the DOM snapshot list by selected (1) double, (2) integer and (3) string properties fitting the compared condition
   CArrayObj        *GetList(ENUM_MBOOK_ORD_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByMBookProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_MBOOK_ORD_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMBookProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_MBOOK_ORD_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByMBookProperty(this.GetList(),property,value,mode); }

//--- Return the DOM snapshot object by (1) index in the list, (2) time and (3) actual list size
   CMBookSnapshot   *GetMBookByListIndex(const uint index)        const { return this.m_list.At(index);              }
   CMBookSnapshot   *GetLastMBook(void)                           const { return this.m_list.At(this.DataTotal()-1); }
   CMBookSnapshot   *GetMBook(const long time_msc); 
   int               DataTotal(void)                              const { return this.m_list.Total();                }

//--- Set a (1) symbol, (2) a number of days for DOM snapshots
   void              SetSymbol(const string symbol);
   void              SetRequiredUsedDays(const uint required=0);

//--- The comparison method for searching and sorting DOM snapshot series objects by symbol
   virtual int       Compare(const CObject *node,const int mode=0) const 
                       {   
                        const CMBookSeries *compared_obj=node;
                        return(this.Symbol()==compared_obj.Symbol() ? 0 : this.Symbol()>compared_obj.Symbol() ? 1 : -1);
                       } 
//--- Return the name of the DOM  snapshot series
   string            Header(void);
//--- Display (1) description and (2) short description of a DOM snapshot series
   void              Print(void);
   void              PrintShort(void);

//--- Constructors
                     CMBookSeries(){;}
                     CMBookSeries(const string symbol,const uint required=0);

//+------------------------------------------------------------------+ 
//| Methods of working with objects and accessing their properties   |
//+------------------------------------------------------------------+
//--- Return (1) a symbol, a number of (2) used and (3) requested DOM snapshots in the series and
//--- (4) the time of a DOM snapshot specified by the index in milliseconds
   string            Symbol(void)                                          const { return this.m_symbol;    }
   ulong             AvailableUsedData(void)                               const { return this.m_amount;    }
   ulong             RequiredUsedDays(void)                                const { return this.m_required;  }
   long              MBookTime(const int index) const;
//--- update the list of DOM snapshot series
   bool              Refresh(const long time_msc);
  };
//+------------------------------------------------------------------+

Novamente, o que vemos aqui:

  • métodos padrão para objetos de biblioteca para obter propriedades de objeto e listas de objetos por propriedades especificadas,
  • métodos para definir algumas propriedades,
  • método para comparar dois objetos-listas para classificá-los apenas pelo nome do símbolo,
  • métodos para retornar nomes de objetos e construtores de classes.

Consideremos a implementação de métodos de classe.

Na lista de inicialização do construtor paramétrico é definido o símbolo de lista, enquanto no corpo do método é limpa e a lista, o sinalizador de lista classificada por tempo em milissegundos é definido para ele e o número necessário de dias desses instantâneos do livro de ofertas é definido.

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CMBookSeries::CMBookSeries(const string symbol,const uint required=0) : m_symbol(symbol)
  {
   this.m_list.Clear();
   this.m_list.Sort(SORT_BY_MBOOK_ORD_TIME_MSC);
   this.SetRequiredUsedDays(required);
  }
//+------------------------------------------------------------------+

Método que atualiza a lista de instantâneos do livro de ofertas:

//+------------------------------------------------------------------+
//| Update the list of DOM snapshot series                           |
//+------------------------------------------------------------------+
bool CMBookSeries::Refresh(const long time_msc)
  {
//--- Get DOM entries to the structure array
   if(!::MarketBookGet(this.m_symbol,this.m_book_info))
      return false;
//--- Create a new DOM snapshot object
   CMBookSnapshot *book=new CMBookSnapshot(this.m_symbol,time_msc,this.m_book_info);
   if(book==NULL)
      return false;
//--- Set the flag of a list sorted by time for the list and add the created DOM snapshot to it
   this.m_list.Sort(SORT_BY_MBOOK_ORD_TIME_MSC);
   if(!this.m_list.InsertSort(book))
     {
      delete book;
      return false;
     }
//--- Set time in milliseconds to all DOM snapshot order objects
   book.SetTimeToOrders(time_msc);
//--- If the number of snapshots in the list exceeds the default maximum number,
//--- remove the calculated number of snapshot objects from the end of the list
   if(this.DataTotal()>MBOOKSERIES_MAX_DATA_TOTAL)
     {
      int total_del=this.m_list.Total()-MBOOKSERIES_MAX_DATA_TOTAL;
      for(int i=0;i<total_del;i++)
         this.m_list.Delete(i);
     }
   return true;
  }
//+------------------------------------------------------------------+

O método será chamado quando o manipulador OnBookEvent() for acionado. Assim, o tempo de resposta do manipulador é passado para o método, usando MarketBookGet() obtemos uma série de estruturas do livro de ofertas, com base nessa estrutura, criamos um objeto-instantâneo do livro de ofertas e i adicionamos à lista-séries de instantâneos.
Toda a lógica é detalhada nos comentários ao código do método e, espero, esteja clara.

Método para definir o nome do símbolo para lista-série de instantâneos:

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

Aqui tudo é transparente - se passado NULL ou uma string vazia, então o símbolo atual é definido, caso contrário, o passado para o método.

Método para definir o número de dias para uma série de instantâneos do livro de ofertas:

//+------------------------------------------------------------------+
//| Set the number of days for DOM snapshots in the series           |
//+------------------------------------------------------------------+
void CMBookSeries::SetRequiredUsedDays(const uint required=0)
  {
   this.m_required=(required<1 ? MBOOKSERIES_DEFAULT_DAYS_COUNT : required);
  }
//+------------------------------------------------------------------+

Se for passado um valor zero, será definido o número de dias especificado por padrão, caso contrário, o passado para o método. Até agora, esse método não é usado em nenhum lugar.

Método que retorna um objeto-instantâneo do livro de ofertas num momento especificado:

//+------------------------------------------------------------------+
//| Return the DOM snapshot object by its time                       |
//+------------------------------------------------------------------+
CMBookSnapshot *CMBookSeries::GetMBook(const long time_msc)
  {
   CMBookSnapshot *book=new CMBookSnapshot();
   if(book==NULL)
      return NULL;
   book.SetTime(time_msc);
   this.m_list.Sort();
   int index=this.m_list.Search(book);
   delete book;
   return this.m_list.At(index);
  }
//+------------------------------------------------------------------+

Aqui: criamos um objeto-instantâneo temporário do livro de ofertas, definimos o tempo necessário, sinalizador de lista classificada, e usando o método Search() obtemos o índice do objeto da lista com tempo igual ao desejado. Sempre excluímos o objeto temporário e retornamos um ponteiro para o objeto encontrado por índice na lista.

Método que retorna o tempo em milissegundos do instantâneo do livro de ofertas especificado pelo índice:

//+------------------------------------------------------------------+
//| Returns the time in milliseconds                                 |
//| of a DOM snapshot specified by index                             |
//+------------------------------------------------------------------+
long CMBookSeries::MBookTime(const int index) const
  {
   CMBookSnapshot *book=this.m_list.At(index);
   return(book!=NULL ? book.Time() : 0);
  }
//+------------------------------------------------------------------+

Obtemos um ponteiro para o objeto-instantâneo do livro de ofertas com base no índice especificado e retornamos seu tempo em milissegundos, ou NULL em caso de falha.

Método que retorna o nome de uma série de instantâneos do livro de ofertas:

//+------------------------------------------------------------------+
//| Return the name of the DOM snapshot series                       |
//+------------------------------------------------------------------+
string CMBookSeries::Header(void)
  {
   return CMessage::Text(MSG_MBOOK_SERIES_TEXT_MBOOKSERIES)+" \""+this.m_symbol+"\"";
  }
//+------------------------------------------------------------------+

O método retorna uma string que consiste numa descrição de um objeto e um símbolo, assim:

Series of "EURUSD" DOM snapshots

Método que exibe uma descrição de uma série de instantâneos do livro de ofertas no log:

//+------------------------------------------------------------------+
//| Display the description of the DOM snapshot series in the journal|
//+------------------------------------------------------------------+
void CMBookSeries::Print(void)
  {
   string txt=
     (
      CMessage::Text(MSG_TICKSERIES_REQUIRED_HISTORY_DAYS)+(string)this.RequiredUsedDays()+", "+
      CMessage::Text(MSG_LIB_TEXT_TS_ACTUAL_DEPTH)+(string)this.DataTotal()
     );
   ::Print(this.Header(),": ",txt);
  }
//+------------------------------------------------------------------+

Primeiro, um cabeçalho é criado com uma descrição da série de instantâneos, o número solicitado de dias de dados e o número realmente coletado de instantâneos do livro de ofertas e, em seguida, as descrições de todas as ordens incluídas no objeto-instantâneo são exibidas num ciclo.

Assim fica concluída a criação das classes do objeto-instantâneo e do objeto-série de instantâneos do livro de ofertas.

Teste

Para o teste, vamos pegar o Expert Advisor do artigo anterior e salvá-lo numa nova pasta \MQL5\Experts\TestDoEasy\Part64\ com o novo nome TestDoEasyPart64.mq5.

No Expert Advisor, criaremos um objeto-série de instantâneos do livro de ofertas para o símbolo atual, e cada vez que o manipulador OnBoolEvent() for acionado para o símbolo atual, à lista adicionaremos um novo objeto-instantâneo do livro de ofertas. No gráfico, no comentário, exibimos dados sobre o número de instantâneos adicionados à lista e dois ordens extremas do instantâneo atual - o máximo para venda e o mínimo para compra. Quando recebermos os dados do livro de ofertas pela primeira vez, iremos imprimi-los no log do terminal.

Vamos remover a inclusão de classes de objetos-ordens da lista de EA - eles agora estão anexados aos arquivos das novas classes que criamos hoje:

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

E em vez deles escrevemos a integração do arquivo da classe-série de instantâneos do livro de ofertas:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart64.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\MBookSeries.mqh>
//--- enums

Na lista de variáveis globais do Expert Advisor declaramos um objeto da classe de uma série de instantâneos do livro de ofertas:

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

Todo o trabalho de criação de uma lista de série de instantâneos do livro de ofertas é realizado no manipulador OnBoolEvent():

//+------------------------------------------------------------------+
//| 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;
   //--- Work by the current symbol
   if(symbol==sym.Name())
     {
      //--- Set a symbol and a required number of data days for the DOM snapshot series object
      book_series.SetSymbol(sym.Name());
      book_series.SetRequiredUsedDays();
      //--- Update the DOM snapshot series
      if(!book_series.Refresh(sym.Time()))
         return;
      
      //--- Get the last DOM snapshot object from the DOM snapshot series object
      CMBookSnapshot *book=book_series.GetLastMBook();
      if(book==NULL)
         return;
      //--- Get the very first and last DOM order objects from the DOM snapshot object
      CMarketBookOrd *ord_0=book.GetMBookByListIndex(0);
      CMarketBookOrd *ord_N=book.GetMBookByListIndex(book.DataTotal()-1);
      if(ord_0==NULL || ord_N==NULL) return;
      //--- Display the time of the current DOM snapshot in the chart comment,
      //--- the maximum number of displayed orders in DOM for a symbol,
      //--- the obtained number of orders in the current DOM snapshot,
      //--- the total number of DOM snapshots set in the series list and
      //--- the highest and lowest orders of the current DOM snapshot
      Comment
        (
         DFUN,sym.Name(),": ",TimeMSCtoString(book.Time()),
         ", symbol book size=",sym.TicksBookdepth(),

         ", last book data total: ",book.DataTotal(),
         ", series books total: ",book_series.DataTotal(),
         "\nMax: ",ord_N.Header(),"\nMin: ",ord_0.Header()
        );
      //--- Display the first DOM snapshot in the journal
      if(first)
        {
         //--- series description
         book_series.Print();
         //--- snapshot description
         book.Print();
         first=false;
        }
     }
  }
//+------------------------------------------------------------------+

Aqui, todas as linhas de código estão escritas nos comentários e, espero, não precisam de explicação adicional.
Em qualquer caso, você pode fazer suas perguntas na discussão do artigo.

Vamos compilar o Expert Advisor e iniciá-lo no gráfico do símbolo, tendo especificado anteriormente nas configurações para trabalhar com os dois símbolos especificados e o timeframe atual:


O log irá mostrar os dados sobre a série de instantâneos do livro de ofertas criada e o primeiro instantâneo:

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10428.13 USD, 1:100, Hedge, MetaTrader 5 demo
--- Initializing "DoEasy" library ---
Working with predefined symbol list. The number of used symbols: 2
"AUDUSD" "EURUSD"
Working with the current timeframe only: H1
AUDUSD symbol timeseries: 
- Timeseries "AUDUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 5121
EURUSD symbol timeseries: 
- Timeseries "EURUSD" H1: Requested: 1000, Actual: 1000, Created: 1000, On the server: 6046
Tick series "AUDUSD": Requested number of days: 1, Historical data created: 176033
Tick series "EURUSD": Requested number of days: 1, Historical data created: 181969
Subscribed to Depth of Market  AUDUSD
Subscribed to Depth of Market  EURUSD
Library initialization time: 00:00:12.516
The "EURUSD" DOM snapshot series: Requested number of days: 1, Actual history depth: 1
"EURUSD" DOM snapshot (2021.02.09 22:16:24.557):
 - Sell order: 1.21198 [250.00]
 - Sell order: 1.21193 [100.00]
 - Sell order: 1.21192 [50.00]
 - Sell order: 1.21191 [30.00]
 - Sell order: 1.21190 [6.00]
 - Buy order: 1.21188 [36.00]
 - Buy order: 1.21186 [50.00]
 - Buy order: 1.21185 [100.00]
 - Buy order: 1.21180 [250.00]

O gráfico do símbolo exibirá dados sobre a hora da último instantâneo do livro de ofertas, o número de ordens do símbolo, o número de ordens no instantâneo atual e o número total de instantâneos do livro de ofertas adicionados à lista destes:

A figura mostra os dados de um Expert Advisor que já está trabalhando há algum tempo (5019 instantâneos foram adicionados à lista)

O que vem agora?

No próximo artigo, criaremos uma coleção de séries de instantâneos do livro de ofertas, o que nos permitira trabalhar plenamente com o livro de ofertas de quaisquer símbolos cuja assinatura do livro de ofertas esteja ativada e sua transmissão seja permitida.

Todos os arquivos da versão atual da biblioteca e o arquivo do EA de teste para MQL5 estão anexados abaixo. Você pode baixá-los e testar tudo sozinho.
Gostaria de ressaltar que as classes para trabalhar com o livro de ofertas estão em desenvolvimento e, portanto, o seu uso em programas agora é altamente desaconselhado.
Se você tiver perguntas, comentários e sugestões, poderá expressá-los nos comentários do artigo.

Complementos

*Artigos desta série:

Trabalhando com preços na biblioteca DoEasy (Parte 62): atualização em tempo real da série de ticks, preparação para trabalhar com o livro de ofertas Trabalhando preços na biblioteca DoEasy (Parte 63): livro de ofertas, classe de ordem abstrata do livro de ofertas

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/9044

Arquivos anexados |
MQL5.zip (3897 KB)
Redes Neurais de Maneira Fácil (Parte 11): Uma visão sobre a GPT Redes Neurais de Maneira Fácil (Parte 11): Uma visão sobre a GPT
Talvez um dos modelos mais avançados entre as redes neurais de linguagem atualmente existentes seja a GPT-3, cuja variante máxima contém 175 bilhões de parâmetros. Claro, nós não vamos criar tal monstro em nossos PCs domésticos. No entanto, nós podemos ver quais soluções arquitetônicas podem ser usadas em nosso trabalho e como nós podemos nos beneficiar delas.
Algoritmo auto-adaptável (Parte IV): funcionalidade e testes adicionais Algoritmo auto-adaptável (Parte IV): funcionalidade e testes adicionais
Continuo a complementar o algoritmo com a funcionalidade mínima necessária, vou fazer testes do que obtivemos como resultado. A lucratividade acabou sendo baixa, mas os artigos mostram um modelo que permite negociar com lucro de modo totalmente automático com base em instrumentos de negociação completamente diferentes, e não apenas diferentes, mas também operados em mercados fundamentalmente diferentes.
Técnicas úteis e exóticas para a negociação automatizada Técnicas úteis e exóticas para a negociação automatizada
Neste artigo, eu demonstrarei algumas técnicas muito interessantes e úteis para a negociação automatizada. Algumas delas podem ser familiares para você. Eu tentarei cobrir os métodos mais interessantes e explicarei por que vale a pena usá-los. Além disso, eu mostrarei o que essas técnicas podem fazer na prática. Nós criaremos Expert Advisors e testaremos todas as técnicas descritas usando as cotações históricas.
Trabalhando preços na biblioteca DoEasy (Parte 63): livro de ofertas, classe de ordem abstrata do livro de ofertas Trabalhando preços na biblioteca DoEasy (Parte 63): livro de ofertas, classe de ordem abstrata do livro de ofertas
Neste artigo, começaremos a desenvolver funcionalidades para trabalhar com o livro de ofertas. Criaremos uma classe de objeto para uma ordem abstrata do livro de ofertas e dos seus herdeiros.