Понравилась статья?
Поставьте ссылку на нее -
пусть другие почитают
Используй новые возможности MetaTrader 5

Работа с ценами и Сигналами в библиотеке DoEasy (Часть 65): Коллекция стаканов и класс для работы с Сигналами MQL5.com

26 февраля 2021, 08:25
Artyom Trishkin
4
1 622

Содержание


Концепция

У нас готов функционал для работы со стаканом цен любого символа — в предыдущих статьях мы создали классы объектов абстрактной заявки стакана цен и его наследников, класс снимка стакана цен и класс серии снимков стакана цен. Осталось создать общее хранилище для объектов-серий снимков стакана цен — класс-коллекцию серий снимков, где будут храниться все эти серии с удобным доступом к любому снимку стакана цен из хранящихся в списках в коллекции, а также с их автоматическим обновлением — добавлением новых снимков и удалением старых — для поддержания заданных размеров серий.

Помимо создания класса-коллекции серий снимков стаканов цен, сегодня начнём новый раздел библиотеки — прочие классы библиотеки.
И начнём с функционала для работы с сервисом сигналов MQL5.com, а именно — создадим класс объекта-сигнала, в котором будут храниться все данные одного сигнала, транслируемого сервисом сигналов ресурса MQL5.com.


Доработка классов библиотеки

Сразу же дополним библиотеку новыми сообщениями. В файле \MQL5\Include\DoEasy\Data.mqh впишем индексы новых сообщений:

//--- CMBookSeries
   MSG_MBOOK_SERIES_TEXT_MBOOKSERIES,                 // Серия снимков стакана цен
   MSG_MBOOK_SERIES_ERR_ADD_TO_LIST,                  // Ошибка. Не удалось добавить серию снимков стакана цен в список
   
//--- CMBookSeriesCollection
   MSG_MB_COLLECTION_TEXT_MBCOLLECTION,               // Коллекция серий снимков стакана цен
 
//--- CMQLSignal
   MSG_SIGNAL_MQL5_TEXT_SIGNAL,                       // Сигнал
   MSG_SIGNAL_MQL5_TEXT_SIGNAL_MQL5,                  // Сигнал сервиса сигналов MQL5.com
   MSG_SIGNAL_MQL5_TRADE_MODE,                        // Тип счета
   MSG_SIGNAL_MQL5_DATE_PUBLISHED,                    // Дата публикации
   MSG_SIGNAL_MQL5_DATE_STARTED,                      // Дата начала мониторинга
   MSG_SIGNAL_MQL5_DATE_UPDATED,                      // Дата последнего обновления торговой статистики
   MSG_SIGNAL_MQL5_ID,                                // ID
   MSG_SIGNAL_MQL5_LEVERAGE,                          // Плечо торгового счета
   MSG_SIGNAL_MQL5_PIPS,                              // Результат торговли в пипсах
   MSG_SIGNAL_MQL5_RATING,                            // Позиция в рейтинге сигналов
   MSG_SIGNAL_MQL5_SUBSCRIBERS,                       // Количество подписчиков
   MSG_SIGNAL_MQL5_TRADES,                            // Количество трейдов
   MSG_SIGNAL_MQL5_SUBSCRIPTION_STATUS,               // Состояние подписки счёта на этот сигнал
   
   MSG_SIGNAL_MQL5_EQUITY,                            // Средства на счете
   MSG_SIGNAL_MQL5_GAIN,                              // Прирост счета в процентах
   MSG_SIGNAL_MQL5_MAX_DRAWDOWN,                      // Максимальная просадка
   MSG_SIGNAL_MQL5_PRICE,                             // Цена подписки на сигнал
   MSG_SIGNAL_MQL5_ROI,                               // Значение ROI (Return on Investment) сигнала в %

   MSG_SIGNAL_MQL5_AUTHOR_LOGIN,                      // Логин автора
   MSG_SIGNAL_MQL5_BROKER,                            // Наименование брокера (компании)
   MSG_SIGNAL_MQL5_BROKER_SERVER,                     // Сервер брокера
   MSG_SIGNAL_MQL5_NAME,                              // Имя
   MSG_SIGNAL_MQL5_CURRENCY,                          // Валюта счета
   
   MSG_SIGNAL_MQL5_TEXT_GAIN,                         // Прирост
   MSG_SIGNAL_MQL5_TEXT_DRAWDOWN,                     // Просадка
   
  };
//+------------------------------------------------------------------+

и тексты сообщений, соответствующие вновь добавленным индексам:

//--- CMBookSeries
   {"Серия снимков стакана цен","Series of shots of the Depth of Market"},
   {"Ошибка. Не удалось добавить серию снимков стакана цен в список","Error. Failed to add a shots series of the Depth of Market to the list"},
   
//--- CMBookSeriesCollection
   {"Коллекция серий снимков стакана цен","Collection of series of the Depth of Market shot"},   
   
//--- CMQLSignal
   {"Сигнал","Signal"},
   {"Сигнал сервиса сигналов MQL5.com","Signal from MQL5.com signal service"},
   {"Тип счета","Account type"},
   {"Дата публикации","Publication date"},
   {"Дата начала мониторинга","Monitoring starting date"},
   {"Дата последнего обновления торговой статистики","The date of the last update of the signal's trading statistics"},
   {"ID","ID"},
   {"Плечо торгового счета","Account leverage"},
   {"Результат торговли в пипсах","Profit in pips"},
   {"Позиция в рейтинге сигналов","Position in rating"},
   {"Количество подписчиков","Number of subscribers"},
   {"Количество трейдов","Number of trades"},
   {"Состояние подписки счёта на этот сигнал","Account subscription status for this signal"},

   {"Средства на счете","Account equity"},
   {"Прирост счета в процентах","Account gain"},
   {"Максимальная просадка","Account maximum drawdown"},
   {"Цена подписки на сигнал","Signal subscription price"},
   {"Значение ROI (Return on Investment) сигнала в %","Return on Investment (%)"},
   
   {"Логин автора","Author login"},
   {"Наименование брокера (компании)","Broker name (company)"},
   {"Сервер брокера","Broker server"},
   {"Имя","Name"},
   {"Валюта счета","Base currency"},
   
   {"Прирост","Gain"},
   {"Просадка","Drawdown"},
   
  };
//+---------------------------------------------------------------------+

Так как сегодня будем делать новую коллекцию, то нам необходимо прописать идентификатор этой коллекции. В файле \MQL5\Include\DoEasy\Defines.mqh в разделе идентификаторов впишем идентификатор коллекции серии снимков стаканов цен:

//--- Идентификаторы списков коллекций
#define COLLECTION_HISTORY_ID          (0x777A)                   // Идентификатор списка исторической коллекции
#define COLLECTION_MARKET_ID           (0x777B)                   // Идентификатор списка рыночной коллекции
#define COLLECTION_EVENTS_ID           (0x777C)                   // Идентификатор списка коллекции событий
#define COLLECTION_ACCOUNT_ID          (0x777D)                   // Идентификатор списка коллекции аккаунтов
#define COLLECTION_SYMBOLS_ID          (0x777E)                   // Идентификатор списка коллекции символов
#define COLLECTION_SERIES_ID           (0x777F)                   // Идентификатор списка коллекции таймсерий
#define COLLECTION_BUFFERS_ID          (0x7780)                   // Идентификатор списка коллекции индикаторных буферов
#define COLLECTION_INDICATORS_ID       (0x7781)                   // Идентификатор списка коллекции индикаторов
#define COLLECTION_INDICATORS_DATA_ID  (0x7782)                   // Идентификатор списка коллекции индикаторных данных
#define COLLECTION_TICKSERIES_ID       (0x7783)                   // Идентификатор списка коллекции тиковых серий
#define COLLECTION_MBOOKSERIES_ID      (0x7784)                   // Идентификатор списка коллекции серий стаканов цен
//--- Параметры данных для файловых операций

Доработаем файл класса объекта-серии снимков стакана цен. Иногда нам необходимо вывести описание объекта разово, а иногда — при выводе из объекта-коллекции нам нужно будет выводить описания сразу всех серий снимков стаканов цен. И в этом случае список будет смотреться красивее, если перед описанием каждой серии добавлять дефис. В файле класса серии снимков стакана цен \MQL5\Include\DoEasy\Objects\Book\MBookSeries.mqh допишем изменения в определении методов:

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

//--- Конструкторы

Просто используем флаг необходимости вывода дефиса перед описанием объекта.

В реализации методов тоже пропишем эти изменения:

//+------------------------------------------------------------------+
//| Выводит в журнал описание серии снимков стакана цен              |
//+------------------------------------------------------------------+
void CMBookSeries::Print(const bool dash=false)
  {
   string txt=
     (
      CMessage::Text(MSG_TICKSERIES_REQUIRED_HISTORY_DAYS)+(string)this.RequiredUsedDays()+", "+
      CMessage::Text(MSG_LIB_TEXT_TS_ACTUAL_DEPTH)+(string)this.DataTotal()
     );
   ::Print((dash ? "- " : ""),this.Header(),": ",txt);
  }
//+------------------------------------------------------------------+
//| Выводит в журнал краткое описание серии снимков стакана цен      |
//+------------------------------------------------------------------+
void CMBookSeries::PrintShort(const bool dash=false)
  {
   ::Print((dash ? "- " : ""),this.Header());
  }
//+------------------------------------------------------------------+

По умолчанию флаг имеет значение false, и дефис перед описанием объекта не выводится.


Класс-коллекция стаканов цен

У нас созданы все необходимые объекты для создания коллекции стаканов цен. Есть объект-заявка стакана цен, который в терминале представлен структурой MqlBookInfo. При наступлении события BookEvent вызывается обработчик этого события OnBookEvent(), в котором его параметром указывается символ, на котором произошло событие изменения стакана цен. При этом мы можем в массив структур MqlBookInfo получить все текущие данные стакана цен. Совокупность этих данных у нас в библиотеке описывается объектом-снимком стакана цен — в нём содержатся объекты-заявки — ровно то количество, которое мы получили при обработке события изменения стакана. При каждом изменении стакана цен мы создаём новый объект-снимок, который добавляем к списку. Такой список у нас представлен объектом-серией снимков стаканов цен. Для каждого символа, используемого в программе и на который осуществлена подписка на получение событий стакана цен, мы создаём такие списки. Эти списки у нас создаются в реалтайм-режиме (к сожалению, сам терминал не имеет истории изменений стакана цен для символа). В итоге, нам теперь необходимо все имеющиеся списки объединить в один объект-коллекцию стаканов цен символов.

Коллекция стаканов цен символов будет хранить в себе постоянно пополняемые списки снимков стаканов цен, что позволит нам создать историю изменений стакана цен для каждого символа во время работы программы. Мы сможем получать данные любого снимка из имеющихся в списках коллекции, а из каждого снимка мы можем извлекать любую заявку из содержащихся в нём. У нас будет возможность искать нужные данные, фильтровать списки по заданным критериям и проводить статистические исследования с имеющимися списками коллекции.

Итак, в папке библиотеки \MQL5\Include\DoEasy\Collections\ создадим новый класс CMBookSeriesCollection в файле BookSeriesCollection.mqh.

Базовым классом должен быть класс базового объекта всех объектов библиотеки CBaseObj, и к листингу класса должны быть подключены все необходимые  файлы:

//+------------------------------------------------------------------+
//|                                         BookSeriesCollection.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"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "ListObj.mqh"
#include "..\Objects\Book\MBookSeries.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
//+------------------------------------------------------------------+
//| Коллекция серий стаканов цен символов                            |
//+------------------------------------------------------------------+
class CMBookSeriesCollection : public CBaseObj
  {
  }

Рассмотрим тело класса, его методы, а затем разберём реализацию методов и их назначение:

//+------------------------------------------------------------------+
//| Коллекция серий стаканов цен символов                            |
//+------------------------------------------------------------------+
class CMBookSeriesCollection : public CBaseObj
  {
private:
   CListObj                m_list;                                   // Список используемых серий стаканов цен символов
//--- Возвращает индекс серии стакана цен по имени символа
   int                     IndexMBookSeries(const string symbol);
public:
//--- Возвращает (1) себя, (2) список-коллекцию серий стаканов цен, (3) количество серий стаканов цен в списке
   CMBookSeriesCollection *GetObject(void)                              { return &this;               }
   CArrayObj              *GetList(void)                                { return &this.m_list;        }
   int                     DataTotal(void)                        const { return this.m_list.Total(); }
//--- Возвращает указатель на объект серий стаканов цен (1) по символу, (2) по индексу в списке
   CMBookSeries           *GetMBookseries(const string symbol);
   CMBookSeries           *GetMBookseries(const int index)              { return this.m_list.At(index);}
//--- Создаёт список-коллекцию серий стаканов цен символов
   bool                    CreateCollection(const CArrayObj *list_symbols,const uint required=0);
//--- Устанавливает флаг использования серии стакана цен (1) указанного символа, (2) всех символов
   void                    SetAvailableMBookSeries(const string symbol,const bool flag=true);
   void                    SetAvailableMBookSeries(const bool flag=true);
//--- Возвращает флаг использования серии стакана цен (1) указанного символа, (2) всех символов
   bool                    IsAvailableMBookSeries(const string symbol);
   bool                    IsAvailableMBookSeries(void);

//--- Устанавливает количество дней истории стакана цен (1) указанного символа, (2) всех символов
   bool                    SetRequiredUsedDays(const string symbol,const uint required=0);
   bool                    SetRequiredUsedDays(const uint required=0);

//--- Возвращает объект-снимок стакана цен указанного символа (1) по индексу, (2) по времени в миллисекундах
   CMBookSnapshot         *GetMBook(const string symbol,const int index);
   CMBookSnapshot         *GetMBook(const string symbol,const long time_msc);

//--- Обновляет серию стакана цен указанного символа
   bool                    Refresh(const string symbol,const long time_msc);

//--- Выводит в журнал (1) полное, (2) краткое описание коллекции
   void                    Print(void);
   void                    PrintShort(void);
   
//--- Конструктор
                           CMBookSeriesCollection();
  };
//+------------------------------------------------------------------+

Из описаний методов понятно, что сама коллекция представлена списком m_list, в котором будут храниться объекты-серии снимков стаканов цен разных символов, представленных классом CMBookSeries, в которых в свою очередь содержатся объекты-снимки стакана цен, представленных классом CMBookSnapshot.

Рассмотрим подробнее реализацию методов класса.

В конструкторе класса очищается список-коллекция, списку устанавливается флаг сортированного списка и присваивается идентификатор списка коллекции серий стаканов цен:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CMBookSeriesCollection::CMBookSeriesCollection()
  {
   this.m_list.Clear();
   this.m_list.Sort();
   this.m_list.Type(COLLECTION_MBOOKSERIES_ID);
  }
//+------------------------------------------------------------------+

По умолчанию все списки серий сортируются в списке-коллекции по наименованию символов серий снимков стаканов цен.

Метод создания списка-коллекции серий стаканов цен символов:

//+------------------------------------------------------------------+
//| Создаёт список-коллекцию серий стаканов цен символов             |
//+------------------------------------------------------------------+
bool CMBookSeriesCollection::CreateCollection(const CArrayObj *list_symbols,const uint required=0)
  {
//--- Если передан пустой список объектов-символов - выходим
   if(list_symbols==NULL)
      return false;
//--- Получаем количество объектов-символов в переданном списке
   int total=list_symbols.Total();
//--- Очищаем список-коллекцию серий стаканов цен
   this.m_list.Clear();
//--- В цикле по всем объектам-символам
   for(int i=0;i<total;i++)
     {
      //--- получаем очередной объект-символ
      CSymbol *symbol_obj=list_symbols.At(i);
      //--- если объект-символ получить не удалось - переходим к следующему в списке
      if(symbol_obj==NULL)
         continue;
      //--- Создаём новый объект-серию стаканов цен
      CMBookSeries *bookseries=new CMBookSeries(symbol_obj.Name(),required);
      //--- Если объект-серию стаканов цен создать не удалось - переходим к следующему символу в списке
      if(bookseries==NULL)
         continue;
      //--- Списку-коллекции серий стаканов цен устанавливаем флаг сортированного списка
      this.m_list.Sort();
      //--- Если объект с таким именем символа уже есть в списке-коллекции серий стаканов цен - удаляем объект-сериию стакана цен
      if(this.m_list.Search(bookseries)>WRONG_VALUE)
         delete bookseries;
      //--- иначе - объекта с таким именем символа ещё нет в коллекции
      else
        {
         //--- если объект-серию стакана цен не удалось добавить в список-коллекцию - удаляем объект-серию,
         //--- выводим об этом сообщение и возвращаем false
         if(!this.m_list.Add(bookseries))
           {
            delete bookseries;
            ::Print(DFUN,"\"",symbol_obj.Name(),"\": ",CMessage::Text(MSG_MBOOK_SERIES_ERR_ADD_TO_LIST));
            return false;
           }
        } 
     }
//--- Коллекция успешно создана, возвращаем true
   return true;
  }
//+------------------------------------------------------------------+

Вся логика метода подробно расписана в его листинге. Кратко: в метод передаётся список всех используемых в программе символов и количество снимков стакана цен, которое можно хранить в списках коллекции. В цикле по переданному в метод списку получаем из списка очередной объект-символ, создаём новый объект-серию снимков стакана цен по этому символу и добавляем его в список-коллекцию. В итоге у нас получается список объектов-серий снимков стаканов цен разных символов — тех, которые присутствуют в переданном в метод списке. Каждый созданный список серий снимков изначально пустой.
Наполнение созданных в коллекции списков будет происходить в методе обновления списка-серии снимков стакана цен указанного символа:

//+------------------------------------------------------------------+
//| Обновляет список-серию снимков стакана цен указанного символа    |
//+------------------------------------------------------------------+
bool CMBookSeriesCollection::Refresh(const string symbol,const long time_msc)
  {
   CMBookSeries *bookseries=this.GetMBookseries(symbol);
   if(bookseries==NULL)
      return false;
   return bookseries.Refresh(time_msc);
  }
//+------------------------------------------------------------------+

Так как обработчик OnBookEvent() срабатывает для каждого символа, то и метод обновления мы используем не для всей коллекции, а только для одного из символов коллекции. В метод передаётся наименование символа, на котором произошло событие изменения стакана цен и время регистрации события изменения стакана цен в миллисекундах. Далее получаем объект-серию снимков стакана цен по переданному в метод символу и возвращаем результат работы метода Refresh() класса объекта-серии снимков стакана цен, рассмотренный нами в прошлой статье.

Метод, возвращающий индекс серии стакана цен по имени символа:

//+------------------------------------------------------------------+
//| Возвращает индекс серии стакана цен по имени символа             |
//+------------------------------------------------------------------+
int CMBookSeriesCollection::IndexMBookSeries(const string symbol)
  {
   const CMBookSeries *obj=new CMBookSeries(symbol==NULL || symbol=="" ? ::Symbol() : symbol);
   if(obj==NULL)
      return WRONG_VALUE;
   this.m_list.Sort();
   int index=this.m_list.Search(obj);
   delete obj;
   return index;
  }
//+------------------------------------------------------------------+

Здесь: создаём новый временный объект-серию снимков стакана цен с указанным во входных параметрах символом. Устанавливаем списку флаг сортированного списка (поиск ведётся только в сортированном списке) и получаем индекс объекта с таким же символом в списке. Обязательно удаляем созданный временный объект и возвращаем полученный индекс. Если объект-серия снимков стакана цен с указанным символом есть в списке, то метод Search() вернёт его индекс, в противном случае метод вернёт -1.

Метод, возвращающий объект серии стакана цен указанного символа:

//+------------------------------------------------------------------+
//| Возвращает объект серии стакана цен указанного символа           |
//+------------------------------------------------------------------+
CMBookSeries *CMBookSeriesCollection::GetMBookseries(const string symbol)
  {
   int index=this.IndexMBookSeries(symbol);
   return this.m_list.At(index);
  }
//+------------------------------------------------------------------+

Здесь: при помощи вышерассмотренного метода получаем индекс объекта-серии снимков стакана цен в списке по символу и возвращаем указатель на объект в списке по полученному индексу. Если объекта с указанным символом нет в списке (индекс равен -1), то метод At() вернёт NULL.

Для использования функционала работы со стаканами цен необходимо осуществить подписку на получение событий изменения стакана цен для каждого символа. Это у нас уже готово и работает — в классе объекта-символа нами уже сделан этот функционал. Помимо этого, в классе объекта-серии снимков стаканов цен у нас есть флаг, который указывает нам на необходимость включения/отключения обработки событий изменения стакана цен. Т.е. даже если по символу уже осуществлена подписка на события BookEvent, то мы можем временно отключить работу по данному символу со стаканом цен при помощи управления этим флагом. Для установки флага необходимости обрабатывать события BookEvent по символу у нас есть метод:

//+------------------------------------------------------------------+
//| Устанавливает флаг использования серии                           |
//| стакана цен указанного символа                                   |
//+------------------------------------------------------------------+
void CMBookSeriesCollection::SetAvailableMBookSeries(const string symbol,const bool flag=true)
  {
   CMBookSeries *bookseries=this.GetMBookseries(symbol);
   if(bookseries==NULL)
      return;
   bookseries.SetAvailable(flag);
  }
//+------------------------------------------------------------------+

Здесь: получаем объект-серию стаканов цен указанного символа и устанавливаем для него переданный в метод флаг.
По умолчанию значение флага равно true.

Если наша программа работает со множеством символов, и нам нужно одновременно управлять вышеописанными флагами серий снимков стаканов цен, то для этого у нас есть метод, позволяющий установить значение флага сразу для всех используемых в программе символов:

//+------------------------------------------------------------------+
//|Устанавливает флаг использования серий стаканов цен всех символов |
//+------------------------------------------------------------------+
void CMBookSeriesCollection::SetAvailableMBookSeries(const bool flag=true)
  {
   for(int i=0;i<this.m_list.Total();i++)
     {
      CMBookSeries *bookseries=this.m_list.At(i);
      if(bookseries==NULL)
         continue;
      bookseries.SetAvailable(flag);
     }
  }
//+------------------------------------------------------------------+

Здесь: в цикле по общему количеству объектов-серий снимков стаканов цен в коллекции получаем очередной список-серию и устанавливаем для него переданный в метод флаг. По умолчанию значение флага равно true.

Методы, обратные вышерассмотренным — возвращают значения флагов работы со списком-серией либо указанного символа, либо сразу всех.

Метод, возвращающий флаг использования серии стакана цен указанного символа:

//+------------------------------------------------------------------+
//|Возвращает флаг использования серии стакана цен указанного символа|
//+------------------------------------------------------------------+
bool CMBookSeriesCollection::IsAvailableMBookSeries(const string symbol)
  {
   CMBookSeries *bookseries=this.GetMBookseries(symbol);
   if(bookseries==NULL)
      return false;
   return bookseries.IsAvailable();
  }
//+------------------------------------------------------------------+

Здесь: получаем объект-серию снимков стаканов цен по переданному в метод символу и возвращаем значение флага, установленное для этого объекта.

Метод, возвращающий флаг использования серий стаканов цен всех символов:

//+------------------------------------------------------------------+
//| Возвращает флаг использования серий стаканов цен всех символов   |
//+------------------------------------------------------------------+
bool CMBookSeriesCollection::IsAvailableMBookSeries(void)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CMBookSeries *bookseries=this.m_list.At(i);
      if(bookseries==NULL)
         continue;
      res &=bookseries.IsAvailable();
     }
   return res;
  }
//+------------------------------------------------------------------+

Данный метод возвращает true только в том случае, если для каждого из используемых символов в их сериях стаканов цен установлен флаг необходимости их использования. Здесь мы в цикле по всем объектам-сериям коллекции получаем очередную серию и добавляем к переменной значение флага этой серии. Если хоть у одного из символов флаг будет иметь значение false, то метод вернёт false.

Метод, устанавливающий количество снимков в истории стакана цен указанного символа:

//+------------------------------------------------------------------+
//| Устанавливает количество снимков в истории                       |
//| стакана цен указанного символа                                   |
//+------------------------------------------------------------------+
bool CMBookSeriesCollection::SetRequiredUsedDays(const string symbol,const uint required=0)
  {
   CMBookSeries *bookseries=this.GetMBookseries(symbol);
   if(bookseries==NULL)
      return false;
   bookseries.SetRequiredUsedDays(required);
   return true;
  }
//+------------------------------------------------------------------+

Для ограничения общего количества снимков стакана цен для каждого из символов — чтобы не расходовать лишнюю память на устаревшие данные, служит этот метод. Здесь: получаем список-серию снимков указанного символа и устанавливаем ему максимальное количество снимков в списке. Если список-серию снимков получить не удалось — возвращаем false. При успешной установке требуемого количества возвращаем true.

Метод, устанавливающий количество снимков в истории стаканов всех символов:

//+------------------------------------------------------------------+
//|Устанавливает количество снимков в истории стаканов всех символов |
//+------------------------------------------------------------------+
bool CMBookSeriesCollection::SetRequiredUsedDays(const uint required=0)
  {
   bool res=true;
   for(int i=0;i<this.m_list.Total();i++)
     {
      CMBookSeries *bookseries=this.m_list.At(i);
      if(bookseries==NULL)
        {
         res &=false;
         continue;
        }
      bookseries.SetRequiredUsedDays(required);
     }
   return res;
  }
//+------------------------------------------------------------------+

Данный метод устанавливает одинаковое количество требуемых данных истории стакана цен сразу для всех используемых в программе символов.

В цикле по общему количеству объектов-серий в коллекции получаем очередной объект-серию снимков стакана и устанавливаем ему указанное количество данных. Если серию получить не удалось, то к результату добавляется значение false. Таким образом, если хоть для одной серии не удалось установить нужное количество, то метод вернёт false. По окончании цикла возвращаем полученный в переменной res результат.

Метод, возвращающий объект-снимок стакана цен указанного символа по индексу:

//+------------------------------------------------------------------+
//|Возвращает объект-снимок стакана цен указанного символа по индексу|
//+------------------------------------------------------------------+
CMBookSnapshot *CMBookSeriesCollection::GetMBook(const string symbol,const int index)
  {
   CMBookSeries *bookseries=this.GetMBookseries(symbol);
   if(bookseries==NULL)
      return NULL;
   return bookseries.GetMBookByListIndex(index);
  }
//+------------------------------------------------------------------+

Здесь: получаем объект-серию снимков стакана цен указанного символа и возвращаем указатель на объект-снимок стакана цен по указанному индексу методом GetMBookByListIndex() класса CMBookSeries, описанным в прошлой статье.

Метод, возвращающий объект-снимок стакана цен указанного символа по времени в миллисекундах:

//+------------------------------------------------------------------+
//| Возвращает объект-снимок стакана цен указанного символа          |
//| по времени в миллисекундах                                       |
//+------------------------------------------------------------------+
CMBookSnapshot *CMBookSeriesCollection::GetMBook(const string symbol,const long time_msc)
  {
   CMBookSeries *bookseries=this.GetMBookseries(symbol);
   if(bookseries==NULL)
      return NULL;
   return bookseries.GetMBook(time_msc);
  }
//+------------------------------------------------------------------+

Здесь: получаем объект-серию снимков стакана цен указанного символа и возвращаем указатель на объект-снимок стакана цен по указанному индексу методом GetMBook() класса CMBookSeries, описанным в прошлой статье.

Метод специфичен тем, что для гарантированного получения указателя на объект-снимок стакана цен, нужно точно и заранее знать его время в миллисекундах.

Метод, выводящий в журнал полное описание коллекции серий снимков стаканов цен:

//+------------------------------------------------------------------+
//| Выводит в журнал полное описание коллекции                       |
//+------------------------------------------------------------------+
void CMBookSeriesCollection::Print(void)
  {
   ::Print(CMessage::Text(MSG_MB_COLLECTION_TEXT_MBCOLLECTION),":");
   for(int i=0;i<this.m_list.Total();i++)
     {
      CMBookSeries *bookseries=this.m_list.At(i);
      if(bookseries==NULL)
         continue;
      bookseries.Print(true);
     }
  }
//+------------------------------------------------------------------+

Сначала распечатываем заголовок, а затем в цикле получаем каждый последующий объект-серию стакана цен коллекции и распечатываем его полное описание. При этом в метод Print() объекта-серии снимков передаём true — чтобы метод поставил перед описанием серии дефис.

Метод, выводящий в журнал краткое описание коллекции:

//+------------------------------------------------------------------+
//| Выводит в журнал краткое описание коллекции                      |
//+------------------------------------------------------------------+
void CMBookSeriesCollection::PrintShort(void)
  {
   ::Print(CMessage::Text(MSG_MB_COLLECTION_TEXT_MBCOLLECTION),":");
   for(int i=0;i<this.m_list.Total();i++)
     {
      CMBookSeries *bookseries=this.m_list.At(i);
      if(bookseries==NULL)
         continue;
      bookseries.PrintShort(true);
     }
  }
//+------------------------------------------------------------------+

Метод идентичен вышерассмотренному, за исключением того, что для вывода описания серий коллекции используется метод вывода краткого описания объекта-серии стаканов цен.

Доработаем класс главного объекта библиотеки CEngine в файле \MQL5\Include\DoEasy\Engine.mqh.

Подключим к файлу класс-коллекцию серий снимков стаканов цен:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://MQL5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://MQL5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
#include "Collections\ResourceCollection.mqh"
#include "Collections\TimeSeriesCollection.mqh"
#include "Collections\BuffersCollection.mqh"
#include "Collections\IndicatorsCollection.mqh"
#include "Collections\TickSeriesCollection.mqh"
#include "Collections\BookSeriesCollection.mqh"
#include "TradingControl.mqh"
//+------------------------------------------------------------------+

В списке объектов класса допишем новый объект — объект класса-коллекции серий снимков стаканов цен:

//+------------------------------------------------------------------+
//| Класс-основа библиотеки                                          |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Коллекция исторических ордеров и сделок
   CMarketCollection    m_market;                        // Коллекция рыночных ордеров и сделок
   CEventsCollection    m_events;                        // Коллекция событий
   CAccountsCollection  m_accounts;                      // Коллекция аккаунтов
   CSymbolsCollection   m_symbols;                       // Коллекция символов
   CTimeSeriesCollection m_time_series;                  // Коллекция таймсерий
   CBuffersCollection   m_buffers;                       // Коллекция индикаторных буферов
   CIndicatorsCollection m_indicators;                   // Коллекция индикаторов
   CTickSeriesCollection m_tick_series;                  // Коллекция тиковых серий
   CMBookSeriesCollection m_book_series;                 // Коллекция серий стаканов цен
   CResourceCollection  m_resource;                      // Список ресурсов
   CTradingControl      m_trading;                       // Объект управления торговлей
   CPause               m_pause;                         // Объект "Пауза"
   CArrayObj            m_list_counters;                 // Список счётчиков таймера
   int                  m_global_error;                  // Код глобальной ошибки
   bool                 m_first_start;                   // Флаг первого запуска
   bool                 m_is_hedge;                      // Флаг хедж-счёта
   bool                 m_is_tester;                     // Флаг работы в тестере
   bool                 m_is_market_trade_event;         // Флаг торгового события на счёте
   bool                 m_is_history_trade_event;        // Флаг торгового события в истории счёта
   bool                 m_is_account_event;              // Флаг события изменения аккаунта
   bool                 m_is_symbol_event;               // Флаг события изменения свойств символа
   ENUM_TRADE_EVENT     m_last_trade_event;              // Последнее торговое событие на счёте
   int                  m_last_account_event;            // Последнее событие в свойствах счёта
   int                  m_last_symbol_event;             // Последнее событие в свойствах символа
   ENUM_PROGRAM_TYPE    m_program;                       // Тип программы
   string               m_name;                          // Имя программы

Добавим новый обработчик события BookEvent, и в методе, устанавливающем список используемых символов в коллекции символов, помимо передачи количества дней для тиковых серий, добавим передачу максимального количества снимков стаканов цен:

//--- (1) Таймер, обработчик события (2) NewTick, (3) Calculate, (4) BookEvent, (4) Deinit
   void                 OnTimer(SDataCalculate &data_calculate);
   void                 OnTick(SDataCalculate &data_calculate,const uint required=0);
   int                  OnCalculate(SDataCalculate &data_calculate,const uint required=0);
   bool                 OnBookEvent(const string &symbol);
   void                 OnDeinit(void);
   
//--- Устанавливает список используемых символов в коллекции символов и создаёт коллекцию таймсерий символов
   bool                 SetUsedSymbols(const string &array_symbols[],const uint required_ticks=0,const uint required_books=0);

В списке методов работы с коллекцией тиковых серий добавим новые методы для упрощения работы с коллекцией тиковых серий, и напишем методы для работы с коллекцией серий стаканов цен:

//--- Возвращает (1) коллекцию тиковых серий, (2) список тиковых серий из коллекции тиковых серий
   CTickSeriesCollection *GetTickSeriesCollection(void)                                { return &this.m_tick_series;                                  }
   CArrayObj           *GetListTickSeries(void)                                        { return this.m_tick_series.GetList();                         }

//--- Создаёт (1) указанную тиковыу серию, (2) все тиковые серии
   bool                 TickSeriesCreate(const string symbol,const uint required=0)    { return this.m_tick_series.CreateTickSeries(symbol,required); }
   bool                 TickSeriesCreateAll(const uint required=0)                     { return this.m_tick_series.CreateTickSeriesAll(required);     }

//--- Обновляет тиковую серию (1) указанного символа, (2) всех символов коллекции, (3) всех символов кроме текущего
   void                 TickSeriesRefresh(const string symbol)                         { this.m_tick_series.Refresh(symbol);                          }
   void                 TickSeriesRefreshAll(void)                                     { this.m_tick_series.Refresh();                                }
   void                 TickSeriesRefreshAllExceptCurrent(void)                        { this.m_tick_series.RefreshExpectCurrent();                   }

//--- Возвращает (1) коллекцию серий стаканов цен, (2) список серий стаканов цен из коллекции серий стаканов цен
   CMBookSeriesCollection *GetMBookSeriesCollection(void)                              { return &this.m_book_series;                         }
   CArrayObj           *GetListMBookSeries(void)                                       { return this.m_book_series.GetList();                }

//--- Обновляет серию стакана цен указанного символа
   void                 MBookSeriesRefresh(const string symbol,const long time_msc)    { this.m_book_series.Refresh(symbol,time_msc);        }

//--- Возвращает серию стакана цен указанного символа
   CMBookSeries        *GetMBookSeries(const string symbol)                            { return this.m_book_series.GetMBookseries(symbol);   }

Все эти методы просто вызывают одноимённые методы соответствующих коллекций.

За пределами тела класса напишем реализацию обработчика OnBookEvent() библиотеки:

//+------------------------------------------------------------------+
//| Обработчик события BookEvent                                     |
//+------------------------------------------------------------------+
bool CEngine::OnBookEvent(const string &symbol)
  {
   CSymbol *sym=this.m_symbols.GetSymbolObjByName(symbol);
   if(sym==NULL || !sym.BookdepthSubscription())
      return false;
   return this.m_book_series.Refresh(sym.Name(),sym.Time());
  }
//+------------------------------------------------------------------+

Так как обработчик OnBookEvent() всегда срабатывает на событие изменения стакана цен только одного символа, то этот символ мы можем узнать внутри самого обработчика.
Соответственно, в данный метод мы передаём наименование символа, на котором было зарегистрировано событие изменения стакана цен, а далее — получаем соответствующий объект-символ из класса-коллекции символов и возвращаем результат работы метода обновления соответствующей серии снимков стакана цен, попутно указывая время в миллисекундах, которое будет установлено для вновь созданного снимка стакана цен в методе Refresh().

Доработаем метод, устанавливающий список используемых символов в коллекции символов:

//+------------------------------------------------------------------+
//| Устанавливает список используемых символов в коллекции символов  |
//| и создаёт коллекции таймсерий, тиковых серий                     |
//| и серий стаканов цен символов                                    |
//+------------------------------------------------------------------+
bool CEngine::SetUsedSymbols(const string &array_symbols[],const uint required_ticks=0,const uint required_books=0)
  {
   bool res=this.m_symbols.SetUsedSymbols(array_symbols);
   CArrayObj *list=this.GetListAllUsedSymbols();
   if(list==NULL)
      return false;
   res&=this.m_time_series.CreateCollection(list);
   res&=this.m_tick_series.CreateCollection(list,required_ticks);
   res&=this.m_book_series.CreateCollection(list,required_books);
   return res;
  }
//+------------------------------------------------------------------+

Теперь в метод передаются количество дней тиковых серий и максимальное количество снимков стаканов цен в их сериях.
Здесь мы, соответственно, добавили создание коллекции серий снимков стаканов цен всех символов.

На этом разработка классов для работы с сериями стаканов цен завершена.
В последующем мы, возможно, вернёмся к этой теме для доработок, но пока займёмся другим необходимым функционалом библиотеки.

Сервис сигналов MQL5.com — это сервис копи-трейдинга, когда сделки поставщика автоматически повторяются на вашем торговом счете.
Объектом mql5-сигнала здесь мы будем считать торговый счет, на котором отслеживаются торговые операции для публичной трансляции торговых операций — источник сигнала.
У источника сигнала есть свои параметры, которые можно получить посредством функций SignalBaseGetInteger(), SignalBaseGetDouble() и SignalBaseGetString(). А ещё есть параметры подписки — настроек копирования сигнала для конкретного счёта. Эти параметры можно получить посредством функций SignalInfoGetDouble(), SignalInfoGetInteger() и SignalInfoGetString().

Объект-mql5-сигнала будет описывать собою один источник сигнала, доступный в терминале для подписки. С одного счёта можно подписаться только на один сигнал, но иметь в своём распоряжении полный список всех доступных сигналов для подписки — объектов-mql5-сигналов, с возможностью сортировать их по любому из свойств, фильтровать, сравнивать и подписываться на выбранный сигнал — это весьма удобный и полезный функционал для тех, кто хочет использовать сервис сигналов ресурса MQL5.com.

Класс объекта-mql5-сигнала

В файле \MQL5\Include\DoEasy\Defines.mqh пропишем перечисления целочисленных, вещественных и строковых свойств объекта-mql5-сигнала:

//+------------------------------------------------------------------+
//| Данные для работы с сигналами MQL5.com                           |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Список возможных событий сигнала                                 |
//+------------------------------------------------------------------+
#define SIGNAL_MQL5_EVENTS_NEXT_CODE  (MBOOK_ORD_EVENTS_NEXT_CODE+1)   // Код следующего события после последнего кода события сигнала
//+------------------------------------------------------------------+
//| Целочисленные свойства mql5-сигнала                              |
//+------------------------------------------------------------------+
enum ENUM_SIGNAL_MQL5_PROP_INTEGER
  {
   SIGNAL_MQL5_PROP_TRADE_MODE = 0,                   // Тип счёта
   SIGNAL_MQL5_PROP_DATE_PUBLISHED,                   // Дата публикации сигнала 
   SIGNAL_MQL5_PROP_DATE_STARTED,                     // Дата начала мониторинга сигнала
   SIGNAL_MQL5_PROP_DATE_UPDATED,                     // Дата последнего обновления торговой статистики сигнала
   SIGNAL_MQL5_PROP_ID,                               // ID сигнала
   SIGNAL_MQL5_PROP_LEVERAGE,                         // Плечо торгового счета
   SIGNAL_MQL5_PROP_PIPS,                             // Результат торговли в пипсах
   SIGNAL_MQL5_PROP_RATING,                           // Позиция в рейтинге сигналов
   SIGNAL_MQL5_PROP_SUBSCRIBERS,                      // Количество подписчиков
   SIGNAL_MQL5_PROP_TRADES,                           // Количество трейдов
   SIGNAL_MQL5_PROP_SUBSCRIPTION_STATUS,              // Состояние подписки счёта на этот сигнал
  };
#define SIGNAL_MQL5_PROP_INTEGER_TOTAL (11)           // Общее количество целочисленных свойств
#define SIGNAL_MQL5_PROP_INTEGER_SKIP  (0)            // Количество неиспользуемых в сортировке целочисленных свойств стакана
//+------------------------------------------------------------------+
//| Вещественные свойства mql5-сигнала                               |
//+------------------------------------------------------------------+
enum ENUM_SIGNAL_MQL5_PROP_DOUBLE
  {
   SIGNAL_MQL5_PROP_BALANCE = SIGNAL_MQL5_PROP_INTEGER_TOTAL, // Баланс счета
   SIGNAL_MQL5_PROP_EQUITY,                           // Средства на счете
   SIGNAL_MQL5_PROP_GAIN,                             // Прирост счета в процентах
   SIGNAL_MQL5_PROP_MAX_DRAWDOWN,                     // Максимальная просадка
   SIGNAL_MQL5_PROP_PRICE,                            // Цена подписки на сигнал
   SIGNAL_MQL5_PROP_ROI,                              // Значение ROI (Return on Investment) сигнала в %
  };
#define SIGNAL_MQL5_PROP_DOUBLE_TOTAL  (6)            // Общее количество вещественных свойств
#define SIGNAL_MQL5_PROP_DOUBLE_SKIP   (0)            // Количество неиспользуемых в сортировке вещественных свойств
//+------------------------------------------------------------------+
//| Строковые свойства mql5-сигнала                                  |
//+------------------------------------------------------------------+
enum ENUM_SIGNAL_MQL5_PROP_STRING
  {
   SIGNAL_MQL5_PROP_AUTHOR_LOGIN = (SIGNAL_MQL5_PROP_INTEGER_TOTAL+SIGNAL_MQL5_PROP_DOUBLE_TOTAL), // Логин автора сигнала
   SIGNAL_MQL5_PROP_BROKER,                           // Наименование брокера (компании)
   SIGNAL_MQL5_PROP_BROKER_SERVER,                    // Сервер брокера
   SIGNAL_MQL5_PROP_NAME,                             // Имя сигнала
   SIGNAL_MQL5_PROP_CURRENCY,                         // Валюта счета сигнала
  };
#define SIGNAL_MQL5_PROP_STRING_TOTAL  (5)            // Общее количество строковых свойств
//+------------------------------------------------------------------+

Список возможных событий mql5-сигнала пока делать не будем, а ограничимся константой, указывающей на код следующего события после кодов событий mql5-сигналов, а далее, скорее всего, это изменим.

Для возможности поиска и сортировки по свойствам mql5-сигналов, определим перечисление всех возможных критериев сортировки:

//+------------------------------------------------------------------+
//| Возможные критерии сортировки mql5-сигналов                      |
//+------------------------------------------------------------------+
#define FIRST_SIGNAL_MQL5_DBL_PROP  (SIGNAL_MQL5_PROP_INTEGER_TOTAL-SIGNAL_MQL5_PROP_INTEGER_SKIP)
#define FIRST_SIGNAL_MQL5_STR_PROP  (SIGNAL_MQL5_PROP_INTEGER_TOTAL-SIGNAL_MQL5_PROP_INTEGER_SKIP+SIGNAL_MQL5_PROP_DOUBLE_TOTAL-SIGNAL_MQL5_PROP_DOUBLE_SKIP)
enum ENUM_SORT_SIGNAL_MQL5_MODE
  {
//--- Сортировка по целочисленным свойствам
   SORT_BY_SIGNAL_MQL5_TRADE_MODE = 0,                // Сортировать по типу сигнала
   SORT_BY_SIGNAL_MQL5_DATE_PUBLISHED,                // Сортировать по дате публикации сигнала 
   SORT_BY_SIGNAL_MQL5_DATE_STARTED,                  // Сортировать по дате начала мониторинга сигнала
   SORT_BY_SIGNAL_MQL5_DATE_UPDATED,                  // Сортировать по дате последнего обновления торговой статистики сигнала
   SORT_BY_SIGNAL_MQL5_ID,                            // Сортировать по ID сигнала
   SORT_BY_SIGNAL_MQL5_LEVERAGE,                      // Сортировать по плечу торгового счета
   SORT_BY_SIGNAL_MQL5_PIPS,                          // Сортировать по результату торговли в пипсах
   SORT_BY_SIGNAL_MQL5_RATING,                        // Сортировать по позиции в рейтинге сигналов
   SORT_BY_SIGNAL_MQL5_SUBSCRIBERS,                   // Сортировать по количеству подписчиков
   SORT_BY_SIGNAL_MQL5_TRADES,                        // Сортировать по количеству трейдов
   SORT_BY_SIGNAL_MQL5_SUBSCRIPTION_STATUS,           // Сортировать по состоянию подписки счёта на этот сигнал
//--- Сортировка по вещественным свойствам
   SORT_BY_SIGNAL_MQL5_BALANCE = FIRST_SIGNAL_MQL5_DBL_PROP, // Сортировать по балансу счета
   SORT_BY_SIGNAL_MQL5_EQUITY,                        // Сортировать по средствам на счете
   SORT_BY_SIGNAL_MQL5_GAIN,                          // Сортировать по приросту счета в процентах
   SORT_BY_SIGNAL_MQL5_MAX_DRAWDOWN,                  // Сортировать по максимальной просадке
   SORT_BY_SIGNAL_MQL5_PRICE,                         // Сортировать по цене подписки на сигнал
   SORT_BY_SIGNAL_MQL5_ROI,                           // Сортировать по значению ROI
//--- Сортировка по строковым свойствам
   SORT_BY_SIGNAL_MQL5_AUTHOR_LOGIN = FIRST_SIGNAL_MQL5_STR_PROP, // Сортировать по логину автора сигнала
   SORT_BY_SIGNAL_MQL5_BROKER,                        // Сортировать по наименованию брокера (компании)
   SORT_BY_SIGNAL_MQL5_BROKER_SERVER,                 // Сортировать по серверу брокера
   SORT_BY_SIGNAL_MQL5_NAME,                          // Сортировать по имени сигнала
   SORT_BY_SIGNAL_MQL5_CURRENCY,                      // Сортировать по валюте счета сигнала
  };
//+------------------------------------------------------------------+

Это необходимый минимум для создания нового класса нового объекта библиотеки.

В каталоге библиотеки \MQL5\Include\DoEasy\Objects\ создадим новую папку MQLSignalBase\, а в ней — новый файл  MQLSignal.mqh класса CMQLSignal.

Базовым классом должен быть класс базового объекта всех объектов библиотеки CBaseObj:

//+------------------------------------------------------------------+
//|                                                    MQLSignal.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 "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Класс абстрактного mql5-сигнала                                  |
//+------------------------------------------------------------------+
class CMQLSignal : public CBaseObj
  {
  }

Рассмотрим состав класса, а далее — реализацию его методов:

//+------------------------------------------------------------------+
//|                                                    MQLSignal.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 "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Класс абстрактного mql5-сигнала                                  |
//+------------------------------------------------------------------+
class CMQLSignal : public CBaseObj
  {
private:
   long              m_long_prop[SIGNAL_MQL5_PROP_INTEGER_TOTAL];       // Целочисленные свойства
   double            m_double_prop[SIGNAL_MQL5_PROP_DOUBLE_TOTAL];      // Вещественные свойства
   string            m_string_prop[SIGNAL_MQL5_PROP_STRING_TOTAL];      // Строковые свойства

//--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство
   int               IndexProp(ENUM_SIGNAL_MQL5_PROP_DOUBLE property)   const { return(int)property-SIGNAL_MQL5_PROP_INTEGER_TOTAL;                               }
   int               IndexProp(ENUM_SIGNAL_MQL5_PROP_STRING property)   const { return(int)property-SIGNAL_MQL5_PROP_INTEGER_TOTAL-SIGNAL_MQL5_PROP_DOUBLE_TOTAL; }

public:
//--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство объекта
   void              SetProperty(ENUM_SIGNAL_MQL5_PROP_INTEGER property,long value)    { this.m_long_prop[property]=value;                      }
   void              SetProperty(ENUM_SIGNAL_MQL5_PROP_DOUBLE property,double value)   { this.m_double_prop[this.IndexProp(property)]=value;    }
   void              SetProperty(ENUM_SIGNAL_MQL5_PROP_STRING property,string value)   { this.m_string_prop[this.IndexProp(property)]=value;    }
//--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство объекта
   long              GetProperty(ENUM_SIGNAL_MQL5_PROP_INTEGER property)         const { return this.m_long_prop[property];                     }
   double            GetProperty(ENUM_SIGNAL_MQL5_PROP_DOUBLE property)          const { return this.m_double_prop[this.IndexProp(property)];   }
   string            GetProperty(ENUM_SIGNAL_MQL5_PROP_STRING property)          const { return this.m_string_prop[this.IndexProp(property)];   }
//--- Возвращает себя
   CMQLSignal       *GetObject(void)                                                   { return &this;}

//--- Возвращает флаг поддержания объектом данного свойства
   virtual bool      SupportProperty(ENUM_SIGNAL_MQL5_PROP_INTEGER property)           { return true; }
   virtual bool      SupportProperty(ENUM_SIGNAL_MQL5_PROP_DOUBLE property)            { return true; }
   virtual bool      SupportProperty(ENUM_SIGNAL_MQL5_PROP_STRING property)            { return true; }

//--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства
   string            GetPropertyDescription(ENUM_SIGNAL_MQL5_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_SIGNAL_MQL5_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_SIGNAL_MQL5_PROP_STRING property);

//--- Выводит в журнал описание свойств объекта (full_prop=true - все свойства, false - только поддерживаемые)
   void              Print(const bool full_prop=false);
//--- Выводит в журнал краткое описание объекта
   virtual void      PrintShort(void);
//--- Возвращает краткое наименование объекта
   virtual string    Header(const bool shrt=false);
   
//--- Сравнивает объекты CMQLSignal между собой по указанному свойству (для сортировки списка по свойству объекта-mql5-сигнала)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Сравнивает объекты CMQLSignal между собой по всем свойствам (для поиска равных объектов-mql5-сигналов)
   bool              IsEqual(CMQLSignal* compared_obj) const;
   
//--- Конструкторы
                     CMQLSignal(){;}
                     CMQLSignal(const long signal_id);
                     
public:
//+------------------------------------------------------------------+ 
//| Методы упрощённого доступа к свойствам объекта-mql5-сигнала      |
//+------------------------------------------------------------------+
//--- Возвращает дату (1) публикации, (2) начала мониторинга, (3) последнего обновления торговой статистики,
//--- (4) идентификатор, (5) плечо торгового счёта, (6) результат торговли в пипсах, (7) позицию в рейтинге сигналов,
//--- (8) количество подписчиков, (9) количество трейдов, (10) тип счёта, (11) флаг подписки текущего счёта на данный сигнал
   datetime          DatePublished(void)     const { return (datetime)this.GetProperty(SIGNAL_MQL5_PROP_DATE_PUBLISHED);         }
   datetime          DateStarted(void)       const { return (datetime)this.GetProperty(SIGNAL_MQL5_PROP_DATE_STARTED);           }
   datetime          DateUpdated(void)       const { return (datetime)this.GetProperty(SIGNAL_MQL5_PROP_DATE_UPDATED);           }
   long              ID(void)                const { return this.GetProperty(SIGNAL_MQL5_PROP_ID);                               }
   long              Leverage(void)          const { return this.GetProperty(SIGNAL_MQL5_PROP_LEVERAGE);                         }
   long              Pips(void)              const { return this.GetProperty(SIGNAL_MQL5_PROP_PIPS);                             }
   long              Rating(void)            const { return this.GetProperty(SIGNAL_MQL5_PROP_RATING);                           }
   long              Subscribers(void)       const { return this.GetProperty(SIGNAL_MQL5_PROP_SUBSCRIBERS);                      }
   long              Trades(void)            const { return this.GetProperty(SIGNAL_MQL5_PROP_TRADES);                           }
   long              TradeMode(void)         const { return (long)this.GetProperty(SIGNAL_MQL5_PROP_TRADE_MODE);                 }
   bool              SubscriptionStatus(void)const { return (bool)this.GetProperty(SIGNAL_MQL5_PROP_SUBSCRIPTION_STATUS);        }
//--- Возвращает (1) баланс счёта, (2) средства на счёте, (3) прирост счёта в процентах,
//--- (4) максимальную просадку, (5) цену подписки, (6) значение ROI (Return on Investment) сигнала в %
   double            Balance(void)           const { return this.GetProperty(SIGNAL_MQL5_PROP_BALANCE);                          }
   double            Equity(void)            const { return this.GetProperty(SIGNAL_MQL5_PROP_EQUITY);                           }
   double            Gain(void)              const { return this.GetProperty(SIGNAL_MQL5_PROP_GAIN);                             }
   double            MaxDrawdown(void)       const { return this.GetProperty(SIGNAL_MQL5_PROP_MAX_DRAWDOWN);                     }
   double            Price(void)             const { return this.GetProperty(SIGNAL_MQL5_PROP_PRICE);                            }
   double            ROI(void)               const { return this.GetProperty(SIGNAL_MQL5_PROP_ROI);                              }
//--- Возвращает (1) логин автора сигнала, (2) наименование брокера (компании),
//--- (3) сервер брокера, (4) имя сигнала, (5) валюту счёта сигнала
   string            AuthorLogin(void)       const { return this.GetProperty(SIGNAL_MQL5_PROP_AUTHOR_LOGIN);                     }
   string            Broker(void)            const { return this.GetProperty(SIGNAL_MQL5_PROP_BROKER);                           }
   string            BrokerServer(void)      const { return this.GetProperty(SIGNAL_MQL5_PROP_BROKER_SERVER);                    }
   string            Name(void)              const { return this.GetProperty(SIGNAL_MQL5_PROP_NAME);                             }
   string            Currency(void)          const { return this.GetProperty(SIGNAL_MQL5_PROP_CURRENCY);                         }

//--- Устанавливает дату (1) публикации, (2) начала мониторинга, (3) последнего обновления торговой статистики,
//--- (4) идентификатор, (5) плечо торгового счёта, (6) результат торговли в пипсах, (7) позицию в рейтинге сигналов,
//--- (8) количество подписчиков, (9) количество трейдов, (10) тип счёта, (11) флаг подписки текущего счёта на данный сигнал
   void              SetDatePublished(const datetime date)  { this.SetProperty(SIGNAL_MQL5_PROP_DATE_PUBLISHED,date);            }
   void              SetDateStarted(const datetime date)    { this.SetProperty(SIGNAL_MQL5_PROP_DATE_STARTED,date);              }
   void              SetDateUpdated(const datetime date)    { this.SetProperty(SIGNAL_MQL5_PROP_DATE_UPDATED,date);              }
   void              SetID(const long id)                   { this.SetProperty(SIGNAL_MQL5_PROP_ID,id);                          }
   void              SetLeverage(const long value)          { this.SetProperty(SIGNAL_MQL5_PROP_LEVERAGE,value);                 }
   void              SetPips(const long value)              { this.SetProperty(SIGNAL_MQL5_PROP_PIPS,value);                     }
   void              SetRating(const long value)            { this.SetProperty(SIGNAL_MQL5_PROP_RATING,value);                   }
   void              SetSubscribers(const long value)       { this.SetProperty(SIGNAL_MQL5_PROP_SUBSCRIBERS,value);              }
   void              SetTrades(const long value)            { this.SetProperty(SIGNAL_MQL5_PROP_TRADES,value);                   }
   void              SetTradeMode(const long mode)          { this.SetProperty(SIGNAL_MQL5_PROP_TRADE_MODE,mode);                }
   void              SetSubscriptionStatus(const bool flag) { this.SetProperty(SIGNAL_MQL5_PROP_SUBSCRIPTION_STATUS,flag);       }
//--- Устанавливает (1) баланс счёта, (2) средства на счёте, (3) прирост счёта в процентах,
//--- (4) максимальную просадку, (5) цену подписки, (6) значение ROI (Return on Investment) сигнала в %
   void              SetBalance(const double value)         { this.SetProperty(SIGNAL_MQL5_PROP_BALANCE,value);                  }
   void              SetEquity(const double value)          { this.SetProperty(SIGNAL_MQL5_PROP_EQUITY,value);                   }
   void              SetGain(const double value)            { this.SetProperty(SIGNAL_MQL5_PROP_GAIN,value);                     }
   void              SetMaxDrawdown(const double value)     { this.SetProperty(SIGNAL_MQL5_PROP_MAX_DRAWDOWN,value);             }
   void              SetPrice(const double value)           { this.SetProperty(SIGNAL_MQL5_PROP_PRICE,value);                    }
   void              SetROI(const double value)             { this.SetProperty(SIGNAL_MQL5_PROP_ROI,value);                      }
//--- Устанавливает (1) логин автора сигнала, (2) наименование брокера (компании),
//--- (3) сервер брокера, (4) имя сигнала, (5) валюту счёта сигнала
   void              SetAuthorLogin(const string value)     { this.SetProperty(SIGNAL_MQL5_PROP_AUTHOR_LOGIN,value);             }
   void              SetBroker(const string value)          { this.SetProperty(SIGNAL_MQL5_PROP_BROKER,value);                   }
   void              SetBrokerServer(const string value)    { this.SetProperty(SIGNAL_MQL5_PROP_BROKER_SERVER,value);            }
   void              SetName(const string value)            { this.SetProperty(SIGNAL_MQL5_PROP_NAME,value);                     }
   void              SetCurrency(const string value)        { this.SetProperty(SIGNAL_MQL5_PROP_CURRENCY,value);                 }

//--- Возвращает наименование типа счёта
   string            TradeModeDescription(void);
   
  };
//+------------------------------------------------------------------+

В приватной секции объявлены массивы для хранения целочисленных, вещественных и строковых свойств объекта и методы, возвращающие реальный индекс вещественного и строкового свойств объекта.

В публичной секции класса видим стандартные для объектов библиотеки методы установки и получения свойств объекта, методы для поиска и сортировки, вывода описаний объекта и конструкторы класса.

Также в публичной секции написаны методы для упрощённого доступа к свойствам объекта — методы просто имеют "говорящие" названия — чтобы пользователю библиотеки не помнить наизусть имена всех перечислений объекта для обращения к тому или иному свойству.

Подробно состав классов объектов рассматривался нами в первой части описания библиотеки. Рассмотрим реализацию методов класса.

В параметрический конструктор класса передаётся идентификатор сигнала, а далее (учитывая, что этот сигнал является выбранным) заполняются все свойства объекта значениями, полученными из соответствующих функций:

//+------------------------------------------------------------------+
//| Параметрический конструктор                                      |
//+------------------------------------------------------------------+
CMQLSignal::CMQLSignal(const long signal_id)
  {
   this.m_long_prop[SIGNAL_MQL5_PROP_ID]                             = signal_id;
   this.m_long_prop[SIGNAL_MQL5_PROP_SUBSCRIPTION_STATUS]            = false;
   this.m_long_prop[SIGNAL_MQL5_PROP_TRADE_MODE]                     = ::SignalBaseGetInteger(SIGNAL_BASE_TRADE_MODE);
   this.m_long_prop[SIGNAL_MQL5_PROP_DATE_PUBLISHED]                 = ::SignalBaseGetInteger(SIGNAL_BASE_DATE_PUBLISHED);
   this.m_long_prop[SIGNAL_MQL5_PROP_DATE_STARTED]                   = ::SignalBaseGetInteger(SIGNAL_BASE_DATE_STARTED);
   this.m_long_prop[SIGNAL_MQL5_PROP_DATE_UPDATED]                   = ::SignalBaseGetInteger(SIGNAL_BASE_DATE_UPDATED);
   this.m_long_prop[SIGNAL_MQL5_PROP_LEVERAGE]                       = ::SignalBaseGetInteger(SIGNAL_BASE_LEVERAGE);
   this.m_long_prop[SIGNAL_MQL5_PROP_PIPS]                           = ::SignalBaseGetInteger(SIGNAL_BASE_PIPS);
   this.m_long_prop[SIGNAL_MQL5_PROP_RATING]                         = ::SignalBaseGetInteger(SIGNAL_BASE_RATING);
   this.m_long_prop[SIGNAL_MQL5_PROP_SUBSCRIBERS]                    = ::SignalBaseGetInteger(SIGNAL_BASE_SUBSCRIBERS);
   this.m_long_prop[SIGNAL_MQL5_PROP_TRADES]                         = ::SignalBaseGetInteger(SIGNAL_BASE_TRADES);
   
   this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_BALANCE)]      = ::SignalBaseGetDouble(SIGNAL_BASE_BALANCE);
   this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_EQUITY)]       = ::SignalBaseGetDouble(SIGNAL_BASE_EQUITY);
   this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_GAIN)]         = ::SignalBaseGetDouble(SIGNAL_BASE_GAIN);
   this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_MAX_DRAWDOWN)] = ::SignalBaseGetDouble(SIGNAL_BASE_MAX_DRAWDOWN);
   this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_PRICE)]        = ::SignalBaseGetDouble(SIGNAL_BASE_PRICE);
   this.m_double_prop[this.IndexProp(SIGNAL_MQL5_PROP_ROI)]          = ::SignalBaseGetDouble(SIGNAL_BASE_ROI);
   
   this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_AUTHOR_LOGIN)] = ::SignalBaseGetString(SIGNAL_BASE_AUTHOR_LOGIN);
   this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_BROKER)]       = ::SignalBaseGetString(SIGNAL_BASE_BROKER);
   this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_BROKER_SERVER)]= ::SignalBaseGetString(SIGNAL_BASE_BROKER_SERVER);
   this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_NAME)]         = ::SignalBaseGetString(SIGNAL_BASE_NAME);
   this.m_string_prop[this.IndexProp(SIGNAL_MQL5_PROP_CURRENCY)]     = ::SignalBaseGetString(SIGNAL_BASE_CURRENCY);
  }
//+------------------------------------------------------------------+

Флаг подписки на этот сигнал ставим в значение false — подписка на выбранный сигнал будет осуществляться из другого класса.

Метод для сравнения объектов-mql5-сигналов между собой по указанному свойству:

//+------------------------------------------------------------------+
//| Сравнивает объекты CMQLSignal между собой по указанному свойству |
//+------------------------------------------------------------------+
int CMQLSignal::Compare(const CObject *node,const int mode=0) const
  {
   const CMQLSignal *obj_compared=node;
//--- сравнение целочисленных свойств двух объектов
   if(mode<SIGNAL_MQL5_PROP_INTEGER_TOTAL)
     {
      long value_compared=obj_compared.GetProperty((ENUM_SIGNAL_MQL5_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_SIGNAL_MQL5_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- сравнение вещественных свойств двух объектов
   else if(mode<SIGNAL_MQL5_PROP_DOUBLE_TOTAL+SIGNAL_MQL5_PROP_INTEGER_TOTAL)
     {
      double value_compared=obj_compared.GetProperty((ENUM_SIGNAL_MQL5_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_SIGNAL_MQL5_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- сравнение строковых свойств двух объектов
   else if(mode<SIGNAL_MQL5_PROP_DOUBLE_TOTAL+SIGNAL_MQL5_PROP_INTEGER_TOTAL+SIGNAL_MQL5_PROP_STRING_TOTAL)
     {
      string value_compared=obj_compared.GetProperty((ENUM_SIGNAL_MQL5_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_SIGNAL_MQL5_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

Вкратце: в метод передаётся объект для сравнения и свойство, по которому необходимо сравнить два объекта. В зависимости от переданного свойства происходит сравнение целочисленного, вещественного или строкового свойства двух объектов. И возвращается результат сравнения -1, 1 или 0 (меньше, больше, равно)

Метод для сравнения объектов-mql5-сигналов между собой по всем свойствам:

//+------------------------------------------------------------------+
//| Сравнивает объекты CMQLSignal между собой по всем свойствам      |
//+------------------------------------------------------------------+
bool CMQLSignal::IsEqual(CMQLSignal *compared_obj) const
  {
   int beg=0, end=SIGNAL_MQL5_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_SIGNAL_MQL5_PROP_INTEGER prop=(ENUM_SIGNAL_MQL5_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=SIGNAL_MQL5_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_SIGNAL_MQL5_PROP_DOUBLE prop=(ENUM_SIGNAL_MQL5_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=SIGNAL_MQL5_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_SIGNAL_MQL5_PROP_STRING prop=(ENUM_SIGNAL_MQL5_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+

В трёх циклах по трём массивам свойств объекта сравниваем каждое последующее свойство двух объектов и, если свойства двух объектов не равны — возвращаем false. После сравнения всех свойств двух объектов в трёх массивах, и если не было найдено неравных свойств, возвращаем true — объекты полностью идентичны между собой.

Методы, возвращающие описание указанного целочисленного, вещественного и строкового свойства объекта:

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства объекта              |
//+------------------------------------------------------------------+
string CMQLSignal::GetPropertyDescription(ENUM_SIGNAL_MQL5_PROP_INTEGER property)
  {
   return
     (
      property==SIGNAL_MQL5_PROP_TRADE_MODE     ?  CMessage::Text(MSG_SIGNAL_MQL5_TRADE_MODE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.TradeModeDescription()
         )  :
      property==SIGNAL_MQL5_PROP_DATE_PUBLISHED ?  CMessage::Text(MSG_SIGNAL_MQL5_DATE_PUBLISHED)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::TimeToString(this.DatePublished())
         )  :
      property==SIGNAL_MQL5_PROP_DATE_STARTED   ?  CMessage::Text(MSG_SIGNAL_MQL5_DATE_STARTED)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::TimeToString(this.DateStarted())
         )  :
      property==SIGNAL_MQL5_PROP_DATE_UPDATED   ?  CMessage::Text(MSG_SIGNAL_MQL5_DATE_UPDATED)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::TimeToString(this.DateUpdated())
         )  :
      property==SIGNAL_MQL5_PROP_ID             ?  CMessage::Text(MSG_SIGNAL_MQL5_ID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==SIGNAL_MQL5_PROP_LEVERAGE       ?  CMessage::Text(MSG_SIGNAL_MQL5_LEVERAGE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==SIGNAL_MQL5_PROP_PIPS           ?  CMessage::Text(MSG_SIGNAL_MQL5_PIPS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==SIGNAL_MQL5_PROP_RATING         ?  CMessage::Text(MSG_SIGNAL_MQL5_RATING)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==SIGNAL_MQL5_PROP_SUBSCRIBERS    ?  CMessage::Text(MSG_SIGNAL_MQL5_SUBSCRIBERS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==SIGNAL_MQL5_PROP_TRADES        ?  CMessage::Text(MSG_SIGNAL_MQL5_TRADES)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==SIGNAL_MQL5_PROP_SUBSCRIPTION_STATUS  ?  CMessage::Text(MSG_SIGNAL_MQL5_SUBSCRIPTION_STATUS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Возвращает описание вещественного свойства объекта               |
//+------------------------------------------------------------------+
string CMQLSignal::GetPropertyDescription(ENUM_SIGNAL_MQL5_PROP_DOUBLE property)
  {
   return
     (
      property==SIGNAL_MQL5_PROP_BALANCE         ?  CMessage::Text(MSG_ACC_PROP_BALANCE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==SIGNAL_MQL5_PROP_EQUITY   ?  CMessage::Text(MSG_SIGNAL_MQL5_EQUITY)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
         
      property==SIGNAL_MQL5_PROP_GAIN   ?  CMessage::Text(MSG_SIGNAL_MQL5_GAIN)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==SIGNAL_MQL5_PROP_MAX_DRAWDOWN   ?  CMessage::Text(MSG_SIGNAL_MQL5_MAX_DRAWDOWN)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==SIGNAL_MQL5_PROP_PRICE   ?  CMessage::Text(MSG_SIGNAL_MQL5_PRICE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==SIGNAL_MQL5_PROP_ROI   ?  CMessage::Text(MSG_SIGNAL_MQL5_ROI)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Возвращает описание строкового свойства объекта                  |
//+------------------------------------------------------------------+
string CMQLSignal::GetPropertyDescription(ENUM_SIGNAL_MQL5_PROP_STRING property)
  {
   return
     (
      property==SIGNAL_MQL5_PROP_AUTHOR_LOGIN   ?  CMessage::Text(MSG_SIGNAL_MQL5_AUTHOR_LOGIN)+": \""+this.GetProperty(property)+"\""    :
      property==SIGNAL_MQL5_PROP_BROKER         ?  CMessage::Text(MSG_SIGNAL_MQL5_BROKER)+": \""+this.GetProperty(property)+"\""          :
      property==SIGNAL_MQL5_PROP_BROKER_SERVER  ?  CMessage::Text(MSG_SIGNAL_MQL5_BROKER_SERVER)+": \""+this.GetProperty(property)+"\""   :
      property==SIGNAL_MQL5_PROP_NAME           ?  CMessage::Text(MSG_SIGNAL_MQL5_NAME)+": \""+this.GetProperty(property)+"\""            :
      property==SIGNAL_MQL5_PROP_CURRENCY       ?  CMessage::Text(MSG_SIGNAL_MQL5_CURRENCY)+": \""+this.GetProperty(property)+"\""        :
      ""
     );
  }
//+------------------------------------------------------------------+

В зависимости от переданного в метод свойства, создаётся и возвращается строка с его описанием и значением.

Метод, выводящий в журнал все свойства объекта-mql5-сигнала:

//+------------------------------------------------------------------+
//| Выводит в журнал свойства объекта                                |
//+------------------------------------------------------------------+
void CMQLSignal::Print(const bool full_prop=false)
  {
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") =============");
   int beg=0, end=SIGNAL_MQL5_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_SIGNAL_MQL5_PROP_INTEGER prop=(ENUM_SIGNAL_MQL5_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=SIGNAL_MQL5_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_SIGNAL_MQL5_PROP_DOUBLE prop=(ENUM_SIGNAL_MQL5_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=SIGNAL_MQL5_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_SIGNAL_MQL5_PROP_STRING prop=(ENUM_SIGNAL_MQL5_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n");
  }
//+------------------------------------------------------------------+

В трёх циклах по целочисленным, вещественным и строковым свойствам выводим описание каждого последующего свойства объекта при помощи соответствующего метода GetPropertyDescription().

Метод, возвращающий краткое наименование объекта-mql5-сигнала:

//+------------------------------------------------------------------+
//| Возвращает краткое наименование объекта                          |
//+------------------------------------------------------------------+
string CMQLSignal::Header(const bool shrt=false)
  {
   return(shrt ? CMessage::Text(MSG_SIGNAL_MQL5_TEXT_SIGNAL) : CMessage::Text(MSG_SIGNAL_MQL5_TEXT_SIGNAL_MQL5));
  }
//+------------------------------------------------------------------+

В метод передаётся флаг, указывающий на вывод либо короткого, либо длинного описания. По умолчанию выводится длинное описание:

Сигнал сервиса сигналов MQL5.com

При значении флага true, выводится короткое описание:

Сигнал

Метод, выводящий в журнал краткое описание объекта:

//+------------------------------------------------------------------+
//| Выводит в журнал краткое описание объекта                        |
//+------------------------------------------------------------------+
void CMQLSignal::PrintShort(void)
  {
   ::Print
     (
      this.Header(true),
      " \"",this.Name(),"\". ",
      CMessage::Text(MSG_SIGNAL_MQL5_AUTHOR_LOGIN),": ",this.AuthorLogin(),
      ", ID ",this.ID(),
      ", ",CMessage::Text(MSG_SIGNAL_MQL5_TEXT_GAIN),": ",::DoubleToString(this.Gain(),2),
      ", ",CMessage::Text(MSG_SIGNAL_MQL5_TEXT_DRAWDOWN),": ",::DoubleToString(this.MaxDrawdown(),2),
      ", ",CMessage::Text(MSG_LIB_TEXT_REQUEST_PRICE),": ",::DoubleToString(this.Price(),2)
     );
  }
//+------------------------------------------------------------------+

Сначала создаётся короткий заголовок "Сигнал", и далее к нему добавляются некоторые параметры, которые распечатываются в журнал терминала.
Например так:

Сигнал "DemoForArticle". Логин автора: login, ID XXXXXX, Прирост: XX.XX, Просадка: XX.XX, Цена: XX.XX

Метод, возвращающий наименование типа счёта:

//+------------------------------------------------------------------+
//| Возвращает наименование типа счёта                               |
//+------------------------------------------------------------------+
string CMQLSignal::TradeModeDescription(void)
  {
   return
     (
      this.TradeMode()==0 ? CMessage::Text(MSG_ACC_TRADE_MODE_REAL)     :
      this.TradeMode()==1 ? CMessage::Text(MSG_ACC_TRADE_MODE_DEMO)     :
      this.TradeMode()==2 ? CMessage::Text(MSG_ACC_TRADE_MODE_CONTEST)  :
      CMessage::Text(MSG_ACC_TRADE_MODE_UNKNOWN)
     );
  }
//+------------------------------------------------------------------+

В зависимости от счёта, с которого транслируется сигнал, возвращается строка с типом счёта (реальный, демо или конкурсный)

На этом создание объекта-mql5-сигнала завершено.


Тестирование

Для тестирования возьмём советник из прошлой статьи и сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part65\ под новым именем TestDoEasyPart65.mq5.

Класс-коллекция серий снимков стаканов цен теперь доступны из главного объекта библиотеки, а вот объект-mql5-сигнал мы пока не подключали к CEngine. Поэтому в советнике заменим строку подключения класса объекта-серии снимков стакана цен

#include <DoEasy\Objects\Book\MBookSeries.mqh>

на строку подключения объекта-mql5-сигнала:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart65.mq5 |
//|                        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"
//--- includes
#include <DoEasy\Engine.mqh>
#include <DoEasy\Objects\MQLSignalBase\MQLSignal.mqh>
//--- enums

Из списка глобальных переменных советника удалим объявление объекта-серии снимка стаканов цен:

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

В справке по MQL5 есть пример получения списка прибыльных бесплатных сигналов с ненулевым количеством подписчиков:

void OnStart()
  {
//--- запрашиваем общее количество сигналов в базе
   int total=SignalBaseTotal();
//--- цикл по всем сигналам
   for(int i=0;i<total;i++)
     {
      //--- выбираем сигнал для дальнейшей работы
      if(SignalBaseSelect(i))
        {
         //--- получение свойств сигнала
         long   id    =SignalBaseGetInteger(SIGNAL_BASE_ID);          // id сигнала
         long   pips  =SignalBaseGetInteger(SIGNAL_BASE_PIPS);        // результат торговли в пипсах
         long   subscr=SignalBaseGetInteger(SIGNAL_BASE_SUBSCRIBERS); // количество подписчиков
         string name  =SignalBaseGetString(SIGNAL_BASE_NAME);         // имя сигнала
         double price =SignalBaseGetDouble(SIGNAL_BASE_PRICE);        // цена подписки на сигнал
         string curr  =SignalBaseGetString(SIGNAL_BASE_CURRENCY);     // валюта сигнала
         //--- выводим все прибыльные бесплатные сигналы с ненулевым количеством подписчиков
         if(price==0.0 && pips>0 && subscr>0)
            PrintFormat("id=%d, name=\"%s\", currency=%s, pips=%d, subscribers=%d",id,name,curr,pips,subscr);
        }
      else PrintFormat("Ошибка выбора сигнала. Код ошибки=%d",GetLastError());
     }
  }

Сделаем то же самое, но при помощи нового объекта-mql5-сигнала. Для этого в обработчике OnInit() советника напишем такой блок кода:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Вызов данной функции выводит в журнал список констант перечисления, 
//--- заданного в файле DELib.mqh в строках 22 и 25, для проверки корректности констант
   //EnumNumbersTest();

//--- Установка глобальных переменных советника
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   testing=engine.IsTester();
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;
   stoploss_to_modify=InpStopLossModify;
   takeprofit_to_modify=InpTakeProfitModify;
   distance_pending_request=(InpDistancePReq<5 ? 5 : InpDistancePReq);
   bars_delay_pending_request=(InpBarsDelayPReq<1 ? 1 : InpBarsDelayPReq);
   g_point=SymbolInfoDouble(NULL,SYMBOL_POINT);
   g_digits=(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS);
//--- Инициализация случайных номеров групп
   group1=0;
   group2=0;
   srand(GetTickCount());
   
//--- Инициализация библиотеки DoEasy
   OnInitDoEasy();
   
//--- Проверка и удаление неудалённых графических объектов советника
   if(IsPresentObectByPrefix(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Создание панели кнопок
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_FAILED;
//--- Установка состояния кнопки активизации трейлингов
   ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);
//--- Сброс состояний кнопок активизации работы отложенными запросами
   for(int i=0;i<14;i++)
     {
      ButtonState(butt_data[i].name+"_PRICE",false);
      ButtonState(butt_data[i].name+"_TIME",false);
     }

//--- Проверка воспроизведения стандартного звука по макроподстановке и пользовательского звука по описанию
   engine.PlaySoundByDescription(SND_OK);
//--- Ждём 600 миллисекунд
   engine.Pause(600);
   engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","The sound of a falling coin 2"));

   CArrayObj *list=new CArrayObj();
   if(list!=NULL)
     {
      //--- запрашиваем общее количество сигналов в базе 
      int total=SignalBaseTotal(); 
      //--- цикл по всем сигналам 
      for(int i=0;i<total;i++) 
        { 
         //--- выбираем сигнал для дальнейшей работы 
         if(!SignalBaseSelect(i))
            continue;
         long id=SignalBaseGetInteger(SIGNAL_BASE_ID);
         CMQLSignal *signal=new CMQLSignal(id);
         if(signal==NULL)
            continue;
         if(!list.Add(signal))
           {
            delete signal;
            continue;
           }
        } 
      //--- выводим все прибыльные бесплатные сигналы с ненулевым количеством подписчиков
      Print("");
      static bool done=false;
      for(int i=0;i<list.Total();i++)
        {
         CMQLSignal *signal=list.At(i);
         if(signal==NULL)
            continue;
         if(signal.Price()>0 || signal.Subscribers()==0)
            continue;
         //--- Самый первый подходящий сигнал распечатываем полностью в журнале
         if(!done)
           {
            signal.Print();
            done=true;
           }
         //--- Для остальных выводим краткие описания
         else
            signal.PrintShort();
        }
      delete list;
     }
     
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

На первый взгляд код получился объёмнее, чем код из справки. Но это временное явление... Как только будет готов класс-коллекция сигналов, всё будет куда более лаконично. А пока мы этим кодом проверим корректность работы объекта mql5-сигнала.

Так как теперь у нас есть доступ к классу-коллекции серий снимков стаканов цен из главного объекта библиотеки, то обработчик OnBookEvent() претерпел некоторые изменения. Теперь он выглядит так:

//+------------------------------------------------------------------+
//| OnBookEvent function                                             |
//+------------------------------------------------------------------+
void OnBookEvent(const string& symbol)
  {
   static bool first=true;
   //--- Если не удалось обновить серию снимков символа - уходим
   if(!engine.OnBookEvent(symbol))
      return;
   //--- Работаем по текущему символу
   if(symbol==Symbol())
     {
      //--- Получаем серию снимков стакана цен текущего символа
      CMBookSeries *book_series=engine.GetMBookSeries(symbol);
      if(book_series==NULL)
         return;
      //--- Получаем последний объект-снимок стакана цен из объекта-серии снимков стакана цен
      CMBookSnapshot *book=book_series.GetLastMBook();
      if(book==NULL)
         return;
      //--- Получаем самый первый и самый последний объекты-заявки стакана цен из объекта-снимка стакана цен
      CMarketBookOrd *ord_0=book.GetMBookByListIndex(0);
      CMarketBookOrd *ord_N=book.GetMBookByListIndex(book.DataTotal()-1);
      if(ord_0==NULL || ord_N==NULL) return;
      //--- Выводим на график в комментарии время текущего снимка стакана цен,
      //--- максимальное количество показываемых заявок в стакане для символа,
      //--- полученное количество заявок в текущем снимке стакана цен,
      //--- общее количество снимков стакана цен, записанных в список-серию и
      //--- максимальную и минимальную по цене заявки текущего снимка стакана цен
      Comment
        (
         DFUN,book.Symbol(),": ",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()
        );
      //--- Выведем в журнал первый снимок стакана цен
      if(first)
        {
         //--- описание серии
         book_series.Print();
         //--- описание снимка
         book.Print();
         first=false;
        }
     }
  }
//+------------------------------------------------------------------+

В функции OnInitDoEasy() также внесём небольшие правки. Теперь создание тиковых серий всех используемых символов сделано прямым обращением к соответствующему методу главного объекта библиотеки и добавлена проверка созданных серий стаканов цен:

//--- Проверка созданных таймсерий - выводим в журнал описания всех созданных таймсерий
//--- (true - только созданные, false - созданные и объявленные)
   engine.GetTimeSeriesCollection().PrintShort(false); // Краткие описания

//--- Создание тиковых серий всех используемых символов
   engine.TickSeriesCreateAll();

//--- Проверка созданных тиковых серий - выводим в журнал описания всех созданных тиковых серий
   engine.GetTickSeriesCollection().Print();

//--- Проверка созданных серий стаканов цен - выводим в журнал описания всех созданных серий стаканов цен
   engine.GetMBookSeriesCollection().Print();

Скомпилируем советник и запустим его, предварительно задав в настройках использовать два символа и текущий период графика:


В журнал будут выведены данные о созданной коллекции снимков стаканов цен, полные данные о самом первом подходящем сигнале и краткие данные о всех прибыльных бесплатных сигналах. В конце списка будет выведена информация о серии снимков и по первому полученному снимку стакана цен текущего графика:

Счёт 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10428.13 USD, 1:100, Hedge, Демонстрационный счёт MetaTrader 5
--- Инициализация библиотеки "DoEasy" ---
Работа с предопределённым списком символов. Количество используемых символов: 2
"AUDUSD" "EURUSD"
Работа только с текущим таймфреймом: H1
Таймсерия символа AUDUSD: 
- Таймсерия "AUDUSD" H1: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 5385
Таймсерия символа EURUSD: 
- Таймсерия "EURUSD" H1: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 6310
Тиковая серия "AUDUSD": Запрошенное количество дней: 1, Создано исторических данных: 294221
Тиковая серия "EURUSD": Запрошенное количество дней: 1, Создано исторических данных: 216048
Коллекция серий снимков стакана цен:
- Серия снимков стакана цен "AUDUSD": Запрошенное количество дней: 1, Фактическая глубина истории: 0
- Серия снимков стакана цен "EURUSD": Запрошенное количество дней: 1, Фактическая глубина истории: 0
Осуществлена подписка на стакан цен  AUDUSD
Осуществлена подписка на стакан цен  EURUSD
Время инициализации библиотеки: 00:00:25.015
 
============= Начало списка параметров (Сигнал сервиса сигналов MQL5.com) =============
Тип счета: Демонстрационный счёт
Дата публикации: 2018.06.04 16:45
Дата начала мониторинга: 2018.06.04 16:45
Дата последнего обновления торговой статистики: 2021.02.20 02:01
ID: 435626
Плечо торгового счета: 200
Результат торговли в пипсах: -72124
Позиция в рейтинге сигналов: 12
Количество подписчиков: 132
Количество трейдов: 7866
Состояние подписки счёта на этот сигнал: Нет
------
Баланс счета: 43144.27
Средства на счете: 43150.55
Прирост счета в процентах: 331.44
Максимальная просадка: 43.93
Цена подписки на сигнал: 0.00
Значение ROI (Return on Investment) сигнала в %: 331.51
------
Логин автора: "robots4forex"
Наименование брокера (компании): "MetaQuotes Software Corp."
Сервер брокера: "MetaQuotes-Demo"
Имя: "Prospector Scalper EA"
Валюта счета: "GBP"
============= Конец списка параметров (Сигнал сервиса сигналов MQL5.com) =============
 
Сигнал "ADS MT5". Логин автора: vluxus, ID 478235, Прирост: 251.84, Просадка: 40.26, Цена: 0.00
Сигнал "Sparrow USD ForexClub c 01012019". Логин автора: Tradotrade, ID 519975, Прирост: 12.45, Просадка: 14.98, Цена: 0.00
Сигнал "RAZRED v03". Логин автора: joaoluiz_sa, ID 545382, Прирост: 96.17, Просадка: 28.18, Цена: 0.00
Сигнал "NRDemo". Логин автора: v3sare, ID 655353, Прирост: 2.94, Просадка: 25.37, Цена: 0.00
Сигнал "Amega 1000085182". Логин автора: AmegaTrust, ID 722001, Прирост: 1.92, Просадка: 5.13, Цена: 0.00
Сигнал "Wns My strategy". Логин автора: WaldeliN, ID 727851, Прирост: 103.69, Просадка: 47.01, Цена: 0.00
Сигнал "Bk EA". Логин автора: worknet, ID 749557, Прирост: 506.88, Просадка: 59.78, Цена: 0.00
Сигнал "ROBIN 24". Логин автора: juanca034, ID 752873, Прирост: 926.29, Просадка: 30.25, Цена: 0.00
Сигнал "Deny Forex". Логин автора: deny.mendonca, ID 759729, Прирост: 149.06, Просадка: 39.24, Цена: 0.00
Сигнал "T Strategy". Логин автора: tonarino210, ID 760343, Прирост: 28.87, Просадка: 20.37, Цена: 0.00
Сигнал "IC MT5 Demo". Логин автора: InvestForce, ID 760539, Прирост: 67.01, Просадка: 35.99, Цена: 0.00
Сигнал "Gridingale". Логин автора: Myxx, ID 766073, Прирост: 21.10, Просадка: 15.55, Цена: 0.00
Сигнал "Marcos Monteiro". Логин автора: slovenwill, ID 783988, Прирост: 85.08, Просадка: 17.59, Цена: 0.00
Сигнал "Multi currency trend". Логин автора: mj2019, ID 785447, Прирост: 54.42, Просадка: 18.52, Цена: 0.00
Сигнал "W7 901074879 Campeonato MT5". Логин автора: dramos236, ID 787269, Прирост: 91.99, Просадка: 21.20, Цена: 0.00
Сигнал "Ramon Fx". Логин автора: viniciusramon18, ID 788732, Прирост: 54.31, Просадка: 9.46, Цена: 0.00
Сигнал "Douglas demo w7". Логин автора: douglas.o.carne, ID 792392, Прирост: 219.94, Просадка: 43.61, Цена: 0.00
Сигнал "Suelen". Логин автора: suelenacca, ID 794655, Прирост: 67.40, Просадка: 20.97, Цена: 0.00
Сигнал "Conquers". Логин автора: borgesti, ID 795133, Прирост: 37.23, Просадка: 11.09, Цена: 0.00
Сигнал "Conta demo torneio". Логин автора: Tiagoximenes, ID 798798, Прирост: 42.36, Просадка: 17.94, Цена: 0.00
Сигнал "Conta demo de mil". Логин автора: Tiagoximenes, ID 798802, Прирост: 132.02, Просадка: 27.87, Цена: 0.00
Сигнал "The art of Forex". Логин автора: Myxx, ID 801685, Прирост: 170.29, Просадка: 40.95, Цена: 0.00
Сигнал "BB29 ICM". Логин автора: desmondpylow, ID 806971, Прирост: 2.28, Просадка: 41.60, Цена: 0.00
Сигнал "Prometheus". Логин автора: g0079, ID 808538, Прирост: 91.44, Просадка: 22.98, Цена: 0.00
Сигнал "Prueba robot 2 0 automatico". Логин автора: richwolfcompany, ID 809986, Прирост: 76.76, Просадка: 44.45, Цена: 0.00
Сигнал "Deep Takeover Hedge StressTest 5M Candle". Логин автора: johnnypasado, ID 811819, Прирост: 10.08, Просадка: 13.58, Цена: 0.00
Сигнал "Campeonato". Логин автора: AndreAutotecnic, ID 812233, Прирост: 87.47, Просадка: 13.79, Цена: 0.00
Сигнал "OPM PRO". Логин автора: herinata, ID 812856, Прирост: 38.55, Просадка: 32.35, Цена: 0.00
Сигнал "Slowly but surely 2". Логин автора: gyurmanz, ID 815467, Прирост: 53.73, Просадка: 13.08, Цена: 0.00
Сигнал "Beef Waves". Логин автора: vladimir0005, ID 819055, Прирост: 50.46, Просадка: 32.69, Цена: 0.00
Сигнал "Adriano Garcia". Логин автора: agarcia_ag, ID 823082, Прирост: 111.62, Просадка: 36.00, Цена: 0.00
Сигнал "Max ScalperSpeed MT5". Логин автора: paran1615, ID 824333, Прирост: 74.51, Просадка: 40.62, Цена: 0.00
Сигнал "SyH". Логин автора: gtrader2017, ID 826520, Прирост: 42.78, Просадка: 36.81, Цена: 0.00
Сигнал "ECmp5s free". Логин автора: VallaLorenzo, ID 830456, Прирост: 146.90, Просадка: 27.64, Цена: 0.00
Сигнал "MaxScalperSpeed MT5". Логин автора: paran1615, ID 835890, Прирост: 64.33, Просадка: 35.14, Цена: 0.00
Сигнал "YEARNSIGNALS". Логин автора: yearnsignal2k19, ID 837512, Прирост: 11.10, Просадка: 2.54, Цена: 0.00
Сигнал "AGS test 2". Логин автора: alireza.akbari, ID 838427, Прирост: 7.93, Просадка: 10.89, Цена: 0.00
Сигнал "Waldeli003". Логин автора: WaldeliN, ID 838605, Прирост: 32.98, Просадка: 5.54, Цена: 0.00
Сигнал "Michele". Логин автора: michele-m-r, ID 843351, Прирост: 49.27, Просадка: 13.90, Цена: 0.00
Сигнал "SNAILER". Логин автора: 8F117EE2, ID 843458, Прирост: 83.65, Просадка: 11.86, Цена: 0.00
Сигнал "Juniornicks". Логин автора: juniornicks, ID 845611, Прирост: 100.25, Просадка: 43.93, Цена: 0.00
Сигнал "Black Hunter". Логин автора: christianlara, ID 845761, Прирост: 51.94, Просадка: 24.44, Цена: 0.00
Сигнал "Master dizicheh1". Логин автора: awdtghuoilp, ID 857594, Прирост: 5.04, Просадка: 37.93, Цена: 0.00
Сигнал "EUROS". Логин автора: Marketsystem, ID 858449, Прирост: 5.31, Просадка: 2.94, Цена: 0.00
Сигнал "Scalpers risk10 pairs7 leverage100". Логин автора: leron34, ID 861750, Прирост: 27.98, Просадка: 20.53, Цена: 0.00
Сигнал "EUREKA". Логин автора: Edmed933, ID 861927, Прирост: 59.89, Просадка: 7.32, Цена: 0.00
Сигнал "Nadando Com Tubaroes". Логин автора: jun152, ID 862191, Прирост: 21.18, Просадка: 5.45, Цена: 0.00
Сигнал "Demo using a grid system". Логин автора: RyanAfriansyah, ID 865900, Прирост: 20.56, Просадка: 8.38, Цена: 0.00
Сигнал "Pilares". Логин автора: ValterCezar, ID 866672, Прирост: 29.87, Просадка: 18.96, Цена: 0.00
Сигнал "EUROUSD". Логин автора: fxtrader036, ID 866719, Прирост: 303.28, Просадка: 40.70, Цена: 0.00
Сигнал "LanzPower 25 S". Логин автора: sirlanz, ID 868027, Прирост: 36.64, Просадка: 45.53, Цена: 0.00
Сигнал "Amadeu Volpato Desafio Internacional". Логин автора: Amadeu1971, ID 868928, Прирост: 19.79, Просадка: 12.57, Цена: 0.00
Сигнал "Fernando correia W7". Логин автора: nandooo_123-hotmail, ID 870169, Прирост: 41.70, Просадка: 25.16, Цена: 0.00
Сигнал "MAK GO". Логин автора: 9489631, ID 870413, Прирост: 469.22, Просадка: 36.31, Цена: 0.00
Сигнал "Adriano Garcia W7bt 4 Pilares". Логин автора: agarcia_ag, ID 871868, Прирост: 42.84, Просадка: 13.19, Цена: 0.00
Сигнал "Albertofxsemstop". Логин автора: albertosuga, ID 871969, Прирост: 27.84, Просадка: 19.36, Цена: 0.00
Сигнал "BetoSTCDemo". Логин автора: betoabcsp, ID 872141, Прирост: 29.03, Просадка: 18.07, Цена: 0.00
Сигнал "DESAFIOSEMSTOPLOSSCDS". Логин автора: cdsantos42, ID 873575, Прирост: 19.47, Просадка: 13.24, Цена: 0.00
Сигнал "MrGeek7421". Логин автора: KamranAhmadi, ID 873583, Прирост: 86.74, Просадка: 16.33, Цена: 0.00
Сигнал "Douglastorneio2w7". Логин автора: douglas.o.carne, ID 876302, Прирост: 18.13, Просадка: 15.34, Цена: 0.00
Сигнал "Douglasw7demo1". Логин автора: douglas.o.carne, ID 876303, Прирост: 148.80, Просадка: 26.47, Цена: 0.00
Сигнал "Douglastorneio1w7". Логин автора: douglas.o.carne, ID 876932, Прирост: 136.86, Просадка: 41.86, Цена: 0.00
Сигнал "Campeonato mundial sem stop". Логин автора: Lpontes835, ID 878082, Прирост: 23.52, Просадка: 14.93, Цена: 0.00
Сигнал "ALPHA IA v3". Логин автора: avaalpha, ID 878517, Прирост: 2.77, Просадка: 0.77, Цена: 0.00
Сигнал "Gold x10". Логин автора: DynamixFX, ID 878540, Прирост: 6.47, Просадка: 8.87, Цена: 0.00
Сигнал "MultiBolbandsRealM5". Логин автора: 11BREATH11, ID 879072, Прирост: 83.18, Просадка: 20.09, Цена: 0.00
Сигнал "Ticols Stable profit". Логин автора: ticols, ID 879609, Прирост: -56.37, Просадка: 68.68, Цена: 0.00
Сигнал "EA SkyBot MultiPares CENT". Логин автора: 4PerformanceFx, ID 882222, Прирост: 248.38, Просадка: 41.29, Цена: 0.00
Сигнал "Trader Unity M15 100 rec". Логин автора: crifalo, ID 882268, Прирост: 24.36, Просадка: 26.11, Цена: 0.00
Сигнал "Mad Piper Bill Millin". Логин автора: DiXOVERS, ID 882495, Прирост: 251.06, Просадка: 38.78, Цена: 0.00
Сигнал "ProfitGuy STAR M Demo". Логин автора: justbond, ID 882847, Прирост: 27.45, Просадка: 24.89, Цена: 0.00
Сигнал "EA GrayRock". Логин автора: serggray, ID 883235, Прирост: 49.42, Просадка: 28.68, Цена: 0.00
Сигнал "FX FLASH". Логин автора: tradedeal, ID 883322, Прирост: 9.17, Просадка: 2.88, Цена: 0.00
Сигнал "Optimizer". Логин автора: alama1, ID 884765, Прирост: 73.53, Просадка: 28.58, Цена: 0.00
Сигнал "NnaFX Demo 02". Логин автора: 12259468, ID 886070, Прирост: 136.64, Просадка: 30.54, Цена: 0.00
Сигнал "Phantom5000 DEMO". Логин автора: JosephSmith, ID 887046, Прирост: 43.41, Просадка: 17.73, Цена: 0.00
Сигнал "Art of Forex MadCat The G". Логин автора: The_G, ID 888018, Прирост: 215.67, Просадка: 40.86, Цена: 0.00
Сигнал "ICMarkets MT5 AK 05". Логин автора: A.Klimkovsky, ID 889370, Прирост: 13.03, Просадка: 8.55, Цена: 0.00
Сигнал "ProfitGuy STAR G Demo". Логин автора: justbond, ID 890551, Прирост: 58.84, Просадка: 58.80, Цена: 0.00
Сигнал "BetoSemStopDM". Логин автора: betoabcsp, ID 892251, Прирост: 94.96, Просадка: 6.30, Цена: 0.00
Сигнал "Smart Grid 26980 Demo". Логин автора: tm3912, ID 892313, Прирост: 86.30, Просадка: 37.19, Цена: 0.00
Сигнал "Rel Vigor PSar n Red Dragon n mad max". Логин автора: DynamixFX, ID 892523, Прирост: 73.74, Просадка: 38.55, Цена: 0.00
Сигнал "SolangeL". Логин автора: Mulherdeletras1, ID 894019, Прирост: 108.40, Просадка: 20.84, Цена: 0.00
Сигнал "Paulotbone 1". Логин автора: paulotbone, ID 894062, Прирост: 35.14, Просадка: 23.93, Цена: 0.00
Сигнал "GOLD MASTER". Логин автора: THEICD, ID 894983, Прирост: 218.90, Просадка: 22.80, Цена: 0.00
Сигнал "Fxfrance". Логин автора: fxfrance, ID 895838, Прирост: 369.48, Просадка: 41.48, Цена: 0.00
Сигнал "Marcos Paulo Serigatti". Логин автора: mpm4rcos, ID 895960, Прирост: 45.62, Просадка: 18.21, Цена: 0.00
Сигнал "Roma Forex Desafio 4 Pilares". Логин автора: Jhonesroma, ID 896016, Прирост: 29.60, Просадка: 43.45, Цена: 0.00
Сигнал "BOSBM OTC Test 5W". Логин автора: houli, ID 896563, Прирост: 144.65, Просадка: 22.94, Цена: 0.00
Сигнал "FSTickTrade". Логин автора: onlyforsignup, ID 897751, Прирост: 24.68, Просадка: 16.20, Цена: 0.00
Сигнал "Sun AI". Логин автора: Myxx, ID 899179, Прирост: 16.82, Просадка: 21.07, Цена: 0.00
Сигнал "Equinox Demo". Логин автора: Kratoner, ID 905773, Прирост: 44.46, Просадка: 20.55, Цена: 0.00
Сигнал "STRAGA Tornado VM1". Логин автора: stragapede, ID 906398, Прирост: 36.70, Просадка: 26.00, Цена: 0.00
Сигнал "DiegoT". Логин автора: DiegoTT, ID 910230, Прирост: 28.20, Просадка: 11.49, Цена: 0.00
Сигнал "Breaking Bad". Логин автора: Myxx, ID 911569, Прирост: 7.63, Просадка: 8.42, Цена: 0.00
Сигнал "TradesaovivoBr". Логин автора: dhyegorodrigo1988, ID 913924, Прирост: 16.77, Просадка: 5.60, Цена: 0.00
Сигнал "VantageFX Sunphone Dragon". Логин автора: sunphone, ID 916421, Прирост: 345.56, Просадка: 36.04, Цена: 0.00
Сигнал "BYQS121". Логин автора: kadirryildiz, ID 916600, Прирост: 32.85, Просадка: 4.18, Цена: 0.00
Сигнал "FBSLevelUP". Логин автора: manoj, ID 919106, Прирост: 15.29, Просадка: 10.49, Цена: 0.00
Сигнал "Omega". Логин автора: zyntherius74, ID 922043, Прирост: 70.18, Просадка: 25.42, Цена: 0.00
Сигнал "Best Bingo". Логин автора: kalnov-an, ID 926010, Прирост: 59.90, Просадка: 20.63, Цена: 0.00
Сигнал "LCF 1". Логин автора: yosuf, ID 931735, Прирост: 22.42, Просадка: 36.26, Цена: 0.00
Сигнал "Bao365". Логин автора: bao365, ID 933208, Прирост: 28.41, Просадка: 11.49, Цена: 0.00
Серия снимков стакана цен "EURUSD": Запрошенное количество дней: 1, Фактическая глубина истории: 1
Снимок стакана цен "EURUSD" (2021.02.24 22:22:54.654):
 - Заявка на продажу: 1.21576 [250.00]
 - Заявка на продажу: 1.21567 [100.00]
 - Заявка на продажу: 1.21566 [50.00]
 - Заявка на продажу: 1.21565 [36.00]
 - Заявка на покупку: 1.21563 [36.00]
 - Заявка на покупку: 1.21561 [50.00]
 - Заявка на покупку: 1.21559 [100.00]
 - Заявка на покупку: 1.21555 [250.00]


Что дальше

В следующей статье создадим коллекцию mql5-сигналов.

Ниже прикреплены все файлы текущей версии библиотеки и файл тестового советника для MQL5. Их можно скачать и протестировать всё самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.

К содержанию

*Статьи этой серии:

Работа с ценами в библиотеке DoEasy (Часть 62): Реалтайм-обновление тиковых серий, подготовка к работе со стаканом цен
Работа с ценами в библиотеке DoEasy (Часть 63): Стакан цен, класс абстрактной заявки стакана цен
Работа с ценами в библиотеке DoEasy (Часть 64): Стакан цен, классы объекта-снимка и объекта-серии снимков стакана цен

Прикрепленные файлы |
MQL5.zip (3943.87 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (4)
leonerd
leonerd | 2 мар 2021 в 11:35
А библиотека, я так понимаю, не кроссплатформенная?
BillionerClub
BillionerClub | 2 мар 2021 в 11:49
leonerd:
А библиотека, я так понимаю, не кроссплатформенная?

Закончилась на 38-40 статье, не хватило мощи у МТ4 чтобы быть на равных с МТ5

Artyom Trishkin
Artyom Trishkin | 2 мар 2021 в 15:28
leonerd:
А библиотека, я так понимаю, не кроссплатформенная?
К сожалению, политика MeteQuotes стала менее лояльной к четвёртой версии терминала, и статьи про четверку не принимаются. Так как многие вещи, сделанные под пятерку, нужно писать с нуля для переноса в четверку, то это занимает практически весь объём статьи, и она не принимается.
Поэтому было решено писать только для пятерки. Создание кроссплатформенности - через фриланс ко мне. К сожалению...
Artyom Trishkin
Artyom Trishkin | 2 мар 2021 в 15:29
BillionerClub:

Закончилась на 38-40 статье, не хватило мощи у МТ4 чтобы быть на равных с МТ5

Сделать можно. Но не двумя строчками кода :)
Нейросети — это просто (Часть 12): Dropout Нейросети — это просто (Часть 12): Dropout
Продвигаясь дальше в изучении нейронных сетей, наверное, стоит немного уделить внимания методам повышения их сходимости при обучении. Существует несколько таких методов. В этой статье предлагаю рассмотреть один из них — Dropout.
Машинное обучение в торговых системах на сетке и мартингейле. Есть ли рыба? Машинное обучение в торговых системах на сетке и мартингейле. Есть ли рыба?
Данная статья познакомит читателя с техникой машинного обучения для торговли сеткой и мартингейлом. К моему удивлению, такой подход по каким-то причинам совершенно не затронут в глобальной сети. Прочитав статью, вы сможете создавать своих собственных ботов.
Многослойный перцептрон и алгоритм обратного распространения ошибки Многослойный перцептрон и алгоритм обратного распространения ошибки
В последнее время, с ростом популярности этих двух методов появилось много библиотек на Matlab, R, Python, C ++ и т.д., которые получают на вход обучающий набор и автоматически создают соответствующую нейронную сеть для вашей задачи. Мы постараемся понять, как работает базовый тип нейронной сети — перцептрон с одним нейроном и многослойный перцептрон — замечательный алгоритм, который отвечает за обучение сети (градиентный спуск и обратное распространение). Эти сетевые модели будут основой для более сложных моделей, существующих на сегодняшний день.
Полезные и экзотические приемы для автоматической торговли Полезные и экзотические приемы для автоматической торговли
В данной статье я покажу несколько очень интересных и полезных приемов для автоматической торговли. Часть из этих приемов возможно кому-то знакома, кому-то — нет, но я постараюсь привести самые интересные методы и объяснить почему стоит ими пользоваться. Самое главное, покажу на практике, что они могут. Напишем советники и проверим все описанные приемы на истории котировок.