Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXXI): Отложенные торговые запросы - открытие позиций по условиям
Содержание
- Концепция
- Подготовка данных
- Объект-отложенный запрос, создаваемый по запросу
- Тестирование
- Что дальше
Концепция
При проектировании функционала библиотеки была запланирована концепция торговли при помощи отложенных запросов, в которую входят два варианта работы — обработка ошибок торгового сервера и обычная отсылка торговых приказов по программно заданным условиям. В предыдущих статьях, начиная со статьи 26, мы шаг за шагом создали обработку ошибок торгового сервера при помощи отложенных запросов, которые позволяют обрабатывать повторение отсылки торговых приказов в случаях, когда для обработки ошибки требуется повторная отсылка приказа на сервер после исправления ошибочных параметров и некоторого ожидания.
Начиная с этой статьи, мы создадим функционал, позволяющий производить торговлю при помощи отложенных запросов по условию.
Вкратце, что
это даст? Данный функционал библиотеки позволит пользователю самостоятельно программно создавать условия, при выполнении которых
будет отсылаться торговый приказ на сервер.
Как пример:
- При наступлении или превышении некоего времени, и при условии что цена стала ниже заданного значения, открыть позицию Buy (два
условия по значениям свойств символа).
- При превышении заданного размера прибыли закрыть часть позиции (одно условие по значению свойства аккаунта).
- При регистрации события закрытия позиции по стоплосс, открыть противоположную позицию (одно условие по значению свойства событий
счёта).
Это всего три простых примера. Но условий и их комбинаций может быть достаточно много. На данном этапе мы будем разрабатывать контроль
изменения свойств аккаунта, символа и событий, происходящих на текущем счёте. Условия из этих трёх списков можно будет задавать в любых их
комбинациях.
И начнём с простого — с контроля изменений значений свойств символа и аккаунта. Контролем событий счёта и реакцией на них займёмся
позже.
Чтобы объект-отложенный запрос мог работать как часть торговой логики (отсылка торговых приказов по условию), нам нужно добавить в этот объект дополнительные данные для хранения условий активации отложенного запроса и методы их контроля и обработки. Хранилищем таких данных у нас будет двумерный массив, в котором в первом измерении будет храниться номер условия (условий может быть сколь угодно), а во втором измерении — все данные условия, номер которого указан в первом измерении — тип источника условия (символ, аккаунт или событие), само условие (создадим перечисления для каждого из источников), метод сравнения (>,<,==,!=,>=,<=), контрольное значение отслеживаемого свойства и текущее его значение.
Контроль условий, заданных в объектах-отложенных запросах, будет производиться в таймере класса управления отложенными запросами, и из него же будут отсылаться "дождавшиеся своего часа" отложенные запросы на сервер — сразу после регистрации факта исполнения всех условий, прописанных в объекте-отложенном запросе.
Сегодня в рамках данной статьи мы создадим и проверим торговлю при помощи отложенных запросов — открытие позиций по условию. Отслеживаемых
условий в тестовом советнике будет всего два — по значению цены и по значению времени. Условия можно будет задавать как отдельно (либо по
значению цены, либо по значению времени), так и совместно (по значению цены и времени).
Подготовка данных
Начнём как обычно с добавления индексов новых сообщений библиотеки и текстов сообщений, соответствующих индексам.
В файле Datas.mqh впишем все необходимые индексы сообщений:
//--- CEvent MSG_EVN_EVENT, // Событие MSG_EVN_TYPE, // Тип события
...
//--- CAccount MSG_ACC_ACCOUNT, // Аккаунт MSG_ACC_PROP_LOGIN, // Номер счёта
...
MSG_LIB_TEXT_REQUEST, // Отложенный запрос # MSG_LIB_TEXT_REQUEST_ACTIVATED, // Активирован отложенный запрос: # MSG_LIB_TEXT_REQUEST_DATAS, // Параметры торгового запроса MSG_LIB_TEXT_PEND_REQUEST_DATAS, // Параметры отложенного торгового запроса MSG_LIB_TEXT_PEND_REQUEST_CREATED, // Создан отложенный запрос MSG_LIB_TEXT_PEND_REQUEST_DELETED, // Удалён в связи с окончанием времени его действия MSG_LIB_TEXT_PEND_REQUEST_EXECUTED, // Удалён в связи с его исполнением MSG_LIB_TEXT_PEND_REQUEST_GETTING_FAILED, // Не удалось получить объект-отложенный запрос из списка MSG_LIB_TEXT_PEND_REQUEST_FAILED_ADD_PARAMS, // Не удалось добавить параметры активации запроса. Ошибка: MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE, // Цена в момент создания запроса
...
MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_EXPIRATION, // Фактическое время жизни ордера MSG_LIB_TEXT_PEND_REQUEST_NO_FREE_IDS, // Нет свободных идентификаторов для создания отложенного запроса MSG_LIB_TEXT_PEND_REQUEST_ACTIVATION_TERMS, // Условия активации MSG_LIB_TEXT_PEND_REQUEST_CRITERION, // Критерий MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS, // Добавлены условия активации отложенного запроса }; //+------------------------------------------------------------------+
и тексты сообщений, соответствующие вновь добавленным индексам:
//--- CEvent {"Событие","Event"}, {"Тип события","Event's type"},
...
//--- CAccount {"Аккаунт","Account"}, {"Номер счёта","Account number"},
...
{"Отложенный запрос #","Pending request #"}, {"Активирован отложенный запрос: #","Pending request activated: #"}, {"Параметры торгового запроса","Trade request parameters"}, {"Параметры отложенного торгового запроса","Pending trade request parameters"}, {"Создан отложенный запрос","Pending request created"}, {"Удалён в связи с окончанием времени его действия","Deleted due to expiration"}, {"Удалён в связи с его исполнением","Deleted due completed"}, {"Не удалось получить объект-отложенный запрос из списка","Failed to get pending request object from list"}, {"Не удалось добавить параметры активации запроса. Ошибка: ","Failed to add request activation parameters. Error: "}, {"Цена в момент создания запроса","Price at time of request create"},
...
{"Фактическое время жизни ордера","Actual of order lifetime"}, {"Нет свободных идентификаторов для создания отложенного запроса","No free IDs to create a pending request"}, {"Условия активации","Activation terms"}, {"Критерий","Criterion"}, {"Добавлены условия активации отложенного запроса","Pending request activation conditions added"}, }; //+---------------------------------------------------------------------+
Так как одним объектом-отложенным запросом будут обрабатываться контролируемые условия из совершенно разных источников (в данном случае — аккаунт, символ и события счёта, а дальше можем и ещё что-либо добавить), то для отслеживания срабатывания заданного условия активации отложенного запроса, нам нужно указывать источник данных, параметры которого мы отслеживаем. Ведь при отслеживании параметров аккаунта и символа, у них могут совпадать индексы свойств, но это будут совсем разные свойства. Чтобы не было никакой путаницы, мы и будем указывать источник данных, в котором отслеживаются значения его свойств.
В файле Defines.mqh напишем перечисление источников активации отложенных запросов:
//+------------------------------------------------------------------+ //| Тип отложенного запроса | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_TYPE { PEND_REQ_TYPE_ERROR=PENDING_REQUEST_ID_TYPE_ERR, // Отложенный запрос, созданный по коду возврата или ошибке PEND_REQ_TYPE_REQUEST=PENDING_REQUEST_ID_TYPE_REQ, // Отложенный запрос, созданный по запросу }; //+------------------------------------------------------------------+ //| Источник активации отложенного запроса | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_ACTIVATION_SOURCE { PEND_REQ_ACTIVATION_SOURCE_ACCOUNT, // Отложенный запрос активируется данными аккаунта PEND_REQ_ACTIVATION_SOURCE_SYMBOL, // Отложенный запрос активируется данными символа PEND_REQ_ACTIVATION_SOURCE_EVENT, // Отложенный запрос активируется данными торгового события }; //+------------------------------------------------------------------+ //| Целочисленные свойства отложенного торгового запроса | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_PROP_INTEGER {
И там же добавим перечисления возможных критериев, по которым будут активироваться отложенные запросы.
Для критериев активации по свойствам
аккаунта, символа
и событий будем использовать раздельные перечисления:
//+------------------------------------------------------------------+ //| Возможные критерии активизации запросов по свойствам аккаунта | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_ACTIVATE_BY_ACCOUNT_PROP { //--- long PEND_REQ_ACTIVATE_BY_ACCOUNT_EMPTY = MSG_LIB_PROP_NOT_SET, // Значение не задано PEND_REQ_ACTIVATE_BY_ACCOUNT_LEVERAGE = MSG_ACC_PROP_LEVERAGE, // Активация по размеру предоставленного плеча PEND_REQ_ACTIVATE_BY_ACCOUNT_LIMIT_ORDERS = MSG_ACC_PROP_LIMIT_ORDERS, // Активация по максимально допустимому количество действующих отложенных ордеров PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_ALLOWED = MSG_ACC_PROP_TRADE_ALLOWED, // Активация по разрешенности торговли для текущего счета со стороны сервера PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_EXPERT = MSG_ACC_PROP_TRADE_EXPERT, // Активация по разрешенности торговли для эксперта со стороны сервера //--- double PEND_REQ_ACTIVATE_BY_ACCOUNT_BALANCE = MSG_ACC_PROP_BALANCE, // Активация по балансу счета в валюте депозита PEND_REQ_ACTIVATE_BY_ACCOUNT_CREDIT = MSG_ACC_PROP_CREDIT, // Активация по размеру предоставленного кредита в валюте депозита PEND_REQ_ACTIVATE_BY_ACCOUNT_PROFIT = MSG_ACC_PROP_PROFIT, // Активация по размеру текущей прибыли на счете в валюте депозита PEND_REQ_ACTIVATE_BY_ACCOUNT_EQUITY = MSG_ACC_PROP_EQUITY, // Активация по значению собственных средств на счете в валюте депозита PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN = MSG_ACC_PROP_MARGIN, // Активация по размеру зарезервированных залоговых средств на счете в валюте депозита PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_FREE = MSG_ACC_PROP_MARGIN_FREE, // Активация по размеру свободных средств на счете в валюте депозита, доступных для открытия позиции PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_LEVEL = MSG_ACC_PROP_MARGIN_LEVEL, // Активация по уровню залоговых средств на счете в процентах PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_INITIAL = MSG_ACC_PROP_MARGIN_INITIAL, // Активация по размеру средств, зарезервированных на счёте, для обеспечения гарантийной суммы по всем отложенным ордерам PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_MAINTENANCE = MSG_ACC_PROP_MARGIN_MAINTENANCE, // Активация по размеру средств, зарезервированных на счёте, для обеспечения минимальной суммы по всем открытым позициям PEND_REQ_ACTIVATE_BY_ACCOUNT_ASSETS = MSG_ACC_PROP_ASSETS, // Активация по текущему размеру активов на счёте PEND_REQ_ACTIVATE_BY_ACCOUNT_LIABILITIES = MSG_ACC_PROP_LIABILITIES, // Активация по текущему размеру обязательств на счёте PEND_REQ_ACTIVATE_BY_ACCOUNT_COMMISSION_BLOCKED = MSG_ACC_PROP_COMMISSION_BLOCKED // Активация по текущей сумме заблокированных комиссий по счёту }; //+------------------------------------------------------------------+ //| Возможные критерии активизации запросов по свойствам символа | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_ACTIVATE_BY_SYMBOL_PROP { PEND_REQ_ACTIVATE_BY_SYMBOL_EMPTY = MSG_LIB_PROP_NOT_SET, // Значение не задано //--- double PEND_REQ_ACTIVATE_BY_SYMBOL_BID = MSG_LIB_PROP_BID, // Активация по Bid - лучшее предложение на продажу PEND_REQ_ACTIVATE_BY_SYMBOL_ASK = MSG_LIB_PROP_ASK, // Активация по Ask - лучшее предложение на покупку PEND_REQ_ACTIVATE_BY_SYMBOL_LAST = MSG_LIB_PROP_LAST, // Активация по цене, по которой совершена последняя сделка //--- long PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_DEALS = MSG_SYM_PROP_SESSION_DEALS, // Активация по количеству сделок в текущей сессии PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS = MSG_SYM_PROP_SESSION_BUY_ORDERS, // Активация по общему числу ордеров на покупку в текущий момент PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS = MSG_SYM_PROP_SESSION_SELL_ORDERS, // Активация по общему числу ордеров на продажу в текущий момент PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME = MSG_SYM_PROP_VOLUME, // Активация по объему в последней сделке PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH = MSG_SYM_PROP_VOLUMEHIGH, // Активация по максимальному Volume за день PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW = MSG_SYM_PROP_VOLUMELOW, // Активация по минимальному Volume за день PEND_REQ_ACTIVATE_BY_SYMBOL_TIME = MSG_SYM_PROP_TIME, // Активация по времени последней котировки PEND_REQ_ACTIVATE_BY_SYMBOL_SPREAD = MSG_SYM_PROP_SPREAD, // Активация по размеру спреда в пунктах PEND_REQ_ACTIVATE_BY_SYMBOL_START_TIME = MSG_SYM_PROP_START_TIME, // Активация по дате начала торгов по инструменту (обычно используется для фьючерсов) PEND_REQ_ACTIVATE_BY_SYMBOL_EXPIRATION_TIME = MSG_SYM_PROP_EXPIRATION_TIME, // Активация по дате окончания торгов по инструменту (обычно используется для фьючерсов) PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_STOPS_LEVEL = MSG_SYM_PROP_TRADE_STOPS_LEVEL, // Активация по минимальному отступу в пунктах от текущей цены закрытия для установки Stop ордеров PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FREEZE_LEVEL = MSG_SYM_PROP_TRADE_FREEZE_LEVEL, // Активация по дистанции заморозки торговых операций (в пунктах) //--- double PEND_REQ_ACTIVATE_BY_SYMBOL_BIDHIGH = MSG_SYM_PROP_BIDHIGH, // Активация по максимальному Bid за день PEND_REQ_ACTIVATE_BY_SYMBOL_BIDLOW = MSG_SYM_PROP_BIDLOW, // Активация по минимальному Bid за день PEND_REQ_ACTIVATE_BY_SYMBOL_ASKHIGH = MSG_SYM_PROP_ASKHIGH, // Активация по максимальному Ask за день PEND_REQ_ACTIVATE_BY_SYMBOL_ASKLOW = MSG_SYM_PROP_ASKLOW, // Активация по минимальному Ask за день PEND_REQ_ACTIVATE_BY_SYMBOL_LASTHIGH = MSG_SYM_PROP_LASTHIGH, // Активация по максимальному Last за день PEND_REQ_ACTIVATE_BY_SYMBOL_LASTLOW = MSG_SYM_PROP_LASTLOW, // Активация по минимальному Last за день PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME_REAL = MSG_SYM_PROP_VOLUME_REAL, // Активация по Volume за день PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH_REAL = MSG_SYM_PROP_VOLUMEHIGH_REAL, // Активация по максимальному Volume за день PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW_REAL = MSG_SYM_PROP_VOLUMELOW_REAL, // Активация по минимальному Volume за день PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE = MSG_SYM_PROP_OPTION_STRIKE, // Активация по цене исполнения опциона PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_ACCRUED_INTEREST = MSG_SYM_PROP_TRADE_ACCRUED_INTEREST, // Активация по накопленному купонному доходу PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FACE_VALUE = MSG_SYM_PROP_TRADE_FACE_VALUE, // Активация по номинальной стоимости – начальная стоимость облигации, установленная эмитентом PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_LIQUIDITY_RATE = MSG_SYM_PROP_TRADE_LIQUIDITY_RATE, // Активация по коэффициенту ликвидности – доля от стоимости актива, которую можно использовать в качестве залога PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_LONG = MSG_SYM_PROP_SWAP_LONG, // Активация по значению свопа в покупку PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_SHORT = MSG_SYM_PROP_SWAP_SHORT, // Активация по значению свопа в продажу PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_VOLUME = MSG_SYM_PROP_SESSION_VOLUME, // Активация по суммарному объёму сделок в текущую сессию PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_TURNOVER = MSG_SYM_PROP_SESSION_TURNOVER, // Активация по суммарному обороту в текущую сессию PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_INTEREST = MSG_SYM_PROP_SESSION_INTEREST, // Активация по суммарному объёму открытых позиций PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME = MSG_SYM_PROP_SESSION_BUY_ORDERS_VOLUME, // Активация по общему объёму ордеров на покупку в текущий момент PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME= MSG_SYM_PROP_SESSION_SELL_ORDERS_VOLUME, // Активация по общему объёму ордеров на продажу в текущий момент PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_OPEN = MSG_SYM_PROP_SESSION_OPEN, // Активация по цене открытия сессии PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_CLOSE = MSG_SYM_PROP_SESSION_CLOSE, // Активация по цене закрытия сессии PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_AW = MSG_SYM_PROP_SESSION_AW, // Активация по средневзвешенной цене сессии PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_SETTLEMENT = MSG_SYM_PROP_SESSION_PRICE_SETTLEMENT, // Активация по цене поставки на текущую сессию PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN = MSG_SYM_PROP_SESSION_PRICE_LIMIT_MIN, // Активация по минимально допустимому значению цены на сессию PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX = MSG_SYM_PROP_SESSION_PRICE_LIMIT_MAX, // Активация по максимально допустимому значению цены на сессию }; //+------------------------------------------------------------------+ //| Возможные критерии активизации запросов по событиям | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_ACTIVATE_BY_EVENT { PEND_REQ_ACTIVATE_BY_EVENT_EMPTY = MSG_LIB_PROP_NOT_SET, // Значение не задано PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED = MSG_EVN_STATUS_MARKET_POSITION, // Позиция открыта PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED = MSG_EVN_STATUS_HISTORY_POSITION, // Позиция закрыта PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_PLASED = MSG_EVN_PENDING_ORDER_PLASED, // Отложенный ордер установлен PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_REMOVED = MSG_EVN_PENDING_ORDER_REMOVED, // Отложенный ордер удалён PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CREDIT = MSG_EVN_ACCOUNT_CREDIT, // Начисление кредита (3) PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CHARGE = MSG_EVN_ACCOUNT_CHARGE, // Дополнительные сборы PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CORRECTION = MSG_EVN_ACCOUNT_CORRECTION, // Корректирующая запись PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BONUS = MSG_EVN_ACCOUNT_BONUS, // Перечисление бонусов PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION = MSG_EVN_ACCOUNT_COMISSION, // Дополнительные комиссии PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_DAILY = MSG_EVN_ACCOUNT_COMISSION_DAILY, // Комиссия, начисляемая в конце торгового дня PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_MONTHLY = MSG_EVN_ACCOUNT_COMISSION_MONTHLY, // Комиссия, начисляемая в конце месяца PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_DAILY = MSG_EVN_ACCOUNT_COMISSION_AGENT_DAILY, // Агентская комиссия, начисляемая в конце торгового дня PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY = MSG_EVN_ACCOUNT_COMISSION_AGENT_MONTHLY, // Агентская комиссия, начисляемая в конце месяца PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_INTEREST = MSG_EVN_ACCOUNT_INTEREST, // Начисления процентов на свободные средства PEND_REQ_ACTIVATE_BY_EVENT_BUY_CANCELLED = MSG_EVN_BUY_CANCELLED, // Отмененная сделка покупки PEND_REQ_ACTIVATE_BY_EVENT_SELL_CANCELLED = MSG_EVN_SELL_CANCELLED, // Отмененная сделка продажи PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT = MSG_EVN_DIVIDENT, // Начисление дивиденда PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT_FRANKED = MSG_EVN_DIVIDENT_FRANKED, // Начисление франкированного дивиденда PEND_REQ_ACTIVATE_BY_EVENT_TAX = MSG_EVN_TAX, // Начисление налога PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_REFILL = MSG_EVN_BALANCE_REFILL, // Пополнение средств на балансе PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_WITHDRAWAL = MSG_EVN_BALANCE_WITHDRAWAL, // Снятие средств с баланса PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED = MSG_EVN_ACTIVATED_PENDING, // Отложенный ордер активирован ценой PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL = MSG_EVN_ACTIVATED_PENDING_PARTIALLY, // Отложенный ордер активирован ценой частично PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED_PARTIAL = MSG_EVN_POSITION_OPENED_PARTIALLY, // Позиция открыта частично PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL = MSG_EVN_POSITION_CLOSED_PARTIALLY, // Позиция закрыта частично PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_POS = MSG_EVN_POSITION_CLOSED_BY_POS, // Позиция закрыта встречной PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_POS = MSG_EVN_POSITION_CLOSED_PARTIALLY_BY_POS, // Позиция закрыта частично встречной PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_SL = MSG_EVN_POSITION_CLOSED_BY_SL, // Позиция закрыта по StopLoss PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_TP = MSG_EVN_POSITION_CLOSED_BY_TP, // Позиция закрыта по TakeProfit PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_SL = MSG_EVN_POSITION_CLOSED_PARTIALLY_BY_SL, // Позиция закрыта частично по StopLoss PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_TP = MSG_EVN_POSITION_CLOSED_PARTIALLY_BY_TP, // Позиция закрыта частично по TakeProfit PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET = MSG_EVN_POSITION_REVERSED_BY_MARKET, // Разворот позиции новой сделкой (неттинг) PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING = MSG_EVN_POSITION_REVERSED_BY_PENDING, // Разворот позиции активацией отложенного ордера (неттинг) PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL = MSG_EVN_POSITION_REVERSE_PARTIALLY, // Разворот позиции частичным исполнением маркет-ордера (неттинг) PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_MARKET = MSG_EVN_POSITION_VOLUME_ADD_BY_MARKET, // Добавлен объём к позиции новой сделкой (неттинг) PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_PENDING = MSG_EVN_POSITION_VOLUME_ADD_BY_PENDING, // Добавлен объём к позиции активацией отложенного ордера (неттинг) PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE = MSG_EVN_MODIFY_ORDER_PRICE, // Изменение цены установки ордера PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL = MSG_EVN_MODIFY_ORDER_PRICE_SL, // Изменение цены установки ордера и StopLoss PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_TP = MSG_EVN_MODIFY_ORDER_PRICE_TP, // Изменение цены установки ордера и TakeProfit PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL_TP = MSG_EVN_MODIFY_ORDER_PRICE_SL_TP, // Изменение цены установки ордера, StopLoss и TakeProfit PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL_TP = MSG_EVN_MODIFY_ORDER_SL_TP, // Изменение StopLoss и TakeProfit ордера PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL = MSG_EVN_MODIFY_ORDER_SL, // Изменение StopLoss ордера PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_TP = MSG_EVN_MODIFY_ORDER_TP, // Изменение TakeProfit ордера PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL_TP = MSG_EVN_MODIFY_POSITION_SL_TP, // Изменение StopLoss и TakeProfit позиции PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL = MSG_EVN_MODIFY_POSITION_SL, // Изменение StopLoss позиции PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_TP = MSG_EVN_MODIFY_POSITION_TP, // Изменение TakeProfit позиции PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_MARKET_PARTIAL = MSG_EVN_REASON_ADD_PARTIALLY, // Добавлен объём к позиции частичным исполнением маркет-ордера (неттинг) PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_PENDING_PARTIAL = MSG_EVN_REASON_ADD_BY_PENDING_PARTIALLY, // Добавлен объём к позиции частичной активацией отложенного ордера (неттинг) PEND_REQ_ACTIVATE_BY_EVENT_TRIGGERED_STOP_LIMIT_ORDER = MSG_EVN_REASON_STOPLIMIT_TRIGGERED, // Срабатывание StopLimit ордера PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL= MSG_EVN_REASON_REVERSE_BY_PENDING_PARTIALLY, // Разворот позиции частичной активацией отложенного ордера (неттинг) }; //+------------------------------------------------------------------+
Значения констант перечислений приравнены к значениям констант текстовых сообщений соответствующих свойств символа, аккаунта и события. Это
даст нам возможность при выводе сообщений в журнал в методах описания условий активации запроса не идентифицировать дополнительно
принадлежность описываемой константы к символу, аккаунту или событию, а просто использовать индекс самой константы для вывода
сообщения.
Используя три разных перечисления условий активации в итоге мы сможем задать любое сочетание констант из всех трёх перечислений для составления требуемого критерия активации отложенного запроса.
В файл сервисных функций DELib.mqh впишем функцию, возвращающую описание типа сравнения:
//+------------------------------------------------------------------+ //| Возвращает описание типа сравнения | //+------------------------------------------------------------------+ string ComparisonTypeDescription(const ENUM_COMPARER_TYPE type) { switch((int)type) { case EQUAL : return " == "; case MORE : return " > "; case LESS : return " < "; case EQUAL_OR_MORE : return " >= "; case EQUAL_OR_LESS : return " <= "; default : return " != "; } } //+------------------------------------------------------------------+
Во многих файлах библиотеки были изменены наименования констант перечислений, в которых были прописаны строки "STOP_LOSS" и
"TAKE_PROFIT". Теперь вхождения этих строк заменены на "SL" и "TP", соответственно.
Объект-отложенный запрос, создаваемый по запросу
Базовый объект абстрактного отложенного запроса теперь будет
унаследован
от базового объекта всех объектов библиотеки.
Подключим
файл базового объекта всех объектов библиотеки к файлу класса CPendRequest и сделаем
класс наследником базового объекта:
//+------------------------------------------------------------------+ //| PendRequest.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, 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 "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| Класс абстрактного отложенного торгового запроса | //+------------------------------------------------------------------+ class CPendRequest : public CBaseObj {
В приватной секции класса объявим массив для хранения данных по отслеживаемым свойствам критериев активации отложенного запроса:
//+------------------------------------------------------------------+ //| Класс абстрактного отложенного торгового запроса | //+------------------------------------------------------------------+ class CPendRequest : public CBaseObj { private: MqlTradeRequest m_request; // Структура торгового запроса CPause m_pause; // Объект класса "Пауза" /* Данные активации отложенного запроса в массиве: В первом измерении находится номер критерия активации Во втором измерении находятся: m_activated_control[criterion number][0] - источник контролируемого свойства m_activated_control[criterion number][1] - контролируемое свойство m_activated_control[criterion number][2] - тип сравнения контролируемого свойства с фактической величиной (=,>,<,!=,>=,<=) m_activated_control[criterion number][3] - контрольное значение величины свойства для активации m_activated_control[criterion number][4] - фактическая величина свойства */ double m_activated_control[][5]; // Массив контрольных значений критериев активации отложенного запроса //--- Копирует данные торгового запроса void CopyRequest(const MqlTradeRequest &request);
И там же — в приватной секции — напишем методы, возвращающие магик,
идентификатор магика, заданный в настройках советника, и идентификаторы
первой и второй групп.
А также объявим метод для возврата
флага успешности проверки контролируемого свойства с его фактическим значением,
метод
сравнения двух значений контролируемого свойства, и метод, возвращающий
количество знаков после запятой у отслеживаемого свойства для корректного вывода значений в журнал:
//--- Возвращает (1) магический номер, идентификатор (2) магика, (3) первой, (4) второй группы, //--- (5) флаг счёта с типом "хедж", (6) флаг равенства вещественного свойства значению ulong GetMagic(void) const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC); } ushort GetMagicID(void) const { return CBaseObj::GetMagicID((uint)this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC)); } uchar GetGroupID1(void) const { return CBaseObj::GetGroupID1((uint)this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC));} uchar GetGroupID2(void) const { return CBaseObj::GetGroupID2((uint)this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC));} bool IsHedge(void) const { return this.m_is_hedge; } bool IsEqualByMode(const int mode,const double value) const; bool IsEqualByMode(const int mode,const long value) const; //--- Возвращает флаги завершения работы отложенного запроса по изменению каждого из параметров ордера/позиции bool IsCompletedVolume(void) const; bool IsCompletedPrice(void) const; bool IsCompletedStopLimit(void) const; bool IsCompletedStopLoss(void) const; bool IsCompletedTakeProfit(void) const; bool IsCompletedTypeFilling(void) const; bool IsCompletedTypeTime(void) const; bool IsCompletedExpiration(void) const; //--- Возвращает флаг успешности проверки контролируемого свойства объекта и соответствующего ему фактического свойства bool IsComparisonCompleted(const uint index) const; //--- Сравнивает два значения источника данных по типу сравнения bool IsCompared(const double actual_value,const double control_value,const ENUM_COMPARER_TYPE compare) const; //--- Возвращает количество знаков после запятой контролируемого свойства int DigitsControlledValue(const uint index) const; public:
Методы возврата идентификаторов магика и
групп используют одноимённые методы родительского объекта CBaseObj, от которого мы теперь унаследовали объект базового
абстрактного отложенного запроса.
В публичной секции класса в блоке методов для упрощённого доступа к свойствам объекта-запроса добавим объявления всех необходимых публичных методов, которые рассмотрим дальше:
//+------------------------------------------------------------------+ //| Методы упрощённого доступа к свойствам объекта-запроса | //+------------------------------------------------------------------+ //--- Возвращает (1) структуру запроса, (2) статус, (3) тип запроса, (4) цену при создании запроса, //--- (5) время создания запроса, (6) время активации очередной попытки, //--- (7) продолжительность ожидания между запросами, (8) номер текущей попытки, //--- (9) количество попыток, (10) идентификатор запроса //--- (11) результат, на основании которого создан запрос, //--- (12) тикет ордера, (13) тикет позиции, (14) тип торговой операции MqlTradeRequest MqlRequest(void) const { return this.m_request; } ENUM_PEND_REQ_STATUS Status(void) const { return (ENUM_PEND_REQ_STATUS)this.GetProperty(PEND_REQ_PROP_STATUS); } ENUM_PEND_REQ_TYPE TypeRequest(void) const { return (ENUM_PEND_REQ_TYPE)this.GetProperty(PEND_REQ_PROP_TYPE); } double PriceCreate(void) const { return this.GetProperty(PEND_REQ_PROP_PRICE_CREATE); } ulong TimeCreate(void) const { return this.GetProperty(PEND_REQ_PROP_TIME_CREATE); } ulong TimeActivate(void) const { return this.GetProperty(PEND_REQ_PROP_TIME_ACTIVATE); } ulong WaitingMSC(void) const { return this.GetProperty(PEND_REQ_PROP_WAITING); } uchar CurrentAttempt(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_CURRENT_ATTEMPT); } uchar TotalAttempts(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_TOTAL); } uchar ID(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_ID); } int Retcode(void) const { return (int)this.GetProperty(PEND_REQ_PROP_RETCODE); } ulong Order(void) const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_ORDER); } ulong Position(void) const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_POSITION); } ENUM_TRADE_REQUEST_ACTIONS Action(void) const { return (ENUM_TRADE_REQUEST_ACTIONS)this.GetProperty(PEND_REQ_PROP_MQL_REQ_ACTION); } //--- Возвращает фактический (1) объём, (2) цену установки ордера, (3) цену установки limit-ордера, //--- (4) цену установки stoploss-ордера, (5) цену установки takeprofit-ордера, (6) тип заливки ордера, //--- (7) тип экспирации ордера, (8) время жизни ордера double ActualVolume(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_VOLUME); } double ActualPrice(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_PRICE); } double ActualStopLimit(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT); } double ActualSL(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_SL); } double ActualTP(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_TP); } ENUM_ORDER_TYPE_FILLING ActualTypeFilling(void) const { return (ENUM_ORDER_TYPE_FILLING)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING); } ENUM_ORDER_TYPE_TIME ActualTypeTime(void) const { return (ENUM_ORDER_TYPE_TIME)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME); } datetime ActualExpiration(void) const { return (datetime)this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION); } //--- Устанавливает (1) цену при создании запроса, (2) время создания запроса, //--- (3) время текущей попытки, (4) продолжительность ожидания между запросами, //--- (5) номер текущей попытки, (6) количество попыток, (7) идентификатор, //--- (8) тикет ордера, (9) тикет позиции, (10) тип отложенного запроса void SetPriceCreate(const double price) { this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price); } void SetTimeCreate(const ulong time) { this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time); this.m_pause.SetTimeBegin(time); } void SetTimeActivate(const ulong time) { this.SetProperty(PEND_REQ_PROP_TIME_ACTIVATE,time); } void SetWaitingMSC(const ulong miliseconds) { this.SetProperty(PEND_REQ_PROP_WAITING,miliseconds); this.m_pause.SetWaitingMSC(miliseconds); } void SetCurrentAttempt(const uchar number) { this.SetProperty(PEND_REQ_PROP_CURRENT_ATTEMPT,number); } void SetTotalAttempts(const uchar number) { this.SetProperty(PEND_REQ_PROP_TOTAL,number); } void SetID(const uchar id) { this.SetProperty(PEND_REQ_PROP_ID,id); } void SetOrder(const ulong ticket) { this.SetProperty(PEND_REQ_PROP_MQL_REQ_ORDER,ticket); } void SetPosition(const ulong ticket) { this.SetProperty(PEND_REQ_PROP_MQL_REQ_POSITION,ticket); } void SetTypeRequest(const ENUM_PEND_REQ_TYPE type) { this.SetProperty(PEND_REQ_PROP_TYPE,type); } //--- Устанавливает фактический (1) объём, (2) цену установки ордера, (3) цену установки limit-ордера, //--- (4) цену установки stoploss-ордера, (5) цену установки takeprofit-ордера, (6) тип заливки ордера, //--- (7) тип экспирации ордера, (8) время жизни ордера void SetActualVolume(const double volume) { this.SetProperty(PEND_REQ_PROP_ACTUAL_VOLUME,volume); } void SetActualPrice(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_PRICE,price); } void SetActualStopLimit(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT,price); } void SetActualSL(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_SL,price); } void SetActualTP(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TP,price); } void SetActualTypeFilling(const ENUM_ORDER_TYPE_FILLING type) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING,type); } void SetActualTypeTime(const ENUM_ORDER_TYPE_TIME type) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME,type); } void SetActualExpiration(const datetime expiration) { this.SetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION,expiration); } //--- Устанавливает в данные критерия срабатывания запроса по его индексу контролируемое свойство, метод сравнения, фактическое и в объекте значения свойств //--- аккаунта, символа или торгового события (зависит от значения source) для активации отложенного запроса void SetNewActivationProperties(const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value); //--- Устанавливает (1) контролируемое свойство, (2) тип сравнения, (3) значение величины в объекте, //--- (4) фактической величины контролируемого свойства для активации отложенного запроса bool SetActivationProperty(const uint index,const ENUM_PEND_REQ_ACTIVATION_SOURCE source,const int property); bool SetActivationComparerType(const uint index,const ENUM_COMPARER_TYPE comparer_type); bool SetActivationControlValue(const uint index,const double value); bool SetActivationActualValue(const uint index,const double value); //--- Возвращает (1) источник активации отложенного запроса, (2) контролируемое свойство, (3) тип сравнения, //--- (4) значение величины в объекте,(5) фактической величины контролируемого свойства для активации отложенного запроса ENUM_PEND_REQ_ACTIVATION_SOURCE GetActivationSource(const uint index) const; int GetActivationProperty(const uint index) const; ENUM_COMPARER_TYPE GetActivationComparerType(const uint index) const; double GetActivationControlValue(const uint index) const; double GetActivationActualValue(const uint index) const; //--- Возвращает флаг успешности проверки всех контролируемых свойств объекта и соответствующих фактических свойств bool IsAllComparisonCompleted(void) const;
Метод SetTypeRequest() устанавливает свойству "тип отложенного запроса" переданный в метод тип. Типом может быть либо
"отложенный запрос, созданный по коду ошибки", либо "отложенный запрос, созданный по запросу". Тип отложенного запроса
устанавливается в объекте автоматически в конструкторе класса в зависимости от значения параметра "код ошибки". Если код равен нулю, то
это отложенный запрос, созданный по запросу из программы. Таким образом, данный метод сейчас нигде не используется, и создан на случай,
если вдруг понадобится оперативно извне сменить тип отложенного запроса (лично мне пока не получается придумать в этом надобность).
В блок методов, возвращающих описания свойств объекта-запроса, впишем
объявления методов, возвращающих описания контролируемых свойств:
//+------------------------------------------------------------------+ //| Описания свойств объекта-запроса | //+------------------------------------------------------------------+ //--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства запроса string GetPropertyDescription(ENUM_PEND_REQ_PROP_INTEGER property); string GetPropertyDescription(ENUM_PEND_REQ_PROP_DOUBLE property); string GetPropertyDescription(ENUM_PEND_REQ_PROP_STRING property); //--- Возвращает описание (1) контролируемого свойства, (2), типа сравнения, (3) значения контролируемой величины свойства в объекте, //--- (4) фактического значения контролируемого свойства для активации отложенного запроса, (5) общее количество условий активации string GetActivationPropertyDescription(const uint index) const; string GetActivationComparerTypeDescription(const uint index) const; string GetActivationControlValueDescription(const uint index) const; string GetActivationActualValueDescription(const uint index) const; uint GetActivationCriterionTotal(void) const { return ::ArrayRange(this.m_activated_control,0); } //--- Возвращает наименования параметров объекта-отложенного запроса string StatusDescription(void) const; string TypeRequestDescription(void) const; string IDDescription(void) const; string RetcodeDescription(void) const; string TimeCreateDescription(void) const; string TimeActivateDescription(void) const; string TimeWaitingDescription(void) const; string CurrentAttemptDescription(void) const; string TotalAttemptsDescription(void) const; string PriceCreateDescription(void) const; string TypeFillingActualDescription(void) const; string TypeTimeActualDescription(void) const; string ExpirationActualDescription(void) const; string VolumeActualDescription(void) const; string PriceActualDescription(void) const; string StopLimitActualDescription(void) const; string StopLossActualDescription(void) const; string TakeProfitActualDescription(void) const; //--- Возвращает наименования параметров структуры торгового запроса в объекте-запросе string MqlReqActionDescription(void) const; string MqlReqMagicDescription(void) const; string MqlReqOrderDescription(void) const; string MqlReqSymbolDescription(void) const; string MqlReqVolumeDescription(void) const; string MqlReqPriceDescription(void) const; string MqlReqStopLimitDescription(void) const; string MqlReqStopLossDescription(void) const; string MqlReqTakeProfitDescription(void) const; string MqlReqDeviationDescription(void) const; string MqlReqTypeOrderDescription(void) const; string MqlReqTypeFillingDescription(void) const; string MqlReqTypeTimeDescription(void) const; string MqlReqExpirationDescription(void) const; string MqlReqCommentDescription(void) const; string MqlReqPositionDescription(void) const; string MqlReqPositionByDescription(void) const; //--- Выводит в журнал (1) описание свойств запроса (full_prop=true - все свойства, false - только поддерживаемые), //--- (2) параметры активации запроса, (3) краткое сообщение о запросе, (4) краткое наименование запроса (3 и 4 - реализация в потомках класса) void Print(const bool full_prop=false); void PrintActivations(void); virtual void PrintShort(void){;} virtual string Header(void){return NULL;} }; //+------------------------------------------------------------------+
Метод GetActivationCriterionTotal() возвращает размер первого измерения массива данных условий активации, а иными
словами — количество условий активации в объекте-отложенном запросе.
В конструкторе класса зададим массиву данных условий активации нулевой размер в первом измерении:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CPendRequest::CPendRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) { this.CopyRequest(request); this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; this.m_digits=(int)::SymbolInfoInteger(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL),SYMBOL_DIGITS); int dg=(int)DigitsLots(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL)); this.m_digits_lot=(dg==0 ? 1 : dg); this.SetProperty(PEND_REQ_PROP_STATUS,status); this.SetProperty(PEND_REQ_PROP_ID,id); this.SetProperty(PEND_REQ_PROP_RETCODE,retcode); this.SetProperty(PEND_REQ_PROP_TYPE,this.GetProperty(PEND_REQ_PROP_RETCODE)>0 ? PEND_REQ_TYPE_ERROR : PEND_REQ_TYPE_REQUEST); this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time); this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price); this.m_pause.SetTimeBegin(this.GetProperty(PEND_REQ_PROP_TIME_CREATE)); this.m_pause.SetWaitingMSC(this.GetProperty(PEND_REQ_PROP_WAITING)); ::ArrayResize(this.m_activated_control,0,10); } //+------------------------------------------------------------------+
Размер массива данных условий активации будет изменяться автоматически при добавлении каждого очередного условия активации.
В методе, выводящем полный список данных объекта-отложенного запроса, после вывода всех его свойств добавим вывод списка условий активации:
//+------------------------------------------------------------------+ //| Выводит в журнал свойства отложенного запроса | //+------------------------------------------------------------------+ void CPendRequest::Print(const bool full_prop=false) { int header_code= ( this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_OPEN ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN : this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_CLOSE ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_CLOSE : this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_SLTP ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_SLTP : this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_PLACE ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_PLACE : this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_REMOVE ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE : this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_MODIFY ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY : WRONG_VALUE ); ::Print("============= \"",CMessage::Text(header_code),"\" ============="); int beg=0, end=PEND_REQ_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_PEND_REQ_PROP_INTEGER prop=(ENUM_PEND_REQ_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=PEND_REQ_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_PEND_REQ_PROP_DOUBLE prop=(ENUM_PEND_REQ_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=PEND_REQ_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_PEND_REQ_PROP_STRING prop=(ENUM_PEND_REQ_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } this.PrintActivations(); ::Print("================== ",CMessage::Text(MSG_LIB_PARAMS_LIST_END),": \"",CMessage::Text(header_code),"\" ==================\n"); } //+------------------------------------------------------------------+
Реализация метода, выводящего в журнал данные условий активации отложенного запроса:
//+------------------------------------------------------------------+ //| Выводит в журнал параметры активации запроса | //+------------------------------------------------------------------+ void CPendRequest::PrintActivations(void) { //--- Получаем размер первого измерения массива данных условий активации, //--- и если размер больше нуля - выводим в журнал все записанные в массиве данные int range=::ArrayRange(this.m_activated_control,0); if(range>0) { ::Print("--- ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTIVATION_TERMS)," ---"); for(int i=0;i<range;i++) { ENUM_PEND_REQ_ACTIVATION_SOURCE source=(ENUM_PEND_REQ_ACTIVATION_SOURCE)this.m_activated_control[i][0]; string type= ( source==PEND_REQ_ACTIVATION_SOURCE_ACCOUNT ? CMessage::Text(MSG_ACC_ACCOUNT) : source==PEND_REQ_ACTIVATION_SOURCE_SYMBOL ? CMessage::Text(MSG_LIB_PROP_SYMBOL) : source==PEND_REQ_ACTIVATION_SOURCE_EVENT ? CMessage::Text(MSG_EVN_EVENT) : "" ); ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CRITERION)," #",string(i+1),". ",type,": ",this.GetActivationPropertyDescription(i)); } } ::Print(""); } //+------------------------------------------------------------------+
Метод для создания нового условия активации отложенного запроса в массиве данных условий активации:
//+------------------------------------------------------------------+ //| Устанавливает контролируемое свойство, значения | //| и метод сравнения для активации отложенного запроса | //+------------------------------------------------------------------+ void CPendRequest::SetNewActivationProperties(const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value) { int range=::ArrayRange(this.m_activated_control,0); if(::ArrayResize(this.m_activated_control,range+1,10)==WRONG_VALUE) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_FAILED_ADD_PARAMS)); return; } this.m_activated_control[range][0]=source; this.m_activated_control[range][1]=property; this.m_activated_control[range][2]=comparer_type; this.m_activated_control[range][3]=control_value; this.m_activated_control[range][4]=actual_value; } //+---------------------------------------------------------------------+
В метод передаются источник данных активации, условие
активации, контролируемое и фактическое
значения условия активации, и метод сравнения.
Размер
массива данных условий активации увеличивается на 1 и заполняются все
необходимые данные в массиве значениями, переданными во входных параметрах метода. Данный метод нужно использовать только для
добавления нового условия активации.
Для корректировки уже существующих в объекте-запросе условий его активации предназначены следующие методы:
//+---------------------------------------------------------------------+ //| Устанавливает контролируемое свойство для активации запроса | //+---------------------------------------------------------------------+ bool CPendRequest::SetActivationProperty(const uint index,const ENUM_PEND_REQ_ACTIVATION_SOURCE source,const int property) { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return false; } this.m_activated_control[index][0]=source; this.m_activated_control[index][1]=property; return true; } //+------------------------------------------------------------------+ //| Устанавливает тип сравнения свойства объекта | //| с фактическим для активации отложенного запроса | //+------------------------------------------------------------------+ bool CPendRequest::SetActivationComparerType(const uint index,const ENUM_COMPARER_TYPE comparer_type) { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return false; } this.m_activated_control[index][2]=comparer_type; return true; } //+------------------------------------------------------------------+ //| Устанавливает значение контролируемой величины | //| свойства для активации отложенного запроса | //+------------------------------------------------------------------+ bool CPendRequest::SetActivationControlValue(const uint index,const double value) { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return false; } this.m_activated_control[index][3]=value; return true; } //+------------------------------------------------------------------+ //| Устанавливает значение фактической величины | //| контролируемого свойства в объекте-запросе | //+------------------------------------------------------------------+ bool CPendRequest::SetActivationActualValue(const uint index,const double value) { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return false; } this.m_activated_control[index][4]=value; return true; } //+------------------------------------------------------------------+
В метод установки контролируемого свойства SetActivationProperty() передаются индекс и два параметра условия —
источник условия (символ, аккаунт или событие) и само условие активации (из соответствующих перечислений, рассмотренных выше), так как
условие активации состоит из двух параметров — источник и тип изменения свойства. В остальные методы установки значений активации
передаются только индекс и значение.
Индексом является номер условия активации. Если условие одно, то индекс должен быть нулевым. Если условий два, то индекс должен
быть 0 или 1 в зависимости от того, какое по счёту условие хотим изменить, и т.д. При передаче индекса, выходящего за пределы размера первого
измерения массива, выводится запись в журнал об ошибочном индексе и возвращается
false.
Методы, возвращающие параметры условий активации:
//+------------------------------------------------------------------+ //| Возвращает источник активации отложенного запроса | //+------------------------------------------------------------------+ ENUM_PEND_REQ_ACTIVATION_SOURCE CPendRequest::GetActivationSource(const uint index) const { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return WRONG_VALUE; } return (ENUM_PEND_REQ_ACTIVATION_SOURCE)this.m_activated_control[index][0]; } //+------------------------------------------------------------------+ //| Возвращает контролируемое свойство для активации запроса | //+------------------------------------------------------------------+ int CPendRequest::GetActivationProperty(const uint index) const { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return WRONG_VALUE; } return (int)this.m_activated_control[index][1]; } //+------------------------------------------------------------------+ //| Возвращает тип сравнения свойства объекта | //| с фактическим для активации отложенного запроса | //+------------------------------------------------------------------+ ENUM_COMPARER_TYPE CPendRequest::GetActivationComparerType(const uint index) const { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return WRONG_VALUE; } return (ENUM_COMPARER_TYPE)this.m_activated_control[index][2]; } //+------------------------------------------------------------------+ //| Возвращает значение контролируемой величины | //| свойства для активации отложенного запроса | //+------------------------------------------------------------------+ double CPendRequest::GetActivationControlValue(const uint index) const { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return EMPTY_VALUE; } return this.m_activated_control[index][3]; } //+------------------------------------------------------------------+ //| Возвращает значение фактической величины | //| контролируемого свойства в объекте-запросе | //+------------------------------------------------------------------+ double CPendRequest::GetActivationActualValue(const uint index) const { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return EMPTY_VALUE; } return this.m_activated_control[index][4]; } //+------------------------------------------------------------------+
Здесь всё аналогично установке свойств активации, за исключением того, что все свойства условий возвращаются по одному, поэтому в каждом
методе достаточно передать только индекс запрашиваемого условия активации. В случае передачи неверного индекса, в журнал выводится
запись об ошибочном индексе и для методов, возвращающих целочисленные значения возвращается -1, а для методов, возвращающих
вещественные значения, возвращается
EMPTY_VALUE.
Метод сравнения двух величин по заданному типу сравнения:
//+------------------------------------------------------------------+ //| Сравнивает два значения источника данных по типу сравнения | //+------------------------------------------------------------------+ bool CPendRequest::IsCompared(const double actual_value,const double control_value,const ENUM_COMPARER_TYPE compare) const { switch((int)compare) { case EQUAL : return(actual_value<control_value || actual_value>control_value ? false : true); case NO_EQUAL : return(actual_value<control_value || actual_value>control_value ? true : false); case MORE : return(actual_value>control_value ? true : false); case LESS : return(actual_value<control_value ? true : false); case EQUAL_OR_MORE : return(actual_value<control_value ? false : true); case EQUAL_OR_LESS : return(actual_value>control_value ? false : true); default : break; } return false; } //+------------------------------------------------------------------+
В метод передаётся текущее значение сравниваемого свойства, контрольная
величина, с которой сравнивается текущее значение и тип
сравнения.
В зависимости от типа сравнения,
сравниваются значения текущего значения свойства с его контрольным значением и возвращается результат сравнения.
Метод, возвращающий флаг успешности сравнения условия активации по его индексу в массиве данных условий активации:
//+------------------------------------------------------------------+ //| Возвращает флаг успешности проверки контролируемого свойства | //| объекта и соответствующего ему фактического свойства | //+------------------------------------------------------------------+ bool CPendRequest::IsComparisonCompleted(const uint index) const { //--- Если контролируемое свойство не задано - возвращаем false if(this.m_activated_control[index][1]==MSG_LIB_PROP_NOT_SET) return false; //--- Возвращаем результат заданного сравнения величины контролируемого свойства с его фактическим значением ENUM_COMPARER_TYPE comparer=(ENUM_COMPARER_TYPE)this.m_activated_control[index][2]; return this.IsCompared(this.m_activated_control[index][4],this.m_activated_control[index][3],comparer); } //+------------------------------------------------------------------+
Метод возвращает флаг срабатывания одного из условий активации отложенного запроса. Во входном параметре метода передаётся индекс
проверяемого условия в массиве данных условий активации. Сравнение производится рассмотренным выше методом
IsCompared() и возвращается результат сравнения.
Метод, возвращающий флаг успешности проверки всех, созданных для объекта-запроса условий его активации:
//+------------------------------------------------------------------+ //| Возвращает флаг успешности проверки всех контролируемых свойств | //| объекта и соответствующих им фактических свойств | //+------------------------------------------------------------------+ bool CPendRequest::IsAllComparisonCompleted(void) const { bool res=true; int range=::ArrayRange(this.m_activated_control,0); if(range==0) return false; for(int i=0;i<range;i++) res &=this.IsComparisonCompleted(i); return res; } //+-------------------------------------------------------------------+
Это универсальный метод, позволяющий проверить любой объект-отложенный запрос на наступление момента его активации.
Здесь
в цикле по первому измерению массива данных условий активации при
помощи метода
IsComparisonCompleted() в результат проверки (переменную res)
добавляется флаг успешности проверки соответствующего индексу цикла контролируемого свойства. По завершению цикла
возвращается результат проверки всех условий. Если хоть одно из условий не выполнено или массив данных имеет нулевой размер в первом
измерении, то результатом будет
false.
Метод, возвращающий количество знаков после запятой для корректного вывода описания условия активации в журнал:
//+-------------------------------------------------------------------+ //|Возвращает количество знаков после запятой контролируемого свойства| //+-------------------------------------------------------------------+ int CPendRequest::DigitsControlledValue(const uint index) const { int dg=0; //--- В зависимости от источника условия активации проверяем условия активации //--- и записываем в результат требуемое количество знаков после запятой switch((int)this.m_activated_control[index][0]) { //--- Аккаунт. Если условие активации - целочисленное значение, то 0, //--- если вещественное - то количество знаков после запятой в текущей валюте case PEND_REQ_ACTIVATION_SOURCE_ACCOUNT : dg=(this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_ACCOUNT_BALANCE ? 0 : this.m_digits_currency); break; //--- Символ. В зависимости от условия, записываем либо количество знаков после запятой в котировке символа, //--- либо количество знаков после запятой в текущей валюте, либо количество знаков после запятой в значении лота, либо ноль case PEND_REQ_ACTIVATION_SOURCE_SYMBOL : //--- digits if( (this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_DEALS && this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_EMPTY) || this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE || this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME || (this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FREEZE_LEVEL && this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME_REAL) ) dg=this.m_digits; //--- не digits else if( this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_LASTLOW) { //--- digits currency if( (this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE && this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_VOLUME) || this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_TURNOVER ) dg=this.m_digits_currency; //--- digits lots else dg=(this.m_digits_lot==0 ? 1 : this.m_digits_lot); } //--- 0 else dg=0; break; //--- По умолчанию ноль default: break; } return dg; } //+------------------------------------------------------------------+
В методе проверяется источник активации, и уже в зависимости от источника проверяется условие активации. В зависимости от условия
активации возвращается либо количество знаков после запятой в значении котировки символа, либо количество знаков после запятой в
значении текущей валюты счёта, либо количество знаков после запятой в значении лота символа, либо ноль.
Метод, возвращающий текстовое описание контролируемого свойства:
//+------------------------------------------------------------------+ //| Возвращает описание контролируемого свойства по индексу | //+------------------------------------------------------------------+ string CPendRequest::GetActivationPropertyDescription(const uint index) const { //--- Получаем источник активации и в зависимости от источника создаём текст описания типа сравнения с контролируемым значением ENUM_PEND_REQ_ACTIVATION_SOURCE source=(ENUM_PEND_REQ_ACTIVATION_SOURCE)this.m_activated_control[index][0]; string value= ( source==PEND_REQ_ACTIVATION_SOURCE_EVENT ? "" : ( this.m_activated_control[index][1]==MSG_LIB_PROP_NOT_SET ? "" : this.GetActivationComparerTypeDescription(index)+this.GetActivationControlValueDescription(index) ) ); //--- Возвращаем описание условия активации + тип сравнения + контролируемое значение return ( source==PEND_REQ_ACTIVATION_SOURCE_ACCOUNT ? CMessage::Text((ENUM_PEND_REQ_ACTIVATE_BY_ACCOUNT_PROP)this.m_activated_control[index][1])+value : source==PEND_REQ_ACTIVATION_SOURCE_SYMBOL ? CMessage::Text((ENUM_PEND_REQ_ACTIVATE_BY_SYMBOL_PROP)this.m_activated_control[index][1])+value : source==PEND_REQ_ACTIVATION_SOURCE_EVENT ? CMessage::Text((ENUM_PEND_REQ_ACTIVATE_BY_EVENT)this.m_activated_control[index][1])+value : "" ); } //+------------------------------------------------------------------+
В метод передаётся индекс условия в массиве данных условий активации. По индексу получаем источник активации, и в зависимости от него
получаем остальные текстовые описания, из которых составляем итоговый текст и возвращаем его.
Метод, возвращающий описание типа сравнения:
//+------------------------------------------------------------------+ //| Возвращает описание типа сравнения | //+------------------------------------------------------------------+ string CPendRequest::GetActivationComparerTypeDescription(const uint index) const { return ComparisonTypeDescription((ENUM_COMPARER_TYPE)this.m_activated_control[index][2]); } //+------------------------------------------------------------------+
Просто возвращает текстовое описание типа сравнения, записанного в массиве данных по индексу условия активации, переданного параметром в
метод.
Метод, возвращающий описание значения контролируемого свойства в объекте-запросе:
//+------------------------------------------------------------------+ //| Возвращает описание значения контролируемого свойства в объекте | //+------------------------------------------------------------------+ string CPendRequest::GetActivationControlValueDescription(const uint index) const { return ( this.m_activated_control[index][3]!=EMPTY_VALUE ? ( this.m_activated_control[index][0]==PEND_REQ_ACTIVATION_SOURCE_SYMBOL && this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_TIME ? ::TimeToString((ulong)this.m_activated_control[index][3]) : ::DoubleToString(this.m_activated_control[index][3],this.DigitsControlledValue(index)) ) : "" ); } //+------------------------------------------------------------------+
В метод передаётся индекс условия.
Проверяется значение контролируемого
свойства, записанное в массив по указанному индексу, и если оно не равно "пустому значению" (
EMPTY_VALUE), то проверяется
условие и его тип. Если
в итоге проверяется время символа, то возвращается текстовое описание
времени,
иначе —
текстовое описание целочисленного или вещественного значения с корректным
количеством знаков после запятой.
Метод, возвращающий описание фактического значения контролируемого свойства в объекте-запросе:
//+------------------------------------------------------------------+ //|Возвращает описание фактического значения контролируемого свойства| //+------------------------------------------------------------------+ string CPendRequest::GetActivationActualValueDescription(const uint index) const { return ( this.m_activated_control[index][4]!=EMPTY_VALUE ? ( this.m_activated_control[index][0]==PEND_REQ_ACTIVATION_SOURCE_SYMBOL && this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_TIME ? ::TimeToString((ulong)this.m_activated_control[index][4]) : ::DoubleToString(this.m_activated_control[index][4],this.DigitsControlledValue(index)) ) : "" ); } //+------------------------------------------------------------------+
Метод идентичен предыдущему, за исключением того, что данные получаем по индексу 4 во втором измерении массива данных условий активации. Это все изменения в базовом объекте абстрактного отложенного торгового запроса.
Теперь внесём некоторые доработки в классы объектов-наследников базового объекта абстрактного запроса.
Так как теперь у нас реализованы два типа отложенных запросов — по коду ошибки и по запросу, то второй тип объектов не подразумевает наличия некоторых свойств — таких как код возврата сервера (здесь он всегда нулевой), время активации запроса (это время в запросах второго типа может задаваться как одно из условий активации запроса, и находится оно в этом случае в массиве данных условий активации отложенного торгового запроса), время ожидания (здесь совсем не используется) и номер текущей попытки (здесь даётся одна попытка, а далее уже будет работать как стандартный отсыл торгового приказа и его обработка по коду возврата торгового сервера).
В связи с этим внесём дополнение во все объекты-наследники базового объекта-отложенного запроса в их методы, возвращающие поддержание объектом целочисленных свойств, и впишем вызов метода, выводящего в журнал список условий активации отложенного запроса в метод PrintShort() каждого из объектов-наследников.
В файлы объектов-наследников базового объекта абстрактного отложенного запроса PendReqOpen.mqh, PendReqClose.mqh, PendReqSLTP.mqh, PendReqPlace.mqh, PendReqRemove.mqh и PendReqModify.mqh внесём такие изменения (на примере класса CPendReqOpen)://+------------------------------------------------------------------+ //| Возвращает истину, если ордер поддерживает переданное | //| целочисленное свойство, возвращает ложь в противном случае | //+------------------------------------------------------------------+ bool CPendReqOpen::SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property) { if( (this.GetProperty(PEND_REQ_PROP_TYPE)==PEND_REQ_TYPE_REQUEST && (property==PEND_REQ_PROP_RETCODE || property==PEND_REQ_PROP_TIME_ACTIVATE || property==PEND_REQ_PROP_WAITING || property==PEND_REQ_PROP_CURRENT_ATTEMPT ) ) || property==PEND_REQ_PROP_MQL_REQ_ORDER || property==PEND_REQ_PROP_MQL_REQ_POSITION || property==PEND_REQ_PROP_MQL_REQ_POSITION_BY || property==PEND_REQ_PROP_MQL_REQ_EXPIRATION || property==PEND_REQ_PROP_MQL_REQ_TYPE_TIME ) return false; return true; } //+------------------------------------------------------------------+
Здесь проверяется что это объект, созданный по запросу, и если да, то исключаются свойства, озвученные выше.
//+------------------------------------------------------------------+ //| Выводит в журнал краткое сообщение с данными запроса | //+------------------------------------------------------------------+ void CPendReqOpen::PrintShort(void) { string params=this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME),this.m_digits_lot)+" "+ OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE)); string price=CMessage::Text(MSG_LIB_TEXT_REQUEST_PRICE)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_PRICE),this.m_digits); string sl=this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL)>0 ? ", "+CMessage::Text(MSG_LIB_TEXT_REQUEST_SL)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL),this.m_digits) : ""; string tp=this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP)>0 ? ", "+CMessage::Text(MSG_LIB_TEXT_REQUEST_TP)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP),this.m_digits) : ""; string time=this.IDDescription()+", "+CMessage::Text(MSG_LIB_TEXT_CREATED)+" "+TimeMSCtoString(this.GetProperty(PEND_REQ_PROP_TIME_CREATE)); string attempts=CMessage::Text(MSG_LIB_TEXT_ATTEMPTS)+" "+(string)this.GetProperty(PEND_REQ_PROP_TOTAL); string wait=CMessage::Text(MSG_LIB_TEXT_WAIT)+" "+::TimeToString(this.GetProperty(PEND_REQ_PROP_WAITING)/1000,TIME_SECONDS); string end=CMessage::Text(MSG_LIB_TEXT_END)+" "+ TimeMSCtoString(this.GetProperty(PEND_REQ_PROP_TIME_CREATE)+this.GetProperty(PEND_REQ_PROP_WAITING)*this.GetProperty(PEND_REQ_PROP_TOTAL)); //--- string message=CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN)+": "+ "\n- "+params+", "+price+sl+tp+ "\n- "+time+", "+attempts+", "+wait+", "+end; ::Print(message); this.PrintActivations(); } //+------------------------------------------------------------------+
Здесь мы убрали после строки "+end" добавление кода переноса на новую строку (+"\n"), и после строки ::Print(message); добавили вызов
метода, выводящего список условий активации. Если массив с условиями имеет нулевой размер (в объектах, созданных по коду ошибки), то
метод PrintActivations() ничего не распечатывает кроме кода переноса на новую строку ("\n"). В противном случае метод выводит полный
список всех условий, записанных в массиве данных.
В некоторые эти классы были внесены незначительные "косметические" исправления, касающиеся только вывода в журнал, поэтому здесь на
них останавливаться не будем — всё есть в прилагаемых к статье файлах, и можно ознакомиться с изменениями самостоятельно.
Теперь займёмся торговыми классами.
В основном торговом классе CTrading перенесём
из приватной секции в защищённую три переменные-члены класса и метод GetFreeID():
private: CArrayInt m_list_errors; // Список ошибок bool m_is_trade_disable; // Флаг запрета торговли bool m_use_sound; // Флаг использования звуков торговых событий объекта uchar m_total_try; // Количество торговых попыток MqlTradeRequest m_request; // Цены торгового запроса ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags; // Флаги причин ошибок в торговом методе ENUM_ERROR_HANDLING_BEHAVIOR m_err_handling_behavior; // Поведение при обработке ошибок //--- Добавляет код ошибки в список
Эти переменные и методы нужны в классе-наследнике, поэтому, чтобы он смог иметь к ним
доступ, они должны находиться в защищённой секции (в публичной не нужны — доступ к ним извне запрещён). И добавим
в защищённой секции класса метод, возвращающий флаг наличия рыночного ордера/позиции с идентификатором отложенного запроса.
В итоге защищённая секция класса выглядит так:
//+------------------------------------------------------------------+ //| Торговый класс | //+------------------------------------------------------------------+ class CTrading : public CBaseObj { protected: CAccount *m_account; // Указатель на объект-текущий аккаунт CSymbolsCollection *m_symbols; // Указатель на список коллекции символов CMarketCollection *m_market; // Указатель на список коллекции рыночных ордеров и позиций CHistoryCollection *m_history; // Указатель на список коллекции исторических ордеров и сделок CEventsCollection *m_events; // Указатель на список коллекции событий CArrayObj m_list_request; // Список отложенных запросов uchar m_total_try; // Количество торговых попыток MqlTradeRequest m_request; // Структура торгового запроса ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags; // Флаги причин ошибок в торговом методе //--- Ищет первый свободный идентификатор отложенного запроса int GetFreeID(void); //--- Возвращает флаг наличия рыночного ордера/позиции с идентификатором отложенного запроса bool IsPresentOrderByID(const uchar id); private:
Здесь цветом указаны переменные и методы, перенесённый из приватной секции,
и
определение нового метода.
В публичной секции класса впишем объявление метода, возвращающего указатель
на объект-запрос по его идентификатору в списке, и объявление метода,
возвращающего уровень логирования торгового объекта символа:
//--- Создаёт отложенный запрос bool CreatePendingRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const uchar attempts, const ulong wait, const MqlTradeRequest &request, const int retcode, CSymbol *symbol_obj, COrder *order); //--- Возвращает (1) указатель на объект-запрос по его идентификатору в списке, //--- (2) уровень логирования торгового объекта символа CPendRequest *GetPendRequestByID(const uchar id); ENUM_LOG_LEVEL GetTradeObjLogLevel(const string symbol_name); }; //+------------------------------------------------------------------+
За пределами тела класса напишем реализацию этих методов.
Реализация метода, возвращающего уровень логирования торгового объекта символа:
//+------------------------------------------------------------------+ //| Возвращает уровень логирования торгового объекта символа | //+------------------------------------------------------------------+ ENUM_LOG_LEVEL CTrading::GetTradeObjLogLevel(const string symbol_name) { CTradeObj *trade_obj=this.GetTradeObjBySymbol(symbol_name,DFUN); return(trade_obj!=NULL ? trade_obj.GetLogLevel() : LOG_LEVEL_NO_MSG); } //+------------------------------------------------------------------+
В метод передаётся наименование символа, уровень логирования торгового объекта которого нужно получить, получаем
торговый объект из объекта-символа, и если объект получен, возвращаем
уровень логирования этого объекта, иначе —
возвращаем запрет логирования.
Реализация метода, возвращающего указатель на объект-запрос по идентификатору в списке:
//+------------------------------------------------------------------+ //| Возвращает указатель на объект-запрос по идентификатору в списке | //+------------------------------------------------------------------+ CPendRequest* CTrading::GetPendRequestByID(const uchar id) { int index=this.GetIndexPendingRequestByID(id); if(index==WRONG_VALUE) return NULL; return this.m_list_request.At(index); } //+------------------------------------------------------------------+
В метод передаётся идентификатор запроса, затем получаем индекс
объекта-отложенного запроса в списке по его идентификатору, и если
объекта в списке нет, то возвращаем
NULL, иначе —
объект из списка по полученному его индексу в списке.
Реализация метода, возвращающего флаг наличия рыночного ордера/позиции с идентификатором отложенного запроса:
//+------------------------------------------------------------------+ //| Возвращает флаг наличия рыночного ордера/позиции | //| с идентификатором отложенного запроса | //+------------------------------------------------------------------+ bool CTrading::IsPresentOrderByID(const uchar id) { CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL); return(list==NULL ? false : list.Total()!=0); } //+------------------------------------------------------------------+
В метод передаётся идентификатор отложенного запроса. Далее получаем
список раночных ордеров/позиций, отфильтрованный по идентификатору отложенного запроса и его значению. Если список не получен
или пуст (нет ордеров/позиций с искомым идентификатором), то возвращаем
false, иначе — возвращаем true.
Дополним ещё одной проверкой метод, возвращающий номер свободного идентификатора:
//+------------------------------------------------------------------+ //| Ищет первый свободный идентификатор отложенного запроса | //+------------------------------------------------------------------+ int CTrading::GetFreeID(void) { int id=WRONG_VALUE; CPendRequest *element=new CPendRequest(); if(element==NULL) return 0; for(int i=1;i<256;i++) { element.SetID((uchar)i); this.m_list_request.Sort(SORT_BY_PEND_REQ_ID); if(this.m_list_request.Search(element)==WRONG_VALUE) { if(this.IsPresentOrderByID((uchar)i)) continue; id=i; break; } } delete element; return id; } //+------------------------------------------------------------------+
Для чего нужна ещё одна проверка на то, что присутствует в рынке ордер/позиция с
таким идентификатором? Дело в том, что если у нас есть активированный отложенный запрос, и по нему, например, открыта позиция, то
этот запрос удаляется из списков отложенных запросов, и его идентификатор становится доступным для использования при создании новых
отложенных запросов.
При создании нового отложенного запроса будет создан отложенный запрос с таким же идентификатором, по которому в данный момент есть
открытая позиция. При наступлении условий активации этого нового отложенного запроса, проверяется наличие позиции с таким же
идентификатором (а она есть — открыта по прошлому такому же идентификатору), и новый отложенный запрос просто удаляется — считается, что
раз позиция с таким ID существует, значит запрос отработан. Т.е., запрос не пошлёт торговый приказ на сервер, а просто будет удалён.
Для избегания таких ситуаций есть пара выходов: каким-либо образом проводить дополнительную идентификацию на то, что это уже другой
запрос с тем же идентификатором, что и у открытой позиции, либо просто проверять наличие позиции с таким ID при отсутствии отложенного
запроса в списке с тем же ID.
Второй вариант мне показался менее затратным, хотя и таит в себе ограничение — невозможно использовать идентификатор со свободным ID
до тех пор, пока не будет закрыта позиция с таким же идентификатором. Т.е., жёсткое ограничение на 255 позиций с разными идентификаторами
отложенных запросов.
На этом доработка основного торгового класса завершена.
Теперь доработаем класс управления торговлей CTradingControl — класс-наследник основного торгового класса CTrading.
При создании класса управления отложенными запросами в прошлой
статье, мы сделали обработку объектов отложенных запросов в таймере класса.
Так как там мы обрабатывали лишь один тип
отложенных запросов — созданных по коду возврата сервера, то вполне достаточно было разместить весь код обработки в таймере класса.
Сегодня мы добавим обработку второго типа отложенных запросов — созданных по запросу из программы.
Соответственно, нам нужно будет сделать два обработчика — первый для запросов, созданных по коду ошибки, а второй — по запросу.
Поэтому мы поступим так: сделаем два обработчика объектов-отложенных запросов, разделённых по типу обрабатываемых запросов, а
одинаковый код для обоих обработчиков оформим отдельным методом. Тогда для обработки обоих типов отложенных запросов нам достаточно
будет в таймере проверять тип запроса и вызывать соответствующий обработчик.
Внесём в тело класса все необходимые дополнения, а далее разберём их:
//+------------------------------------------------------------------+ //| Класс управления отложенными торговыми запросами | //+------------------------------------------------------------------+ class CTradingControl : public CTrading { private: //--- Устнанавливает в объект-отложенный запрос фактические данные ордера/позиции void SetOrderActualProperties(CPendRequest *req_obj,const COrder *order); //--- Обработчик отложенных запросов, созданных (1) по коду ошибки, (2) по запросу void OnPReqByErrCodeHandler(CPendRequest *req_obj,const int index); void OnPReqByRequestHandler(CPendRequest *req_obj,const int index); //--- Проверка актуальности отложенного запроса (сработал или нет) bool CheckPReqRelevance(CPendRequest *req_obj,const MqlTradeRequest &request,const int index); //--- Обновляет актуальные значения контролируемых свойств в объектах-отложенных запросах, void RefreshControlActualDatas(CPendRequest *req_obj,const CSymbol *symbol); //--- Возвращает для контроля активации актуальные данные (1) аккаунта, (2) символа, (3) событий double GetActualDataAccount(const int property); double GetActualDataSymbol(const int property,const CSymbol *symbol); double GetActualDataEvent(const int property); public: //--- Возвращает себя CTradingControl *GetObject(void) { return &this; } //--- Таймер virtual void OnTimer(void); //--- Конструктор CTradingControl(); //--- (1) Создаёт отложенный запрос (1) на открытие позиции, (2) на установку отложенного ордера template<typename SL,typename TP> int OpenPositionPending(const ENUM_POSITION_TYPE type, const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PS,typename PL,typename SL,typename TP> int PlaceOrderPending( const ENUM_ORDER_TYPE order_type, const double volume, const string symbol, const PS price_stop, const PL price_limit=0, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Устанавливает критерии активации отложенного запроса bool SetNewActivationProperties(const uchar id, const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value); }; //+------------------------------------------------------------------+
Так как теперь у нас появилась необходимость устанавливать в объект-отложенный запрос данные контролируемых параметров, а ранее для
отслеживания срабатывания отложенного запроса мы также вписывали в объект-запрос актуальные данные ордера, которому соответствует
данный запрос, то во избежание путаницы,
переименуем метод установки актуальных данных ордера в объект-запрос из SetActualProperties()
в SetOrderActualProperties().
Сегодня мы делаем только работу с открытием позиций при помощи отложенных запросов, поэтому метод создания отложенного запроса на установку
отложенного ордера оставим за рамками данной статьи, и
рассмотрим метод создания отложенного запроса на открытие позиции:
//+------------------------------------------------------------------+ //| Создаёт отложенный запрос на открытие позиции | //+------------------------------------------------------------------+ template<typename SL,typename TP> int CTradingControl::OpenPositionPending(const ENUM_POSITION_TYPE type, const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { //--- Если установлен флаг глобального запрета торговли - выходим if(this.IsTradingDisable()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE)); return false; } //--- Устанавливаем результат торгового запроса как true и флаг ошибки как "нет ошибок" bool res=true; this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR; ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)type; ENUM_ACTION_TYPE action=(ENUM_ACTION_TYPE)order_type; //--- Получаем объект-символ по имени символа. CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol); //--- Если получить не удалось - записываем флаг "внутренняя ошибка", выводим сообщение в журнал и возвращаем false if(symbol_obj==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ)); return false; } //--- получаем торговый объект из объекта-символа CTradeObj *trade_obj=symbol_obj.GetTradeObj(); //--- Если получить не удалось - записываем флаг "внутренняя ошибка", выводим сообщение в журнал и возвращаем false if(trade_obj==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } //--- Устанавливаем цены //--- Если установить не удалось - записываем флаг "внутренняя ошибка" устанавливаем код ошибки в структуру возврата, //--- выводим сообщение в журнал и возвращаем false if(!this.SetPrices(order_type,0,sl,tp,0,DFUN,symbol_obj)) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; trade_obj.SetResultRetcode(10021); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(10021)); // Отсутствуют котировки для обработки запроса return false; } //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось - возвращаем false int id=this.GetFreeID(); if(id<1) { //--- Нет свободных идентификаторов для создания отложенного запроса if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_NO_FREE_IDS)); return false; } //--- Записываем объём, отклонение, комментарий и тип заливки в структуру запроса this.m_request.volume=volume; this.m_request.deviation=(deviation==ULONG_MAX ? trade_obj.GetDeviation() : deviation); this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment); this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : trade_obj.GetTypeFilling()); //--- Записываем в магик идентификатор объекта-отложенного запроса, идентификаторы групп в значение магика, //--- и заполняем остальные незаполненные поля структуры торгового запроса uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic); this.SetPendReqID((uchar)id,mn); if(group_id1>0) this.SetGroupID1(group_id1,mn); if(group_id2>0) this.SetGroupID2(group_id2,mn); this.m_request.magic=mn; this.m_request.action=TRADE_ACTION_DEAL; this.m_request.symbol=symbol_obj.Name(); this.m_request.type=order_type; //--- В результате создания отложенного торгового запроса возвращаем либо его идентификатор, либо -1 при неудаче if(this.CreatePendingRequest(PEND_REQ_STATUS_OPEN,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,NULL)) return id; return WRONG_VALUE; } //+------------------------------------------------------------------+
Метод представляет собой урезанную версию метода для открытия позиции из
статьи 26 (и последующих статей) с созданием отложенного запроса в случае получения ошибки от торгового сервера. Достаточно
подробно прокомментирован, и здесь останавливаться на его разборе подробно не будем.
В метод передаются все необходимые данные для открытия позиции, заполняются поля структуры торгового запроса и отправляются в метод
создания отложенного запроса.
В итоге, при удачном создании отложенного запроса, возвращается идентификатор вновь созданного отложенного запроса, в противном
случае возвращается -1.
В качестве задержки между повторными попытками здесь используется рассчитанное максимально-возможное время ожидания как
разница между максимально-возможным временем в терминале и текущим временем. Таким образом время действия отложенного запроса
будет максимально-возможным (до 31.12.3000 г.).
Метод, устанавливающий критерии активации отложенного запроса:
//+------------------------------------------------------------------+ //| Устанавливает критерии активации отложенного запроса | //+------------------------------------------------------------------+ bool CTradingControl::SetNewActivationProperties(const uchar id, const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value) { CPendRequest *req_obj=this.GetPendRequestByID(id); if(req_obj==NULL) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_GETTING_FAILED)); return false; } req_obj.SetNewActivationProperties(source,property,control_value,comparer_type,actual_value); return true; } //+------------------------------------------------------------------+
В метод передаются идентификатор отложенного запроса, в который необходимо добавить новое условие его активации, источник активации
запроса (символ, аккаунт или событие), условие активации, контрольное значение, тип сравнения и фактическое значение контролируемого
для активации запроса свойства.
Затем получаем объект-отложенный запрос по переданному в метод идентификатору
и создаём для него новое условие активации с переданными в метод
параметрами.
Метод проверки актуальности отложенного запроса:
//+------------------------------------------------------------------+ //| Проверка актуальности отложенного запроса | //+------------------------------------------------------------------+ bool CTradingControl::CheckPReqRelevance(CPendRequest *req_obj,const MqlTradeRequest &request,const int index) { //--- Если это открытие позиции или установка отложенного ордера if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()==0) || req_obj.Action()==TRADE_ACTION_PENDING) { //--- Получаем идентификатор отложенного запроса uchar id=this.GetPendReqID((uint)request.magic); //--- Получаем список ордеров/позиций, содержащий ордер/позицию с таким идентификатором отложенного запроса CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL); if(::CheckPointer(list)==POINTER_INVALID) return false; //--- Если есть такой ордер/позиция - запрос отработан: удаляем его и переходим к следующему (уходим из метода во внешний цикл) if(list.Total()>0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(index); return false; } } //--- Иначе: полное и частичное закрытие позиции, удаление ордера, модификация параметров ордера и стоп приказов позиции else { CArrayObj *list=NULL; //--- если это закрытие позиции, в том числе и встречным if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()>0) || req_obj.Action()==TRADE_ACTION_CLOSE_BY) { //--- Получаем из списка открытых позиций позицию с нужным тикетом list=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Position(),EQUAL); if(::CheckPointer(list)==POINTER_INVALID) return false; //--- Если в рынке нет такой позиции - запрос отработан: удаляем его и переходим к следующему (уходим из метода во внешний цикл) if(list.Total()==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(index); return false; } //--- Иначе, если позиция ещё существует, то это частичное закрытие else { //--- Получаем список всех торговых событий аккаунта list=this.m_events.GetList(); if(list==NULL) return false; //--- В цикле с конца списка торговых событий аккаунта int events_total=list.Total(); for(int j=events_total-1; j>WRONG_VALUE; j--) { //--- получаем очередное торговое событие CEvent *event=list.At(j); if(event==NULL) continue; //--- Если это событие - частичное закрытие, или при закрытии встречным было частичное закрытие if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL || event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS) { //--- Если тикет позиции в торговом событии совпадает с тикетом в отложенном торговом запросе if(event.PositionID()==req_obj.Position()) { //--- Получаем объект-позицию из списка рыночных позиций CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL); if(list_orders==NULL || list_orders.Total()==0) break; COrder *order=list_orders.At(list_orders.Total()-1); if(order==NULL) break; //--- Устанавливаем фактические данные позиции в объект-отложенный запрос this.SetOrderActualProperties(req_obj,order); //--- Если (исполненный объём заявки + неисполненный объём заявки) равен запрашиваемому объёму в отложенном запросе - //--- запрос отработан: удаляем его и прерываем цикл по списку торговых событий аккаунта if(req_obj.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME)==event.VolumeOrderExecuted()+event.VolumeOrderCurrent()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(index); break; } } } } //--- Если в цикле по списку торговых событий был удалён отработанный объект-отложенный запрос - переходим к следующему (уходим из метода во внешний цикл) if(::CheckPointer(req_obj)==POINTER_INVALID) return false; } } //--- Если это модификация стоп-приказов позиции if(req_obj.Action()==TRADE_ACTION_SLTP) { //--- Получаем список всех торговых событий аккаунта list=this.m_events.GetList(); if(list==NULL) return false; //--- В цикле с конца списка торговых событий аккаунта int events_total=list.Total(); for(int j=events_total-1; j>WRONG_VALUE; j--) { //--- получаем очередное торговое событие CEvent *event=list.At(j); if(event==NULL) continue; //--- Если это событие - любое изменение стоп-приказов позиции if(event.TypeEvent()>TRADE_EVENT_MODIFY_ORDER_TP) { //--- Если тикет позиции в торговом событии совпадает с тикетом в отложенном торговом запросе if(event.PositionID()==req_obj.Position()) { //--- Получаем объект-позицию из списка рыночных позиций CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL); if(list_orders==NULL || list_orders.Total()==0) break; COrder *order=list_orders.At(list_orders.Total()-1); if(order==NULL) break; //--- Устанавливаем фактические данные позиции в объект-отложенный запрос this.SetOrderActualProperties(req_obj,order); //--- Если все модификации отработаны - //--- запрос отработан: удаляем его и прерываем цикл по списку торговых событий аккаунта if(req_obj.IsCompleted()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(index); break; } } } } //--- Если в цикле по списку торговых событий был удалён отработанный объект-отложенный запрос - переходим к следующему (уходим из метода во внешний цикл) if(::CheckPointer(req_obj)==POINTER_INVALID) return false; } //--- Если это удаление отложенного ордера if(req_obj.Action()==TRADE_ACTION_REMOVE) { //--- Получаем список удалённых отложенных ордеров из исторического списка list=this.m_history.GetList(ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL); if(::CheckPointer(list)==POINTER_INVALID) return false; //--- Оставляем в списке только один ордер с нужным тикетом list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,req_obj.Order(),EQUAL); //--- Если есть такой ордер - запрос отработан: удаляем его и переходим к следующему (уходим из метода во внешний цикл) if(list.Total()>0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(index); return false; } } //--- Если это модификация отложенного ордера if(req_obj.Action()==TRADE_ACTION_MODIFY) { //--- Получаем список всех торговых событий аккаунта list=this.m_events.GetList(); if(list==NULL) return false; //--- В цикле с конца списка торговых событий аккаунта int events_total=list.Total(); for(int j=events_total-1; j>WRONG_VALUE; j--) { //--- получаем очередное торговое событие CEvent *event=list.At(j); if(event==NULL) continue; //--- Если это событие - любое изменение модифицируемых параметров отложенного ордера if(event.TypeEvent()>TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER && event.TypeEvent()<TRADE_EVENT_MODIFY_POSITION_SL_TP) { //--- Если тикет ордера в торговом событии совпадает с тикетом в отложенном торговом запросе if(event.TicketOrderEvent()==req_obj.Order()) { //--- Получаем объект-ордер из списка CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Order(),EQUAL); if(list_orders==NULL || list_orders.Total()==0) break; COrder *order=list_orders.At(0); if(order==NULL) break; //--- Устанавливаем фактические данные ордера в объект-отложенный запрос this.SetOrderActualProperties(req_obj,order); //--- Если все модификации отработаны - //--- запрос отработан: удаляем его и прерываем цикл по списку торговых событий аккаунта if(req_obj.IsCompleted()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(index); break; } } } } } } //--- Если после проверки отработки запроса объект-отложенный запрос удалён - выходим (уходим из метода во внешний цикл) return(::CheckPointer(req_obj)==POINTER_INVALID ? false : true); } //+------------------------------------------------------------------+
Данный метод проверяет факт исполнения отложенного запроса и удаляет его при подтверждении такого факта. Ранее этот код уже рассматривался
нами
в составе кода таймера класса управления торговлей. Сейчас же, в
связи с тем, что мы разделяем обработку объектов-отложенных запросов на два обработчика по типам отложенных запросов, и этот код одинаков
для обоих обработчиков, то мы его вынесли в отдельный метод и будем вызывать в каждом обработчике.
Обработчик отложенных запросов, созданных по коду ошибки:
//+------------------------------------------------------------------+ //| Обработчик отложенных запросов, созданных по коду ошибки | //+------------------------------------------------------------------+ void CTradingControl::OnPReqByErrCodeHandler(CPendRequest *req_obj,const int index) { //--- получаем структуру запроса и из неё объект-символ, на котором должна быть проведена торговая операция MqlTradeRequest request=req_obj.MqlRequest(); CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol); if(symbol_obj==NULL || !symbol_obj.RefreshRates()) return; //--- Устанавливаем флаг запрета торговли со стороны терминала одновременно с двух свойств //--- (кнопка Авто-торговля в терминале и галочка "Разрешить автоматическую торговлю" в настройках советника) //--- Если любое из двух свойств имеет значение false, то флаг будет иметь значение false bool terminal_trade_allowed=::TerminalInfoInteger(TERMINAL_TRADE_ALLOWED); terminal_trade_allowed &=::MQLInfoInteger(MQL_TRADE_ALLOWED); //--- если ошибка - запрет торговли со стороны терминала, и её причина устранена if(req_obj.Retcode()==10027 && terminal_trade_allowed) { //--- если текущая попытка ещё не превысила установленное количество торговых попыток if(req_obj.CurrentAttempt()<req_obj.TotalAttempts()+1) { //--- Установим время создания запроса равным времени его создания минус время ожидания - т.е., отослать запрос немедленно //--- И уменьшим номер текущей попытки, так как при последующем запросе номер попытки будет увеличен, и если это последняя попытка, //--- то она уже исполнена не будет. Но это исправление причины ошибки пользователем, а значит - нужно дать ещё время на последнюю попытку req_obj.SetTimeCreate(req_obj.TimeCreate()-req_obj.WaitingMSC()); req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()>0 ? req_obj.CurrentAttempt()-1 : 0)); } } //--- если текущая попытка превысила установленное количество торговых попыток, //--- или текущее время больше времени ожидания всех попыток //--- удаляем текущий объект-запрос и переходим к следующему (уходим из метода во внешний цикл) if(req_obj.CurrentAttempt()>req_obj.TotalAttempts() || req_obj.CurrentAttempt()>=UCHAR_MAX || (long)symbol_obj.Time()>long(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.TotalAttempts()+1))) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DELETED)); this.m_list_request.Delete(index); return; } //--- Проверяем актуальность отложенного запроса и уходим во внешний цикл если запрос отработан или произошла ошибка if(!this.CheckPReqRelevance(req_obj,request,index)) return; //--- Устанавливаем время активации запроса в объекте-запросе req_obj.SetTimeActivate(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.CurrentAttempt()+1)); //--- Если текущее время меньше времени активации запроса, //--- значит время запроса не наступило - идём к следующему запросу в списке (уходим из метода во внешний цикл) if((long)symbol_obj.Time()<(long)req_obj.TimeActivate()) return; //--- Устанавливаем номер попытки в объекте-запросе req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1)); //--- Выводим в журнал номер торговой попытки if(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_RE_TRY_N)+(string)req_obj.CurrentAttempt()+":"); req_obj.PrintShort(); } //--- В зависимости от типа выполняемого действия в торговом запросе switch(request.action) { //--- Открытие/закрытие позиции case TRADE_ACTION_DEAL : //--- Если тикета нет в структуре запроса - это открытие позиции if(request.position==0) this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling); //--- Если тикет есть в структуре запроса - это закрытие позиции else this.ClosePosition(request.position,request.volume,request.comment,request.deviation); break; //--- Модификация StopLoss/TakeProfit позиции case TRADE_ACTION_SLTP : this.ModifyPosition(request.position,request.sl,request.tp); break; //--- Закрытие встречным case TRADE_ACTION_CLOSE_BY : this.ClosePositionBy(request.position,request.position_by); break; //--- //--- Установка отложенного ордера case TRADE_ACTION_PENDING : this.PlaceOrder(request.type,request.volume,request.symbol,request.price,request.stoplimit,request.sl,request.tp,request.magic,request.comment,request.expiration,request.type_time,request.type_filling); break; //--- Модификация отложенного ордера case TRADE_ACTION_MODIFY : this.ModifyOrder(request.order,request.price,request.sl,request.tp,request.stoplimit,request.expiration,request.type_time,request.type_filling); break; //--- Удаление отложенного ордера case TRADE_ACTION_REMOVE : this.DeleteOrder(request.order); break; //--- default: break; } } //+------------------------------------------------------------------+
Этот код также рассматривалмя нами в составе таймера класса управления
торговлей, и отличается лишь тем, что обработка проверки срабатывания
запроса теперь вынесена в вызов соответствующего метода.
Обработчик отложенных запросов, созданных по запросу:
//+------------------------------------------------------------------+ //| Обработчик отложенных запросов, созданных по запросу | //+------------------------------------------------------------------+ void CTradingControl::OnPReqByRequestHandler(CPendRequest *req_obj,const int index) { //--- получаем структуру запроса и из неё объект-символ, на котором должна быть проведена торговая операция MqlTradeRequest request=req_obj.MqlRequest(); CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol); if(symbol_obj==NULL || !symbol_obj.RefreshRates()) return; //--- Проверяем актуальность отложенного запроса и уходим во внешний цикл если запрос отработан или произошла ошибка if(!this.CheckPReqRelevance(req_obj,request,index)) return; //--- Обновляем актуальные данные условий активации запросов this.RefreshControlActualDatas(req_obj,symbol_obj); //--- Если все условия срабатывания отложенного запроса исполнены if(req_obj.IsAllComparisonCompleted()) { //--- Устанавливаем номер попытки в объекте-запросе req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1)); //--- Выводим в журнал сообщение о срабатывании запроса if(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_REQUEST_ACTIVATED)+(string)req_obj.ID()+":"); req_obj.PrintShort(); } //--- В зависимости от типа выполняемого действия в торговом запросе switch(request.action) { //--- Открытие/закрытие позиции case TRADE_ACTION_DEAL : //--- Если тикета нет в структуре запроса - это открытие позиции if(request.position==0) this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling); //--- Если тикет есть в структуре запроса - это закрытие позиции else this.ClosePosition(request.position,request.volume,request.comment,request.deviation); break; //--- Модификация StopLoss/TakeProfit позиции case TRADE_ACTION_SLTP : this.ModifyPosition(request.position,request.sl,request.tp); break; //--- Закрытие встречным case TRADE_ACTION_CLOSE_BY : this.ClosePositionBy(request.position,request.position_by); break; //--- //--- Установка отложенного ордера case TRADE_ACTION_PENDING : this.PlaceOrder(request.type,request.volume,request.symbol,request.price,request.stoplimit,request.sl,request.tp,request.magic,request.comment,request.expiration,request.type_time,request.type_filling); break; //--- Модификация отложенного ордера case TRADE_ACTION_MODIFY : this.ModifyOrder(request.order,request.price,request.sl,request.tp,request.stoplimit,request.expiration,request.type_time,request.type_filling); break; //--- Удаление отложенного ордера case TRADE_ACTION_REMOVE : this.DeleteOrder(request.order); break; //--- default: break; } } } //+------------------------------------------------------------------+
Данный метод чуть проще предыдущего, так как для него требуется лишь проверка его срабатывания, и проверка наступления момента отсылки
торгового приказа (срабатывание условий активации отложенного запроса).
В нём точно так же вызывается метод проверки срабатывания запроса, а
далее уже контролируется срабатывание условий активации отложенного запроса, которые прописаны в его параметрах, и
при подтверждении факта срабатывания всех условий, отсылается
торговый приказ на сервер.
Метод, обновляющий актуальные значения контролируемых свойств в объектах-отложенных запросах:
//+------------------------------------------------------------------+ //| Обновляет актуальные значения контролируемых свойств | //| в объектах-отложенных запросах | //+------------------------------------------------------------------+ void CTradingControl::RefreshControlActualDatas(CPendRequest *req_obj,const CSymbol *symbol) { //--- Если объект-запрос имеет тип запроса, созданного по коду ошибки - уходим if(req_obj.GetProperty(PEND_REQ_PROP_TYPE)==PEND_REQ_TYPE_ERROR) return; double res=EMPTY_VALUE; //--- В цикле по всем условиям активации объекта-запроса uint total=req_obj.GetActivationCriterionTotal(); for(uint i=0;i<total;i++) { //--- получаем источник активации ENUM_PEND_REQ_ACTIVATION_SOURCE source=req_obj.GetActivationSource(i); //--- получаем текущее значение контролируемого свойства double value=req_obj.GetActivationActualValue(i),actual=EMPTY_VALUE; //--- В зависимости от источника активации //--- записываем текущее значение контролируемого свойства в массив данных условий активации switch((int)source) { //--- Аккаунт case PEND_REQ_ACTIVATION_SOURCE_ACCOUNT : actual=this.GetActualDataAccount(req_obj.GetActivationProperty(i)); req_obj.SetActivationActualValue(i,(actual!=EMPTY_VALUE ? actual : value)); break; //--- Символ case PEND_REQ_ACTIVATION_SOURCE_SYMBOL : actual=this.GetActualDataSymbol(req_obj.GetActivationProperty(i),symbol); req_obj.SetActivationActualValue(i,(actual!=EMPTY_VALUE ? actual : value)); break; //--- Событие case PEND_REQ_ACTIVATION_SOURCE_EVENT : actual=this.GetActualDataEvent(req_obj.GetActivationProperty(i)); req_obj.SetActivationActualValue(i,(actual!=EMPTY_VALUE ? actual : value)); break; //--- По умолчанию EMPTY_VALUE default: break; } } } //+------------------------------------------------------------------+
В методе получаем размер массива данных условий активации, проходим в цикле по всем условиям, в зависимости от источника условий активации, получаем фактические (текущие) данные из соответствующих коллекций и записываем их обратно в массив данных условий активации объекта-отложенного запроса.
Метод, возвращающий актуальные данные аккаунта:
//+------------------------------------------------------------------+ //| Возвращает актуальные данные аккаунта для контроля активации | //+------------------------------------------------------------------+ double CTradingControl::GetActualDataAccount(const int property) { switch(property) { case PEND_REQ_ACTIVATE_BY_ACCOUNT_LEVERAGE : return (double)this.m_account.Leverage(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_LIMIT_ORDERS : return (double)this.m_account.LimitOrders(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_ALLOWED : return (double)this.m_account.TradeAllowed(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_EXPERT : return (double)this.m_account.TradeExpert(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_BALANCE : return this.m_account.Balance(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_CREDIT : return this.m_account.Credit(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_PROFIT : return this.m_account.Profit(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_EQUITY : return this.m_account.Equity(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN : return this.m_account.Margin(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_FREE : return this.m_account.MarginFree(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_LEVEL : return this.m_account.MarginLevel(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_INITIAL : return this.m_account.MarginInitial(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_MAINTENANCE : return this.m_account.MarginMaintenance(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_ASSETS : return this.m_account.Assets(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_LIABILITIES : return this.m_account.Liabilities(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_COMMISSION_BLOCKED : return this.m_account.ComissionBlocked(); default: return EMPTY_VALUE; } } //+------------------------------------------------------------------+
В зависимости от типа условия, в соответствии с перечислением типов условий аккаунта, возвращаем значение соответствующего свойства объекта-аккаунта.
Метод, возвращающий актуальные данные символа:
//+------------------------------------------------------------------+ //| Возвращает актуальные данные символа для контроля активации | //+------------------------------------------------------------------+ double CTradingControl::GetActualDataSymbol(const int property,const CSymbol *symbol) { switch(property) { case PEND_REQ_ACTIVATE_BY_SYMBOL_BID : return symbol.Bid(); case PEND_REQ_ACTIVATE_BY_SYMBOL_ASK : return symbol.Ask(); case PEND_REQ_ACTIVATE_BY_SYMBOL_LAST : return symbol.Last(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_DEALS : return (double)symbol.SessionDeals(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS : return (double)symbol.SessionBuyOrders(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS : return (double)symbol.SessionSellOrders(); case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME : return (double)symbol.Volume(); case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH : return (double)symbol.VolumeHigh(); case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW : return (double)symbol.VolumeLow(); case PEND_REQ_ACTIVATE_BY_SYMBOL_TIME : return (double)symbol.Time()/1000; case PEND_REQ_ACTIVATE_BY_SYMBOL_SPREAD : return symbol.Spread(); case PEND_REQ_ACTIVATE_BY_SYMBOL_START_TIME : return (double)symbol.StartTime(); case PEND_REQ_ACTIVATE_BY_SYMBOL_EXPIRATION_TIME : return (double)symbol.ExpirationTime(); case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_STOPS_LEVEL : return symbol.TradeStopLevel(); case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FREEZE_LEVEL : return symbol.TradeFreezeLevel(); case PEND_REQ_ACTIVATE_BY_SYMBOL_BIDHIGH : return symbol.BidHigh(); case PEND_REQ_ACTIVATE_BY_SYMBOL_BIDLOW : return symbol.BidLow(); case PEND_REQ_ACTIVATE_BY_SYMBOL_ASKHIGH : return symbol.AskHigh(); case PEND_REQ_ACTIVATE_BY_SYMBOL_ASKLOW : return symbol.AskLow(); case PEND_REQ_ACTIVATE_BY_SYMBOL_LASTHIGH : return symbol.LastHigh(); case PEND_REQ_ACTIVATE_BY_SYMBOL_LASTLOW : return symbol.LastLow(); case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME_REAL : return symbol.VolumeReal(); case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH_REAL : return symbol.VolumeHighReal(); case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW_REAL : return symbol.VolumeLowReal(); case PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE : return symbol.OptionStrike(); case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_ACCRUED_INTEREST : return symbol.TradeAccuredInterest(); case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FACE_VALUE : return symbol.TradeFaceValue(); case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_LIQUIDITY_RATE : return symbol.TradeLiquidityRate(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_LONG : return symbol.SwapLong(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_SHORT : return symbol.SwapShort(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_VOLUME : return symbol.SessionVolume(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_TURNOVER : return symbol.SessionTurnover(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_INTEREST : return symbol.SessionInterest(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME : return symbol.SessionBuyOrdersVolume(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME : return symbol.SessionSellOrdersVolume(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_OPEN : return symbol.SessionOpen(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_CLOSE : return symbol.SessionClose(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_AW : return symbol.SessionAW(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_SETTLEMENT : return symbol.SessionPriceSettlement(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN : return symbol.SessionPriceLimitMin(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX : return symbol.SessionPriceLimitMax(); default: return EMPTY_VALUE; } } //+------------------------------------------------------------------+
В зависимости от типа условия, в соответствии с перечислением типов условий символа, возвращаем значение соответствующего свойства объекта-символа, указатель на который передан в метод.
Метод, возвращающий актуальные данные события:
//+------------------------------------------------------------------+ //| Возвращает актуальные данные событий для контроля активации | //+------------------------------------------------------------------+ double CTradingControl::GetActualDataEvent(const int property) { if(this.m_events.IsEvent()) { ENUM_TRADE_EVENT event=this.m_events.GetLastTradeEvent(); switch(property) { case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED : return event==TRADE_EVENT_POSITION_OPENED; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED : return event==TRADE_EVENT_POSITION_CLOSED; case PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_PLASED : return event==TRADE_EVENT_PENDING_ORDER_PLASED; case PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_REMOVED : return event==TRADE_EVENT_PENDING_ORDER_REMOVED; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CREDIT : return event==TRADE_EVENT_ACCOUNT_CREDIT; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CHARGE : return event==TRADE_EVENT_ACCOUNT_CHARGE; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CORRECTION : return event==TRADE_EVENT_ACCOUNT_CORRECTION; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BONUS : return event==TRADE_EVENT_ACCOUNT_BONUS; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION : return event==TRADE_EVENT_ACCOUNT_COMISSION; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_DAILY : return event==TRADE_EVENT_ACCOUNT_COMISSION_DAILY; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_MONTHLY : return event==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_DAILY : return event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY : return event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_INTEREST : return event==TRADE_EVENT_ACCOUNT_INTEREST; case PEND_REQ_ACTIVATE_BY_EVENT_BUY_CANCELLED : return event==TRADE_EVENT_BUY_CANCELLED; case PEND_REQ_ACTIVATE_BY_EVENT_SELL_CANCELLED : return event==TRADE_EVENT_SELL_CANCELLED; case PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT : return event==TRADE_EVENT_DIVIDENT; case PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT_FRANKED : return event==TRADE_EVENT_DIVIDENT_FRANKED; case PEND_REQ_ACTIVATE_BY_EVENT_TAX : return event==TRADE_EVENT_TAX; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_REFILL : return event==TRADE_EVENT_ACCOUNT_BALANCE_REFILL; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_WITHDRAWAL : return event==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL; case PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED : return event==TRADE_EVENT_PENDING_ORDER_ACTIVATED; case PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL : return event==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED_PARTIAL : return event==TRADE_EVENT_POSITION_OPENED_PARTIAL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL : return event==TRADE_EVENT_POSITION_CLOSED_PARTIAL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_POS : return event==TRADE_EVENT_POSITION_CLOSED_BY_POS; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_POS : return event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_SL : return event==TRADE_EVENT_POSITION_CLOSED_BY_SL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_TP : return event==TRADE_EVENT_POSITION_CLOSED_BY_TP; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_SL : return event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_TP : return event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET : return event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING : return event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL : return event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_MARKET : return event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_PENDING : return event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE : return event==TRADE_EVENT_MODIFY_ORDER_PRICE; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL : return event==TRADE_EVENT_MODIFY_ORDER_PRICE_SL; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_TP : return event==TRADE_EVENT_MODIFY_ORDER_PRICE_TP; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL_TP : return event==TRADE_EVENT_MODIFY_ORDER_PRICE_SL_TP; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL_TP : return event==TRADE_EVENT_MODIFY_ORDER_SL_TP; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL : return event==TRADE_EVENT_MODIFY_ORDER_SL; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_TP : return event==TRADE_EVENT_MODIFY_ORDER_TP; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL_TP : return event==TRADE_EVENT_MODIFY_POSITION_SL_TP; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL : return event==TRADE_EVENT_MODIFY_POSITION_SL; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_TP : return event==TRADE_EVENT_MODIFY_POSITION_TP; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_MARKET_PARTIAL : return event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_PENDING_PARTIAL : return event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL; case PEND_REQ_ACTIVATE_BY_EVENT_TRIGGERED_STOP_LIMIT_ORDER : return event==TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL : return event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL; default: return EMPTY_VALUE; } } return EMPTY_VALUE; } //+------------------------------------------------------------------+
В зависимости от типа условия, и если на счёте на данный момент времени зарегестрирован факт нового события, получаем последнее событие на счёте, и в соответствии с перечислением типов условий события, возвращаем флаг равенства последнего события значению, контролируемому в объекте-отложенном запросе (возвращаем флаг произошедшего контролируемого события).
Таймер класса теперь стал намного компактнее:
//+------------------------------------------------------------------+ //| Таймер | //+------------------------------------------------------------------+ void CTradingControl::OnTimer(void) { //--- В цикле по списку отложенных запросов int total=this.m_list_request.Total(); for(int i=total-1;i>WRONG_VALUE;i--) { //--- получаем очередной объект-запрос CPendRequest *req_obj=this.m_list_request.At(i); if(req_obj==NULL) continue; //--- Если объект-запрос создан по коду ошибки - обрабатываем обработчиком объектов, созданных по коду ошибки if(req_obj.TypeRequest()==PEND_REQ_TYPE_ERROR) this.OnPReqByErrCodeHandler(req_obj,i); //--- Иначе - это объект, созданный по запросу - обрабатываем обработчиком объектов, созданных по запросу else this.OnPReqByRequestHandler(req_obj,i); } } //+------------------------------------------------------------------+
Теперь в таймере класса только проверяем тип объекта-запроса, полученного из списка объектов-отложенных запросов, и в зависимости от его типа, вызываем соответствующий обработчик отложенных запросов, которые мы рассмотрели выше.
Если не вдаваться в детали, то это все доработки класса управления торговлей на данный момент.
В любом
случае, всегда можно ознакомиться с полным кодом в прикреплённых к статье файлах
Внесём дополнения в публичную секцию класса основного объекта библиотеки CEngine.
Чтобы иметь возможность получать значения уровня логирования торговых объектов для вывода из них сообщений из программы, созданной на
основе библиотеки,
добавим метод получения уровня логирования торгового объекта по символу:
void TradingSetTotalTry(const uchar attempts) { this.m_trading.SetTotalTry(attempts); } //--- Возвращает уровень логирования торгового объекта символа торгового класса ENUM_LOG_LEVEL TradingGetLogLevel(const string symbol_name) { return this.m_trading.GetTradeObjLogLevel(symbol_name); } //--- Устанавливает стандартные звуки (symbol==NULL) торговому объекту символа, (symbol!=NULL) торговым объектам всех символов
Метод возвращает результат работы метода класса управления торговлей GetTradeObjLogLevel().
Объявим методы создания отложенного запроса на открытие позиции Buy, на
открытие позиции Sell, метод установки нового условия активации
отложенного запроса, и напишем метод, возвращающий указатель на
объект-отложенный запрос по его идентификатору:
//--- Удаляет отложенный ордер bool DeleteOrder(const ulong ticket); //--- Создаёт отложенный запрос (1) на открытие позиции Buy, (2) на открытие позиции Sell template<typename SL,typename TP> int OpenBuyPending(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename SL,typename TP> int OpenSellPending(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Устанавливает критерии активации отложенного запроса bool SetNewActivationProperties(const uchar id, const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value); //--- Возвращает указатель на объект-запрос по его идентификатору в списке CPendRequest *GetPendRequestByID(const uchar id) { return this.m_trading.GetPendRequestByID(id); } //--- Возвращает (1) милисекунды, (2) причину, (3) источник события из его long-значения
Метод GetPendRequestByID(), возвращающий указатель на объект-отложенный запрос, возвращает результат работы
одноимённого метода класса управления торговлей.
Реализация метода создания отложенного запроса на открытие позиции Buy:
//+------------------------------------------------------------------+ //| Создаёт отложенный запрос на открытие позиции Buy | //+------------------------------------------------------------------+ template<typename SL,typename TP> int CEngine::OpenBuyPending(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.OpenPositionPending(POSITION_TYPE_BUY,volume,symbol,magic,sl,tp,group_id1,group_id2,comment,deviation,type_filling); } //+------------------------------------------------------------------+
Метод вызывает рассмотренный выше метод создания отложенного запроса на открытие позиции класса управления торговлей. В качестве типа
открываемой позиции передаём в метод константу
POSITION_TYPE_BUY, и остальные, переданные в метод, параметры будущей позиции.
Реализация метода создания отложенного запроса на открытие позиции Sell:
//+------------------------------------------------------------------+ //| Создаёт отложенный запрос на открытие позиции Sell | //+------------------------------------------------------------------+ template<typename SL,typename TP> int CEngine::OpenSellPending(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.OpenPositionPending(POSITION_TYPE_SELL,volume,symbol,magic,sl,tp,group_id1,group_id2,comment,deviation,type_filling); } //+------------------------------------------------------------------+
Метод вызывает рассмотренный выше метод создания отложенного запроса на открытие позиции класса управления торговлей. В качестве типа открываемой позиции передаём в метод константу POSITION_TYPE_SELL, и остальные, переданные в метод, параметры будущей позиции.
Реализация метода установки в объекте-отложенном запросе нового условия активации:
//+------------------------------------------------------------------+ //| Устанавливает критерии активации отложенного запроса | //+------------------------------------------------------------------+ bool CEngine::SetNewActivationProperties(const uchar id, const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value) { return this.m_trading.SetNewActivationProperties(id,source,property,control_value,comparer_type,actual_value); } //+------------------------------------------------------------------+
Метод вызывает рассмотренный выше метод добавления нового условия активации в объект-отложенный запрос класса управления торговлей. И это
все доработки библиотеки на сегодня.
Тестирование
Для тестирования торговли отложенными запросами на открытие позиций возьмём советник из прошлой статьи и сохраним его в новой папке \MQL5\Experts\TestDoEasy\ Part31\ под новым именем TestDoEasyPart31.mq5.
Для проверки работы отложенных запросов по условиям мы сделаем дополнительные кнопки к торговой панели тестового советника. Кнопки будут обозначены как " P" — условие по цене, и как "T" — условие по времени. Отложенные запросы будут создаваться при нажатии на кнопку "Buy" или " Sell" при условии, что нажата одна из кнопок "P" или "T", или обе сразу. Если нажаты обе сразу, то это означает, что у отложенного запроса два условия активации — по значению цены и времени.
Также добавим два входных параметра для указания дистанции от текущей цены для задания контролируемой цены и количества баров текущего
таймфрейма для задания времени срабатывания запроса.
Таким образом, если нажата кнопка Buy и включена соответствующая ей кнопка "P", то от текущей цены будет отложена дистанция ниже текущей цены на количество пунктов, заданных в настройках, и это значение будет установлено как контрольное для срабатывания отложенного запроса — когда цена станет равна или ниже рассчитанной, отложенный запрос сработает.
Если включена кнопка "T", то к текущему времени будет добавлено время, рассчитанное как текущее время + время указанного количества баров текущего таймфрейма, и это время будет записано как контрольное для срабатывания отложенного запроса — когда текущее время станет равно или больше рассчитанного, отложенный запрос сработает.Если нажаты обе кнопки "P" и "T", то для срабатывания отложенного запроса должны быть удовлетворены оба условия
одновременно.
Для открытия позиции Sell, контролируемая цена будет рассчитываться как текущая цена + указанное количество пунктов в
настройках. Т.е. для срабатывания отложенного запроса текущая цена должна стать выше той, которая была на момент создания отложенного
запроса (на момент нажатия кнопки "
Sell").
В блок входных параметров советника добавим дистанцию отступа
контрольного значения цены активации запроса от текущей цены на момент создания запроса и количество
баров задержки для задания времени активации отложенного запроса:
//--- input variables input ushort InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 150; // StopLoss in points input uint InpTakeProfit = 150; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpDistancePReq = 50; // Distance for Pending Request's activate (points) input uint InpBarsDelayPReq = 5; // Bars delay for Pending Request's activate (current timeframe) input uint InpSlippage = 5; // Slippage in points input uint InpSpreadMultiplier = 1; // Spread multiplier for adjusting stop-orders by StopLevel input uchar InpTotalAttempts = 5; // Number of trading attempts sinput double InpWithdrawal = 10; // Withdrawal funds (in tester) sinput uint InpButtShiftX = 0; // Buttons X shift sinput uint InpButtShiftY = 10; // Buttons Y shift input uint InpTrailingStop = 50; // Trailing Stop (points) input uint InpTrailingStep = 20; // Trailing Step (points) input uint InpTrailingStart = 0; // Trailing Start (points) input uint InpStopLossModify = 20; // StopLoss for modification (points) input uint InpTakeProfitModify = 60; // TakeProfit for modification (points) sinput ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; // Mode of used symbols list sinput string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) sinput bool InpUseSounds = true; // Use sounds
В блок глобальных переменных советника добавим соответствующие переменные для хранения отступа цены активации и задержки в барах для задания времени активации отложенного запроса, а также флаги состояний кнопок отложенных запросов:
//--- global variables CEngine engine; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ushort magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint distance_pending_request; uint bars_delay_pending_request; uint slippage; bool trailing_on; bool pending_buy; bool pending_buy_limit; bool pending_buy_stop; bool pending_buy_stoplimit; bool pending_close_buy; bool pending_close_buy2; bool pending_close_buy_by_sell; bool pending_sell; bool pending_sell_limit; bool pending_sell_stop; bool pending_sell_stoplimit; bool pending_close_sell; bool pending_close_sell2; bool pending_close_sell_by_buy; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; int used_symbols_mode; string used_symbols; string array_used_symbols[]; bool testing; uchar group1; uchar group2; //+------------------------------------------------------------------+
В обработчике OnInit() советника присвоим переменным корректные значения
входных параметров и сбросим состояния кнопок отложенных запросов:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Вызов данной функции выводит в журнал список констант перечисления, //--- заданного в файле DELib.mqh в строках 22 и 25, для проверки корректности констант //EnumNumbersTest(); //--- Установка глобальных переменных советника prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_"; testing=engine.IsTester(); for(int i=0;i<TOTAL_BUTT;i++) { butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i); butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i); } lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0)); magic_number=InpMagic; stoploss=InpStopLoss; takeprofit=InpTakeProfit; distance_pending=InpDistance; distance_stoplimit=InpDistanceSL; slippage=InpSlippage; trailing_stop=InpTrailingStop*Point(); trailing_step=InpTrailingStep*Point(); trailing_start=InpTrailingStart; stoploss_to_modify=InpStopLossModify; takeprofit_to_modify=InpTakeProfitModify; distance_pending_request=(InpDistancePReq<5 ? 5 : InpDistancePReq); bars_delay_pending_request=(InpBarsDelayPReq<1 ? 1 : InpBarsDelayPReq); //--- Инициализация случайных номеров групп group1=0; group2=0; srand(GetTickCount()); //--- Инициализация библиотеки DoEasy OnInitDoEasy(); //--- Проверка и удаление неудалённых графических объектов советника if(IsPresentObects(prefix)) ObjectsDeleteAll(0,prefix); //--- Создание панели кнопок if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- Установка состояния кнопки активизации трейлингов ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on); //--- Сброс состояний кнопок активизации работы отложенными запросами for(int i=0;i<14;i++) { ButtonState(butt_data[i].name+"_PRICE",false); ButtonState(butt_data[i].name+"_TIME",false); } //--- Проверка воспроизведения стандартного звука по макроподстановке и пользовательского звука по описанию engine.PlaySoundByDescription(SND_OK); Sleep(600); engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","The sound of a falling coin 2")); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Для дистанции цены активации отложенного запроса расстояние меньше пяти пунктов приравниваем к пяти пунктам, а для задержки в барах задаём
минимальную задержку не менее одного бара текущего таймфрейма.
Кнопки включения работы отложенными запросами просто сбрасываем в неактивное состояние — советник тестовый, кнопки созданы лишь для
проверки, и отслеживать их состояние для его сохранения мы не будем.
В функции создания кнопок панели добавим переменную, хранящую ширину новых кнопок и в ещё одном цикле создадим новые кнопки включения торговли отложенными запросами:
//+------------------------------------------------------------------+ //| Создаёт панель кнопок | //+------------------------------------------------------------------+ bool CreateButtons(const int shift_x=20,const int shift_y=0) { int h=18,w=82,offset=2,wpt=14; int cx=offset+shift_x+wpt*2+2,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+3*h+1; int x=cx,y=cy; int shift=0; for(int i=0;i<TOTAL_BUTT;i++) { x=x+(i==7 ? w+2 : 0); if(i==TOTAL_BUTT-6) x=cx; y=(cy-(i-(i>6 ? 7 : 0))*(h+1)); if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-6 ? w : w*2+2),h,butt_data[i].text,(i<4 ? clrGreen : i>6 && i<11 ? clrRed : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text); return false; } } h=18; offset=2; cx=offset+shift_x; cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+3*h+1; x=cx; y=cy; shift=0; for(int i=0;i<14;i++) { y=(cy-(i-(i>6 ? 7 : 0))*(h+1)); if(!ButtonCreate(butt_data[i].name+"_PRICE",((i>6 && i<11) || i>10 ? x+wpt*2+w*2+5 : x),y,wpt,h,"P",(i<4 ? clrGreen : i>6 && i<11 ? clrChocolate : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text+" \"P\""); return false; } if(!ButtonCreate(butt_data[i].name+"_TIME",((i>6 && i<11) || i>10 ? x+wpt*2+w*2+5+wpt+1 : x+wpt+1),y,wpt,h,"T",(i<4 ? clrGreen : i>6 && i<11 ? clrChocolate : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text+" \"T\""); return false; } } ChartRedraw(0); return true; } //+------------------------------------------------------------------+
В функции установки состояния кнопок (цвета активной кнопки) добавим
установку цвета активных кнопок торговли отложенными запросами:
//+------------------------------------------------------------------+ //| Устанавливает состояние кнопки | //+------------------------------------------------------------------+ void ButtonState(const string name,const bool state) { ObjectSetInteger(0,name,OBJPROP_STATE,state); //--- Кнопка активации трейлинга if(name==butt_data[TOTAL_BUTT-1].name) { if(state) ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'220,255,240'); else ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'240,240,240'); } //--- Кнопки активации работы отложенными запросами if(StringFind(name,"_PRICE")>0 || StringFind(name,"_TIME")>0) { if(state) ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'255,220,90'); else ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'240,240,240'); } } //+------------------------------------------------------------------+
В функцию обработки нажатий кнопок добавим коды обработки нажатий кнопок работы отложенными запросами:
//+------------------------------------------------------------------+ //| Обработка нажатий кнопок | //+------------------------------------------------------------------+ void PressButtonEvents(const string button_name) { bool comp_magic=true; // Временная переменная для выбора использования составного магика со случайными идентификаторами групп string comment=""; //--- Преобразуем имя кнопки в её строковый идентификатор string button=StringSubstr(button_name,StringLen(prefix)); //--- Случайные номера групп 1 и 2 в диапазоне 0 - 15 group1=(uchar)Rand(); group2=(uchar)Rand(); uint magic=(comp_magic ? engine.SetCompositeMagicNumber(magic_number,group1,group2) : magic_number); //--- Если кнопка в нажатом состоянии if(ButtonState(button_name)) { //--- Если нажата кнопка BUTT_BUY: Открыть позицию Buy if(button==EnumToString(BUTT_BUY)) { //--- Если не нажаты кнопки создания отложенного запроса - открываем позицию Buy if(!pending_buy) engine.OpenBuy(lot,Symbol(),magic,stoploss,takeprofit); // Нет комментария - будет установлен комментарий по умолчанию //--- Иначе - создаём отложенный запрос на открытие позиции Buy else { int id=engine.OpenBuyPending(lot,Symbol(),magic,stoploss,takeprofit); if(id>0) { //--- Если выбран критерий по цене if(ButtonState(prefix+EnumToString(BUTT_BUY)+"_PRICE")) { double ask=SymbolInfoDouble(NULL,SYMBOL_ASK); double control_value=NormalizeDouble(ask-distance_pending_request*SymbolInfoDouble(NULL,SYMBOL_POINT),(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS)); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_ASK,control_value,EQUAL_OR_LESS,ask); } //--- Если выбран критерий по времени if(ButtonState(prefix+EnumToString(BUTT_BUY)+"_TIME")) { ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent()); } } CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id); if(req_obj==NULL) return; if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":"); req_obj.PrintActivations(); } } } //--- Если нажата кнопка BUTT_BUY_LIMIT: Выставить BuyLimit else if(button==EnumToString(BUTT_BUY_LIMIT)) { //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер BuyLimit if(!pending_buy_limit) engine.PlaceBuyLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyLimit","Pending order BuyLimit")); //--- Иначе - в данной редакции ничего не делаем else { } } //--- Если нажата кнопка BUTT_BUY_STOP: Выставить BuyStop else if(button==EnumToString(BUTT_BUY_STOP)) { //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер BuyStop if(!pending_buy_stop) engine.PlaceBuyStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStop","Pending order BuyStop")); //--- Иначе - в данной редакции ничего не делаем else { } } //--- Если нажата кнопка BUTT_BUY_STOP_LIMIT: Выставить BuyStopLimit else if(button==EnumToString(BUTT_BUY_STOP_LIMIT)) { //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер BuyStopLimit if(!pending_buy_stoplimit) engine.PlaceBuyStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStopLimit","Pending order BuyStopLimit")); //--- Иначе - в данной редакции ничего не делаем else { } } //--- Если нажата кнопка BUTT_SELL: Открыть позицию Sell else if(button==EnumToString(BUTT_SELL)) { //--- Если не нажаты кнопки создания отложенного запроса - открываем позицию Sell if(!pending_sell) engine.OpenSell(lot,Symbol(),magic,stoploss,takeprofit); // Нет комментария - будет установлен комментарий по умолчанию //--- Иначе - создаём отложенный запрос на открытие позиции Sell else { int id=engine.OpenSellPending(lot,Symbol(),magic,stoploss,takeprofit); if(id>0) { //--- Если выбран критерий по цене if(ButtonState(prefix+EnumToString(BUTT_SELL)+"_PRICE")) { double bid=SymbolInfoDouble(NULL,SYMBOL_BID); double control_value=NormalizeDouble(bid+distance_pending_request*SymbolInfoDouble(NULL,SYMBOL_POINT),(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS)); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_BID,control_value,EQUAL_OR_MORE,bid); } //--- Если выбран критерий по времени if(ButtonState(prefix+EnumToString(BUTT_SELL)+"_TIME")) { ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent()); } } CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id); if(req_obj==NULL) return; if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":"); req_obj.PrintActivations(); } } } //--- Если нажата кнопка BUTT_SELL_LIMIT: Выставить SellLimit else if(button==EnumToString(BUTT_SELL_LIMIT)) { //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер SellLimit if(!pending_sell_limit) engine.PlaceSellLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellLimit","Pending order SellLimit")); //--- Иначе - в данной редакции ничего не делаем else { } } //--- Если нажата кнопка BUTT_SELL_STOP: Выставить SellStop else if(button==EnumToString(BUTT_SELL_STOP)) { //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер SellStop if(!pending_sell_stop) engine.PlaceSellStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStop","Pending order SellStop")); //--- Иначе - в данной редакции ничего не делаем else { } } //--- Если нажата кнопка BUTT_SELL_STOP_LIMIT: Выставить SellStopLimit else if(button==EnumToString(BUTT_SELL_STOP_LIMIT)) { //--- Если не нажаты кнопки создания отложенного запроса - устанавливаем ордер SellStopLimit if(!pending_sell_stoplimit) engine.PlaceSellStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStopLimit","Pending order SellStopLimit")); //--- Иначе - в данной редакции ничего не делаем else { } } //--- Если нажата кнопка BUTT_CLOSE_BUY: Закрыть Buy с максимальной прибылью else if(button==EnumToString(BUTT_CLOSE_BUY)) { //--- Получаем список всех открытых позиций CArrayObj* list=engine.GetListMarketPosition(); //--- Выбираем из списка только позиции Buy и только по текущему символу list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); 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) { //--- Получаем объект-позицию Buy и закрываем позицию по тикету COrder* position=list.At(index); if(position!=NULL) engine.ClosePosition((ulong)position.Ticket()); } } //--- Если нажата кнопка BUTT_CLOSE_BUY2: Закрыть половину Buy с максимальной прибылью else if(button==EnumToString(BUTT_CLOSE_BUY2)) { //--- Получаем список всех открытых позиций CArrayObj* list=engine.GetListMarketPosition(); //--- Выбираем из списка только позиции Buy и только по текущему символу list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); 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); //--- Закрываем частично позицию Buy if(position!=NULL) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); } } //--- Если нажата кнопка BUTT_CLOSE_BUY_BY_SELL: Закрыть Buy с максимальной прибылью встречной Sell с максимальной прибылью else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)) { //--- Если счёт хеджевый if(engine.IsHedge()) { CArrayObj *list_buy=NULL, *list_sell=NULL; //--- Получаем список всех открытых позиций CArrayObj* list=engine.GetListMarketPosition(); if(list==NULL) return; //--- Выбираем из списка только позиции по текущему символу list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); //--- Выбираем из списка только позиции Buy list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); if(list_buy==NULL) return; //--- Сортируем список по прибыли с учётом комиссии и свопа list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Получаем индекс позиции Buy с наибольшей прибылью int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); //--- Выбираем из списка только позиции Sell list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); if(list_sell==NULL) return; //--- Сортируем список по прибыли с учётом комиссии и свопа 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); //--- Закрываем позицию Buy встречной позицией Sell if(position_buy!=NULL && position_sell!=NULL) engine.ClosePositionBy((ulong)position_buy.Ticket(),(ulong)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_SYMBOL,Symbol(),EQUAL); 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) { //--- Получаем объект-позицию Sell и закрываем позицию по тикету COrder* position=list.At(index); if(position!=NULL) engine.ClosePosition((ulong)position.Ticket()); } } //--- Если нажата кнопка BUTT_CLOSE_SELL2: Закрыть половину Sell с максимальной прибылью else if(button==EnumToString(BUTT_CLOSE_SELL2)) { //--- Получаем список всех открытых позиций CArrayObj* list=engine.GetListMarketPosition(); //--- Выбираем из списка только позиции Sell и только по текущему символу list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); 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); //--- Закрываем частично позицию Sell if(position!=NULL) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); } } //--- Если нажата кнопка BUTT_CLOSE_SELL_BY_BUY: Закрыть Sell с максимальной прибылью встречной Buy с максимальной прибылью else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)) { //--- Если счёт хеджевый if(engine.IsHedge()) { CArrayObj *list_buy=NULL, *list_sell=NULL; //--- Получаем список всех открытых позиций CArrayObj* list=engine.GetListMarketPosition(); if(list==NULL) return; //--- Выбираем из списка только позиции по текущему символу list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); //--- Выбираем из списка только позиции Sell list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); if(list_sell==NULL) return; //--- Сортируем список по прибыли с учётом комиссии и свопа list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Получаем индекс позиции Sell с наибольшей прибылью int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); //--- Выбираем из списка только позиции Buy list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); if(list_buy==NULL) return; //--- Сортируем список по прибыли с учётом комиссии и свопа 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); //--- Закрываем позицию Sell встречной позицией Buy if(position_sell!=NULL && position_buy!=NULL) engine.ClosePositionBy((ulong)position_sell.Ticket(),(ulong)position_buy.Ticket()); } } } //--- Если нажата кнопка BUTT_CLOSE_ALL: Закрыть все позиции, начиная от позиции с наименьшим профитом else if(button==EnumToString(BUTT_CLOSE_ALL)) { //--- Получаем список всех открытых позиций CArrayObj* list=engine.GetListMarketPosition(); //--- Выбираем из списка только позиции по текущему символу list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); 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; //--- закрываем каждую позицию по её тикету engine.ClosePosition((ulong)position.Ticket()); } } } //--- Если нажата кнопка BUTT_DELETE_PENDING: Удалить отложенные ордера, начиная с самого раннего else if(button==EnumToString(BUTT_DELETE_PENDING)) { //--- Получаем список всех ордеров CArrayObj* list=engine.GetListMarketPendings(); //--- Выбираем из списка только ордера по текущему символу list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); if(list!=NULL) { //--- Сортируем список по времени установки list.Sort(SORT_BY_ORDER_TIME_OPEN); int total=list.Total(); //--- В цикле от ордера с наибольшим временем for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; //--- удаяем ордер по его тикету engine.DeleteOrder((ulong)order.Ticket()); } } } //--- Если нажата кнопка BUTT_PROFIT_WITHDRAWAL: Вывести средства со счёта if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL)) { //--- Если программа запущена в тестере if(MQLInfoInteger(MQL_TESTER)) { //--- Эмулируем вывод средств TesterWithdrawal(withdrawal); } } //--- Если нажата кнопка BUTT_SET_STOP_LOSS: Установить StopLoss всем ордерам и позициям, где его нету if(button==EnumToString(BUTT_SET_STOP_LOSS)) { SetStopLoss(); } //--- Если нажата кнопка BUTT_SET_TAKE_PROFIT: Установить TakeProfit всем ордерам и позициям, где его нету if(button==EnumToString(BUTT_SET_TAKE_PROFIT)) { SetTakeProfit(); } //--- Подождём 1/10 секунды Sleep(100); //--- "Отожмём" кнопку (если это не кнопка трейлинга и не кнопки активации работы отложенными запросами) if(button!=EnumToString(BUTT_TRAILING_ALL) && StringFind(button,"_PRICE")<0 && StringFind(button,"_TIME")<0) ButtonState(button_name,false); //--- Если нажата кнопка BUTT_TRAILING_ALL или кнопки включения работы отложенными запросами else { //--- Поставим цвет активной кнопки для кнопки включения трейлинга if(button==EnumToString(BUTT_TRAILING_ALL)) { ButtonState(button_name,true); trailing_on=true; } //--- Покупки //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для открытия Buy по цене или времени if(button==EnumToString(BUTT_BUY)+"_PRICE" || button==EnumToString(BUTT_BUY)+"_TIME") { ButtonState(button_name,true); pending_buy=true; } //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки BuyLimit по цене или времени if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_LIMIT)+"_TIME") { ButtonState(button_name,true); pending_buy_limit=true; } //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки BuyStop по цене или времени if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP)+"_TIME") { ButtonState(button_name,true); pending_buy_stop=true; } //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки BuyStopLimit по цене или времени if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME") { ButtonState(button_name,true); pending_buy_stoplimit=true; } //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия Buy по цене или времени if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY)+"_TIME") { ButtonState(button_name,true); pending_close_buy=true; } //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия 1/2 Buy по цене или времени if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME") { ButtonState(button_name,true); pending_close_buy2=true; } //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия Buy встречной Sell по цене или времени if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME") { ButtonState(button_name,true); pending_close_buy_by_sell=true; } //--- Продажи //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для открытия Sell по цене или времени if(button==EnumToString(BUTT_SELL)+"_PRICE" || button==EnumToString(BUTT_SELL)+"_TIME") { ButtonState(button_name,true); pending_sell=true; } //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки SellLimit по цене или времени if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_LIMIT)+"_TIME") { ButtonState(button_name,true); pending_sell_limit=true; } //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки SellStop по цене или времени if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP)+"_TIME") { ButtonState(button_name,true); pending_sell_stop=true; } //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для установки SellStopLimit по цене или времени if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME") { ButtonState(button_name,true); pending_sell_stoplimit=true; } //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия Sell по цене или времени if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL)+"_TIME") { ButtonState(button_name,true); pending_close_sell=true; } //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия 1/2 Sell по цене или времени if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME") { ButtonState(button_name,true); pending_close_sell2=true; } //--- Поставим цвет активной кнопки для кнопки активации работы отложенными запросами для закрытия Sell встречной Buy по цене или времени if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME") { ButtonState(button_name,true); pending_close_sell_by_buy=true; } } //--- перерисуем чарт ChartRedraw(); } //--- Вернём цвет неактивным кнопкам else { //--- кнопка трейлинга if(button==EnumToString(BUTT_TRAILING_ALL)) { ButtonState(button_name,false); trailing_on=false; } //--- Покупки //--- кнопка активации работы отложенными запросами для открытия Buy по цене if(button==EnumToString(BUTT_BUY)+"_PRICE") { ButtonState(button_name,false); pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_TIME")); } //--- кнопка активации работы отложенными запросами для открытия Buy по времени if(button==EnumToString(BUTT_BUY)+"_TIME") { ButtonState(button_name,false); pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_PRICE")); } //--- кнопка активации работы отложенными запросами для установки BuyLimit по цене if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE") { ButtonState(button_name,false); pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_TIME")); } //--- кнопка активации работы отложенными запросами для установки BuyLimit по времени if(button==EnumToString(BUTT_BUY_LIMIT)+"_TIME") { ButtonState(button_name,false); pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_PRICE")); } //--- кнопка активации работы отложенными запросами для установки BuyStop по цене if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE") { ButtonState(button_name,false); pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_TIME")); } //--- кнопка активации работы отложенными запросами для установки BuyStop по времени if(button==EnumToString(BUTT_BUY_STOP)+"_TIME") { ButtonState(button_name,false); pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_PRICE")); } //--- кнопка активации работы отложенными запросами для установки BuyStopLimit по цене if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE") { ButtonState(button_name,false); pending_buy_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME")); } //--- кнопка активации работы отложенными запросами для установки BuyStopLimit по времени if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME") { ButtonState(button_name,false); pending_buy_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE")); } //--- кнопка активации работы отложенными запросами для закрытия Buy по цене if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE") { ButtonState(button_name,false); pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_TIME")); } //--- кнопка активации работы отложенными запросами для закрытия Buy по времени if(button==EnumToString(BUTT_CLOSE_BUY)+"_TIME") { ButtonState(button_name,false); pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_PRICE")); } //--- кнопка активации работы отложенными запросами для закрытия 1/2 Buy по цене if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE") { ButtonState(button_name,false); pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_TIME")); } //--- кнопка активации работы отложенными запросами для закрытия 1/2 Buy по времени if(button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME") { ButtonState(button_name,false); pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_PRICE")); } //--- кнопка активации работы отложенными запросами для закрытия Buy встречной Sell по цене if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE") { ButtonState(button_name,false); pending_close_buy_by_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME")); } //--- кнопка активации работы отложенными запросами для закрытия Buy встречной Sell по времени if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME") { ButtonState(button_name,false); pending_close_buy_by_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE")); } //--- Продажи //--- кнопка активации работы отложенными запросами для открытия Sell по цене if(button==EnumToString(BUTT_SELL)+"_PRICE") { ButtonState(button_name,false); pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_TIME")); } //--- кнопка активации работы отложенными запросами для открытия Sell по времени if(button==EnumToString(BUTT_SELL)+"_TIME") { ButtonState(button_name,false); pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_PRICE")); } //--- кнопка активации работы отложенными запросами для установки SellLimit по цене if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE") { ButtonState(button_name,false); pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_TIME")); } //--- кнопка активации работы отложенными запросами для установки SellLimit по времени if(button==EnumToString(BUTT_SELL_LIMIT)+"_TIME") { ButtonState(button_name,false); pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_PRICE")); } //--- кнопка активации работы отложенными запросами для установки SellStop по цене if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE") { ButtonState(button_name,false); pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_TIME")); } //--- кнопка активации работы отложенными запросами для установки SellStop по времени if(button==EnumToString(BUTT_SELL_STOP)+"_TIME") { ButtonState(button_name,false); pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_PRICE")); } //--- кнопка активации работы отложенными запросами для установки SellStopLimit по цене if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE") { ButtonState(button_name,false); pending_sell_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME")); } //--- кнопка активации работы отложенными запросами для установки SellStopLimit по времени if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME") { ButtonState(button_name,false); pending_sell_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE")); } //--- кнопка активации работы отложенными запросами для закрытия Sell по цене if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE") { ButtonState(button_name,false); pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_TIME")); } //--- кнопка активации работы отложенными запросами для закрытия Sell по времени if(button==EnumToString(BUTT_CLOSE_SELL)+"_TIME") { ButtonState(button_name,false); pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_PRICE")); } //--- кнопка активации работы отложенными запросами для закрытия 1/2 Sell по цене if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE") { ButtonState(button_name,false); pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_TIME")); } //--- кнопка активации работы отложенными запросами для закрытия 1/2 Sell по времени if(button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME") { ButtonState(button_name,false); pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_PRICE")); } //--- кнопка активации работы отложенными запросами для закрытия Sell встречной Buy по цене if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE") { ButtonState(button_name,false); pending_close_sell_by_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME")); } //--- кнопка активации работы отложенными запросами для закрытия Sell встречной Buy по времени if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME") { ButtonState(button_name,false); pending_close_sell_by_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE")); } //--- перерисуем чарт ChartRedraw(); } } //+------------------------------------------------------------------+
Функция достаночно объёмная, но код подробно прокомментирован, и, как мне кажется, не нуждается в пояснениях. В любом случае все вопросы можно задать в обсуждении к статье.
Скомпилируем советник. По умолчанию отступ цены для отложенного запроса равен 50 пунктов, а задержка в барах равна пяти барам. Оставим эти настройки
без изменений и запустим советник в тестере стратегий.
Включим кнопки активации отложенных запросов на открытие позиции Buy по цене и времени, после чего дождёмся срабатывания отложенных
запросов.
Затем включим кнопку активации отложенных запросов на открытие позиции Sell только по времени и так же дождёмся срабатывания
отложенного запроса:
Как видим из записей в журнале, отложенные запросы на покупку создаются, и им устанавливаются условия их активации. При достижении ценой и
временем заданных условий оба отложенных запроса срабатывают и объекты отложенных запросов удаляются по причине их активации.
Затем мы создаём отложенный запрос на продажу и он срабатывает по прошествии пяти баров, а далее по результату открытия позиции запрос
удаляется как исполненный.
Что дальше
В следующей статье продолжим развитие концепции торговли отложенными торговыми запросами и реализуем установку отложенных ордеров по
условию.
Ниже прикреплены все файлы текущей версии библиотеки и файлы тестового советника. Их можно скачать и протестировать всё
самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.
Статьи этой серии:
Часть 1. Концепция, организация данныхЧасть 2. Коллекция исторических ордеров и сделок
Часть 3. Коллекция рыночных ордеров и позиций, организация поиска
Часть 4. Торговые события. Концепция
Часть 5. Классы и коллекция торговых событий. Отправка событий в программу
Часть 6. События на счёте с типом неттинг
Часть 7. События срабатывания StopLimit-ордеров, подготовка функционала для регистрации событий модификации ордеров и позиций
Часть 8. События модификации ордеров и позиций
Часть 9. Совместимость с MQL4 — Подготовка данных
Часть 10. Совместимость с MQL4 — События открытия позиций и активации отложенных ордеров
Часть 11. Совместимость с MQL4 — События закрытия позиций
Часть 12. Класс объекта "аккаунт", коллекция объектов-аккаунтов
Часть 13. События объекта "аккаунт"
Часть 14. Объект "Символ"
Часть 15. Коллекция объектов-символов
Часть 16. События коллекции символов
Часть 17. Интерактивность объектов библиотеки
Часть 18. Интерактивность объекта-аккаунт и любых других объектов библиотеки
Часть 19. Класс сообщений библиотеки
Часть 20. Создание и хранение ресурсов программы
Часть 21. Торговые классы — Базовый кроссплатформенный торговый объект
Часть 22. Торговые классы — Основной торговый класс, контроль ограничений
Часть 23. Торговые классы — Основной торговый класс, контроль допустимых параметров
Часть 24. Торговые классы — Основной торговый класс, автоматическая коррекция ошибочных параметров
Часть 25. Торговые классы — Основной торговый класс, обработка ошибок, возвращаемых торговым сервером
Часть 26. Работа с отложенными торговыми запросами - первая реализация (открытие позиций)
Часть 27. Работа с отложенными торговыми запросами - выставление отложенных ордеров
Часть 28. Работа с отложенными торговыми запросами - закрытие, удаление, модификации
Часть 29. Работа с отложенными торговыми запросами - классы объектов-запросов
Часть 30. Работа с отложенными запросами - управление объектами-запросами
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования