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

9 мая 2019, 09:09
Artyom Trishkin
10
656

Содержание

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

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

Класс события модификации

Начнём как обычно — с подготовки требуемых констант перечислений.
Откроем файл библиотеки Defines.mqh, и в целочисленных свойствах ордера — в макроподстановке, содержащей количество неиспользуемых целочисленных свойств ордера при сортировке по этим свойствам, впишем количество пропускаемых свойств равным нулю — все целочисленные свойства ордера нам в дальнейшем потребуются. Ранее у нас при поиске и сортировке пропускалось одно свойство — последнее из списка констант перечисления: ORDER_PROP_DIRECTION — тип ордера по его направлению (помним, что все неиспользуемые свойства мы располагаем в конце списка констант перечисления). В данной статье и далее нам это свойство потребуется для поиска всех однонаправленных отложенных ордеров в списке рыночной коллекции.

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

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

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

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

//+------------------------------------------------------------------+
//| Список флагов торговых событий на счёте                          |
//+------------------------------------------------------------------+
enum ENUM_TRADE_EVENT_FLAGS
  {
   TRADE_EVENT_FLAG_NO_EVENT        =  0,                   // Нет события
   TRADE_EVENT_FLAG_ORDER_PLASED    =  1,                   // Отложенный ордер установлен
   TRADE_EVENT_FLAG_ORDER_REMOVED   =  2,                   // Отложенный ордер удалён
   TRADE_EVENT_FLAG_ORDER_ACTIVATED =  4,                   // Отложенный ордер активирован ценой
   TRADE_EVENT_FLAG_POSITION_OPENED =  8,                   // Позиция открыта
   TRADE_EVENT_FLAG_POSITION_CHANGED=  16,                  // Позиция изменена
   TRADE_EVENT_FLAG_POSITION_REVERSE=  32,                  // Разворот позиции
   TRADE_EVENT_FLAG_POSITION_CLOSED =  64,                  // Позиция закрыта
   TRADE_EVENT_FLAG_ACCOUNT_BALANCE =  128,                 // Балансная операция (уточнение в типе сделки)
   TRADE_EVENT_FLAG_PARTIAL         =  256,                 // Частичное исполнение
   TRADE_EVENT_FLAG_BY_POS          =  512,                 // Исполнение встречной позицией
   TRADE_EVENT_FLAG_PRICE           =  1024               // Модификация цены установки
   TRADE_EVENT_FLAG_SL              =  2048,                // Исполнение по StopLoss
   TRADE_EVENT_FLAG_TP              =  4096,                // Исполнение по TakeProfit
   TRADE_EVENT_FLAG_ORDER_MODIFY    =  8192               // Модификация ордера
   TRADE_EVENT_FLAG_POSITION_MODIFY =  16384,               // Модификация позиции
  };
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Список возможных торговых событий на счёте                       |
//+------------------------------------------------------------------+
enum ENUM_TRADE_EVENT
  {
   TRADE_EVENT_NO_EVENT = 0,                                // Нет торгового события
   TRADE_EVENT_PENDING_ORDER_PLASED,                        // Отложенный ордер установлен
   TRADE_EVENT_PENDING_ORDER_REMOVED,                       // Отложенный ордер удалён
//--- члены перечисления, совпадающие с членами перечисления ENUM_DEAL_TYPE
//--- (порядок следования констант ниже менять нельзя, удалять и добавлять новые - нельзя)
   TRADE_EVENT_ACCOUNT_CREDIT = DEAL_TYPE_CREDIT,           // Начисление кредита (3)
   TRADE_EVENT_ACCOUNT_CHARGE,                              // Дополнительные сборы
   TRADE_EVENT_ACCOUNT_CORRECTION,                          // Корректирующая запись
   TRADE_EVENT_ACCOUNT_BONUS,                               // Перечисление бонусов
   TRADE_EVENT_ACCOUNT_COMISSION,                           // Дополнительные комиссии
   TRADE_EVENT_ACCOUNT_COMISSION_DAILY,                     // Комиссия, начисляемая в конце торгового дня
   TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY,                   // Комиссия, начисляемая в конце месяца
   TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY,               // Агентская комиссия, начисляемая в конце торгового дня
   TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY,             // Агентская комиссия, начисляемая в конце месяца
   TRADE_EVENT_ACCOUNT_INTEREST,                            // Начисления процентов на свободные средства
   TRADE_EVENT_BUY_CANCELLED,                               // Отмененная сделка покупки
   TRADE_EVENT_SELL_CANCELLED,                              // Отмененная сделка продажи
   TRADE_EVENT_DIVIDENT,                                    // Начисление дивиденда
   TRADE_EVENT_DIVIDENT_FRANKED,                            // Начисление франкированного дивиденда
   TRADE_EVENT_TAX                        = DEAL_TAX,       // Начисление налога
//--- константы, относящиеся к типу сделки DEAL_TYPE_BALANCE из перечисления ENUM_DEAL_TYPE
   TRADE_EVENT_ACCOUNT_BALANCE_REFILL     = DEAL_TAX+1,     // Пополнение средств на балансе
   TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL = DEAL_TAX+2,     // Снятие средств с баланса
//--- Остальные возможные торговые события
//--- (менять порядок следования констант ниже, удалять и добавлять новые - можно)
   TRADE_EVENT_PENDING_ORDER_ACTIVATED    = DEAL_TAX+3,     // Отложенный ордер активирован ценой
   TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL,             // Отложенный ордер активирован ценой частично
   TRADE_EVENT_POSITION_OPENED,                             // Позиция открыта
   TRADE_EVENT_POSITION_OPENED_PARTIAL,                     // Позиция открыта частично
   TRADE_EVENT_POSITION_CLOSED,                             // Позиция закрыта
   TRADE_EVENT_POSITION_CLOSED_BY_POS,                      // Позиция закрыта встречной
   TRADE_EVENT_POSITION_CLOSED_BY_SL,                       // Позиция закрыта по StopLoss
   TRADE_EVENT_POSITION_CLOSED_BY_TP,                       // Позиция закрыта по TakeProfit
   TRADE_EVENT_POSITION_REVERSED_BY_MARKET,                 // Разворот позиции новой сделкой (неттинг)
   TRADE_EVENT_POSITION_REVERSED_BY_PENDING,                // Разворот позиции активацией отложенного ордера (неттинг)
   TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL,         // Разворот позиции частичным исполнением маркет-ордера (неттинг)
   TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL,        // Разворот позиции частичной активацией отложенного ордера (неттинг)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET,               // Добавлен объём к позиции новой сделкой (неттинг)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL,       // Добавлен объём к позиции частичным исполнением маркет-ордера (неттинг)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING,              // Добавлен объём к позиции активацией отложенного ордера (неттинг)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL,      // Добавлен объём к позиции частичной активацией отложенного ордера (неттинг)
   TRADE_EVENT_POSITION_CLOSED_PARTIAL,                     // Позиция закрыта частично
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS,              // Позиция закрыта частично встречной
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL,               // Позиция закрыта частично по StopLoss
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP,               // Позиция закрыта частично по TakeProfit
   TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER,                  // Срабатывание StopLimit ордера
   TRADE_EVENT_MODIFY_ORDER_PRICE,                          // Изменение цены установки ордера
   TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS,                // Изменение цены установки ордера и StopLoss 
   TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT,              // Изменение цены установки ордера и TakeProfit
   TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT,    // Изменение цены установки ордера, StopLoss и TakeProfit
   TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT,          // Изменение StopLoss и TakeProfit ордера
   TRADE_EVENT_MODIFY_ORDER_STOP_LOSS,                      // Изменение StopLoss ордера
   TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT,                    // Изменение TakeProfit ордера
   TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT,       // Изменение StopLoss и TakeProfit позиции
   TRADE_EVENT_MODIFY_POSITION_STOP_LOSS,                   // Изменение StopLoss позиции
   TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT,                 // Изменение TakeProfit позиции
  };
//+------------------------------------------------------------------+

Так как сегодня будем делать новый класс-наследник абстрактного класса-события CEvent, то нам потребуется задать ещё один статус события — "модификация" для нового класса-наследника. Впишем его в список перечисления статусов событий:

//+------------------------------------------------------------------+
//| Статус события                                                   |
//+------------------------------------------------------------------+
enum ENUM_EVENT_STATUS
  {
   EVENT_STATUS_MARKET_POSITION,                            // Событие рыночной позиции (открытие, частичное открытие, частичное закрытие, добавление объёма, разворот)
   EVENT_STATUS_MARKET_PENDING,                             // Событие рыночного отложенного ордера (установка)
   EVENT_STATUS_HISTORY_PENDING,                            // Событие исторического отложенного ордера (удаление)
   EVENT_STATUS_HISTORY_POSITION,                           // Событие исторической позиции (закрытие)
   EVENT_STATUS_BALANCE,                                    // Событие балансной операции (начисление баланса, снятие средств и события из перечисления ENUM_DEAL_TYPE)
   EVENT_STATUS_MODIFY                                      // Событие модификации ордера/позиции
  };
//+------------------------------------------------------------------+

И дополним список перечисления причин событий причиной события "модификация":

//+------------------------------------------------------------------+
//| Причина события                                                  |
//+------------------------------------------------------------------+
enum ENUM_EVENT_REASON
  {
   EVENT_REASON_REVERSE,                                    // Разворот позиции (неттинг)
   EVENT_REASON_REVERSE_PARTIALLY,                          // Разворот позиции частичным исполнением заявки (неттинг)
   EVENT_REASON_REVERSE_BY_PENDING,                         // Разворот позиции при срабатывании отложенного ордера (неттинг)
   EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY,               // Разворот позиции при при частичном срабатывании отложенного ордера (неттинг)
   //--- Все константы, относящиеся к развороту позиции, должны быть в списке выше
   EVENT_REASON_ACTIVATED_PENDING,                          // Срабатывание отложенного ордера
   EVENT_REASON_ACTIVATED_PENDING_PARTIALLY,                // Частичное срабатывание отложенного ордера
   EVENT_REASON_STOPLIMIT_TRIGGERED,                        // Срабатывание StopLimit-ордера
   EVENT_REASON_MODIFY,                                     // Модификация
   EVENT_REASON_CANCEL,                                     // Отмена
   EVENT_REASON_EXPIRED,                                    // Истечение срока действия ордера
   EVENT_REASON_DONE,                                       // Заявка исполнена полностью
   EVENT_REASON_DONE_PARTIALLY,                             // Заявка исполнена частично
   EVENT_REASON_VOLUME_ADD,                                 // Добавление объёма к позиции (неттинг)
   EVENT_REASON_VOLUME_ADD_PARTIALLY,                       // Добавление объёма к позиции частичным исполнением заявки (неттинг)
   EVENT_REASON_VOLUME_ADD_BY_PENDING,                      // Добавление объёма к позиции при срабатывании отложенного ордера (неттинг)
   EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY,            // Добавление объёма к позиции при частичном срабатывании отложенного ордера (неттинг)
   EVENT_REASON_DONE_SL,                                    // Закрытие по StopLoss
   EVENT_REASON_DONE_SL_PARTIALLY,                          // Частичное закрытие по StopLoss
   EVENT_REASON_DONE_TP,                                    // Закрытие по TakeProfit
   EVENT_REASON_DONE_TP_PARTIALLY,                          // Частичное закрытие по TakeProfit
   EVENT_REASON_DONE_BY_POS,                                // Закрытие встречной позицией
   EVENT_REASON_DONE_PARTIALLY_BY_POS,                      // Частичное закрытие встречной позицией
   EVENT_REASON_DONE_BY_POS_PARTIALLY,                      // Закрытие частичным объёмом встречной позиции
   EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY,            // Частичное закрытие частичным объёмом встречной позиции
   //--- Константы, относящиеся к типу сделки DEAL_TYPE_BALANCE из перечисления ENUM_DEAL_TYPE
   EVENT_REASON_BALANCE_REFILL,                             // Пополнение счёта
   EVENT_REASON_BALANCE_WITHDRAWAL,                         // Снятие средств со счёта
   //--- Список констант соотносится с TRADE_EVENT_ACCOUNT_CREDIT из перечисления ENUM_TRADE_EVENT, смещено на +13 относительно ENUM_DEAL_TYPE (EVENT_REASON_ACCOUNT_CREDIT-3)
   EVENT_REASON_ACCOUNT_CREDIT,                             // Начисление кредита
   EVENT_REASON_ACCOUNT_CHARGE,                             // Дополнительные сборы
   EVENT_REASON_ACCOUNT_CORRECTION,                         // Корректирующая запись
   EVENT_REASON_ACCOUNT_BONUS,                              // Перечисление бонусов
   EVENT_REASON_ACCOUNT_COMISSION,                          // Дополнительные комиссии
   EVENT_REASON_ACCOUNT_COMISSION_DAILY,                    // Комиссия, начисляемая в конце торгового дня
   EVENT_REASON_ACCOUNT_COMISSION_MONTHLY,                  // Комиссия, начисляемая в конце месяца
   EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY,              // Агентская комиссия, начисляемая в конце торгового дня
   EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY,            // Агентская комиссия, начисляемая в конце месяца
   EVENT_REASON_ACCOUNT_INTEREST,                           // Начисления процентов на свободные средства
   EVENT_REASON_BUY_CANCELLED,                              // Отмененная сделка покупки
   EVENT_REASON_SELL_CANCELLED,                             // Отмененная сделка продажи
   EVENT_REASON_DIVIDENT,                                   // Начисление дивиденда
   EVENT_REASON_DIVIDENT_FRANKED,                           // Начисление франкированного дивиденда
   EVENT_REASON_TAX                                         // Начисление налога
  };
#define REASON_EVENT_SHIFT    (EVENT_REASON_ACCOUNT_CREDIT-3)
//+------------------------------------------------------------------+

Чтобы нам в любой момент знать, что у нас изменилось в свойствах ордера или позиции, в вещественные свойства события добавим цены свойств ордера или позиции до модификации (цены после модификации будут браться из уже существующих свойств), свойства для записи текущих цен на момент события, а также изменим значения количества вещественных свойств события с 10 на 15 и добавим количество неиспользуемых свойств при поиске и сортировке (данные о модификации и цены на момент события модификации не будем использовать для поиска и сортировки):

//+------------------------------------------------------------------+
//| Вещественные свойства события                                    |
//+------------------------------------------------------------------+
enum ENUM_EVENT_PROP_DOUBLE
  {
   EVENT_PROP_PRICE_EVENT = EVENT_PROP_INTEGER_TOTAL,       // Цена, на которой произошло событие
   EVENT_PROP_PRICE_OPEN,                                   // Цена открытия ордера/сделки/позиции
   EVENT_PROP_PRICE_CLOSE,                                  // Цена закрытия ордера/сделки/позиции
   EVENT_PROP_PRICE_SL,                                     // Цена StopLoss ордера/сделки/позиции
   EVENT_PROP_PRICE_TP,                                     // Цена TakeProfit ордера/сделки/позиции
   EVENT_PROP_VOLUME_ORDER_INITIAL,                         // Запрашиваемый объём ордера
   EVENT_PROP_VOLUME_ORDER_EXECUTED,                        // Исполненный объём ордера
   EVENT_PROP_VOLUME_ORDER_CURRENT,                         // Оставшийся объём ордера
   EVENT_PROP_VOLUME_POSITION_EXECUTED,                     // Текущий исполненный объём позиции после сделки
   EVENT_PROP_PROFIT,                                       // Профит
   //---
   EVENT_PROP_PRICE_OPEN_BEFORE,                            // Цена установки ордера до модификации
   EVENT_PROP_PRICE_SL_BEFORE,                              // Цена StopLoss до модификации
   EVENT_PROP_PRICE_TP_BEFORE,                              // Цена TakeProfit до модификации
   EVENT_PROP_PRICE_EVENT_ASK,                              // Цена Ask в момент события
   EVENT_PROP_PRICE_EVENT_BID,                              // Цена Bid в момент события
  };
