Библиотека для простого и быстрого создания программ для MetaTrader (Часть VI): События на счёте с типом неттинг
Содержание
- Сходства и различия типов счетов
- Реализация обработки событий на неттинговом счёте
- Тест работы на хеджевом и неттинговом счёте
- Что дальше
Сходства и различия типов счетов
Для реализации отслеживания событий, происходящих на счёте с типом неттинг, нужно понимать различия между счетами "хедж" и "неттинг".
Различия касаются представления позиций. Если на счёте с типом хедж мы можем открыть любое количество позиций по одному символу, то на счёте с
типом неттинг — только одну. Хеджевый счёт позволяет закрывать одну позицию объёмом другой позиции, имеющей противоположное
направление — закрытие встречной позицией.
При этом:
- Если объём встречной позиции меньше объёма закрываемой, то встречная будет закрыта полностью, а закрываемая — частично,
- если объём встречной позиции больше объёма закрываемой, то встречная будет закрыта частично, а закрываемая — полностью,
- если объёмы встречной и закрываемой позиции равны, то будут закрыты обе позиции;
- У каждой позиции имеется идентификатор позиции, равный тикету открывающего ордера, и этот идентификатор не меняется за всё время существования позиции;
- У каждой позиции имеется свой собственный тикет, равный тикету ордера, срабатывание которого привело к открытию позиции;
- Если отправить запрос на открытие новой позиции в сторону направления текущей позиции, то будет открыта новая позиция с новым идентификатором и тикетом.
На неттинговом типе счёта работа с одной позицией на одном символе не позволяет закрывать позицию встречной, но в отдалённо похожей ситуации — при срабатывании ордера, имеющего противоположное направление к направлению текущей позиции, эта позиция может быть либо частично закрыта, либо закрыта полностью, либо изменить своё направление:
- Если объём сработавшего встречного ордера меньше объёма текущей позиции, то позиция будет частично закрыта,
- если объём сработавшего встречного ордера равен объёму текущей позиции, то позиция будет полностью закрыта,
- если объём сработавшего встречного ордера больше объёма текущей позиции, то позиция изменит своё направление — разворот позиции;
- У каждой позиции имеется идентификатор позиции, равный тикету открывающего ордера, и этот идентификатор не меняется за всё время существования позиции;
- У каждой позиции имеется тикет, равный тикету ордера, срабатывание которого привело к развороту позиции. Тикет может отличаться от идентификатора, и в какой-то мере повторяет тикеты множества позиций на хеджевом счёте;
- Если отправить запрос на открытие новой позиции в сторону направления текущей позиции, то к объёму текущей позиции будет добавлен
объём сработавшего ордера, тикет позиции не изменится.
Реализация обработки событий на неттинговом счёте
Для реализации отслеживания событий на счёте с типом "неттинг", мы пойдём простым путём: разделим обработку событий, происходящих с позициями по типам счетов. Кода в итоге получится больше, но за счёт разделения функционала, логика будет понятнее. В последующем — после отладки и подтверждения стабильности работы, мы обязательно оптимизируем код и избавимся от всех излишеств.
При добавлении новых констант в перечисления типов событий я заметил, что в некоторых случаях неправильно работает сортировка. Проверка причин такого поведения выявила, что имеет значение не только соответствие свойств событий или ордеров сортировке по данному типу, но и их расположение в перечислении, независимо от того, что каждая константа пронумерована. Так, если для поиска не используется какое-либо свойство, то мало его просто пропустить и дать верные номера константам перечисления методов поиска, соответствующие константам перечисления типов свойств, но важно ещё и разместить неиспользуемые в сортировке свойства события или ордера в конце списка данного типа свойств. А для вычисления начального номера следующих типов свойств нужно от начального индекса типов свойств отнимать количество неиспользуемых свойств из количества свойств предыдущего типа.
Для проверки корректности создания перечислений методов сортировки была добавлена небольшая функция в файл сервисных функций DELib.mqh:
//+------------------------------------------------------------------+ //| Распечатывает в журнал все константы перечисления сортировок | //+------------------------------------------------------------------+ void EnumNumbersTest() { string enm="ENUM_SORT_ORDERS_MODE"; string t=StringSubstr(enm,5,5)+"BY"; Print("Search of the values of the enumaration ",enm,":"); ENUM_SORT_ORDERS_MODE type=0; while(StringFind(EnumToString(type),t)==0) { Print(enm,"[",type,"]=",EnumToString(type)); if(type>500) break; type++; } Print("\nNumber of members of the ",enm,"=",type); } //+------------------------------------------------------------------+
Для проверки состава конкретного перечисления типов сортировки, нужно его вручную ввести в две строки функции (способа сделать автоматическое задание конкретного перечисления в строке ENUM_SORT_ORDERS_MODE type=0; я-таки не нашёл).
Теперь, если в тестовом советнике в его обработчике OnInit() вызвать эту функцию, то в журнал будут выведены все наименования констант
заданного перечисления и соответствующие им индексы.
И вот как раз при проверке состава перечислений и обнаружилось неправильное их создание. Для исправления этой ошибки пришлось немного
переработать перечисления в файле
Defines.mqh.
Просто нужно задать иной порядок следования констант в перечислениях — неиспользуемые
в сортировке свойства разместим в конце списка констант перечисления свойств объекта, добавим макроподстановки
для указания на количество неиспользуемых свойств для поиска и сортировки. Эти макроподстановки будем использовать
при вычислении начальных индексов свойств в перечислениях сортировки, что приведёт к вычислению верных индексов начальных
констант в перечислениях.
Так же добавим новые типы констант для событий на неттинговых счетах
и
константы для хранения магика и символа встречной позиции для хеджевых счетов.
И как дополнение к текущей теме: часто в своих программах требуется группировать ордера и позиции, и обрабатывать одновременно группу
выбранных ордеров и позиций. Библиотека позволяет выполнять это очень просто — стоит лишь добавить к свойству абстрактного ордера
идентификатор группы. И после этого можно будет выбирать в один список любые ордера и позиции, имеющие одинаковый идентификатор, и
работать с выбранной группой.
Такой идентификатор был добавлен в список свойств ордера и в список сортировки ордеров.
Приведу полный листинг изменённого Defines.mqh:
//+------------------------------------------------------------------+ //| Defines.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" //+------------------------------------------------------------------+ //| Макроподстановки | //+------------------------------------------------------------------+ //--- "Описание функции с номером строки ошибки" #define DFUN_ERR_LINE (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Стр. " : ", Line ")+(string)__LINE__+": ") #define DFUN (__FUNCTION__+": ") // "Описание функции" #define COUNTRY_LANG ("Russian") // Язык страны #define END_TIME (D'31.12.3000 23:59:59') // Конечная дата для запросов данных истории счёта #define TIMER_FREQUENCY (16) // Минимальная частота таймера библиотеки в милисекундах #define COLLECTION_PAUSE (250) // Пауза таймера коллекции ордеров и сделок в милисекундах #define COLLECTION_COUNTER_STEP (16) // Шаг приращения счётчика таймера коллекции ордеров и сделок #define COLLECTION_COUNTER_ID (1) // Идентификатор счётчика таймера коллекции ордеров и сделок #define COLLECTION_HISTORY_ID (0x7778+1) // Идентификатор списка исторической коллекции #define COLLECTION_MARKET_ID (0x7778+2) // Идентификатор списка рыночной коллекции #define COLLECTION_EVENTS_ID (0x7778+3) // Идентификатор списка коллекции событий //+------------------------------------------------------------------+ //| Структуры | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Перечисления | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Данные для поиска и сортировки | //+------------------------------------------------------------------+ enum ENUM_COMPARER_TYPE { EQUAL, // Равно MORE, // Больше LESS, // Меньше NO_EQUAL, // Не равно EQUAL_OR_MORE, // Больше или равно EQUAL_OR_LESS // Меньше или равно }; //+------------------------------------------------------------------+ //| Возможные варианты выбора по времени | //+------------------------------------------------------------------+ enum ENUM_SELECT_BY_TIME { SELECT_BY_TIME_OPEN, // По времени открытия SELECT_BY_TIME_CLOSE, // По времени закрытия SELECT_BY_TIME_OPEN_MSC, // По времени открытия в милисекундах SELECT_BY_TIME_CLOSE_MSC, // По времени закрытия в милисекундах }; //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Данные для работы с ордерами | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Тип (статус) абстрактного ордера | //+------------------------------------------------------------------+ enum ENUM_ORDER_STATUS { ORDER_STATUS_MARKET_PENDING, // Рыночный отложенный ордер ORDER_STATUS_MARKET_ORDER, // Рыночный маркет-ордер ORDER_STATUS_MARKET_POSITION, // Рыночная позиция ORDER_STATUS_HISTORY_ORDER, // Исторический маркет-ордер ORDER_STATUS_HISTORY_PENDING, // Удаленный отложенный ордер ORDER_STATUS_BALANCE, // Балансная операция ORDER_STATUS_CREDIT, // Кредитная операция ORDER_STATUS_DEAL, // Сделка ORDER_STATUS_UNKNOWN // Неизвестный статус }; //+------------------------------------------------------------------+ //| Целочисленные свойства ордера, сделки, позиции | //+------------------------------------------------------------------+ enum ENUM_ORDER_PROP_INTEGER { ORDER_PROP_TICKET = 0, // Тикет ордера ORDER_PROP_MAGIC, // Мэджик ордера ORDER_PROP_TIME_OPEN, // Время открытия (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 (1) // Количество неиспользуемых в сортировке свойств ордера //+------------------------------------------------------------------+ //| Вещественные свойства ордера, сделки, позиции | //+------------------------------------------------------------------+ enum ENUM_ORDER_PROP_DOUBLE { ORDER_PROP_PRICE_OPEN = ORDER_PROP_INTEGER_TOTAL, // Цена открытия (MQL5 цена сделки) ORDER_PROP_PRICE_CLOSE, // Цена закрытия ORDER_PROP_SL, // Цена StopLoss ORDER_PROP_TP, // Цена TaleProfit ORDER_PROP_PROFIT, // Профит ORDER_PROP_COMMISSION, // Комиссия ORDER_PROP_SWAP, // Своп ORDER_PROP_VOLUME, // Объём ORDER_PROP_VOLUME_CURRENT, // Невыполненный объем ORDER_PROP_PROFIT_FULL, // Профит+комиссия+своп ORDER_PROP_PRICE_STOP_LIMIT, // Цена постановки Limit ордера при срабатывании StopLimit ордера }; #define ORDER_PROP_DOUBLE_TOTAL (11) // Общее количество вещественных свойств //+------------------------------------------------------------------+ //| Строковые свойства ордера, сделки, позиции | //+------------------------------------------------------------------+ enum ENUM_ORDER_PROP_STRING { ORDER_PROP_SYMBOL = (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL), // Символ ордера ORDER_PROP_COMMENT, // Комментарий ордера ORDER_PROP_EXT_ID // Идентификатор ордера во внешней торговой системе }; #define ORDER_PROP_STRING_TOTAL (3) // Общее количество строковых свойств //+------------------------------------------------------------------+ //| Возможные критерии сортировки ордеров и сделок | //+------------------------------------------------------------------+ #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_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_SL = 1024, // Исполнение по StopLoss TRADE_EVENT_FLAG_TP = 2048 // Исполнение по TakeProfit }; //+------------------------------------------------------------------+ //| Список возможных торговых событий на счёте | //+------------------------------------------------------------------+ 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 }; //+------------------------------------------------------------------+ //| Статус события | //+------------------------------------------------------------------+ 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) }; //+------------------------------------------------------------------+ //| Причина события | //+------------------------------------------------------------------+ 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_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) //+------------------------------------------------------------------+ //| Целочисленные свойства события | //+------------------------------------------------------------------+ enum ENUM_EVENT_PROP_INTEGER { EVENT_PROP_TYPE_EVENT = 0, // Тип торгового события на счёте (из перечисления ENUM_TRADE_EVENT) EVENT_PROP_TIME_EVENT, // Время события в милисекундах EVENT_PROP_STATUS_EVENT, // Статус события (из перечисления ENUM_EVENT_STATUS) EVENT_PROP_REASON_EVENT, // Причина события (из перечисления ENUM_EVENT_REASON) //--- EVENT_PROP_TYPE_DEAL_EVENT, // Тип сделки события EVENT_PROP_TICKET_DEAL_EVENT, // Тикет сделки события EVENT_PROP_TYPE_ORDER_EVENT, // Тип ордера, на основании которого открыта сделка события (последний ордер позиции) EVENT_PROP_TICKET_ORDER_EVENT, // Тикет ордера, на основании которого открыта сделка события (последний ордер позиции) //--- EVENT_PROP_TIME_ORDER_POSITION, // Время ордера, на основании которого открыта первая сделка позиции (первый ордер позиции на хедж-счёте) EVENT_PROP_TYPE_ORDER_POSITION, // Тип ордера, на основании которого открыта первая сделка позиции (первый ордер позиции на хедж-счёте) EVENT_PROP_TICKET_ORDER_POSITION, // Тикет ордера, на основании которого открыта первая сделка позиции (первый ордер позиции на хедж-счёте) EVENT_PROP_POSITION_ID, // Идентификатор позиции //--- EVENT_PROP_POSITION_BY_ID, // Идентификатор встречной позиции EVENT_PROP_MAGIC_ORDER, // Магический номер ордера/сделки/позиции EVENT_PROP_MAGIC_BY_ID, // Магический номер встречной позиции //--- EVENT_PROP_TYPE_ORD_POS_BEFORE, // Тип позиции до смены направления EVENT_PROP_TICKET_ORD_POS_BEFORE, // Тикет ордера позиции до смены направления EVENT_PROP_TYPE_ORD_POS_CURRENT, // Тип текущей позиции EVENT_PROP_TICKET_ORD_POS_CURRENT // Тикет ордера текущей позиции }; #define EVENT_PROP_INTEGER_TOTAL (19) // Общее количество целочисленных свойств события #define EVENT_PROP_INTEGER_SKIP (4) // Количество неиспользуемых в сортировке свойств события //+------------------------------------------------------------------+ //| Вещественные свойства события | //+------------------------------------------------------------------+ 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 // Профит }; #define EVENT_PROP_DOUBLE_TOTAL (10) // Общее количество вещественных свойств события //+------------------------------------------------------------------+ //| Строковые свойства события | //+------------------------------------------------------------------+ enum ENUM_EVENT_PROP_STRING { EVENT_PROP_SYMBOL = (EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_DOUBLE_TOTAL), // Символ ордера EVENT_PROP_SYMBOL_BY_ID // Символ встречной позиции }; #define EVENT_PROP_STRING_TOTAL (2) // Общее количество строковых свойств события //+------------------------------------------------------------------+ //| Возможные критерии сортировки событий | //+------------------------------------------------------------------+ #define FIRST_EVN_DBL_PROP (EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_INTEGER_SKIP) #define FIRST_EVN_STR_PROP (EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_DOUBLE_TOTAL-EVENT_PROP_INTEGER_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 // Сортировать по символу встречной позици }; //+------------------------------------------------------------------+
И раз уж в дополнение к теме статьи мы добавили идентификатор групп ордеров, то нужно и в объект абстрактного ордера внести некоторые изменения. Добавим метод, возвращающий идентификатор группы, присвоенный ордеру, и метод, устанавливающий значение идентификатора группы:
//+------------------------------------------------------------------+ //| Методы упрощённого доступа к свойствам объекта-ордера | //+------------------------------------------------------------------+ //--- Возвращает (1) тикет, (2) тикет родительского ордера, (3) тикет дочернего ордера, (4) магик, (5) причину выставления ордера, //--- (6) идентификатор позиции, (7) идентификатор встречной позиции, (8) идентификатор группы, (9) тип, (10) флаг закрытия по StopLoss, //--- (11) флаг закрытия по TakeProfit (12) время открытия, (13) время закрытия, (14) время открытия в милисекундах, //--- (15) время закрытия в милисекундах, (16) дату экспирации, (17) состояние, (18) статус (19) тип ордера по направлению long Ticket(void) const { return this.GetProperty(ORDER_PROP_TICKET); } long TicketFrom(void) const { return this.GetProperty(ORDER_PROP_TICKET_FROM); } long TicketTo(void) const { return this.GetProperty(ORDER_PROP_TICKET_TO); } long Magic(void) const { return this.GetProperty(ORDER_PROP_MAGIC); } long Reason(void) const { return this.GetProperty(ORDER_PROP_REASON); } long PositionID(void) const { return this.GetProperty(ORDER_PROP_POSITION_ID); } long PositionByID(void) const { return this.GetProperty(ORDER_PROP_POSITION_BY_ID); } long GroupID(void) const { return this.GetProperty(ORDER_PROP_GROUP_ID); } long TypeOrder(void) const { return this.GetProperty(ORDER_PROP_TYPE); } bool IsCloseByStopLoss(void) const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_SL); } bool IsCloseByTakeProfit(void) const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_TP); } datetime TimeOpen(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN); } datetime TimeClose(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE); } datetime TimeOpenMSC(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN_MSC); } datetime TimeCloseMSC(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE_MSC); } datetime TimeExpiration(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_EXP); } ENUM_ORDER_STATE State(void) const { return (ENUM_ORDER_STATE)this.GetProperty(ORDER_PROP_STATE); } ENUM_ORDER_STATUS Status(void) const { return (ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS); } ENUM_ORDER_TYPE TypeByDirection(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_DIRECTION); }
//--- Возвращает (1) цену открытия, (2) цену закрытия, (3) профит, (4) комиссию, (5) своп, (6) объём, //--- (7) невыполненный объём (8) StopLoss и (9) TakeProfit (10) цену установки StopLimit-ордера double PriceOpen(void) const { return this.GetProperty(ORDER_PROP_PRICE_OPEN); } double PriceClose(void) const { return this.GetProperty(ORDER_PROP_PRICE_CLOSE); } double Profit(void) const { return this.GetProperty(ORDER_PROP_PROFIT); } double Comission(void) const { return this.GetProperty(ORDER_PROP_COMMISSION); } double Swap(void) const { return this.GetProperty(ORDER_PROP_SWAP); } double Volume(void) const { return this.GetProperty(ORDER_PROP_VOLUME); } double VolumeCurrent(void) const { return this.GetProperty(ORDER_PROP_VOLUME_CURRENT); } double StopLoss(void) const { return this.GetProperty(ORDER_PROP_SL); } double TakeProfit(void) const { return this.GetProperty(ORDER_PROP_TP); } double PriceStopLimit(void) const { return this.GetProperty(ORDER_PROP_PRICE_STOP_LIMIT); } //--- Возвращает (1) символ, (2) комментарий, (3) идентификатор на бирже string Symbol(void) const { return this.GetProperty(ORDER_PROP_SYMBOL); } string Comment(void) const { return this.GetProperty(ORDER_PROP_COMMENT); } string ExternalID(void) const { return this.GetProperty(ORDER_PROP_EXT_ID); } //--- Возвращает полный профит ордера double ProfitFull(void) const { return this.Profit()+this.Comission()+this.Swap(); } //--- Возвращает профит ордера в пунктах int ProfitInPoints(void) const; //--- Устанавливает идентификатор группы void SetGroupID(long group_id) { this.SetProperty(ORDER_PROP_GROUP_ID,group_id); }
По умолчанию идентификатору группы будет присвоен ноль. Для этого в закрытом конструкторе класса COrder установим равным нулю значение данного свойства ордера:
//+------------------------------------------------------------------+ //| Закрытый параметрический конструктор | //+------------------------------------------------------------------+ COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket) { //--- Сохранение целочисленных свойств this.m_ticket=ticket; this.m_long_prop[ORDER_PROP_STATUS] = order_status; this.m_long_prop[ORDER_PROP_MAGIC] = this.OrderMagicNumber(); this.m_long_prop[ORDER_PROP_TICKET] = this.OrderTicket(); this.m_long_prop[ORDER_PROP_TIME_OPEN] = (long)(ulong)this.OrderOpenTime(); this.m_long_prop[ORDER_PROP_TIME_CLOSE] = (long)(ulong)this.OrderCloseTime(); this.m_long_prop[ORDER_PROP_TIME_EXP] = (long)(ulong)this.OrderExpiration(); this.m_long_prop[ORDER_PROP_TYPE] = this.OrderType(); this.m_long_prop[ORDER_PROP_STATE] = this.OrderState(); this.m_long_prop[ORDER_PROP_DIRECTION] = this.OrderTypeByDirection(); this.m_long_prop[ORDER_PROP_POSITION_ID] = this.OrderPositionID(); this.m_long_prop[ORDER_PROP_REASON] = this.OrderReason(); this.m_long_prop[ORDER_PROP_DEAL_ORDER_TICKET] = this.DealOrderTicket(); this.m_long_prop[ORDER_PROP_DEAL_ENTRY] = this.DealEntry(); this.m_long_prop[ORDER_PROP_POSITION_BY_ID] = this.OrderPositionByID(); this.m_long_prop[ORDER_PROP_TIME_OPEN_MSC] = this.OrderOpenTimeMSC(); this.m_long_prop[ORDER_PROP_TIME_CLOSE_MSC] = this.OrderCloseTimeMSC(); this.m_long_prop[ORDER_PROP_TIME_UPDATE] = (long)(ulong)this.PositionTimeUpdate(); this.m_long_prop[ORDER_PROP_TIME_UPDATE_MSC] = (long)(ulong)this.PositionTimeUpdateMSC(); //--- Сохранение вещественных свойств this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)] = this.OrderOpenPrice(); this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)] = this.OrderClosePrice(); this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)] = this.OrderProfit(); this.m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)] = this.OrderCommission(); this.m_double_prop[this.IndexProp(ORDER_PROP_SWAP)] = this.OrderSwap(); this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)] = this.OrderVolume(); this.m_double_prop[this.IndexProp(ORDER_PROP_SL)] = this.OrderStopLoss(); this.m_double_prop[this.IndexProp(ORDER_PROP_TP)] = this.OrderTakeProfit(); this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)] = this.OrderVolumeCurrent(); this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)] = this.OrderPriceStopLimit(); //--- Сохранение строковых свойств this.m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)] = this.OrderSymbol(); this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)] = this.OrderComment(); this.m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)] = this.OrderExternalID(); //--- Сохранение дополнительных целочисленных свойств this.m_long_prop[ORDER_PROP_PROFIT_PT] = this.ProfitInPoints(); this.m_long_prop[ORDER_PROP_TICKET_FROM] = this.OrderTicketFrom(); this.m_long_prop[ORDER_PROP_TICKET_TO] = this.OrderTicketTo(); this.m_long_prop[ORDER_PROP_CLOSE_BY_SL] = this.OrderCloseByStopLoss(); this.m_long_prop[ORDER_PROP_CLOSE_BY_TP] = this.OrderCloseByTakeProfit(); this.m_long_prop[ORDER_PROP_GROUP_ID] = 0; //--- Сохранение дополнительных вещественных свойств this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)] = this.ProfitFull(); } //+------------------------------------------------------------------+
И не забудем вписать в метод, возвращающий описание свойства, возврат описания идентификатора групп:
//+------------------------------------------------------------------+ //| Возвращает описание целочисленного свойства ордера | //+------------------------------------------------------------------+ 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 ? 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) ) : "" ); } //+------------------------------------------------------------------+
После этих изменений любому ордеру или позиции можно будет задавать идентификатор группы, тем самым группируя различные ордера и позиции в определённые группы для работы только с конкретной группой. По умолчанию всем новым открываемым позициям и устанавливаемым ордерам присваивается группа 0. Но мы всегда сможем назначить любому ордеру или позиции иную группу при помощи метода SetGroupID(номер_группы), а посмотреть группу у любого ордера — при помощи метода GroupID().
Вернёмся к реализации отслеживания событий на счёте с типом "неттинг".
Для разделения функционала по типам
счетов добавим в приватную секцию класса абстрактного события CEvent в файле
Event.mqh переменную-член класса:
protected: ENUM_TRADE_EVENT m_trade_event; // Торговое событие bool m_is_hedge; // Флаг хедж-счёта long m_chart_id; // Идентификатор графика управляющей программы 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; }
В публичную секцию класса в список методов упрощённого доступа добавим объявление методов, возвращающих
(для
хеджевого счёта)
магик встречной
позиции и символ встречной позиции,
(для учёта разворота позиции на неттинговом счёте) тип
и тикет ордера предыдущей позиции, тип и тикет ордера текущей позиции,
тип и тикет позиции до смены её направления, тип
и тикет позиции после смены её направления:
//+------------------------------------------------------------------+ //| Методы упрощённого доступа к свойствам объекта-события | //+------------------------------------------------------------------+ //--- Возвращает (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) символ встречной позиции string Symbol(void) const { return this.GetProperty(EVENT_PROP_SYMBOL); } string SymbolCloseBy(void) const { return this.GetProperty(EVENT_PROP_SYMBOL_BY_ID); } //+------------------------------------------------------------------+
Методы просты: для ордеров возвращается непосредственно соответствующее свойство события, а для позиций — для тикетов возвращается тикет
ордера, который открыл или изменил позицию, для наименования типов — возвращается при помощи ранее рассмотренной функции
PositionTypeByOrderType() из файла сервисных функций DELib.mqh тип позиции по типу ордера, который её открыл.
В конструкторе класса впишем сохранение данных о типе счёта:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code) { 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(); } //+------------------------------------------------------------------+
В методы описания свойств события добавим определение методов, возвращающих наименование
ордера, по которому произошла сделка события, самого первого
(открывающего) ордера позиции, ордера, срабатывание которого привело к
открытию (неттинг, хедж) или изменению (неттинг) текущей позиции, наименование
типа текущей позиции, типа ордера, срабатывание которого привело к
открытию предыдущей позиции, и наименование типа предыдущей позиции:
//+------------------------------------------------------------------+ //| Описания свойств объекта-ордера | //+------------------------------------------------------------------+ //--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства ордера string GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property); string GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property); string GetPropertyDescription(ENUM_EVENT_PROP_STRING property); //--- Возвращает наименование (1) статуса, (2) типа события string StatusDescription(void) const; string TypeEventDescription(void) const; //--- Возвращает наименование (1) ордера сделки события, (2) родительского ордера позиции, (3) ордера текущей позиции, (4) текущей позиции //--- Возвращает наименование (5) ордера и (6) позиции до смены направления string TypeOrderDealDescription(void) const; string TypeOrderFirstDescription(void) const; string TypeOrderEventDescription(void) const; string TypePositionCurrentDescription(void) const; string TypeOrderPreviousDescription(void) const; string TypePositionPreviousDescription(void) const; //--- Возвращает наименование причины сделки/ордера/позиции string ReasonDescription(void) const;
А за пределами тела класса — их реализацию:
//+------------------------------------------------------------------+ //| Возвращает наименование ордера/позиции/сделки | //+------------------------------------------------------------------+ string CEvent::TypeOrderDealDescription(void) const { ENUM_EVENT_STATUS status=this.Status(); return ( status==EVENT_STATUS_MARKET_PENDING || status==EVENT_STATUS_HISTORY_PENDING ? OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT)) : status==EVENT_STATUS_MARKET_POSITION || status==EVENT_STATUS_HISTORY_POSITION ? PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : status==EVENT_STATUS_BALANCE ? DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : TextByLanguage("Неизвестный тип ордера","Unknown order type") ); } //+------------------------------------------------------------------+ //| Возвращает наименование первого ордера позиции | //+------------------------------------------------------------------+ string CEvent::TypeOrderFirstDescription(void) const { return OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION)); } //+------------------------------------------------------------------+ //| Возвращает наименование ордера, изменившего позицию | //+------------------------------------------------------------------+ string CEvent::TypeOrderEventDescription(void) const { return OrderTypeDescription(this.TypeOrderEvent()); } //+------------------------------------------------------------------+ //| Возвращает наименование текущей позиции | //+------------------------------------------------------------------+ string CEvent::TypePositionCurrentDescription(void) const { return PositionTypeDescription(this.TypePositionCurrent()); } //+------------------------------------------------------------------+ //| Возвращает наименование ордера до смены направления | //+------------------------------------------------------------------+ string CEvent::TypeOrderPreviousDescription(void) const { return OrderTypeDescription(this.TypeOrderPosPrevious()); } //+------------------------------------------------------------------+ //| Возвращает наименование позиции до смены направления | //+------------------------------------------------------------------+ string CEvent::TypePositionPreviousDescription(void) const { return PositionTypeDescription(this.TypePositionPrevious()); } //+------------------------------------------------------------------+
Эти методы так же просты, как и методы, возвращающие типы ордеров и позиций. Разница лишь в функциях из файла DELib.mqh, возвращающих тип ордеров и позиций в виде описания их типов: PositionTypeDescription() и OrderTypeDescription().
Теперь нам необходимо в метод ReasonDescription() внести дополнения для учёта и возврата описаний вновь добавленных перечислений о причинах события для неттингового счёта:
//+------------------------------------------------------------------+ //| Возвращает наименование причины сделки/ордера/позиции | //+------------------------------------------------------------------+ 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_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) ); } //+------------------------------------------------------------------+
Ранее, в пятой части описания библиотеки, мы уже сделали метод,
расшифровывающий код торгового события, вспомним его логику:
В метод передаётся код события, и далее проверяются флаги кода события. При наличии в коде проверяемого флага устанавливается соответствующее торговое событие. Так как флагов в коде события может быть несколько, то проверяются все возможные флаги для данного события и из их комбинации определяется тип события. Далее тип события записывается в соответствующую переменную класса и вносится в свойство объекта-события (EVENT_PROP_TYPE_EVENT).
Теперь нам необходимо просто добавить в него отслеживание новых флагов в коде торгового события, соответствующих возможным событиям на неттинговом счёте:
//+------------------------------------------------------------------+ //| Расшифровывает код события и устанавливает торговое событие | //+------------------------------------------------------------------+ void CEvent::SetTypeEvent(void) { //--- Отложенный ордер установлен (проверяем на равенство коду события, так как здесь может быть только один флаг) 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_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; } } //+------------------------------------------------------------------+
Вся логика метода расписана в комментариях, и достаточна проста и прозрачна. Поэтому не будем задерживаться на разборе данного метода
<if-else>
На этом изменения класса абстрактного события завершены, приведём его полный листинг:
//+------------------------------------------------------------------+ //| Event.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" #property strict // Нужно для mql4 //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include <Object.mqh> #include "\..\..\Services\DELib.mqh" #include "..\..\Collections\HistoryCollection.mqh" #include "..\..\Collections\MarketCollection.mqh" //+------------------------------------------------------------------+ //| Класс абстрактного события | //+------------------------------------------------------------------+ 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_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){;} //--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство события void SetProperty(ENUM_EVENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_EVENT_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_EVENT_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value; } //--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство события long GetProperty(ENUM_EVENT_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_EVENT_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_EVENT_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Возвращает флаг поддержания событием данного свойства virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_STRING property) { return true; } //--- Устанавливает идентификатор графика управляющей программы void SetChartID(const long id) { this.m_chart_id=id; } //--- Расшифровывает код события и устанавливает торговое событие, (2) возвращает торговое событие void SetTypeEvent(void); ENUM_TRADE_EVENT TradeEvent(void) const { return this.m_trade_event; } //--- Отправляет событие на график (реализация в потомках класса) virtual void SendEvent(void) {;} //--- Сравнивает объекты CEvent между собой по заданному свойству (для сортировки списков по указанному свойству объекта-события) virtual int Compare(const CObject *node,const int mode=0) const; //--- Сравнивает объекты CEvent между собой по всем свойствам (для поиска равных объектов-событий) bool IsEqual(CEvent* compared_event); //+------------------------------------------------------------------+ //| Методы упрощённого доступа к свойствам объекта-события | //+------------------------------------------------------------------+ //--- Возвращает (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) символ встречной позиции string Symbol(void) const { return this.GetProperty(EVENT_PROP_SYMBOL); } string SymbolCloseBy(void) const { return this.GetProperty(EVENT_PROP_SYMBOL_BY_ID); } //+------------------------------------------------------------------+ //| Описания свойств объекта-ордера | //+------------------------------------------------------------------+ //--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства ордера string GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property); string GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property); string GetPropertyDescription(ENUM_EVENT_PROP_STRING property); //--- Возвращает наименование (1) статуса, (2) типа события string StatusDescription(void) const; string TypeEventDescription(void) const; //--- Возвращает наименование (1) ордера сделки события, (2) родительского ордера позиции, (3) ордера текущей позиции, (4) текущей позиции //--- Возвращает наименование (5) ордера и (6) позиции до смены направления string TypeOrderDealDescription(void) const; string TypeOrderFirstDescription(void) const; string TypeOrderEventDescription(void) const; string TypePositionCurrentDescription(void) const; string TypeOrderPreviousDescription(void) const; string TypePositionPreviousDescription(void) const; //--- Возвращает наименование причины сделки/ордера/позиции string ReasonDescription(void) const; //--- Выводит в журнал (1) описание свойств ордера (full_prop=true - все свойства, false - только поддерживаемые), //--- (2) краткое сообщение о событии (реализация в потомках класса) void Print(const bool full_prop=false); virtual void PrintShort(void) {;} }; //+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code) { 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(); } //+------------------------------------------------------------------+ //| Сравнивает объекты CEvent между собой по заданному свойству | //+------------------------------------------------------------------+ int CEvent::Compare(const CObject *node,const int mode=0) const { const CEvent *event_compared=node; //--- сравнение целочисленных свойств двух событий if(mode<EVENT_PROP_INTEGER_TOTAL) { long value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_EVENT_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- сравнение вещественных свойств двух событий if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL) { double value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- сравнение строковых свойств двух событий else if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_STRING_TOTAL) { string value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_EVENT_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+ //| Сравнивает объекты CEvent между собой по всем свойствам | //+------------------------------------------------------------------+ bool CEvent::IsEqual(CEvent *compared_event) { int beg=0, end=EVENT_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } beg=end; end+=EVENT_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } beg=end; end+=EVENT_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } //--- return true; } //+------------------------------------------------------------------+ //| Расшифровывает код события и устанавливает торговое событие | //+------------------------------------------------------------------+ void CEvent::SetTypeEvent(void) { //--- Отложенный ордер установлен (проверяем на равенство коду события, так как здесь может быть только один флаг) 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_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; } } //+------------------------------------------------------------------+ //| Возвращает описание целочисленного свойства события | //+------------------------------------------------------------------+ 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") : property==EVENT_PROP_TICKET_ORD_POS_BEFORE ? TextByLanguage("Тикет ордера позиции до смены направления","Ticket order of position before changing direction") : property==EVENT_PROP_TYPE_ORD_POS_CURRENT ? TextByLanguage("Тип ордера текущей позиции","Type order of current position") : property==EVENT_PROP_TICKET_ORD_POS_CURRENT ? TextByLanguage("Тикет ордера текущей позиции","Ticket order of current position") : 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) : EnumToString(property) ); } //+------------------------------------------------------------------+ //| Возвращает описание строкового свойства события | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_STRING property) { return ( property==EVENT_PROP_SYMBOL ? TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\"" : TextByLanguage("Символ встречной позиции","Symbol of opposite position")+": \""+this.GetProperty(property)+"\"" ); } //+------------------------------------------------------------------+ //| Возвращает наименование статуса события | //+------------------------------------------------------------------+ string CEvent::StatusDescription(void) const { ENUM_EVENT_STATUS status=(ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); return ( status==EVENT_STATUS_MARKET_PENDING ? TextByLanguage("Установлен отложенный ордер","Pending order placed") : status==EVENT_STATUS_MARKET_POSITION ? TextByLanguage("Открыта позиция","Position is open") : status==EVENT_STATUS_HISTORY_PENDING ? TextByLanguage("Удален отложенный ордер","Pending order removed") : status==EVENT_STATUS_HISTORY_POSITION ? TextByLanguage("Закрыта позиция","Position closed") : status==EVENT_STATUS_BALANCE ? TextByLanguage("Балансная операция","Balance operation") : TextByLanguage("Неизвестный статус","Unknown status") ); } //+------------------------------------------------------------------+ //| Возвращает наименование торгового события | //+------------------------------------------------------------------+ string CEvent::TypeEventDescription(void) const { ENUM_TRADE_EVENT event=this.TypeEvent(); return ( 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") : TextByLanguage("Нет торгового события","No trade event") ); } //+------------------------------------------------------------------+ //| Возвращает наименование ордера/позиции/сделки | //+------------------------------------------------------------------+ string CEvent::TypeOrderDealDescription(void) const { ENUM_EVENT_STATUS status=this.Status(); return ( status==EVENT_STATUS_MARKET_PENDING || status==EVENT_STATUS_HISTORY_PENDING ? OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT)) : status==EVENT_STATUS_MARKET_POSITION || status==EVENT_STATUS_HISTORY_POSITION ? PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : status==EVENT_STATUS_BALANCE ? DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : TextByLanguage("Неизвестный тип ордера","Unknown order type") ); } //+------------------------------------------------------------------+ //| Возвращает наименование первого ордера позиции | //+------------------------------------------------------------------+ string CEvent::TypeOrderFirstDescription(void) const { return OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION)); } //+------------------------------------------------------------------+ //| Возвращает наименование ордера, изменившего позицию | //+------------------------------------------------------------------+ string CEvent::TypeOrderEventDescription(void) const { return OrderTypeDescription(this.TypeOrderEvent()); } //+------------------------------------------------------------------+ //| Возвращает наименование текущей позиции | //+------------------------------------------------------------------+ string CEvent::TypePositionCurrentDescription(void) const { return PositionTypeDescription(this.TypePositionCurrent()); } //+------------------------------------------------------------------+ //| Возвращает наименование ордера до смены направления | //+------------------------------------------------------------------+ string CEvent::TypeOrderPreviousDescription(void) const { return OrderTypeDescription(this.TypeOrderPosPrevious()); } //+------------------------------------------------------------------+ //| Возвращает наименование позиции до смены направления | //+------------------------------------------------------------------+ string CEvent::TypePositionPreviousDescription(void) const { return PositionTypeDescription(this.TypePositionPrevious()); } //+------------------------------------------------------------------+ //| Возвращает наименование причины сделки/ордера/позиции | //+------------------------------------------------------------------+ 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_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) ); } //+------------------------------------------------------------------+ //| Выводит в журнал свойства события | //+------------------------------------------------------------------+ void CEvent::Print(const bool full_prop=false) { ::Print("============= ",TextByLanguage("Начало списка параметров события: \"","The beginning of the event parameter list: \""),this.StatusDescription(),"\" ============="); int beg=0, end=EVENT_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=EVENT_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=EVENT_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of the parameter list: \""),this.StatusDescription(),"\" ==================\n"); } //+------------------------------------------------------------------+
Так как торговля на хеджевом и неттинговом счёте различается только при работе с позициями, то классы-наследники CEventPositionOpen и CEventPositionClose класса абстрактного события CEvent требуют косметической доработки — доработаем только методы вывода сообщений о событиях в журнал. Остальные методы этих классов остаются без изменений.
Откроем файл EventPositionOpen.mqh и добавим приватный метод, создающий и возвращающий краткое описание события:
//+------------------------------------------------------------------+ //| Событие открытия позиции | //+------------------------------------------------------------------+ class CEventPositionOpen : public CEvent { private: //--- Создаёт и возвращает краткое сообщение события string EventsMessage(void); public: //--- Конструктор CEventPositionOpen(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_POSITION,event_code,ticket) {} //--- Поддерживаемые свойства ордера (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); }; //+------------------------------------------------------------------+
За пределами тела класса напишем его реализацию:
//+------------------------------------------------------------------+ //| Создаёт и возвращает краткое сообщение события | //+------------------------------------------------------------------+ string CEventPositionOpen::EventsMessage(void) { //--- количество знаков после запятой в котировке символа события int digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS); //--- (1) заголовок, (2) исполненный объём ордера, (3) исполненный объём позиции, (4) цена, на которой произошло событие //--- (5) цена StopLoss, (6) цена TakeProfit, (7) магический номер, (6) профит в валюте счёта string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string vol_ord=::DoubleToString(this.VolumeOrderExecuted(),DigitsLots(this.Symbol())); string vol_pos=::DoubleToString(this.VolumePositionExecuted(),DigitsLots(this.Symbol())); string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceEvent(),digits); string sl=(this.PriceStopLoss()>0 ? ", sl "+ ::DoubleToString(this.PriceStopLoss(),digits) : ""); string tp=(this.PriceTakeProfit()>0 ? ", tp "+ ::DoubleToString(this.PriceTakeProfit(),digits) : ""); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string profit=TextByLanguage(", профит ",", profit ")+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY); //--- string text=""; //--- Разворот позиции if(this.GetProperty(EVENT_PROP_REASON_EVENT)<EVENT_REASON_ACTIVATED_PENDING) { //--- EURUSD: Buy #xx изменён на 0.1 Sell #xx (0.2 ордер SellLimit #XX) по цене х.ххххх, sl х.ххххх, tp x.xxxxx, magic, профит xxxx text= ( this.Symbol()+" "+ this.TypePositionPreviousDescription()+" #"+(string)this.TicketPositionPrevious()+ TextByLanguage(" изменен на "," turned to ")+vol_pos+" "+this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic+profit ); } else { //--- Добавление объёма if(this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT)!=this.GetProperty(EVENT_PROP_POSITION_ID)) { //--- EURUSD: Добавлено 0.1 к Buy #xx (ордер BuyLimit #XX) по цене х.ххххх, magic text= ( this.Symbol()+" "+ TextByLanguage("Добавлено ","Added ")+vol_ord+TextByLanguage(" к "," to ")+ this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+magic ); } //--- Открытие позиции else { //--- EURUSD: Открыт 0.1 Buy #xx (ордер BuyLimit #XX) по цене х.ххххх, sl х.ххххх, tp x.xxxxx, magic text= ( this.Symbol()+" "+ TextByLanguage("Открыт ","Open ")+vol_pos+" "+ this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic ); } } return head+text; } //+------------------------------------------------------------------+
Метод просто создаёт варианты сообщений в зависимости от состояния события и наличия некоторых свойств у объекта-события.
Если,
например, задан StopLoss, то в текст добавляется заголовок "sl" и его цена, если не задан, то вместо записи о StopLoss подставляется пустая
строка. Так же делаем и с остальными некоторыми свойствами события. В листинге метода в комментариях описаны
условия, при которых создаётся тот или иной текст события, а также
приведены
примеры возвращаемого методом текста.
Созданный в методе текст выводится в журнал из метода PrintShort(), который в свою очередь будем вызывать из метода Refresh() в классе-коллекции событий вызовом виртуального метода SendEvent() класса CEvent, и переопределённого здесь — в классе CEventPositionOpen.
Приведём полный листинг класса CEventPositionOpen:
//+------------------------------------------------------------------+ //| EventPositionOpen.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 CEventPositionOpen : public CEvent { private: //--- Создаёт и возвращает краткое сообщение события string EventsMessage(void); public: //--- Конструктор CEventPositionOpen(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_POSITION,event_code,ticket) {} //--- Поддерживаемые свойства ордера (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 CEventPositionOpen::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return(property==EVENT_PROP_POSITION_BY_ID ? false : true); } //+------------------------------------------------------------------+ //| Возвращает истину, если событие поддерживает переданное | //| вещественное свойство, возвращает ложь в противном случае | //+------------------------------------------------------------------+ bool CEventPositionOpen::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { if(property==EVENT_PROP_PRICE_CLOSE || property==EVENT_PROP_PROFIT ) return false; return true; } //+------------------------------------------------------------------+ //| Выводит в журнал краткое сообщение о событии | //+------------------------------------------------------------------+ void CEventPositionOpen::PrintShort(void) { ::Print(this.EventsMessage()); } //+------------------------------------------------------------------+ //| Отправляет событие на график | //+------------------------------------------------------------------+ void CEventPositionOpen::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.PositionID(),this.PriceOpen(),this.Symbol()); } //+------------------------------------------------------------------+ //| Создаёт и возвращает краткое сообщение события | //+------------------------------------------------------------------+ string CEventPositionOpen::EventsMessage(void) { //--- количество знаков после запятой в котировке символа события int digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS); //--- (1) заголовок, (2) исполненный объём ордера, (3) исполненный объём позиции, (4) цена, на которой произошло событие //--- (5) цена StopLoss, (6) цена TakeProfit, (7) магический номер, (6) профит в валюте счёта string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string vol_ord=::DoubleToString(this.VolumeOrderExecuted(),DigitsLots(this.Symbol())); string vol_pos=::DoubleToString(this.VolumePositionExecuted(),DigitsLots(this.Symbol())); string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceEvent(),digits); string sl=(this.PriceStopLoss()>0 ? ", sl "+ ::DoubleToString(this.PriceStopLoss(),digits) : ""); string tp=(this.PriceTakeProfit()>0 ? ", tp "+ ::DoubleToString(this.PriceTakeProfit(),digits) : ""); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string profit=TextByLanguage(", профит ",", profit ")+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY); //--- string text=""; //--- Разворот позиции if(this.GetProperty(EVENT_PROP_REASON_EVENT)<EVENT_REASON_ACTIVATED_PENDING) { //--- EURUSD: Buy #xx изменён на 0.1 Sell #xx [0.2 ордер SellLimit #XX] по цене х.ххххх, sl х.ххххх, tp x.xxxxx, magic, профит xxxx text= ( this.Symbol()+" "+ this.TypePositionPreviousDescription()+" #"+(string)this.TicketPositionPrevious()+ TextByLanguage(" изменен на "," turned to ")+vol_pos+" "+this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic+profit ); } else { //--- Добавление объёма if(this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT)!=this.GetProperty(EVENT_PROP_POSITION_ID)) { //--- EURUSD: Добавлено 0.1 к Buy #xx [ордер BuyLimit #XX] по цене х.ххххх, magic text= ( this.Symbol()+" "+ TextByLanguage("Добавлено ","Added ")+vol_ord+TextByLanguage(" к "," to ")+ this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+magic ); } //--- Открытие позиции else { //--- EURUSD: Открыт 0.1 Buy #xx [ордер BuyLimit #XX] по цене х.ххххх, sl х.ххххх, tp x.xxxxx, magic text= ( this.Symbol()+" "+ TextByLanguage("Открыт ","Open ")+vol_pos+" "+ this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic ); } } return head+text; } //+------------------------------------------------------------------+
По аналогии изменим класс CEventPositionClose:
//+------------------------------------------------------------------+ //| EventPositionClose.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 CEventPositionClose : public CEvent { private: //--- Создаёт и возвращает краткое сообщение события string EventsMessage(void); public: //--- Конструктор CEventPositionClose(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_POSITION,event_code,ticket) {} //--- Поддерживаемые свойства ордера (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 CEventPositionClose::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return true; } //+------------------------------------------------------------------+ //| Возвращает истину, если событие поддерживает переданное | //| вещественное свойство, возвращает ложь в противном случае | //+------------------------------------------------------------------+ bool CEventPositionClose::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return true; } //+------------------------------------------------------------------+ //| Выводит в журнал краткое сообщение о событии | //+------------------------------------------------------------------+ void CEventPositionClose::PrintShort(void) { ::Print(this.EventsMessage()); } //+------------------------------------------------------------------+ //| Отправляет событие на график | //+------------------------------------------------------------------+ void CEventPositionClose::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.PositionID(),this.PriceClose(),this.Symbol()); } //+------------------------------------------------------------------+ //| Создаёт и возвращает краткое сообщение события | //+------------------------------------------------------------------+ string CEventPositionClose::EventsMessage(void) { //--- количество знаков после запятой в котировке символа события int digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS); //--- (1) заголовок, (2) исполненный объём ордера, (3) исполненный объём позиции, (4) цена, на которой произошло событие //--- (5) цена StopLoss, (6) цена TakeProfit, (7) магический номер, (6) профит в валюте счёта, (7,8) Варианты сообщения закрытия string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string vol_ord=::DoubleToString(this.VolumeOrderExecuted(),DigitsLots(this.Symbol())); string vol_pos=::DoubleToString(this.VolumePositionExecuted(),DigitsLots(this.Symbol())); string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceEvent(),digits); string sl=(this.PriceStopLoss()>0 ? ", sl "+ ::DoubleToString(this.PriceStopLoss(),digits) : ""); string tp=(this.PriceTakeProfit()>0 ? ", tp "+ ::DoubleToString(this.PriceTakeProfit(),digits) : ""); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string profit=TextByLanguage(", профит ",", profit ")+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY); string close=TextByLanguage("Закрыт ","Close "); string in_pos=""; //--- if(this.GetProperty(EVENT_PROP_TYPE_EVENT)>TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL) { close=TextByLanguage("Закрыт объём ","Closed volume ")+vol_ord; in_pos=TextByLanguage(" в "," in "); } string opposite= ( this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS) ? TextByLanguage(" встречным "," by opposite ")+this.SymbolCloseBy()+" "+ this.TypeOrderDealDescription()+" #"+(string)this.PositionByID()+(this.MagicCloseBy()> 0 ? "("+(string)this.MagicCloseBy()+" ]" : "") : "" ); //--- EURUSD: Закрыт 0.1 Sell #xx [0.2 ордер SellLimit #XX] по цене х.ххххх, sl х.ххххх, tp x.xxxxx, magic, профит xxxx string text= ( this.Symbol()+" "+close+in_pos+this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ opposite+" ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic+profit ); return head+text; } //+------------------------------------------------------------------+
Все классы объектов-событий изменили под новые задачи работы на неттинговых счетах.
Теперь займёмся
классом-коллекцией событий CEventCollection.
Ранее, в методе создания нового события CreateNewEvent(), рассмотренного нами в
пятой части описания библиотеки, имелась локальная переменная для хранения кода торгового события.
Сделаем её
приватным членом класса, удалив из метода создания нового события и объявив
в приватной секции класса. Сразу же впишем объявления необходимых
нам методов для создания нового события для
хеджевого и неттингового
типов счетов, метод для возврата списка всех сделок InOut по идентификатору
позиции и метод для получения объекта рыночной позиции по её идентификатору.
//+------------------------------------------------------------------+ //| Коллекция событий счёта | //+------------------------------------------------------------------+ 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; // Объект-событие для поиска по свойству //--- Создаёт торговое событие в зависимости от статуса ордера void CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market); //--- Создаёт событие для (1) хеджевого счёта, (2) неттингового счёта void NewDealEventHedge(COrder* deal,CArrayObj* list_history,CArrayObj* list_market); void NewDealEventNetto(COrder* deal,CArrayObj* list_history,CArrayObj* list_market); //--- Выбирает из списка и возвращает список рыночных отложенных ордеров CArrayObj* GetListMarketPendings(CArrayObj* list); //--- Выбирает из списка и возвращает список исторических (1) удалённых отложенных ордеров, (2) сделок, (3) всех закрывающих ордеров CArrayObj* GetListHistoryPendings(CArrayObj* list); CArrayObj* GetListDeals(CArrayObj* list); CArrayObj* GetListCloseByOrders(CArrayObj* list); //--- Возвращает список (1) всех ордеров позиции по её идентификатору, (2) всех сделок позиции по её идентификатору //--- (3) всех сделок на вход в рынок по идентификатору позиции, (4) всех сделок на выход из рынка по идентификатору позиции, //--- (5) всех сделок на разворот позиции по идентификатору позиции CArrayObj* GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsInByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsOutByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsInOutByPosID(CArrayObj* list,const ulong position_id); //--- Возвращает суммарный объём всех сделок (1) IN, (2) OUT позиции по её идентификатору double SummaryVolumeDealsInByPosID(CArrayObj* list,const ulong position_id); double SummaryVolumeDealsOutByPosID(CArrayObj* list,const ulong position_id); //--- Возвращает (1) первый, (2) последний и (3) закрывающий ордер из списка всех ордеров позиции, //--- (4) ордер по тикету, (5) рыночную позицию по идентификатору, //--- (6) последнюю и (7) предпоследнюю сделку InOut по идентификатору позиции COrder* GetFirstOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetLastOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetCloseByOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetHistoryOrderByTicket(CArrayObj* list,const ulong order_ticket); COrder* GetPositionByID(CArrayObj* list,const ulong position_id); //--- Возвращает флаг наличия объекта-события в списке событий bool IsPresentEventInList(CEvent* compared_event); public: //--- Выбирает события из коллекции со временем в диапазоне от begin_time до end_time CArrayObj *GetListByTime(const datetime begin_time=0,const datetime end_time=0); //--- Возвращает полный список-коллекцию событий "как есть" CArrayObj *GetList(void) { return &this.m_list_events; } //--- Возвращает список по выбранному (1) целочисленному, (2) вещественному и (3) строковому свойству, удовлетворяющему сравниваемому критерию CArrayObj *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } //--- Обновляет список событий void Refresh(CArrayObj* list_history, CArrayObj* list_market, const bool is_history_event, const bool is_market_event, const int new_history_orders, const int new_market_pendings, const int new_market_positions, const int new_deals); //--- Устанавливает идентификатор графика управляющей программы void SetChartID(const long id) { this.m_chart_id=id; } //--- Возвращает последнее торговое событие на счёте ENUM_TRADE_EVENT GetLastTradeEvent(void) const { return this.m_trade_event; } //--- Сбрасывает последнее торговое событие void ResetLastTradeEvent(void) { this.m_trade_event=TRADE_EVENT_NO_EVENT; } //--- Конструктор CEventsCollection(void); }; //+------------------------------------------------------------------+
В конструкторе класса, в его списке инициализации, сбросим код торгового события:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ 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(); } //+------------------------------------------------------------------+
При множестве торговых манипуляций на неттинговом счёте — а там может быть лишь одна позиция на одном символе, с этой позицией будут происходить различные метаморфозы — как изменение её объёма при частичных закрытиях, вызванных срабатыванием ордеров в противоположном направлении, но с меньшим объёмом чем у позиции, так и добавление объёма к позиции — при срабатывании ордеров, выставленных в том же направлении, что и у позиции.
Но самые интересные изменения происходят с позицией при срабатывании ордеров, выставленных в противоположном направлении, и с большим
объёмом чем у позиции. В такой ситуации позиции присваивается новый тикет, соответствующий сработавшему ордеру и тип позиции меняется на
противоположный — происходит разворот позиции. При этом идентификатор позиции остаётся неизменным и равным тикету самого первого
ордера, срабатывание которого привело к появлению позиции на счёте.
Нам, чтобы (1) правильно выводить записи в журнал о развороте позиции и (2) иметь возможность получения данных о событии разворота
позиции в своих программах — какая она была и какой она стала, необходимо иметь возможность отследить все изменения направления позиции,
которые происходили с ней за время её жизни. Для этого нам необходимо иметь доступ ко всем её сделкам, имеющим способ изменения позиции
DEAL_ENTRY_INOUT из перечисления
ENUM_DEAL_ENTRY.
И тогда нам остаётся только расположить такие сделки в порядке их следования по времени их появления и брать нужную нам сделку. А в самой сделке будут прописаны все свойства ордеров, которые привели к сделке.
Соответственно, имея "на руках" ордер сделки, можно получить и тикет позиции, направление которой было изменено, и тип ордера, срабатывание которого привело к развороту позиции, и новые уровни StopLoss и TakeProfit, и т.д. А всё, что нам нужно для получения таких возможностей — это создать список всех сделок InOut позиции по её идентификатору, что очень просто сделать, используя создаваемую нами библиотеку.Рассмотрим метод получения всех сделок InOut позиции по её идентификатору:
//+------------------------------------------------------------------+ //| Возвращает список всех сделок на разворот (IN_OUT) | //| по идентификатору позиции | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListAllDealsInOutByPosID(CArrayObj *list,const ulong position_id) { if(list.Type()!=COLLECTION_HISTORY_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection")); return NULL; } CArrayObj* list_deals=this.GetListAllDealsByPosID(list,position_id); list_deals=CSelect::ByOrderProperty(list_deals,ORDER_PROP_DEAL_ENTRY,DEAL_ENTRY_INOUT,EQUAL); return list_deals; } //+------------------------------------------------------------------+
Проверяем тип переданного в метод списка, и если этот список не является списком исторической коллекции ордеров и сделок, то предупреждаем об ошибке и возвращаем NULL.
Хочу отметить, что все такие проверки списков в классах нужны нам для контроля собственных ошибок, и в последующем — после отладки — будут удалены, чтобы не нагружать расчёты ненужными проверками.
Далее получаем список только сделок по идентификатору позиции (метод был рассмотрен нами в прошлой статье) и фильтруем полученный список по способу изменения позиции InOut и возвращаем итоговый список.
Для получения данных об открытой позиции, или определения её отсутствия, создадим метод, получающий объект-рыночную позицию по её идентификатору:
//+------------------------------------------------------------------+ //| Возвращает позицию по идентификатору | //+------------------------------------------------------------------+ COrder* CEventsCollection::GetPositionByID(CArrayObj *list,const ulong position_id) { if(list.Type()!=COLLECTION_MARKET_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком рыночной коллекции","Error. The list is not a list of the market collection")); return NULL; } CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_POSITION,EQUAL); list_orders=CSelect::ByOrderProperty(list_orders,ORDER_PROP_POSITION_ID,position_id,EQUAL); if(list_orders==NULL || list_orders.Total()==0) return NULL; COrder* order=list_orders.At(0); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
Этот метод, впрочем как и все похожие из состава библиотеки, тоже простой. Проверяем
тип переданного списка, и если это не список коллекции рыночных ордеров и позиций, то сообщаем об ошибке и возвращаем NULL.
Затем берём из переданного в метод списка только объекты активных позиций
и
фильтруем его по переданному в метод идентификатору позиции.
Если список не удалось получить, или список не имеет ни одного объекта, то возвращаем NULL—
нет запрашиваемой позиции.
Затем получаем из списка единственный объект-рыночную позицию
(позиция с заданным идентификатором может быть в рынке лишь одна) и возвращаем
либо сам объект, либо
NULL при ошибке его получения.
Метод создания нового объекта-события CreateNewEvent() был нами рассмотрен в прошлой
статье.
Здесь же скажу лишь о его изменениях.
Из метода была удалена локальная переменная
int trade_event_code
Теперь она — член класса уже созданный нами в приватной секции.
Логика метода осталась прежней, и лишь в самом конце был добавлен вызов нужных методов для обработки типов счёта, на котором работаем — если тип счёта хеджевый, то вызывается метод создания нового события для хеджевого счёта, иначе — вызывается метод создания нового события для неттингового счёта:
//+------------------------------------------------------------------+ //| Создаёт торговое событие в зависимости от статуса ордера | //+------------------------------------------------------------------+ void CEventsCollection::CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market) { this.m_trade_event_code=TRADE_EVENT_FLAG_NO_EVENT; ENUM_ORDER_STATUS status=order.Status(); //--- Установлен отложенный ордер if(status==ORDER_STATUS_MARKET_PENDING) { this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_PLASED; CEvent* event=new CEventOrderPlased(this.m_trade_event_code,order.Ticket()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Время события event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_DONE); // Причина события (из перечисления ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Тип сделки события event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Тикет ордера события event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Тип ордера события event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Тип ордера события event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Тикет ордера события event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Тикет ордера event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Идентификатор позиции event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // Идентификатор встречной позиции event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order.Magic()); // Магический номер встречной позиции event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrder()); // Тип ордера позиции до смены направления 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_MAGIC_ORDER,order.Magic()); // Магический номер ордера event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC()); // Время ордера event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Цена, на которой произошло событие event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Цена установки ордера event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Цена закрытия ордера 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,order.Volume()-order.VolumeCurrent()); // Исполненный объём ордера event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.VolumeCurrent()); // Оставшийся (неисполненный) объём ордера event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,0); // Исполненный объём позиции event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Профит 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; } } } //--- Удалён отложенный ордер if(status==ORDER_STATUS_HISTORY_PENDING) { this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_REMOVED; CEvent* event=new CEventOrderRemoved(this.m_trade_event_code,order.Ticket()); if(event!=NULL) { ENUM_EVENT_REASON reason= ( order.State()==ORDER_STATE_CANCELED ? EVENT_REASON_CANCEL : order.State()==ORDER_STATE_EXPIRED ? EVENT_REASON_EXPIRED : EVENT_REASON_DONE ); event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeCloseMSC()); // Время события event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Причина события (из перечисления ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Тип ордера события event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Тикет ордера события event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Тип ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Тип ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Тикет ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Тикет ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Идентификатор позиции event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // Идентификатор встречной позиции event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order.Magic()); // Магический номер встречной позиции event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrder()); // Тип ордера позиции до смены направления 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_MAGIC_ORDER,order.Magic()); // Магический номер ордера event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC()); // Время ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Цена, на которой произошло событие event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Цена установки ордера event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Цена закрытия ордера 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,order.Volume()-order.VolumeCurrent()); // Исполненный объём ордера event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.VolumeCurrent()); // Оставшийся (неисполненный) объём ордера event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,0); // Исполненный объём позиции event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Профит 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; } } } //--- Открыта позиция (__MQL4__) if(status==ORDER_STATUS_MARKET_POSITION) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED; CEvent* event=new CEventPositionOpen(this.m_trade_event_code,order.Ticket()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpen()); // Время события event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_DONE); // Причина события (из перечисления ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Тип сделки события event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Тикет сделки события event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Тип ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Тип ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Тикет ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Тикет ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Идентификатор позиции event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // Идентификатор встречной позиции event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order.Magic()); // Магический номер встречной позиции event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrder()); // Тип ордера позиции до смены направления 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_MAGIC_ORDER,order.Magic()); // Магический номер ордера/сделки/позиции event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpen()); // Время ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Цена, на которой произошло событие event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Цена открытия ордера/сделки/позиции event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Цена закрытия ордера/сделки/позиции 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,order.Volume()-order.VolumeCurrent()); // Исполненный объём ордера event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.VolumeCurrent()); // Оставшийся (неисполненный) объём ордера event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,order.Volume()); // Исполненный объём позиции event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Профит 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; } } } //--- Новая сделка (__MQL5__) if(status==ORDER_STATUS_DEAL) { //--- Новая балансная оберация if((ENUM_DEAL_TYPE)order.TypeOrder()>DEAL_TYPE_SELL) { this.m_trade_event_code=TRADE_EVENT_FLAG_ACCOUNT_BALANCE; CEvent* event=new CEventBalanceOperation(this.m_trade_event_code,order.Ticket()); if(event!=NULL) { ENUM_EVENT_REASON reason= ( (ENUM_DEAL_TYPE)order.TypeOrder()==DEAL_TYPE_BALANCE ? (order.Profit()>0 ? EVENT_REASON_BALANCE_REFILL : EVENT_REASON_BALANCE_WITHDRAWAL) : (ENUM_EVENT_REASON)(order.TypeOrder()+REASON_EVENT_SHIFT) ); event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Время события event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Причина события (из перечисления ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Тип сделки события event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Тикет сделки события event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Тип ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Тип ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Тикет ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Тикет ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Идентификатор позиции event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // Идентификатор встречной позиции event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order.Magic()); // Магический номер встречной позиции event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrder()); // Тип ордера позиции до смены направления 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_MAGIC_ORDER,order.Magic()); // Магический номер ордера/сделки/позиции event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC()); // Время ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Цена, на которой произошло событие event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Цена открытия ордера/сделки/позиции event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceOpen()); // Цена закрытия ордера/сделки/позиции event.SetProperty(EVENT_PROP_PRICE_SL,0); // Цена StopLoss сделки event.SetProperty(EVENT_PROP_PRICE_TP,0); // Цена TakeProfit сделки event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume()); // Запрашиваемый объём сделки event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,order.Volume()); // Исполненный объём сделки event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,0); // Оставшийся (неисполненный) объём сделки event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,order.Volume()); // Исполненный объём позиции event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Профит 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; } } } //--- Если это не балансная операция else { if(this.m_is_hedge) this.NewDealEventHedge(order,list_history,list_market); else this.NewDealEventNetto(order,list_history,list_market); } } } //+------------------------------------------------------------------+
Метод создания нового события для хеджевого счёта:
//+------------------------------------------------------------------+ //| Создаёт событие для хеджевого счёта | //+------------------------------------------------------------------+ void CEventsCollection::NewDealEventHedge(COrder* deal,CArrayObj* list_history,CArrayObj* list_market) { //--- Вход в рынок if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_IN) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED; int reason=EVENT_REASON_DONE; //--- Ищем все сделки позиции в направлении её открытия и считаем их общий объём double volume_in=this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID()); //--- Возьмём ордер сделки и последний ордер позиции из списка всех ордеров позиции ulong order_ticket=deal.GetProperty(ORDER_PROP_DEAL_ORDER_TICKET); COrder* order_first=this.GetHistoryOrderByTicket(list_history,order_ticket); COrder* order_last=this.GetLastOrderFromList(list_history,deal.PositionID()); //--- Получим открытую позицию по тикету COrder* position=this.GetPositionByID(list_market,deal.PositionID()); double vol_position=(position!=NULL ? position.Volume() : 0); //--- Если последнего ордера нет, то первый и последний ордер позиции - один и тот же if(order_last==NULL) order_last=order_first; if(order_first!=NULL) { //--- Если не весь объём ордера открыт - значит частичное исполнение if(this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID())<order_first.Volume()) { this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; reason=EVENT_REASON_DONE_PARTIALLY; } //--- Если открывающий ордер - отложенный, значит - активирован отложенный ордер if(order_first.TypeOrder()>ORDER_TYPE_SELL && order_first.TypeOrder()<ORDER_TYPE_CLOSE_BY) { this.m_trade_event_code+=TRADE_EVENT_FLAG_ORDER_ACTIVATED; //--- Если ордер исполнен частично, ставим в причину события частичное исполнение ордера reason= (this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID())<order_first.Volume() ? EVENT_REASON_ACTIVATED_PENDING_PARTIALLY : EVENT_REASON_ACTIVATED_PENDING ); } CEvent* event=new CEventPositionOpen(this.m_trade_event_code,deal.PositionID()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC()); // Время события (Время открытия позиции) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Причина события (из перечисления ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder()); // Тип сделки события event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket()); // Тикет сделки события event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder()); // Тип ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket()); // Тикет ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_last.TypeOrder()); // Тип ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_last.Ticket()); // Тикет ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID()); // Идентификатор позиции event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last.PositionByID()); // Идентификатор встречной позиции //--- event.SetProperty(EVENT_PROP_MAGIC_BY_ID,deal.Magic()); // Магический номер встречной позиции event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order_first.TypeOrder()); // Тип ордера позиции до смены направления event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order_first.Ticket()); // Тикет ордера позиции до смены направления event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order_first.TypeOrder()); // Тип ордера текущей позиции event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order_first.Ticket()); // Тикет ордера текущей позиции event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,deal.Symbol()); // Символ встречной позиции //--- event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic()); // Магический номер ордера/сделки/позиции event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC()); // Время ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen()); // Цена, на которой произошло событие (Цена открытия позиции) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen()); // Цена открытия ордера (Цена установки открывающего ордера позиции) event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last.PriceClose()); // Цена закрытия ордера (Цена закрытия последнего ордера позиции) event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss()); // Цена StopLoss (Цена StopLoss ордера позиции) event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit()); // Цена TakeProfit (Цена TakeProfit ордера позиции) event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_first.Volume()); // Запрашиваемый объём ордера event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,(order_first.Volume()-order_first.VolumeCurrent())); // Исполненный объём ордера event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order_first.VolumeCurrent()); // Оставшийся (неисполненный) объём ордера event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position); // Исполненный объём позиции event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull()); // Профит event.SetProperty(EVENT_PROP_SYMBOL,deal.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; } } } } //--- Выход из рынка else if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED; int reason=EVENT_REASON_DONE; //--- Возьмём первый и последний ордера позиции из списка всех ордеров позиции COrder* order_first=this.GetFirstOrderFromList(list_history,deal.PositionID()); COrder* order_last=this.GetLastOrderFromList(list_history,deal.PositionID()); //--- Получим открытую позицию по тикету COrder* position=this.GetPositionByID(list_market,deal.PositionID()); double vol_position=(position!=NULL ? position.Volume() : 0); if(order_first!=NULL && order_last!=NULL) { //--- Ищем все сделки позиции в направлении её открытия и закрытия, и считаем их общий объём double volume_in=this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID()); double volume_out=this.SummaryVolumeDealsOutByPosID(list_history,deal.PositionID()); //--- Рассчитываем текущий объём закрытой позиции int dgl=(int)DigitsLots(deal.Symbol()); double volume_current=::NormalizeDouble(volume_in-volume_out,dgl); //--- Если не весь объём позиции закрыт - значит частичное исполнение if(volume_current>0) { this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; } //--- Если закрывающий ордер исполнен частично, ставим в причину события частичное исполнение закрывающего ордера if(order_last.VolumeCurrent()>0) { reason=EVENT_REASON_DONE_PARTIALLY; } //--- Если у закрывающего ордера позиции выставлен флаг закрытия по StopLoss - значит закрытие по StopLoss //--- Если StopLoss-ордер исполнен частично, ставим в причину события частичное исполнение ордера StopLoss if(order_last.IsCloseByStopLoss()) { this.m_trade_event_code+=TRADE_EVENT_FLAG_SL; reason=(order_last.VolumeCurrent()>0 ? EVENT_REASON_DONE_SL_PARTIALLY : EVENT_REASON_DONE_SL); } //--- Если у закрывающего ордера позиции выставлен флаг закрытия по TakeProfit - значит закрытие по TakeProfit //--- Если TakeProfit-ордер исполнен частично, ставим в причину события частичное исполнение ордера TakeProfit else if(order_last.IsCloseByTakeProfit()) { this.m_trade_event_code+=TRADE_EVENT_FLAG_TP; reason=(order_last.VolumeCurrent()>0 ? EVENT_REASON_DONE_TP_PARTIALLY : EVENT_REASON_DONE_TP); } //--- CEvent* event=new CEventPositionClose(this.m_trade_event_code,deal.PositionID()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC()); // Время события (Время закрытия позиции) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Причина события (из перечисления ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder()); // Тип сделки события event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket()); // Тикет сделки события event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder()); // Тип ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_last.TypeOrder()); // Тип ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket()); // Тикет ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_last.Ticket()); // Тикет ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID()); // Идентификатор позиции event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last.PositionByID()); // Идентификатор встречной позиции //--- event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order_last.Magic()); // Магический номер встречной позиции event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order_first.TypeOrder()); // Тип ордера позиции до смены направления event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order_first.Ticket()); // Тикет ордера позиции до смены направления event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order_first.TypeOrder()); // Тип ордера текущей позиции event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order_first.Ticket()); // Тикет ордера текущей позиции event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order_last.Symbol()); // Символ встречной позиции //--- event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic()); // Магический номер ордера/сделки/позиции event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC()); // Время ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen()); // Цена, на которой произошло событие (Цена закрытия позиции) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen()); // Цена открытия ордера (Цена установки открывающего ордера позиции) event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last.PriceClose()); // Цена закрытия ордера (Цена закрытия последнего ордера позиции) event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss()); // Цена StopLoss (Цена StopLoss ордера позиции) event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit()); // Цена TakeProfit (Цена TakeProfit ордера позиции) event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_last.Volume()); // Запрашиваемый объём ордера event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,order_last.Volume()-order_last.VolumeCurrent()); // Исполненный объём ордера event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order_last.VolumeCurrent()); // Оставшийся (текущий) объём ордера event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position); // Оставшийся (текущий) объём позиции //--- event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull()); // Профит event.SetProperty(EVENT_PROP_SYMBOL,deal.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; } } } } //--- Встречная позиция else if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT_BY) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED; int reason=EVENT_REASON_DONE_BY_POS; //--- Возьмём первый и закрывающий ордера позиции из списка всех ордеров позиции COrder* order_first=this.GetFirstOrderFromList(list_history,deal.PositionID()); COrder* order_close=this.GetCloseByOrderFromList(list_history,deal.PositionID()); //--- Получим открытую позицию по идентификатору COrder* position=this.GetPositionByID(list_market,order_first.PositionID()); double vol_position=(position!=NULL ? position.Volume() : 0); if(order_first!=NULL && order_close!=NULL) { //--- Добавляем флаг закрытия встречной this.m_trade_event_code+=TRADE_EVENT_FLAG_BY_POS; //--- Возьмём первый ордер закрывающей позиции Print(DFUN,"PositionByID=",order_close.PositionByID()); CArrayObj* list_close_by=this.GetListAllOrdersByPosID(list_history,order_close.PositionByID()); COrder* order_close_by=list_close_by.At(0); if(order_close_by==NULL) return; //--- Ищем все сделки закрытой позиции в направлении её открытия и закрытия, и считаем их общий объём double volume_in=this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID()); double volume_out=this.SummaryVolumeDealsOutByPosID(list_history,deal.PositionID());//+order_close.Volume(); //--- Рассчитываем текущий объём закрытой позиции int dgl=(int)DigitsLots(deal.Symbol()); double volume_current=::NormalizeDouble(volume_in-volume_out,dgl); //--- Ищем все сделки встречной позиции в направлении её открытия и закрытия, и считаем их общий объём double volume_opp_in=this.SummaryVolumeDealsInByPosID(list_history,order_close.PositionByID()); double volume_opp_out=this.SummaryVolumeDealsOutByPosID(list_history,order_close.PositionByID()); //--- Рассчитываем текущий объём встречной позиции double volume_opp_current=::NormalizeDouble(volume_opp_in-volume_opp_out,dgl); //--- Если не весь объём закрытой позиции закрыт - значит частичное закрытие if(volume_current>0 || order_close.VolumeCurrent()>0) { //--- Добавляем флаг частичного закрытия this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; //--- Если встречная позиция закрыта частично - значит частичное закрытие частью объёма встречной позиции reason=(volume_opp_current>0 ? EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY : EVENT_REASON_DONE_PARTIALLY_BY_POS); } //--- Если закрыт весь объём позиции и есть частичное исполнение встречной - значит закрытие частью объёма встречной позиции else { if(volume_opp_current>0) { reason=EVENT_REASON_DONE_BY_POS_PARTIALLY; } } CEvent* event=new CEventPositionClose(this.m_trade_event_code,deal.PositionID()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC()); // Время события event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Причина события (из перечисления ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder()); // Тип сделки события event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket()); // Тикет сделки события event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_close.TypeOrder()); // Тип ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_close.Ticket()); // Тикет ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC()); // Время ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder()); // Тип ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket()); // Тикет ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID()); // Идентификатор позиции event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_close.PositionByID()); // Идентификатор встречной позиции //--- event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order_close_by.Magic()); // Магический номер встречной позиции event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order_first.TypeOrder()); // Тип ордера позиции до смены направления event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order_first.Ticket()); // Тикет ордера позиции до смены направления event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order_first.TypeOrder()); // Тип ордера текущей позиции event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order_first.Ticket()); // Тикет ордера текущей позиции event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order_close_by.Symbol()); // Символ встречной позиции //--- event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic()); // Магический номер ордера/сделки/позиции event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen()); // Цена, на которой произошло событие event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen()); // Цена открытия ордера/сделки/позиции event.SetProperty(EVENT_PROP_PRICE_CLOSE,deal.PriceClose()); // Цена закрытия ордера/сделки/позиции event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss()); // Цена StopLoss (Цена StopLoss ордера позиции) event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit()); // Цена TakeProfit (Цена TakeProfit ордера позиции) event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,::NormalizeDouble(volume_in,dgl));// Первоначальный объём event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,deal.Volume()); // Закрытый объём event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,volume_current); // Оставшийся (текущий) объём event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position); // Оставшийся (текущий) объём event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull()); // Профит event.SetProperty(EVENT_PROP_SYMBOL,deal.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; } } } } } //+------------------------------------------------------------------+
Метод достаточно объёмный, но все действия однотипны и описаны в комментариях в листинге метода. Думаю, что вопросов по коду метода быть не должно.
Практически аналогичную логику имеет и метод создания нового события для неттингового счёта:
//+------------------------------------------------------------------+ //| Создаёт событие для неттингового счёта | //+------------------------------------------------------------------+ void CEventsCollection::NewDealEventNetto(COrder *deal,CArrayObj *list_history,CArrayObj *list_market) { //--- Подготовка данных по истории позиции //--- Списки всех сделок и изменений направления позиции CArrayObj* list_deals=this.GetListAllDealsByPosID(list_history,deal.PositionID()); CArrayObj* list_changes=this.GetListAllDealsInOutByPosID(list_history,deal.PositionID()); if(list_deals==NULL || list_changes==NULL) return; list_deals.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); list_changes.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); if(!list_changes.InsertSort(list_deals.At(0))) return; //--- Ордера первой и последней сделок позиции CArrayObj* list_tmp=this.GetListAllOrdersByPosID(list_history,deal.PositionID()); COrder* order_first_deal=list_tmp.At(0); list_tmp=CSelect::ByOrderProperty(list_tmp,ORDER_PROP_TICKET,deal.GetProperty(ORDER_PROP_DEAL_ORDER_TICKET),EQUAL); COrder* order_last_deal=list_tmp.At(list_tmp.Total()-1); if(order_first_deal==NULL || order_last_deal==NULL) return; //--- Тип и тикеты ордеров первой и последней сделок позиции ENUM_ORDER_TYPE type_order_first_deal=(ENUM_ORDER_TYPE)order_first_deal.TypeOrder(); ENUM_ORDER_TYPE type_order_last_deal=(ENUM_ORDER_TYPE)order_last_deal.TypeOrder(); ulong ticket_order_first_deal=order_first_deal.Ticket(); ulong ticket_order_last_deal=order_last_deal.Ticket(); //--- Текущая и прошлая позиции COrder* position_current=list_changes.At(list_changes.Total()-1); COrder* position_previous=(list_changes.Total()>1 ? list_changes.At(list_changes.Total()-2) : position_current); if(position_current==NULL || position_previous==NULL) return; ENUM_ORDER_TYPE type_position_current=(ENUM_ORDER_TYPE)position_current.TypeOrder(); ulong ticket_position_current=position_current.GetProperty(ORDER_PROP_DEAL_ORDER_TICKET); ENUM_ORDER_TYPE type_position_previous=(ENUM_ORDER_TYPE)position_previous.TypeOrder(); ulong ticket_position_previous=position_previous.GetProperty(ORDER_PROP_DEAL_ORDER_TICKET); //--- Получим открытую позицию по тикету и запишем её объём COrder* position=this.GetPositionByID(list_market,deal.PositionID()); double vol_position=(position!=NULL ? position.Volume() : 0); //--- Исполненный объём ордера double vol_order_done=order_last_deal.Volume()-order_last_deal.VolumeCurrent(); //--- Оставшийся (неисполненный) объём ордера double vol_order_current=order_last_deal.VolumeCurrent(); //--- Вход в рынок if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_IN) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED; int num_deals=list_deals.Total(); int reason=(num_deals>1 ? EVENT_REASON_VOLUME_ADD : EVENT_REASON_DONE); //--- Если это не первая сделка в позиции, добавляем флаг изменения позиции if(num_deals>1) { this.m_trade_event_code+=TRADE_EVENT_FLAG_POSITION_CHANGED; } //--- Если не весь объём ордера открыт - значит частичное исполнение if(order_last_deal.VolumeCurrent()>0) { this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; //--- Если это не первая сделка позиции - значит добавление объёма частичным исполнением, иначе - частичное открытие reason=(num_deals>1 ? EVENT_REASON_VOLUME_ADD_PARTIALLY : EVENT_REASON_DONE_PARTIALLY); } //--- Если открывающий ордер - отложенный, значит - активирован отложенный ордер if(order_last_deal.TypeOrder()>ORDER_TYPE_SELL && order_last_deal.TypeOrder()<ORDER_TYPE_CLOSE_BY) { this.m_trade_event_code+=TRADE_EVENT_FLAG_ORDER_ACTIVATED; //--- Если это не первая сделка позиции if(num_deals>1) { //--- Если ордер исполнен частично, ставим в причину события добавление объёма к позиции частичным исполнением отложенного ордера //--- иначе - добавление объёма к позиции исполнением отложенного ордера reason= (order_last_deal.VolumeCurrent()>0 ? EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY : EVENT_REASON_VOLUME_ADD_BY_PENDING ); } //--- Если это новая позиция else { //--- Если ордер исполнен частично, ставим в причину события частичноем исполнение отложенного ордера, //--- иначе - открытие позиции активацией отложенного ордера reason= (order_last_deal.VolumeCurrent()>0 ? EVENT_REASON_ACTIVATED_PENDING_PARTIALLY : EVENT_REASON_ACTIVATED_PENDING ); } } CEvent* event=new CEventPositionOpen(this.m_trade_event_code,deal.PositionID()); if(event!=NULL) { //--- Параметры сделки события event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC()); // Время события (Время открытия позиции) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Причина события (из перечисления ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder()); // Тип сделки события event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket()); // Тикет сделки события event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic()); // Магический номер ордера/сделки/позиции event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen()); // Цена, на которой произошло событие (Цена открытия позиции) event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull()); // Профит event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol()); // Символ ордера event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,deal.Symbol()); // Символ встречной позиции event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID()); // Идентификатор позиции //--- Параметры ордера события event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,type_order_last_deal); // Тип ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,ticket_order_last_deal); // Тикет ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last_deal.PositionByID()); // Идентификатор встречной позиции event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last_deal.PriceClose()); // Цена закрытия ордера (Цена закрытия последнего ордера позиции) event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_last_deal.Volume()); // Запрашиваемый объём ордера event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,vol_order_done); // Исполненный объём ордера event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,vol_order_current); // Оставшийся (неисполненный) объём ордера event.SetProperty(EVENT_PROP_MAGIC_BY_ID,deal.Magic()); // Магический номер встречной позиции //--- Параметры позиции event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,type_order_first_deal); // Тип ордера, на основании которого открыта первая сделка позиции event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,ticket_order_first_deal); // Тикет ордера, на основании которого открыта первая сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first_deal.TimeOpenMSC()); // Время ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first_deal.PriceOpen()); // Цена открытия первого ордера позиции event.SetProperty(EVENT_PROP_PRICE_SL,order_first_deal.StopLoss()); // Цена StopLoss (Цена StopLoss ордера позиции) event.SetProperty(EVENT_PROP_PRICE_TP,order_first_deal.TakeProfit()); // Цена TakeProfit (Цена TakeProfit ордера позиции) event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,type_position_previous); // Тип позиции до смены направления event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,ticket_position_previous); // Тикет ордера позиции до смены направления event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,type_position_current); // Тип ордера текущей позиции event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,ticket_position_current); // Тикет ордера текущей позиции event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position); // Исполненный объём позиции //--- Установка идентификатора графика управляющей программы и расшифровка кода события, установка типа события 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; } } } //--- Разворот позиции else if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_INOUT) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED+TRADE_EVENT_FLAG_POSITION_CHANGED+TRADE_EVENT_FLAG_POSITION_REVERSE; int reason=EVENT_REASON_REVERSE; //--- Если не весь объём ордера открыт - значит частичное исполнение if(order_last_deal.VolumeCurrent()>0) { this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; reason=EVENT_REASON_REVERSE_PARTIALLY; } //--- Если открывающий ордер - отложенный, значит - активирован отложенный ордер if(order_last_deal.TypeOrder()>ORDER_TYPE_SELL && order_last_deal.TypeOrder()<ORDER_TYPE_CLOSE_BY) { this.m_trade_event_code+=TRADE_EVENT_FLAG_ORDER_ACTIVATED; //--- Если ордер исполнен частично, ставим в причину события разворот позиции частичным исполнением отложенного ордера reason= (order_last_deal.VolumeCurrent()>0 ? EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY : EVENT_REASON_REVERSE_BY_PENDING ); } CEvent* event=new CEventPositionOpen(this.m_trade_event_code,deal.PositionID()); if(event!=NULL) { //--- Параметры сделки события event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC()); // Время события (Время открытия позиции) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Причина события (из перечисления ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder()); // Тип сделки события event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket()); // Тикет сделки события event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic()); // Магический номер ордера/сделки/позиции event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen()); // Цена, на которой произошло событие (Цена открытия позиции) event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull()); // Профит event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol()); // Символ ордера event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,deal.Symbol()); // Символ встречной позиции event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID()); // Идентификатор позиции //--- Параметры ордера события event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,type_order_last_deal); // Тип ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,ticket_order_last_deal); // Тикет ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last_deal.PositionByID()); // Идентификатор встречной позиции event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last_deal.PriceClose()); // Цена закрытия ордера (Цена закрытия последнего ордера позиции) event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_last_deal.Volume()); // Запрашиваемый объём ордера event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,vol_order_done); // Исполненный объём ордера event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,vol_order_current); // Оставшийся (неисполненный) объём ордера event.SetProperty(EVENT_PROP_MAGIC_BY_ID,deal.Magic()); // Магический номер встречной позиции //--- Параметры позиции event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,type_order_first_deal); // Тип ордера, на основании которого открыта первая сделка позиции event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,ticket_order_first_deal); // Тикет ордера, на основании которого открыта первая сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first_deal.TimeOpenMSC()); // Время ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first_deal.PriceOpen()); // Цена открытия первого ордера позиции event.SetProperty(EVENT_PROP_PRICE_SL,order_first_deal.StopLoss()); // Цена StopLoss (Цена StopLoss ордера позиции) event.SetProperty(EVENT_PROP_PRICE_TP,order_first_deal.TakeProfit()); // Цена TakeProfit (Цена TakeProfit ордера позиции) event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,type_position_previous); // Тип ордера позиции до смены направления event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,ticket_position_previous); // Тикет ордера позиции до смены направления event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,type_position_current); // Тип ордера текущей позиции event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,ticket_position_current); // Тикет ордера текущей позиции event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position); // Исполненный объём позиции //--- Установка идентификатора графика управляющей программы и расшифровка кода события, установка типа события 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; } } } //--- Выход из рынка else if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED; int reason=EVENT_REASON_DONE; //--- Если позиция с идентификатором ещё в рынке - значит частичное исполнение if(this.GetPositionByID(list_market,deal.PositionID())!=NULL) { this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; } //--- Если закрывающий ордер исполнен частично, ставим в причину события частичное исполнение закрывающего ордера if(order_last_deal.VolumeCurrent()>0) { reason=EVENT_REASON_DONE_PARTIALLY; } //--- Если у закрывающего ордера позиции выставлен флаг закрытия по StopLoss - значит закрытие по StopLoss //--- Если StopLoss-ордер исполнен частично, ставим в причину события частичное исполнение ордера StopLoss if(order_last_deal.IsCloseByStopLoss()) { this.m_trade_event_code+=TRADE_EVENT_FLAG_SL; reason=(order_last_deal.VolumeCurrent()>0 ? EVENT_REASON_DONE_SL_PARTIALLY : EVENT_REASON_DONE_SL); } //--- Если у закрывающего ордера позиции выставлен флаг закрытия по TakeProfit - значит закрытие по TakeProfit //--- Если TakeProfit-ордер исполнен частично, ставим в причину события частичное исполнение ордера TakeProfit else if(order_last_deal.IsCloseByTakeProfit()) { this.m_trade_event_code+=TRADE_EVENT_FLAG_TP; reason=(order_last_deal.VolumeCurrent()>0 ? EVENT_REASON_DONE_TP_PARTIALLY : EVENT_REASON_DONE_TP); } //--- CEvent* event=new CEventPositionClose(this.m_trade_event_code,deal.PositionID()); if(event!=NULL) { //--- Параметры сделки события event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC()); // Время события (Время открытия позиции) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Причина события (из перечисления ENUM_EVENT_REASON) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder()); // Тип сделки события event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket()); // Тикет сделки события event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic()); // Магический номер ордера/сделки/позиции event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen()); // Цена, на которой произошло событие (Цена открытия позиции) event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull()); // Профит event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol()); // Символ ордера event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,deal.Symbol()); // Символ встречной позиции event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID()); // Идентификатор позиции //--- Параметры ордера события event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,type_order_last_deal); // Тип ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,ticket_order_last_deal); // Тикет ордера, на основании которого открыта сделка события (последний ордер позиции) event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last_deal.PositionByID()); // Идентификатор встречной позиции event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last_deal.PriceClose()); // Цена закрытия ордера (Цена закрытия последнего ордера позиции) event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_last_deal.Volume()); // Запрашиваемый объём ордера event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,vol_order_done); // Исполненный объём ордера event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,vol_order_current); // Оставшийся (неисполненный) объём ордера event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order_last_deal.Magic()); // Магический номер встречной позиции //--- Параметры позиции event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,type_order_first_deal); // Тип ордера, на основании которого открыта первая сделка позиции event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,ticket_order_first_deal); // Тикет ордера, на основании которого открыта первая сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first_deal.TimeOpenMSC()); // Время ордера, на основании которого открыта сделка позиции (первый ордер позиции) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first_deal.PriceOpen()); // Цена открытия первого ордера позиции event.SetProperty(EVENT_PROP_PRICE_SL,order_first_deal.StopLoss()); // Цена StopLoss (Цена StopLoss ордера позиции) event.SetProperty(EVENT_PROP_PRICE_TP,order_first_deal.TakeProfit()); // Цена TakeProfit (Цена TakeProfit ордера позиции) event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,type_position_previous); // Тип ордера позиции до смены направления event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,ticket_position_previous); // Тикет ордера позиции до смены направления event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,type_position_current); // Тип ордера текущей позиции event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,ticket_position_current); // Тикет ордера текущей позиции event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position); // Исполненный объём позиции //--- Установка идентификатора графика управляющей программы и расшифровка кода события, установка типа события 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; } } } } //+------------------------------------------------------------------+
Что хочется отметить по методам CreateNewEvent(), NewDealEventHedge() и NewDealEventNetto() — это то, что логика у них идентичная, действия — тоже. Это наталкивает на мысль об их объединении. Но пока мы сделали так (с учётом принципа "от простого к сложному"). Так что коды классов и их методов в дальнейшем будут оптимизированы, о чём я говорил уже в начале этого раздела статьи.
Изменения класса-коллекции событий для работы на хеджевых и неттинговых типах счетов завершили. С полным листингом класса можно будет
ознакомиться в прикреплённых в конце статьи файлах библиотеки в виду достаточно большого объёма кода.
Тест работы на хеджевом и неттинговом счёте
Для проверки внесённых изменений создадим тестовый советник на основе советника из прошлой
статьи.
Сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part06 под именем TestDoEasyPart06.mq5.
Удалим из обработчика OnInit() советника строки, проверяющие тип счёта:
int OnInit() { //--- Check account type if(!engine.IsHedge()) { Alert(TextByLanguage("Ошибка. Счёт должен быть хеджевым","Error. Account must be hedge")); return INIT_FAILED; } //--- set global variables
Вместо них я вписал вызов функции проверки корректности создания перечислений для поиска и сортировки по свойствам объектов:
int OnInit() { //--- Вызов данной функции выводит в журнал список констант перечисления, //--- заданного в файле DELib.mqh в строках 22 и 25, для проверки корректности констант //EnumNumbersTest(); //--- set global variables
Так как на неттинговом счёте для закрытия части позиции по символу требуется выставить противоположный направлению существующей позиции
ордер с объёмом, равным требуемой для частичного закрытия величиной, то нам необходимо в функции-обработчике событий нажатия кнопки
PressButtonEvents() внести небольшие правки.
Для закрытия части позиции Buy:
//--- Если нажата кнопка 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)); } } }
Проверяем тип счёта, и если это хедж, то закрываем
часть позиции,
иначе (если неттинг) — отправляем приказ на открытие
позиции Sell с объёмом, равным половине объёма текущей позиции Buy.
Для закрытия части позиции Sell:
//--- Если нажата кнопка 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)); } } }
Проверяем тип счёта, и если это хедж, то закрываем
часть позиции,
иначе (если неттинг) — отправляем приказ на открытие
позиции Buy с объёмом, равным половине объёма текущей позиции Sell.
Это все необходимые изменения, которые нужно сделать для работы советника на неттинговом счёте.
Полный листинг тестового советника:
//+------------------------------------------------------------------+ //| TestDoEasyPart06.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 }; #define TOTAL_BUTT (17) //--- 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 //--- 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; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Вызов данной функции выводит в журнал список констант перечисления, //--- заданного в файле DELib.mqh в строках 22 и 25, для проверки корректности констант //EnumNumbersTest(); //--- 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; //--- create buttons if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- 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(); 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); } } if(engine.LastTradeEvent()!=last_event) { Comment("\nLast trade event: ",EnumToString(engine.LastTradeEvent())); last_event=engine.LastTradeEvent(); } } //+------------------------------------------------------------------+ //| 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 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)+2*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-3) x=cx; y=(cy-(i-(i>6 ? 7 : 0))*(h+1)); if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-3 ? 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); } //+------------------------------------------------------------------+ //| Преобразует перечисление в текст кнопки | //+------------------------------------------------------------------+ string EnumToButtText(const ENUM_BUTTONS member) { string txt=StringSubstr(EnumToString(member),5); StringToLower(txt); 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); } } //--- Подождём 1/10 секунды Sleep(100); //--- "Отожмём" кнопку и перерисуем чарт ButtonState(button_name,false); ChartRedraw(); } } //+------------------------------------------------------------------+
Скомпилируем советник, запустим на хеджевом счёте и понажимаем на кнопки:
В журнал выводятся краткие сообщения о событиях, происходящих на счёте, а в комментарии на графике — описание последнего события, произошедшего на счёте.
Теперь переключимся на неттинговый счёт и запустим тест:
Здесь уже в журнал выводятся записи о событиях, происходящих с позицией, возможных только на неттинговом счёте — новых позиций не
открывается, а работа идёт лишь с одной. Но тикеты ей присваиваются разные — это видно в начале после переворота позиции с Sell #2 на Buy #3.
Что дальше
Далее мы сделаем отслеживание срабатывания StopLimit-ордеров и подготовим функционал для отслеживания модификации ордеров и позиций.
Ниже прикреплены все файлы текущей версии библиотеки и файлы тестового советника. Их можно скачать и протестировать всё
самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.
Статьи этой серии:
Часть 1. Концепция, организация данных.
Часть
2. Коллекция исторических ордеров и сделок.
Часть 3. Коллекция рыночных ордеров и
позиций, организация поиска.
Часть 4. Торговые события. Концепция.
Часть 5. Классы и коллекция торговых событий. Отправка событий в программу.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования