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

Artyom Trishkin | 27 февраля, 2019

Содержание

Введение

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

Фактически, логику любой программы можно разбить на множество одинаковых для всех (ну или практически всех) действий, результат которых и используется для построения логики программ. Что, кстати, не раз подтверждалось и однотипностью вопросов на форумах по языку MQL4/MQL5 — от разных людей в разное время звучат по сути одни и те же вопросы об алгоритмах и задачах, ими решаемых.

Исходя из таких умозаключений у меня родилась идея сделать большую библиотеку, в которой будут встроенные функции для запроса и получения нужных данных. Для использования набора данных предлагаемой библиотеки, пользователю нужно всего лишь прибегнуть к методу "вопрос-ответ" для получения огромного количества совершенно разных данных — в разном их сочетании и любой сортировке, которые библиотека самостоятельно собирает и хранит в своей базе.

Концепция

Любой тип данных можно представить как совокупность объектов, наделённых одинаковыми свойствами.
Например, таймсерию можно представить в виде длинного упорядоченного списка, каждая последующая ячейка которого хранит объект, набор свойств которого является однотипным для всех и каждого последующего таких же объектов этой таймсерии. Данные такого объекта представлены структурой MqlRates:

Структура для хранения информации о ценах, объемах и спреде.

struct MqlRates 
  { 
   datetime time;         // время начала периода 
   double   open;         // цена открытия 
   double   high;         // наивысшая цена за период 
   double   low;          // наименьшая цена за период 
   double   close;        // цена закрытия 
   long     tick_volume;  // тиковый объем 
   int      spread;       // спред 
   long     real_volume;  // биржевой объем 
  };

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

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

struct MqlTick 
  { 
   datetime     time;          // Время последнего обновления цен 
   double       bid;           // Текущая цена Bid 
   double       ask;           // Текущая цена Ask 
   double       last;          // Текущая цена последней сделки (Last) 
   ulong        volume;        // Объем для текущей цены Last 
   long         time_msc;      // Время последнего обновления цен в миллисекундах 
   uint         flags;         // Флаги тиков 
   double       volume_real;   // Объем для текущей цены Last c повышенной точностью 
  };

И все остальные — любые данные, которые нужны для анализа и составления логики программы — будут так же организованы как простые списки объектов.

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

Структура данных библиотеки

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

Я планирую шаг за шагом, начиная с самого простого, постепенно добавляя новый функционал и новые данные к уже существующим, описать все шаги создания данной библиотеки. Библиотека будет разрабатываться "вживую", от версии к версии в каждой статье будут вноситься требуемые правки и дополнения. Такой стиль изложения по моему представлению наиболее полезен, так как даёт ощущение участия читателя в разработке.

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

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

Первая реализация

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

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

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

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

В директории <каталог данных терминала>\MQL5\Include  создайте папку DoEasy — в ней будем располагать файлы библиотеки. На данном этапе нам потребуется создать ещё два каталога в папке DoEasy: папка Objects — в неё будем размещать классы объектов, и папка Collections — в ней будут размещаться коллекции данных — списки объектов.
Чтобы найти каталог данных терминала можно воспользоваться пунктом меню редактора Файл -> Открыть каталог данных, или нажать сочетание клавиш Ctrl + Shift + D

Теперь можно создать первый класс — класс абстрактного ордера. Создайте в папке Objects новый класс и назовите его COrder (1). Не забудьте в качестве базового класса указать класс стандартной библиотеки CObject (2) — в этом случае наш новый класс объекта будет унаследован от класса CObject и его можно будет размещать в списке объектов CArrayObj стандартной библиотеки.


После нажатия на кнопку "Готово" в папке Objects каталога библиотеки появится новый файл — Order.mqh (3).  Пока что это просто заготовка класса:

//+------------------------------------------------------------------+
//|                                                        Order.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 COrder : public CObject
  {
private:

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

Если попробовать его скомпилировать, то получим пять ошибок. Все они указывают на то, что у нас нет класса CObject, потомком которого мы сделали класс COrder. Подключим к листингу файл класса CObject и скомпилируем ещё раз - теперь всё хорошо.

//+------------------------------------------------------------------+
//|                                                        Order.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 <Object.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:

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

Объект данного класса будет создаваться в цикле по ордерам, сделкам или позициям в тот момент, когда будет выбран очередной ордер, сделка или позиция. Чтобы сразу же при создании объекта инициализировать его поля, создадим закрытый конструктор, в который при создании будем передавать тип (статус) этого объекта и его тикет для его последующей идентификации. Но сначала создадим в корневой папке проекта новый включаемый файл 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"
//+------------------------------------------------------------------+
//| Тип (статус) абстрактного ордера                                 |
//+------------------------------------------------------------------+
enum ENUM_ORDER_STATUS
  {
   ORDER_STATUS_MARKET_PENDING,                             // Действующий отложенный ордер
   ORDER_STATUS_MARKET_ACTIVE,                              // Активный маркет-ордер
   ORDER_STATUS_HISTORY_ORDER,                              // Исторический маркет-ордер
   ORDER_STATUS_HISTORY_PENDING,                            // Удаленный отложенный ордер
   ORDER_STATUS_BALANCE,                                    // Балансная операция
   ORDER_STATUS_CREDIT,                                     // Кредитная операция
   ORDER_STATUS_DEAL,                                       // Сделка
   ORDER_STATUS_UNKNOWN                                     // Неизвестный статус
  };
//+------------------------------------------------------------------+
//| Целочисленные свойства ордера, сделки, позиции                   |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_INTEGER
  {
   ORDER_PROP_TICKET = 0,                                   // Тикет ордера
   ORDER_PROP_MAGIC,                                        // Магик ордера
   ORDER_PROP_TIME_OPEN,                                    // Время открытия
   ORDER_PROP_TIME_CLOSE,                                   // Время закрытия
   ORDER_PROP_TIME_EXP,                                     // Дата экспирации ордера (для отложенных ордеров)
   ORDER_PROP_TYPE,                                         // Тип ордера, сделки
   ORDER_PROP_STATUS,                                       // Статус ордера (из перечисления ENUM_ORDER_STATUS)
   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_OPEN_MSC,                                // Время открытия в милисекундах
   ORDER_PROP_TIME_CLOSE_MSC,                               // Время закрытия в милисекундах (Время исполнения или снятия - ORDER_TIME_DONE_MSC)
   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
   ORDER_PROP_DIRECTION,                                    // Тип по направлению (Buy, Sell)
  }; 
#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_PROFIT,                                       // Профит
   ORDER_PROP_COMMISSION,                                   // Комиссия
   ORDER_PROP_SWAP,                                         // Своп
   ORDER_PROP_VOLUME,                                       // Объём
   ORDER_PROP_VOLUME_CURRENT,                               // Невыполненный объем
   ORDER_PROP_SL,                                           // Цена StopLoss
   ORDER_PROP_TP,                                           // Цена TaleProfit
   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)                     // Общее количество строковых свойств
//+------------------------------------------------------------------+

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

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

Итак. После создания файла Defines.mqh, подключим его к текущему файлу класса COrder и создадим в приватной секции переменную-член класса для хранения тикета ордера и три массива для хранения свойств ордера — целочисленных, вещественных и строковых:

//+------------------------------------------------------------------+
//|                                                        Order.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 <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                 // Тикет выбранного ордера/сделки (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];    // Целочисленные свойства
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];   // Вещественные свойства
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];   // Строковые свойства
public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