#define EVENT_PROP_DOUBLE_TOTAL  (15)                       // Общее количество вещественных свойств события
#define EVENT_PROP_DOUBLE_SKIP   (5                       // Количество неиспользуемых в сортировке свойств события
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возможные критерии сортировки событий                            |
//+------------------------------------------------------------------+
#define FIRST_EVN_DBL_PROP       (EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_INTEGER_SKIP)
#define FIRST_EVN_STR_PROP       (EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_INTEGER_SKIP+EVENT_PROP_DOUBLE_TOTAL-EVENT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_EVENTS_MODE
  {
   //--- Сортировка по целочисленным свойствам
   SORT_BY_EVENT_TYPE_EVENT               = 0,                       // Сортировать по типу события
   SORT_BY_EVENT_TIME_EVENT               = 1,                       // Сортировать по времени события
   SORT_BY_EVENT_STATUS_EVENT             = 2,                       // Сортировать по статусу события (из перечисления ENUM_EVENT_STATUS)
   SORT_BY_EVENT_REASON_EVENT             = 3,                       // Сортировать по причине события (из перечисления ENUM_EVENT_REASON)
   SORT_BY_EVENT_TYPE_DEAL_EVENT          = 4,                       // Сортировать по типу сделки события
   SORT_BY_EVENT_TICKET_DEAL_EVENT        = 5,                       // Сортировать по тикету сделки события
   SORT_BY_EVENT_TYPE_ORDER_EVENT         = 6,                       // Сортировать по типу ордера, на основании которого открыта сделка события (последний ордер позиции)
   SORT_BY_EVENT_TICKET_ORDER_EVENT       = 7,                       // Сортировать по тикету ордера, на основании которого открыта сделка события (последний ордер позиции)
   SORT_BY_EVENT_TIME_ORDER_POSITION      = 8,                       // Сортировать по времени ордера, на основании которого открыта сделка позиции (первый ордер позиции)
   SORT_BY_EVENT_TYPE_ORDER_POSITION      = 9,                       // Сортировать по типу ордера, на основании которого открыта сделка позиции (первый ордер позиции)
   SORT_BY_EVENT_TICKET_ORDER_POSITION    = 10,                      // Сортировать по тикету ордера, на основании которого открыта сделка позиции (первый ордер позиции)
   SORT_BY_EVENT_POSITION_ID              = 11,                      // Сортировать по идентификатору позиции
   SORT_BY_EVENT_POSITION_BY_ID           = 12,                      // Сортировать по идентификатору встречной позиции
   SORT_BY_EVENT_MAGIC_ORDER              = 13,                      // Сортировать по магическому номеру ордера/сделки/позиции
   SORT_BY_EVENT_MAGIC_BY_ID              = 14,                      // Сортировать по магическому номеру встречной позиции
   //--- Сортировка по вещественным свойствам
   SORT_BY_EVENT_PRICE_EVENT              =  FIRST_EVN_DBL_PROP,     // Сортировать по цене, на которой произошло событие
   SORT_BY_EVENT_PRICE_OPEN               =  FIRST_EVN_DBL_PROP+1,   // Сортировать по цене открытия позиции
   SORT_BY_EVENT_PRICE_CLOSE              =  FIRST_EVN_DBL_PROP+2,   // Сортировать по цене закрытия позиции
   SORT_BY_EVENT_PRICE_SL                 =  FIRST_EVN_DBL_PROP+3,   // Сортировать по цене StopLoss позиции
   SORT_BY_EVENT_PRICE_TP                 =  FIRST_EVN_DBL_PROP+4,   // Сортировать по цене TakeProfit позиции
   SORT_BY_EVENT_VOLUME_ORDER_INITIAL     =  FIRST_EVN_DBL_PROP+5,   // Сортировать по первоначальному объёму
   SORT_BY_EVENT_VOLUME_ORDER_EXECUTED    =  FIRST_EVN_DBL_PROP+6,   // Сортировать по текущему объёму
   SORT_BY_EVENT_VOLUME_ORDER_CURRENT     =  FIRST_EVN_DBL_PROP+7,   // Сортировать по оставшемуся объёму
   SORT_BY_EVENT_VOLUME_POSITION_EXECUTED =  FIRST_EVN_DBL_PROP+8,   // Сортировать по оставшемуся объёму
   SORT_BY_EVENT_PROFIT                   =  FIRST_EVN_DBL_PROP+9,   // Сортировать по профиту
   //--- Сортировка по строковым свойствам
   SORT_BY_EVENT_SYMBOL                   =  FIRST_EVN_STR_PROP,     // Сортировать по символу ордера/позици/сделки
   SORT_BY_EVENT_SYMBOL_BY_ID                                        // Сортировать по символу встречной позици
  };
//+------------------------------------------------------------------+

Доработаем класс абстрактного события CEvent.

Так как в классах-наследниках абстрактного события CEvent мы выводим информацию в журнал, то требуется знать количество цифр после запятой в котировке символа, на котором произошло событие — Digits() символа. Чтобы не получать его каждый раз в каждом из наследников, просто получим его единожды в родительском классе.

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

//+------------------------------------------------------------------+
//| Класс абстрактного события                                       |
//+------------------------------------------------------------------+
class CEvent : public CObject
  {
private:
   int               m_event_code;                                   // Код события
//--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство события
   int               IndexProp(ENUM_EVENT_PROP_DOUBLE property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL;                         }
   int               IndexProp(ENUM_EVENT_PROP_STRING property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_DOUBLE_TOTAL; }
protected:
   ENUM_TRADE_EVENT  m_trade_event;                                  // Торговое событие
   bool              m_is_hedge;                                     // Флаг хедж-счёта
   long              m_chart_id;                                     // Идентификатор графика управляющей программы
   int               m_digits;                                       // Digits() символа
   int               m_digits_acc;                                   // Количество знаков после запятой для валюты счета
   long              m_long_prop[EVENT_PROP_INTEGER_TOTAL];          // Целочисленные свойства события
   double            m_double_prop[EVENT_PROP_DOUBLE_TOTAL];         // Вещественные свойства события
   string            m_string_prop[EVENT_PROP_STRING_TOTAL];         // Строковые свойства события
//--- возвращает факт наличия флага в торговом событии
   bool              IsPresentEventFlag(const int event_code)  const { return (this.m_event_code & event_code)==event_code;            }

//--- Защищённый параметрический конструктор
                     CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket);
public:
//--- Конструктор по умолчанию
                     CEvent(void){;}
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code),m_digits(0)
  {
   this.m_long_prop[EVENT_PROP_STATUS_EVENT]       =  event_status;
   this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] =  (long)ticket;
   this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
   this.m_digits_acc=(int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS);
   this.m_chart_id=::ChartID();
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства события              |
//+------------------------------------------------------------------+
string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property)
  {
   return
     (
      property==EVENT_PROP_TYPE_EVENT              ?  TextByLanguage("Тип события","Event's type")+": "+this.TypeEventDescription()                                                       :
      property==EVENT_PROP_TIME_EVENT              ?  TextByLanguage("Время события","Time of event")+": "+TimeMSCtoString(this.GetProperty(property))                                    :
      property==EVENT_PROP_STATUS_EVENT            ?  TextByLanguage("Статус события","Status of event")+": \""+this.StatusDescription()+"\""                                             :
      property==EVENT_PROP_REASON_EVENT            ?  TextByLanguage("Причина события","Reason of event")+": "+this.ReasonDescription()                                                   :
      property==EVENT_PROP_TYPE_DEAL_EVENT         ?  TextByLanguage("Тип сделки","Deal's type")+": "+DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(property))                     :
      property==EVENT_PROP_TICKET_DEAL_EVENT       ?  TextByLanguage("Тикет сделки","Deal's ticket")+": #"+(string)this.GetProperty(property)                                              :
      property==EVENT_PROP_TYPE_ORDER_EVENT        ?  TextByLanguage("Тип ордера события","Event's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property))    :
      property==EVENT_PROP_TYPE_ORDER_POSITION     ?  TextByLanguage("Тип ордера позиции","Position's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) :
      property==EVENT_PROP_TICKET_ORDER_POSITION   ?  TextByLanguage("Тикет первого ордера позиции","Position's first order ticket")+": #"+(string)this.GetProperty(property)              :
      property==EVENT_PROP_TICKET_ORDER_EVENT      ?  TextByLanguage("Тикет ордера события","Event's order ticket")+": #"+(string)this.GetProperty(property)                               :
      property==EVENT_PROP_POSITION_ID             ?  TextByLanguage("Идентификатор позиции","Position ID")+": #"+(string)this.GetProperty(property)                                       :
      property==EVENT_PROP_POSITION_BY_ID          ?  TextByLanguage("Идентификатор встречной позиции","Opposite position's ID")+": #"+(string)this.GetProperty(property)                  :
      property==EVENT_PROP_MAGIC_ORDER             ?  TextByLanguage("Магический номер","Magic number")+": "+(string)this.GetProperty(property)                                           :
      property==EVENT_PROP_MAGIC_BY_ID             ?  TextByLanguage("Магический номер встречной позиции","Magic number of opposite position")+": "+(string)this.GetProperty(property)    :
      property==EVENT_PROP_TIME_ORDER_POSITION     ?  TextByLanguage("Время открытия позиции","Position's opened time")+": "+TimeMSCtoString(this.GetProperty(property))                  :
      property==EVENT_PROP_TYPE_ORD_POS_BEFORE     ?  TextByLanguage("Тип ордера позиции до смены направления","Type order of position before changing direction")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) :
      property==EVENT_PROP_TICKET_ORD_POS_BEFORE   ?  TextByLanguage("Тикет ордера позиции до смены направления","Ticket order of position before changing direction")+": #"+(string)this.GetProperty(property)                            :
      property==EVENT_PROP_TYPE_ORD_POS_CURRENT    ?  TextByLanguage("Тип ордера текущей позиции","Type order of current position")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property))                                :
      property==EVENT_PROP_TICKET_ORD_POS_CURRENT  ?  TextByLanguage("Тикет ордера текущей позиции","Ticket order of current position")+": #"+(string)this.GetProperty(property)                                                           :
      EnumToString(property)
     );
  }
