Библиотека для простого и быстрого создания программ для MetaTrader (Часть II): Коллекция исторических ордеров и сделок

5 марта 2019, 08:05
Artyom Trishkin
11
1 136

Содержание

В прошлой статье мы начали создавать большую кроссплатформенную библиотеку, целью которой является облегчение создания программ для платформ MetaTrader 5 и MetaTrader 4. Создали абстрактный объект COrder, который является базовым объектом для хранения данных исторических ордеров и сделок, а также рыночных ордеров и позиций.

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

Объекты исторических ордеров и сделок

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

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

В папке Objects библиотеки создайте новый класс CHistoryOrder.
Для этого правой кнопкой нажмите на папку Objects и выберите пункт меню "Новый файл Ctrl+N". В открывшемся Мастере MQL5 выберите "Новый класс" и нажмите кнопку "Далее". В поле с именем класса введите CHistoryOrder (1), а в поле указания базового класса введите название нашего абстрактного класса COrder (2) и нажмите "Готово".


После этого в папке Objects будет создан файл HistoryOrder.mqh (3). Откроем его:

//+------------------------------------------------------------------+
//|                                                 HistoryOrder.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CHistoryOrder : public COrder
  {
private:

public:
                     CHistoryOrder();
                    ~CHistoryOrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CHistoryOrder::CHistoryOrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CHistoryOrder::~CHistoryOrder()
  {
  }
//+------------------------------------------------------------------+

Пока это просто шаблон класса, попытка компиляции которого приводит к появлению уже знакомых нам пяти ошибок — новый класс, унаследованный от COrder, совсем ничего не знает о своём родителе. Добавим подключение файла Order.mqh:

//+------------------------------------------------------------------+
//|                                                 HistoryOrder.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Order.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CHistoryOrder : public COrder
  {
private:

public:
                     CHistoryOrder();
                    ~CHistoryOrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CHistoryOrder::CHistoryOrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CHistoryOrder::~CHistoryOrder()
  {
  }
//+------------------------------------------------------------------+

Теперь всё компилируется.

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

//+------------------------------------------------------------------+
//|                                                 HistoryOrder.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Order.mqh"
//+------------------------------------------------------------------+
//| Исторический маркет-ордер                                        |
//+------------------------------------------------------------------+
class CHistoryOrder : public COrder
  {
public:
   //--- Конструктор
                     CHistoryOrder(const ulong ticket) : COrder(ORDER_STATUS_HISTORY_ORDER,ticket) {}
   //--- Поддерживаемые целочисленные свойства ордера
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property);
  };
//+------------------------------------------------------------------+
//| Возвращает истину, если ордер поддерживает переданное свойство,  |
//| возвращает ложь в противном случае                               |
//+------------------------------------------------------------------+
bool CHistoryOrder::SupportProperty(ENUM_ORDER_PROP_INTEGER property)
  {
   if(property==ORDER_PROP_TIME_EXP       || 
      property==ORDER_PROP_DEAL_ENTRY     || 
      property==ORDER_PROP_TIME_UPDATE    || 
      property==ORDER_PROP_TIME_UPDATE_MSC
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

Итак, что имеем: в конструктор класса передаём тикет выбранного ордера, в защищённый конструктор родительского объекта COrder передаём статус ордера (исторический ордер) и его тикет.

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

В методе поддержания целочисленных свойств ордера проверяем свойство и, если это время экспирации, или направление сделки, или время изменения позиции, то возвращаем ложь — такие свойства не поддерживаются маркет-ордерами. Все остальные свойства поддерживаются и возвращается истина.

По аналогии создадим класс исторического (удалённого) отложенного ордера CHistoryPending и класс исторической сделки CHistoryDeal:

//+------------------------------------------------------------------+
//|                                               HistoryPending.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Order.mqh"
//+------------------------------------------------------------------+
//| Удалённый отложенный ордер                                       |
//+------------------------------------------------------------------+
class CHistoryPending : public COrder
  {
public:
   //--- Конструктор
                     CHistoryPending(const ulong ticket) : COrder(ORDER_STATUS_HISTORY_PENDING,ticket) {}
   //--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property);
  };
//+------------------------------------------------------------------+
//| Возвращает истину, если ордер поддерживает переданное свойство,  |
//| возвращает ложь в противном случае                               |
//+------------------------------------------------------------------+
bool CHistoryPending::SupportProperty(ENUM_ORDER_PROP_INTEGER property)
  {
   if(property==ORDER_PROP_PROFIT_PT         ||
      property==ORDER_PROP_DEAL_ORDER        ||
      property==ORDER_PROP_DEAL_ENTRY        ||
      property==ORDER_PROP_TIME_UPDATE       ||
      property==ORDER_PROP_TIME_UPDATE_MSC   ||
      property==ORDER_PROP_TICKET_FROM       ||
      property==ORDER_PROP_TICKET_TO         ||
      property==ORDER_PROP_CLOSE_BY_SL       ||
      property==ORDER_PROP_CLOSE_BY_TP
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Возвращает истину, если ордер поддерживает переданное свойство,  |
//| возвращает ложь в противном случае                               |
//+------------------------------------------------------------------+
bool CHistoryPending::SupportProperty(ENUM_ORDER_PROP_DOUBLE property)
  {
   if(property==ORDER_PROP_COMMISSION  ||
      property==ORDER_PROP_SWAP        ||
      property==ORDER_PROP_PROFIT      ||
      property==ORDER_PROP_PROFIT_FULL ||
      property==ORDER_PROP_PRICE_CLOSE
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//|                                                  HistoryDeal.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Order.mqh"
//+------------------------------------------------------------------+
//| Историческая сделка                                              |
//+------------------------------------------------------------------+
class CHistoryDeal : public COrder
  {
public:
   //--- Конструктор
                     CHistoryDeal(const ulong ticket) : COrder(ORDER_STATUS_DEAL,ticket) {}
   //--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property);
  };
//+------------------------------------------------------------------+
//| Возвращает истину, если ордер поддерживает переданное свойство,  |
//| возвращает ложь в противном случае                               |
//+------------------------------------------------------------------+
bool CHistoryDeal::SupportProperty(ENUM_ORDER_PROP_INTEGER property)
  {
   if(property==ORDER_PROP_TIME_EXP          || 
      property==ORDER_PROP_PROFIT_PT         ||
      property==ORDER_PROP_POSITION_BY_ID    ||
      property==ORDER_PROP_TIME_UPDATE       ||
      property==ORDER_PROP_TIME_UPDATE_MSC   ||
      property==ORDER_PROP_STATE             ||
      (
       this.OrderType()==DEAL_TYPE_BALANCE &&
       (
        property==ORDER_PROP_POSITION_ID     ||
        property==ORDER_PROP_POSITION_BY_ID  ||
        property==ORDER_PROP_TICKET_FROM     ||
        property==ORDER_PROP_TICKET_TO       ||
        property==ORDER_PROP_DEAL_ORDER      ||
        property==ORDER_PROP_MAGIC           ||
        property==ORDER_PROP_TIME_CLOSE      ||
        property==ORDER_PROP_TIME_CLOSE_MSC  ||
        property==ORDER_PROP_CLOSE_BY_SL     ||
        property==ORDER_PROP_CLOSE_BY_TP
       )
      )
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+
bool CHistoryDeal::SupportProperty(ENUM_ORDER_PROP_DOUBLE property)
  {
   if(property==ORDER_PROP_TP                || 
      property==ORDER_PROP_SL                || 
      property==ORDER_PROP_PRICE_CLOSE       ||
      property==ORDER_PROP_VOLUME_CURRENT    ||
      property==ORDER_PROP_PRICE_STOP_LIMIT  ||
      (
       this.OrderType()==DEAL_TYPE_BALANCE &&
       (
        property==ORDER_PROP_PRICE_OPEN      ||
        property==ORDER_PROP_COMMISSION      ||
        property==ORDER_PROP_SWAP            ||
        property==ORDER_PROP_VOLUME
       )
      )
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

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

Следует отметить, что не все поддерживаемые или неподдерживаемые свойства учтены в методах SupportProperty() для вывода свойств ордера в журнал. В сделках, например, учтены только три типа: сделка на покупку, сделка на продажу и балансовая операция.

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

Коллекция исторических ордеров и сделок

Всегда полезно иметь историю счёта под рукой. Да, терминал её предоставляет и даёт инструменты получения её в программах. Но нам для наших нужд необходимо иметь свой список, который мы сможем по запросу сортировать и перестраивать для возврата требуемых данных в свои программы. Соответственно, необходимо на каждом тике проверять изменение прошлого состояния истории счёта и при его изменении пересчитывать наш список исторических ордеров и сделок. Но это слишком накладно — перебирать всю историю на каждом тике. Поэтому мы сделаем только дополнение в наш список новых данных, а старые данные — они уже ранее считаны и хранятся в списке.

Давайте создадим новый класс CHistoryCollection в папке Collections:

Нажмите правой кнопкой по папке Collections, выберите пункт "Новый файл", в окне Мастера MQL выберите "Новый класс" и нажмите "Далее". Введите имя класса CHistoryCollection, оставьте поле базового класса пустым и нажмите "Готово".


В папке Collections будет создан новый файл HistoryCollection:

//+------------------------------------------------------------------+
//|                                            HistoryCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CHistoryCollection
  {
private:

public:
                     CHistoryCollection();
                    ~CHistoryCollection();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CHistoryCollection::CHistoryCollection()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CHistoryCollection::~CHistoryCollection()
  {
  }
//+------------------------------------------------------------------+

Займёмся его наполнением.

Для списка будем использовать динамический список указателей на экземпляры объектов стандартной библиотеки CArrayObj. Подключим его к нашему файлу и сразу его определим. (Кстати, чтобы проще это сделать, можно воспользоваться контекстным меню правой кнопки мыши)


Подключим CArrayObj и определим список исторических ордеров и сделок в приватной секции класса:

//+------------------------------------------------------------------+
//|                                            HistoryCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
//+------------------------------------------------------------------+
//| Коллекция исторических ордеров и сделок                          |
//+------------------------------------------------------------------+
class CHistoryCollection
  {
private:
   CArrayObj         m_list_all_orders;      // Список исторических ордеров и сделок

public:
                     CHistoryCollection();
                    ~CHistoryCollection();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CHistoryCollection::CHistoryCollection()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CHistoryCollection::~CHistoryCollection()
  {
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//|                                            HistoryCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
//+------------------------------------------------------------------+
//| Коллекция исторических ордеров и сделок                          |
//+------------------------------------------------------------------+
class CHistoryCollection
  {
private:
   CArrayObj         m_list_all_orders;      // Список исторических ордеров и сделок
   int               m_index_order;          // Индекс последнего добавленного ордера в коллекцию из списка истории терминала (MQL4, MQL5)
   int               m_index_deal;           // Индекс последней добавленной сделки в коллекцию из списка истории терминала (MQL5)
   int               m_delta_order;          // Разница в количестве ордеров по сравнению с прошлой проверкой
   int               m_delta_deal;           // Разница в количестве сделок по сравнению с прошлой проверкой
public:
                     CHistoryCollection();
                    ~CHistoryCollection();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CHistoryCollection::CHistoryCollection()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CHistoryCollection::~CHistoryCollection()
  {
  }
//+------------------------------------------------------------------+

При первом запуске все приватные члены класса должны обнулиться и история пересчитаться заново. Чтобы это сделать, нужно просто в конструкторе класса добавить список инициализации членов класса и задать по какому критерию по умолчанию будет сортироваться список-коллекция.

Сейчас мы имеем конструктор по умолчанию и, прежде, чем написать его реализацию, нам необходимо создать перечисление, в котором будут прописаны все возможные критерии сортировки ордеров и сделок в списке-коллекции.
Но сначала упорядочим целочисленные, вещественные и строковые свойства ордера для более логичного их вывода на печать в журнал. Откроем файл Defines.mqh из корневой папки библиотеки и расположим члены перечислений в нужном порядке:

//+------------------------------------------------------------------+
//| Целочисленные свойства ордера, сделки, позиции                   |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_INTEGER
  {
   ORDER_PROP_TICKET = 0,                                   // Тикет ордера
   ORDER_PROP_MAGIC,                                        // Мэджик ордера
   ORDER_PROP_TIME_OPEN,                                    // Время открытия (MQL5 Время сделки)
   ORDER_PROP_TIME_CLOSE,                                   // Время закрытия (MQL5 Время исполнения или снятия - ORDER_TIME_DONE)
   ORDER_PROP_TIME_OPEN_MSC,                                // Время открытия в милисекундах (MQL5 Время сделки в мсек.)
   ORDER_PROP_TIME_CLOSE_MSC,                               // Время закрытия в милисекундах (MQL5 Время исполнения или снятия - ORDER_TIME_DONE_MSC)
   ORDER_PROP_TIME_EXP,                                     // Дата экспирации ордера (для отложенных ордеров)
   ORDER_PROP_STATUS,                                       // Статус ордера (из перечисления ENUM_ORDER_STATUS)
   ORDER_PROP_TYPE,                                         // Тип ордера (MQL5 тип сделки)
   ORDER_PROP_DIRECTION,                                    // Тип по направлению (Buy, Sell)
   ORDER_PROP_REASON,                                       // Причина или источник сделки/ордера/позиции
   ORDER_PROP_POSITION_ID,                                  // Идентификатор позиции
   ORDER_PROP_POSITION_BY_ID,                               // Идентификатор встречной позиции
   ORDER_PROP_DEAL_ORDER,                                   // Ордер, на основании которого выполнена сделка
   ORDER_PROP_DEAL_ENTRY,                                   // Направление сделки – IN, OUT или IN/OUT
   ORDER_PROP_TIME_UPDATE,                                  // Время изменения позиции в секундах
   ORDER_PROP_TIME_UPDATE_MSC,                              // Время изменения позиции в милисекундах
   ORDER_PROP_TICKET_FROM,                                  // Тикет родительского ордера
   ORDER_PROP_TICKET_TO,                                    // Тикет дочернего ордера
   ORDER_PROP_PROFIT_PT,                                    // Профит в пунктах
   ORDER_PROP_CLOSE_BY_SL,                                  // Признак закрытия по StopLoss
   ORDER_PROP_CLOSE_BY_TP,                                  // Признак закрытия по TakeProfit
  }; 
#define ORDER_PROP_INTEGER_TOTAL    (22)                    // Общее количество целочисленных свойств
//+------------------------------------------------------------------+
//| Вещественные свойства ордера, сделки, позиции                    |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_DOUBLE
  {
   ORDER_PROP_PRICE_OPEN = ORDER_PROP_INTEGER_TOTAL,        // Цена открытия (MQL5 цена сделки)
   ORDER_PROP_PRICE_CLOSE,                                  // Цена закрытия
   ORDER_PROP_SL,                                           // Цена StopLoss
   ORDER_PROP_TP,                                           // Цена TaleProfit
   ORDER_PROP_PROFIT,                                       // Профит
   ORDER_PROP_COMMISSION,                                   // Комиссия
   ORDER_PROP_SWAP,                                         // Своп
   ORDER_PROP_VOLUME,                                       // Объём
   ORDER_PROP_VOLUME_CURRENT,                               // Невыполненный объем
   ORDER_PROP_PROFIT_FULL,                                  // Профит+комиссия+своп
   ORDER_PROP_PRICE_STOP_LIMIT,                             // Цена постановки Limit ордера при срабатывании StopLimit ордера
  };
#define ORDER_PROP_DOUBLE_TOTAL     (11)                    // Общее количество вещественных свойств
//+------------------------------------------------------------------+
//| Строковые свойства ордера, сделки, позиции                       |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_STRING
  {
   ORDER_PROP_SYMBOL = (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL), // Символ ордера
   ORDER_PROP_COMMENT,                                      // Комментарий ордера
   ORDER_PROP_EXT_ID                                        // Идентификатор ордера во внешней торговой системе
  };
#define ORDER_PROP_STRING_TOTAL     (3)                     // Общее количество строковых свойств
//+------------------------------------------------------------------+

Теперь в этот же файл впишем перечисление со всеми возможными типами сортировки ордеров и сделок:

//+------------------------------------------------------------------+
//| Возможные критерии сортировки ордеров и сделок                   |
//+------------------------------------------------------------------+
enum ENUM_SORT_ORDERS_MODE
  {
   //--- Сортировка по целочисленным свойствам
   SORT_BY_ORDER_TICKET          =  0,                      // Сортировать по тикету ордера
   SORT_BY_ORDER_MAGIC           =  1,                      // Сортировать по магику ордера
   SORT_BY_ORDER_TIME_OPEN       =  2,                      // Сортировать по времени открытия ордера
   SORT_BY_ORDER_TIME_CLOSE      =  3,                      // Сортировать по времени закрытия ордера
   SORT_BY_ORDER_TIME_OPEN_MSC   =  4,                      // Сортировать по времени открытия ордера в милисекундах
   SORT_BY_ORDER_TIME_CLOSE_MSC  =  5,                      // Сортировать по времени закрытия ордера в милисекундах
   SORT_BY_ORDER_TIME_EXP        =  6,                      // Сортировать по дате экспирации ордера
   SORT_BY_ORDER_STATUS          =  7,                      // Сортировать по статусу ордера (маркет-ордер/отложенный ордер/сделка)
   SORT_BY_ORDER_TYPE            =  8,                      // Сортировать по типу ордера
   SORT_BY_ORDER_REASON          =  10,                     // Сортировать по причине/источнику сделки/ордера/позиции
   SORT_BY_ORDER_POSITION_ID     =  11,                     // Сортировать по идентификатору позиции
   SORT_BY_ORDER_POSITION_BY_ID  =  12,                     // Сортировать по идентификатору встречной позиции
   SORT_BY_ORDER_DEAL_ORDER      =  13,                     // Сортировать по ордеру, на основание которого выполнена сделка
   SORT_BY_ORDER_DEAL_ENTRY      =  14,                     // Сортировать по направлению сделки – IN, OUT или IN/OUT
   SORT_BY_ORDER_TIME_UPDATE     =  15,                     // Сортировать по времени изменения позиции в секундах
   SORT_BY_ORDER_TIME_UPDATE_MSC =  16,                     // Сортировать по времени изменения позиции в милисекундах
   SORT_BY_ORDER_TICKET_FROM     =  17,                     // Сортировать по тикету родительского ордера
   SORT_BY_ORDER_TICKET_TO       =  18,                     // Сортировать по тикету дочернего ордера
   SORT_BY_ORDER_PROFIT_PT       =  19,                     // Сортировать по профиту ордера в пунктах
   SORT_BY_ORDER_CLOSE_BY_SL     =  20,                     // Сортировать по признаку закрытия ордера по StopLoss
   SORT_BY_ORDER_CLOSE_BY_TP     =  21,                     // Сортировать по признаку закрытия ордера по TakeProfit
   //--- Сортировка по вещественным свойствам
   SORT_BY_ORDER_PRICE_OPEN      =  ORDER_PROP_INTEGER_TOTAL,// Сортировать по цене открытия
   SORT_BY_ORDER_PRICE_CLOSE     =  23,                     // Сортировать по цене закрытия
   SORT_BY_ORDER_SL              =  24,                     // Сортировать по цене StopLoss
   SORT_BY_ORDER_TP              =  25,                     // Сортировать по цене TaleProfit
   SORT_BY_ORDER_PROFIT          =  26,                     // Сортировать по профиту
   SORT_BY_ORDER_COMMISSION      =  27,                     // Сортировать по комиссии
   SORT_BY_ORDER_SWAP            =  28,                     // Сортировать по свопу
   SORT_BY_ORDER_VOLUME          =  29,                     // Сортировать по объёму
   SORT_BY_ORDER_VOLUME_CURRENT  =  30,                     // Сортировать по невыполненному объему
   SORT_BY_ORDER_PROFIT_FULL     =  31,                     // Сортировать по критерию профит+комиссия+своп
   SORT_BY_ORDER_PRICE_STOP_LIMIT=  32,                     // Сортировать по цене постановки Limit ордера при срабатывании StopLimit ордера
   //--- Сортировка по строковым свойствам
   SORT_BY_ORDER_SYMBOL          =  ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL,// Сортировать по символу
   SORT_BY_ORDER_COMMENT         =  34,                     // Сортировать по комментарию
   SORT_BY_ORDER_EXT_ID          =  35                      // Сортировать по идентификатору ордера во внешней торговой системе
  };
//+------------------------------------------------------------------+

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

Как можно было заметить, в данном списке пропущено свойство для сортировки ORDER_PROP_DIRECTION, так как это свойство является сервисным и используется для нужд библиотеки. Впрочем, как и иные пользовательские свойства, ранее нами добавленные. Но по ним возможно потребуется сортировка, потому они были оставлены.

Теперь можно написать реализацию конструктора класса CHistoryCollection:

//+------------------------------------------------------------------+
//|                                            HistoryCollection.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\DELib.mqh"
//+------------------------------------------------------------------+
//| Коллекция исторических ордеров и сделок                          |
//+------------------------------------------------------------------+
class CHistoryCollection
  {
private:
   CArrayObj         m_list_all_orders;      // Список всех исторических ордеров и сделок
   int               m_index_order;          // Индекс последнего добавленного ордера в коллекцию из списка истории терминала (MQL4, MQL5)
   int               m_index_deal;           // Индекс последней добавленной сделки в коллекцию из списка истории терминала (MQL5)
   int               m_delta_order;          // Разница в количестве ордеров по сравнению с прошлой проверкой
   int               m_delta_deal;           // Разница в количестве сделок по сравнению с прошлой проверкой
public:
                     CHistoryCollection();
                    ~CHistoryCollection();
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CHistoryCollection::CHistoryCollection(void) : m_index_deal(0), 
                                               m_delta_deal(0), 
                                               m_index_order(0),
                                               m_delta_order(0) 
  {
   m_list_all_orders.Sort(SORT_BY_ORDER_TIME_CLOSE);
  }
//+------------------------------------------------------------------+

Разберём листинг выше.
Так как в конструкторе класса используется значение только что добавленного перечисления, то нужно подключить файл Defines.mqh к файлу класса.
В первой статье
, при подготовке базового класса абстрактного ордера, мы создали библиотеку сервисных функций DELib.mqh, к которой подключили файл Defines.mqh со всеми необходимыми перечислениями и макроподстановками. Поэтому подключим именно библиотеку сервисных функций.
В конструкторе в списке инициализации обнуляются все индексы и значения разниц текущего количества с прошлым, и затем в теле конструктора задаётся списку-коллекции сортировка по умолчанию по времени закрытия.

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

//+------------------------------------------------------------------+
//| Коллекция исторических ордеров и сделок                          |
//+------------------------------------------------------------------+
class CHistoryCollection
  {
private:
   CArrayObj         m_list_all_orders;      // Список всех исторических ордеров и сделок
   bool              m_is_trade_event;       // Флаг торгового события
   int               m_index_order;          // Индекс последнего добавленного ордера в коллекцию из списка истории терминала (MQL4, MQL5)
   int               m_index_deal;           // Индекс последней добавленной сделки в коллекцию из списка истории терминала (MQL5)
   int               m_delta_order;          // Разница в количестве ордеров по сравнению с прошлой проверкой
   int               m_delta_deal;           // Разница в количестве сделок по сравнению с прошлой проверкой
public:
                     CHistoryCollection();
   //--- Обновляет список ордеров, заполняет данные о количестве новых и устанавливает флаг торгового события
   void              Refresh(void);
  };
//+------------------------------------------------------------------+

Для реализации обновления списка ордеров коллекции нам потребуется ещё одна макроподстановка — для запроса исторических данных в полном объёме. Для этого служит функция HistorySelect(). В её параметрах передаются даты начала и конца требуемых данных. Для получения полного списка всей истории счёта нужно передавать дату начала как 0, а дату конца как TimeCurrent(), но в этом случае могут быть ситуации, когда не всё, что есть в исторических данных будет возвращено. Это нюансы, и чтобы такого не происходило, нужно вместо TimeCurrent() вводить заведомо большую дату, чем текущее время сервера. Будем вводить туда максимально-возможную дату: 31.12.3000 23:59:59. Это ещё удобно тем, что в пользовательских символах может существовать такая дата, и такое получение истории будет работать.

Впишем в файл Defines.mqh новую макроподстановку:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
//+------------------------------------------------------------------+
//| Макроподстановки                                                 |
//+------------------------------------------------------------------+
#define COUNTRY_LANG   ("Russian")              // Язык страны
#define DFUN           (__FUNCTION__+": ")      // "Описание функции"
#define END_TIME       (D'31.12.3000 23:59:59') // Конечная дата для запросов данных истории счёта
//+------------------------------------------------------------------+

Теперь, вместо ввода TimeCurrent() как конечного времени, будем вводить макрос END_TIME

Реализация обновления списка ордеров коллекции:

//+------------------------------------------------------------------+
//| Обновляет список ордеров                                         |
//+------------------------------------------------------------------+
void CHistoryCollection::Refresh(void)
  {
#ifdef __MQL4__
   int total=::OrdersHistoryTotal(),i=m_index_order;
   for(; i<total; i++)
     {
      if(!::OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) continue;
      ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)::OrderType();
      //--- Закрытые позиции и балансные/кредитные операции
      if(order_type<ORDER_TYPE_BUY_LIMIT || order_type>ORDER_TYPE_SELL_STOP)
        {
         CHistoryOrder *order=new CHistoryOrder(::OrderTicket());
         if(order==NULL) continue;
         m_list_all_orders.InsertSort(order);
        }
      else
        {
         //--- Удалённые отложенные ордера
         CHistoryPending *order=new CHistoryPending(::OrderTicket());
         if(order==NULL) continue;
         m_list_all_orders.InsertSort(order);
        }
     }
//---
   int delta_order=i-m_index_order;
   this.m_index_order=i;
   this.m_delta_order=delta_order;
   this.m_is_trade_event=(this.m_delta_order!=0 ? true : false);
//--- __MQL5__
#else 
   if(!::HistorySelect(0,END_TIME)) return;
//--- Ордера
   int total_orders=::HistoryOrdersTotal(),i=m_index_order;
   for(; i<total_orders; i++)
     {
      ulong order_ticket=::HistoryOrderGetTicket(i);
      if(order_ticket==0) continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(order_ticket,ORDER_TYPE);
      if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL)
        {
         CHistoryOrder *order=new CHistoryOrder(order_ticket);
         if(order==NULL) continue;
         m_list_all_orders.InsertSort(order);
        }
      else
        {
         CHistoryPending *order=new CHistoryPending(order_ticket);
         if(order==NULL) continue;
         m_list_all_orders.InsertSort(order);
        }
     }
//--- сохранение индекса последнего добавленного ордера и разницы по сравнению с прошлой проверкой
   int delta_order=i-this.m_index_order;
   this.m_index_order=i;
   this.m_delta_order=delta_order;
   
//--- Сделки
   int total_deals=::HistoryDealsTotal(),j=m_index_deal;
   for(; j<total_deals; j++)
     {
      ulong deal_ticket=::HistoryDealGetTicket(j);
      if(deal_ticket==0) continue;
      CHistoryDeal *deal=new CHistoryDeal(deal_ticket);
      if(deal==NULL) continue;
      m_list_all_orders.InsertSort(deal);
     }
//--- сохранение индекса последней добавленной сделки и разницы по сравнению с прошлой проверкой
   int delta_deal=j-this.m_index_deal;
   this.m_index_deal=j;
   this.m_delta_deal=delta_deal;
//--- Установка флага нового события в истории
   this.m_is_trade_event=(this.m_delta_order+this.m_delta_deal);
#endif 
  }
//+------------------------------------------------------------------+

Проверяем принадлежность к MQL4 или к MQL5. Разберём код на примере MQL5, так как он немного сложнее.

Сначала запрашиваем всю историю счёта и, если не получилось, то выходим до следующего тика. После успешного запроса истории, будут созданы два списка — список ордеров и список сделок.

Сначала идём в цикле по списку всех ордеров. Начальным индексом, с которого будет идти цикл, является результат работы данного цикла в прошлый раз (при первом запуске = 0). Это позволяет нам идти только по новым ордерам, появившимся в истории с последней проверки, а не гонять дорогой цикл по всей истории.

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

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

Цикл в версии MQL4-кода практически тот же, только там идёт цикл от всего возможного размера истории, доступной в истории счёта, а она может задаваться пользователем во вкладке терминала "История", поэтому тут уже самому пользователю стоит озаботиться доступностью всей истории если того требует его программа. Либо для получения полной истории использовать WinAPI, что не входит в задачи этих статей.

По завершению работы циклов проверяется количество новых ордеров и сделок и, если количество больше нуля, то устанавливается флаг произошедшего торгового события.

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

Для этого добавим в публичную секцию класса CHistoryCollection метод GetList() без параметров (в следующей статье будут ещё добавлены методы получения списка, но уже с параметрами):
//+------------------------------------------------------------------+
//| Коллекция исторических ордеров и сделок                          |
//+------------------------------------------------------------------+
class CHistoryCollection
  {
private:
   CArrayObj         m_list_all_orders;      // Список всех исторических ордеров и сделок
   bool              m_is_trade_event;       // Флаг торгового события
   int               m_index_order;          // Индекс последнего добавленного ордера в коллекцию из списка истории терминала (MQL4, MQL5)
   int               m_index_deal;           // Индекс последней добавленной сделки в коллекцию из списка истории терминала (MQL5)
   int               m_delta_order;          // Разница в количестве ордеров по сравнению с прошлой проверкой
   int               m_delta_deal;           // Разница в количестве сделок по сравнению с прошлой проверкой
public:
   //--- Возвращает полный список-коллекцию "как есть"
   CArrayObj*        GetList(void)           { return &m_list_all_orders;  }
//--- Конструктор
                     CHistoryCollection();
   //--- Обновляет список ордеров, заполняет данные о количестве новых и устанавливает флаг торгового события
   void              Refresh(void);
  };
//+------------------------------------------------------------------+

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

А сейчас давайте проверим как заполняется список. Для этого создадим небольшой тестовый советник. Создадим в папке TestDoEasy, которую создавали в первой части, подпапку Part02 — в ней будет расположен файл второго тестового советника с именем TestDoEasyPart02, и подключим к нему созданную коллекцию:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart02.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Collections\HistoryCollection.mqh>
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart02.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Collections\HistoryCollection.mqh>
//--- enums
enum ENUM_TYPE_ORDERS
  {
   TYPE_ORDER_MARKET,   // Market-orders
   TYPE_ORDER_PENDING,  // Pending orders
   TYPE_ORDER_DEAL      // Deals
  };
//--- input parameters
input ENUM_TYPE_ORDERS  InpOrderType   =  TYPE_ORDER_DEAL;  // Show type:
//--- global variables
CHistoryCollection history;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

Теперь точно так же, как и в прошлом тестовом советнике из первой части описания библиотеки, в обработчике OnInit() считаем историю счёта и в цикле выведем информацию об ордерах в журнал:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- обновляем историю
   history.Refresh();
//--- получаем указатель на полный список-коллекцию
   CArrayObj* list=history.GetList();
   if(list==NULL)
     {
      Print("Could not get collection list");
      return INIT_FAILED;
     }
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      //--- получаем ордер из списка
      COrder* order=list.At(i);
      if(order==NULL) continue;
      //--- если это сделка
      if(order.Status()==ORDER_STATUS_DEAL && InpOrderType==TYPE_ORDER_DEAL)
         order.Print();
      //--- если это исторический маркет-ордер
      if(order.Status()==ORDER_STATUS_HISTORY_ORDER && InpOrderType==TYPE_ORDER_MARKET)
         order.Print();
      //--- если это удалённый отложенный ордер
      if(order.Status()==ORDER_STATUS_HISTORY_PENDING && InpOrderType==TYPE_ORDER_PENDING)
         order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Здесь: создаём указатель на список-коллекцию и получаем его из CHistoryCollection при помощи созданного там же метода GetList().
Далее в цикле получаем объект-ордер из списка, проверяем его статус и разрешение для вывода в журнал в настройках советника.
В зависимости от результата проверки — печатаем данные в журнал, или не печатаем.
Следует отметить, что хоть мы и получаем из списка базовый объект COrder, но в журнал выводятся только те данные, которые присущи наследникам этого базового ордера — маркет-ордера, отложенные ордера и сделки, у которых переопределены виртуальные методы, возвращающие флаг поддержания ордером только присущих ему свойств.

Компилируем и запускаем советник. В журнал будут выведены данные по выбранным типам ордеров и сделок:

Если внимательно посмотреть на типы выводимых свойств, то для ордеров увидим не присущие в MQL5 ордерам свойства: профит, своп, комиссия и профит в пунктах.

Внесём некоторые исправления и дополнения в уже созданные объекты.

Допишем в класс CHistoryOrder в метод SupportProperty(ENUM_ORDER_PROP_INTEGER property) неподдерживаемое свойство для MQL5 профит в пунктах:

//+------------------------------------------------------------------+
//| Возвращает истину, если ордер поддерживает переданное свойство,  |
//| возвращает ложь в противном случае                               |
//+------------------------------------------------------------------+
bool CHistoryOrder::SupportProperty(ENUM_ORDER_PROP_INTEGER property)
  {
   if(property==ORDER_PROP_TIME_EXP       || 
      property==ORDER_PROP_DEAL_ENTRY     || 
      property==ORDER_PROP_TIME_UPDATE    || 
      property==ORDER_PROP_TIME_UPDATE_MSC
      #ifdef __MQL5__                     ||
      property==ORDER_PROP_PROFIT_PT
      #endif                        
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Исторический маркет-ордер                                        |
//+------------------------------------------------------------------+
class CHistoryOrder : public COrder
  {
public:
   //--- Конструктор
                     CHistoryOrder(const ulong ticket) : COrder(ORDER_STATUS_HISTORY_ORDER,ticket) {}
   //--- Поддерживаемые целочисленные свойства ордера
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property);
   //--- Поддерживаемые вещественные свойства ордера
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property);
  };
//+------------------------------------------------------------------+

и его реализацию:

//+------------------------------------------------------------------+
//| Возвращает истину, если ордер поддерживает переданное свойство,  |
//| возвращает ложь в противном случае                               |
//+------------------------------------------------------------------+
bool CHistoryOrder::SupportProperty(ENUM_ORDER_PROP_DOUBLE property)
  {
#ifdef __MQL5__
   if(property==ORDER_PROP_PROFIT      || 
      property==ORDER_PROP_PROFIT_FULL || 
      property==ORDER_PROP_SWAP        || 
      property==ORDER_PROP_COMMISSION  ||
      property==ORDER_PROP_PRICE_STOP_LIMIT
     ) return false;
#endif 
   return true;
  }
//+------------------------------------------------------------------+

Если это MQL5, то профит, своп, комиссия, полный профит и цена постановки StopLimit-ордера не поддерживаются. Для MQL4 и для остальных вещественных свойств ордера в MQL5 возвращаем флаг поддержания ордером вещественных свойств.

У ордеров в MQL5 есть свойство ORDER_STATE — это состояние ордера, прописанное в перечислении ENUM_ORDER_STATE.
Добавим его в список целочисленных свойств ордеров в перечисление ENUM_ORDER_PROP_INTEGER в файле Defines.mqh:

//+------------------------------------------------------------------+
//| Целочисленные свойства ордера, сделки, позиции                   |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_INTEGER
  {
   ORDER_PROP_TICKET = 0,                                   // Тикет ордера
   ORDER_PROP_MAGIC,                                        // Мэджик ордера
   ORDER_PROP_TIME_OPEN,                                    // Время открытия (MQL5 Время сделки)
   ORDER_PROP_TIME_CLOSE,                                   // Время закрытия (MQL5 Время исполнения или снятия - ORDER_TIME_DONE)
   ORDER_PROP_TIME_OPEN_MSC,                                // Время открытия в милисекундах (MQL5 Время сделки в мсек.)
   ORDER_PROP_TIME_CLOSE_MSC,                               // Время закрытия в милисекундах (MQL5 Время исполнения или снятия - ORDER_TIME_DONE_MSC)
   ORDER_PROP_TIME_EXP,                                     // Дата экспирации ордера (для отложенных ордеров)
   ORDER_PROP_STATUS,                                       // Статус ордера (из перечисления ENUM_ORDER_STATUS)
   ORDER_PROP_TYPE,                                         // Тип ордера (MQL5 тип сделки)
   ORDER_PROP_DIRECTION,                                    // Тип по направлению (Buy, Sell)
   ORDER_PROP_REASON,                                       // Причина или источник сделки/ордера/позиции
   ORDER_PROP_STATE,                                        // Состояние ордера (из перечисления ENUM_ORDER_STATE)
   ORDER_PROP_POSITION_ID,                                  // Идентификатор позиции
   ORDER_PROP_POSITION_BY_ID,                               // Идентификатор встречной позиции
   ORDER_PROP_DEAL_ORDER,                                   // Ордер, на основании которого выполнена сделка
   ORDER_PROP_DEAL_ENTRY,                                   // Направление сделки – IN, OUT или IN/OUT
   ORDER_PROP_TIME_UPDATE,                                  // Время изменения позиции в секундах
   ORDER_PROP_TIME_UPDATE_MSC,                              // Время изменения позиции в милисекундах
   ORDER_PROP_TICKET_FROM,                                  // Тикет родительского ордера
   ORDER_PROP_TICKET_TO,                                    // Тикет дочернего ордера
   ORDER_PROP_PROFIT_PT,                                    // Профит в пунктах
   ORDER_PROP_CLOSE_BY_SL,                                  // Признак закрытия по StopLoss
   ORDER_PROP_CLOSE_BY_TP,                                  // Признак закрытия по TakeProfit
  }; 
#define ORDER_PROP_INTEGER_TOTAL    (23)                    // Общее количество целочисленных свойств
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возможные критерии сортировки ордеров и сделок                   |
//+------------------------------------------------------------------+
#define FIRST_DBL_PROP              (ORDER_PROP_INTEGER_TOTAL)
#define FIRST_STR_PROP              (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL)
enum ENUM_SORT_ORDERS_MODE
  {
   //--- Сортировка по целочисленным свойствам
   SORT_BY_ORDER_TICKET          =  0,                      // Сортировать по тикету ордера
   SORT_BY_ORDER_MAGIC           =  1,                      // Сортировать по магику ордера
   SORT_BY_ORDER_TIME_OPEN       =  2,                      // Сортировать по времени открытия ордера
   SORT_BY_ORDER_TIME_CLOSE      =  3,                      // Сортировать по времени закрытия ордера
   SORT_BY_ORDER_TIME_OPEN_MSC   =  4,                      // Сортировать по времени открытия ордера в милисекундах
   SORT_BY_ORDER_TIME_CLOSE_MSC  =  5,                      // Сортировать по времени закрытия ордера в милисекундах
   SORT_BY_ORDER_TIME_EXP        =  6,                      // Сортировать по дате экспирации ордера
   SORT_BY_ORDER_STATUS          =  7,                      // Сортировать по статусу ордера (маркет-ордер/отложенный ордер/сделка)
   SORT_BY_ORDER_TYPE            =  8,                      // Сортировать по типу ордера
   SORT_BY_ORDER_REASON          =  10,                     // Сортировать по причине/источнику сделки/ордера/позиции
   SORT_BY_ORDER_STATE           =  11,                     // Сортировать по состоянию ордера
   SORT_BY_ORDER_POSITION_ID     =  12,                     // Сортировать по идентификатору позиции
   SORT_BY_ORDER_POSITION_BY_ID  =  13,                     // Сортировать по идентификатору встречной позиции
   SORT_BY_ORDER_DEAL_ORDER      =  14,                     // Сортировать по ордеру, на основание которого выполнена сделка
   SORT_BY_ORDER_DEAL_ENTRY      =  15,                     // Сортировать по направлению сделки – IN, OUT или IN/OUT
   SORT_BY_ORDER_TIME_UPDATE     =  16,                     // Сортировать по времени изменения позиции в секундах
   SORT_BY_ORDER_TIME_UPDATE_MSC =  17,                     // Сортировать по времени изменения позиции в милисекундах
   SORT_BY_ORDER_TICKET_FROM     =  18,                     // Сортировать по тикету родительского ордера
   SORT_BY_ORDER_TICKET_TO       =  19,                     // Сортировать по тикету дочернего ордера
   SORT_BY_ORDER_PROFIT_PT       =  20,                     // Сортировать по профиту ордера в пунктах
   SORT_BY_ORDER_CLOSE_BY_SL     =  21,                     // Сортировать по признаку закрытия ордера по StopLoss
   SORT_BY_ORDER_CLOSE_BY_TP     =  22,                     // Сортировать по признаку закрытия ордера по TakeProfit
   //--- Сортировка по вещественным свойствам
   SORT_BY_ORDER_PRICE_OPEN      =  FIRST_DBL_PROP,         // Сортировать по цене открытия
   SORT_BY_ORDER_PRICE_CLOSE     =  FIRST_DBL_PROP+1,       // Сортировать по цене закрытия
   SORT_BY_ORDER_SL              =  FIRST_DBL_PROP+2,       // Сортировать по цене StopLoss
   SORT_BY_ORDER_TP              =  FIRST_DBL_PROP+3,       // Сортировать по цене TaleProfit
   SORT_BY_ORDER_PROFIT          =  FIRST_DBL_PROP+4,       // Сортировать по профиту
   SORT_BY_ORDER_COMMISSION      =  FIRST_DBL_PROP+5,       // Сортировать по комиссии
   SORT_BY_ORDER_SWAP            =  FIRST_DBL_PROP+6,       // Сортировать по свопу
   SORT_BY_ORDER_VOLUME          =  FIRST_DBL_PROP+7,       // Сортировать по объёму
   SORT_BY_ORDER_VOLUME_CURRENT  =  FIRST_DBL_PROP+8,       // Сортировать по невыполненному объему
   SORT_BY_ORDER_PROFIT_FULL     =  FIRST_DBL_PROP+9,       // Сортировать по критерию профит+комиссия+своп
   SORT_BY_ORDER_PRICE_STOP_LIMIT=  FIRST_DBL_PROP+10,      // Сортировать по цене постановки Limit ордера при срабатывании StopLimit ордера
   //--- Сортировка по строковым свойствам
   SORT_BY_ORDER_SYMBOL          =  FIRST_STR_PROP,         // Сортировать по символу
   SORT_BY_ORDER_COMMENT         =  FIRST_STR_PROP+1,       // Сортировать по комментарию
   SORT_BY_ORDER_EXT_ID          =  FIRST_STR_PROP+2        // Сортировать по идентификатору ордера во внешней торговой системе
  };
//+------------------------------------------------------------------+

В защищённой секции класса абстрактного ордера COrder в файле Order.mqh объявим метод  OrderState(), который записывает в свойства ордера его состояние из перечисления ENUM_ORDER_STATE:

protected:
   //--- Защищённый параметрический конструктор
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);

   //--- Получает и возвращает целочисленные свойства выбранного ордера из его параметров
   long              OrderMagicNumber(void)        const;
   long              OrderTicket(void)             const;
   long              OrderTicketFrom(void)         const;
   long              OrderTicketTo(void)           const;
   long              OrderPositionID(void)         const;
   long              OrderPositionByID(void)       const;
   long              OrderOpenTimeMSC(void)        const;
   long              OrderCloseTimeMSC(void)       const;
   long              OrderType(void)               const;
   long              OrderState(void)              const;
   long              OrderTypeByDirection(void)    const;
   long              OrderTypeFilling(void)        const;
   long              OrderTypeTime(void)           const;
   long              OrderReason(void)             const;
   long              DealOrder(void)               const;
   long              DealEntry(void)               const;
   bool              OrderCloseByStopLoss(void)    const;
   bool              OrderCloseByTakeProfit(void)  const;
   datetime          OrderOpenTime(void)           const;
   datetime          OrderCloseTime(void)          const;
   datetime          OrderExpiration(void)         const;
   datetime          PositionTimeUpdate(void)      const;
   datetime          PositionTimeUpdateMSC(void)   const;

и впишем его реализацию:

//+------------------------------------------------------------------+
//| Возвращает состояние ордера                                      |
//+------------------------------------------------------------------+
long COrder::OrderState(void) const
  {
#ifdef __MQL4__              
   return ORDER_STATE_FILLED;
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_STATE); break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_STATE);                 break;
      case ORDER_STATUS_MARKET_ACTIVE     : 
      case ORDER_STATUS_DEAL              : 
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+

Для MQL4 пока будем возвращать, что ордер выполнен полностью. Для MQL5 в зависимости от статуса ордера либо возвращаем 0 (если это сделка или позиция), либо состояние ордера (если это маркет-ордер или отложенный ордер).

В публичной секции класса COrder объявим метод, возвращающий описание состояния ордера:

//+------------------------------------------------------------------+
//| Описания свойств объекта-ордера                                  |
//+------------------------------------------------------------------+
   //--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства ордера
   string            GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_ORDER_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_ORDER_PROP_STRING property);
   //--- Возвращает наименование статуса ордера
   string            StatusDescription(void)    const;
   //--- Возвращает наименование ордера или позиции
   string            TypeDescription(void)      const;
   //--- Возвращает описание состояния ордера
   string            StateDescription(void)     const;
   //--- Возвращает наименование направления сделки
   string            DealEntryDescription(void) const;
   //--- Возвращает тип по направлению ордера/позиции
   string            DirectionDescription(void) const;
   //--- Выводит в журнал описание свойств ордера (full_prop=true - все свойства, false - только поддерживаемые)
   void              Print(const bool full_prop=false);

и его реализацию:

//+------------------------------------------------------------------+
//| Возвращает описание состояния ордера                             |
//+------------------------------------------------------------------+
string COrder::StateDescription(void) const
  {
   if(this.Status()==ORDER_STATUS_DEAL || this.Status()==ORDER_STATUS_MARKET_ACTIVE)
      return "";                       
   else switch(this.StateOrder())
     {
      case ORDER_STATE_STARTED         :  return TextByLanguage("Ордер проверен на корректность, но еще не принят брокером","The order is checked for correctness, but not yet accepted by the broker");
      case ORDER_STATE_PLACED          :  return TextByLanguage("Ордер принят","Order accepted");
      case ORDER_STATE_CANCELED        :  return TextByLanguage("Ордер снят клиентом","Order withdrawn by client");
      case ORDER_STATE_PARTIAL         :  return TextByLanguage("Ордер выполнен частично","The order is filled partially");
      case ORDER_STATE_FILLED          :  return TextByLanguage("Ордер выполнен полностью","The order is filled");
      case ORDER_STATE_REJECTED        :  return TextByLanguage("Ордер отклонен","Order rejected");
      case ORDER_STATE_EXPIRED         :  return TextByLanguage("Ордер снят по истечении срока его действия","Order withdrawn upon expiration");
      case ORDER_STATE_REQUEST_ADD     :  return TextByLanguage("Ордер в состоянии регистрации (выставление в торговую систему)","Order in the state of registration (placing in the trading system)");
      case ORDER_STATE_REQUEST_MODIFY  :  return TextByLanguage("Ордер в состоянии модификации","The order is in a state of modification.");
      case ORDER_STATE_REQUEST_CANCEL  :  return TextByLanguage("Ордер в состоянии удаления","Order is in deletion state");
      default                          :  return TextByLanguage("Неизвестное состояние","Unknown state");
     }
  }
//+------------------------------------------------------------------+

Если это сделка или позиция, то возвращаем пустую строку, иначе — проверяем состояние ордера и возвращаем его описание.

В реализацию метода GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property) добавим возврат описания состояния ордера:

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства ордера               |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property)
  {
   return
     (
   //--- Общие свойства
      property==ORDER_PROP_MAGIC             ?  TextByLanguage("Магик","Magic")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET            ?  TextByLanguage("Тикет","Ticket")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET_FROM       ?  TextByLanguage("Тикет родительского ордера","Ticket of parent order")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET_TO         ?  TextByLanguage("Тикет наследуемого ордера","Inherited order ticket")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TIME_OPEN         ?  TextByLanguage("Время открытия","Time open")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==ORDER_PROP_TIME_CLOSE        ?  TextByLanguage("Время закрытия","Time close")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==ORDER_PROP_TIME_EXP          ?  TextByLanguage("Дата экспирации","Date of expiration")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Не задана",": Not set") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS))
         )  :
      property==ORDER_PROP_TYPE              ?  TextByLanguage("Тип","Type")+": "+this.TypeDescription()                   :
      property==ORDER_PROP_DIRECTION         ?  TextByLanguage("Тип по направлению","Type by direction")+": "+this.DirectionDescription() :
      
      property==ORDER_PROP_REASON            ?  TextByLanguage("Причина","Reason")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+this.GetReasonDescription(this.GetProperty(property))
         )  :
      property==ORDER_PROP_POSITION_ID       ?  TextByLanguage("Идентификатор позиции","Position identifier")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_DEAL_ORDER        ?  TextByLanguage("Сделка на основании ордера","Deal by order")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_DEAL_ENTRY        ?  TextByLanguage("Направление сделки","Deal entry")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+this.GetEntryDescription(this.GetProperty(property))
         )  :
      property==ORDER_PROP_POSITION_BY_ID    ?  TextByLanguage("Идентификатор встречной позиции","Identifier opposite position")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TIME_OPEN_MSC     ?  TextByLanguage("Время открытия в милисекундах","Opening time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property))
         )  :
      property==ORDER_PROP_TIME_CLOSE_MSC    ?  TextByLanguage("Время закрытия в милисекундах","Closing time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property))
         )  :
      property==ORDER_PROP_TIME_UPDATE       ?  TextByLanguage("Время изменения позиции","Position change time")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(this.GetProperty(property)!=0 ? ::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) : "0")
         )  :
      property==ORDER_PROP_TIME_UPDATE_MSC   ?  TextByLanguage("Время изменения позиции в милисекундах","Time to change the position in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(this.GetProperty(property)!=0 ? (string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property)) : "0")
         )  :
      property==ORDER_PROP_STATE             ?  TextByLanguage("Состояние","Statе")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": \""+this.StateDescription()+"\""
         )  :
   //--- Дополнительное свойство
      property==ORDER_PROP_STATUS            ?  TextByLanguage("Статус","Status")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": \""+this.StatusDescription()+"\""
         )  :
      property==ORDER_PROP_PROFIT_PT         ?  TextByLanguage("Прибыль в пунктах","Profit in points")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_CLOSE_BY_SL       ?  TextByLanguage("Закрытие по StopLoss","Close by StopLoss")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      property==ORDER_PROP_CLOSE_BY_TP       ?  TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

И на этом исправления завершены.

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

Что дальше

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

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

К содержанию


Прикрепленные файлы |
MQL5.zip (24.17 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (11)
Artyom Trishkin
Artyom Trishkin | 6 мар 2019 в 15:10
Victor Nikolaev:

Молодец.

Хорошо получается.

Жаль. Первую статью пока не смотрел. Но посмотрю. Теперь понятно куда пропал. Хотя наверно пропал я. Это точнее будет

Спасибо, Вить, рад слышать тебя, хоть и так редко...
Vasiliy Sokolov
Vasiliy Sokolov | 6 мар 2019 в 15:38
Victor Nikolaev:

...

О, какие люди к нам захаживают. Ни че се! Рад, что дорожку не совсем к нам забыли:)

Stanislav Korotky
Stanislav Korotky | 6 мар 2019 в 15:56
На самом деле - пример того, как не надо программировать (речь не про функционал, а про реализацию): в базовый класс запихивается все подряд, в наследниках делается куча флагов и затычек, чтобы определить, что где поддерживается, простыня копипейстов... Антипаттерны. Понятно, что это все "вживую", но уже претендует на полную переделку, если по хорошему.
Artyom Trishkin
Artyom Trishkin | 6 мар 2019 в 16:08
Stanislav Korotky:
На самом деле - пример того, как не надо программировать (речь не про функционал, а про реализацию): в базовый класс запихивается все подряд, в наследниках делается куча флагов и затычек, чтобы определить, что где поддерживается, простыня копипейстов... Антипаттерны. Понятно, что это все "вживую", но уже претендует на полную переделку, если по хорошему.
Спасибо за мнение. Не буду ни спорить, ни менять концепцию.
BillionerClub
BillionerClub | 7 мар 2019 в 05:26
Статью не читал но если грубо то сама идея просто улет - еще бы довести с хелпом опенсоурса до типа stl на все случаи жизни а потом уже вставлять  как precompiled header в любой проект. 
Использование вычислительных возможностей MATLAB 2018 в MetaTrader 5 Использование вычислительных возможностей MATLAB 2018 в MetaTrader 5

После модернизации пакета MATLAB в 2015 году необходимо рассмотреть современный способ создания DLL-библиотек. На примере прогнозирующего индикатора в статье иллюстрируются особенности связывания MetaTrader 5 и MATLAB с использованием современных 64-х разрядных версий платформ, применяемых в настоящее время. Рассмотрение всей последовательности подключения MATLAB позволит разработчику на MQL5 быстрее создавать приложения с расширенными вычислительными возможностями, избегая «подводных камней».

Исследование методов свечного анализа (Часть II): Автопоиск новых паттернов Исследование методов свечного анализа (Часть II): Автопоиск новых паттернов

В предыдущей статье были рассмотрены всего 14 паттернов, но, как известно, существуют и другие свечные модели. И чтобы монотонно не рассматривать всё великое многообразие остальных паттернов, было решено пойти другим путем. Теперь вашему вниманию предлагается система поиска и тестирования новых свечных моделей на основе известных типов свечей.

Создаем кроссплатформенный советник-сеточник (гридер) Создаем кроссплатформенный советник-сеточник (гридер)

В данной статье мы научимся писать советники, которые работают сразу и в MetaTrader 4, и в MetaTrader 5. Для этого мы попробуем написать советник, работающий по принципу создания сетки из ордеров. Сеточники или гридеры — это советники, основной принцип работы которых заключается в одновременном выставлении нескольких лимитных ордеров выше текущей цены, и такого же количества лимитных ордеров ниже текущей цены.

Библиотека для простого и быстрого создания программ для MetaTrader (Часть III): Коллекция рыночных ордеров и позиций, поиск и фильтрация Библиотека для простого и быстрого создания программ для MetaTrader (Часть III): Коллекция рыночных ордеров и позиций, поиск и фильтрация

В первой статье мы начали создавать большую кроссплатформенную библиотеку для легкого создания программ на платформах MetaTrader 5 и MetaTrader 4. Далее продолжили развитие библиотеки и сделали коллекцию исторических ордеров и сделок. Теперь создадим класс для удобного выбора и фильтрации ордеров, сделок и позиций в списках коллекций, а именно создадим базовый объект библиотеки — Engine, и добавим в библиотеку коллекцию рыночных ордеров и позиций.