Стоит отметить, что для подключения файла Defines.mqh мы использовали не угловые скобки (<>) как это делали при подключении CObject, а задали в кавычках относительный путь к файлу. Это сделано для того, чтобы при переносе библиотеки в любой другой каталог, связь между файлами не утерялась и всегда ссылалась на расположение файла Defines.mqh относительно текущего каталога.

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

//+------------------------------------------------------------------+
//|                                                        Order.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 <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| Класс абстрактного ордера                                        |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Тикет выбранного ордера/сделки (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Целочисленные свойства
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Вещественные свойства
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // Строковые свойства
   
   //--- Возвращает индекс массива, по которому фактически расположено double-свойство
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                  }
   //--- Возвращает индекс массива, по которому фактически расположено string-свойство
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- Конструктор по умолчанию
                     COrder(void){;}
protected:                                                                     
   //--- Защищённый параметрический конструктор                                
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//|                                                        Order.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 <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| Класс абстрактного ордера                                        |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Тикет выбранного ордера/сделки (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Целочисленные свойства
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Вещественные свойства
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // Строковые свойства
   
   //--- Возвращает индекс массива, по которому фактически расположено double-свойство
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                        }
   //--- Возвращает индекс массива, по которому фактически расположено string-свойство
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- Конструктор по умолчанию
                     COrder(void){;}
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              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;
   
   //--- Получает и возвращает вещественные свойства выбранного ордера из его параметров: (1) цену открытия, (2) цену закрытия, (3) профит,
   //---  (4) комиссию, (5) своп, (6) объём, (7) невыполненный объём (8) цену StopLoss, (9) цену TakeProfit (10) цену установки StopLimit-ордера
   double            OrderOpenPrice(void)          const;
   double            OrderClosePrice(void)         const;
   double            OrderProfit(void)             const;
   double            OrderCommission(void)         const;
   double            OrderSwap(void)               const;
   double            OrderVolume(void)             const;
   double            OrderVolumeCurrent(void)      const;
   double            OrderStopLoss(void)           const;
   double            OrderTakeProfit(void)         const;
   double            OrderPriceStopLimit(void)     const;
   
   //--- Получает и возвращает строковые свойства выбранного ордера из его параметров: (1) символ, (2) комментарий, (3) идентификатор на бирже
   string            OrderSymbol(void)             const;
   string            OrderComment(void)            const;
   string            OrderExternalID(void)         const;
   
//---
  };
//+------------------------------------------------------------------+
//| Закрытый параметрический конструктор                             |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//|                                                        Order.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 <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| Класс абстрактного ордера                                        |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Тикет выбранного ордера/сделки (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Целочисленные свойства
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Вещественные свойства
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // Строковые свойства
   
   //--- Возвращает индекс массива, по которому фактически расположено double-свойство
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                        }
   //--- Возвращает индекс массива, по которому фактически расположено string-свойство
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- Конструктор по умолчанию
                     COrder(void){;}
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              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;
   
   //--- Получает и возвращает вещественные свойства выбранного ордера из его параметров: (1) цену открытия, (2) цену закрытия, (3) профит,
   //---  (4) комиссию, (5) своп, (6) объём, (7) невыполненный объём (8) цену StopLoss, (9) цену TakeProfit (10) цену установки StopLimit-ордера
   double            OrderOpenPrice(void)          const;
   double            OrderClosePrice(void)         const;
   double            OrderProfit(void)             const;
   double            OrderCommission(void)         const;
   double            OrderSwap(void)               const;
   double            OrderVolume(void)             const;
   double            OrderVolumeCurrent(void)      const;
   double            OrderStopLoss(void)           const;
   double            OrderTakeProfit(void)         const;
   double            OrderPriceStopLimit(void)     const;
   
   //--- Получает и возвращает строковые свойства выбранного ордера из его параметров: (1) символ, (2) комментарий, (3) идентификатор на бирже
   string            OrderSymbol(void)             const;
   string            OrderComment(void)            const;
   string            OrderExternalID(void)         const;
   
public:
   //--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство ордера
   long              GetProperty(ENUM_ORDER_PROP_INTEGER property)      const { return m_long_prop[property];                    }
   double            GetProperty(ENUM_ORDER_PROP_DOUBLE property)       const { return m_double_prop[this.IndexProp(property)];  }
   string            GetProperty(ENUM_ORDER_PROP_STRING property)       const { return m_string_prop[this.IndexProp(property)];  }
   
   //--- Возвращает флаг поддержания ордером данного свойства
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property)        { return true; }
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property)         { return true; }
   virtual bool      SupportProperty(ENUM_ORDER_PROP_STRING property)         { return true; }
   
   //--- Сравнивает объекты COrder между собой по всем возможным свойствам
   virtual int       Compare(const CObject *node,const int mode=0) const;
   
//---
  };
//+------------------------------------------------------------------+
//| Закрытый параметрический конструктор                             |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

Реализация метода сравнения двух ордеров между собой по заданному свойству:

//+------------------------------------------------------------------+
//| Сравнивает объекты COrder между собой по всем возможным свойствам|
//+------------------------------------------------------------------+
int COrder::Compare(const CObject *node,const int mode=0) const
  {
   const COrder *order_compared=node;
//--- сравнение целочисленных свойств двух ордеров
   if(mode<ORDER_PROP_INTEGER_TOTAL)
     {
      long value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_ORDER_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- сравнение вещественных свойств двух ордеров
   else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL)
     {
      double value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_ORDER_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- сравнение строковых свойств двух ордеров
   else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_STRING_TOTAL)
     {
      string value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_ORDER_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

В метод передаётся указатель на объект-ордер, свойство которого нужно сравнить с неким заданным значением, и само значение из перечисления свойств ордера.
Далее всё просто: если значение ордера больше значения, с которым сравниваем, то возвращается 1, иначе, если меньше, то возвращается -1, иначе - возвращается 0. Список ордеров, в котором проводится сравнение должен быть предварительно отсортирован по сравниваемому свойству.

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

//+------------------------------------------------------------------+
//| Возвращает магик                                                 |
//+------------------------------------------------------------------+
long COrder::OrderMagicNumber() const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_MAGIC);           break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_MAGIC);                 break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_MAGIC);   break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_MAGIC); break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+

Если это код для MQL4, то возвращается магик mql4-функцией OrderMagicNumber(). Иначе, проверяем статус ордера и в зависимости от того, с чем именно имеем дело, возвращаем либо магик позиции, либо магик ордера, либо магик сделки.

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

Методы получения целочисленных свойств ордера/сделки/позиции:
//+------------------------------------------------------------------+
//| Возвращает тикет                                                 |
//+------------------------------------------------------------------+
long COrder::OrderTicket(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     :
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     :
      case ORDER_STATUS_DEAL              : res=(long)m_ticket;                                 break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Возвращает тикет родительского ордера                            |
//+------------------------------------------------------------------+
long COrder::OrderTicketFrom(void) const
  {
   long ticket=0;
#ifdef __MQL4__
   string order_comment=::OrderComment();
   if(::StringFind(order_comment,"from #")>WRONG_VALUE) ticket=::StringToInteger(::StringSubstr(order_comment,6));
#endif
   return ticket;
  }
//+------------------------------------------------------------------+
//| Возвращает тикет дочернего ордера                                |
//+------------------------------------------------------------------+
long COrder::OrderTicketTo(void) const
  {
   long ticket=0;
#ifdef __MQL4__
   string order_comment=::OrderComment();
   if(::StringFind(order_comment,"to #")>WRONG_VALUE) ticket=::StringToInteger(::StringSubstr(order_comment,4));
#endif
   return ticket;
  }
//+------------------------------------------------------------------+
//| Возвращает идентификатор позиции                                 |
//+------------------------------------------------------------------+
long COrder::OrderPositionID(void) const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_IDENTIFIER);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_ID); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_POSITION_ID);   break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Возвращает идентификатор встречной позиции                       |
//+------------------------------------------------------------------+
long COrder::OrderPositionByID(void) const
  {
#ifdef __MQL4__
   return 0;
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_BY_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_BY_ID); break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Возвращает время открытия в милисекундах                         |
//+------------------------------------------------------------------+
long COrder::OrderOpenTimeMSC(void) const
  {
#ifdef __MQL4__
   return (long)::OrderOpenTime();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_TIME_MSC);                 break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TIME_SETUP_MSC);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TIME_SETUP_MSC); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_TIME_MSC);         break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Возвращает время закрытия в милисекундах                         |
//+------------------------------------------------------------------+
long COrder::OrderCloseTimeMSC(void) const
  {
#ifdef __MQL4__
   return (long)::OrderCloseTime();
#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_TIME_DONE_MSC);     break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME_MSC);  break;
      default                             : res=0;                                                          break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Возвращает тип                                                   |
//+------------------------------------------------------------------+
long COrder::OrderType(void) const
  {
#ifdef __MQL4__
   return (long)::OrderType();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_TYPE);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_TYPE);    break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Возвращает тип по направлению                                    |
//+------------------------------------------------------------------+
long COrder::OrderTypeByDirection(void) const
  {
   ENUM_ORDER_STATUS status=(ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);
   if(status==ORDER_STATUS_MARKET_ACTIVE)
     {
      return(this.OrderType()==POSITION_TYPE_BUY ? ORDER_TYPE_BUY : ORDER_TYPE_SELL);
     }
   if(status==ORDER_STATUS_MARKET_PENDING || status==ORDER_STATUS_HISTORY_PENDING)
     {
      return
        (
         this.OrderType()==ORDER_TYPE_BUY_LIMIT || 
         this.OrderType()==ORDER_TYPE_BUY_STOP 
         #ifdef __MQL5__                        ||
         this.OrderType()==ORDER_TYPE_BUY_STOP_LIMIT 
         #endif ? 
         ORDER_TYPE_BUY : 
         ORDER_TYPE_SELL
        );
     }
   if(status==ORDER_STATUS_HISTORY_ORDER)
     {
      return this.OrderType();
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+
//| Возвращает тип исполнения по остатку                             |
//+------------------------------------------------------------------+
long COrder::OrderTypeFilling(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_FILLING_RETURN;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_FILLING);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_FILLING);break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает время жизни ордера                                    |
//+------------------------------------------------------------------+
long COrder::OrderTypeTime(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_TIME_GTC;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_TIME);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_TIME);break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Причина или источник выставления ордера                          |
//+------------------------------------------------------------------+
long COrder::OrderReason(void) const
  {
#ifdef __MQL4__
   return
     (
      this.OrderCloseByStopLoss()   ?  ORDER_REASON_SL      :
      this.OrderCloseByTakeProfit() ?  ORDER_REASON_TP      :  
      this.OrderMagicNumber()!=0    ?  ORDER_REASON_EXPERT  : WRONG_VALUE
     );
#else 
   long res=WRONG_VALUE;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_REASON);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_REASON);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_REASON);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_REASON);  break;
      default                             : res=WRONG_VALUE;                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Ордер, на основании которого выполнена сделка                    |
//+------------------------------------------------------------------+
long COrder::DealOrder(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_IDENTIFIER);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_ID); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_ORDER);         break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Направление сделки IN, OUT, IN/OUT                               |
//+------------------------------------------------------------------+
long COrder::DealEntry(void) const
  {
#ifdef __MQL4__
   return ::OrderType();
#else 
   long res=WRONG_VALUE;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_DEAL  : res=::HistoryDealGetInteger(m_ticket,DEAL_ENTRY);break;
      default                 : res=WRONG_VALUE;                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает флаг закрытия позиции по StopLoss                     |
//+------------------------------------------------------------------+
bool COrder::OrderCloseByStopLoss(void) const
  {
#ifdef __MQL4__
   return(::StringFind(::OrderComment(),"[sl")>WRONG_VALUE);
#else 
   return
     (
      this.Status()==ORDER_STATUS_HISTORY_ORDER ? this.OrderReason()==ORDER_REASON_SL : 
      this.Status()==ORDER_STATUS_DEAL ? this.OrderReason()==DEAL_REASON_SL : false
     );
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает флаг закрытия позиции по TakeProfit                   |
//+------------------------------------------------------------------+
bool COrder::OrderCloseByTakeProfit(void) const
  {
#ifdef __MQL4__
   return(::StringFind(::OrderComment(),"[tp")>WRONG_VALUE);
#else 
   return
     (
      this.Status()==ORDER_STATUS_HISTORY_ORDER ? this.OrderReason()==ORDER_REASON_TP : 
      this.Status()==ORDER_STATUS_DEAL ? this.OrderReason()==DEAL_REASON_TP : false
     );
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает время открытия                                        |
//+------------------------------------------------------------------+
datetime COrder::OrderOpenTime(void) const
  {
#ifdef __MQL4__
   return ::OrderOpenTime();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=(datetime)::PositionGetInteger(POSITION_TIME);                 break;
      case ORDER_STATUS_MARKET_PENDING    : res=(datetime)::OrderGetInteger(ORDER_TIME_SETUP);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_SETUP); break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME);         break;
      default                             : res=0;                                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает время закрытия                                        |
//+------------------------------------------------------------------+
datetime COrder::OrderCloseTime(void) const
  {
#ifdef __MQL4__
   return ::OrderCloseTime();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_DONE);  break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME);         break;
      default                             : res=0;                                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает время экспирации                                      |
//+------------------------------------------------------------------+
datetime COrder::OrderExpiration(void) const
  {
#ifdef __MQL4__
   return ::OrderExpiration();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=(datetime)::OrderGetInteger(ORDER_TIME_EXPIRATION);                  break;
      case ORDER_STATUS_HISTORY_PENDING   : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_EXPIRATION);  break;
      default                             : res=0;                                                                   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Время изменения позиции в секундах                               |
//+------------------------------------------------------------------+
datetime COrder::PositionTimeUpdate(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=(datetime)::PositionGetInteger(POSITION_TIME_UPDATE); break;
      default                          : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Время изменения позиции в милисекундах                           |
//+------------------------------------------------------------------+
datetime COrder::PositionTimeUpdateMSC(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=(datetime)::PositionGetInteger(POSITION_TIME_UPDATE_MSC);break;
      default                          : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Методы получения вещественных свойств ордера/сделки/позиции:

//+------------------------------------------------------------------+
//| Возвращает цену открытия                                         |
//+------------------------------------------------------------------+
double COrder::OrderOpenPrice(void) const
  {
#ifdef __MQL4__
   return ::OrderOpenPrice();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_PRICE_OPEN);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_PRICE_OPEN);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_OPEN);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_PRICE);       break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает цену закрытия                                         |
//+------------------------------------------------------------------+
double COrder::OrderClosePrice(void) const
  {
#ifdef __MQL4__
   return ::OrderClosePrice();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_OPEN);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_PRICE);       break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает профит                                                |
//+------------------------------------------------------------------+
double COrder::OrderProfit(void) const
  {
#ifdef __MQL4__
   return ::OrderProfit();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=::PositionGetDouble(POSITION_PROFIT);        break;
      case ORDER_STATUS_DEAL           : res=::HistoryDealGetDouble(m_ticket,DEAL_PROFIT);break;
      default                          : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает комиссию                                              |
//+------------------------------------------------------------------+
double COrder::OrderCommission(void) const
  {
#ifdef __MQL4__
   return ::OrderCommission();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_DEAL  : res=::HistoryDealGetDouble(m_ticket,DEAL_COMMISSION);  break;
      default                 : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает своп                                                  |
//+------------------------------------------------------------------+
double COrder::OrderSwap(void) const
  {
#ifdef __MQL4__
   return ::OrderSwap();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=::PositionGetDouble(POSITION_SWAP);          break;
      case ORDER_STATUS_DEAL           : res=::HistoryDealGetDouble(m_ticket,DEAL_SWAP);  break;
      default                          : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает объём                                                 |
//+------------------------------------------------------------------+
double COrder::OrderVolume(void) const
  {
#ifdef __MQL4__
   return ::OrderLots();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_VOLUME);                    break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_VOLUME_INITIAL);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_VOLUME_INITIAL);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_VOLUME);            break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает невыполненный объем                                   |
//+------------------------------------------------------------------+
double COrder::OrderVolumeCurrent(void) const
  {
#ifdef __MQL4__
   return ::OrderLots();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_VOLUME_CURRENT);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_VOLUME_CURRENT);  break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает цену StopLoss                                         |
//+------------------------------------------------------------------+
double COrder::OrderStopLoss(void) const
  {
#ifdef __MQL4__
   return ::OrderStopLoss();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_SL);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_SL);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_SL);  break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает цену TakeProfit                                       |
//+------------------------------------------------------------------+
double COrder::OrderTakeProfit(void) const
  {
#ifdef __MQL4__
   return ::OrderTakeProfit();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_TP);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_TP);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_TP);  break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает цену постановки Limit ордера                          |
//| при срабатывании StopLimit ордера                                |
//+------------------------------------------------------------------+
double COrder::OrderPriceStopLimit(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_PRICE_STOPLIMIT);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_STOPLIMIT); break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Методы получения строковых свойств ордера/сделки/позиции:

//+------------------------------------------------------------------+
//| Возвращает символ                                                |
//+------------------------------------------------------------------+
string COrder::OrderSymbol(void) const
  {
#ifdef __MQL4__
   return ::OrderSymbol();
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetString(POSITION_SYMBOL);           break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_SYMBOL);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_SYMBOL); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_SYMBOL);   break;
      default                             : res="";                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает комментарий                                           |
//+------------------------------------------------------------------+
string COrder::OrderComment(void) const
  {
#ifdef __MQL4__
   return ::OrderComment();
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetString(POSITION_COMMENT);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_COMMENT);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_COMMENT);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_COMMENT);  break;
      default                             : res="";                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Возвращает идентификатор на бирже                                |
//+------------------------------------------------------------------+
string COrder::OrderExternalID(void) const
  {
#ifdef __MQL4__
   return "";
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_EXTERNAL_ID);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_EXTERNAL_ID);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_EXTERNAL_ID);    break;
      default                             : res="";                                                   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Приватные методы получения свойств из данных ордера объявили и реализовали.

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

Реализация защищённого конструктора класса:

//+------------------------------------------------------------------+
//| Закрытый параметрический конструктор                             |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
//--- Сохранение целочисленных свойств
   m_ticket=ticket;
   m_long_prop[ORDER_PROP_STATUS]                              = order_status;
   m_long_prop[ORDER_PROP_MAGIC]                               = this.OrderMagicNumber();
   m_long_prop[ORDER_PROP_TICKET]                              = this.OrderTicket();
   m_long_prop[ORDER_PROP_TIME_OPEN]                           = (long)(ulong)this.OrderOpenTime();
   m_long_prop[ORDER_PROP_TIME_CLOSE]                          = (long)(ulong)this.OrderCloseTime();
   m_long_prop[ORDER_PROP_TIME_EXP]                            = (long)(ulong)this.OrderExpiration();
   m_long_prop[ORDER_PROP_TYPE]                                = this.OrderType();
   m_long_prop[ORDER_PROP_DIRECTION]                           = this.OrderTypeByDirection();
   m_long_prop[ORDER_PROP_POSITION_ID]                         = this.OrderPositionID();
   m_long_prop[ORDER_PROP_REASON]                              = this.OrderReason();
   m_long_prop[ORDER_PROP_DEAL_ORDER]                          = this.DealOrder();
   m_long_prop[ORDER_PROP_DEAL_ENTRY]                          = this.DealEntry();
   m_long_prop[ORDER_PROP_POSITION_BY_ID]                      = this.OrderPositionByID();
   m_long_prop[ORDER_PROP_TIME_OPEN_MSC]                       = this.OrderOpenTimeMSC();
   m_long_prop[ORDER_PROP_TIME_CLOSE_MSC]                      = this.OrderCloseTimeMSC();
   m_long_prop[ORDER_PROP_TIME_UPDATE]                         = (long)(ulong)this.PositionTimeUpdate();
   m_long_prop[ORDER_PROP_TIME_UPDATE_MSC]                     = (long)(ulong)this.PositionTimeUpdateMSC();
   
//--- Сохранение вещественных свойств
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)]        = this.OrderOpenPrice();
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)]       = this.OrderClosePrice();
   m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)]            = this.OrderProfit();
   m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)]        = this.OrderCommission();
   m_double_prop[this.IndexProp(ORDER_PROP_SWAP)]              = this.OrderSwap();
   m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)]            = this.OrderVolume();
   m_double_prop[this.IndexProp(ORDER_PROP_SL)]                = this.OrderStopLoss();
   m_double_prop[this.IndexProp(ORDER_PROP_TP)]                = this.OrderTakeProfit();
   m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)]    = this.OrderVolumeCurrent();
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)]  = this.OrderPriceStopLimit();
   
//--- Сохранение строковых свойств
   m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)]            = this.OrderSymbol();
   m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)]           = this.OrderComment();
   m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)]            = this.OrderExternalID();
   
//--- Сохранение дополнительных целочисленных свойств
   m_long_prop[ORDER_PROP_PROFIT_PT]                           = this.ProfitInPoints();
   m_long_prop[ORDER_PROP_TICKET_FROM]                         = this.OrderTicketFrom();
   m_long_prop[ORDER_PROP_TICKET_TO]                           = this.OrderTicketTo();
   m_long_prop[ORDER_PROP_CLOSE_BY_SL]                         = this.OrderCloseByStopLoss();
   m_long_prop[ORDER_PROP_CLOSE_BY_TP]                         = this.OrderCloseByTakeProfit();
   
//--- Сохранение дополнительных вещественных свойств
   m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)]       = this.ProfitFull();
  }
//+------------------------------------------------------------------+

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

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

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

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

Данные методы можно будет использовать в своих программах для чтения свойств выбранного ордера, сделки или позиции:
   //--- Возвращает (1) тикет, (2) тикет родительского ордера, (3) тикет дочернего ордера, (4) магик, (5) причину выставления ордера (6) идентификатор позиции
   //--- (7) идентификатор встречной позиции, (8) флаг закрытия по StopLoss, (9) флаг закрытия по TakeProfit (10) время открытия, (11) время закрытия,
   //--- (12) время открытия в милисекундах, (13) время закрытия в милисекундах (14) дату экспирации, (15) тип, (16) статус, (17) тип ордера по направлению
   long              Ticket(void)                                       const { return this.GetProperty(ORDER_PROP_TICKET);                     }
   long              TicketFrom(void)                                   const { return this.GetProperty(ORDER_PROP_TICKET_FROM);                }
   long              TicketTo(void)                                     const { return this.GetProperty(ORDER_PROP_TICKET_TO);                  }
   long              Magic(void)                                        const { return this.GetProperty(ORDER_PROP_MAGIC);                      }
   long              Reason(void)                                       const { return this.GetProperty(ORDER_PROP_REASON);                     }
   long              PositionID(void)                                   const { return this.GetProperty(ORDER_PROP_POSITION_ID);                }
   long              PositionByID(void)                                 const { return this.GetProperty(ORDER_PROP_POSITION_BY_ID);             }
   bool              IsCloseByStopLoss(void)                            const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_SL);          }
   bool              IsCloseByTakeProfit(void)                          const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_TP);          }
   datetime          TimeOpen(void)                                     const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN);        }
   datetime          TimeClose(void)                                    const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE);       }
   datetime          TimeOpenMSC(void)                                  const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN_MSC);    }
   datetime          TimeCloseMSC(void)                                 const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE_MSC);   }
   datetime          TimeExpiration(void)                               const { return (datetime)this.GetProperty(ORDER_PROP_TIME_EXP);         }
   ENUM_ORDER_TYPE   TypeOrder(void)                                    const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_TYPE);      }
   ENUM_ORDER_STATUS Status(void)                                       const { return (ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);  }
   ENUM_ORDER_TYPE   TypeByDirection(void)                              const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_DIRECTION); }
   
   //--- Возвращает (1) цену открытия, (2) цену закрытия, (3) профит, (4) комиссию, (5) своп, (6) объём, 
   //--- (7) невыполненный объём (8) StopLoss и (9) TakeProfit (10) цену установки StopLimit-ордера
   double            PriceOpen(void)                                    const { return this.GetProperty(ORDER_PROP_PRICE_OPEN);                 }
   double            PriceClose(void)                                   const { return this.GetProperty(ORDER_PROP_PRICE_CLOSE);                }
   double            Profit(void)                                       const { return this.GetProperty(ORDER_PROP_PROFIT);                     }
   double            Comission(void)                                    const { return this.GetProperty(ORDER_PROP_COMMISSION);                 }
   double            Swap(void)                                         const { return this.GetProperty(ORDER_PROP_SWAP);                       }
   double            Volume(void)                                       const { return this.GetProperty(ORDER_PROP_VOLUME);                     }
   double            VolumeCurrent(void)                                const { return this.GetProperty(ORDER_PROP_VOLUME_CURRENT);             }
   double            StopLoss(void)                                     const { return this.GetProperty(ORDER_PROP_SL);                         }
   double            TakeProfit(void)                                   const { return this.GetProperty(ORDER_PROP_TP);                         }
   double            PriceStopLimit(void)                               const { return this.GetProperty(ORDER_PROP_PRICE_STOP_LIMIT);           }
   
   //--- Возвращает (1) символ, (2) комментарий, (3) идентификатор на бирже
   string            Symbol(void)                                       const { return this.GetProperty(ORDER_PROP_SYMBOL);                     }
   string            Comment(void)                                      const { return this.GetProperty(ORDER_PROP_COMMENT);                    }
   string            ExternalID(void)                                   const { return this.GetProperty(ORDER_PROP_EXT_ID);                     }

   //--- Возвращает полный профит ордера
   double            ProfitFull(void)                                   const { return this.Profit()+this.Comission()+this.Swap();              }
   //--- Возвращает профит ордера в пунктах
   int               ProfitInPoints(void) const;

Реализация метода получения профита в пунктах:

//+------------------------------------------------------------------+
//| Возвращает профит ордера в пунктах                               |
//+------------------------------------------------------------------+
int COrder::ProfitInPoints(void) const
  {
   ENUM_ORDER_TYPE type=this.TypeOrder();
   string symbol=this.Symbol();
   double point=::SymbolInfoDouble(symbol,SYMBOL_POINT);
   if(type>ORDER_TYPE_SELL || point==0) return 0;
   if(this.Status()==ORDER_STATUS_HISTORY_ORDER)
      return int(type==ORDER_TYPE_BUY ? (this.PriceClose()-this.PriceOpen())/point : type==ORDER_TYPE_SELL ? (this.PriceOpen()-this.PriceClose())/point : 0);
   else if(this.Status()==ORDER_STATUS_MARKET_ACTIVE)
     {
      if(type==ORDER_TYPE_BUY)
         return int((::SymbolInfoDouble(symbol,SYMBOL_BID)-this.PriceOpen())/point);
      else if(type==ORDER_TYPE_SELL)
         return int((this.PriceOpen()-::SymbolInfoDouble(symbol,SYMBOL_ASK))/point);
     }
   return 0;
  }
//+------------------------------------------------------------------+

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

   //--- Возвращает описание (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            DealEntryDescription(void) const;
   //--- Возвращает тип по направлению ордера/позиции
   string            DirectionDescription(void) const;
   //--- Выводит в журнал описание свойств ордера (full_prop=true - все свойства, false - только поддерживаемые)
   void              Print(const bool full_prop=false);

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

Создадим в корневой папке библиотеки новый включаемый файл

и назовём его DELib. Это будет файл библиотеки сервисных функций, которые будут доступны для использования как самими классами библиотеки, так и программами на основе библиотеки.

После нажатия кнопки "Готово" будет создан файл-шаблон:

//+------------------------------------------------------------------+
//|                                                        DELib.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"
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
// #define MacrosHello   "Hello, world!"
// #define MacrosYear    2010
//+------------------------------------------------------------------+
//| DLL imports                                                      |
//+------------------------------------------------------------------+
// #import "user32.dll"
//   int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
// #import "my_expert.dll"
//   int      ExpertRecalculate(int wParam,int lParam);
// #import
//+------------------------------------------------------------------+
//| EX5 imports                                                      |
//+------------------------------------------------------------------+
// #import "stdlib.ex5"
//   string ErrorDescription(int error_code);
// #import
//+------------------------------------------------------------------+

Сразу подключим к нему наш файл Defines.mqh и видоизменим шаблон под наши нужды:

//+------------------------------------------------------------------+
//|                                                        DELib.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 strict  // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Сервисные функции                                                |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+

Так как мы к этому файлу подключили наш файл Defines.mqh, то теперь можно в файле класса COrder подключить не Defines.mqh, а этот новый файл, чтобы они оба были доступны в библиотеке, и сделаем это одной строчкой, а не двумя.
Заменим директиву подключения в файле Order.mqh
:

//+------------------------------------------------------------------+
//|                                                        Order.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 <Object.mqh>
#include "..\DELib.mqh"
//+------------------------------------------------------------------+
//| Класс абстрактного ордера                                        |
//+------------------------------------------------------------------+

Давайте ещё добавим определение языка страны пользователя как макроподстановку в файле 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"
//+------------------------------------------------------------------+

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

В файл DELib.mqh впишем функцию возврата сообщений на одном из двух языков:

//+------------------------------------------------------------------+
//|                                                        DELib.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 strict  // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Сервисные функции                                                |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Возвращает текст на одном из двух языков                         |
//+------------------------------------------------------------------+
string TextByLanguage(const string text_country_lang,const string text_en)
  {
   return(TerminalInfoString(TERMINAL_LANGUAGE)==COUNTRY_LANG ? text_country_lang : text_en);
  }
//+------------------------------------------------------------------+

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

И добавим функцию вывода времени с милисекундами:

//+------------------------------------------------------------------+
//|                                                        DELib.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 strict  // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Сервисные функции                                                |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Возвращает текст на одном из двух языков                         |
//+------------------------------------------------------------------+
string TextByLanguage(const string text_country_lang,const string text_en)
  {
   return(TerminalInfoString(TERMINAL_LANGUAGE)==COUNTRY_LANG ? text_country_lang : text_en);
  }
//+------------------------------------------------------------------+
//| Возвращает время с милисекундами                                 |
//+------------------------------------------------------------------+
string TimeMSCtoString(const long time_msc)
  {
   return TimeToString(time_msc/1000,TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"."+IntegerToString(time_msc%1000,3,'0');
  }
//+------------------------------------------------------------------+

Здесь всё просто: в функцию передаётся время, заданное в милисекундах. Для вычисления времени в секундах нужно разделить переданное в функцию значение на 1000, а для вычисления милисекунд нужно взять остаток от этого деления. Все полученные значения оформляются в виде строки и возвращаются в вызывающую программу.

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

//+------------------------------------------------------------------+
//| Возвращает количество десятичных знаков в лоте символа           |
//+------------------------------------------------------------------+
uint DigitsLots(const string symbol_name) 
  { 
   return (int)ceil(fabs(log(SymbolInfoDouble(symbol_name,SYMBOL_VOLUME_STEP))/log(10)));
  }
//+------------------------------------------------------------------+

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

   //--- Возвращает описание (1) причины, (2) направления, (3) типа сделки
   string            GetReasonDescription(const long reason)            const;
   string            GetEntryDescription(const long deal_entry)         const;
   string            GetTypeDealDescription(const long type_deal)       const;

И их реализацию:

//+------------------------------------------------------------------+
//| Описание причины                                                 |
//+------------------------------------------------------------------+
string COrder::GetReasonDescription(const long reason) const
  {
#ifdef __MQL4__
   return
     (
      this.IsCloseByStopLoss()            ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss")                  :
      this.IsCloseByTakeProfit()          ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit")              :
      this.Reason()==ORDER_REASON_EXPERT  ?  TextByLanguage("Выставлен из mql4-программы","Placed from mql4 program")   :
      TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4")
     );
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE        : 
         res=
           (
            reason==POSITION_REASON_CLIENT   ?  TextByLanguage("Позиция открыта из десктопного терминала","Position is open from the desktop terminal") :
            reason==POSITION_REASON_MOBILE   ?  TextByLanguage("Позиция открыта из мобильного приложения","Position is open from the mobile app") :
            reason==POSITION_REASON_WEB      ?  TextByLanguage("Позиция открыта из веб-платформы","Position is open from the web platform") :
            reason==POSITION_REASON_EXPERT   ?  TextByLanguage("Позиция открыта из советника или скрипта","The position is open from the EA or script") : ""
           );
         break;
      case ORDER_STATUS_MARKET_PENDING       :
      case ORDER_STATUS_HISTORY_PENDING      : 
      case ORDER_STATUS_HISTORY_ORDER        : 
         res=
           (
            reason==ORDER_REASON_CLIENT      ?  TextByLanguage("Ордер выставлен из десктопного терминала","Order is set from the desktop terminal") :
            reason==ORDER_REASON_MOBILE      ?  TextByLanguage("Ордер выставлен из мобильного приложения","Order is set from the mobile app") :
            reason==ORDER_REASON_WEB         ?  TextByLanguage("Ордер выставлен из веб-платформы","Oder is set from the web platform") :
            reason==ORDER_REASON_EXPERT      ?  TextByLanguage("Ордер выставлен советником или скриптом","Order is set from the EA or script") :
            reason==ORDER_REASON_SL          ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss") :
            reason==ORDER_REASON_TP          ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit") :
            reason==ORDER_REASON_SO          ?  TextByLanguage("Ордер выставлен в результате наступления Stop Out","Due to Stop Out") : ""
           );
         break;
      case ORDER_STATUS_DEAL                 : 
         res=
           (
            reason==DEAL_REASON_CLIENT       ?  TextByLanguage("Сделка проведена из десктопного терминала","Deal was carried out from the desktop terminal") :
            reason==DEAL_REASON_MOBILE       ?  TextByLanguage("Сделка проведена из мобильного приложения","Deal was carried out from the mobile app") :
            reason==DEAL_REASON_WEB          ?  TextByLanguage("Сделка проведена из веб-платформы","Deal was carried out from the web platform") :
            reason==DEAL_REASON_EXPERT       ?  TextByLanguage("Сделка проведена из советника или скрипта","Deal was carried out from the EA or script") :
            reason==DEAL_REASON_SL           ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss") :
            reason==DEAL_REASON_TP           ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit") :
            reason==DEAL_REASON_SO           ?  TextByLanguage("Сделка проведена в результате наступления Stop Out","Due to Stop Out") :
            reason==DEAL_REASON_ROLLOVER     ?  TextByLanguage("Сделка проведена по причине переноса позиции","Due to position rollover") :
            reason==DEAL_REASON_VMARGIN      ?  TextByLanguage("Сделка проведена по причине начисления/списания вариационной маржи","Due to variation margin") :
            reason==DEAL_REASON_SPLIT        ?  TextByLanguage("Сделка проведена по причине сплита (понижения цены) инструмента","Due to the split") : ""
           );
         break;
      default                                : res="";   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Описание направления сделки                                      |
//+------------------------------------------------------------------+
string COrder::GetEntryDescription(const long deal_entry) const
  {
#ifdef __MQL4__
   return TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4");
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE     : 
         res=TextByLanguage("Свойство не поддерживается у позиции","Property not supported for position"); 
         break;
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   : 
         res=TextByLanguage("Свойство не поддерживается у отложенного ордера","The property is not supported for a pending order"); 
         break;
      case ORDER_STATUS_HISTORY_ORDER     : 
         res=TextByLanguage("Свойство не поддерживается у исторического ордера","The property is not supported for a history order"); 
         break;
      case ORDER_STATUS_DEAL              : 
         res=
           (
            deal_entry==DEAL_ENTRY_IN     ?  TextByLanguage("Вход в рынок","Entry to the market") :
            deal_entry==DEAL_ENTRY_OUT    ?  TextByLanguage("Выход из рынка","Out from the market") :
            deal_entry==DEAL_ENTRY_INOUT  ?  TextByLanguage("Разворот","Turning in the opposite direction") :
            deal_entry==DEAL_ENTRY_OUT_BY ?  TextByLanguage("Закрытие встречной позицией","Closing the opposite position") : ""
           );
         break;
      default                             : res=""; break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Проверяется принадлежность к MQL4 или MQL5 и, в соответствии со статусом ордера, проверяется направление сделки, переданное во входных параметрах, и возвращается его описание.

//+------------------------------------------------------------------+
//| Возвращает наименование типа сделки                              |
//+------------------------------------------------------------------+
string COrder::GetTypeDealDescription(const long deal_type) const
  {
#ifdef __MQL4__
   return TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4");
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE     : 
         res=TextByLanguage("Свойство не поддерживается у позиции","Property not supported for position"); 
         break;
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   : 
         res=TextByLanguage("Свойство не поддерживается у отложенного ордера","The property is not supported for a pending order"); 
         break;
      case ORDER_STATUS_HISTORY_ORDER     : 
         res=TextByLanguage("Свойство не поддерживается у исторического ордера","The property is not supported for a history order"); 
         break;
      case ORDER_STATUS_DEAL              : 
         res=
           (
            deal_type==DEAL_TYPE_BUY                      ?  TextByLanguage("Сделка на покупку","The deal to buy") :
            deal_type==DEAL_TYPE_SELL                     ?  TextByLanguage("Сделка на продажу","The deal to sell") :
            deal_type==DEAL_TYPE_BALANCE                  ?  TextByLanguage("Начисление баланса","Balance accrual") :
            deal_type==DEAL_TYPE_CREDIT                   ?  TextByLanguage("Начисление кредита","Credit accrual") :
            deal_type==DEAL_TYPE_CHARGE                   ?  TextByLanguage("Дополнительные сборы","Extra charges") :
            deal_type==DEAL_TYPE_CORRECTION               ?  TextByLanguage("Корректирующая запись","Corrective entry") :
            deal_type==DEAL_TYPE_BONUS                    ?  TextByLanguage("Перечисление бонусов","Bonuses") :
            deal_type==DEAL_TYPE_COMMISSION               ?  TextByLanguage("Дополнительные комиссии","Additional comissions") :
            deal_type==DEAL_TYPE_COMMISSION_DAILY         ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Commission, accrued at the end of the trading day") :
            deal_type==DEAL_TYPE_COMMISSION_MONTHLY       ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Commission, accrued at the end of the month") :
            deal_type==DEAL_TYPE_COMMISSION_AGENT_DAILY   ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Agency commission charged at the end of the trading day") :
            deal_type==DEAL_TYPE_COMMISSION_AGENT_MONTHLY ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","") :
            deal_type==DEAL_TYPE_INTEREST                 ?  TextByLanguage("Начисления процентов на свободные средства","Agency commission charged at the end of the month") :
            deal_type==DEAL_TYPE_BUY_CANCELED             ?  TextByLanguage("Отмененная сделка покупки","Canceled buy transaction") :
            deal_type==DEAL_TYPE_SELL_CANCELED            ?  TextByLanguage("Отмененная сделка продажи","Canceled sell transaction") :
            deal_type==DEAL_DIVIDEND                      ?  TextByLanguage("Начисление дивиденда","Accrued dividends") :
            deal_type==DEAL_DIVIDEND_FRANKED              ?  TextByLanguage("Начисление франкированного дивиденда","Accrual of franked dividend") :
            deal_type==DEAL_TAX                           ?  TextByLanguage("Начисление налога","Tax accrual") : ""
           );
         break;
      default                             : res=""; break;
     }
   return res;
#endif 
  }

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

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

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства ордера               |
//+------------------------------------------------------------------+
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_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"))
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Возвращает описание вещественного свойства ордера                |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_DOUBLE property)
  {
   int dg=(int)::SymbolInfoInteger(this.GetProperty(ORDER_PROP_SYMBOL),SYMBOL_DIGITS);
   int dgl=(int)DigitsLots(this.GetProperty(ORDER_PROP_SYMBOL));
   return
     (
      //--- Общие свойства
      property==ORDER_PROP_PRICE_CLOSE       ?  TextByLanguage("Цена закрытия","Price close")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==ORDER_PROP_PRICE_OPEN        ?  TextByLanguage("Цена открытия","Price open")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==ORDER_PROP_SL                ?  TextByLanguage("Цена StopLoss","Price StopLoss")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Отсутствует",": Not set"):": "+::DoubleToString(this.GetProperty(property),dg))
         )  :
      property==ORDER_PROP_TP                ?  TextByLanguage("Цена TakeProfit","Price TakeProfit")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Отсутствует",": Not set"):": "+::DoubleToString(this.GetProperty(property),dg))
         )  :
      property==ORDER_PROP_PROFIT            ?  TextByLanguage("Прибыль","Profit")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_COMMISSION        ?  TextByLanguage("Комиссия","Comission")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_SWAP              ?  TextByLanguage("Своп","Swap")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_VOLUME            ?  TextByLanguage("Объём","Volume")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),dgl)
          ) :
      property==ORDER_PROP_VOLUME_CURRENT    ?  TextByLanguage("Невыполненный объём","Unfulfilled volume")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),dgl)
          ) :
      property==ORDER_PROP_PRICE_STOP_LIMIT  ? 
         TextByLanguage("Цена постановки Limit ордера при активации StopLimit ордера","The price of placing Limit order when StopLimit order are activated")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
          ) :
      //--- Дополнительное свойство
      property==ORDER_PROP_PROFIT_FULL       ?  TextByLanguage("Прибыль+комиссия+своп","Profit+Comission+Swap")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+::DoubleToString(this.GetProperty(property),2)
          ) :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Возвращает описание строкового свойства ордера                   |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_STRING property)
  {
   return
     (
      property==ORDER_PROP_SYMBOL         ?  TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\""            :
      property==ORDER_PROP_COMMENT        ?  TextByLanguage("Комментарий","Comment")+
         (this.GetProperty(property)==""  ?  TextByLanguage(": Отсутствует",": Not set"):": \""+this.GetProperty(property)+"\"") :
      property==ORDER_PROP_EXT_ID         ?  TextByLanguage("Идентификатор на бирже","Exchange identifier")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
         (this.GetProperty(property)==""  ?  TextByLanguage(": Отсутствует",": Not set"):": \""+this.GetProperty(property)+"\"")):
      ""
     );
  }