//+------------------------------------------------------------------+
//| Возвращает описание вещественного свойства события               |
//+------------------------------------------------------------------+
string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property)
  {
   int dg=(int)::SymbolInfoInteger(this.GetProperty(EVENT_PROP_SYMBOL),SYMBOL_DIGITS);
   int dgl=(int)DigitsLots(this.GetProperty(EVENT_PROP_SYMBOL));
   return
     (
      property==EVENT_PROP_PRICE_EVENT             ?  TextByLanguage("Цена на момент события","Price at the time of the event")+": "+::DoubleToString(this.GetProperty(property),dg)               :
      property==EVENT_PROP_PRICE_OPEN              ?  TextByLanguage("Цена открытия","Price open")+": "+::DoubleToString(this.GetProperty(property),dg)                                            :
      property==EVENT_PROP_PRICE_CLOSE             ?  TextByLanguage("Цена закрытия","Price close")+": "+::DoubleToString(this.GetProperty(property),dg)                                           :
      property==EVENT_PROP_PRICE_SL                ?  TextByLanguage("Цена StopLoss","Price StopLoss")+": "+::DoubleToString(this.GetProperty(property),dg)                                        :
      property==EVENT_PROP_PRICE_TP                ?  TextByLanguage("Цена TakeProfit","Price TakeProfit")+": "+::DoubleToString(this.GetProperty(property),dg)                                    :
      property==EVENT_PROP_VOLUME_ORDER_INITIAL    ?  TextByLanguage("Начальный объём ордера","Order initial volume")+": "+::DoubleToString(this.GetProperty(property),dgl)                        :
      property==EVENT_PROP_VOLUME_ORDER_EXECUTED   ?  TextByLanguage("Исполненный объём ордера","Order executed volume")+": "+::DoubleToString(this.GetProperty(property),dgl)                     :
      property==EVENT_PROP_VOLUME_ORDER_CURRENT    ?  TextByLanguage("Оставшийся объём ордера","Order remaining volume")+": "+::DoubleToString(this.GetProperty(property),dgl)                     :
      property==EVENT_PROP_VOLUME_POSITION_EXECUTED ? TextByLanguage("Текущий объём позиции","Position current volume")+": "+::DoubleToString(this.GetProperty(property),dgl)                      :
      property==EVENT_PROP_PROFIT                  ?  TextByLanguage("Профит","Profit")+": "+::DoubleToString(this.GetProperty(property),this.m_digits_acc)                                        :
      property==EVENT_PROP_PRICE_OPEN_BEFORE       ?  TextByLanguage("Цена открытия до модификации","Price open before modification")+": "+::DoubleToString(this.GetProperty(property),dg)         :
      property==EVENT_PROP_PRICE_SL_BEFORE         ?  TextByLanguage("Цена StopLoss до модификации","Price StopLoss before modification")+": "+::DoubleToString(this.GetProperty(property),dg)     :
      property==EVENT_PROP_PRICE_TP_BEFORE         ?  TextByLanguage("Цена TakeProfit до модификации","Price TakeProfit before modification")+": "+::DoubleToString(this.GetProperty(property),dg) :
      property==EVENT_PROP_PRICE_EVENT_ASK         ?  TextByLanguage("Цена Ask в момент события","Price Ask at the time of the event")+": "+::DoubleToString(this.GetProperty(property),dg)        :
      property==EVENT_PROP_PRICE_EVENT_BID         ?  TextByLanguage("Цена Bid в момент события","Price Bid at the time of the event")+": "+::DoubleToString(this.GetProperty(property),dg)        :
      EnumToString(property)
     );
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Возвращает наименование торгового события                        |
//+------------------------------------------------------------------+
string CEvent::TypeEventDescription(void) const
  {
   ENUM_TRADE_EVENT event=this.TypeEvent();
   return
     (
      event==TRADE_EVENT_NO_EVENT                              ?  TextByLanguage("Нет торгового события","No trade event")                                              :
      event==TRADE_EVENT_PENDING_ORDER_PLASED                  ?  TextByLanguage("Отложенный ордер установлен","Pending order placed")                                  :
      event==TRADE_EVENT_PENDING_ORDER_REMOVED                 ?  TextByLanguage("Отложенный ордер удалён","Pending order removed")                                     :
      event==TRADE_EVENT_ACCOUNT_CREDIT                        ?  TextByLanguage("Начисление кредита","Credit")                                                         :
      event==TRADE_EVENT_ACCOUNT_CHARGE                        ?  TextByLanguage("Дополнительные сборы","Additional charge")                                            :
      event==TRADE_EVENT_ACCOUNT_CORRECTION                    ?  TextByLanguage("Корректирующая запись","Correction")                                                  :
      event==TRADE_EVENT_ACCOUNT_BONUS                         ?  TextByLanguage("Перечисление бонусов","Bonus")                                                        :
      event==TRADE_EVENT_ACCOUNT_COMISSION                     ?  TextByLanguage("Дополнительные комиссии","Additional commission")                                     :
      event==TRADE_EVENT_ACCOUNT_COMISSION_DAILY               ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission")                      :
      event==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY             ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission")                           :
      event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY         ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission")      :
      event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY       ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission")           :
      event==TRADE_EVENT_ACCOUNT_INTEREST                      ?  TextByLanguage("Начисления процентов на свободные средства","Interest rate")                          :
      event==TRADE_EVENT_BUY_CANCELLED                         ?  TextByLanguage("Отмененная сделка покупки","Canceled buy deal")                                       :
      event==TRADE_EVENT_SELL_CANCELLED                        ?  TextByLanguage("Отмененная сделка продажи","Canceled sell deal")                                      :
      event==TRADE_EVENT_DIVIDENT                              ?  TextByLanguage("Начисление дивиденда","Dividend operations")                                          :
      event==TRADE_EVENT_DIVIDENT_FRANKED                      ?  TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations")    :
      event==TRADE_EVENT_TAX                                   ?  TextByLanguage("Начисление налога","Tax charges")                                                     :
      event==TRADE_EVENT_ACCOUNT_BALANCE_REFILL                ?  TextByLanguage("Пополнение средств на балансе","Balance refill")                                      :
      event==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL            ?  TextByLanguage("Снятие средств с баланса","Withdrawals")                                              :
      event==TRADE_EVENT_PENDING_ORDER_ACTIVATED               ?  TextByLanguage("Отложенный ордер активирован ценой","Pending order activated")                        :
      event==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL       ?  TextByLanguage("Отложенный ордер активирован ценой частично","Pending order activated partially")     :
      event==TRADE_EVENT_POSITION_OPENED                       ?  TextByLanguage("Позиция открыта","Position is open")                                                  :
      event==TRADE_EVENT_POSITION_OPENED_PARTIAL               ?  TextByLanguage("Позиция открыта частично","Position is open partially")                               :
      event==TRADE_EVENT_POSITION_CLOSED                       ?  TextByLanguage("Позиция закрыта","Position closed")                                                   :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL               ?  TextByLanguage("Позиция закрыта частично","Position closed partially")                                :
      event==TRADE_EVENT_POSITION_CLOSED_BY_POS                ?  TextByLanguage("Позиция закрыта встречной","Position closed by opposite position")                    :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS        ?  TextByLanguage("Позиция закрыта встречной частично","Position closed partially by opposite position") :
      event==TRADE_EVENT_POSITION_CLOSED_BY_SL                 ?  TextByLanguage("Позиция закрыта по StopLoss","Position closed by StopLoss")                           :
      event==TRADE_EVENT_POSITION_CLOSED_BY_TP                 ?  TextByLanguage("Позиция закрыта по TakeProfit","Position closed by TakeProfit")                       :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL         ?  TextByLanguage("Позиция закрыта частично по StopLoss","Position closed partially by StopLoss")        :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP         ?  TextByLanguage("Позиция закрыта частично по TakeProfit","Position closed partially by TakeProfit")    :
      event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET           ?  TextByLanguage("Разворот позиции по рыночному запросу","Position reversal by market request")         :
      event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING          ?  TextByLanguage("Разворот позиции срабатыванием отложенного ордера","Position reversal by triggered a pending order")                            :
      event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET         ?  TextByLanguage("Добавлен объём к позиции по рыночному запросу","Added volume to position by market request")                                    :
      event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING        ?  TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to the position by activation of a pending order")        :
      
      event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL   ?  TextByLanguage("Разворот позиции частичным исполнением запроса","Position reversal by partially completed of market request")                   :
      event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL  ?  TextByLanguage("Разворот позиции частичным срабатыванием отложенного ордера","Position reversal by partially triggered a pending order")        :
      event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL ?  TextByLanguage("Добавлен объём к позиции частичным исполнением запроса","Added volume to position by partially completed of market request")    :
      event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL ? TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to the position by partially triggered a pending order")  :

      event==TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER            ?  TextByLanguage("Сработал StopLimit-ордер","StopLimit order triggered.")                                                                         :
      event==TRADE_EVENT_MODIFY_ORDER_PRICE                    ?  TextByLanguage("Модифицирована цена установки ордера ","Modified order price")                                                                  :
      event==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS          ?  TextByLanguage("Модифицированы цена установки и StopLoss ордера","Modified of order price and StopLoss")                                        :
      event==TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT        ?  TextByLanguage("Модифицированы цена установки и TakeProfit ордера","Modified of order price and TakeProfit")                                    :
      event==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT ?  TextByLanguage("Модифицированы цена установки, StopLoss и TakeProfit ордера","Modified of order price, StopLoss and TakeProfit")             :
      event==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT    ?  TextByLanguage("Модифицированы цены StopLoss и TakeProfit ордера","Modified of order StopLoss and TakeProfit")                                  :
      event==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS                ?  TextByLanguage("Модифицирован StopLoss ордера","Modified order StopLoss")                                                                       :
      event==TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT              ?  TextByLanguage("Модифицирован TakeProfit ордера","Modified order TakeProfit")                                                                   :
      event==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT ?  TextByLanguage("Модифицированы цены StopLoss и TakeProfit позиции","Modified of position StopLoss and TakeProfit")                              :
      event==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS             ?  TextByLanguage("Модифицирован StopLoss позиции","Modified position StopLoss")                                                                   :
      event==TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT           ?  TextByLanguage("Модифицирован TakeProfit позиции","Modified position TakeProfit")                                                               :
      EnumToString(event)
     );   
  }
//+------------------------------------------------------------------+

В метод, возвращающий описание причины события добавим две новые причины:

//+------------------------------------------------------------------+
//| Возвращает наименование причины сделки/ордера/позиции            |
//+------------------------------------------------------------------+
string CEvent::ReasonDescription(void) const
  {
   ENUM_EVENT_REASON reason=this.Reason();
   return 
     (
      reason==EVENT_REASON_ACTIVATED_PENDING                ?  TextByLanguage("Активирован отложенный ордер","Pending order activated")                           :
      reason==EVENT_REASON_ACTIVATED_PENDING_PARTIALLY      ?  TextByLanguage("Частичное срабатывание отложенного ордера","Pending order partially triggered")    :
      reason==EVENT_REASON_STOPLIMIT_TRIGGERED              ?  TextByLanguage("Срабатывание StopLimit-ордера","StopLimit-order triggered")                        :
      reason==EVENT_REASON_MODIFY                           ?  TextByLanguage("Модификация","Modified")                                                           :
      reason==EVENT_REASON_CANCEL                           ?  TextByLanguage("Отмена","Canceled")                                                                :
      reason==EVENT_REASON_EXPIRED                          ?  TextByLanguage("Истёк срок действия","Expired")                                                    :
      reason==EVENT_REASON_DONE                             ?  TextByLanguage("Рыночный запрос, выполненный в полном объёме","Fully completed market request")    :
      reason==EVENT_REASON_DONE_PARTIALLY                   ?  TextByLanguage("Выполненный частично рыночный запрос","Partially completed market request")        :
      reason==EVENT_REASON_VOLUME_ADD                       ?  TextByLanguage("Добавлен объём к позиции","Added volume to position")                              :
      reason==EVENT_REASON_VOLUME_ADD_PARTIALLY             ?  TextByLanguage("Добавлен объём к позиции частичным исполнением заявки","Volume added to the position by partially completed request")                 :
      reason==EVENT_REASON_VOLUME_ADD_BY_PENDING            ?  TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by pending order's triggered")                      :
      reason==EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY  ?  TextByLanguage("Добавлен объём к позиции частичной активацией отложенного ордера","Added volume to position by pending order's triggered partially")  :
      reason==EVENT_REASON_REVERSE                          ?  TextByLanguage("Разворот позиции","Position reversal")  :
      reason==EVENT_REASON_REVERSE_PARTIALLY                ?  TextByLanguage("Разворот позиции частичным исполнением заявки","Position reversal by partially completed of the request")                             :
      reason==EVENT_REASON_REVERSE_BY_PENDING               ?  TextByLanguage("Разворот позиции при срабатывании отложенного ордера","Position reversal on a triggered pending order")                               :
      reason==EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY     ?  TextByLanguage("Разворот позиции при при частичном срабатывании отложенного ордера","Position reversal on a partially triggered pending order")       :
      reason==EVENT_REASON_DONE_SL                          ?  TextByLanguage("Закрытие по StopLoss","Close by StopLoss triggered")                               :
      reason==EVENT_REASON_DONE_SL_PARTIALLY                ?  TextByLanguage("Частичное закрытие по StopLoss","Partially close by StopLoss triggered")           :
      reason==EVENT_REASON_DONE_TP                          ?  TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit triggered")                           :
      reason==EVENT_REASON_DONE_TP_PARTIALLY                ?  TextByLanguage("Частичное закрытие по TakeProfit","Partially close by TakeProfit triggered")       :
      reason==EVENT_REASON_DONE_BY_POS                      ?  TextByLanguage("Закрытие встречной позицией","Closed by opposite position")                        :
      reason==EVENT_REASON_DONE_PARTIALLY_BY_POS            ?  TextByLanguage("Частичное закрытие встречной позицией","Closed partially by opposite position")    :
      reason==EVENT_REASON_DONE_BY_POS_PARTIALLY            ?  TextByLanguage("Закрытие частью объёма встречной позиции","Closed by incomplete volume of opposite position") :
      reason==EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY  ?  TextByLanguage("Частичное закрытие частью объёма встречной позиции","Closed partially by incomplete volume of opposite position")  :
      reason==EVENT_REASON_BALANCE_REFILL                   ?  TextByLanguage("Пополнение баланса","Balance refill")                                              :
      reason==EVENT_REASON_BALANCE_WITHDRAWAL               ?  TextByLanguage("Снятие средств с баланса","Withdrawals from the balance")                          :
      reason==EVENT_REASON_ACCOUNT_CREDIT                   ?  TextByLanguage("Начисление кредита","Credit")                                                      :
      reason==EVENT_REASON_ACCOUNT_CHARGE                   ?  TextByLanguage("Дополнительные сборы","Additional charge")                                         :
      reason==EVENT_REASON_ACCOUNT_CORRECTION               ?  TextByLanguage("Корректирующая запись","Correction")                                               :
      reason==EVENT_REASON_ACCOUNT_BONUS                    ?  TextByLanguage("Перечисление бонусов","Bonus")                                                     :
      reason==EVENT_REASON_ACCOUNT_COMISSION                ?  TextByLanguage("Дополнительные комиссии","Additional commission")                                  :
      reason==EVENT_REASON_ACCOUNT_COMISSION_DAILY          ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission")                   :
      reason==EVENT_REASON_ACCOUNT_COMISSION_MONTHLY        ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission")                        :
      reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY    ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission")   :
      reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY  ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission")        :
      reason==EVENT_REASON_ACCOUNT_INTEREST                 ?  TextByLanguage("Начисления процентов на свободные средства","Interest rate")                       :
      reason==EVENT_REASON_BUY_CANCELLED                    ?  TextByLanguage("Отмененная сделка покупки","Canceled buy deal")                                    :
      reason==EVENT_REASON_SELL_CANCELLED                   ?  TextByLanguage("Отмененная сделка продажи","Canceled sell deal")                                   :
      reason==EVENT_REASON_DIVIDENT                         ?  TextByLanguage("Начисление дивиденда","Dividend operations")                                       :
      reason==EVENT_REASON_DIVIDENT_FRANKED                 ?  TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") :
      reason==EVENT_REASON_TAX                              ?  TextByLanguage("Начисление налога","Tax charges")                                                  :
      EnumToString(reason)
     );
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Методы упрощённого доступа к свойствам объекта-события           |
//+------------------------------------------------------------------+
//--- Возвращает (1) тип события, (2) время события в милисекундах, (3) статус события, (4) причину события, (5) тип сделки, (6) тикет сделки, 
//--- (7) тип ордера, на основании которого исполнена сделка, (8) тип открывающего ордера позиции, (9) тикет последнего ордера позиции, 
//--- (10) тикет первого ордера позиции, (11) идентификатор позиции, (12) идентификатор встречной позиции, (13) магик, (14) магик встречной, (15) время открытия позиции
   ENUM_TRADE_EVENT  TypeEvent(void)                                    const { return (ENUM_TRADE_EVENT)this.GetProperty(EVENT_PROP_TYPE_EVENT);           }
   long              TimeEvent(void)                                    const { return this.GetProperty(EVENT_PROP_TIME_EVENT);                             }
   ENUM_EVENT_STATUS Status(void)                                       const { return (ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT);        }
   ENUM_EVENT_REASON Reason(void)                                       const { return (ENUM_EVENT_REASON)this.GetProperty(EVENT_PROP_REASON_EVENT);        }
   ENUM_DEAL_TYPE    TypeDeal(void)                                     const { return (ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT);        }
   long              TicketDeal(void)                                   const { return this.GetProperty(EVENT_PROP_TICKET_DEAL_EVENT);                      }
   ENUM_ORDER_TYPE   TypeOrderEvent(void)                               const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT);      }
   ENUM_ORDER_TYPE   TypeFirstOrderPosition(void)                       const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION);   }
   long              TicketOrderEvent(void)                             const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT);                     }
   long              TicketFirstOrderPosition(void)                     const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_POSITION);                  }
   long              PositionID(void)                                   const { return this.GetProperty(EVENT_PROP_POSITION_ID);                            }
   long              PositionByID(void)                                 const { return this.GetProperty(EVENT_PROP_POSITION_BY_ID);                         }
   long              Magic(void)                                        const { return this.GetProperty(EVENT_PROP_MAGIC_ORDER);                            }
   long              MagicCloseBy(void)                                 const { return this.GetProperty(EVENT_PROP_MAGIC_BY_ID);                            }
   long              TimePosition(void)                                 const { return this.GetProperty(EVENT_PROP_TIME_ORDER_POSITION);                    }

//--- При смене направления позиции возвращает (1) тип ордера предыдущей позиции, (2) тикет ордера предыдущей позиции
//--- (3) тип ордера текущей позиции, (4) тикет ордера текущей позиции
//--- (5) тип и (6) тикет позиции до смены направления, (7) тип и (8) тикет позиции после смены направления
   ENUM_ORDER_TYPE   TypeOrderPosPrevious(void)                         const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE);   }
   long              TicketOrderPosPrevious(void)                       const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE); }
   ENUM_ORDER_TYPE   TypeOrderPosCurrent(void)                          const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT);  }
   long              TicketOrderPosCurrent(void)                        const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT);}
   ENUM_POSITION_TYPE TypePositionPrevious(void)                        const { return PositionTypeByOrderType(this.TypeOrderPosPrevious());                }
   ulong             TicketPositionPrevious(void)                       const { return this.TicketOrderPosPrevious();                                       }
   ENUM_POSITION_TYPE TypePositionCurrent(void)                         const { return PositionTypeByOrderType(this.TypeOrderPosCurrent());                 }
   ulong             TicketPositionCurrent(void)                        const { return this.TicketOrderPosCurrent();                                        }
   
//--- Возвращает (1) цену, на которой произошло событие, (2) цену открытия, (3) цену закрытия,
//--- (4) цену StopLoss, (5) цену TakeProfit, (6) профит, (7) Запрашиваемый объём ордера, 
//--- 8) Исполненный объём ордера, (9) Оставшийся объём ордера, (10) исполненный объём позиции
   double            PriceEvent(void)                                   const { return this.GetProperty(EVENT_PROP_PRICE_EVENT);                            }
   double            PriceOpen(void)                                    const { return this.GetProperty(EVENT_PROP_PRICE_OPEN);                             }
   double            PriceClose(void)                                   const { return this.GetProperty(EVENT_PROP_PRICE_CLOSE);                            }
   double            PriceStopLoss(void)                                const { return this.GetProperty(EVENT_PROP_PRICE_SL);                               }
   double            PriceTakeProfit(void)                              const { return this.GetProperty(EVENT_PROP_PRICE_TP);                               }
   double            Profit(void)                                       const { return this.GetProperty(EVENT_PROP_PROFIT);                                 }
   double            VolumeOrderInitial(void)                           const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL);                   }
   double            VolumeOrderExecuted(void)                          const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED);                  }
   double            VolumeOrderCurrent(void)                           const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT);                   }
   double            VolumePositionExecuted(void)                       const { return this.GetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED);               }

//--- При модификации цен возвращает (1) цену установки ордера, (2) StopLoss, (3) TakeProfit до модификации
   double            PriceOpenBefore(void)                              const { return this.GetProperty(EVENT_PROP_PRICE_OPEN_BEFORE);                      }
   double            PriceStopLossBefore(void)                          const { return this.GetProperty(EVENT_PROP_PRICE_SL_BEFORE);                        }
   double            PriceTakeProfitBefore(void)                        const { return this.GetProperty(EVENT_PROP_PRICE_TP_BEFORE);                        }
   double            PriceEventAsk(void)                                const { return this.GetProperty(EVENT_PROP_PRICE_EVENT_ASK);                        }
   double            PriceEventBid(void)                                const { return this.GetProperty(EVENT_PROP_PRICE_EVENT_BID);                        }

//--- Возвращает (1) символ, (2) символ встречной позиции
   string            Symbol(void)                                       const { return this.GetProperty(EVENT_PROP_SYMBOL);                                 }
   string            SymbolCloseBy(void)                                const { return this.GetProperty(EVENT_PROP_SYMBOL_BY_ID);                           }
   
//+------------------------------------------------------------------+

Так как большинство свойств класса заполняются в классе-коллекции событий после создания нового объекта класса-события в методе CreateNewEvent(), а далее устанавливается тип события посредством вызова метода SetTypeEvent() класса CEvent, то установку значения Digits() символа события сделаем в методе SetTypeEvent() класса CEvent наряду с определением событий-модификаций:

//+------------------------------------------------------------------+
//| Расшифровывает код события и устанавливает торговое событие      |
//+------------------------------------------------------------------+
void CEvent::SetTypeEvent(void)
  {
//--- Установка Digits() символа события
   this.m_digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS);
