Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXI): Торговые классы - Базовый кроссплатформенный торговый объект
Содержание
С этой статьи мы начинаем новую достаточно обширную тему — торговые классы.Концепция
Иметь в наличии множество разнообразных данных, получать к ним простой доступ в любое время — это очень хорошо. Но смысла в этих данных мало,
если мы не сможем на них реагировать по прямому назначению — торговать. Естественно, нам, наряду с имеющимся уже функционалом, необходим и
торговый функционал.
Данный раздел будет объёмным, и делать всё будем пошагово.
- Нам необходимо иметь возможность отправлять любые торговые приказы из любой платформы — будь то MetaTrader 5, будь то MetaTrader 4. При этом совершенно не задумываясь о том, в какой именно платформе мы отправляем торговый приказ — всё будет одинаково.
- Нам необходимо предварительно проверить корректность торговых запросов — чтобы не нагружать сервер заведомо ошибочными
запросами.
- Нам необходимо учитывать и правильно обрабатывать коды возврата торгового сервера. Ведь что делает советник отправляя приказ на сервер? Он ведёт диалог с сервером в виде запрос-ответ. И чтобы советник с сервером мог общаться — наша задача правильно организовать такой "канал общения", т.е., создать методы обработки ответов торгового сервера.
- Нам необходимо создать несколько вариантов обработки ответов сервера — ведь иногда нам необходимо открыть позицию "желательно
любой ценой". Для этого нужно организовать повтор отправки приказа на сервер в случае отказа в постановке ордера — мы можем либо
подкорректировать параметры торгового приказа и заново его послать, либо все параметры оставить неизменными, но дождаться
подходящего момента, при котором приказ с этими параметрами пройдёт, и сразу же его отослать. При всём при этом нам нужно будет
учитывать и уровень цены — чтобы не отсылать повторно ордер по заведомо худшей цене.
Но иногда нам требуется просто отослать торговый приказ, и независимо от результата запроса продолжить работу.
- Нам необходимо сделать работу с торговыми классами ещё и так, чтобы при размещении программы, созданной на основе библиотеки,
в mql5-маркет, не было никаких проблем — все проверки такая программа должна проходить без каких-либо осложнений.
Такие вот минимальные планы у нас на данный момент относительно торговых классов.
Сегодня же мы рассмотрим создание
базового торгового объекта — класса, отсылающего торговый запрос на сервер из любой платформы одинаково. Такой торговый объект будет
подразумевать при отправке запроса на сервер, что в него переданы уже проверенные и корректные параметры торгового запроса. Т.е.,
проверок корректности параметров у данного объекта не будет — они будут проверяться в другом — основном торговом классе, который будет
создан позже.
Справедливости ради стоит отметить, что выбор ордера или позиции по тикету пока сделаем в данном торговом объекте, а уже далее — при
создании основного торгового класса, мы эти проверки перенесём в него.
Так как вся торговля напрямую завязана на символ, то базовый торговый объект будет входить в состав объекта-символа, рассмотренного нами в
статье 14. А доступ к торговым объектам символов мы организуем позже — в основном торговом классе. Сегодня же мы сделаем временный
доступ к торговым объектам символов из базового класса библиотеки
CEngine, рассмотренного нами в статье 3 — ведь именно в
базовом классе аккумулируются все данные окружения, и именно там нам доступны любые свойства аккаунта и символа, необходимые для работы с
торговыми классами.
Создаём базовый торговый объект
Для логирования работы торговых классов нам потребуется создать перечисление уровней логирования в файле библиотеки Defines.mqh.
В самом конце листинга впишем требуемое перечисление:
//+------------------------------------------------------------------+ //| Данные для работы с торговыми классами | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Уровень логирования | //+------------------------------------------------------------------+ enum ENUM_LOG_LEVEL { LOG_LEVEL_NO_MSG, // Логирование торговли отключено LOG_LEVEL_ERROR_MSG, // Только торговые ошибки LOG_LEVEL_ALL_MSG // Полное логирование }; //+------------------------------------------------------------------+
Для вывода сообщений в журнал нам потребуются тексты сообщений и их индексы в списке сообщений библиотеки.
Впишем
нужные индексы в файл Datas.mqh:
MSG_LIB_SYS_NOT_SYMBOL_ON_SERVER, // Ошибка. Такого символа нет на сервере MSG_LIB_SYS_NOT_SYMBOL_ON_LIST, // Ошибка. Такого символа нет в списке используемых символов: MSG_LIB_SYS_FAILED_PUT_SYMBOL, // Не удалось поместить в обзор рынка. Ошибка:
MSG_LIB_SYS_ERROR_NOT_POSITION, // Ошибка. Не позиция: MSG_LIB_SYS_ERROR_NO_OPEN_POSITION_WITH_TICKET, // Ошибка. Нет открытой позиции с тикетом # MSG_LIB_SYS_ERROR_NO_PLACED_ORDER_WITH_TICKET, // Ошибка. Нет установленного ордера с тикетом # MSG_LIB_SYS_ERROR_FAILED_CLOSE_POS, // Не удалось закрыть позицию. Ошибка
MSG_LIB_SYS_ERROR_FAILED_MODIFY_ORD, // Не удалось модифицировать ордер. Ошибка MSG_LIB_SYS_ERROR_UNABLE_PLACE_WITHOUT_TIME_SPEC, // Ошибка: невозможно разместить ордер без явно заданного его времени истечения MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ, // Ошибка. Не удалось получить торговый объект MSG_LIB_SYS_ERROR_FAILED_GET_POS_OBJ, // Ошибка. Не удалось получить объект-позицию MSG_LIB_SYS_ERROR_FAILED_GET_ORD_OBJ, // Ошибка. Не удалось получить объект-ордер MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ, // Ошибка. Не удалось получить объект-символ MSG_LIB_SYS_ERROR_CODE_OUT_OF_RANGE, // Код возврата вне заданного диапазона кодов ошибок
MSG_LIB_TEXT_FAILED_ADD_TO_LIST, // не удалось добавить в список MSG_LIB_TEXT_TIME_UNTIL_THE_END_DAY, // Будет использоваться время действия ордера до конца текущего дня MSG_LIB_TEXT_SUNDAY, // Воскресение
MSG_ACC_MARGIN_MODE_RETAIL_EXCHANGE, // Биржевой рынок MSG_ACC_UNABLE_CLOSE_BY, // Закрытие встречным доступно только на счетах с типом "Хеджинг" MSG_ACC_SAME_TYPE_CLOSE_BY, // Ошибка. Позиции для встречного закрытия имеют один и тот же тип //--- CEngine MSG_ENG_NO_TRADE_EVENTS, // С момента последнего запуска ЕА торговых событий не было MSG_ENG_FAILED_GET_LAST_TRADE_EVENT_DESCR, // Не удалось получить описание последнего торгового события MSG_ENG_FAILED_GET_MARKET_POS_LIST, // Не удалось получить список открытых позиций MSG_ENG_FAILED_GET_PENDING_ORD_LIST, // Не удалось получить список установленных ордеров MSG_ENG_NO_OPEN_POSITIONS, // Нет открытых позиций MSG_ENG_NO_PLACED_ORDERS, // Нет установленных ордеров };
Здесь показаны только части файла с "привязкой к местности", куда необходимо вписать константы перечислений индексов.
Теперь впишем в массивы текстовых сообщений требуемые сообщения, индексы которых только что определили:
{"Ошибка. Такого символа нет на сервере","Error. There is no such symbol on the server"}, {"Ошибка. Такого символа нет в списке используемых символов: ","Error. This symbol is not in the list of the symbols used: "}, {"Не удалось поместить в обзор рынка. Ошибка: ","Failed to put in the market watch. Error: "},
{"Ошибка. Не позиция: ","Error. Not position: "}, {"Ошибка. Нет открытой позиции с тикетом #","Error. No open position with ticket #"}, {"Ошибка. Нет установленного ордера с тикетом #","Error. No placed order with ticket #"}, {"Не удалось закрыть позицию. Ошибка ","Could not close position. Error "},
{"Не удалось модифицировать ордер. Ошибка ","Failed to order modify. Error "}, {"Ошибка: невозможно разместить ордер без явно заданного его времени истечения","Error: Unable to place order without explicitly specified expiration time"}, {"Ошибка. Не удалось получить торговый объект","Error. Failed to get a trade object"}, {"Ошибка. Не удалось получить объект-позицию","Error. Failed to get position object"}, {"Ошибка. Не удалось получить объект-ордер","Error. Failed to get order object"}, {"Ошибка. Не удалось получить объект-символ","Error. Failed to get symbol object"}, {"Код возврата вне заданного диапазона кодов ошибок","Return code out of range of error codes"},
{"не удалось добавить в список","failed to add to the list"}, {"Будет использоваться время действия ордера до конца текущего дня","The order validity time until the end of the current day will be used"}, {"Воскресение","Sunday"},
{"Биржевой рынок","Exchange market mode"}, {"Закрытие встречным доступно только на счетах с типом \"Хеджинг\"","Close by opposite position is available only on accounts with the type \"Hedging\""}, {"Ошибка. Позиции для встречного закрытия имеют один и тот же тип","Error. Positions of the same type in a counterclosure request"}, //--- CEngine {"С момента последнего запуска ЕА торговых событий не было","There have been no trade events since the last launch of EA"}, {"Не удалось получить описание последнего торгового события","Failed to get the description of the last trading event"}, {"Не удалось получить список открытых позиций","Failed to get open positions list"}, {"Не удалось получить список установленных ордеров","Failed to get pending orders list"}, {"Нет открытых позиций","No open positions"}, {"Нет установленных ордеров","No placed orders"}, };
Здесь точно так же, как и при определении констант индексов, показаны только места вставки нужных текстов сообщений. В прикреплённых файлах в конце статьи есть полная версия доработанного Datas.mqh, который можно открыть и посмотреть.
При отсылке торговых приказов на закрытие позиции нам потребуется знать тип ордера, противоположный направлению закрываемой позиции (в
MQL5 именно открытием встречной позиции производится закрытие, а в торговый приказ отправляется тип ордера, а не тип позиции).
В файле сервисных функций библиотеки DELib.mqh напишем две функции для
получения типа ордера по направлению позиции, и типа ордера,
противоположного направлению позиции:
//+------------------------------------------------------------------+ //| Возвращает тип ордера по типу позиции | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE OrderTypeByPositionType(ENUM_POSITION_TYPE type_position) { return(type_position==POSITION_TYPE_BUY ? ORDER_TYPE_BUY : ORDER_TYPE_SELL); } //+------------------------------------------------------------------+ //| Возвращает обратный тип ордера по типу позиции | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE OrderTypeOppositeByPositionType(ENUM_POSITION_TYPE type_position) { return(type_position==POSITION_TYPE_BUY ? ORDER_TYPE_SELL : ORDER_TYPE_BUY); } //+------------------------------------------------------------------+
Все данные подготовили, теперь займёмся непосредственно классом торгового объекта.
В папке объектов библиотеки \MQL5\Include\DoEasy\Objects\ создадим подпапку Trade\, а в ней — новый класс CTradeObj в
файле
TradeObj.mqh.
Сразу же подключим к вновь созданному файлу файл
сервисных функций:
//+------------------------------------------------------------------+ //| TradeObj.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" //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "..\..\Services\DELib.mqh" //+------------------------------------------------------------------+
Впишем в файл класса все необходимые переменные-члены класса и методы:
//+------------------------------------------------------------------+ //| TradeObj.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" //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "..\..\Services\DELib.mqh" //+------------------------------------------------------------------+ //| Класс торгового объекта | //+------------------------------------------------------------------+ class CTradeObj { private: MqlTick m_tick; // Структура тика для получения цен MqlTradeRequest m_request; // Структура торгового запроса MqlTradeResult m_result; // Структура результата исполнения торгового запроса ENUM_ACCOUNT_MARGIN_MODE m_margin_mode; // Режим расчёта маржи ENUM_ORDER_TYPE_FILLING m_type_filling; // Политика исполнения ENUM_ORDER_TYPE_TIME m_type_expiration; // Тип истечения ордера int m_symbol_expiration_flags; // Флаги режимов истечения ордера для символа торгового объекта ulong m_magic; // Магик string m_symbol; // Символ string m_comment; // Комментарий ulong m_deviation; // Размер проскальзывания в пунктах double m_volume; // Объём datetime m_expiration; // Срок истечения ордера (для ордеров типа ORDER_TIME_SPECIFIED) bool m_async_mode; // Флаг асинхронной отправки торгового запроса ENUM_LOG_LEVEL m_log_level; // Уровень логирования int m_stop_limit; // Дистанция установки StopLimit ордера в пунктах public: //--- Конструктор CTradeObj();; //--- Устанавливает значения по умолчанию void Init(const string symbol, const ulong magic, const double volume, const ulong deviation, const int stoplimit, const datetime expiration, const bool async_mode, const ENUM_ORDER_TYPE_FILLING type_filling, const ENUM_ORDER_TYPE_TIME type_expiration, ENUM_LOG_LEVEL log_level); //--- (1) Возвращает режим расчёта маржи (2) флаг счёта хедж ENUM_ACCOUNT_MARGIN_MODE GetMarginMode(void) const { return this.m_margin_mode; } bool IsHedge(void) const { return this.GetMarginMode()==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING; } //--- (1) Устанавливает, (2) возвращает уровень логирования ошибок void SetLogLevel(const ENUM_LOG_LEVEL level) { this.m_log_level=level; } ENUM_LOG_LEVEL GetLogLevel(void) const { return this.m_log_level; } //--- (1) Устанавливает, (2) возвращает политику исполнения void SetTypeFilling(const ENUM_ORDER_TYPE_FILLING type) { this.m_type_filling=type; } ENUM_ORDER_TYPE_FILLING GetTypeFilling(void) const { return this.m_type_filling; } //--- (1) Устанавливает, (2) возвращает тип истечения ордера void SetTypeExpiration(const ENUM_ORDER_TYPE_TIME type) { this.m_type_expiration=type; } ENUM_ORDER_TYPE_TIME GetTypeExpiration(void) const { return this.m_type_expiration; } //--- (1) Устанавливает, (2) возвращает магик void SetMagic(const ulong magic) { this.m_magic=magic; } ulong GetMagic(void) const { return this.m_magic; } //--- (1) Устанавливает, (2) возвращает символ void SetSymbol(const string symbol) { this.m_symbol=symbol; } string GetSymbol(void) const { return this.m_symbol; } //--- (1) Устанавливает, (2) возвращает комментарий void SetComment(const string comment) { this.m_comment=comment; } string GetComment(void) const { return this.m_comment; } //--- (1) Устанавливает, (2) возвращает размер проскальзывания void SetDeviation(const ulong deviation) { this.m_deviation=deviation; } ulong GetDeviation(void) const { return this.m_deviation; } //--- (1) Устанавливает, (2) возвращает объём void SetVolume(const double volume) { this.m_volume=volume; } double GetVolume(void) const { return this.m_volume; } //--- (1) Устанавливает, (2) возвращает срок истечения ордера void SetExpiration(const datetime time) { this.m_expiration=time; } datetime GetExpiration(void) const { return this.m_expiration; } //--- (1) Устанавливает, (2) возвращает флаг асинхронной отправки торгового запроса void SetAsyncMode(const bool async) { this.m_async_mode=async; } bool GetAsyncMode(void) const { return this.m_async_mode; } //--- Данные последнего запроса: //--- Возвращает (1) Тип выполненного действия, (2) магик, (3) тикет ордера, (4) объём, //--- цены (5) открытия, (6) StopLimit-ордера, (7) StopLoss, (8) TakeProfit, (9) отклонение, //--- тип (10) ордера, (11) исполнения, (12) времени действия, (13) срок истечения ордера, //--- (14) комментарий, (15) тикет позиции, (16) тикет встречной позиции ENUM_TRADE_REQUEST_ACTIONS GetLastRequestAction(void) const { return this.m_request.action; } ulong GetLastRequestMagic(void) const { return this.m_request.magic; } ulong GetLastRequestOrder(void) const { return this.m_request.order; } double GetLastRequestVolume(void) const { return this.m_request.volume; } double GetLastRequestPrice(void) const { return this.m_request.price; } double GetLastRequestStopLimit(void) const { return this.m_request.stoplimit; } double GetLastRequestStopLoss(void) const { return this.m_request.sl; } double GetLastRequestTakeProfit(void) const { return this.m_request.tp; } ulong GetLastRequestDeviation(void) const { return this.m_request.deviation; } ENUM_ORDER_TYPE GetLastRequestType(void) const { return this.m_request.type; } ENUM_ORDER_TYPE_FILLING GetLastRequestTypeFilling(void) const { return this.m_request.type_filling; } ENUM_ORDER_TYPE_TIME GetLastRequestTypeTime(void) const { return this.m_request.type_time; } datetime GetLastRequestExpiration(void) const { return this.m_request.expiration; } string GetLastRequestComment(void) const { return this.m_request.comment; } ulong GetLastRequestPosition(void) const { return this.m_request.position; } ulong GetLastRequestPositionBy(void) const { return this.m_request.position_by; } //--- Данные результата последнего запроса: //--- Возвращает (1) код результата операции, (2) тикет сделки, если она совершена, (3) тикет ордера, если он выставлен, //--- (4) объем сделки, подтверждённый брокером, (5) цена в сделке, подтверждённая брокером, //--- (6) текущая рыночная цена предложения (цена реквоты), (7) текущая рыночная цена спроса (цена реквоты) //--- (8) комментарий брокера к операции (по умолчанию заполняется расшифровкой кода возврата торгового сервера), //--- (9) идентификатор запроса, устанавливаемый терминалом при отправке, (10) код ответа внешней торговой системы uint GetResultRetcode(void) const { return this.m_result.retcode; } ulong GetResultDeal(void) const { return this.m_result.deal; } ulong GetResultOrder(void) const { return this.m_result.order; } double GetResultVolume(void) const { return this.m_result.volume; } double GetResultPrice(void) const { return this.m_result.price; } double GetResultBid(void) const { return this.m_result.bid; } double GetResultAsk(void) const { return this.m_result.ask; } string GetResultComment(void) const { return this.m_result.comment; } uint GetResultRequestID(void) const { return this.m_result.request_id; } uint GetResultRetcodeEXT(void) const { return this.m_result.retcode_external;} //--- Открывает позицию bool OpenPosition(const ENUM_POSITION_TYPE type, const double volume, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const ulong deviation=ULONG_MAX, const string comment=NULL); //--- Закрывает позицию bool ClosePosition(const ulong ticket, const ulong deviation=ULONG_MAX, const string comment=NULL); //--- Частично закрывает позицию bool ClosePositionPartially(const ulong ticket, const double volume, const ulong deviation=ULONG_MAX, const string comment=NULL); //--- Закрывает позицию встречной bool ClosePositionBy(const ulong ticket,const ulong ticket_by); //--- Модифицирует позицию bool ModifyPosition(const ulong ticket,const double sl=WRONG_VALUE,const double tp=WRONG_VALUE); //--- Устанавливает ордер bool SetOrder(const ENUM_ORDER_TYPE type, const double volume, const double price, const double sl=0, const double tp=0, const double price_stoplimit=0, const ulong magic=ULONG_MAX, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC, const string comment=NULL); //--- Удаляет ордер bool DeleteOrder(const ulong ticket); //--- Модифицирует ордер bool ModifyOrder(const ulong ticket, const double price=WRONG_VALUE, const double sl=WRONG_VALUE, const double tp=WRONG_VALUE, const double price_stoplimit=WRONG_VALUE, const datetime expiration=WRONG_VALUE, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE); }; //+------------------------------------------------------------------+
Рассмотрим что мы тут понаписали.
Для получения текущих цен нам нужно обращаться к свойствам символа, на котором будет отсылаться торговый запрос. Так как цены нужно иметь актуальные, то получать их нужно непосредственно перед отправкой торгового запроса, именно по этой причине мы разместим переменную m_tick с типом структуры MqlTick прямо в базовом торговом объекте (а могли бы передавать из объекта-символа, но лучше всё-таки обойтись без лишних, пусть и минимальных, затрат на передачу свойства в торговый объект)
Переменная m_request с типом структуры торгового запроса MqlTradeRequest нужна для заполнения всех свойств торгового запроса и отправки их в функцию OrderSend(). В эту же функцию передаётся переменная m_result с типом структуры результата торгового запроса MqlTradeResult — она будет заполнена сервером при получении ответа от торгового сервера, и при ошибочном результате отправки ордера на сервер, мы всегда можем прочитать поля структуры результата торгового запроса чтобы понять что произошло.
Остальные переменные-члены класса, думаю, в пояснениях не нуждаются.
Рассмотрим реализацию методов класса.
Методы установки и получения свойств (Set- и Get-методы) торгового приказа прописаны в теле класса. Всё, что они делают — это записывают в соответствующую переменную переданное в метод значение, либо возвращают значение соответствующей переменной. Работают эти методы только с переменными, хранящими значения по умолчанию. Т.е. при помощи этих методов можно установить требуемое свойство торгового запроса, и далее оно будет иметь по умолчанию установленное значение. Если же нужно разово использовать иное значение для торгового приказа — отличное от заданного по умолчанию, то в методах отсылки торговых приказов предусмотрена передача требуемых значений для разового использования переданного в метод значения.
Методы, возвращающие параметры последнего торгового запроса,
нужны для того, чтобы была возможность поглядеть какое именно значение было передано в то или иное свойство последнего торгового запроса,
и принять какие-либо действия по устранению ошибок или использовать эти значения для следующего запроса на сервер.
Методы просто возвращают содержание соответствующих методу полей структуры торгового запроса. Перед отправкой запроса эта
структура — некоторые её поля, соответствующие торговому запросу, заполняются и передаются в функцию отправки запроса на сервер. Именно
из этой структуры мы и получаем те значения, которые были заполнены в последний раз.
Методы, возвращающие результат торгового запроса, служат для
получения информации о результате обработки торгового запроса. Если запрос оказался ошибочным, то в
retcode мы сможем увидеть уточняющую информацию по коду ошибки. Либо же структура будет заполнена данными об открытой позиции или
установленного отложенного ордера, а в
request_id будет записан код запроса, который впоследствии можно анализировать в обработчике OnTradeTransaction()
чтобы можно было связать торговый запрос, отосланный на сервер посредством OrderSendAsync()
с результатом этого запроса.
В данной библиотеке мы не используем OnTradeTransaction() по причине его отсутствия в MQL4, и анализ
асинхронной отправки приказов и их результатов будем проводить самостоятельно.
Конструктор класса:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CTradeObj::CTradeObj(void) : m_magic(0), m_deviation(5), m_stop_limit(0), m_expiration(0), m_async_mode(false), m_type_filling(ORDER_FILLING_FOK), m_type_expiration(ORDER_TIME_GTC), m_comment(::MQLInfoString(MQL_PROGRAM_NAME)+" by DoEasy"), m_log_level(LOG_LEVEL_ERROR_MSG) { //--- Режим расчёта маржи this.m_margin_mode= ( #ifdef __MQL5__ (ENUM_ACCOUNT_MARGIN_MODE)::AccountInfoInteger(ACCOUNT_MARGIN_MODE) #else /* MQL4 */ ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif ); } //+------------------------------------------------------------------+
В его списке инициализации задаём инициализирующие значения:
- магик равен нулю,
- размер проскальзывания установим равным пяти пунктам,
- цену StopLimit-ордера установим в ноль (нет цены),
- срок истечения ордера также будет нулевым (неограниченное время),
- режим асинхронной отправки торговых запросов
выключен,
- политика исполнения ордеров "Всё или ничего",
- время истечения ордера — неограниченно
- в комментарий ордера запишем имя программы + " by DoEasy",
- режим логирования работы торгового класса — только ошибки.
В теле класса вписываем в переменную m_margin_mode режим расчёта маржи, установленный для аккаунта.
Для MQL5 — получаем требуемое значение посредством функции
AccountInfoInteger() с идентификатором свойства ACCOUNT_MARGIN_MODE.
А для MQL4 сразу же вписываем хеджевый режим расчёта маржи ( ACCOUNT_MARGIN_MODE_RETAIL_HEDGING).
В торговые методы у нас будет возможность передать требуемые значения нужных свойств для заполнения торгового запроса. Но часто нам не нужно заполнять все необходимые свойства — они обычно должны быть неизменны для каждого торгового приказа. Поэтому нам необходимо иметь возможность инициализировать переменные значениями по умолчанию, и в торговых методах уже выбирать какие именно значения использовать в торговом приказе — либо переданное в метод отправки запроса на сервер, либо заданное по умолчанию значение.
Напишем метод инициализации параметров по умолчанию торгового запроса:
//+------------------------------------------------------------------+ //| Устанавливает значения по умолчанию | //+------------------------------------------------------------------+ void CTradeObj::Init(const string symbol, const ulong magic, const double volume, const ulong deviation, const int stoplimit, const datetime expiration, const bool async_mode, const ENUM_ORDER_TYPE_FILLING type_filling, const ENUM_ORDER_TYPE_TIME type_expiration, ENUM_LOG_LEVEL log_level) { this.SetSymbol(symbol); this.SetMagic(magic); this.SetDeviation(deviation); this.SetVolume(volume); this.SetExpiration(expiration); this.SetTypeFilling(type_filling); this.SetTypeExpiration(type_expiration); this.SetAsyncMode(async_mode); this.SetLogLevel(log_level); this.m_symbol_expiration_flags=(int)::SymbolInfoInteger(this.m_symbol,SYMBOL_EXPIRATION_MODE); this.m_volume=::SymbolInfoDouble(this.m_symbol,SYMBOL_VOLUME_MIN); } //+------------------------------------------------------------------+
В метод передаются требуемые значения параметров торгового запроса, и в теле метода переданные значения устанавливаются
соответствующим переменным при помощи методов их установки, рассмотренные выше.
Флаги
разрешённых режимов истечения ордера устанавливаем при помощи функции SymbolInfoInteger()
с идентификатором свойства SYMBOL_EXPIRATION_MODE. А
объём устанавливаем минимально разрешённый для символа при помощи функции
SymbolInfoDouble() с идентификатором
свойства
SYMBOL_VOLUME_MIN.
Метод открытия позиции:
//+------------------------------------------------------------------+ //| Открывает позицию | //+------------------------------------------------------------------+ bool CTradeObj::OpenPosition(const ENUM_POSITION_TYPE type, const double volume, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const ulong deviation=ULONG_MAX, const string comment=NULL) { //--- Если не удалось получить текущие цены - записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false if(!::SymbolInfoTick(this.m_symbol,this.m_tick)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode)); return false; } //--- Очищаем структуры ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Заполняем структуру запроса this.m_request.action = TRADE_ACTION_DEAL; this.m_request.symbol = this.m_symbol; this.m_request.magic = (magic==ULONG_MAX ? this.m_magic : magic); this.m_request.type = OrderTypeByPositionType(type); this.m_request.price = (type==POSITION_TYPE_BUY ? this.m_tick.ask : this.m_tick.bid); this.m_request.volume = volume; this.m_request.sl = sl; this.m_request.tp = tp; this.m_request.deviation= (deviation==ULONG_MAX ? this.m_deviation : deviation); this.m_request.comment = (comment==NULL ? this.m_comment : comment); //--- Возвращаем результат отсылки запроса на сервер return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else (::OrderSend(m_request.symbol,m_request.type,m_request.volume,m_request.price,(int)m_request.deviation,m_request.sl,m_request.tp,m_request.comment,(int)m_request.magic,m_request.expiration,clrNONE)!=WRONG_VALUE) #endif ); } //+------------------------------------------------------------------+
В метод передаётся тип позиции, её объём, цены StopLoss и TakeProfit, магик позиции, величина проскальзывания и комментарий.
По
умолчанию для StopLoss, TakeProfit, магика, проскальзывания и комментария заданы значения. Если эти значения оставить без изменения
при вызове метода, то для этих значений будут использованы значения, установленные по умолчанию в методе Init(), или заданные
непосредственно из программы методами установки умолчательных значений, которые мы рассмотрели выше. Вся логика метода прописана в
комментариях к коду.
Единственное, что тут можно отметить — в поле структуры торгового
запроса, хранящего тип ордера, отсылаем результат, возвращаемый функцией OrderTypeByPositionType(), которую мы написали в
DELib.mqh для получения типа ордера по типу позиции. И ещё: метод никак не проверяет верность переданных в него параметров, и считает, что
они уже проверены и корректны.
Для MQL4 мы пока также ничего не проверяем при возврате
результата отправки запроса на сервер, и не заполняем структуру результата торгового запроса — на данный момент нам необходимо быстро
собрать торговые методы для тестирования. А в последующих статьях будем всё приводить в порядок.
Метод закрытия позиции:
//+------------------------------------------------------------------+ //| Закрывает позицию | //+------------------------------------------------------------------+ bool CTradeObj::ClosePosition(const ulong ticket, const ulong deviation=ULONG_MAX, const string comment=NULL) { //--- Если не удалось выбрать позицию. Записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Получаем символ позиции string symbol=::PositionGetString(POSITION_SYMBOL); //--- Если не удалось получить текущие цены - записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false if(!::SymbolInfoTick(symbol,this.m_tick)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode)); return false; } //--- Получаем тип позиции и тип ордера, обратный типу позиции ENUM_POSITION_TYPE position_type=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); ENUM_ORDER_TYPE type=OrderTypeOppositeByPositionType(position_type); //--- Получаем объём и магик позиции double position_volume=::PositionGetDouble(POSITION_VOLUME); ulong magic=::PositionGetInteger(POSITION_MAGIC); //--- Очищаем структуры ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Заполняем структуру запроса this.m_request.action = TRADE_ACTION_DEAL; this.m_request.symbol = symbol; this.m_request.magic = magic; this.m_request.type = type; this.m_request.price = (position_type==POSITION_TYPE_SELL ? this.m_tick.ask : this.m_tick.bid); this.m_request.volume = position_volume; this.m_request.deviation= (deviation==ULONG_MAX ? this.m_deviation : deviation); this.m_request.comment = (comment==NULL ? this.m_comment : comment); //--- Если счёт хеджевый, то в запишем в структуру тикет закрываемой позиции if(this.IsHedge()) this.m_request.position=::PositionGetInteger(POSITION_TICKET); //--- Возвращаем результат отсылки запроса на сервер return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderClose((int)m_request.position,m_request.volume,m_request.price,(int)m_request.deviation,clrNONE) #endif ); } //+------------------------------------------------------------------+
В метод передётся тикет закрываемой позиции, проскальзывание и комментарий.
Здесь и в остальных торговых методах — всё
аналогично рассмотренному выше методу открытия позиции.
Метод для частичного закрытия позиции:
//+------------------------------------------------------------------+ //| Частично закрывает позицию | //+------------------------------------------------------------------+ bool CTradeObj::ClosePositionPartially(const ulong ticket, const double volume, const ulong deviation=ULONG_MAX, const string comment=NULL) { //--- Если не удалось выбрать позицию. Записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Получаем символ позиции string symbol=::PositionGetString(POSITION_SYMBOL); //--- Если не удалось получить текущие цены - записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false if(!::SymbolInfoTick(symbol,this.m_tick)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode)); return false; } //--- Получаем тип позиции и тип ордера, обратный типу позиции ENUM_POSITION_TYPE position_type=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); ENUM_ORDER_TYPE type=OrderTypeOppositeByPositionType(position_type); //--- Получаем объём и магик позиции double position_volume=::PositionGetDouble(POSITION_VOLUME); ulong magic=::PositionGetInteger(POSITION_MAGIC); //--- Очищаем структуры ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Заполняем структуру запроса this.m_request.action = TRADE_ACTION_DEAL; this.m_request.position = ticket; this.m_request.symbol = symbol; this.m_request.magic = magic; this.m_request.type = type; this.m_request.price = (position_type==POSITION_TYPE_SELL ? this.m_tick.ask : this.m_tick.bid); this.m_request.volume = (volume<position_volume ? volume : position_volume); this.m_request.deviation= (deviation==ULONG_MAX ? this.m_deviation : deviation); this.m_request.comment = (comment==NULL ? this.m_comment : comment); //--- Если счёт хеджевый, то в запишем в структуру тикет закрываемой позиции if(this.IsHedge()) this.m_request.position=::PositionGetInteger(POSITION_TICKET); //--- Возвращаем результат отсылки запроса на сервер return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderClose((int)m_request.position,m_request.volume,m_request.price,(int)m_request.deviation,clrNONE) #endif ); } //+------------------------------------------------------------------+
В метод передаётся тикет закрываемой позиции, объём, который нужно закрыть, размер проскальзывания и комментарий.
Здесь стоит отметить, что
если в метод передан закрываемый объём больше, чем имеющийся объём у позиции, то
позиция будет закрыта полностью.
Метод закрытия позиции встречной:
//+------------------------------------------------------------------+ //| Закрывает позицию встречной | //+------------------------------------------------------------------+ bool CTradeObj::ClosePositionBy(const ulong ticket,const ulong ticket_by) { #ifdef __MQL5__ //--- Если счёт не хеджевый. if(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) { //--- Закрытие встречным доступно только на счетах с типом "Хеджинг". //--- Записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false this.m_result.retcode=MSG_ACC_UNABLE_CLOSE_BY; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_ACC_UNABLE_CLOSE_BY)); return false; } #endif //--- Закрываемая позиция //--- Если не удалось выбрать позицию - записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Получаем тип и магик закрываемой позиции ENUM_POSITION_TYPE position_type=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); ulong magic=::PositionGetInteger(POSITION_MAGIC); //--- Встречная позиция //--- Если не удалось выбрать позицию - записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false if(!::PositionSelectByTicket(ticket_by)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket_by,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Получаем тип встречной позиции ENUM_POSITION_TYPE position_type_by=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); //--- Если типы закрываемой и встречной позиции одинаковы - записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false if(position_type==position_type_by) { this.m_result.retcode=MSG_ACC_SAME_TYPE_CLOSE_BY; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,CMessage::Text(MSG_ACC_SAME_TYPE_CLOSE_BY)); return false; } //--- Очищаем структуры ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Заполняем структуру запроса this.m_request.action = TRADE_ACTION_CLOSE_BY; this.m_request.position = ticket; this.m_request.position_by = ticket_by; this.m_request.magic = magic; //--- Возвращаем результат отсылки запроса на сервер return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderCloseBy((int)m_request.position,(int)m_request.position_by,clrNONE) #endif ); } //+------------------------------------------------------------------+
В метод передаётся тикет закрываемой позиции и тикет встречной.
Метод модификации стоп-уровней позиции:
//+------------------------------------------------------------------+ //| Модифицирует позицию | //+------------------------------------------------------------------+ bool CTradeObj::ModifyPosition(const ulong ticket,const double sl=WRONG_VALUE,const double tp=WRONG_VALUE) { //--- Если переданы все значения по умолчанию - значит модифицировать нечего if(sl==WRONG_VALUE && tp==WRONG_VALUE) { //--- В запросе нет изменений - записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false this.m_result.retcode= #ifdef __MQL5__ TRADE_RETCODE_NO_CHANGES #else 10025 #endif ; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,CMessage::Text(this.m_result.retcode),CMessage::Retcode(this.m_result.retcode)); return false; } //--- Если не удалось выбрать позицию - записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- Очищаем структуры ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Заполняем структуру запроса m_request.action = TRADE_ACTION_SLTP; m_request.position= ticket; m_request.symbol = ::PositionGetString(POSITION_SYMBOL); m_request.magic = ::PositionGetInteger(POSITION_MAGIC); m_request.sl = (sl==WRONG_VALUE ? ::PositionGetDouble(POSITION_SL) : sl); m_request.tp = (tp==WRONG_VALUE ? ::PositionGetDouble(POSITION_TP) : tp); //--- Возвращаем результат отсылки запроса на сервер return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderModify((int)m_request.position,::OrderOpenPrice(),m_request.sl,m_request.tp,::OrderExpiration(),clrNONE) #endif ); } //+------------------------------------------------------------------+
В метод передаются тикет модифицируемой позиции и новые уровни StopLoss и TakeProfit.
Метод для установки отложенного ордера:
//+------------------------------------------------------------------+ //| Устанавливает ордер | //+------------------------------------------------------------------+ bool CTradeObj::SetOrder(const ENUM_ORDER_TYPE type, const double volume, const double price, const double sl=0, const double tp=0, const double price_stoplimit=0, const ulong magic=ULONG_MAX, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC, const string comment=NULL) { //--- Если передан не правильный тип ордера - записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL || type==ORDER_TYPE_CLOSE_BY #ifdef __MQL4__ || type==ORDER_TYPE_BUY_STOP_LIMIT || type==ORDER_TYPE_SELL_STOP_LIMIT #endif ) { this.m_result.retcode=MSG_LIB_SYS_INVALID_ORDER_TYPE; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_INVALID_ORDER_TYPE),OrderTypeDescription(type)); return false; } //--- Очищаем структуры ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Заполняем структуру запроса m_request.action = TRADE_ACTION_PENDING; m_request.symbol = this.m_symbol; m_request.magic = (magic==ULONG_MAX ? this.m_magic : magic); m_request.volume = volume; m_request.type = type; m_request.stoplimit = price_stoplimit; m_request.price = price; m_request.sl = sl; m_request.tp = tp; m_request.type_time = type_time; m_request.expiration = expiration; m_request.comment = (comment==NULL ? this.m_comment : comment); //--- Возвращаем результат отсылки запроса на сервер return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else (::OrderSend(m_request.symbol,m_request.type,m_request.volume,m_request.price,(int)m_request.deviation,m_request.sl,m_request.tp,m_request.comment,(int)m_request.magic,m_request.expiration,clrNONE)!=WRONG_VALUE) #endif ); } //+------------------------------------------------------------------+
В метод передаются тип отложенного ордера, его объём, цена установки, цены StopLoss, TakeProfit и StopLimit-ордера, магик, время жизни
ордера, тип истечения ордера и комментарий.
Метод для удаления отложенного ордера:
//+------------------------------------------------------------------+ //| Удаляет ордер | //+------------------------------------------------------------------+ bool CTradeObj::DeleteOrder(const ulong ticket) { //--- Очищаем структуры ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Заполняем структуру запроса m_request.action = TRADE_ACTION_REMOVE; m_request.order = ticket; //--- Возвращаем результат отсылки запроса на сервер return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderDelete((int)m_request.order,clrNONE) #endif ); } //+------------------------------------------------------------------+
В метод передаётся тикет удаляемого ордера.
Метод для модификации отложенного ордера:
//+------------------------------------------------------------------+ //| Модифицирует ордер | //+------------------------------------------------------------------+ bool CTradeObj::ModifyOrder(const ulong ticket, const double price=WRONG_VALUE, const double sl=WRONG_VALUE, const double tp=WRONG_VALUE, const double price_stoplimit=WRONG_VALUE, const datetime expiration=WRONG_VALUE, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE) { //--- Если не удалось выбрать ордер - записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false #ifdef __MQL5__ if(!::OrderSelect(ticket)) #else if(!::OrderSelect((int)ticket,SELECT_BY_TICKET)) #endif { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_ORD),CMessage::Text(this.m_result.retcode)); return false; } double order_price=::OrderGetDouble(ORDER_PRICE_OPEN); double order_sl=::OrderGetDouble(ORDER_SL); double order_tp=::OrderGetDouble(ORDER_TP); double order_stoplimit=::OrderGetDouble(ORDER_PRICE_STOPLIMIT); ENUM_ORDER_TYPE_TIME order_type_time=(ENUM_ORDER_TYPE_TIME)::OrderGetInteger(ORDER_TYPE_TIME); datetime order_expiration=(datetime)::OrderGetInteger(ORDER_TIME_EXPIRATION); //--- Если переданы значения по умолчанию и цена равна цене, записанной в ордере - значит в запросе нет изменений //--- Записываем код ошибки, описание ошибки, выводим сообщение в журнал и возвращаем false if(price==order_price && sl==WRONG_VALUE && tp==WRONG_VALUE && price_stoplimit==WRONG_VALUE && type_time==WRONG_VALUE && expiration==WRONG_VALUE) { this.m_result.retcode = #ifdef __MQL5__ TRADE_RETCODE_NO_CHANGES #else 10025 #endif ; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) Print(DFUN,CMessage::Text(this.m_result.retcode),CMessage::Retcode(this.m_result.retcode)); return false; } //--- Очищаем структуры ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Заполняем структуру запроса m_request.action = TRADE_ACTION_MODIFY; m_request.order = ticket; m_request.price = (price==WRONG_VALUE ? order_price : price); m_request.sl = (sl==WRONG_VALUE ? order_sl : sl); m_request.tp = (tp==WRONG_VALUE ? order_tp : tp); m_request.stoplimit = (price_stoplimit==WRONG_VALUE ? order_stoplimit : price_stoplimit); m_request.type_time = (type_time==WRONG_VALUE ? order_type_time : type_time); m_request.expiration = (expiration==WRONG_VALUE ? order_expiration : expiration); //--- Возвращаем результат модификации ордера return ( #ifdef __MQL5__ !this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result) #else ::OrderModify((int)m_request.order,m_request.price,m_request.sl,m_request.tp,m_request.expiration,clrNONE) #endif ); Print(DFUN); } //+------------------------------------------------------------------+
В метод передаются тикет модифицируемого ордера, новые значения цены, уровней StopLoss, TakeProfit и StopLimit-ордера, время жизни и
тип истечения ордера.
Во всех методах организованы идентичные проверки переданных в метод умолчательных значений, все действия прокомментированы, так что оставим их однотипное описание для самостоятельного разбора.
На этом создание минимального функционала базового торгового класса завершено.
Так как любые торговые приказы мы отсылаем так или иначе применительно к какому-либо символу, то базовый торговый объект разместим в объекте-символе и сделаем к нему доступ извне.
Откроем файл объекта-символа \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh и
подключим
к нему файл торгового объекта TradeObj.mqh:
//+------------------------------------------------------------------+ //| Symbol.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 "..\BaseObj.mqh" #include "..\Trade\TradeObj.mqh" //+------------------------------------------------------------------+
В приватной секции объявим переменную-объект торгового класса:
//+------------------------------------------------------------------+ //| Класс абстрактного символа | //+------------------------------------------------------------------+ class CSymbol : public CBaseObj { private: struct MqlMarginRate { double Initial; // коэффициент взимания начальной маржи double Maintenance; // коэффициент взимания поддерживающей маржи }; struct MqlMarginRateMode { MqlMarginRate Long; // MarginRate длинных позиций MqlMarginRate Short; // MarginRate коротких позиций MqlMarginRate BuyStop; // MarginRate BuyStop-ордеров MqlMarginRate BuyLimit; // MarginRate BuyLimit-ордеров MqlMarginRate BuyStopLimit; // MarginRate BuyStopLimit-ордеров MqlMarginRate SellStop; // MarginRate SellStop-ордеров MqlMarginRate SellLimit; // MarginRate SellLimit-ордеров MqlMarginRate SellStopLimit; // MarginRate SellStopLimit-ордеров }; MqlMarginRateMode m_margin_rate; // Структура коэффициентов взимания маржи MqlBookInfo m_book_info_array[]; // Массив структур данных стакана long m_long_prop[SYMBOL_PROP_INTEGER_TOTAL]; // Целочисленные свойства double m_double_prop[SYMBOL_PROP_DOUBLE_TOTAL]; // Вещественные свойства string m_string_prop[SYMBOL_PROP_STRING_TOTAL]; // Строковые свойства bool m_is_change_trade_mode; // Флаг изменения режима торговли для символа CTradeObj m_trade; // Объект торгового класса //--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство символа int IndexProp(ENUM_SYMBOL_PROP_DOUBLE property) const { return(int)property-SYMBOL_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_SYMBOL_PROP_STRING property) const { return(int)property-SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_DOUBLE_TOTAL; } //--- (1) Заполняет все свойства символа "коэффициент взимания маржи", (2) инициализирует коэффициенты bool MarginRates(void); void InitMarginRates(void); //--- Обнуляет все данные объекта-символа void Reset(void); //--- Возвращает текущий день недели ENUM_DAY_OF_WEEK CurrentDayOfWeek(void) const; public: //--- Конструктор по умолчанию
В публичной секции класса объявим два метода:
метод,
возвращающий корректную политику исполнения, и метод, возвращающий
корректный тип истечения ордеров:
public: //--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство символа void SetProperty(ENUM_SYMBOL_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_SYMBOL_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_SYMBOL_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство символа long GetProperty(ENUM_SYMBOL_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_SYMBOL_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_SYMBOL_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Возвращает флаг поддержания символом данного свойства virtual bool SupportProperty(ENUM_SYMBOL_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_SYMBOL_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_SYMBOL_PROP_STRING property) { return true; } //--- Возвращает флаг разрешения (1) рыночных, (2) лимитных, (3) стоп-, (4) стоп-лимит ордеров, //--- флаг разрешения установки (5) StopLoss, (6) TakeProfit приказов, (7) закрытия встречной bool IsMarketOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_MARKET)==SYMBOL_ORDER_MARKET); } bool IsLimitOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_LIMIT)==SYMBOL_ORDER_LIMIT); } bool IsStopOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_STOP)==SYMBOL_ORDER_STOP); } bool IsStopLimitOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_STOP_LIMIT)==SYMBOL_ORDER_STOP_LIMIT); } bool IsStopLossOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_SL)==SYMBOL_ORDER_SL); } bool IsTakeProfitOrdersAllowed(void) const { return((this.OrderModeFlags() & SYMBOL_ORDER_TP)==SYMBOL_ORDER_TP); } bool IsCloseByOrdersAllowed(void) const; //--- Возвращает флаг заливки (1) FOK, (2) IOC bool IsFillingModeFOK(void) const { return((this.FillingModeFlags() & SYMBOL_FILLING_FOK)==SYMBOL_FILLING_FOK); } bool IsFillingModeIOC(void) const { return((this.FillingModeFlags() & SYMBOL_FILLING_IOC)==SYMBOL_FILLING_IOC); } //--- Возвращает флаг истечения действия ордера (1) GTC, (2) DAY, (3) Specified, (4) Specified Day bool IsExpirationModeGTC(void) const { return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_GTC)==SYMBOL_EXPIRATION_GTC); } bool IsExpirationModeDAY(void) const { return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_DAY)==SYMBOL_EXPIRATION_DAY); } bool IsExpirationModeSpecified(void) const { return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_SPECIFIED)==SYMBOL_EXPIRATION_SPECIFIED); } bool IsExpirationModeSpecifiedDay(void) const { return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_SPECIFIED_DAY)==SYMBOL_EXPIRATION_SPECIFIED_DAY); } //--- Возвращает описание разрешения (1) рыночных, (2) лимитных, (3) стоп- и (4) стоп-лимит ордеров, //--- описание разрешения установки (5) StopLoss и (6) TakeProfit приказов, (7) закрытия встречной string GetMarketOrdersAllowedDescription(void) const; string GetLimitOrdersAllowedDescription(void) const; string GetStopOrdersAllowedDescription(void) const; string GetStopLimitOrdersAllowedDescription(void) const; string GetStopLossOrdersAllowedDescription(void) const; string GetTakeProfitOrdersAllowedDescription(void) const; string GetCloseByOrdersAllowedDescription(void) const; //--- Возвращает описание разрешения типа заливки (1) FOK и (2) IOC, (3) разрешенных режимов истечения ордера string GetFillingModeFOKAllowedDescrioption(void) const; string GetFillingModeIOCAllowedDescrioption(void) const; //--- Возвращает описание истечения действия ордера (1) GTC, (2) DAY, (3) Specified, (4) Specified Day string GetExpirationModeGTCDescription(void) const; string GetExpirationModeDAYDescription(void) const; string GetExpirationModeSpecifiedDescription(void) const; string GetExpirationModeSpecDayDescription(void) const; //--- Возвращает описание (1) статуса, (2) типа цены для построения баров, //--- (3) способа вычисления залоговых средств, (4) режима торговли по инструменту, //--- (5) режима заключения сделок по инструменту, (6) модели расчета свопа, //--- (7) срока действия StopLoss и TakeProfit ордеров, (8) типа опциона, (9) права опциона //--- флагов (10) разрешенных типов ордеров, (11) разрешённых типов заливки, //--- (12) разрешенных режимов истечения ордера string GetStatusDescription(void) const; string GetChartModeDescription(void) const; string GetCalcModeDescription(void) const; string GetTradeModeDescription(void) const; string GetTradeExecDescription(void) const; string GetSwapModeDescription(void) const; string GetOrderGTCModeDescription(void) const; string GetOptionTypeDescription(void) const; string GetOptionRightDescription(void) const; string GetOrderModeFlagsDescription(void) const; string GetFillingModeFlagsDescription(void) const; string GetExpirationModeFlagsDescription(void) const; //--- Возвращает (1) тип исполнения, (2) тип истечения ордера, равный type, если он доступен на символе, иначе - корректный вариант ENUM_ORDER_TYPE_FILLING GetCorrectTypeFilling(const uint type=ORDER_FILLING_RETURN); ENUM_ORDER_TYPE_TIME GetCorrectTypeExpiration(uint expiration=ORDER_TIME_GTC); //+------------------------------------------------------------------+
За пределами тела класса напишем их реализацию:
//+------------------------------------------------------------------+ //| Возвращает тип исполнения ордера, равный type, | //| если он доступен на символе, иначе - корректный вариант | //| https://www.mql5.com/ru/forum/170952/page4#comment_4128864 | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE_FILLING CSymbol::GetCorrectTypeFilling(const uint type=ORDER_FILLING_RETURN) { const ENUM_SYMBOL_TRADE_EXECUTION exe_mode=this.TradeExecutionMode(); const int filling_mode=this.FillingModeFlags(); return( (filling_mode == 0 || (type >= ORDER_FILLING_RETURN) || ((filling_mode & (type + 1)) != type + 1)) ? (((exe_mode == SYMBOL_TRADE_EXECUTION_EXCHANGE) || (exe_mode == SYMBOL_TRADE_EXECUTION_INSTANT)) ? ORDER_FILLING_RETURN : ((filling_mode == SYMBOL_FILLING_IOC) ? ORDER_FILLING_IOC : ORDER_FILLING_FOK)) : (ENUM_ORDER_TYPE_FILLING)type ); } //+------------------------------------------------------------------+ //| Возвращает тип истечения ордера, равный expiration, | //| если он доступен на символе Symb, иначе - корректный вариант | //| https://www.mql5.com/ru/forum/170952/page4#comment_4128871 | //| Применение: | //| Request.type_time = GetExpirationType((uint)Expiration); | //| Expiration может быть datetime-временем | //| if(Expiration > ORDER_TIME_DAY) Request.expiration = Expiration; | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE_TIME CSymbol::GetCorrectTypeExpiration(uint expiration=ORDER_TIME_GTC) { #ifdef __MQL5__ const int expiration_mode=this.ExpirationModeFlags(); if((expiration > ORDER_TIME_SPECIFIED_DAY) || (((expiration_mode >> expiration) & 1) == 0)) { if((expiration < ORDER_TIME_SPECIFIED) || (expiration_mode < SYMBOL_EXPIRATION_SPECIFIED)) expiration=ORDER_TIME_GTC; else if(expiration > ORDER_TIME_DAY) expiration=ORDER_TIME_SPECIFIED; uint i=1 << expiration; while((expiration <= ORDER_TIME_SPECIFIED_DAY) && ((expiration_mode & i) != i)) { i <<= 1; expiration++; } } #endif return (ENUM_ORDER_TYPE_TIME)expiration; } //+------------------------------------------------------------------+
Чтобы не "придумывать велосипед", логика данных методов взята из постов участника форума fxsaber, и в шапке кодов
даны ссылки на посты с кодами.
Честно говоря, разбираться самому в хитросплетениях этой логики не очень приятное занятие, а зная человека, опубликовавшего функции, как
серьёзного доверенного разработчика, я решил, что можно положиться на авторитет автора. В принципе, можно разложить всю логику методов
на составляющие, получить обширные по своему содержанию методы и описать всю их логику. Но проще сделать лишь описание к методам:
В методы передаются желаемые политика исполнения и тип истечения ордеров. Если символ поддерживает данную политику или тип, то он и
возвращается, если же желаемые режимы не поддерживаются на символе, то возвращаются разрешённые режимы. Таким образом методы всегда
вернут поддерживаемые — корректные режимы политики исполнения или истечения ордеров.
В публичной секции, в блоке с методами упрощённого доступа к целочисленным свойствам объекта-символа,
добавим объявление
метода, возвращающего нормализованный лот:
//+------------------------------------------------------------------+ //| Методы упрощённого доступа к свойствам объекта-символа | //+------------------------------------------------------------------+ //--- Целочисленные свойства long Status(void) const { return this.GetProperty(SYMBOL_PROP_STATUS); } int IndexInMarketWatch(void) const { return (int)this.GetProperty(SYMBOL_PROP_INDEX_MW); } bool IsCustom(void) const { return (bool)this.GetProperty(SYMBOL_PROP_CUSTOM); } color ColorBackground(void) const { return (color)this.GetProperty(SYMBOL_PROP_BACKGROUND_COLOR); } ENUM_SYMBOL_CHART_MODE ChartMode(void) const { return (ENUM_SYMBOL_CHART_MODE)this.GetProperty(SYMBOL_PROP_CHART_MODE); } bool IsExist(void) const { return (bool)this.GetProperty(SYMBOL_PROP_EXIST); } bool IsExist(const string name) const { return this.SymbolExists(name); } bool IsSelect(void) const { return (bool)this.GetProperty(SYMBOL_PROP_SELECT); } bool IsVisible(void) const { return (bool)this.GetProperty(SYMBOL_PROP_VISIBLE); } long SessionDeals(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_DEALS); } long SessionBuyOrders(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS); } long SessionSellOrders(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS); } long Volume(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME); } long VolumeHigh(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH); } long VolumeLow(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW); } datetime Time(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_TIME); } int Digits(void) const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS); } int DigitsLot(void) const { return (int)this.GetProperty(SYMBOL_PROP_DIGITS_LOTS); } int Spread(void) const { return (int)this.GetProperty(SYMBOL_PROP_SPREAD); } bool IsSpreadFloat(void) const { return (bool)this.GetProperty(SYMBOL_PROP_SPREAD_FLOAT); } int TicksBookdepth(void) const { return (int)this.GetProperty(SYMBOL_PROP_TICKS_BOOKDEPTH); } ENUM_SYMBOL_CALC_MODE TradeCalcMode(void) const { return (ENUM_SYMBOL_CALC_MODE)this.GetProperty(SYMBOL_PROP_TRADE_CALC_MODE); } ENUM_SYMBOL_TRADE_MODE TradeMode(void) const { return (ENUM_SYMBOL_TRADE_MODE)this.GetProperty(SYMBOL_PROP_TRADE_MODE); } datetime StartTime(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_START_TIME); } datetime ExpirationTime(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_EXPIRATION_TIME); } int TradeStopLevel(void) const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_STOPS_LEVEL); } int TradeFreezeLevel(void) const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_FREEZE_LEVEL); } ENUM_SYMBOL_TRADE_EXECUTION TradeExecutionMode(void) const { return (ENUM_SYMBOL_TRADE_EXECUTION)this.GetProperty(SYMBOL_PROP_TRADE_EXEMODE); } ENUM_SYMBOL_SWAP_MODE SwapMode(void) const { return (ENUM_SYMBOL_SWAP_MODE)this.GetProperty(SYMBOL_PROP_SWAP_MODE); } ENUM_DAY_OF_WEEK SwapRollover3Days(void) const { return (ENUM_DAY_OF_WEEK)this.GetProperty(SYMBOL_PROP_SWAP_ROLLOVER3DAYS); } bool IsMarginHedgedUseLeg(void) const { return (bool)this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED_USE_LEG); } int ExpirationModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_EXPIRATION_MODE); } int FillingModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_FILLING_MODE); } int OrderModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_ORDER_MODE); } ENUM_SYMBOL_ORDER_GTC_MODE OrderModeGTC(void) const { return (ENUM_SYMBOL_ORDER_GTC_MODE)this.GetProperty(SYMBOL_PROP_ORDER_GTC_MODE); } ENUM_SYMBOL_OPTION_MODE OptionMode(void) const { return (ENUM_SYMBOL_OPTION_MODE)this.GetProperty(SYMBOL_PROP_OPTION_MODE); } ENUM_SYMBOL_OPTION_RIGHT OptionRight(void) const { return (ENUM_SYMBOL_OPTION_RIGHT)this.GetProperty(SYMBOL_PROP_OPTION_RIGHT); } //--- Вещественные свойства double Bid(void) const { return this.GetProperty(SYMBOL_PROP_BID); } double BidHigh(void) const { return this.GetProperty(SYMBOL_PROP_BIDHIGH); } double BidLow(void) const { return this.GetProperty(SYMBOL_PROP_BIDLOW); } double Ask(void) const { return this.GetProperty(SYMBOL_PROP_ASK); } double AskHigh(void) const { return this.GetProperty(SYMBOL_PROP_ASKHIGH); } double AskLow(void) const { return this.GetProperty(SYMBOL_PROP_ASKLOW); } double Last(void) const { return this.GetProperty(SYMBOL_PROP_LAST); } double LastHigh(void) const { return this.GetProperty(SYMBOL_PROP_LASTHIGH); } double LastLow(void) const { return this.GetProperty(SYMBOL_PROP_LASTLOW); } double VolumeReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_REAL); } double VolumeHighReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH_REAL); } double VolumeLowReal(void) const { return this.GetProperty(SYMBOL_PROP_VOLUMELOW_REAL); } double OptionStrike(void) const { return this.GetProperty(SYMBOL_PROP_OPTION_STRIKE); } double Point(void) const { return this.GetProperty(SYMBOL_PROP_POINT); } double TradeTickValue(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE); } double TradeTickValueProfit(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT); } double TradeTickValueLoss(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS); } double TradeTickSize(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_TICK_SIZE); } double TradeContractSize(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_CONTRACT_SIZE); } double TradeAccuredInterest(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_ACCRUED_INTEREST); } double TradeFaceValue(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_FACE_VALUE); } double TradeLiquidityRate(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_LIQUIDITY_RATE); } double LotsMin(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MIN); } double LotsMax(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MAX); } double LotsStep(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_STEP); } double VolumeLimit(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_LIMIT); } double SwapLong(void) const { return this.GetProperty(SYMBOL_PROP_SWAP_LONG); } double SwapShort(void) const { return this.GetProperty(SYMBOL_PROP_SWAP_SHORT); } double MarginInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_INITIAL); } double MarginMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_MAINTENANCE); } double MarginLongInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_INITIAL); } double MarginBuyStopInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL); } double MarginBuyLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL); } double MarginBuyStopLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL); } double MarginLongMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE); } double MarginBuyStopMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE); } double MarginBuyLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE); } double MarginBuyStopLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE); } double MarginShortInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_INITIAL); } double MarginSellStopInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL); } double MarginSellLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL); } double MarginSellStopLimitInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL); } double MarginShortMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE); } double MarginSellStopMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE); } double MarginSellLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE); } double MarginSellStopLimitMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE); } double SessionVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_VOLUME); } double SessionTurnover(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_TURNOVER); } double SessionInterest(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_INTEREST); } double SessionBuyOrdersVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME); } double SessionSellOrdersVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME); } double SessionOpen(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_OPEN); } double SessionClose(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_CLOSE); } double SessionAW(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_AW); } double SessionPriceSettlement(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT); } double SessionPriceLimitMin(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN); } double SessionPriceLimitMax(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX); } double MarginHedged(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED); } double NormalizedPrice(const double price) const; double NormalizedLot(const double volume) const; double BidLast(void) const; double BidLastHigh(void) const; double BidLastLow(void) const; //--- Строковые свойства
И в самом конце тела класса добавим метод, возвращающий торговый объект, принадлежащий объекту-символу:
//--- Средневзвешенная цена сессии //--- установка контролируемой величины (1) прироста, (2) уменьшения, (3) контрольного уровня средневзвешенной цены сессии //--- получение (4) величины изменения средневзвешенной цены сессии, //--- получение флага изменения средневзвешенной цены сессии больше, чем на величину (5) прироста, (6) уменьшения void SetControlSessionPriceAWInc(const double value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_AW,::fabs(value)); } void SetControlSessionPriceAWDec(const double value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_AW,::fabs(value)); } void SetControlSessionPriceAWLevel(const double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_AW,::fabs(value)); } double GetValueChangedSessionPriceAW(void) const { return this.GetPropDoubleChangedValue(SYMBOL_PROP_SESSION_AW); } bool IsIncreasedSessionPriceAW(void) const { return (bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SESSION_AW); } bool IsDecreasedSessionPriceAW(void) const { return (bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SESSION_AW); } //--- Возвращает торговый объект CTradeObj *GetTradeObj(void) { return &this.m_trade; } }; //+------------------------------------------------------------------+
Так как торговый объект создаётся сразу при создании объекта-символа, и при создании торговый объект имеет начальные инициализирующие
значения всех его полей, то его необходимо инициализировать нужными нам значениями по умолчанию. Для этого в самом конце конструктора
класса CSymbol
вызовем метод Init() торгового объекта с указанием требуемых значений по
умолчанию:
//--- Заполнение текущих данных символа for(int i=0;i<SYMBOL_PROP_INTEGER_TOTAL;i++) this.m_long_prop_event[i][3]=this.m_long_prop[i]; for(int i=0;i<SYMBOL_PROP_DOUBLE_TOTAL;i++) this.m_double_prop_event[i][3]=this.m_double_prop[i]; //--- Обновление данных в базовом объекте и поиск изменений CBaseObj::Refresh(); //--- if(!select) this.RemoveFromMarketWatch(); //--- Инициализация умолчательных значений торгового объекта this.m_trade.Init(this.Name(),0,this.LotsMin(),5,0,0,false,this.GetCorrectTypeFilling(),this.GetCorrectTypeExpiration(),LOG_LEVEL_ERROR_MSG); } //+------------------------------------------------------------------+
При вызове метода в торговый объект передаём:
- наименование символа,
- минимально-допустимый лот по данному символу,
- величину проскальзывания, равную пяти пунктам,
- цену StopLoss, равную нулю — отсутствие StopLoss,
- цену TakeProfit, равную нулю — отсутствие TakeProfit,
- флаг асинхронной отправки торговых приказов, равный false — синхронная отправка,
- сразу получаем корректную политику исполнения ордеров и устанавливаем её для торгового объекта,
- сразу получаем корректный режим срока действия ордеров и устанавливаем его для торгового объекта,
- и задаём уровень логирования торговых методов как "только ошибки"
Эти значения сразу устанавливаются торговому объекту по умолчанию, но их всегда можно поменять посредством Set-методов, рассмотренных выше, для каждого свойства в отдельности, либо можно оставить значения по умолчанию, но при вызове торгового метода передать в него иной параметр, и он разово будет использован при отправке запроса на сервер.
За пределами тела класса напишем реализацию метода нормализации лота:
//+------------------------------------------------------------------+ //| Возвращает нормализованный лот с учетом свойств символа | //+------------------------------------------------------------------+ double CSymbol::NormalizedLot(const double volume) const { double ml=this.LotsMin(); double mx=this.LotsMax(); double ln=::NormalizeDouble(volume,this.DigitsLot()); return(ln<ml ? ml : ln>mx ? mx : ln); } //+------------------------------------------------------------------+
В метод передаётся требуемое для нормализации значение лота. Затем получаем минимальный и максимальный лоты, разрешённые для символа, нормализуем переданное в метод значение лота, и затем простым сравниванием нормализованного значения с минимальным и максимальным лотом, определяем значение, которое необходимо вернуть — если лот, переданный в метод, меньше или больше мин./макс. лота символа, то возвращаем мин./макс. лот соответственно, иначе — возвращаем нормализованный лот с учётом количества знаков после запятой в значении лота (метод DigitsLot()).
Доработка класса CSymbol завершена.
Теперь нам необходимо протестировать торговые методы, а так как у нас пока нет основного торгового класса, то мы напишем временно методы в классе базового объекта библиотеки CEngine для доступа к торговому объекту нужного символа. Так как именно в этом объекте у нас имеется полный оступ ко всем важным коллекциям библиотеки, то именно в нём мы и будем размещать методы для тестирования торгового объекта.
Обратите внимание — методы в классе будут временными, далее мы создадим полноценный торговый класс, где и будут находиться все требуемые для проверки значений и торговли методы.
В публичную секцию класса CEngine добавим все необходимые на сейчас методы для тестирования торгового объекта:
//--- Устанавливает для торговых классов: //--- (1) корректное значение политики исполнения, (2) значение политики исполнения, //--- (3) корректный тип истечения ордера, (4) тип истечения ордера, //--- (5) магик, (6) Комментарий, (7) размер проскальзывания, (8) объём, (9) срок истечения ордера, //--- (10) флаг асинхронной отправки торгового запроса, (11) уровень логирования void SetTradeCorrectTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL); void SetTradeTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL); void SetTradeCorrectTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL); void SetTradeTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL); void SetTradeMagic(const ulong magic,const string symbol_name=NULL); void SetTradeComment(const string comment,const string symbol_name=NULL); void SetTradeDeviation(const ulong deviation,const string symbol_name=NULL); void SetTradeVolume(const double volume=0,const string symbol_name=NULL); void SetTradeExpiration(const datetime expiration=0,const string symbol_name=NULL); void SetTradeAsyncMode(const bool mode=false,const string symbol_name=NULL); void SetTradeLogLevel(const ENUM_LOG_LEVEL log_level=LOG_LEVEL_ERROR_MSG,const string symbol_name=NULL); //--- Возвращает торговый объект символа по тикету (1) позиции, (2) ордера CTradeObj *GetTradeObjByPosition(const ulong ticket); CTradeObj *GetTradeObjByOrder(const ulong ticket); //--- Открывает позицию (1) Buy, (2) Sell bool OpenBuy(const double volume,const string symbol,const ulong magic=ULONG_MAX,double sl=0,double tp=0,const string comment=NULL); bool OpenSell(const double volume,const string symbol,const ulong magic=ULONG_MAX,double sl=0,double tp=0,const string comment=NULL); //--- Модифицирует позицию bool ModifyPosition(const ulong ticket,const double sl=WRONG_VALUE,const double tp=WRONG_VALUE); //--- Закрывает позицию (1) полностью, (2) частично, (3) встречной bool ClosePosition(const ulong ticket); bool ClosePositionPartially(const ulong ticket,const double volume); bool ClosePositionBy(const ulong ticket,const ulong ticket_by); //--- Устанавливает отложенный ордер (1) BuyStop, (2) BuyLimit, (3) BuyStopLimit bool PlaceBuyStop(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); bool PlaceBuyLimit(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); bool PlaceBuyStopLimit(const double volume, const string symbol, const double price_stop, const double price_limit, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); //--- Устанавливает отложенный ордер (1) SellStop, (2) SellLimit, (3) SellStopLimit bool PlaceSellStop(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); bool PlaceSellLimit(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); bool PlaceSellStopLimit(const double volume, const string symbol, const double price_stop, const double price_limit, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC); //--- Модифицирует отложенный ордер bool ModifyOrder(const ulong ticket, const double price=WRONG_VALUE, const double sl=WRONG_VALUE, const double tp=WRONG_VALUE, const double stoplimit=WRONG_VALUE, datetime expiration=WRONG_VALUE, ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE); //--- Удаляет отложенный ордер bool DeleteOrder(const ulong ticket); //--- Возвращает (1) милисекунды, (2) причину, (3) источник события из его long-значения ushort EventMSC(const long lparam) const { return this.LongToUshortFromByte(lparam,0); } ushort EventReason(const long lparam) const { return this.LongToUshortFromByte(lparam,1); } ushort EventSource(const long lparam) const { return this.LongToUshortFromByte(lparam,2); } //--- Конструктор/Деструктор CEngine(); ~CEngine(); }; //+------------------------------------------------------------------+
За пределами тела класса напишем реализацию объявленных методов.
Метод для открытия позиции Buy:
//+------------------------------------------------------------------+ //| Открывает позицию Buy | //+------------------------------------------------------------------+ bool CEngine::OpenBuy(const double volume,const string symbol,const ulong magic=ULONG_MAX,double sl=0,double tp=0,const string comment=NULL) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.OpenPosition(POSITION_TYPE_BUY,volume,sl,tp,magic,trade_obj.GetDeviation(),comment); } //+------------------------------------------------------------------+
В метод передаются:
- объём открываемой позиции (задаётся обязательно),
- символ, на котором нужно открыть позицию (задаётся обязательно),
- магик, который будет присвоен открытой позиции (по умолчанию будет 0),
- цена StopLoss позиции (по умолчанию — нет),
- цена TakeProfit позиции (по умолчанию — нет),
- комментарий позиции (по умолчанию — имя программы+" by DoEasy")
Получаем объект-символ по имени символа. Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Получаем торговый объект из объекта-символа. Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Возвращаем результат работы метода открытия позиции торгового объекта,
рассмотренный нами ранее.
Метод для открытия позиции Sell:
//+------------------------------------------------------------------+ //| Открывает позицию Sell | //+------------------------------------------------------------------+ bool CEngine::OpenSell(const double volume,const string symbol,const ulong magic=ULONG_MAX,double sl=0,double tp=0,const string comment=NULL) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.OpenPosition(POSITION_TYPE_SELL,volume,sl,tp,magic,trade_obj.GetDeviation(),comment); } //+------------------------------------------------------------------+
В метод передаются:
- объём открываемой позиции (задаётся обязательно),
- символ, на котором нужно открыть позицию (задаётся обязательно),
- магик, который будет присвоен открытой позиции (по умолчанию будет 0),
- цена StopLoss позиции (по умолчанию — нет),
- цена TakeProfit позиции (по умолчанию — нет),
- комментарий позиции (по умолчанию — имя программы+" by DoEasy")
Получаем объект-символ по имени символа. Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Получаем торговый объект из объекта-символа. Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Возвращаем результат работы метода открытия позиции торгового объекта,
рассмотренный нами ранее.
Метод для модификации цен StopLoss и TakeProfit позиции:
//+------------------------------------------------------------------+ //| Модифицирует позицию | //+------------------------------------------------------------------+ bool CEngine::ModifyPosition(const ulong ticket,const double sl=WRONG_VALUE,const double tp=WRONG_VALUE) { CTradeObj *trade_obj=this.GetTradeObjByPosition(ticket); if(trade_obj==NULL) { //--- Ошибка. Не удалось получить торговый объект ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.ModifyPosition(ticket,sl,tp); } //+------------------------------------------------------------------+
В метод передаются:
- тикет модифицируемой позиции (задаётся обязательно),
- новая цена для StopLoss позиции (по умолчанию — без изменений),
- новая цена для TakeProfit позиции (по умолчанию — без изменений)
Получаем торговый объект по тикету позиции посредством метода
GetTradeObjByPosition(), который рассмотрим ниже.
Если объект
получить не удалось — выводим об этом сообщение и возвращаем false.
Возвращаем результат работы метода модификации позиции торгового объекта,
рассмотренный нами ранее.
Метод для полного закрытия позиции:
//+------------------------------------------------------------------+ //| Закрывает позицию полностью | //+------------------------------------------------------------------+ bool CEngine::ClosePosition(const ulong ticket) { CTradeObj *trade_obj=this.GetTradeObjByPosition(ticket); if(trade_obj==NULL) { //--- Ошибка. Не удалось получить торговый объект ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.ClosePosition(ticket); } //+------------------------------------------------------------------+
В метод передаётся тикет закрываемой позиции.
Получаем торговый объект по тикету позиции посредством метода
GetTradeObjByPosition(), который рассмотрим ниже.
Если объект
получить не удалось — выводим об этом сообщение и возвращаем false.
Возвращаем результат работы метода закрытия позиции торгового объекта,
рассмотренный нами ранее.
Метод для частичного закрытия позиции:
//+------------------------------------------------------------------+ //| Закрывает позицию частично | //+------------------------------------------------------------------+ bool CEngine::ClosePositionPartially(const ulong ticket,const double volume) { CTradeObj *trade_obj=this.GetTradeObjByPosition(ticket); if(trade_obj==NULL) { //--- Ошибка. Не удалось получить торговый объект ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } CSymbol *symbol=this.GetSymbolObjByName(trade_obj.GetSymbol()); return trade_obj.ClosePositionPartially(ticket,symbol.NormalizedLot(volume)); } //+------------------------------------------------------------------+
В метод передаётся тикет позиции и закрываемый объём.
Получаем торговый объект по тикету позиции посредством метода
GetTradeObjByPosition(), который рассмотрим ниже.
Если объект
получить не удалось — выводим об этом сообщение и возвращаем false.
Получаем объект-символ по
имени символа торгового объекта.
Возвращаем результат работы
метода частичного закрытия позиции торгового объекта, рассмотренный нами ранее. В
метод передаём нормализованный закрываемый объём.
Метод для закрытия позиции встречной:
//+------------------------------------------------------------------+ //| Закрывает позицию встречной | //+------------------------------------------------------------------+ bool CEngine::ClosePositionBy(const ulong ticket,const ulong ticket_by) { CTradeObj *trade_obj_pos=this.GetTradeObjByPosition(ticket); if(trade_obj_pos==NULL) { //--- Ошибка. Не удалось получить торговый объект ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } CTradeObj *trade_obj_by=this.GetTradeObjByPosition(ticket_by); if(trade_obj_by==NULL) { //--- Ошибка. Не удалось получить торговый объект ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj_pos.ClosePositionBy(ticket,ticket_by); } //+------------------------------------------------------------------+
В метод передаются:
- тикет закрываемой позиции,
- тикет встречной позиции
Получаем торговый объект по тикету закрываемой позиции посредством метода
GetTradeObjByPosition(), который рассмотрим ниже.
Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Получаем торговый объект по тикету встречной позиции посредством метода
GetTradeObjByPosition(), который рассмотрим ниже.
Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Возвращаем результат работы метода закрытия позиции встречной торгового объекта,
рассмотренный нами ранее.
Метод для установки отложенного ордера BuyStop:
//+------------------------------------------------------------------+ //| Устанавливает отложенный ордер BuyStop | //+------------------------------------------------------------------+ bool CEngine::PlaceBuyStop(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_BUY_STOP,volume,price,sl,tp,0,magic,expiration,type_time,comment); } //+------------------------------------------------------------------+
В метод передаются:
- объём устанавливаемого ордера (задаётся обязательно),
- символ, на котором необходимо выставить ордер (задаётся обязательно),
- цена, на которую необходимо выставить ордер (задаётся обязательно),
- цена StopLoss устанавливаемого ордера (по умолчанию — нет),
- цена TakeProfit устанавливаемого ордера (по умолчанию — нет),
- магик устанавливаемого ордера (по умолчанию — 0),
- комментарий устанавливаемого ордера (по умолчанию — имя программы+" by DoEasy"),
- время жизни устанавливаемого ордера (по умолчанию — неограниченно),
- тип времени жизни устанавливаемого ордера (по умолчанию — до явной отмены)
Получаем объект-символ по имени символа. Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Получаем торговый объект из объекта-символа. Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Возвращаем результат работы метода установки отложенного ордера торгового объекта,
рассмотренный нами ранее.
Метод для установки отложенного ордера BuyLimit:
//+------------------------------------------------------------------+ //| Устанавливает отложенный ордер BuyLimit | //+------------------------------------------------------------------+ bool CEngine::PlaceBuyLimit(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_BUY_LIMIT,volume,price,sl,tp,0,magic,expiration,type_time,comment); } //+------------------------------------------------------------------+
В метод передаются:
- объём устанавливаемого ордера (задаётся обязательно),
- символ, на котором необходимо выставить ордер (задаётся обязательно),
- цена, на которую необходимо выставить ордер (задаётся обязательно),
- цена StopLoss устанавливаемого ордера (по умолчанию — нет),
- цена TakeProfit устанавливаемого ордера (по умолчанию — нет),
- магик устанавливаемого ордера (по умолчанию — 0),
- комментарий устанавливаемого ордера (по умолчанию — имя программы+" by DoEasy"),
- время жизни устанавливаемого ордера (по умолчанию — неограниченно),
- тип времени жизни устанавливаемого ордера (по умолчанию — до явной отмены)
Получаем объект-символ по имени символа. Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Получаем торговый объект из объекта-символа. Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Возвращаем результат работы метода установки отложенного ордера торгового объекта,
рассмотренный нами ранее.
Метод для установки отложенного ордера BuyStopLimit:
//+------------------------------------------------------------------+ //| Устанавливает отложенный ордер BuyStopLimit | //+------------------------------------------------------------------+ bool CEngine::PlaceBuyStopLimit(const double volume, const string symbol, const double price_stop, const double price_limit, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { #ifdef __MQL5__ CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_BUY_STOP_LIMIT,volume,price_stop,sl,tp,price_limit,magic,expiration,type_time,comment); //--- MQL4 #else return true; #endif } //+------------------------------------------------------------------+
В метод передаются:
- объём устанавливаемого ордера (задаётся обязательно),
- символ, на котором необходимо выставить ордер (задаётся обязательно),
- цена, на которую необходимо выставить BuyStop-ордер (задаётся обязательно),
- цена, на которую необходимо выставить BuyLimit-ордер при срабатывании BuyStop-ордера (задаётся обязательно),
- цена StopLoss устанавливаемого ордера (по умолчанию — нет),
- цена TakeProfit устанавливаемого ордера (по умолчанию — нет),
- магик устанавливаемого ордера (по умолчанию — 0),
- комментарий устанавливаемого ордера (по умолчанию — имя программы+" by DoEasy"),
- время жизни устанавливаемого ордера (по умолчанию — неограниченно),
- тип времени жизни устанавливаемого ордера (по умолчанию — до явной отмены)
Для MQL5:
Получаем объект-символ по имени символа. Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Получаем торговый объект из объекта-символа. Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Возвращаем результат работы метода установки отложенного ордера торгового объекта,
рассмотренный нами ранее.
Для MQL4:
Ничего не делаем — возвращаем true.
Методы для установки отложенных ордеров SellStop, SellLimit и SellStopLimit:
//+------------------------------------------------------------------+ //| Устанавливает отложенный ордер SellStop | //+------------------------------------------------------------------+ bool CEngine::PlaceSellStop(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_SELL_STOP,volume,price,sl,tp,0,magic,expiration,type_time,comment); } //+------------------------------------------------------------------+ //| Устанавливает отложенный ордер SellLimit | //+------------------------------------------------------------------+ bool CEngine::PlaceSellLimit(const double volume, const string symbol, const double price, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_SELL_LIMIT,volume,price,sl,tp,0,magic,expiration,type_time,comment); } //+------------------------------------------------------------------+ //| Устанавливает отложенный ордер SellStopLimit | //+------------------------------------------------------------------+ bool CEngine::PlaceSellStopLimit(const double volume, const string symbol, const double price_stop, const double price_limit, const double sl=0, const double tp=0, const ulong magic=WRONG_VALUE, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC) { #ifdef __MQL5__ CSymbol *symbol_obj=this.GetSymbolObjByName(symbol); if(symbol_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_LIST)); return false; } CTradeObj *trade_obj=symbol_obj.GetTradeObj(); if(trade_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.SetOrder(ORDER_TYPE_SELL_STOP_LIMIT,volume,price_stop,sl,tp,price_limit,magic,expiration,type_time,comment); //--- MQL4 #else return true; #endif } //+------------------------------------------------------------------+
В этих методах всё аналогично методам установки отложенных ордеров на покупку.
Метод для модификации отложенного ордера:
//+------------------------------------------------------------------+ //| Модифицирует отложенный ордер | //+------------------------------------------------------------------+ bool CEngine::ModifyOrder(const ulong ticket, const double price=WRONG_VALUE, const double sl=WRONG_VALUE, const double tp=WRONG_VALUE, const double stoplimit=WRONG_VALUE, datetime expiration=WRONG_VALUE, ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE) { CTradeObj *trade_obj=this.GetTradeObjByOrder(ticket); if(trade_obj==NULL) { //--- Ошибка. Не удалось получить торговый объект ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.ModifyOrder(ticket,price,sl,tp,stoplimit,expiration,type_time); } //+------------------------------------------------------------------+
В метод передаются:
- тикет модифицируемого ордера (задаётся обязательно),
- новая цена установки отложенного ордера (по умолчанию — без изменений),
- новая цена StopLoss отложенного ордера (по умолчанию — без изменений),
- новая цена TakeProfit отложенного ордера (по умолчанию — без изменений),
- новая цена StopLimit отложенного ордера (по умолчанию — без изменений),
- новое время экспирации отложенного ордера (по умолчанию — без изменений),
- новый режим времени жизни отложенного ордера (по умолчанию — без изменений)
Получаем торговый объект по тикету модифицируемого ордера посредством метода
GetTradeObjByOrder(), который рассмотрим ниже.
Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Возвращаем результат работы метода модификации отложенного ордера торгового
объекта, рассмотренный нами ранее.
Метод для удаления отложенного ордера:
//+------------------------------------------------------------------+ //| Удаляет отложенный ордер | //+------------------------------------------------------------------+ bool CEngine::DeleteOrder(const ulong ticket) { CTradeObj *trade_obj=this.GetTradeObjByOrder(ticket); if(trade_obj==NULL) { //--- Ошибка. Не удалось получить торговый объект ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } return trade_obj.DeleteOrder(ticket); } //+------------------------------------------------------------------+
В метод передаётся тикет удаляемого ордера.
Получаем торговый объект по тикету ордера посредством метода
GetTradeObjByOrder(), который рассмотрим ниже.
Если
объект получить не удалось — выводим об этом сообщение и возвращаем false.
Возвращаем результат работы метода удаления отложенного ордера торгового объекта,
рассмотренный нами ранее.
Методы, возвращающие торговый объект символа по тикету позициии ордера:
//+------------------------------------------------------------------+ //| Возвращает торговый объект символа по тикету позиции | //+------------------------------------------------------------------+ CTradeObj *CEngine::GetTradeObjByPosition(const ulong ticket) { //--- Получаем список открытых позиций CArrayObj *list=this.GetListMarketPosition(); //--- Если не удалось получить список открытых позиций - выводим сообщение и возвращаем NULL if(list==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_ENG_FAILED_GET_MARKET_POS_LIST)); return NULL; } //--- Если список пустой (Нет открытых позиций) - выводим сообщение и возвращаем NULL if(list.Total()==0) { ::Print(DFUN,CMessage::Text(MSG_ENG_NO_OPEN_POSITIONS)); return NULL; } //--- Фильтруем список по тикету list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,ticket,EQUAL); //--- Если не удалось получить список открытых позиций - выводим сообщение и возвращаем NULL if(list==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_ENG_FAILED_GET_MARKET_POS_LIST)); return NULL; } //--- Если список пустой (нет искомого тикета) - выводим сообщение и возвращаем NULL if(list.Total()==0) { //--- Ошибка. Нет открытой позиции с тикетом #ticket ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_NO_OPEN_POSITION_WITH_TICKET),(string)ticket); return NULL; } //--- Получаем позицию с тикетом #ticket из полученного списка COrder *pos=list.At(0); //--- Если не удалось получить объект-позицию - выводим сообщение и возвращаем NULL if(pos==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_POS_OBJ)); return NULL; } //--- Получаем объект-символ по имени CSymbol * symbol_obj=this.GetSymbolObjByName(pos.Symbol()); //--- Если не удалось получить объект-символ - выводим сообщение и возвращаем NULL if(symbol_obj==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ)); return NULL; } //--- Получаем и возвращаем торговый объект из объекта-символа CTradeObj *obj=symbol_obj.GetTradeObj(); return obj; } //+------------------------------------------------------------------+ //| Возвращает торговый объект символа по тикету ордера | //+------------------------------------------------------------------+ CTradeObj *CEngine::GetTradeObjByOrder(const ulong ticket) { //--- Получаем список установленных ордеров CArrayObj *list=this.GetListMarketPendings(); //--- Если не удалось получить список установленных ордеров - выводим сообщение и возвращаем NULL if(list==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_ENG_FAILED_GET_PENDING_ORD_LIST)); return NULL; } //--- Если список пустой (Нет установленных ордеров) - выводим сообщение и возвращаем NULL if(list.Total()==0) { ::Print(DFUN,CMessage::Text(MSG_ENG_NO_PLACED_ORDERS)); return NULL; } //--- Фильтруем список по тикету list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,ticket,EQUAL); //--- Если не удалось получить список установленных ордеров - выводим сообщение и возвращаем NULL if(list==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_ENG_FAILED_GET_PENDING_ORD_LIST)); return NULL; } //--- Если список пустой (нет искомого тикета) - выводим сообщение и возвращаем NULL if(list.Total()==0) { //--- Ошибка. Нет установленного ордера с тикетом #ticket ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_NO_PLACED_ORDER_WITH_TICKET),(string)ticket); return NULL; } //--- Получаем ордер с тикетом #ticket из полученного списка COrder *ord=list.At(0); //--- Если не удалось получить объект-ордер - выводим сообщение и возвращаем NULL if(ord==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_ORD_OBJ)); return NULL; } //--- Получаем объект-символ по имени CSymbol *symbol_obj=this.GetSymbolObjByName(ord.Symbol()); //--- Если не удалось получить объект-символ - выводим сообщение и возвращаем NULL if(symbol_obj==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ)); return NULL; } //--- Получаем и возвращаем торговый объект из объекта-символа CTradeObj *obj=symbol_obj.GetTradeObj(); return obj; } //+--------------------------------------------------------------------+
Оба метода практически одинаковы, за исключением того, что в
первом методе мы получаем список всех открытых позиций, а во
втором — всех установленных отложенных ордеров. Далее — логика абсолютно одинаковая у обоих методов, и она вся расписана в
комментариях к коду — оставим для самостоятельного изучения.
Методы для установки политики исполнения и корректной политики исполнения в торговых объектах всех символов, находящихся в списке коллекции символов, или для одного заданного символа:
//+------------------------------------------------------------------+ //| Устанавливает корректную политику исполнения | //| для торговых объектов всех символов | //+------------------------------------------------------------------+ void CEngine::SetTradeCorrectTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL) { //--- Объявляем пустой указатель на объект-символ CSymbol *symbol=NULL; //--- Если имя символа, переданного во входных параметрах метода, не задано - значит устанавливаем политику исполнения всем символам if(symbol_name==NULL) { //--- получаем список всех используемых символов CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); //--- В цикле по списку объектов-символов for(int i=0;i<total;i++) { //--- получаем очередной объект-символ symbol=list.At(i); if(symbol==NULL) continue; //--- получаем из объекта-символа его торговый объект CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; //--- устанавливаем торговому объекту корректную политику исполнения (по умолчанию "всё или ничего") obj.SetTypeFilling(symbol.GetCorrectTypeFilling(type)); } } //--- Если во входных параметрах метода имя символа задано - значит устанавливаем политику исполнения только заданному символу else { //--- Получаем объект-символ по имени символа symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; //--- получаем из объекта-символа его торговый объект CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; //--- устанавливаем торговому объекту корректную политику исполнения (по умолчанию "всё или ничего") obj.SetTypeFilling(symbol.GetCorrectTypeFilling(type)); } } //+------------------------------------------------------------------+ //| Устанавливает политику исполнения | //| для торговых объектов всех символов | //+------------------------------------------------------------------+ void CEngine::SetTradeTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_FOK,const string symbol_name=NULL) { //--- Объявляем пустой указатель на объект-символ CSymbol *symbol=NULL; //--- Если имя символа, переданного во входных параметрах метода, не задано - значит устанавливаем политику исполнения всем символам if(symbol_name==NULL) { //--- получаем список всех используемых символов CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); //--- В цикле по списку объектов-символов for(int i=0;i<total;i++) { //--- получаем очередной объект-символ symbol=list.At(i); if(symbol==NULL) continue; //--- получаем из объекта-символа его торговый объект CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; //--- устанавливаем торговому объекту политику исполнения, переданную в метод во входных параметрах (по умолчанию "всё или ничего") obj.SetTypeFilling(type); } } //--- Если во входных параметрах метода имя символа задано - значит устанавливаем политику исполнения только заданному символу else { //--- Получаем объект-символ по имени символа symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; //--- получаем из объекта-символа его торговый объект CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; //--- устанавливаем торговому объекту политику исполнения, переданную в метод во входных параметрах (по умолчанию "всё или ничего") obj.SetTypeFilling(type); } } //+------------------------------------------------------------------+
В методы передаются политика исполнения (по умолчанию "всё или ничего") и символ (по умолчанию — все символы из коллекции символов).
Логика методов расписана в комментариях к кодам и, думаю, она понятна. В любом случае все вопросы можно задать в обсуждении к статье.
Остальные методы установки умолчательных значений для торговых объектов символов имеют совершенно такую же логику, и в них уже нет комментариев. В любом случае — можно изучить логику по этим двум методам.
Все оставшиеся методы установки умолчательных значений торговым объектам символов:
//+------------------------------------------------------------------+ //| Устанавливает корректный тип истечения ордера | //| для торговых объектов всех символов | //+------------------------------------------------------------------+ void CEngine::SetTradeCorrectTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetTypeExpiration(symbol.GetCorrectTypeExpiration(type)); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetTypeExpiration(symbol.GetCorrectTypeExpiration(type)); } } //+------------------------------------------------------------------+ //| Устанавливает тип истечения ордера | //| для торговых объектов всех символов | //+------------------------------------------------------------------+ void CEngine::SetTradeTypeExpiration(const ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetTypeExpiration(type); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetTypeExpiration(type); } } //+------------------------------------------------------------------+ //| Устанавливает магик для торговых объектов всех символов | //+------------------------------------------------------------------+ void CEngine::SetTradeMagic(const ulong magic,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetMagic(magic); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetMagic(magic); } } //+------------------------------------------------------------------+ //| Устанавливает комментарий для торговых объектов всех символов | //+------------------------------------------------------------------+ void CEngine::SetTradeComment(const string comment,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetComment(comment); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetComment(comment); } } //+------------------------------------------------------------------+ //| Устанавливает размер проскальзывания | //| для торговых объектов всех символов | //+------------------------------------------------------------------+ void CEngine::SetTradeDeviation(const ulong deviation,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetDeviation(deviation); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetDeviation(deviation); } } //+------------------------------------------------------------------+ //| Устанавливает объём для торговых объектов всех символов | //+------------------------------------------------------------------+ void CEngine::SetTradeVolume(const double volume=0,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetVolume(volume!=0 ? symbol.NormalizedLot(volume) : symbol.LotsMin()); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetVolume(volume!=0 ? symbol.NormalizedLot(volume) : symbol.LotsMin()); } } //+------------------------------------------------------------------+ //| Устанавливает срок истечения ордера | //| для торговых объектов всех символов | //+------------------------------------------------------------------+ void CEngine::SetTradeExpiration(const datetime expiration=0,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetExpiration(expiration); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetExpiration(expiration); } } //+------------------------------------------------------------------+ //| Устанавливает флаг асинхронной отправки торговых запросов | //| для торговых объектов всех символов | //+------------------------------------------------------------------+ void CEngine::SetTradeAsyncMode(const bool mode=false,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetAsyncMode(mode); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetAsyncMode(mode); } } //+------------------------------------------------------------------+ //| Устанавливает уровень логирования торговых запросов | //| для торговых объектов всех символов | //+------------------------------------------------------------------+ void CEngine::SetTradeLogLevel(const ENUM_LOG_LEVEL log_level=LOG_LEVEL_ERROR_MSG,const string symbol_name=NULL) { CSymbol *symbol=NULL; if(symbol_name==NULL) { CArrayObj *list=this.GetListAllUsedSymbols(); if(list==NULL || list.Total()==0) return; int total=list.Total(); for(int i=0;i<total;i++) { symbol=list.At(i); if(symbol==NULL) continue; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) continue; obj.SetLogLevel(log_level); } } else { symbol=this.GetSymbolObjByName(symbol_name); if(symbol==NULL) return; CTradeObj *obj=symbol.GetTradeObj(); if(obj==NULL) return; obj.SetLogLevel(log_level); } } //+------------------------------------------------------------------+
Мы написали все вспомогательные временные методы в классе CEngine для тестирования торговых объектов символов.
Благодаря имеющимся кроссплатформенным торговым методам (пусть пока и в зачаточном состоянии), мы имеем возможность уйти в тестовом
советнике от условной компиляции для MQL5 или MQL4 — теперь все торговые функции тестового советника будут одинаковыми для любой
платформы. И далее мы будем развивать работу с торговыми классами библиотеки так, чтобы в итоге получить весь необходимый функционал
для спокойной и беспроблемной работы со своими программами.
Тестирование базового торгового объекта
Для тестирования торговых объектов символов, возьмём тестовый советник из
прошлой статьи и подправим его торговые функции для работы с торговыми объектами символов. Не забываем, что пока у нас нет вообще
никаких проверок корректности значений торговых запросов, но это даёт нам возможность тестирования реакции на ошибочные параметры,
которую будем делать далее.
Сохраним советник в новой папке \MQL5\Experts\TestDoEasy\Part21\ под новым именем TestDoEasyPart21.mq5.
В первую очередь удалим подключение торгового класса CTrade стандартной библиотеки и объявление торгового объекта с типом класса CTrade:
//+------------------------------------------------------------------+ //| TestDoEasyPart20.mq5 | //| Copyright 2018, 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" //--- includes #include <DoEasy\Engine.mqh> #ifdef __MQL5__ #include <Trade\Trade.mqh> #endif //--- enums enum ENUM_BUTTONS { BUTT_BUY, BUTT_BUY_LIMIT, BUTT_BUY_STOP, BUTT_BUY_STOP_LIMIT, BUTT_CLOSE_BUY, BUTT_CLOSE_BUY2, BUTT_CLOSE_BUY_BY_SELL, BUTT_SELL, BUTT_SELL_LIMIT, BUTT_SELL_STOP, BUTT_SELL_STOP_LIMIT, BUTT_CLOSE_SELL, BUTT_CLOSE_SELL2, BUTT_CLOSE_SELL_BY_BUY, BUTT_DELETE_PENDING, BUTT_CLOSE_ALL, BUTT_PROFIT_WITHDRAWAL, BUTT_SET_STOP_LOSS, BUTT_SET_TAKE_PROFIT, BUTT_TRAILING_ALL }; #define TOTAL_BUTT (20) //--- structures struct SDataButt { string name; string text; }; //--- input variables input ulong InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 50; // StopLoss in points input uint InpTakeProfit = 50; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpSlippage = 0; // Slippage in points input double InpWithdrawal = 10; // Withdrawal funds (in tester) input uint InpButtShiftX = 40; // Buttons X shift input uint InpButtShiftY = 10; // Buttons Y shift input uint InpTrailingStop = 50; // Trailing Stop (points) input uint InpTrailingStep = 20; // Trailing Step (points) input uint InpTrailingStart = 0; // Trailing Start (points) input uint InpStopLossModify = 20; // StopLoss for modification (points) input uint InpTakeProfitModify = 60; // TakeProfit for modification (points) input ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; // Mode of used symbols list input string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) //--- global variables CEngine engine; #ifdef __MQL5__ CTrade trade; #endif SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ulong magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint slippage; bool trailing_on; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; int used_symbols_mode; string used_symbols; string array_used_symbols[]; //+------------------------------------------------------------------+
В обработчике OnInit() удалим установку параметров объекту trade торгового класса CTrade:
//--- Установка состояния кнопки активизации трейлингов ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on); //--- Установка параметров торгового класса CTrade #ifdef __MQL5__ trade.SetDeviationInPoints(slippage); trade.SetExpertMagicNumber(magic_number); trade.SetTypeFillingBySymbol(Symbol()); trade.SetMarginMode(); trade.LogLevel(LOG_LEVEL_NO); #endif //--- Создание и проверка файлов ресурсов
А далее всё просто — при помощи поиска (Ctrl+F) ищем вхождение строки "trade" и заменяем вызов торговых методов стандартной библиотеки на свои.
Например, это:
COrder* position=list_positions.At(index); if(position!=NULL) { //--- Получаем тикет позиции с наибольшей прибылью и закрываем позицию по тикету #ifdef __MQL5__ trade.PositionClose(position.Ticket()); #else PositionClose(position.Ticket(),position.Volume()); #endif }
меняем на это:
COrder* position=list_positions.At(index); if(position!=NULL) { //--- Получаем тикет позиции с наибольшей прибылью и закрываем позицию по тикету engine.ClosePosition(position.Ticket()); }
И далее, по ходу нахождения вызовов торговых методов СБ, просто меняем на вызов своих методов.
Рассмотрим получившийся обработчик нажатия кнопок панели. Все вызовы новых торговых методов помечены цветом:
//+------------------------------------------------------------------+ //| Обработка нажатий кнопок | //+------------------------------------------------------------------+ void PressButtonEvents(const string button_name) { string comment=""; //--- Преобразуем имя кнопки в её строковый идентификатор string button=StringSubstr(button_name,StringLen(prefix)); //--- Если кнопка в нажатом состоянии if(ButtonState(button_name)) { //--- Если нажата кнопка BUTT_BUY: Открыть позицию Buy if(button==EnumToString(BUTT_BUY)) { //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY,0,takeprofit); //--- Открываем позицию Buy engine.OpenBuy(lot,Symbol(),magic_number,sl,tp); // Нет комментария - будет установлен комментарий по умолчанию } //--- Если нажата кнопка BUTT_BUY_LIMIT: Выставить BuyLimit else if(button==EnumToString(BUTT_BUY_LIMIT)) { //--- Получаем корректную цену установки ордера относительно уровня StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_pending); //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня установки ордера с учётом StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,takeprofit); //--- Устанавливаем ордер BuyLimit engine.PlaceBuyLimit(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный BuyLimit","Pending order BuyLimit")); } //--- Если нажата кнопка BUTT_BUY_STOP: Выставить BuyStop else if(button==EnumToString(BUTT_BUY_STOP)) { //--- Получаем корректную цену установки ордера относительно уровня StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня установки ордера с учётом StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set,takeprofit); //--- Устанавливаем ордер BuyStop engine.PlaceBuyStop(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный BuyStop","Pending order BuyStop")); } //--- Если нажата кнопка BUTT_BUY_STOP_LIMIT: Выставить BuyStopLimit else if(button==EnumToString(BUTT_BUY_STOP_LIMIT)) { //--- Получаем корректную цену установки ордера BuyStop относительно уровня StopLevel double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- Рассчитываем цену установки ордера BuyLimit относительно уровня установки BuyStop с учётом уровня StopLevel double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_stoplimit,price_set_stop); //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня установки ордера с учётом StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,takeprofit); //--- Устанавливаем ордер BuyStopLimit engine.PlaceBuyStopLimit(lot,Symbol(),price_set_stop,price_set_limit,sl,tp,magic_number,TextByLanguage("Отложенный BuyStopLimit","Pending order BuyStopLimit")); } //--- Если нажата кнопка BUTT_SELL: Открыть позицию Sell else if(button==EnumToString(BUTT_SELL)) { //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL,0,takeprofit); //--- Открываем позицию Sell engine.OpenSell(lot,Symbol(),magic_number,sl,tp); // Нет комментария - будет установлен комментарий по умолчанию } //--- Если нажата кнопка BUTT_SELL_LIMIT: Выставить SellLimit else if(button==EnumToString(BUTT_SELL_LIMIT)) { //--- Получаем корректную цену установки ордера относительно уровня StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_pending); //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня установки ордера с учётом StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,takeprofit); //--- Устанавливаем ордер SellLimit engine.PlaceSellLimit(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный SellLimit","Pending order SellLimit")); } //--- Если нажата кнопка BUTT_SELL_STOP: Выставить SellStop else if(button==EnumToString(BUTT_SELL_STOP)) { //--- Получаем корректную цену установки ордера относительно уровня StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня установки ордера с учётом StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set,takeprofit); //--- Устанавливаем ордер SellStop engine.PlaceSellStop(lot,Symbol(),price_set,sl,tp,magic_number,TextByLanguage("Отложенный SellStop","Pending order SellStop")); } //--- Если нажата кнопка BUTT_SELL_STOP_LIMIT: Выставить SellStopLimit else if(button==EnumToString(BUTT_SELL_STOP_LIMIT)) { //--- Получаем корректную цену установки ордера SellStop относительно уровня StopLevel double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- Рассчитываем цену установки ордера SellLimit относительно уровня установки SellStop с учётом уровня StopLevel double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_stoplimit,price_set_stop); //--- Получаем корректные цены StopLoss и TakeProfit относительно уровня установки ордера с учётом StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,takeprofit); //--- Устанавливаем ордер SellStopLimit engine.PlaceSellStopLimit(lot,Symbol(),price_set_stop,price_set_limit,sl,tp,magic_number,TextByLanguage("Отложенный SellStopLimit","Pending order SellStopLimit")); } //--- Если нажата кнопка BUTT_CLOSE_BUY: Закрыть Buy с максимальной прибылью else if(button==EnumToString(BUTT_CLOSE_BUY)) { //--- Получаем список всех открытых позиций CArrayObj* list=engine.GetListMarketPosition(); //--- Выбираем из списка только позиции Buy list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Сортируем список по прибыли с учётом комиссии и свопа list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Получаем индекс позиции Buy с наибольшей прибылью int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { //--- Получаем объект-позицию 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_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Сортируем список по прибыли с учётом комиссии и свопа list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Получаем индекс позиции Buy с наибольшей прибылью int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Если счёт хеджевый, закрываем половину позиции Buy по тикету if(engine.IsHedge()) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); //--- Если счёт неттинговый, открываем позицию Sell с половиной объёма позиции Buy else engine.OpenSell(NormalizeLot(position.Symbol(),position.Volume()/2.0),Symbol(),magic_number,position.StopLoss(),position.TakeProfit(),"Частичное закрытие Buy #"+(string)position.Ticket()); } } } //--- Если нажата кнопка BUTT_CLOSE_BUY_BY_SELL: Закрыть Buy с максимальной прибылью встречной Sell с максимальной прибылью else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)) { //--- Если счёт хеджевый if(engine.IsHedge()) { //--- Получаем список всех открытых позиций CArrayObj* list_buy=engine.GetListMarketPosition(); //--- Выбираем из списка только позиции Buy list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Сортируем список по прибыли с учётом комиссии и свопа list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Получаем индекс позиции Buy с наибольшей прибылью int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); //--- Получаем список всех открытых позиций CArrayObj* list_sell=engine.GetListMarketPosition(); //--- Выбираем из списка только позиции Sell list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Сортируем список по прибыли с учётом комиссии и свопа list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Получаем индекс позиции Sell с наибольшей прибылью int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE) { //--- Выбираем позицию Buy с наибольшей прибылью COrder* position_buy=list_buy.At(index_buy); //--- Выбираем позицию Sell с наибольшей прибылью COrder* position_sell=list_sell.At(index_sell); //--- Закрываем позицию 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_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_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Сортируем список по прибыли с учётом комиссии и свопа list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Получаем индекс позиции Sell с наибольшей прибылью int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Если счёт хеджевый, закрываем половину позиции Sell по тикету if(engine.IsHedge()) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); //--- Если счёт неттинговый, открываем позицию Buy с половиной объёма позиции Sell else engine.OpenBuy(NormalizeLot(position.Symbol(),position.Volume()/2.0),Symbol(),position.Magic(),position.StopLoss(),position.TakeProfit(),"Частичное закрытие Buy #"+(string)position.Ticket()); } } } //--- Если нажата кнопка BUTT_CLOSE_SELL_BY_BUY: Закрыть Sell с максимальной прибылью встречной Buy с максимальной прибылью else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)) { //--- Получаем список всех открытых позиций CArrayObj* list_sell=engine.GetListMarketPosition(); //--- Выбираем из списка только позиции Sell list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Сортируем список по прибыли с учётом комиссии и свопа list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Получаем индекс позиции Sell с наибольшей прибылью int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); //--- Получаем список всех открытых позиций CArrayObj* list_buy=engine.GetListMarketPosition(); //--- Выбираем из списка только позиции Buy list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Сортируем список по прибыли с учётом комиссии и свопа list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Получаем индекс позиции Buy с наибольшей прибылью int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE) { //--- Выбираем позицию Sell с наибольшей прибылью COrder* position_sell=list_sell.At(index_sell); //--- Выбираем позицию Buy с наибольшей прибылью COrder* position_buy=list_buy.At(index_buy); if(position_sell!=NULL && position_buy!=NULL) { //--- Закрываем позицию Sell встречной позицией Buy engine.ClosePositionBy((ulong)position_sell.Ticket(),(ulong)position_buy.Ticket()); } } } //--- Если нажата кнопка BUTT_CLOSE_ALL: Закрыть все позиции, начиная от позиции с наименьшим профитом else if(button==EnumToString(BUTT_CLOSE_ALL)) { //--- Получаем список всех открытых позиций CArrayObj* list=engine.GetListMarketPosition(); if(list!=NULL) { //--- Сортируем список по прибыли с учётом комиссии и свопа list.Sort(SORT_BY_ORDER_PROFIT_FULL); int total=list.Total(); //--- В цикле от позиции с наименьшей прибылью for(int i=0;i<total;i++) { COrder* position=list.At(i); if(position==NULL) continue; //--- закрываем каждую позицию по её тикету engine.ClosePosition((ulong)position.Ticket()); } } } //--- Если нажата кнопка BUTT_DELETE_PENDING: Удалить первый отложенный ордер else if(button==EnumToString(BUTT_DELETE_PENDING)) { //--- Получаем список всех ордеров CArrayObj* list=engine.GetListMarketPendings(); if(list!=NULL) { //--- Сортируем список по времени установки list.Sort(SORT_BY_ORDER_TIME_OPEN); 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)) ButtonState(button_name,false); //--- Если нажата кнопка BUTT_TRAILING_ALL else { //--- Поставим цвет активной кнопки ButtonState(button_name,true); trailing_on=true; } //--- перерисуем чарт ChartRedraw(); } //--- Вернём цвет неактивной кнопки (если это кнопка трейлинга) else if(button==EnumToString(BUTT_TRAILING_ALL)) { ButtonState(button_name,false); trailing_on=false; //--- перерисуем чарт ChartRedraw(); } } //+------------------------------------------------------------------+
Остальные доработанные таким же образом функции советника с вызовом методов торгового класса CTrade стандартной библиотеки мы здесь рассматривать не будем — всё есть в прикреплённых в конце статьи файлах.
Сейчас же мы просто скомпилируем советник и запустим его в тестере.
Понажимаем разные кнопки панели и убедимся, что торговые
объекты работают:
Наши первые торговые объекты символов работают как и задумывалось.
Впереди ещё много чего нужно с ними сделать для
полноценной и удобной с ними работы.
Что дальше
Далее у нас по плану создать полноценный класс, через который мы будем обращаться к торговым объектам символов.
Ниже прикреплены все файлы текущей версии библиотеки и файлы тестового советника. Их можно скачать и протестировать всё
самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.
Статьи этой серии:
Часть 1. Концепция, организация данных
Часть
2. Коллекция исторических ордеров и сделок
Часть 3. Коллекция рыночных ордеров и
позиций, организация поиска
Часть 4. Торговые события. Концепция
Часть 5. Классы и коллекция торговых событий. Отправка событий в программу
Часть 6. События на счёте с типом неттинг
Часть
7. События срабатывания StopLimit-ордеров, подготовка функционала для регистрации событий модификации ордеров и позиций
Часть
8. События модификации ордеров и позиций
Часть 9. Совместимость с MQL4 -
Подготовка данных
Часть 10. Совместимость с MQL4 - События открытия позиций и
активации отложенных ордеров
Часть 11. Совместимость с MQL4 - События закрытия
позиций
Часть 12. Класс объекта "аккаунт", коллекция объектов-аккаунтов
Часть 13. События объекта "аккаунт"
Часть
14. Объект "Символ"
Часть 15. Коллекция объектов-символов
Часть
16. События коллекции символов
Часть 17. Интерактивность объектов библиотеки
Часть 18. Интерактивность объекта-аккаунт и любых других объектов библиотеки
Часть 19. Класс сообщений библиотеки
Часть
20. Создание и хранение ресурсов программы
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Да, понял. Это такая "многослойная" реализация обработки ордеров. Проблема в "толще" оберток. Я запутался, какие методы относятся к механизмам, а какие к пользовательскому интерфейсу библиотеки. Сорри. Если бы оберток было меньше, понять было бы легче, но - это ваш выбор.
Понимаете, Пётр, я стараюсь дать не просто один рабочий метод, а даю множество возможностей.
В данной статье организован низкоуровневый доступ к торговым функциям. Всё, что делает торговый объект - это правильно раскладывает полученные параметры в функцию отправки ордера на сервер. Чтобы отправить торговый приказ, нужно получить доступ к коллекции символов, получить объект нужного символа, и из этого объекта получить торговый объект. Далее работать с торговым объектом. Тут я облегчил пользователю заполнение торгового приказа - достаточно передать нужные параметры. Но эти параметры необходимо самостоятельно проверить на корректность, так как торговый объект считает, что все параметры, переданные в него, уже корректны.
Это не удобно. Но такой доступ есть. Кому нужно, тот сам всё проверит и отправит торговый приказ. Никаких обработок ответа сервера нету. Это первое, что сделано.
В следующей статье будет описан торговый класс, в котором будет тот же набор торговых методов с теми же параметрами, но теперь всё будет проверяться на корректность и возвращаться в программу если параметры не корректны (или один из них, или нет возможности для торговли). Это второй этап - тут уже есть два способа - первый, который уже есть, и второй - когда библиотека сама проверяет корректность параметров, и если они все верные, то отсылается приказ, а если хоть один не верен, или нет возможности для торговли, то осуществляется просто возврат в управляющую программу. Тут уже будет организован более высокоуровневый доступ - с проверками корректности.
Но и это ещё не всё. Нам же необходимо реагировать на ошибки. И уже далее, в очередной статье, пойдём ещё дальше - будем обрабатывать полученные ошибки в параметрах торговых приказов (ещё до отправки их на сервер). Ведь можно отказаться при ошибке от открытия позиции, а можно и скорректировать параметры и отправить запрос повторно. Это уже третий уровень работы с торговыми объектами.
А далее будет ещё более высокоуровневый подход - всё будет работать в автоматическом режиме в соответствии с требуемыми настройками. И дойдёт дело и до отложенных запросов - когда советник будет ждать подходящего момента для повторной отсылки нужного приказа, либо просто будет знать в какой ситуации ему необходимо отправить запрос. Все такие отложенные запросы будут в его копилке находиться и ждать подходящего момента для отсылки запроса.
Поэтому, Пётр, судить пока преждевременно что-либо.
Вы предложили сделать единственный доступ к торговым функциям, а я хочу дать разнообразие методов - каждому по его потребностям. Хоть всем, конечно и не угодить.
Artyom Trishkin:
...
Мне понравилось Ваше пояснение, и в связи с этим, возникло внутреннее противоречие.
1. С одной стороны, предоставление пользователям доступа на все уровни движка библиотеки через построение многоярусной "оберточной" структуры, где вместо сжатых алгоритмов развернутые взаимосвязи, а алгоритмический "каркас" масштабирован для удобного восприятия, обеспечивает комфортное понимание устройства решений. Это красиво, эстетично, элегантно. Безусловно, это вопрос оформления и дружелюбности к пользователям. Точнее, дружелюбности к исследователям, потому что пользователям важнее результат и эффективность. Теперь посмотрим на решение с иной стороны.
2. Эффективность предполагает сжатость, минимальный набор сущностей и оберток, с кратчайшим доступом к данным. Все должно быть близко, чтобы не "тянуться" и не передавать "поклажи" параметров из обертки в обертку. Эффективность и развернутый, многоярусный, элегантный стиль со "странствующими" между обертками "отрядами" параметров - чуждые и почти несочетающиеся вещи.
//-----------------------------------
В связи с этим противоречием, я бы предложил компромисс. Частично ограничить рост количества оберток увеличив сжатость решений. В принципе, функции OrderSend() достаточно одной обертки. На крайней случай - несколько. Но, в этом случае, решение будет тяжело для понимая "исследователям", а доступ на "ярусы" усложниться. Поэтому, попробуйте сочетать сжатые, эффективные блоки и "лианы" ООП-джунглей, столь любимые программистами. И Вы найдете золотую середину.
...
А далее будет ещё более высокоуровневый подход - всё будет работать в автоматическом режиме в соответствии с требуемыми настройками. И дойдёт дело и до отложенных запросов - когда советник будет ждать подходящего момента для повторной отсылки нужного приказа, либо просто будет знать в какой ситуации ему необходимо отправить запрос. Все такие отложенные запросы будут в его копилке находиться и ждать подходящего момента для отсылки запроса.
Поэтому, Пётр, судить пока преждевременно что-либо.
Вы предложили сделать единственный доступ к торговым функциям, а я хочу дать разнообразие методов - каждому по его потребностям. Хоть всем, конечно и не угодить.
Разбитие библиотеки на уровни и предоставление возможности на них работать пользователям - очень хорошо. Но, не забывайте, что количество может погубить качество. Чем больше методов получит пользователь, тем больше вероятность что он в них запутается. Это нужно учитывать.
В остальном, я согласен. Многоуровневый подход к торговым функциям - это хорошо.
Спасибо, это важный и полезный труд....
Спасибо, это важный и полезный труд....
Пожалуйста