//+------------------------------------------------------------------+
//|                                           MarketBookSnapshot.mqh |
//|                        Copyright 2021, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\..\Services\Select.mqh"
#include "MarketBookBuy.mqh"
#include "MarketBookSell.mqh"
#include "MarketBookBuyMarket.mqh"
#include "MarketBookSellMarket.mqh"
//+------------------------------------------------------------------+
//| Класс "Снимок стакана цен"                                       |
//+------------------------------------------------------------------+
class CMBookSnapshot : public CBaseObj
  {
private:
   string            m_symbol;                  // Символ
   long              m_time;                    // Время снимка
   int               m_digits;                  // Digits символа
   long              m_volume_buy;              // Объём стакана на покупку
   long              m_volume_sell;             // Объём стакана на продажу
   double            m_volume_buy_real;         // Объём стакана на покупку с повышенной точностью
   double            m_volume_sell_real;        // Объём стакана на продажу с повышенной точностью
   CArrayObj         m_list;                    // Список объектов-заявок стакана цен
public:
//--- Возвращает (1) себя, (2) список объектов-заявок стакана цен
   CMBookSnapshot   *GetObject(void)                                    { return &this;   }
   CArrayObj        *GetList(void)                                      { return &m_list; }

//--- Возвращает список объектов-заявок стакана цен по выбранному (1) double, (2) integer и (3) string свойству, удовлетворяющему сравниваемому условию
   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) Возвращает объект-заявку стакана цен по индексу в списке, (2) размер списка заявок
   CMarketBookOrd   *GetMBookByListIndex(const uint index)              { return this.m_list.At(index);  }
   int               DataTotal(void)                              const { return this.m_list.Total();    }

//--- Метод сравнения для поиска и сортировки по времени объектов снимков стакана цен
   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);
                       } 

//--- Возвращает наименование снимка стакана цен
   string            Header(void);
//--- Выводит в журнал (1) описание, (2) краткое описание снимка стакана цен
   virtual void      Print(const bool full_prop=false,const bool dash=false);
   virtual void      PrintShort(const bool dash=false,const bool symbol=false);

//--- Конструкторы
                     CMBookSnapshot(){ this.m_type=OBJECT_DE_TYPE_BOOK_SNAPSHOT; }
                     CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]);
//+------------------------------------------------------------------+ 
//|Методы упрощённого доступа к свойствам объекта-снимка стакана цен |
//+------------------------------------------------------------------+
//--- Устанавливает (1) символ, (2) время снимка стакана цен, (3) указанное время всем заявкам в стакане
   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);
//--- Возвращает (1) символ стакана цен, (2) Digits символа, (3) время снимка
//--- объём снимка стакана цен (4) на покупку, (5) на продажу,
//--- с повышенной точностью (6) на покупку, (7) на продажу,
   string            Symbol(void)               const { return this.m_symbol;             }
   int               Digits(void)               const { return this.m_digits;             }
   long              Time(void)                 const { return this.m_time;               }
   long              VolumeBuy(void)            const { return this.m_volume_buy;         }
   long              VolumeSell(void)           const { return this.m_volume_sell;        }
   double            VolumeBuyReal(void)        const { return this.m_volume_buy_real;    }
   double            VolumeSellReal(void)       const { return this.m_volume_sell_real;   }
   
//--- Возвращает описание объёма стакана (1) на покупку, (2) на продажу
   string            VolumeBuyDescription(void);
   string            VolumeSellDescription(void);
   
  };