//--- Отложенный ордер установлен (проверяем на равенство коду события, так как здесь может быть только один флаг)
   if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_PLASED)
     {
      this.m_trade_event=TRADE_EVENT_PENDING_ORDER_PLASED;
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- Отложенный ордер удалён (проверяем на равенство коду события, так как здесь может быть только один флаг)
   if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_REMOVED)
     {
      this.m_trade_event=TRADE_EVENT_PENDING_ORDER_REMOVED;
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- Модифицирован отложенный ордер
   if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_MODIFY))
     {
      //--- Если модифицирована цена установки
      if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_PRICE))
        {
         this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_PRICE;
         //--- Если модифицированы StopLoss и TakeProfit
         if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL) && this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP))
            this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT;
         //--- Если модифицирован StopLoss
         else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL))
            this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS;
         //--- Если модифицирован TakeProfit
         else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP))
            this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT;
        }
      //--- Если цена установки не модифицирована
      else
        {
         //--- Если модифицированы StopLoss и TakeProfit
         if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL) && this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP))
            this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT;
         //--- Если модифицирован StopLoss
         else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL))
            this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_STOP_LOSS;
         //--- Если модифицирован TakeProfit
         else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP))
            this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT;
        }
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- Модифицирована позиция
   if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_MODIFY))
     {
      //--- Если модифицированы StopLoss и TakeProfit
      if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL) && this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP))
         this.m_trade_event=TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT;
      //--- Если модифицирован StopLoss
      else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL))
         this.m_trade_event=TRADE_EVENT_MODIFY_POSITION_STOP_LOSS;
      //--- Если модифицирован TakeProfit
      else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP))
         this.m_trade_event=TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT;
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- Позиция открыта (Проверяем наличие множества флагов в коде события)
   if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_OPENED))
     {
   //--- Если это изменение существующей позиции
      if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CHANGED))
        {
         //--- Если это отложенный ордер активирован ценой
         if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED))
           {
            //--- Если это разворот позиции
            if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE))
              {
               //--- проверяем флаг частичного открытия и устанавливаем торговое событие 
               //--- "разворот позиции активацией отложенного ордера" или "разворот позиции частичной активацией отложенного ордера"
               this.m_trade_event=
                 (
                  !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? 
                  TRADE_EVENT_POSITION_REVERSED_BY_PENDING : 
                  TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL
                 );
               this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
               return;
              }
            //--- Если это добавление объёма к позиции
            else
              {
               //--- проверяем флаг частичного открытия и устанавливаем торговое событие 
               //--- "добавлен объём к позиции активацией отложенного ордера" или "добавлен объём к позиции частичной активацией отложенного ордера"
               this.m_trade_event=
                 (
                  !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? 
                  TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING : 
                  TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL
                 );
               this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
               return;
              }
           }
         //--- Если изменение позиции было осуществлено сделкой ро рынку
         else
           {
            //--- Если это разворот позиции
            if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE))
              {
               //--- проверяем флаг частичного открытия и устанавливаем торговое событие "разворот позиции" или "разворот позиции частичным исполнением"
               this.m_trade_event=
                 (
                  !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? 
                  TRADE_EVENT_POSITION_REVERSED_BY_MARKET : 
                  TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL
                 );
               this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
               return;
              }
            //--- Если это добавление объёма к позиции
            else
              {
               //--- проверяем флаг частичного открытия и устанавливаем торговое событие "добавлен объём к позиции" или "добавлен объём к позиции частичным исполнением"
               this.m_trade_event=
                 (
                  !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? 
                  TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET : 
                  TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL
                 );
               this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
               return;
              }
           }
        }
   //--- Если это открытие новой позиции
      else
        {
         //--- Если это отложенный ордер активирован ценой
         if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED))
           {
            //--- проверяем флаг частичного открытия и устанавливаем торговое событие "отложенный ордер активирован" или "отложенный ордер активирован частично"
            this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_PENDING_ORDER_ACTIVATED : TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL);
            this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
            return;
           }
         //--- проверяем флаг частичного открытия и устанавливаем торговое событие "Позиция открыта" или "Позиция открыта частично"
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_OPENED : TRADE_EVENT_POSITION_OPENED_PARTIAL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
     }
     
//--- Позиция закрыта (Проверяем наличие множества флагов в коде события)
   if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CLOSED))
     {
      //--- если позиция закрыта по StopLoss
      if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL))
        {
         //--- проверяем флаг частичного закрытия и устанавливаем торговое событие "Позиция закрыта по StopLoss" или "Позиция закрыта по StopLoss частично"
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_SL : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- если позиция закрыта по TakeProfit
      else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP))
        {
         //--- проверяем флаг частичного закрытия и устанавливаем торговое событие "Позиция закрыта по TakeProfit" или "Позиция закрыта по TakeProfit частично"
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_TP : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- если позиция закрыта встречной
      else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS))
        {
         //--- проверяем флаг частичного закрытия и устанавливаем торговое событие "Позиция закрыта встречной" или "Позиция закрыта встречной частично"
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_POS : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- Если позиция закрыта
      else
        {
         //--- проверяем флаг частичного закрытия и устанавливаем торговое событие "Позиция закрыта" или "Позиция закрыта частично"
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED : TRADE_EVENT_POSITION_CLOSED_PARTIAL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
     }
//--- Балансная операция на счёте (уточняем событие по типу сделки)
   if(this.m_event_code==TRADE_EVENT_FLAG_ACCOUNT_BALANCE)
     {
      //--- Инициализируем торговое событие
      this.m_trade_event=TRADE_EVENT_NO_EVENT;
      //--- Возьмём тип сделки
      ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT);
      //--- если сделка - балансная операция
      if(deal_type==DEAL_TYPE_BALANCE)
        {
        //--- проверим профит сделки и установим событие (пополнение или снятие средств)
         this.m_trade_event=(this.GetProperty(EVENT_PROP_PROFIT)>0 ? TRADE_EVENT_ACCOUNT_BALANCE_REFILL : TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL);
        }
      //--- Остальные типы балансной операции совпадают с перечислением ENUM_DEAL_TYPE начиная от DEAL_TYPE_CREDIT
      else if(deal_type>DEAL_TYPE_BALANCE)
        {
        //--- установим это событие
         this.m_trade_event=(ENUM_TRADE_EVENT)deal_type;
        }
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
  }
//+------------------------------------------------------------------+

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

Забегая вперёд: при проверке отслеживания модификации цен установки отложенных ордеров в тестовом советнике, возникла необходимость найти самый далёкий от цены ордер. Просматривая свойства ордеров я понял, что быстрого и однозначного решения в библиотеке для этого нет. Выход — мы будем использовать одно из дополнительных целочисленных свойств ордера — профит в пунктах. Для отложенных ордеров это будет дистанцией ордера от цены в пунктах. Таким образом, для нахождения ордера, находящегося от цены дальше всех, мы просто будем искать ордер с наибольшей "прибылью" (дистанцией) в пунктах.
Ксати — к этой же ситуации относится и поиск всех отложенных ордеров по их направлению — чтобы найти самый далёкий от цены отложенный ордер, мы выбираем все ордера в одном направлении и фильтруем полученный список по наибольшей дистанции. В итоге получим один ордер из всех ордеров разного типа, но в одном направлении (BuyLimit, BuyStop и BuyStopLimit — все имеют одно направление — Buy. Для Sell — наоборот).

Изменим метод получения типа ордера по его направлению в листинге класса абстрактного ордера Order.mqh:

//+------------------------------------------------------------------+
//| Возвращает профит ордера в пунктах                               |
//+------------------------------------------------------------------+
int COrder::ProfitInPoints(void) const
  {
   MqlTick tick={0};
   string symbol=this.Symbol();
   if(!::SymbolInfoTick(symbol,tick))
      return 0;
   ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)this.TypeOrder();
   double point=::SymbolInfoDouble(symbol,SYMBOL_POINT);
   if(type==ORDER_TYPE_CLOSE_BY || 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_POSITION)
     {
      if(type==ORDER_TYPE_BUY)
         return int((tick.bid-this.PriceOpen())/point);
      else if(type==ORDER_TYPE_SELL)
         return int((this.PriceOpen()-tick.ask)/point);
     }
   else if(this.Status()==ORDER_STATUS_MARKET_PENDING)
     {
      if(type==ORDER_TYPE_BUY_LIMIT || type==ORDER_TYPE_BUY_STOP || type==ORDER_TYPE_BUY_STOP_LIMIT)
         return (int)fabs((tick.bid-this.PriceOpen())/point);
      else if(type==ORDER_TYPE_SELL_LIMIT || type==ORDER_TYPE_SELL_STOP || type==ORDER_TYPE_SELL_STOP_LIMIT)
         return (int)fabs((this.PriceOpen()-tick.ask)/point);
     }
   return 0;
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства ордера               |
//+------------------------------------------------------------------+
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_TICKET ?  TextByLanguage("Сделка на основании ордера с тикетом","Deal by order ticket")+
         (!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") :
          ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")"
         )  :
      property==ORDER_PROP_TIME_CLOSE_MSC    ?  TextByLanguage("Время закрытия в милисекундах","Closing time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)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 ? TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")" : "0")
         )  :
      property==ORDER_PROP_STATE             ?  TextByLanguage("Состояние","Statе")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": \""+this.StateDescription()+"\""
         )  :
   //--- Дополнительное свойство
      property==ORDER_PROP_STATUS            ?  TextByLanguage("Статус","Status")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": \""+this.StatusDescription()+"\""
         )  :
      property==ORDER_PROP_PROFIT_PT         ?  (
                                                 this.Status()==ORDER_STATUS_MARKET_PENDING ? 
                                                 TextByLanguage("Дистанция от цены в пунктах","Distance from price in points") : 
                                                 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"))
         )  :
      property==ORDER_PROP_GROUP_ID          ?  TextByLanguage("Идентификатор группы","Group's identifier")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property is not support") :
          ": "+(string)this.GetProperty(property)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

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

Изменения класса абстрактного ордера на этом завершены.

Теперь нам необходимо создать ещё один клас-наследник класса абстрактного события CEvent. Это будет класс события-модификации.

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

  • Может быть изменена только цена установки ордера — отправляем новую цену установки отложенного ордера,
  • может быть изменена цена установки ордера и StopLoss — отправляем новую цену установки отложенного ордера,
  • может быть изменена цена установки ордера и TakeProfit — отправляем новую цену установки отложенного ордера,
  • может быть изменена цена установки ордера,  StopLoss и TakeProfit — отправляем новую цену установки отложенного ордера,
  • может быть изменён StopLoss ордера — отправляем новую цену StopLoss,
  • может быть изменён TakeProfit ордера — отправляем новую цену TakeProfit.
  • Может быть изменён StopLoss позиции — отправляем StopLoss позиции,
  • Может быть изменён TakeProfit позиции — отправляем TakeProfit позиции,
  • Может быть изменён StopLoss и TakeProfit позиции — отправляем цену открытия позиции.

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

В каталоге библиотеки \MQL5\Include\DoEasy\Objects\Events в новом файле EventModify.mqh создадим новый класс CEventModify.
Базовым классом для него укажем класс абстрактного события CEvent.
Не забудем подключить к файлу класса-модификации файл класса абстрактного события.
Так как класс у нас будет небольшой, то сразу приведу его полный листинг для самостоятельного изучения — тем более, что идентичный класс нами был рассмотрен в шестой части описания библиотеки при реализации изменений класса CEventPositionOpen.

//+------------------------------------------------------------------+
//|                                                  EventModify.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 "Event.mqh"
//+------------------------------------------------------------------+
//| Событие установки отложенного ордера                             |
//+------------------------------------------------------------------+
class CEventModify : public CEvent
  {
private:
   double            m_price;                               // Цена, отправляемая в событие
//--- Создаёт и возвращает краткое сообщение события
   string            EventsMessage(void);
public:
//--- Конструктор
                     CEventModify(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MODIFY,event_code,ticket),m_price(0) {}
//--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) Выводит в журнал краткое сообщение о событии, (2) Отправляет событие на график
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+
//| Возвращает истину, если событие поддерживает переданное          |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+
bool CEventModify::SupportProperty(ENUM_EVENT_PROP_INTEGER property)
  {
   if(property==EVENT_PROP_TYPE_DEAL_EVENT         ||
      property==EVENT_PROP_TICKET_DEAL_EVENT       ||
      property==EVENT_PROP_TYPE_ORDER_POSITION     ||
      property==EVENT_PROP_TICKET_ORDER_POSITION   ||
      property==EVENT_PROP_POSITION_ID             ||
      property==EVENT_PROP_POSITION_BY_ID          ||
      property==EVENT_PROP_TIME_ORDER_POSITION
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

//| Возвращает истину, если событие поддерживает переданное          |
//| вещественное свойство, возвращает ложь в противном случае        |
//+------------------------------------------------------------------+
bool CEventModify::SupportProperty(ENUM_EVENT_PROP_DOUBLE property)
  {
   if(property==EVENT_PROP_PRICE_CLOSE             ||
      property==EVENT_PROP_PROFIT
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Выводит в журнал краткое сообщение о событии                     |
//+------------------------------------------------------------------+
void CEventModify::PrintShort(void)
  {
   ::Print(this.EventsMessage());
  }
//+------------------------------------------------------------------+
//| Отправляет событие на график                                     |
//+------------------------------------------------------------------+
void CEventModify::SendEvent(void)
  {
   this.PrintShort();
   ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.TicketOrderEvent(),this.m_price,this.Symbol());
  }
//+------------------------------------------------------------------+
//| Создаёт и возвращает краткое сообщение события                   |
//+------------------------------------------------------------------+
string CEventModify::EventsMessage(void)
  {
//--- (1) заголовок, (2) магический номер
   string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n";
   string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : "");
   string text="";

//--- Модифицирована цена отложенного ордера
   if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE)
     {
      string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent();
      string price="["+::DoubleToString(this.PriceOpenBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceOpen(),this.m_digits)+"]";
      text=order+TextByLanguage(": модифицирована цена: ",": modified price: ")+price+magic;
      this.m_price=this.PriceOpen();
     }
//--- Модифицирована цена отложенного ордера и его StopLoss
   else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS)
     {
      string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent();
      string price="["+::DoubleToString(this.PriceOpenBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceOpen(),this.m_digits)+"]";
      string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]";
      text=order+TextByLanguage(": модифицирована цена: ",": modified price: ")+price+TextByLanguage(" и"," and")+" StopLoss: "+sl+magic;
      this.m_price=this.PriceOpen();
     }
//--- Модифицирована цена отложенного ордера и его TakeProfit
   else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT)
     {
      string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent();
      string price="["+::DoubleToString(this.PriceOpenBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceOpen(),this.m_digits)+"]";
      string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]";
      text=order+TextByLanguage(": модифицирована цена: ",": modified price: ")+price+TextByLanguage(" и"," and")+" TakeProfit: "+tp+magic;
      this.m_price=this.PriceOpen();
     }
//--- Модифицирована цена отложенного ордера, его StopLoss и TakeProfit
   else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT)
     {
      string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent();
      string price="["+::DoubleToString(this.PriceOpenBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceOpen(),this.m_digits)+"]";
      string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]";
      string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]";
      text=order+TextByLanguage(": модифицирована цена: ",": modified price: ")+price+", StopLoss: "+sl+TextByLanguage(" и"," and")+" TakeProfit: "+tp+magic;
      this.m_price=this.PriceOpen();
     }
//--- Модифицирован StopLoss отложенного ордера
   else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS)
     {
      string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent();
      string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]";
      text=order+TextByLanguage(": модифицирован StopLoss: ",": modified StopLoss: ")+sl+magic;
      this.m_price=this.PriceStopLoss();
     }
//--- Модифицирован TakeProfit отложенного ордера
   else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT)
     {
      string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent();
      string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]";
      text=order+TextByLanguage(": модифицирован TakeProfit: ",": modified TakeProfit: ")+tp+magic;
      this.m_price=this.PriceTakeProfit();
     }
//--- Модифицирован StopLoss и TakeProfit отложенного ордера
   else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT)
     {
      string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent();
      string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]";
      string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]";
      text=order+TextByLanguage(": модифицирован StopLoss: ",": modified StopLoss: ")+sl+TextByLanguage(" и"," and")+" TakeProfit: "+tp+magic;
      this.m_price=this.PriceOpen();
     }
//--- Модифицирован StopLoss позиции
   else if(this.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS)
     {
      string order=PositionTypeDescription(this.TypePositionCurrent())+" #"+(string)this.TicketPositionCurrent();
      string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]";
      text=order+TextByLanguage(": модифицирован StopLoss: ",": modified StopLoss: ")+sl+magic;
      this.m_price=this.PriceStopLoss();
     }
//--- Модифицирован TakeProfit позиции
   else if(this.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT)
     {
      string order=PositionTypeDescription(this.TypePositionCurrent())+" #"+(string)this.TicketPositionCurrent();
      string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]";
      text=order+TextByLanguage(": модифицирован TakeProfit: ",": modified TakeProfit: ")+tp+magic;
      this.m_price=this.PriceTakeProfit();
     }
//--- Модифицирован StopLoss и TakeProfit позиции
   else if(this.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT)
     {
      string order=PositionTypeDescription(this.TypePositionCurrent())+" #"+(string)this.TicketPositionCurrent();
      string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]";
      string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]";
      text=order+TextByLanguage(": модифицирован StopLoss: ",": modified StopLoss: ")+sl+TextByLanguage(" и"," and")+" TakeProfit: "+tp+magic;
      this.m_price=this.PriceOpen();
     }
   return head+this.Symbol()+" "+text;
  }