//+------------------------------------------------------------------+

Реализация методов возврата наименования статуса ордера, наименования ордера или позиции, наименования направления сделки, описания типа по направлению ордера/позиции и метода удобного вывода в журнал описания свойств ордера.

Сначала в файл Defines.mqh добавим ещё одну макроподстановку для удобного вывода в журнал названия функции/метода:

//+------------------------------------------------------------------+
//| Макроподстановки                                                 |
//+------------------------------------------------------------------+
#define COUNTRY_LANG    "Russian"            // Язык страны
#define DFUN           (__FUNCTION__+": ")   // "Описание функции"
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возвращает наименование статуса ордера                           |
//+------------------------------------------------------------------+
string COrder::StatusDescription(void) const
  {
   ENUM_ORDER_STATUS status=this.Status();
   ENUM_ORDER_TYPE   type=(ENUM_ORDER_TYPE)this.TypeOrder();
   return
     (
      status==ORDER_STATUS_BALANCE        ?  TextByLanguage("Балансная операция","Balance operation") :
      status==ORDER_STATUS_CREDIT         ?  TextByLanguage("Кредитная операция","Credits operation") :
      status==ORDER_STATUS_HISTORY_ORDER  || status==ORDER_STATUS_HISTORY_PENDING                     ?  
      (
       type>ORDER_TYPE_SELL ? TextByLanguage("Отложенный ордер","Pending order")                      :
       TextByLanguage("Ордер на ","The order to ")+(type==ORDER_TYPE_BUY ? TextByLanguage("покупку","buy") : TextByLanguage("продажу","sell"))
      )                                                                                               :
      status==ORDER_STATUS_DEAL           ?  TextByLanguage("Сделка","Deal")                          :
      status==ORDER_STATUS_MARKET_ACTIVE  ?  TextByLanguage("Позиция","Active position")              :
      status==ORDER_STATUS_MARKET_PENDING ?  TextByLanguage("Установленный отложенный ордер","Active pending order") :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Возвращает наименование ордера или позиции                       |
//+------------------------------------------------------------------+
string COrder::TypeDescription(void) const
  {
   if(this.Status()==ORDER_STATUS_DEAL)
      return this.GetTypeDealDescription(this.TypeOrder());
   else switch(this.TypeOrder())
     {
      case ORDER_TYPE_BUY              :  return "Buy";
      case ORDER_TYPE_BUY_LIMIT        :  return "Buy Limit";
      case ORDER_TYPE_BUY_STOP         :  return "Buy Stop";
      case ORDER_TYPE_SELL             :  return "Sell";
      case ORDER_TYPE_SELL_LIMIT       :  return "Sell Limit";
      case ORDER_TYPE_SELL_STOP        :  return "Sell Stop";
      #ifdef __MQL4__
      case ORDER_TYPE_BALANCE          :  return TextByLanguage("Балансовая операция","Balance operation");
      case ORDER_TYPE_CREDIT           :  return TextByLanguage("Кредитная операция","Credit operation");
      #else 
      case ORDER_TYPE_BUY_STOP_LIMIT   :  return "Buy Stop Limit";
      case ORDER_TYPE_SELL_STOP_LIMIT  :  return "Sell Stop Limit";
      #endif 
      default                          :  return TextByLanguage("Неизвестный тип","Unknown type");
     }
  }
//+------------------------------------------------------------------+
//| Возвращает наименование направления сделки                       |
//+------------------------------------------------------------------+
string COrder::DealEntryDescription(void) const
  {
   return(this.Status()==ORDER_STATUS_DEAL ? this.GetEntryDescription(this.GetProperty(ORDER_PROP_DEAL_ENTRY)) : "");
  }
//+------------------------------------------------------------------+
//| Возвращает тип по направлению ордера/позиции                     |
//+------------------------------------------------------------------+
string COrder::DirectionDescription(void) const
  {
   if(this.Status()==ORDER_STATUS_DEAL)
      return this.TypeDescription();
   switch(this.TypeByDirection())
     {
      case ORDER_TYPE_BUY  :  return "Buy";
      case ORDER_TYPE_SELL :  return "Sell";
      default              :  return TextByLanguage("Неизвестный тип","Unknown type");
     }
  }
//+------------------------------------------------------------------+
//| Выводит в журнал свойства ордера                                 |
//+------------------------------------------------------------------+
void COrder::Print(const bool full_prop=false)
  {
   ::Print("============= ",TextByLanguage("Начало списка параметров: \"","The beginning of the parameter list: \""),this.StatusDescription(),"\" =============");
   int beg=0, end=ORDER_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_INTEGER prop=(ENUM_ORDER_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=ORDER_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_DOUBLE prop=(ENUM_ORDER_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=ORDER_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_STRING prop=(ENUM_ORDER_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of the parameter list: \""),this.StatusDescription(),"\" ==================\n");
  }
//+------------------------------------------------------------------+

Теперь реализованы все методы класса абстрактного ордера COrder и можно попробовать посмотреть что из этого мы получили на данном этапе.

Тест абстрактного ордера COrder

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

В каталоге Experts, или в его подпапке, создайте новую папку и назовите её TestDoEasy и внутри только что созданной папки создайте ещё одну: Part01 — в ней будет расположен файл тестового советника.

Щёлкните в навигаторе редактора правой кнопкой по папке Part01 и из выпадающего меню выберите "Новый файл", или нажмите Ctrl+N при выделенной папке Part01. Откроется окно Мастера MQL, где сразу будет выбран пункт "Советник (шаблон)", нажмите кнопку "Далее" и на следующей странице в поле ввода имени допишите к уже прописанному пути к файлу имя нового файла "TestDoEasyPart01" и нажимайте "Далее" пока не дойдёте до конца работы мастера. Нам для теста не нужны будут никакие дополнительные обработчики событий, так что все поля с чекбоксами можно оставлять пустыми. Нажмите кнопку "Готово" и будет создан шаблон нового эксперта:
//+------------------------------------------------------------------+
//|                                             TestDoEasyPart01.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"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

Долго придумывать ничего не будем — у нас простейший тест, так что просто подключим класс нашего абстрактного ордера COrder и класс CArrayObj из стандартной библиотеки и создадим список объектов:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart01.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\Objects\Order.mqh>
#include <Arrays\ArrayObj.mqh>
//--- global variables
CArrayObj      list_all_orders;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

Далее в OnInit() зададим списку объектов флаг сортированного списка и заполним его в двух циклах всеми историческими сделками и ордерами.

Затем опять же в цикле выведем в журнал терминала "Эксперты" данные по каждому из объектов, находящихся в заполненном списке:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   list_all_orders.Sort();
   list_all_orders.Clear();
   if(!HistorySelect(0,TimeCurrent()))
     {
      Print(DFUN,TextByLanguage(": Не удалось получить историю сделок и ордеров",": Failed to get the history of deals and orders"));
      return INIT_FAILED;
     }
//--- Сделки
   int total_deals=HistoryDealsTotal();
   for(int i=0; i<total_deals; i++)
     {
      ulong deal_ticket=::HistoryDealGetTicket(i);
      if(deal_ticket==0) continue;
      COrder *deal=new COrder(ORDER_STATUS_DEAL,deal_ticket);
      if(deal==NULL) continue;
      list_all_orders.InsertSort(deal);
     }
//--- Ордера
   int total_orders=HistoryOrdersTotal();
   for(int i=0; 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)
        {
         COrder *order=new COrder(ORDER_STATUS_HISTORY_ORDER,order_ticket);
         if(order==NULL) continue;
         list_all_orders.InsertSort(order);
        }
      else
        {
         COrder *order=new COrder(ORDER_STATUS_HISTORY_PENDING,order_ticket);
         if(order==NULL) continue;
         list_all_orders.InsertSort(order);
        }
     }
//--- Вывод в журнал
   int total=list_all_orders.Total();
   for(int i=0;i<total;i++)
     {
      COrder *order=list_all_orders.At(i);
      if(order==NULL)
         continue;
      order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Если сейчас попытаться откомпилировать советник, то получим три ошибки:


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


Теперь всё компилируется без ошибок и в журнале терминала выводятся данные по всем ордерам и сделкам истории торгового счёта.


Выводятся абсолютно все свойства каждого ордера/сделки, даже не поддерживаемые.


Дело в том, что методы, возвращающие флаги поддержания конкретных свойств данным ордером, мы сделали виртуальными — для того, чтобы их переопределить в классах-наследниках. И уже из них выводить данные в журнал — тогда всё должно будет выводиться верно — если есть неподдерживаемое ордером свойство, то оно не будет выведено в журнал, так как по умолчанию метод Print(const bool full_prop=false) класса COrder имеет флаг невывода в журнал неподдерживаемых свойств, но виртуальные методы SupportProperty() данного класса просто возвращают истину для любого свойства.

Что дальше

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


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

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

Отдельно стоит отметить что библиотека создаётся "вживую", прямо во время изложения текста статей, поэтому в неё от статьи к статье, в её разные методы и переменные вносятся требуемые правки. В связи с этим могут наблюдаться незначительные несоответствия в содержании приложенных файлов и текстов описания в статье. Например, возможны правки в комментариях, перестановка расположения членов перечислений, и пр. Это не влияет на работоспособность приложенных примеров.

К содержанию.