//+------------------------------------------------------------------+
//| Параметрический конструктор                                      |
//+------------------------------------------------------------------+
CMBookSnapshot::CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]) : m_time(time)
  {
   this.m_type=OBJECT_DE_TYPE_BOOK_SNAPSHOT;
   //--- Устанавливаем символ
   this.SetSymbol(symbol);
   //--- Очищаем список
   this.m_list.Clear();
   //--- В цикле по массиву структур
   int total=::ArraySize(book_array);
   this.m_volume_buy=this.m_volume_sell=0;
   this.m_volume_buy_real=this.m_volume_sell_real=0;
   for(int i=0;i<total;i++)
     {
      //--- Создаём объекты-заявки текущего снимка стакана цен в зависимости от типа заявки
      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;
      //--- Устанавливаем заявке время снимка стакана цен
      mbook_ord.SetTime(this.m_time);

      //--- Ставим списку флаг сортированного списка (по значению цены) и добавляем в него текущий объект-заявку
      //--- Если не удалось добавить объект в список заявок стакана цен - удаляем объект-заявку
      this.m_list.Sort(SORT_BY_MBOOK_ORD_PRICE);
      if(!this.m_list.InsertSort(mbook_ord))
         delete mbook_ord;
      //--- Если объект-заявка успешно добавлен в список заявок снимка стакана - дополняем общие объёмы снимка
      else
        {
         switch(mbook_ord.TypeOrd())
           {
            case BOOK_TYPE_BUY         : 
              this.m_volume_buy+=mbook_ord.Volume(); 
              this.m_volume_buy_real+=mbook_ord.VolumeReal();
              break;
            case BOOK_TYPE_SELL        : 
              this.m_volume_sell+=mbook_ord.Volume(); 
              this.m_volume_sell_real+=mbook_ord.VolumeReal();
              break;
            case BOOK_TYPE_BUY_MARKET  : 
              this.m_volume_buy+=mbook_ord.Volume(); 
              this.m_volume_buy_real+=mbook_ord.VolumeReal();
              break;
            case BOOK_TYPE_SELL_MARKET : 
              this.m_volume_buy+=mbook_ord.Volume(); 
              this.m_volume_buy_real+=mbook_ord.VolumeReal();
              break;
            default: break;
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Возвращает краткое наименование объекта                          |
//+------------------------------------------------------------------+
string CMBookSnapshot::Header(void)
  {
   return CMessage::Text(MSG_MBOOK_SNAP_TEXT_SNAPSHOT)+" \""+this.Symbol()+"\"";
  }
//+------------------------------------------------------------------+
//| Выводит в журнал краткое описание объекта                        |
//+------------------------------------------------------------------+
void CMBookSnapshot::PrintShort(const bool dash=false,const bool symbol=false)
  {
   string vol_buy="Buy vol: "+(this.VolumeBuyReal()>0 ? ::DoubleToString(this.VolumeBuyReal(),2) : (string)this.VolumeBuy());
   string vol_sell="Sell vol: "+(this.VolumeSellReal()>0 ? ::DoubleToString(this.VolumeSellReal(),2) : (string)this.VolumeSell());
   ::Print(this.Header()," ",vol_buy,", ",vol_sell," ("+TimeMSCtoString(this.m_time),")");
  }
//+------------------------------------------------------------------+
//| Выводит в журнал свойства объекта                                |
//+------------------------------------------------------------------+
void CMBookSnapshot::Print(const bool full_prop=false,const bool dash=false)
  {
   string vol_buy=CMessage::Text(MSG_MBOOK_SNAP_VOLUME_BUY)+": "+(this.VolumeBuyReal()>0 ? ::DoubleToString(this.VolumeBuyReal(),2) : (string)this.VolumeBuy());
   string vol_sell=CMessage::Text(MSG_MBOOK_SNAP_VOLUME_SELL)+": "+(this.VolumeSellReal()>0 ? ::DoubleToString(this.VolumeSellReal(),2) : (string)this.VolumeSell());
   ::Print(this.Header(),": ",vol_buy,", ",vol_sell," ("+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());
     }
  }
//+------------------------------------------------------------------+
//| Устанавливает указанное время всем заявкам в стакане             |
//+------------------------------------------------------------------+
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);
     }
  }
//+------------------------------------------------------------------+
//| Возвращает описание объёма стакана на покупку                    |
//+------------------------------------------------------------------+
string CMBookSnapshot::VolumeBuyDescription(void)
  {
   return(CMessage::Text(MSG_MBOOK_SNAP_VOLUME_BUY)+": "+(this.VolumeBuyReal()>0 ? ::DoubleToString(this.VolumeBuyReal(),2) : (string)this.VolumeBuy()));
  }
//+------------------------------------------------------------------+
//| Возвращает описание объёма стакана на продажу                    |
//+------------------------------------------------------------------+
string CMBookSnapshot::VolumeSellDescription(void)
  {
   return(CMessage::Text(MSG_MBOOK_SNAP_VOLUME_SELL)+": "+(this.VolumeSellReal()>0 ? ::DoubleToString(this.VolumeSellReal(),2) : (string)this.VolumeSell()));
  }
//+------------------------------------------------------------------+