//+------------------------------------------------------------------+

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

Внесём необходимые доработки в класс CEventsCollection в файле EventsCollection.mqh из папки библиотеки \MQL5\Include\DoEasy\Collections.

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

//+------------------------------------------------------------------+
//|                                             EventsCollection.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 "ListObj.mqh"
#include "..\Services\Select.mqh"
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\EventBalanceOperation.mqh"
#include "..\Objects\Events\EventOrderPlaced.mqh"
#include "..\Objects\Events\EventOrderRemoved.mqh"
#include "..\Objects\Events\EventPositionOpen.mqh"
#include "..\Objects\Events\EventPositionClose.mqh"
#include "..\Objects\Events\EventModify.mqh"
//+------------------------------------------------------------------+
//| Коллекция событий счёта                                          |
//+------------------------------------------------------------------+
class CEventsCollection : public CListObj
  {
private:
   CListObj          m_list_events;                   // Список событий
   bool              m_is_hedge;                      // Флаг хедж-счёта
   long              m_chart_id;                      // Идентификатор графика управляющей программы
   int               m_trade_event_code;              // Код торгового события
   ENUM_TRADE_EVENT  m_trade_event;                   // Торговое событие на счёте
   CEvent            m_event_instance;                // Объект-событие для поиска по свойству
   MqlTick           m_tick;                          // Структура последнего тика
   

И в конструкторе класса инициализируем структуру тика:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CEventsCollection::CEventsCollection(void) : m_trade_event(TRADE_EVENT_NO_EVENT),m_trade_event_code(TRADE_EVENT_FLAG_NO_EVENT)
  {
   this.m_list_events.Clear();
   this.m_list_events.Sort(SORT_BY_EVENT_TIME_EVENT);
   this.m_list_events.Type(COLLECTION_EVENTS_ID);
   this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
   this.m_chart_id=::ChartID();
   ::ZeroMemory(this.m_tick);
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Создаёт торговое событие в зависимости от типа изменения ордера  |
//+------------------------------------------------------------------+
void CEventsCollection::CreateNewEvent(COrderControl* order)
  {
   if(!::SymbolInfoTick(order.Symbol(),this.m_tick))
     {
      Print(DFUN,TextByLanguage("Не удалось получить текущие цены по символу события ","Failed to get current prices by event symbol "),order.Symbol());
      return;
     }
   CEvent* event=NULL;
//--- Сработал отложенный StopLimit-ордер
   if(order.GetChangeType()==CHANGE_TYPE_ORDER_TYPE)
     {
      this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_PLASED;
      event=new CEventOrderPlased(this.m_trade_event_code,order.Ticket());
     }
//--- Модификация
   else
     {
      //--- Модифицирована цена отложенного ордера
      if(order.GetChangeType()==CHANGE_TYPE_ORDER_PRICE)
         this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_PRICE;
      //--- Модифицирована цена отложенного ордера и его StopLoss
      else if(order.GetChangeType()==CHANGE_TYPE_ORDER_PRICE_STOP_LOSS)
         this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_PRICE+TRADE_EVENT_FLAG_SL;
      //--- Модифицирована цена отложенного ордера и его TakeProfit
      else if(order.GetChangeType()==CHANGE_TYPE_ORDER_PRICE_TAKE_PROFIT)
         this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_PRICE+TRADE_EVENT_FLAG_TP;
      //--- Модифицирована цена отложенного ордера, его StopLoss и TakeProfit
      else if(order.GetChangeType()==CHANGE_TYPE_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT)
         this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_PRICE+TRADE_EVENT_FLAG_SL+TRADE_EVENT_FLAG_TP;
      //--- Модифицирован StopLoss отложенного ордера
      else if(order.GetChangeType()==CHANGE_TYPE_ORDER_STOP_LOSS)
         this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_SL;
      //--- Модифицирован TakeProfit отложенного ордера
      else if(order.GetChangeType()==CHANGE_TYPE_ORDER_TAKE_PROFIT)
         this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_TP;
      //--- Модифицирован StopLoss и TakeProfit отложенного ордера
      else if(order.GetChangeType()==CHANGE_TYPE_ORDER_STOP_LOSS_TAKE_PROFIT)
         this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_SL+TRADE_EVENT_FLAG_TP;

      //--- Модифицирован StopLoss позиции
      else if(order.GetChangeType()==CHANGE_TYPE_POSITION_STOP_LOSS)
         this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_MODIFY+TRADE_EVENT_FLAG_SL;
      //--- Модифицирован TakeProfit позиции
      else if(order.GetChangeType()==CHANGE_TYPE_POSITION_TAKE_PROFIT)
         this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_MODIFY+TRADE_EVENT_FLAG_TP;
      //--- Модифицирован StopLoss и TakeProfit позиции
      else if(order.GetChangeType()==CHANGE_TYPE_POSITION_STOP_LOSS_TAKE_PROFIT)
         this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_MODIFY+TRADE_EVENT_FLAG_SL+TRADE_EVENT_FLAG_TP;
      
      //--- Создаём событие модификации
      event=new CEventModify(this.m_trade_event_code,order.Ticket());
     }
//--- Создание события
   if(event!=NULL)
     {
      event.SetProperty(EVENT_PROP_TIME_EVENT,order.Time());                        // Время события
      event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_STOPLIMIT_TRIGGERED);  // Причина события (из перечисления ENUM_EVENT_REASON)
      event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,PositionTypeByOrderType((ENUM_ORDER_TYPE)order.TypeOrderPrev())); // Тип ордера, срабатывание которого привело к событию
      event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket());               // Тикет ордера , срабатывание которого привело к событию
      event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder());             // Тип ордера события
      event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket());              // Тикет ордера события
      event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder());          // Тип первого ордера позиции
      event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket());           // Тикет первого ордера позиции
      event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID());                 // Идентификатор позиции
      event.SetProperty(EVENT_PROP_POSITION_BY_ID,0);                               // Идентификатор встречной позиции
      event.SetProperty(EVENT_PROP_MAGIC_BY_ID,0);                                  // Магический номер встречной позиции
         
      event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrderPrev());      // Тип ордера позиции до смены направления
      event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order.Ticket());           // Тикет ордера позиции до смены направления
      event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order.TypeOrder());         // Тип ордера текущей позиции
      event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order.Ticket());          // Тикет ордера текущей позиции
      
      event.SetProperty(EVENT_PROP_PRICE_OPEN_BEFORE,order.PricePrev());            // Цена установки ордера до модификации
      event.SetProperty(EVENT_PROP_PRICE_SL_BEFORE,order.StopLossPrev());           // Цена StopLoss до модификации
      event.SetProperty(EVENT_PROP_PRICE_TP_BEFORE,order.TakeProfitPrev());         // Цена TakeProfit до модификации
      event.SetProperty(EVENT_PROP_PRICE_EVENT_ASK,this.m_tick.ask);                // Цена Ask в момент события
      event.SetProperty(EVENT_PROP_PRICE_EVENT_BID,this.m_tick.bid);                // Цена Bid в момент события
         
      event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic());                      // Магический номер ордера
      event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimePrev());           // Время первого ордера позиции
      event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PricePrev());                  // Цена, на которой произошло событие
      event.SetProperty(EVENT_PROP_PRICE_OPEN,order.Price());                       // Цена установки ордера
      event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.Price());                      // Цена закрытия ордера
      event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss());                      // Цена StopLoss ордера
      event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit());                    // Цена TakeProfit ордера
      event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume());            // Запрашиваемый объём ордера
      event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,0);                        // Исполненный объём ордера
      event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.Volume());            // Оставшийся (неисполненный) объём ордера
      event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,0);                     // Исполненный объём позиции
      event.SetProperty(EVENT_PROP_PROFIT,0);                                       // Профит
      event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol());                          // Символ ордера
      event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order.Symbol());                    // Символ встречной позиции
      //--- Установка идентификатора графика управляющей программы, расшифровка кода события и установка типа события
      event.SetChartID(this.m_chart_id);
      event.SetTypeEvent();
      //--- Если объекта-события нет в списке - добавляем
      if(!this.IsPresentEventInList(event))
        {
         this.m_list_events.InsertSort(event);
         //--- Отправляем сообщение о событии и устанавливаем значение последнего торгового события
         event.SendEvent();
         this.m_trade_event=event.TradeEvent();
        }
      //--- Если это событие уже есть в списке - удаляем новый объект-событие и выводим отладочное сообщение
      else
        {
         ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event is already in the list."));
         delete event;
        }
     }
  }
//+------------------------------------------------------------------+

Обработка условий разных типов модификации достаточно проста и понятна, и описана в комментариях к коду — в зависимости от типа изменения ордера/позиции создаётся код события из набора флагов, и этот код отправляется в конструктор класса CEventModify при создании нового события-модификации.

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

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

Тест событий модификации ордеров и позиций

Для тестирования нам необходимо будет дополнить существующий набор кнопок тестового советника из седьмой статьи.
Добавим в набор его кнопок ещё три кнопки и обработчики их нажатия: Set StopLoss, Set TakeProfit и Trailing All.
Первые две кнопки будут устанавливать стоплосс и тейкпрофит всем ордерам и позициям, у которых эти уровни отсутствуют, третья же кнопка будет у нас иметь два положения — Вкл/Выкл, т.е., при её нажатии она останется нажатой и начнут работать две функции-трала. В результате советник начнёт тралить стопы всех позиций и подтягивать за ценой все активные отложенные ордера. Повторное нажатие кнопки отключит оба трала.

Возьмём советник TestDoEasyPart07.mq5 из расположения \MQL5\Experts\TestDoEasy\Part07 и сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part08  под именем TestDoEasyPart08.mq5.

В перечисление кнопок добавим три новые константы и изменим общее количество кнопок с 17 на 20 в соответствующей макроподстановке:

//--- enums
enum ENUM_BUTTONS
  {
   BUTT_BUY,
   BUTT_BUY_LIMIT,
   BUTT_BUY_STOP,
   BUTT_BUY_STOP_LIMIT,
   BUTT_CLOSE_BUY,
   BUTT_CLOSE_BUY2,
   BUTT_CLOSE_BUY_BY_SELL,
   BUTT_SELL,
   BUTT_SELL_LIMIT,
   BUTT_SELL_STOP,
   BUTT_SELL_STOP_LIMIT,
   BUTT_CLOSE_SELL,
   BUTT_CLOSE_SELL2,
   BUTT_CLOSE_SELL_BY_BUY,
   BUTT_DELETE_PENDING,
   BUTT_CLOSE_ALL,
   BUTT_PROFIT_WITHDRAWAL,
   BUTT_SET_STOP_LOSS,   
   BUTT_SET_TAKE_PROFIT, 
   BUTT_TRAILING_ALL     
  };
#define TOTAL_BUTT   (20)

Во входные параметры добавим переменные для указания дистанции уровня StopLoss от цены, шага трала, количество пунктов прибыли для начала трейлинга и размеры StopLoss и TakeProfit в пунктах для их установки по нажатию соответствующих кнопок (параметры InpStopLoss и InpTakeProfit используются для установки стоп-уровней сразу при открытии позиции/выставлении отложенного ордера).
Добавим в список глобальных переменных необходимые переменные для хранения значений вновь добавленных входных переменных, и переменную-флаг, указывающую на активность функций трейлингов:

//--- input variables
input ulong    InpMagic             =  123;  // Magic number
input double   InpLots              =  0.1;  // Lots
input uint     InpStopLoss          =  50;   // StopLoss in points
input uint     InpTakeProfit        =  50;   // TakeProfit in points
input uint     InpDistance          =  50;   // Pending orders distance (points)
input uint     InpDistanceSL        =  50;   // StopLimit orders distance (points)
input uint     InpSlippage          =  0;    // Slippage in points
input double   InpWithdrawal        =  10;   // Withdrawal funds (in tester)
input uint     InpButtShiftX        =  40;   // Buttons X shift 
input uint     InpButtShiftY        =  10;   // Buttons Y shift 
input uint     InpTrailingStop      =  50;   // Trailing Stop (points)
input uint     InpTrailingStep      =  20;   // Trailing Step (points)
input uint     InpTrailingStart     =  0   // Trailing Start (points)
input uint     InpStopLossModify    =  20;   // StopLoss for modification (points)
input uint     InpTakeProfitModify  =  60;   // TakeProfit for modification (points)
//--- global variables
CEngine        engine;
CTrade         trade;
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ulong          magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           slippage;
bool           trailing_on;          
double         trailing_stop;       
double         trailing_step;       
uint           trailing_start;      
uint           stoploss_to_modify;  
uint           takeprofit_to_modify;
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Вызов данной функции выводит в журнал список констант перечисления, 
//--- заданного в файле DELib.mqh в строках 22 и 25, для проверки корректности констант
   //EnumNumbersTest();
//--- check for undeleted objects
   if(IsPresentObects(prefix))   
      ObjectsDeleteAll(0,prefix);
//--- set global variables
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;      
   stoploss_to_modify=InpStopLossModify;    
   takeprofit_to_modify=InpTakeProfitModify;
//--- create buttons
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_FAILED;
//--- set button trailing
   ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);
//--- setting trade parameters
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Напишем функцию для определения наличия на графике графического объекта с заданным префиксом и функцию контроля состояния кнопок — просто для удобства восприятия кода вынесем этот контроль из обработчика OnTick() советника в отдельную функцию:

//+------------------------------------------------------------------+
//| Возвращает флаг наличия объекта с префиксом                      |
//+------------------------------------------------------------------+
bool IsPresentObects(const string object_prefix)
  {
   for(int i=ObjectsTotal(0)-1;i>=0;i--)
      if(StringFind(ObjectName(0,i,0),object_prefix)>WRONG_VALUE)
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Контроль состояния кнопок                                        |
//+------------------------------------------------------------------+
void PressButtonsControl(void)
  {
   int total=ObjectsTotal(0);
   for(int i=0;i<total;i++)
     {
      string obj_name=ObjectName(0,i);
      if(StringFind(obj_name,prefix+"BUTT_")<0)
         continue;
      PressButtonEvents(obj_name);
     }
  }
//+------------------------------------------------------------------+

Внесём изменения в функцию установки состояния объекта-кнопки:

//+------------------------------------------------------------------+
//| Устанавливает состояние кнопки                                   |
//+------------------------------------------------------------------+
void ButtonState(const string name,const bool state)
  {
   ObjectSetInteger(0,name,OBJPROP_STATE,state);
   if(name==butt_data[TOTAL_BUTT-1].name)
     {
      if(state)
         ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'220,255,240');
      else
         ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'240,240,240');
     }
  }
//+------------------------------------------------------------------+

Здесь:
устанавливаем состояние кнопки
(нажата/отжата),
если это самая последняя кнопка и
если она имеет состояние "нажата"
, то изменим цвет фона объекта-кнопки,
иначе
— вернём цвет фона в состояние "не нажата".

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

//+------------------------------------------------------------------+
//| Преобразует перечисление в текст кнопки                          |
//+------------------------------------------------------------------+
string EnumToButtText(const ENUM_BUTTONS member)
  {
   string txt=StringSubstr(EnumToString(member),5);
   StringToLower(txt);
   StringReplace(txt,"set_take_profit","Set TakeProfit");
   StringReplace(txt,"set_stop_loss","Set StopLoss");    
   StringReplace(txt,"trailing_all","Trailing All");     
   StringReplace(txt,"buy","Buy");
   StringReplace(txt,"sell","Sell");
   StringReplace(txt,"_limit"," Limit");
   StringReplace(txt,"_stop"," Stop");
   StringReplace(txt,"close_","Close ");
   StringReplace(txt,"2"," 1/2");
   StringReplace(txt,"_by_"," by ");
   StringReplace(txt,"profit_","Profit ");
   StringReplace(txt,"delete_","Delete ");
   return txt;
  }
//+------------------------------------------------------------------+

Теперь нам необходимо обрабатывать нажатие трёх новых кнопок. Для этого в функцию обработки нажатия кнопок PressButtonEvents() — в самом её конце (после блока кода обработки нажатия кнопки вывода средств) — допишем строки кода:

      //--- Если нажата кнопка BUTT_PROFIT_WITHDRAWAL: Вывести средства со счёта
      if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL))
        {
         //--- Если программа запущена в тестере
         if(MQLInfoInteger(MQL_TESTER))
           {
            //--- Эмулируем вывод средств
            TesterWithdrawal(withdrawal);
           }
        }
      //--- Если нажата кнопка BUTT_SET_STOP_LOSS: Установить StopLoss всем ордерам и позициям, где его нету
      if(button==EnumToString(BUTT_SET_STOP_LOSS))
        {
         SetStopLoss();
        }
      //--- Если нажата кнопка BUTT_SET_TAKE_PROFIT: Установить TakeProfit всем ордерам и позициям, где его нету
      if(button==EnumToString(BUTT_SET_TAKE_PROFIT))
        {
         SetTakeProfit();
        }
      //--- Подождём 1/10 секунды
      Sleep(100);
      //--- "Отожмём" кнопку (если это не кнопка трейлинга)
      if(button!=EnumToString(BUTT_TRAILING_ALL))
         ButtonState(button_name,false);
      //--- Если нажата кнопка BUTT_TRAILING_ALL
      else
        {
         //--- Поставим цвет активной кнопки
         ButtonState(button_name,true);
         trailing_on=true;
        }
      //--- перерисуем чарт
      ChartRedraw();
     }
   //--- Вернём цвет неактивной кнопки (если это кнопка трейлинга)
   else if(button==EnumToString(BUTT_TRAILING_ALL))
     {
      ButtonState(button_name,false);
      trailing_on=false;
      //--- перерисуем чарт
      ChartRedraw();
     }
  }
//+------------------------------------------------------------------+

Как видим, здесь вызываются две новые функции: SetStopLoss() и SetTakeProfit() для установки соответствующих уровней ордерам и позициям:

//+------------------------------------------------------------------+
//| Установка StopLoss всем ордерам и позициям                       |
//+------------------------------------------------------------------+
void SetStopLoss(void)
  {
   if(stoploss_to_modify==0)
      return;
//--- Установка StopLoss всем позициям, где его нету
   CArrayObj* list=engine.GetListMarketPosition();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL);
   if(list==NULL)
      return;
   int total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* position=list.At(i);
      if(position==NULL)
         continue;
      double sl=CorrectStopLoss(position.Symbol(),position.TypeByDirection(),0,stoploss_to_modify);
      trade.PositionModify(position.Ticket(),sl,position.TakeProfit());
     }
//--- Установка StopLoss всем отложенным ордерам, где его нету
   list=engine.GetListMarketPendings();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL);
   if(list==NULL)
      return;
   total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* order=list.At(i);
      if(order==NULL)
         continue;
      double sl=CorrectStopLoss(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),stoploss_to_modify);
      trade.OrderModify(order.Ticket(),order.PriceOpen(),sl,order.TakeProfit(),trade.RequestTypeTime(),trade.RequestExpiration(),order.PriceStopLimit());
     }
  }
//+------------------------------------------------------------------+
//| Установка TakeProfit всем ордерам и позициям                     |
//+------------------------------------------------------------------+
void SetTakeProfit(void)
  {
   if(takeprofit_to_modify==0)
      return;                 
//--- Установка TakeProfit всем позициям, где его нету
   CArrayObj* list=engine.GetListMarketPosition();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL);
   if(list==NULL)
      return;
   int total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* position=list.At(i);
      if(position==NULL)
         continue;
      double tp=CorrectTakeProfit(position.Symbol(),position.TypeByDirection(),0,takeprofit_to_modify);
      trade.PositionModify(position.Ticket(),position.StopLoss(),tp);
     }
//--- Установка TakeProfit всем отложенным ордерам, где его нету
   list=engine.GetListMarketPendings();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL);
   if(list==NULL)
      return;
   total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* order=list.At(i);
      if(order==NULL)
         continue;
      double tp=CorrectTakeProfit(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),takeprofit_to_modify);
      trade.OrderModify(order.Ticket(),order.PriceOpen(),order.StopLoss(),tp,trade.RequestTypeTime(),trade.RequestExpiration(),order.PriceStopLimit());
     }
  }
//+------------------------------------------------------------------+

Функции достаточно просты. Рассмотрим на примере установки TakeProfit всем ордерам и позициям, где он не установлен:

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

Осталось написать функции трейлинга стопов позиций и цен установки ордеров:

//+------------------------------------------------------------------+
//| Трал стопа позиции с наибольшей прибылью                         |
//+------------------------------------------------------------------+
void TrailingPositions(void)
  {
   MqlTick tick;
   if(!SymbolInfoTick(Symbol(),tick))
      return;
   double stop_level=StopLevel(Symbol(),2)*Point();
   //--- Получаем список всех открытых позиций
   CArrayObj* list=engine.GetListMarketPosition();
   //--- Выбираем из списка только позиции Buy
   CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
   //--- Сортируем список по прибыли с учётом комиссии и свопа
   list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
   //--- Получаем индекс позиции Buy с наибольшей прибылью
   int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
   if(index_buy>WRONG_VALUE)
     {
      COrder* buy=list_buy.At(index_buy);
      if(buy!=NULL)
        {
         //--- Рассчитываем новый StopLoss
         double sl=NormalizeDouble(tick.bid-trailing_stop,Digits());
         //--- Если цена и отложенный от неё уровень StopLevel выше нового StopLoss (соблюдена дистанция по StopLevel)
         if(tick.bid-stop_level>sl) 
           {
            //--- Если новый уровень StopLoss выше, чем шаг трала, отложенный от текущего StopLoss
            if(buy.StopLoss()+trailing_step<sl)
              {
               //--- Если тралим при любой прибыли или прибыль позиции в пунктах больше значения начала трейлинга - модифицируем StopLoss
               if(trailing_start==0 || buy.ProfitInPoints()>(int)trailing_start)
                  trade.PositionModify(buy.Ticket(),sl,buy.TakeProfit());
              }
           }
        }
     }
   //--- Выбираем из списка только позиции Sell
   CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
   //--- Сортируем список по прибыли с учётом комиссии и свопа
   list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
   //--- Получаем индекс позиции Sell с наибольшей прибылью
   int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
   if(index_sell>WRONG_VALUE)
     {
      COrder* sell=list_sell.At(index_sell);
      if(sell!=NULL)
        {
         //--- Рассчитываем новый StopLoss
         double sl=NormalizeDouble(tick.ask+trailing_stop,Digits());
         //--- Если цена и отложенный от неё уровень StopLevel ниже нового StopLoss (соблюдена дистанция по StopLevel)
         if(tick.ask+stop_level<sl) 
           {
            //--- Если новый уровень StopLoss ниже, чем шаг трала, отложенный от текущего StopLoss или у позиции ещё не установлен StopLoss
            if(sell.StopLoss()-trailing_step>sl || sell.StopLoss()==0)
              {
               //--- Если тралим при любой прибыли или прибыль позиции в пунктах больше значения начала трейлинга - модифицируем StopLoss
               if(trailing_start==0 || sell.ProfitInPoints()>(int)trailing_start)
                  trade.PositionModify(sell.Ticket(),sl,sell.TakeProfit());
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Трал самых дальних отложенных ордеров                            |
//+------------------------------------------------------------------+
void TrailingOrders(void)
  {
   MqlTick tick;
   if(!SymbolInfoTick(Symbol(),tick))
      return;
   double stop_level=StopLevel(Symbol(),2)*Point();
//--- Получаем список всех установленных ордеров
   CArrayObj* list=engine.GetListMarketPendings();
//--- Выбираем из списка только ордера в направлении Buy
   CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_BUY,EQUAL);
   //--- Сортируем список по дистанции от цены в пунктах (по прибыли в пунктах)
   list_buy.Sort(SORT_BY_ORDER_PROFIT_PT);
   //--- Получаем индекс ордера в направлении Buy с наибольшей дистанцией
   int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_PT);
   if(index_buy>WRONG_VALUE)
     {
      COrder* buy=list_buy.At(index_buy);
      if(buy!=NULL)
        {
         //--- Если ордер установлен ниже цены (BuyLimit), и его необходимо "поднимать" за ценой
         if(buy.TypeOrder()==ORDER_TYPE_BUY_LIMIT)
           {
            //--- Рассчитываем новую цену установки ордера и стоп-уровни от новой цены
            double price=NormalizeDouble(tick.ask-trailing_stop,Digits());
            double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0);
            double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0);
            //--- Если рассчитанная цена ниже, чем дистанция StopLevel, отложенная от цены установки ордера Ask (соблюдена дистанция по StopLevel)
            if(price<tick.ask-stop_level) 
              {
               //--- Если рассчитанная цена выше, чем шаг трала, отложенный от цены установки ордера - модифицируем цену установки ордера
               if(price>buy.PriceOpen()+trailing_step)
                 {
                  trade.OrderModify(buy.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),buy.PriceStopLimit());
                 }
              }
           }
         //--- Если ордер установлен выше цены (BuyStop и BuyStopLimit), и его необходимо "опускать" за ценой
         else
           {
            //--- Рассчитываем новую цену установки ордера и стоп-уровни от новой цены
            double price=NormalizeDouble(tick.ask+trailing_stop,Digits());
            double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0);
            double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0);
            //--- Если рассчитанная цена выше, чем дистанция StopLevel, отложенная от цены установки ордера Ask (соблюдена дистанция по StopLevel)
            if(price>tick.ask+stop_level) 
              {
               //--- Если рассчитанная цена ниже, чем шаг трала, отложенный от цены установки ордера - модифицируем цену установки ордера
               if(price<buy.PriceOpen()-trailing_step)
                 {
                  trade.OrderModify(buy.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),(buy.PriceStopLimit()>0 ? price-distance_stoplimit*Point() : 0));
                 }
              }
           }
        }
     }
//--- Выбираем из списка только ордера в направлении Sell
   CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_SELL,EQUAL);
   //--- Сортируем список по дистанции от цены в пунктах (по прибыли в пунктах)
   list_sell.Sort(SORT_BY_ORDER_PROFIT_PT);
   //--- Получаем индекс ордера в направлении Sell с наибольшей дистанцией
   int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_PT);
   if(index_sell>WRONG_VALUE)
     {
      COrder* sell=list_sell.At(index_sell);
      if(sell!=NULL)
        {
         //--- Если ордер установлен выше цены (SellLimit), и его необходимо "опускать" за ценой
         if(sell.TypeOrder()==ORDER_TYPE_SELL_LIMIT)
           {
            //--- Рассчитываем новую цену установки ордера и стоп-уровни от новой цены
            double price=NormalizeDouble(tick.bid+trailing_stop,Digits());
            double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0);
            double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0);
            //--- Если рассчитанная цена выше, чем дистанция StopLevel, отложенная от цены установки ордера Bid (соблюдена дистанция по StopLevel)
            if(price>tick.bid+stop_level) 
              {
               //--- Если рассчитанная цена ниже, чем шаг трала, отложенный от цены установки ордера - модифицируем цену установки ордера
               if(price<sell.PriceOpen()-trailing_step)
                 {
                  trade.OrderModify(sell.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),sell.PriceStopLimit());
                 }
              }
           }
         //--- Если ордер установлен ниже цены (SellStop и SellStopLimit), и его необходимо "поднимать" за ценой
         else
           {
            //--- Рассчитываем новую цену установки ордера и стоп-уровни от новой цены
            double price=NormalizeDouble(tick.bid-trailing_stop,Digits());
            double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0);
            double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0);
            //--- Если рассчитанная цена ниже, чем дистанция StopLevel, отложенная от цены установки ордера Bid (соблюдена дистанция по StopLevel)
            if(price<tick.bid-stop_level) 
              {
               //--- Если рассчитанная цена выше, чем шаг трала, отложенный от цены установки ордера - модифицируем цену установки ордера
               if(price>sell.PriceOpen()+trailing_step)
                 {
                  trade.OrderModify(sell.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),(sell.PriceStopLimit()>0 ? price+distance_stoplimit*Point() : 0));
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Инициализация последнего торгового события
   static ENUM_TRADE_EVENT last_event=WRONG_VALUE;
//--- Если работа в тестере
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
      PressButtonsControl();
     }
//--- Если последнее торговое событие изменилось
   if(engine.LastTradeEvent()!=last_event)
     {
      last_event=engine.LastTradeEvent();
     }
//--- Если установлен флаг трейлинга
   if(trailing_on)
     {
      TrailingPositions();
      TrailingOrders();   
     }
  }
//+------------------------------------------------------------------+

Полный листинг тестового советника:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart08.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\Engine.mqh>
#include <Trade\Trade.mqh>
//--- enums
enum ENUM_BUTTONS
  {
   BUTT_BUY,
   BUTT_BUY_LIMIT,
   BUTT_BUY_STOP,
   BUTT_BUY_STOP_LIMIT,
   BUTT_CLOSE_BUY,
   BUTT_CLOSE_BUY2,
   BUTT_CLOSE_BUY_BY_SELL,
   BUTT_SELL,
   BUTT_SELL_LIMIT,
   BUTT_SELL_STOP,
   BUTT_SELL_STOP_LIMIT,
   BUTT_CLOSE_SELL,
   BUTT_CLOSE_SELL2,
   BUTT_CLOSE_SELL_BY_BUY,
   BUTT_DELETE_PENDING,
   BUTT_CLOSE_ALL,
   BUTT_PROFIT_WITHDRAWAL,
   BUTT_SET_STOP_LOSS,
   BUTT_SET_TAKE_PROFIT,
   BUTT_TRAILING_ALL
  };
#define TOTAL_BUTT   (20)
//--- structures
struct SDataButt
  {
   string      name;
   string      text;
  };
//--- input variables
input ulong    InpMagic             =  123;  // Magic number
input double   InpLots              =  0.1;  // Lots
input uint     InpStopLoss          =  50;   // StopLoss in points
input uint     InpTakeProfit        =  50;   // TakeProfit in points
input uint     InpDistance          =  50;   // Pending orders distance (points)
input uint     InpDistanceSL        =  50;   // StopLimit orders distance (points)
input uint     InpSlippage          =  0;    // Slippage in points
input double   InpWithdrawal        =  10;   // Withdrawal funds (in tester)
input uint     InpButtShiftX        =  40;   // Buttons X shift 
input uint     InpButtShiftY        =  10;   // Buttons Y shift 
input uint     InpTrailingStop      =  50;   // Trailing Stop (points)
input uint     InpTrailingStep      =  20;   // Trailing Step (points)
input uint     InpTrailingStart     =  0;    // Trailing Start (points)
input uint     InpStopLossModify    =  20;   // StopLoss for modification (points)
input uint     InpTakeProfitModify  =  60;   // TakeProfit for modification (points)
//--- global variables
CEngine        engine;
CTrade         trade;
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ulong          magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           slippage;
bool           trailing_on;
double         trailing_stop;
double         trailing_step;
uint           trailing_start;
uint           stoploss_to_modify;
uint           takeprofit_to_modify;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Вызов данной функции выводит в журнал список констант перечисления, 
//--- заданного в файле DELib.mqh в строках 22 и 25, для проверки корректности констант
   //EnumNumbersTest();
//--- check for undeleted objects
   if(IsPresentObects(prefix))
      ObjectsDeleteAll(0,prefix);
//--- set global variables
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;
   stoploss_to_modify=InpStopLossModify;
   takeprofit_to_modify=InpTakeProfitModify;
//--- create buttons
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_FAILED;
//--- set button trailing
   ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);
//--- setting trade parameters
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- delete objects
   ObjectsDeleteAll(0,prefix);
   Comment("");
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Инициализация последнего торгового события
   static ENUM_TRADE_EVENT last_event=WRONG_VALUE;
//--- Если работа в тестере
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
      PressButtonsControl();
     }
//--- Если последнее торговое событие изменилось
   if(engine.LastTradeEvent()!=last_event)
     {
      last_event=engine.LastTradeEvent();
     }
//--- Если установлен флаг трейлинга
   if(trailing_on)
     {
      TrailingPositions();
      TrailingOrders();
     }
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
   if(!MQLInfoInteger(MQL_TESTER))
      engine.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   if(MQLInfoInteger(MQL_TESTER))
      return;
   if(id==CHARTEVENT_OBJECT_CLICK && StringFind(sparam,"BUTT_")>0)
     {
      PressButtonEvents(sparam);
     }
   if(id>=CHARTEVENT_CUSTOM)
     {
      ushort event=ushort(id-CHARTEVENT_CUSTOM);
      Print(DFUN,"id=",id,", event=",EnumToString((ENUM_TRADE_EVENT)event),", lparam=",lparam,", dparam=",DoubleToString(dparam,Digits()),", sparam=",sparam);
     } 
  }
//+------------------------------------------------------------------+
//| Возвращает флаг наличия объекта с префиксом                      |
//+------------------------------------------------------------------+
bool IsPresentObects(const string object_prefix)
  {
   for(int i=ObjectsTotal(0)-1;i>=0;i--)
      if(StringFind(ObjectName(0,i,0),object_prefix)>WRONG_VALUE)
         return true;
   return false;
  }
//+------------------------------------------------------------------+
//| Контроль состояния кнопок                                        |
//+------------------------------------------------------------------+
void PressButtonsControl(void)
  {
   int total=ObjectsTotal(0);
   for(int i=0;i<total;i++)
     {
      string obj_name=ObjectName(0,i);
      if(StringFind(obj_name,prefix+"BUTT_")<0)
         continue;
      PressButtonEvents(obj_name);
     }
  }
//+------------------------------------------------------------------+
//| Создаёт панель кнопок                                            |
//+------------------------------------------------------------------+
bool CreateButtons(const int shift_x=30,const int shift_y=0)
  {
   int h=18,w=84,offset=2;
   int cx=offset+shift_x,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+3*h+1;
   int x=cx,y=cy;
   int shift=0;
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      x=x+(i==7 ? w+2 : 0);
      if(i==TOTAL_BUTT-6) x=cx;
      y=(cy-(i-(i>6 ? 7 : 0))*(h+1));
      if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-6 ? w : w*2+2),h,butt_data[i].text,(i<4 ? clrGreen : i>6 && i<11 ? clrRed : clrBlue)))
        {
         Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text);
         return false;
        }
     }
   ChartRedraw(0);
   return true;
  }
//+------------------------------------------------------------------+
//| Создаёт кнопку                                                   |
//+------------------------------------------------------------------+
bool ButtonCreate(const string name,const int x,const int y,const int w,const int h,const string text,const color clr,const string font="Calibri",const int font_size=8)
  {
   if(ObjectFind(0,name)<0)
     {
      if(!ObjectCreate(0,name,OBJ_BUTTON,0,0,0)) 
        { 
         Print(DFUN,TextByLanguage("не удалось создать кнопку! Код ошибки=","Could not create button! Error code="),GetLastError()); 
         return false; 
        } 
      ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
      ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
      ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
      ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
      ObjectSetInteger(0,name,OBJPROP_XSIZE,w);
      ObjectSetInteger(0,name,OBJPROP_YSIZE,h);
      ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_LOWER);
      ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,font_size);
      ObjectSetString(0,name,OBJPROP_FONT,font);
      ObjectSetString(0,name,OBJPROP_TEXT,text);
      ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
      ObjectSetString(0,name,OBJPROP_TOOLTIP,"\n");
      ObjectSetInteger(0,name,OBJPROP_BORDER_COLOR,clrGray);
      return true;
     }
   return false;
  }
//+------------------------------------------------------------------+
//| Возвращает состояние кнопки                                      |
//+------------------------------------------------------------------+
bool ButtonState(const string name)
  {
   return (bool)ObjectGetInteger(0,name,OBJPROP_STATE);
  }
//+------------------------------------------------------------------+
//| Устанавливает состояние кнопки                                   |
//+------------------------------------------------------------------+
void ButtonState(const string name,const bool state)
  {
   ObjectSetInteger(0,name,OBJPROP_STATE,state);
   if(name==butt_data[TOTAL_BUTT-1].name)
     {
      if(state)
         ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'220,255,240');
      else
         ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'240,240,240');
     }
  }
//+------------------------------------------------------------------+
//| Преобразует перечисление в текст кнопки                          |
//+------------------------------------------------------------------+
string EnumToButtText(const ENUM_BUTTONS member)
  {
   string txt=StringSubstr(EnumToString(member),5);
   StringToLower(txt);
   StringReplace(txt,"set_take_profit","Set TakeProfit");
   StringReplace(txt,"set_stop_loss","Set StopLoss");
   StringReplace(txt,"trailing_all","Trailing All");
   StringReplace(txt,"buy","Buy");
   StringReplace(txt,"sell","Sell");
   StringReplace(txt,"_limit"," Limit");
   StringReplace(txt,"_stop"," Stop");
   StringReplace(txt,"close_","Close ");
   StringReplace(txt,"2"," 1/2");
   StringReplace(txt,"_by_"," by ");
   StringReplace(txt,"profit_","Profit ");
   StringReplace(txt,"delete_","Delete ");
   return txt;
  }
//+------------------------------------------------------------------+
//| Обработка нажатий кнопок                                         |
//+------------------------------------------------------------------+
void PressButtonEvents(const string button_name)
  {
   //--- Преобразуем имя кнопки в её строковый идентификатор
   string button=StringSubstr(button_name,StringLen(prefix));
   //--- Если кнопка в нажатом состоянии
   if(ButtonState(button_name))
     {
      //--- Если нажата кнопка BUTT_BUY: Открыть позицию Buy
      if(button==EnumToString(BUTT_BUY))
        {
         //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY,0,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY,0,takeprofit);
         //--- Открываем позицию Buy
         trade.Buy(NormalizeLot(Symbol(),lot),Symbol(),0,sl,tp);
        }
      //--- Если нажата кнопка BUTT_BUY_LIMIT: Выставить BuyLimit
      else if(button==EnumToString(BUTT_BUY_LIMIT))
        {
         //--- Получаем корректную цену установки ордера относительно уровня StopLevel
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_pending);
         //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня установки ордера с учётом StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,takeprofit);
         //--- Устанавливаем ордер BuyLimit
         trade.BuyLimit(lot,price_set,Symbol(),sl,tp);
        }
      //--- Если нажата кнопка BUTT_BUY_STOP: Выставить BuyStop
      else if(button==EnumToString(BUTT_BUY_STOP))
        {
         //--- Получаем корректную цену установки ордера относительно уровня StopLevel
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending);
         //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня установки ордера с учётом StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set,takeprofit);
         //--- Устанавливаем ордер BuyStop
         trade.BuyStop(lot,price_set,Symbol(),sl,tp);
        }
      //--- Если нажата кнопка BUTT_BUY_STOP_LIMIT: Выставить BuyStopLimit
      else if(button==EnumToString(BUTT_BUY_STOP_LIMIT))
        {
         //--- Получаем корректную цену установки ордера BuyStop относительно уровня StopLevel
         double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending);
         //--- Рассчитываем цену установки ордера BuyLimit относительно уровня установки BuyStop с учётом уровня StopLevel
         double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_stoplimit,price_set_stop);
         //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня установки ордера с учётом StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,takeprofit);
         //--- Устанавливаем ордер BuyStopLimit
         trade.OrderOpen(Symbol(),ORDER_TYPE_BUY_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp);
        }
      //--- Если нажата кнопка BUTT_SELL: Открыть позицию Sell
      else if(button==EnumToString(BUTT_SELL))
        {
         //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL,0,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL,0,takeprofit);
         //--- Открываем позицию Sell
         trade.Sell(lot,Symbol(),0,sl,tp);
        }
      //--- Если нажата кнопка BUTT_SELL_LIMIT: Выставить SellLimit
      else if(button==EnumToString(BUTT_SELL_LIMIT))
        {
         //--- Получаем корректную цену установки ордера относительно уровня StopLevel
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_pending);
         //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня установки ордера с учётом StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,takeprofit);
         //--- Устанавливаем ордер SellLimit
         trade.SellLimit(lot,price_set,Symbol(),sl,tp);
        }
      //--- Если нажата кнопка BUTT_SELL_STOP: Выставить SellStop
      else if(button==EnumToString(BUTT_SELL_STOP))
        {
         //--- Получаем корректную цену установки ордера относительно уровня StopLevel
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending);
         //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня установки ордера с учётом StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set,takeprofit);
         //--- Устанавливаем ордер SellStop
         trade.SellStop(lot,price_set,Symbol(),sl,tp);
        }
      //--- Если нажата кнопка BUTT_SELL_STOP_LIMIT: Выставить SellStopLimit
      else if(button==EnumToString(BUTT_SELL_STOP_LIMIT))
        {
         //--- Получаем корректную цену установки ордера SellStop относительно уровня StopLevel
         double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending);
         //--- Рассчитываем цену установки ордера SellLimit относительно уровня установки SellStop с учётом уровня StopLevel
         double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_stoplimit,price_set_stop);
         //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня установки ордера с учётом StopLevel
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,takeprofit);
         //--- Устанавливаем ордер SellStopLimit
         trade.OrderOpen(Symbol(),ORDER_TYPE_SELL_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp);
        }
      //--- Если нажата кнопка BUTT_CLOSE_BUY: Закрыть Buy с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_BUY))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Buy
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Buy с наибольшей прибылью
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- Получаем тикет позиции Buy и закрываем позицию по тикету
               trade.PositionClose(position.Ticket());
              }
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_BUY2: Закрыть половину Buy с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_BUY2))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Buy
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Buy с наибольшей прибылью
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- Рассчитываем закрываемый объём и закрываем половину позиции Buy по тикету
               if(engine.IsHedge())
                  trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0));
               else
                  trade.Sell(NormalizeLot(position.Symbol(),position.Volume()/2.0));
              }
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_BUY_BY_SELL: Закрыть Buy с максимальной прибылью встречной Sell с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list_buy=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Buy
         list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Buy с наибольшей прибылью
         int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
         //--- Получаем список всех открытых позиций
         CArrayObj* list_sell=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Sell
         list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Sell с наибольшей прибылью
         int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
         if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE)
           {
            //--- Выбираем позицию Buy с наибольшей прибылью
            COrder* position_buy=list_buy.At(index_buy);
            //--- Выбираем позицию Sell с наибольшей прибылью
            COrder* position_sell=list_sell.At(index_sell);
            if(position_buy!=NULL && position_sell!=NULL)
              {
               //--- Закрываем позицию Buy встречной позицией Sell
               trade.PositionCloseBy(position_buy.Ticket(),position_sell.Ticket());
              }
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_SELL: Закрыть Sell с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_SELL))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Sell
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Sell с наибольшей прибылью
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- Получаем тикет позиции Sell и закрываем позицию по тикету
               trade.PositionClose(position.Ticket());
              }
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_SELL2: Закрыть половину Sell с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_SELL2))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Sell
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Sell с наибольшей прибылью
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- Рассчитываем закрываемый объём и закрываем половину позиции Sell по тикету
               if(engine.IsHedge())
                  trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0));
               else
                  trade.Buy(NormalizeLot(position.Symbol(),position.Volume()/2.0));
              }
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_SELL_BY_BUY: Закрыть Sell с максимальной прибылью встречной Buy с максимальной прибылью
      else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list_sell=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Sell
         list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Sell с наибольшей прибылью
         int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
         //--- Получаем список всех открытых позиций
         CArrayObj* list_buy=engine.GetListMarketPosition();
         //--- Выбираем из списка только позиции Buy
         list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Сортируем список по прибыли с учётом комиссии и свопа
         list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Получаем индекс позиции Buy с наибольшей прибылью
         int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
         if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE)
           {
            //--- Выбираем позицию Sell с наибольшей прибылью
            COrder* position_sell=list_sell.At(index_sell);
            //--- Выбираем позицию Buy с наибольшей прибылью
            COrder* position_buy=list_buy.At(index_buy);
            if(position_sell!=NULL && position_buy!=NULL)
              {
               //--- Закрываем позицию Sell встречной позицией Buy
               trade.PositionCloseBy(position_sell.Ticket(),position_buy.Ticket());
              }
           }
        }
      //--- Если нажата кнопка BUTT_CLOSE_ALL: Закрыть все позиции, начиная от позиции с наименьшим профитом
      else if(button==EnumToString(BUTT_CLOSE_ALL))
        {
         //--- Получаем список всех открытых позиций
         CArrayObj* list=engine.GetListMarketPosition();
         if(list!=NULL)
           {
            //--- Сортируем список по прибыли с учётом комиссии и свопа
            list.Sort(SORT_BY_ORDER_PROFIT_FULL);
            int total=list.Total();
            //--- В цикле от позиции с наименьшей прибылью
            for(int i=0;i<total;i++)
              {
               COrder* position=list.At(i);
               if(position==NULL)
                  continue;
               //--- закрываем каждую позицию по её тикету
               trade.PositionClose(position.Ticket());
              }
           }
        }
      //--- Если нажата кнопка BUTT_DELETE_PENDING: Удалить первый отложенный ордер
      else if(button==EnumToString(BUTT_DELETE_PENDING))
        {
         //--- Получаем список всех ордеров
         CArrayObj* list=engine.GetListMarketPendings();
         if(list!=NULL)
           {
            //--- Сортируем список по времени установки
            list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
            int total=list.Total();
            //--- В цикле от позиции с наибольшим временем
            for(int i=total-1;i>=0;i--)
              {
               COrder* order=list.At(i);
               if(order==NULL)
                  continue;
               //--- удаяем ордер по его тикету
               trade.OrderDelete(order.Ticket());
              }
           }
        }
      //--- Если нажата кнопка BUTT_PROFIT_WITHDRAWAL: Вывести средства со счёта
      if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL))
        {
         //--- Если программа запущена в тестере
         if(MQLInfoInteger(MQL_TESTER))
           {
            //--- Эмулируем вывод средств
            TesterWithdrawal(withdrawal);
           }
        }
      //--- Если нажата кнопка BUTT_SET_STOP_LOSS: Установить StopLoss всем ордерам и позициям, где его нету
      if(button==EnumToString(BUTT_SET_STOP_LOSS))
        {
         SetStopLoss();
        }
      //--- Если нажата кнопка BUTT_SET_TAKE_PROFIT: Установить TakeProfit всем ордерам и позициям, где его нету
      if(button==EnumToString(BUTT_SET_TAKE_PROFIT))
        {
         SetTakeProfit();
        }
      //--- Подождём 1/10 секунды
      Sleep(100);
      //--- "Отожмём" кнопку (если это не кнопка трейлинга)
      if(button!=EnumToString(BUTT_TRAILING_ALL))
         ButtonState(button_name,false);
      //--- Если нажата кнопка BUTT_TRAILING_ALL
      else
        {
         //--- Поставим цвет активной кнопки
         ButtonState(button_name,true);
         trailing_on=true;
        }
      //--- перерисуем чарт
      ChartRedraw();
     }
   //--- Вернём цвет неактивной кнопки (если это кнопка трейлинга)
   else if(button==EnumToString(BUTT_TRAILING_ALL))
     {
      ButtonState(button_name,false);
      trailing_on=false;
      //--- перерисуем чарт
      ChartRedraw();
     }
  }
//+------------------------------------------------------------------+
//| Установка StopLoss всем ордерам и позициям                       |
//+------------------------------------------------------------------+
void SetStopLoss(void)
  {
   if(stoploss_to_modify==0)
      return;
//--- Установка StopLoss всем позициям, где его нету
   CArrayObj* list=engine.GetListMarketPosition();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL);
   if(list==NULL)
      return;
   int total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* position=list.At(i);
      if(position==NULL)
         continue;
      double sl=CorrectStopLoss(position.Symbol(),position.TypeByDirection(),0,stoploss_to_modify);
      trade.PositionModify(position.Ticket(),sl,position.TakeProfit());
     }
//--- Установка StopLoss всем отложенным ордерам, где его нету
   list=engine.GetListMarketPendings();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL);
   if(list==NULL)
      return;
   total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* order=list.At(i);
      if(order==NULL)
         continue;
      double sl=CorrectStopLoss(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),stoploss_to_modify);
      trade.OrderModify(order.Ticket(),order.PriceOpen(),sl,order.TakeProfit(),trade.RequestTypeTime(),trade.RequestExpiration(),order.PriceStopLimit());
     }
  }
//+------------------------------------------------------------------+
//| Установка TakeProfit всем ордерам и позициям                     |
//+------------------------------------------------------------------+
void SetTakeProfit(void)
  {
   if(takeprofit_to_modify==0)
      return;
//--- Установка TakeProfit всем позициям, где его нету
   CArrayObj* list=engine.GetListMarketPosition();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL);
   if(list==NULL)
      return;
   int total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* position=list.At(i);
      if(position==NULL)
         continue;
      double tp=CorrectTakeProfit(position.Symbol(),position.TypeByDirection(),0,takeprofit_to_modify);
      trade.PositionModify(position.Ticket(),position.StopLoss(),tp);
     }
//--- Установка TakeProfit всем отложенным ордерам, где его нету
   list=engine.GetListMarketPendings();
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL);
   if(list==NULL)
      return;
   total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* order=list.At(i);
      if(order==NULL)
         continue;
      double tp=CorrectTakeProfit(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),takeprofit_to_modify);
      trade.OrderModify(order.Ticket(),order.PriceOpen(),order.StopLoss(),tp,trade.RequestTypeTime(),trade.RequestExpiration(),order.PriceStopLimit());
     }
  }
//+------------------------------------------------------------------+
//| Трал стопа позиции с наибольшей прибылью                         |
//+------------------------------------------------------------------+
void TrailingPositions(void)
  {
   MqlTick tick;
   if(!SymbolInfoTick(Symbol(),tick))
      return;
   double stop_level=StopLevel(Symbol(),2)*Point();
   //--- Получаем список всех открытых позиций
   CArrayObj* list=engine.GetListMarketPosition();
   //--- Выбираем из списка только позиции Buy
   CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
   //--- Сортируем список по прибыли с учётом комиссии и свопа
   list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
   //--- Получаем индекс позиции Buy с наибольшей прибылью
   int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
   if(index_buy>WRONG_VALUE)
     {
      COrder* buy=list_buy.At(index_buy);
      if(buy!=NULL)
        {
         //--- Рассчитываем новый StopLoss
         double sl=NormalizeDouble(tick.bid-trailing_stop,Digits());
         //--- Если цена и отложенный от неё уровень StopLevel выше нового StopLoss (соблюдена дистанция по StopLevel)
         if(tick.bid-stop_level>sl) 
           {
            //--- Если новый уровень StopLoss выше, чем шаг трала, отложенный от текущего StopLoss
            if(buy.StopLoss()+trailing_step<sl)
              {
               //--- Если тралим при любой прибыли или прибыль позиции в пунктах больше значения начала трейлинга - модифицируем StopLoss
               if(trailing_start==0 || buy.ProfitInPoints()>(int)trailing_start)
                  trade.PositionModify(buy.Ticket(),sl,buy.TakeProfit());
              }
           }
        }
     }
   //--- Выбираем из списка только позиции Sell
   CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
   //--- Сортируем список по прибыли с учётом комиссии и свопа
   list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
   //--- Получаем индекс позиции Sell с наибольшей прибылью
   int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
   if(index_sell>WRONG_VALUE)
     {
      COrder* sell=list_sell.At(index_sell);
      if(sell!=NULL)
        {
         //--- Рассчитываем новый StopLoss
         double sl=NormalizeDouble(tick.ask+trailing_stop,Digits());
         //--- Если цена и отложенный от неё уровень StopLevel ниже нового StopLoss (соблюдена дистанция по StopLevel)
         if(tick.ask+stop_level<sl) 
           {
            //--- Если новый уровень StopLoss ниже, чем шаг трала, отложенный от текущего StopLoss или у позиции ещё не установлен StopLoss
            if(sell.StopLoss()-trailing_step>sl || sell.StopLoss()==0)
              {
               //--- Если тралим при любой прибыли или прибыль позиции в пунктах больше значения начала трейлинга - модифицируем StopLoss
               if(trailing_start==0 || sell.ProfitInPoints()>(int)trailing_start)
                  trade.PositionModify(sell.Ticket(),sl,sell.TakeProfit());
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Трал самых дальних отложенных ордеров                            |
//+------------------------------------------------------------------+
void TrailingOrders(void)
  {
   MqlTick tick;
   if(!SymbolInfoTick(Symbol(),tick))
      return;
   double stop_level=StopLevel(Symbol(),2)*Point();
//--- Получаем список всех установленных ордеров
   CArrayObj* list=engine.GetListMarketPendings();
//--- Выбираем из списка только ордера в направлении Buy
   CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_BUY,EQUAL);
   //--- Сортируем список по дистанции от цены в пунктах (по прибыли в пунктах)
   list_buy.Sort(SORT_BY_ORDER_PROFIT_PT);
   //--- Получаем индекс ордера в направлении Buy с наибольшей дистанцией
   int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_PT);
   if(index_buy>WRONG_VALUE)
     {
      COrder* buy=list_buy.At(index_buy);
      if(buy!=NULL)
        {
         //--- Если ордер установлен ниже цены (BuyLimit), и его необходимо "поднимать" за ценой
         if(buy.TypeOrder()==ORDER_TYPE_BUY_LIMIT)
           {
            //--- Рассчитываем новую цену установки ордера и стоп-уровни от новой цены
            double price=NormalizeDouble(tick.ask-trailing_stop,Digits());
            double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0);
            double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0);
            //--- Если рассчитанная цена ниже, чем дистанция StopLevel, отложенная от цены установки ордера Ask (соблюдена дистанция по StopLevel)
            if(price<tick.ask-stop_level) 
              {
               //--- Если рассчитанная цена выше, чем шаг трала, отложенный от цены установки ордера - модифицируем цену установки ордера
               if(price>buy.PriceOpen()+trailing_step)
                 {
                  trade.OrderModify(buy.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),buy.PriceStopLimit());
                 }
              }
           }
         //--- Если ордер установлен выше цены (BuyStop и BuyStopLimit), и его необходимо "опускать" за ценой
         else
           {
            //--- Рассчитываем новую цену установки ордера и стоп-уровни от новой цены
            double price=NormalizeDouble(tick.ask+trailing_stop,Digits());
            double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0);
            double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0);
            //--- Если рассчитанная цена выше, чем дистанция StopLevel, отложенная от цены установки ордера Ask (соблюдена дистанция по StopLevel)
            if(price>tick.ask+stop_level) 
              {
               //--- Если рассчитанная цена ниже, чем шаг трала, отложенный от цены установки ордера - модифицируем цену установки ордера
               if(price<buy.PriceOpen()-trailing_step)
                 {
                  trade.OrderModify(buy.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),(buy.PriceStopLimit()>0 ? price-distance_stoplimit*Point() : 0));
                 }
              }
           }
        }
     }
//--- Выбираем из списка только ордера в направлении Sell
   CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_SELL,EQUAL);
   //--- Сортируем список по дистанции от цены в пунктах (по прибыли в пунктах)
   list_sell.Sort(SORT_BY_ORDER_PROFIT_PT);
   //--- Получаем индекс ордера в направлении Sell с наибольшей дистанцией
   int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_PT);
   if(index_sell>WRONG_VALUE)
     {
      COrder* sell=list_sell.At(index_sell);
      if(sell!=NULL)
        {
         //--- Если ордер установлен выше цены (SellLimit), и его необходимо "опускать" за ценой
         if(sell.TypeOrder()==ORDER_TYPE_SELL_LIMIT)
           {
            //--- Рассчитываем новую цену установки ордера и стоп-уровни от новой цены
            double price=NormalizeDouble(tick.bid+trailing_stop,Digits());
            double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0);
            double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0);
            //--- Если рассчитанная цена выше, чем дистанция StopLevel, отложенная от цены установки ордера Bid (соблюдена дистанция по StopLevel)
            if(price>tick.bid+stop_level) 
              {
               //--- Если рассчитанная цена ниже, чем шаг трала, отложенный от цены установки ордера - модифицируем цену установки ордера
               if(price<sell.PriceOpen()-trailing_step)
                 {
                  trade.OrderModify(sell.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),sell.PriceStopLimit());
                 }
              }
           }
         //--- Если ордер установлен ниже цены (SellStop и SellStopLimit), и его необходимо "поднимать" за ценой
         else
           {
            //--- Рассчитываем новую цену установки ордера и стоп-уровни от новой цены
            double price=NormalizeDouble(tick.bid-trailing_stop,Digits());
            double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0);
            double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0);
            //--- Если рассчитанная цена ниже, чем дистанция StopLevel, отложенная от цены установки ордера Bid (соблюдена дистанция по StopLevel)
            if(price<tick.bid-stop_level) 
              {
               //--- Если рассчитанная цена выше, чем шаг трала, отложенный от цены установки ордера - модифицируем цену установки ордера
               if(price>sell.PriceOpen()+trailing_step)
                 {
                  trade.OrderModify(sell.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),(sell.PriceStopLimit()>0 ? price+distance_stoplimit*Point() : 0));
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+

Скомпилируем советник.
Зададим в его параметрах значения StopLoss in points и TaleProfit in points равными нулю — чтобы открывать позиции и устанавливать отложенные ордера изначально без стоп-уровней, зададим в параметрах StopLoss for modification (points) и TakeProfit for modification (points) значения 20 и 60 соответственно (значения по умолчанию) — эти уровни StopLoss и TakeProfit будут устанавливаться по нажатию кнопок.
Запустим советник в тестере и выставим отложенные ордера. Затем нажмём поочерёдно кнопки установки StopLoss и TakeProfit — уровни будут выставлены и в журнал будут выведены об этом записи. Затем включим трейлинг и понаблюдаем за ордерами — они перемещаются за ценой, и в журнал об этих событиях выводятся записи. У позиций, появившихся в результате срабатывания ордеров, будут тралиться уровни StopLoss, и об этих событиях в журнале будут появляться записи.

Неттинг:


Хедж:


Что дальше

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

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

К содержанию

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

Часть 1. Концепция, организация данных.
Часть 2. Коллекция исторических ордеров и сделок.
Часть 3. Коллекция рыночных ордеров и позиций, организация поиска.
Часть 4. Торговые события. Концепция.
Часть 5. Классы и коллекция торговых событий. Отправка событий в программу.
Часть 6. События на счёте с типом неттинг.
Часть 7. События срабатывания StopLimit-ордеров, подготовка функционала для регистрации событий модификации ордеров и позиций.



Прикрепленные файлы |
MQL5.zip (99.05 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (10)
Sergey Voytsekhovsky
Sergey Voytsekhovsky | 19 май 2019 в 16:54
Artyom Trishkin:

Что в журнале "Эксперты" пишется? Создаются ли хендлы индикаторов?

Добрый день. Да, хендл создается, равен 10. 

Добился небольшого прогресса (очень небольшого!). Появились записи Comment

Есть записи Print()


2019.05.19 17:31:13.127 2018.01.02 00:00:00 Хендл iCrossAD = 10

2019.05.19 17:31:13.580 2018.01.02 09:00:00 Скопированны данные из 2-го буфера индикатора iCrossAD

2019.05.19 17:31:13.580 2018.01.02 09:00:00 CrossAD = 10, period_find = 400, Last_Arrow_Buy = 1.797693134862316e+308

2019.05.19 17:31:13.580 2018.01.02 09:00:00 Скопированны данные из 3-го буфера индикатора iCrossAD

2019.05.19 17:31:13.580 2018.01.02 09:00:00 CrossAD = 10, period_find = 400, Last_Arrow_Sell = 1.797693134862316e+308

2019.05.19 17:31:13.580 2018.01.02 09:00:20 Скопированны данные из 2-го буфера индикатора iCrossAD

2019.05.19 17:31:13.581 2018.01.02 09:00:20 CrossAD = 10, period_find = 400, Last_Arrow_Buy = 1.797693134862316e+308

2019.05.19 17:31:13.581 2018.01.02 09:00:20 Скопированны данные из 3-го буфера индикатора iCrossAD

2019.05.19 17:31:13.581 2018.01.02 09:00:20 CrossAD = 10, period_find = 400, Last_Arrow_Sell = 1.797693134862316e+308


Два буфера в индикаторе, из которых копируются данные, содержат значения на которые установлены стрелки индикатора. Записываются они туда в момент установки стрелки. Соответственно делаю вывод что они должны быть заполнены double ценами, на момент формирования стрелки. А у меня почему-то выводиться космическая цифра, если я не ошибаюсь, это максимальное из возможных значений double в МТ5.

Правда пробовал и добивался я этого в тестере, котировок вчера и сегодня не было, сегодня воскресенье.

Если есть желание, помогите разобраться плиз. Советник и индикатор приложу к посту. Советник изменен, я писал об этом выше, индикатор тоже Ваш, в нем нет изменений.

Artyom Trishkin
Artyom Trishkin | 19 май 2019 в 16:58
Sergey Voytsekhovsky:

Добрый день. Да, хендл создается, равен 10. 

Добился небольшого прогресса (очень небольшого!). Появились записи Comment

Есть записи Print()


2019.05.19 17:31:13.127 2018.01.02 00:00:00 Хендл iCrossAD = 10

2019.05.19 17:31:13.580 2018.01.02 09:00:00 Скопированны данные из 2-го буфера индикатора iCrossAD

2019.05.19 17:31:13.580 2018.01.02 09:00:00 CrossAD = 10, period_find = 400, Last_Arrow_Buy = 1.797693134862316e+308

2019.05.19 17:31:13.580 2018.01.02 09:00:00 Скопированны данные из 3-го буфера индикатора iCrossAD

2019.05.19 17:31:13.580 2018.01.02 09:00:00 CrossAD = 10, period_find = 400, Last_Arrow_Sell = 1.797693134862316e+308

2019.05.19 17:31:13.580 2018.01.02 09:00:20 Скопированны данные из 2-го буфера индикатора iCrossAD

2019.05.19 17:31:13.581 2018.01.02 09:00:20 CrossAD = 10, period_find = 400, Last_Arrow_Buy = 1.797693134862316e+308

2019.05.19 17:31:13.581 2018.01.02 09:00:20 Скопированны данные из 3-го буфера индикатора iCrossAD

2019.05.19 17:31:13.581 2018.01.02 09:00:20 CrossAD = 10, period_find = 400, Last_Arrow_Sell = 1.797693134862316e+308


Два буфера в индикаторе, из которых копируются данные, содержат значения на которые установлены стрелки индикатора. Записываются они туда в момент установки стрелки. Соответственно делаю вывод что они не должны быть заполнены double ценами. А у меня почему-то выводиться космическая цифра, если я не ошибаюсь, это максимальное из возможных значений double в МТ5.

Правда пробовал и добивался я этого в тестере, котировок вчера и сегодня небыло, сегодня воскресенье.

Если есть желание, помогите разобраться плиз. Советник и индикатор приложк к посту. Советник изменен, я писал об этом выше, индикатор тоже Ваш, в нем нет изменений.

Ну здесь-то обсуждаются вопросы по библиотеке, а не банальное незнание о пустых значениях индикаторных буферов. EMPTY_VALUE == DBL_MAX    
Sergey Voytsekhovsky
Sergey Voytsekhovsky | 19 май 2019 в 17:07
Artyom Trishkin:
Ну здесь-то обсуждаются вопросы по библиотеке, а не банальное незнание о пустых значениях индикаторных буферов. EMPTY_VALUE == DBL_MAX    

Принял, услышал, спасибо. Про пустые значения кажется что-то знал, щас изучу вопрос.

Если здесь это не уместно, то можете ли вы порекомендовать какую-либо ветку, где я смогу получать ответы рт Вас или Ваших коллег на подобные ламерские вопросы?

Если такого места нет, то его наверное стоило бы создать.

Извините за беспокойство, но вроде оживленных бесед тут нету, мешать некому, а кто как не автор подскажет лучше всего. 

К тому-же я и пытаюсь использовать Вашу библиотеку. Примитивно конечно, но уж как могу, извините.

Artyom Trishkin
Artyom Trishkin | 19 май 2019 в 18:19
Sergey Voytsekhovsky:

Принял, услышал, спасибо. Про пустые значения кажется что-то знал, щас изучу вопрос.

Если здесь это не уместно, то можете ли вы порекомендовать какую-либо ветку, где я смогу получать ответы рт Вас или Ваших коллег на подобные ламерские вопросы?

Если такого места нет, то его наверное стоило бы создать.

Извините за беспокойство, но вроде оживленных бесед тут нету, мешать некому, а кто как не автор подскажет лучше всего. 

К тому-же я и пытаюсь использовать Вашу библиотеку. Примитивно конечно, но уж как могу, извините.

По вопросам, не связанным с библиотекой, пишите пожалуйста сюда:

Sergey Voytsekhovsky
Sergey Voytsekhovsky | 19 май 2019 в 19:39
Artyom Trishkin:

По вопросам, не связанным с библиотекой, пишите пожалуйста сюда:

Спасибо большое, ходил, посмотрел, почитал. То что нужно, буду использовать.

Исследование методов свечного анализа (Часть IV): Обновление и дополнение приложения Исследование методов свечного анализа (Часть IV): Обновление и дополнение приложения

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

Применение OLAP в трейдинге (Часть 1): Основы оперативного анализа многомерных данных Применение OLAP в трейдинге (Часть 1): Основы оперативного анализа многомерных данных

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

Применение OLAP в трейдинге (Часть 2): Визуализация результатов интерактивного анализа многомерных данных Применение OLAP в трейдинге (Часть 2): Визуализация результатов интерактивного анализа многомерных данных

В статье рассматриваются различные аспекты создания интерактивного графического интерфейса MQL-программы, предназначенной для OLAP-обработки истории счета и торговых отчетов. Для получения наглядного результата используются максимизируемые и масштабируемые окна, адаптивная раскладка "резиновых" элементов управления, новый "контрол" для вывода диаграмм. На основе этого реализован GUI с выбором показателей по координатным осям, агрегатных функций, типов графиков и сортировок.

Библиотека для простого и быстрого создания программ для MetaTrader (Часть IX): Совместимость с MQL4 - Подготовка данных Библиотека для простого и быстрого создания программ для MetaTrader (Часть IX): Совместимость с MQL4 - Подготовка данных

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