Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXX): Отложенные торговые запросы - управление объектами-запросами
Содержание
- Концепция
- Объект-пауза
- Немного оптимизации кода
- Класс управления объектами-отложенными запросами
- Тестирование
- Что дальше
Концепция
Начиная со статьи 26 мы постепенно создали и протестировали концепцию работы с отложенными торговыми запросами. В прошлой статье создали классы объектов отложенных запросов, соответствующие общей концепции объектов библиотеки. Сегодня займёмся классом, позволяющем управлять объектами отложенных запросов.
Изначально я хотел сделать независимый класс для управления отложенными запросами, в котором будут сконцентрированы все методы для работы с ними. Но вышло так, что основной торговый класс CTrading библиотеки и создаваемый новый класс управления отложенными запросами, настолько имеют тесные связи друг с другом, что проще оказалось сделать новый класс управления объектами-отложенными запросами наследником основного торгового класса.
Вся работа по контролю за объектами отложенных запросов производится в таймере класса, поэтому мы сделаем таймер основного торгового
класса виртуальным, и, соответственно, виртуальным будет и таймер класса управления отложенными запросами. Тогда всё, что относится к
таймеру основного торгового класса, мы будем прописывать в его таймере, а всё, что должно работать в классе управления объектами
отложенных запросов, будем прописывать в таймере этого класса.
Помимо класса управления объектами отложенных запросов мы создадим маленький класс для организации паузы — чтобы не использовать в будущем функцию Sleep(), которая останавливает выполнение программы на время задержки, и объект-пауза позволит нам не зависеть от тиков, чтобы в выходные можно было тестировать код, требующий какого-либо ожидания — пауза будет контролироваться в таймере.
Вводя хранение некоторых данных в свойстве ордера "Магический номер", мне пришлось раскидать по разным классам, в которых требовалось получение этих данных, одинаковые методы, возвращающие идентификаторы магика и групп, записанные в значении магика ордера. Однако, я несправедливо упустил из виду, что ранее нами был создан базовый объект всех объектов библиотеки, от которого мы можем наследовать вновь создаваемые объекты, и они в этом случае получат событийный функционал базового объекта, а также некоторые методы, которые одинаковы по своему принципу и назначению, и повторяются от класса к классу. Вот в него-то мы и перенесём запись и получение данных в магический номер, а также управление выводом сообщений в журнал — флаг, указывающий уровень логирования каждого класса, где есть вывод различных сообщений — логично унаследовать все эти классы от базового объекта и получить необходимые методы, чем каждый раз в каждом новом классе прописывать одно и то же.
Исходя из вышенаписанного, сегодня нам предстоит сделать класс объекта-паузы (его будем использовать в последующих статьях), провести некоторую оптимизацию готового кода по переносу повторяющихся методов разных классов в класс базового объекта, и создать класс управления отложенными запросами. И, естественно, мы доработаем классы объектов отложенных запросов: нам необходимо будет прописать в них дополнительные свойства, которые позволят сравнивать состояния двух взаимосвязанных объектов — текущее состояние свойств ордера и состояние соответствующих свойств объекта-запроса. Это нужно для регистрации факта завершения работы отложенного запроса — чтобы вовремя его удалить из списка активных запросов, находящихся в ожидании наступления времени отсылки запроса на сервер.
И, как уже стало обычным, в первую очередь пропишем в файле Datas.mqh индексы новых сообщений:
MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE, // Отложенный запрос на удаление отложенного ордера MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY, // Отложенный запрос на модификацию параметров отложенного ордера MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_VOLUME, // Фактический объем MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_PRICE, // Фактическая цена установки ордера MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_STOPLIMIT, // Фактическая цена установки StopLimit-ордера MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_SL, // Фактическая цена установки StopLoss-ордера MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TP, // Фактическая цена установки TakeProfit-ордера MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_FILLING, // Фактический тип заливки ордера MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_TIME, // Фактический тип экспирации ордера MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_EXPIRATION, // Фактическое время жизни ордера }; //+------------------------------------------------------------------+
и тексты, соответствующие этим новым индексам:
{"Отложенный запрос на удаление отложенного ордера","Pending request to remove a pending order"}, {"Отложенный запрос на модификацию параметров отложенного ордера","Pending request to modify pending order parameters"}, {"Фактический объем","Actual volume"}, {"Фактическая цена установки ордера","Actual order placement price"}, {"Фактическая цена установки StopLimit-ордера","Actual StopLimit-order placement price"}, {"Фактическая цена установки StopLoss-ордера","Actual StopLoss-order placement price"}, {"Фактическая цена установки TakeProfit-ордера","Actual TakeProfit-order placement price"}, {"Фактический тип заливки ордера","Actual order filling type"}, {"Фактический тип экспирации ордера","Actual of order expiration type"}, {"Фактическое время жизни ордера","Actual of order lifetime"}, }; //+---------------------------------------------------------------------+
Объект-пауза
В папке сервисных классов и функций библиотеки \MQL5\Include\DoEasy\Services\ создадим новый класс CPause в файле Pause.mqh, и сразу же пропишем в него всё необходимое:
//+------------------------------------------------------------------+ //| Pause.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 "DELib.mqh" //+------------------------------------------------------------------+ //| Класс "Пауза" | //+------------------------------------------------------------------+ class CPause { private: ulong m_start; // Начало отсчёта ulong m_time_begin; // Время начала отсчёта паузы ulong m_wait_msc; // Пауза в милисекундах public: //--- Устанавливает новое (1) время начала отсчёта, (2) паузу в милисекундах void SetTimeBegin(const ulong time) { this.m_time_begin=time; this.m_start=::GetTickCount(); } void SetWaitingMSC(const ulong pause) { this.m_wait_msc=pause; } //--- Возвращает (1) прошедшее время от начала отсчёта в милисекундах, (2) флаг завершения ожидания //--- (3) время начала отсчёта паузы, (4) установленный размер паузы в милисекундах ulong Passed(void) const { return ::GetTickCount()-this.m_start; } bool IsCompleted(void) const { return this.Passed()>this.m_wait_msc; } ulong TimeBegin(void) const { return this.m_time_begin; } ulong TimeWait(void) const { return this.m_wait_msc; } //--- Возвращает описание (1) прошедшего времени от начала отсчёта в милисекундах, //--- (2) времени начала отсчёта паузы, (3) установленного размера паузы в милисекундах string PassedDescription(void) const { return ::TimeToString(this.Passed()/1000,TIME_SECONDS); } string TimeBeginDescription(void) const { return ::TimeMSCtoString(this.m_time_begin); } string WaitingMSCDescription(void) const { return (string)this.m_wait_msc; } string WaitingSECDescription(void) const { return ::TimeToString(this.m_wait_msc/1000,TIME_SECONDS); } //--- Конструктор CPause(void) : m_start(::GetTickCount()){;} }; //+------------------------------------------------------------------+
К файлу класса сразу же подключим файл сервисных функций DELib.mqh, в котором находится функция вывода времени с милисекундами — она потребуется для вспомогательного метода класса, выводящего в журнал время начала паузы.
В приватной секции класса объявим три переменные-члена класса, которые необходимы для расчёта паузы:
- Переменная m_start служит для указания контрольного числа в момент создания объекта-паузы, или для записи в неё нового
контрольного числа начала нового отсчёта в ранее созданном объекте-паузе.
- Переменная m_time_begin хранит заданное время старта отсчёта паузы, и служит лишь для вывода информационных сообщений.
- Переменная m_wait_msc содержит число милисекунд паузы — по прошествии этого количества милисекунд считается что пауза
завершена.
Методы класса, думаю, в пояснениях особо не нуждаются.
- Метод SetTimeBegin(), устанавливающий новое время начала нового отсчёта, сначала записывает в переменную m_time_begin переданное в него время, а затем устанавливает новое контрольное число начала отсчёта в переменную m_start. Контрольным числом начала любого отсчёта в классе является результат, возвращаемый функцией GetTickCount().
- Метод Passed(), возвращающий количество прошедших милисекунд паузы, по сути возвращает разницу в милисекундах между текущим замером контрольного времени и числом, установленным в m_start при запуске отсчёта паузы.
Остальные методы класса достаточно наглядны, и в пояснениях, думаю, не нуждаются.
Данный класс мы пока нигде не используем, но будем использовать в дальнейших разработках библиотеки.
Для того,
чтобы класс паузы был доступен из любой точки библиотеки и программы, работающей на основе библиотеки,
подключим
файл класса CPause к файлу сервисных функций библиотеки DELib.mqh:
//+------------------------------------------------------------------+ //| DELib.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 strict // Нужно для mql4 //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "..\Defines.mqh" #include "Message.mqh" #include "TimerCounter.mqh" #include "Pause.mqh" //+------------------------------------------------------------------+
Немного оптимизации кода
Итак, мы решили, что пора перенести повторяющиеся от класса к классу методы в базовый объект всех объектов библиотеки, и унаследовать эти классы от базового объекта (если это ещё не сделано).
В первую очередь пропишем новые переменные и методы в класс базового объекта всех объектов библиотеки:
//+------------------------------------------------------------------+ //| Класс базового объекта для всех объектов библиотеки | //+------------------------------------------------------------------+ #define CONTROLS_TOTAL (10) class CBaseObj : public CObject { private: int m_long_prop_total; int m_double_prop_total; //--- Заполняет массив свойств объекта template<typename T> bool FillPropertySettings(const int index,T &array[][CONTROLS_TOTAL],T &array_prev[][CONTROLS_TOTAL],int &event_id); protected: CArrayObj m_list_events_base; // Список базовых событий объекта CArrayObj m_list_events; // Список событий объекта ENUM_LOG_LEVEL m_log_level; // Уровень логирования MqlTick m_tick; // Структура тика для получения котировочных данных double m_hash_sum; // Хэш-сумма данных объекта double m_hash_sum_prev; // Хэш-сумма данных объекта на прошлой проверке int m_digits_currency; // Число знаков после запятой валюты счёта int m_global_error; // Код глобальной ошибки long m_chart_id; // Идентификатор графика управляющей программы bool m_is_event; // Флаг события объекта int m_event_code; // Код события объекта int m_event_id; // Идентификатор события (равен значению свойства объекта) string m_name; // Наименование объекта string m_folder_name; // Имя папки хранения объектов-наследников CBaseObj bool m_first_start; // Флаг первого запуска int m_type; // Тип объекта (соответствует идентификаторам коллекций) //--- Данные для хранения, контроля и возврата отслеживаемых свойств: //--- [Индекс свойства][0] Контролируемая величина прироста значения свойства //--- [Индекс свойства][1] Контролируемая величина уменьшения значения свойства //--- [Индекс свойства][2] Контрольный уровень значения свойства //--- [Индекс свойства][3] Значение свойства //--- [Индекс свойства][4] Величина изменения значения свойства //--- [Индекс свойства][5] Флаг изменения значения свойства больше, чем на величину прироста //--- [Индекс свойства][6] Флаг изменения значения свойства больше, чем на величину уменьшения //--- [Индекс свойства][7] Флаг увеличения значения свойства больше контрольного уровня //--- [Индекс свойства][8] Флаг уменьшения значения свойства меньше контрольного уровня //--- [Индекс свойства][9] Флаг равенства значения свойства контрольному уровню long m_long_prop_event[][CONTROLS_TOTAL]; // Массив для хранения величин целочисленных свойств объекта и контролируемых значений изменения свойств double m_double_prop_event[][CONTROLS_TOTAL]; // Массив для хранения величин вещественных свойств объекта и контролируемых значений изменения свойств long m_long_prop_event_prev[][CONTROLS_TOTAL]; // Массив для хранения величин контролируемых целочисленных свойств объекта на прошлой проверке double m_double_prop_event_prev[][CONTROLS_TOTAL]; // Массив для хранения величин контролируемых вещественных свойств объекта на прошлой проверке //--- Возвращает (1) время в милисекундах, (2) милисекунды из значения времени из MqlTick long TickTime(void) const { return #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ; } ushort MSCfromTime(const long time_msc) const { return #ifdef __MQL5__ ushort(this.TickTime()%1000) #else 0 #endif ; } //--- возвращает факт наличия кода события в событии объекта bool IsPresentEventFlag(const int change_code) const { return (this.m_event_code & change_code)==change_code; } //--- Возвращает число знаков после запятой валюты счёта int DigitsCurrency(void) const { return this.m_digits_currency; } //--- Возвращает количество знаков после запятой в double-значении int GetDigits(const double value) const; //--- Устанавливает размер массива (1) целочисленных, (2) вещественных контролируемых свойств объекта bool SetControlDataArraySizeLong(const int size); bool SetControlDataArraySizeDouble(const int size); //--- Проверяет размер массива свойств объекта bool CheckControlDataArraySize(bool check_long=true); //--- Проверяет список изменений свойств объекта и создаёт событие void CheckEvents(void); //--- (1) Упаковывает ushort-число в переданное long-число long UshortToLong(const ushort ushort_value,const uchar to_byte,long &long_value); protected: //--- (1) преобразует ushort-значение в заданный байт long-числа long UshortToByte(const ushort value,const uchar to_byte) const; public: //--- Устанавливает величину контролируемого (1) приращения, (2) уменьшения, (3) контрольный уровень значения свойства объекта template<typename T> void SetControlledValueINC(const int property,const T value); template<typename T> void SetControlledValueDEC(const int property,const T value); template<typename T> void SetControlledValueLEVEL(const int property,const T value); //--- (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) вещественных свойств объекта long GetControlledLongValueINC(const int property) const { return this.m_long_prop_event[property][0]; } double GetControlledDoubleValueINC(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][0]; } //--- Возвращает установленную величину контролируемого уменьшения (1) целочисленных, (2) вещественных свойств объекта long GetControlledLongValueDEC(const int property) const { return this.m_long_prop_event[property][1]; } double GetControlledDoubleValueDEC(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][1]; } //--- Возвращает установленный контрольный уровень (1) целочисленных, (2) вещественных свойств объекта long GetControlledLongValueLEVEL(const int property) const { return this.m_long_prop_event[property][2]; } double GetControlledDoubleValueLEVEL(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][2]; } //--- Возвращает текущее значение (1) целочисленного, (2) вещественного свойства объекта long GetPropLongValue(const int property) const { return this.m_long_prop_event[property][3]; } double GetPropDoubleValue(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][3]; } //--- Возвращает величину изменения контролируемого (1) целочисленного, (2) вещественного свойства объекта long GetPropLongChangedValue(const int property) const { return this.m_long_prop_event[property][4]; } double GetPropDoubleChangedValue(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][4]; } //--- Возвращает Флаг изменения значения (1) целочисленного, (2) вещественного свойства больше, чем на величину прироста long GetPropLongFlagINC(const int property) const { return this.m_long_prop_event[property][5]; } double GetPropDoubleFlagINC(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][5]; } //--- Возвращает Флаг изменения значения (1) целочисленного, (2) вещественного свойства больше, чем на величину уменьшения long GetPropLongFlagDEC(const int property) const { return this.m_long_prop_event[property][6]; } double GetPropDoubleFlagDEC(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][6]; } //--- Возвращает Флаг увеличения значения (1) целочисленного, (2) вещественного свойства больше контрольного уровня long GetPropLongFlagMORE(const int property) const { return this.m_long_prop_event[property][7]; } double GetPropDoubleFlagMORE(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][7]; } //--- Возвращает Флаг уменьшения значения (1) целочисленного, (2) вещественного свойства меньше контрольного уровня long GetPropLongFlagLESS(const int property) const { return this.m_long_prop_event[property][8]; } double GetPropDoubleFlagLESS(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][8]; } //--- Возвращает Флаг равенства значений (1) целочисленного, (2) вещественного свойства и контрольного уровня long GetPropLongFlagEQUAL(const int property) const { return this.m_long_prop_event[property][9]; } double GetPropDoubleFlagEQUAL(const int property) const { return this.m_double_prop_event[property-this.m_long_prop_total][9]; } //--- Сбрасывает переменные (1) отслеживаемых, (2) контролируемых данных объекта (переназначить можно в наследниках) void ResetChangesParams(void); virtual void ResetControlsParams(void); //--- Добавляет (1) событие объекта в список, (2) причину события объекта в список bool EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam); bool EventBaseAdd(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value); //--- Устанавливает/Возвращает флаг произошедшего события в данных объекта void SetEvent(const bool flag) { this.m_is_event=flag; } bool IsEvent(void) const { return this.m_is_event; } //--- Возвращает (1) список событий, (2) код события объекта, (3) код глобальной ошибки CArrayObj *GetListEvents(void) { return &this.m_list_events; } int GetEventCode(void) const { return this.m_event_code; } int GetError(void) const { return this.m_global_error; } //--- Возвращает (1) объект-событие, (2) базовое событие по его номеру в списке CEventBaseObj *GetEvent(const int shift=WRONG_VALUE,const bool check_out=true); CBaseEvent *GetEventBase(const int index); //--- Возвращает количество (1) событий объекта int GetEventsTotal(void) const { return this.m_list_events.Total(); } //--- (1) Устанавливает, (2) возвращает идентификатор графика управляющей программы void SetChartID(const long id) { this.m_chart_id=id; } long GetChartID(void) const { return this.m_chart_id; } //--- (1) Устанавливает имя подпапки, (2) возвращает имя папки для хранения файлов объектов-наследников void SetSubFolderName(const string name) { this.m_folder_name=DIRECTORY+name; } string GetFolderName(void) const { return this.m_folder_name; } //--- Возвращает наименование объекта string GetName(void) const { return this.m_name; } //--- Обновляет данные объекта для поиска изменений в данных объекта (Вызов из наследников: CBaseObj::Refresh()) virtual void Refresh(void); //--- Возвращает тип объекта virtual int Type(void) const { return this.m_type; } //--- Возвращает описание события объекта string EventDescription(const int property, const ENUM_BASE_EVENT_REASON reason, const int source, const string value, const string property_descr, const int digits); //--- Расположение данных в int-значении магика //----------------------------------------------------------- // bit 32|31 24|23 16|15 8|7 0| //----------------------------------------------------------- // byte | 3 | 2 | 1 | 0 | //----------------------------------------------------------- // data | uchar | uchar | ushort | //----------------------------------------------------------- // descr |pend req id| id2 | id1 | magic | //----------------------------------------------------------- //--- Устанавливает идентификатор (1) первой группы, (2) второй группы, (3) отложенного запроса в значение магика void SetGroupID1(const uchar group,uint &magic) { magic &=0xFFF0FFFF; magic |= uint(this.ConvToXX(group,0)<<16); } void SetGroupID2(const uchar group,uint &magic) { magic &=0xFF0FFFFF; magic |= uint(this.ConvToXX(group,1)<<16); } void SetPendReqID(const uchar id,uint &magic) { magic &=0x00FFFFFF; magic |= (uint)id<<24; } //--- Конветирует значение 0 - 15 в нужные биты uchar-числа (0 - младшие, 1 - старшие) uchar ConvToXX(const uchar number,const uchar index) const { return((number>15 ? 15 : number)<<(4*(index>1 ? 1 : index))); } //--- Возвращает (1) заданный магический номер, идентификатор (2) первой группы, (3) второй группы, (4) отложенного запроса из значения магика ushort GetMagicID(const uint magic) const { return ushort(magic & 0xFFFF); } uchar GetGroupID1(const uint magic) const { return uchar(magic>>16) & 0x0F; } uchar GetGroupID2(const uint magic) const { return uchar((magic>>16) & 0xF0)>>4; } uchar GetPendReqID(const uint magic) const { return uchar(magic>>24) & 0xFF; } //--- Конструктор CBaseObj(); }; //+------------------------------------------------------------------+
Защищённая переменная (доступная наследникам, но не программам), хранящая значения уровня логирования для каждого из объектов-наследников базового объекта, в которых требуется вывод информации в журнал. В неё прописывается уровень логирования из перечисления ENUM_LOG_LEVEL, записанного в Defines.mqh:
//+------------------------------------------------------------------+ //| Уровень логирования | //+------------------------------------------------------------------+ enum ENUM_LOG_LEVEL { LOG_LEVEL_NO_MSG, // Логирование торговли отключено LOG_LEVEL_ERROR_MSG, // Только торговые ошибки LOG_LEVEL_ALL_MSG // Полное логирование }; //+------------------------------------------------------------------+
Установка и возврат значений данной переменной осуществляется публичными методами SetLogLevel()
и GetLogLevel() соответственно.
И публичными же методами сделаны методы установки и возврата значений
различных идентификаторов, хранящихся в магике ордера/позиции.
Все эти методы мы уже рассматривали
ранее, и здесь повторяться нет смысла.
Базовый торговый объект символа в файле \MQL5\Include\DoEasy\Objects\Trade\TradeObj.mqh.
Внесём необходимые доработки:
подключим к нему файл базового объекта
библиотеки и сделаем его родительским классом класс объекта CBaseObj:
//+------------------------------------------------------------------+ //| 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" #include "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| Класс торгового объекта | //+------------------------------------------------------------------+ class CTradeObj : public CBaseObj {
После внесения этих правок класс торгового объекта символа стал наследником базового объекта всех объектов библиотеки, и теперь из этого класса нужно удалить все переменные и методы, которые уже есть в родительском классе CBaseObj.
Из приватной секции класса удалим лишние переменные, которые есть в родительском классе:
SActions m_datas; MqlTick m_tick; // Структура тика для получения цен MqlTradeRequest m_request; // Структура торгового запроса MqlTradeResult m_result; // Структура результата исполнения торгового запроса ENUM_SYMBOL_CHART_MODE m_chart_mode; // Тип цены для построения баров ENUM_ACCOUNT_MARGIN_MODE m_margin_mode; // Режим расчёта маржи ENUM_ORDER_TYPE_FILLING m_type_filling; // Политика исполнения ENUM_ORDER_TYPE_TIME m_type_time; // Тип ордера по истечению 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 ордера в пунктах bool m_use_sound; // Флаг использования звуков торговых событий объекта uint m_multiplier; // Множитель спреда для корректировки уровней относительно StopLevel
Из публичной секции класса удалим методы установки и возврата уровня логирования:
//--- (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; }
Из конструктора класса, из его списка инициализации удалим инициализацию уровня логирования:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ 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_time(ORDER_TIME_GTC), m_comment(::MQLInfoString(MQL_PROGRAM_NAME)+" by DoEasy"), m_log_level(LOG_LEVEL_ERROR_MSG)
И пропишем инициализацию уровня логирования в теле метода:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ 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_time(ORDER_TIME_GTC), m_comment(::MQLInfoString(MQL_PROGRAM_NAME)+" by DoEasy") { //--- Режим расчёта маржи this.m_margin_mode= ( #ifdef __MQL5__ (ENUM_ACCOUNT_MARGIN_MODE)::AccountInfoInteger(ACCOUNT_MARGIN_MODE) #else /* MQL4 */ ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif ); //--- Множитель спреда this.m_multiplier=1; //--- Установка звуков и флагов использования звуков по умолчанию this.m_use_sound=false; this.m_log_level=LOG_LEVEL_ERROR_MSG; this.InitSounds(); } //+------------------------------------------------------------------+
Объект базового абстрактного ордера в файле \MQL5\Include\DoEasy\Objects\Orders\Order.mqh.
Внесём необходимые доработки:
подключим к нему файл базового объекта
библиотеки и сделаем его родительским классом класс объекта CBaseObj:
//+------------------------------------------------------------------+ //| Order.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" #property strict // Нужно для mql4 //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include <Object.mqh> #include "..\..\Services\DELib.mqh" #include "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| Класс абстрактного ордера | //+------------------------------------------------------------------+ class COrder : public CBaseObj {
Из приватной секции класса удалим методы получения идентификаторов,
записанных в магике ордера:
(табличку с напоминанием о расположении
значений идентификаторов в значении магика оставим)
//+------------------------------------------------------------------+ //| Класс абстрактного ордера | //+------------------------------------------------------------------+ class COrder : public CObject { private: ulong m_ticket; // Тикет выбранного ордера/сделки (MQL5) long m_long_prop[ORDER_PROP_INTEGER_TOTAL]; // Целочисленные свойства double m_double_prop[ORDER_PROP_DOUBLE_TOTAL]; // Вещественные свойства string m_string_prop[ORDER_PROP_STRING_TOTAL]; // Строковые свойства //--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство ордера int IndexProp(ENUM_ORDER_PROP_DOUBLE property) const { return(int)property-ORDER_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_ORDER_PROP_STRING property) const { return(int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL; } //--- Расположение данных в int-значении магика //----------------------------------------------------------- // bit 32|31 24|23 16|15 8|7 0| //----------------------------------------------------------- // byte | 3 | 2 | 1 | 0 | //----------------------------------------------------------- // data | uchar | uchar | ushort | //----------------------------------------------------------- // descr |pend req id| id2 | id1 | magic | //----------------------------------------------------------- //--- Возвращает (1) заданный магический номер, идентификатор (2) первой группы, (3) второй группы, (4) отложенного запроса из значения магика ushort GetMagicID(void) const { return ushort(this.Magic() & 0xFFFF); } uchar GetGroupID1(void) const { return uchar(this.Magic()>>16) & 0x0F; } uchar GetGroupID2(void) const { return uchar((this.Magic()>>16) & 0xF0)>>4; } uchar GetPendReqID(void) const { return uchar(this.Magic()>>24) & 0xFF; } public:
В закрытом конструкторе класса допишем указание магика в методы получения идентификаторов:
//+------------------------------------------------------------------+ //| Закрытый параметрический конструктор | //+------------------------------------------------------------------+ COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket) { //--- Сохранение целочисленных свойств this.m_ticket=ticket; this.m_long_prop[ORDER_PROP_STATUS] = order_status; this.m_long_prop[ORDER_PROP_MAGIC] = this.OrderMagicNumber(); this.m_long_prop[ORDER_PROP_TICKET] = this.OrderTicket(); this.m_long_prop[ORDER_PROP_TIME_EXP] = this.OrderExpiration(); this.m_long_prop[ORDER_PROP_TYPE_FILLING] = this.OrderTypeFilling(); this.m_long_prop[ORDER_PROP_TYPE_TIME] = this.OrderTypeTime(); this.m_long_prop[ORDER_PROP_TYPE] = this.OrderType(); this.m_long_prop[ORDER_PROP_STATE] = this.OrderState(); this.m_long_prop[ORDER_PROP_DIRECTION] = this.OrderTypeByDirection(); this.m_long_prop[ORDER_PROP_POSITION_ID] = this.OrderPositionID(); this.m_long_prop[ORDER_PROP_REASON] = this.OrderReason(); this.m_long_prop[ORDER_PROP_DEAL_ORDER_TICKET] = this.DealOrderTicket(); this.m_long_prop[ORDER_PROP_DEAL_ENTRY] = this.DealEntry(); this.m_long_prop[ORDER_PROP_POSITION_BY_ID] = this.OrderPositionByID(); this.m_long_prop[ORDER_PROP_TIME_OPEN] = this.OrderOpenTimeMSC(); this.m_long_prop[ORDER_PROP_TIME_CLOSE] = this.OrderCloseTimeMSC(); this.m_long_prop[ORDER_PROP_TIME_UPDATE] = this.PositionTimeUpdateMSC(); //--- Сохранение вещественных свойств this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)] = this.OrderOpenPrice(); this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)] = this.OrderClosePrice(); this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)] = this.OrderProfit(); this.m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)] = this.OrderCommission(); this.m_double_prop[this.IndexProp(ORDER_PROP_SWAP)] = this.OrderSwap(); this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)] = this.OrderVolume(); this.m_double_prop[this.IndexProp(ORDER_PROP_SL)] = this.OrderStopLoss(); this.m_double_prop[this.IndexProp(ORDER_PROP_TP)] = this.OrderTakeProfit(); this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)] = this.OrderVolumeCurrent(); this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)] = this.OrderPriceStopLimit(); //--- Сохранение строковых свойств this.m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)] = this.OrderSymbol(); this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)] = this.OrderComment(); this.m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)] = this.OrderExternalID(); //--- Сохранение дополнительных целочисленных свойств this.m_long_prop[ORDER_PROP_PROFIT_PT] = this.ProfitInPoints(); this.m_long_prop[ORDER_PROP_TICKET_FROM] = this.OrderTicketFrom(); this.m_long_prop[ORDER_PROP_TICKET_TO] = this.OrderTicketTo(); this.m_long_prop[ORDER_PROP_CLOSE_BY_SL] = this.OrderCloseByStopLoss(); this.m_long_prop[ORDER_PROP_CLOSE_BY_TP] = this.OrderCloseByTakeProfit(); this.m_long_prop[ORDER_PROP_MAGIC_ID] = this.GetMagicID((uint)this.GetProperty(ORDER_PROP_MAGIC)); this.m_long_prop[ORDER_PROP_GROUP_ID1] = this.GetGroupID1((uint)this.GetProperty(ORDER_PROP_MAGIC)); this.m_long_prop[ORDER_PROP_GROUP_ID2] = this.GetGroupID2((uint)this.GetProperty(ORDER_PROP_MAGIC)); this.m_long_prop[ORDER_PROP_PEND_REQ_ID] = this.GetPendReqID((uint)this.GetProperty(ORDER_PROP_MAGIC)); //--- Сохранение дополнительных вещественных свойств this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)] = this.ProfitFull(); //--- Сохранение дополнительных строковых свойств this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT_EXT)] = ""; } //+------------------------------------------------------------------+
Основной торговый класс в файле \MQL5\Include\DoEasy\Trading.mqh.
Внесём необходимые доработки. Так как метод теперь стал родительским для будущего класса управления отложенными запросами, то
перенесём
указатели на объекты коллекций и список указателей на отложенные запросы из приватной секции в защищённую:
//+------------------------------------------------------------------+ //| Торговый класс | //+------------------------------------------------------------------+ class CTrading : public CBaseObj { protected: CAccount *m_account; // Указатель на объект-текущий аккаунт CSymbolsCollection *m_symbols; // Указатель на список коллекции символов CMarketCollection *m_market; // Указатель на список коллекции рыночных ордеров и позиций CHistoryCollection *m_history; // Указатель на список коллекции исторических ордеров и сделок CEventsCollection *m_events; // Указатель на список коллекции событий CArrayObj m_list_request; // Список отложенных запросов private:
Из приватной секции удалим переменную для хранения уровня логирования:
private: CArrayInt m_list_errors; // Список ошибок bool m_is_trade_disable; // Флаг запрета торговли bool m_use_sound; // Флаг использования звуков торговых событий объекта uchar m_total_try; // Количество торговых попыток ENUM_LOG_LEVEL m_log_level; // Уровень логирования MqlTradeRequest m_request; // Цены торгового запроса ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags; // Флаги причин ошибок в торговом методе ENUM_ERROR_HANDLING_BEHAVIOR m_err_handling_behavior; // Поведение при обработке ошибок
В публичную секцию класса впишем метод, возвращающий целиком объект торгового класса, и сделаем таймер класса виртуальным:
public: //--- Возвращает себя CTrading *GetObject(void) { return &this; } //--- Конструктор CTrading(); //--- Таймер virtual void OnTimer(void); //--- Получение указателей на списки (вызывать метод обязательно в OnInit() программы, так как список коллекции символов создаётся там)
В методе создания отложенного запроса добавим передачу в метод объекта-ордера, и удалим методы для работы с идентификаторами, записанными в значение магика ордера:
//--- Создаёт отложенный запрос bool CreatePendingRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const uchar attempts, const ulong wait, const MqlTradeRequest &request, const int retcode, CSymbol *symbol_obj, COrder *order); //--- Расположение данных в int-значении магика //----------------------------------------------------------- // bit 32|31 24|23 16|15 8|7 0| //----------------------------------------------------------- // byte | 3 | 2 | 1 | 0 | //----------------------------------------------------------- // data | uchar | uchar | ushort | //----------------------------------------------------------- // descr |pend req id| id2 | id1 | magic | //----------------------------------------------------------- //--- Устанавливает идентификатор (1) первой группы, (2) второй группы, (3) отложенного запроса в значение магика void SetGroupID1(const uchar group,uint &magic) { magic &=0xFFF0FFFF; magic |= uint(ConvToXX(group,0)<<16); } void SetGroupID2(const uchar group,uint &magic) { magic &=0xFF0FFFFF; magic |= uint(ConvToXX(group,1)<<16); } void SetPendReqID(const uchar id,uint &magic) { magic &=0x00FFFFFF; magic |= (uint)id<<24; } //--- Конветирует значение 0 - 15 в нужные биты uchar-числа (0 - младшие, 1 - старшие) uchar ConvToXX(const uchar number,const uchar index) const { return((number>15 ? 15 : number)<<(4*(index>1 ? 1 : index)));} //--- Возвращает (1) заданный магический номер, идентификатор (2) первой группы, (3) второй группы, (4) отложенного запроса из значения магика ushort GetMagicID(const uint magic) const { return ushort(magic & 0xFFFF); } uchar GetGroupID1(const uint magic) const { return uchar(magic>>16) & 0x0F; } uchar GetGroupID2(const uint magic) const { return uchar((magic>>16) & 0xF0)>>4; } uchar GetPendReqID(const uint magic) const { return uchar(magic>>24) & 0xFF; } }; //+------------------------------------------------------------------+
Из реализации таймера класса удалим полностью всё, оставив его пустым:
//+------------------------------------------------------------------+ //| Таймер | //+------------------------------------------------------------------+ void CTrading::OnTimer(void) { } //+------------------------------------------------------------------+
Здесь будем прописывать код в том случае, если в будущем нам нужно будет что-либо обрабатывать только в таймере данного класса, который является родительским для других торговых классов (в данной реализации — для будущего класса управления отложенными запросами).
В методе проверки ограничений для торговли CheckTradeConstraints(), в его блоке проверки корректности объёма при торговых операциях, требующих значение лота (условие дополнено) будем проверять корректность объёма только если его значение, переданное входным параметром, больше нуля:
//--- Если закрытие, или не удаление/модификация if(action_type<ACTION_TYPE_CLOSE_BY || action_type==ACTION_TYPE_CLOSE) { //--- Если разрешены только операции закрытия позиций - записываем код ошибки в список и возвращаем false - нет смысла проверять далее if(symbol_obj.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_SYM_TRADE_MODE_CLOSEONLY); return false; } //--- Проверка минимального объёма if(volume>0) { if(volume<symbol_obj.LotsMin()) { //--- Объём в запросе меньше минимально-допустимого. //--- добавляем код ошибки в список this.m_error_reason_flags |=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_REQ_VOL_LESS_MIN_VOLUME); //--- Если поведение советника при торговой ошибке "прервать торговую операцию", то //--- возвращаем false - нет смысла проверять далее if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK) return false; //--- Если поведение советника при торговой ошибке указано как //--- "скорректировать параметры" или "создать отложенный запрос", то //--- записываем в результат false else res &=false; } //--- Проверка максимального объёма else if(volume>symbol_obj.LotsMax()) { //--- Объём в запросе больше максимально-допустимого. //--- добавляем код ошибки в список this.m_error_reason_flags |=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_REQ_VOL_MORE_MAX_VOLUME); //--- Если поведение советника при торговой ошибке "прервать торговую операцию", то //--- возвращаем false - нет смысла проверять далее if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK) return false; //--- Если поведение советника при торговой ошибке указано как //--- "скорректировать параметры" или "создать отложенный запрос", то //--- записываем в результат false else res &=false; } //--- Проверка минимальной градации объема double step=symbol_obj.LotsStep(); if(fabs((int)round(volume/step)*step-volume)>0.0000001) { //--- Объём в запросе не кратен минимальной градации шага изменения лота //--- добавляем код ошибки в список this.m_error_reason_flags |=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST; this.AddErrorCodeToList(MSG_LIB_TEXT_INVALID_VOLUME_STEP); //--- Если поведение советника при торговой ошибке "прервать торговую операцию", то //--- возвращаем false - нет смысла проверять далее if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK) return false; //--- Если поведение советника при торговой ошибке указано как //--- "скорректировать параметры" или "создать отложенный запрос", то //--- записываем в результат false else res &=false; } } } //--- Если открытие позиции
Далее:
Во всех торговых методах там, где требуется создание объекта отложенного запроса, в метод передаётся объект-ордер. А там, где такого объекта нет (в методах открытия позиции и установки отложенных ордеров) — передаём в метод NULL.
В методе открытия позиции передаём NULL последним параметром:
//--- Если результат проверки "ожидание" - устанавливаем код последней ошибки в структуру возврата, //--- создаём отложенный запрос и возвращаем false (OpenPosition) if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH) { //--- Если в магике торгового запроса нет идентификатора отложенного запроса if(this.GetPendReqID((uint)magic)==0) { //--- Проиграем звук ошибки if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type); //--- устанавливаем код последней ошибки в структуру возврата int code=this.m_list_errors.At(this.m_list_errors.Total()-1); if(code!=NULL) { if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED) code=10027; trade_obj.SetResultRetcode(code); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); } //--- Время ожидания в милисекундах: //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method, ulong wait=method; //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось, //--- или произошла ошибка обновления текущих данных символа - возвращаем false int id=this.GetFreeID(); if(id<1 || !symbol_obj.RefreshRates()) return false; //--- Записываем в магик идентификатор объекта-отложенного запроса и заполняем остальные незаполненные поля структуры торгового запроса uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic); this.SetPendReqID((uchar)id,mn); this.m_request.magic=mn; this.m_request.action=TRADE_ACTION_DEAL; this.m_request.symbol=symbol_obj.Name(); this.m_request.type=order_type; //--- Устанавливаем количество торговых попыток и создаём отложенный запрос uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try); this.CreatePendingRequest(PEND_REQ_STATUS_OPEN,(uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj,NULL); } return false; }
И в методе установки отложенного ордера точно так же передаём NULL в качестве объекта-ордера в метод создания отложенного запроса.
А вот в методах, работающих с уже существующими ордерами и позициями, в
метод создания отложенного запроса передаём объект-ордер, например,
в методе модификации стоп-приказов
существующей позиции передаём объект-ордер:
//--- Если результат проверки "ожидание" - устанавливаем код последней ошибки в структуру возврата, //--- создаём отложенный запрос и возвращаем false (ModifyPosition) if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH) { //--- Если объекта-отложенного запроса с тикетом позиции нет в списке if(this.GetIndexPendingRequestByPosition(ticket)==WRONG_VALUE) { //--- Проиграем звук ошибки if(this.IsUseSounds()) trade_obj.PlaySoundError(action,order_type,(sl<0 ? false : true),(tp<0 ? false : true)); //--- устанавливаем код последней ошибки в структуру возврата int code=this.m_list_errors.At(this.m_list_errors.Total()-1); if(code!=NULL) { if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED) code=10027; trade_obj.SetResultRetcode(code); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); } //--- Время ожидания в милисекундах: //--- для метода обработки "Подождать и повторить" - значение ожидания соответствует значению method, ulong wait=method; //--- Ищем наименьший из возможных идентификаторов, и если найти не удалось, //--- или произошла ошибка обновления текущих данных символа - возвращаем false int id=this.GetFreeID(); if(id<1 || !symbol_obj.RefreshRates()) return false; //--- Записываем тип проводимой операции, символ и тикет модифицируемой позиции в структуру запроса this.m_request.action=TRADE_ACTION_SLTP; this.m_request.symbol=symbol_obj.Name(); this.m_request.position=ticket; this.m_request.type=order_type; this.m_request.volume=order.Volume(); //--- Устанавливаем количество торговых попыток и создаём отложенный запрос uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try); this.CreatePendingRequest(PEND_REQ_STATUS_SLTP,(uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj,order); } return false; }
И в остальных методах, в которых известен ордер или позиция, передаём объект-ордер в метод создания отложенного запроса.
При тестировании обнаружилось, что в метод закрытия позиции ClosePosition() передавался иногда некорректный объём
закрываемой позиции при её частичном закрытии — просто отправлялся нулевой объём, что приводило к пропуску его проверки. И далее из-за
этого он нигде не корректировался. Для исправления ситуации, выше мы уже вписали в метод проверки разрешения торговли проверку объёма при
закрытии позиции, а теперь ещё добавим запись правильного объёма при создании отложенного запроса и
отправку его в метод проверки.
Для этого допишем в метод одну строку:
//--- Записываем отклонение и комментарий в структуру запроса this.m_request.deviation=(deviation==ULONG_MAX ? trade_obj.GetDeviation() : deviation); this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment); this.m_request.volume=(volume==WRONG_VALUE || volume>order.Volume() ? order.Volume() : symbol_obj.NormalizedLot(volume)); //--- Если есть ограничения по разрешённости торговли, объёму //--- есть ограничения по уровню FreezeLevel - воспроизводим звук ошибки и уходим ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(volume,0,action,order_type,symbol_obj,trade_obj,DFUN,0,0,0,ticket); if(method!=ERROR_CODE_PROCESSING_METHOD_OK) {
Здесь мы записываем в структуру торгового запроса объём таким образом: если в метод передано значение -1 или значение объёма, переданного в метод, больше объёма закрываемой позиции, то записываем в структуру полный объём позиции (полное закрытие). Иначе — в структуру записываем нормализованный объём, переданный в метод (он может прийти некорректным, например, при делении объёма 0.05 на 2 при частичном закрытии позиции, в метод придёт объём 0.025, что вызовет ошибку)
В методе модификации отложенного ордера ModifyOrder()
впишем дополнительно в структуру торгового запроса идентификатор
магика и объём ордера:
//--- Записываем магик, объём, тип заливки, дату и тип экспирации в структуру запроса this.m_request.magic=order.GetMagicID((uint)order.Magic()); this.m_request.volume=order.Volume(); this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : order.TypeFilling()); this.m_request.expiration=(expiration>WRONG_VALUE ? expiration : order.TimeExpiration()); this.m_request.type_time=(type_time>WRONG_VALUE ? type_time : order.TypeTime()); //--- Если есть ограничения по разрешённости торговли, //--- есть ограничения по уровням StopLevel или FreezeLevel - воспроизводим звук ошибки и уходим ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(0,this.m_request.price,action,order_type,symbol_obj,trade_obj,DFUN,this.m_request.stoplimit,this.m_request.sl,this.m_request.tp,ticket); if(method!=ERROR_CODE_PROCESSING_METHOD_OK) {
Для реальной торговли это ровным счётом ничего не даёт — ордер модифицируется по тикету, но для отложенных запросов это нам даст корректный вывод в журнал записей о модифицируемом ордере.
Метод создания отложенного запроса претерпел небольшие изменения:
//+------------------------------------------------------------------+ //| Создаёт отложенный запрос | //+------------------------------------------------------------------+ bool CTrading::CreatePendingRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const uchar attempts, const ulong wait, const MqlTradeRequest &request, const int retcode, CSymbol *symbol_obj, COrder *order) { //--- Создаём новый объект-отложенный запрос в зависимости от статуса запроса CPendRequest *req_obj=NULL; switch(status) { case PEND_REQ_STATUS_OPEN : req_obj=new CPendReqOpen(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_CLOSE : req_obj=new CPendReqClose(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_SLTP : req_obj=new CPendReqSLTP(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_PLACE : req_obj=new CPendReqPlace(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_REMOVE : req_obj=new CPendReqRemove(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); break; case PEND_REQ_STATUS_MODIFY : req_obj=new CPendReqModify(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode); break; default: req_obj=NULL; break; } if(req_obj==NULL) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ)); return false; } //--- Если не удалось добавить запрос в список - выводим об этом сообщение, //--- удаляем созданный объект и возвращаем false if(!this.m_list_request.Add(req_obj)) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ)); delete req_obj; return false; } //--- Заполняем свойства успешно созданного объекта переданными в метод значениями req_obj.SetTimeActivate(symbol_obj.Time()+wait); req_obj.SetWaitingMSC(wait); req_obj.SetCurrentAttempt(0); req_obj.SetTotalAttempts(attempts); if(order!=NULL) { req_obj.SetActualVolume(order.Volume()); req_obj.SetActualPrice(order.PriceOpen()); req_obj.SetActualStopLimit(order.PriceStopLimit()); req_obj.SetActualSL(order.StopLoss()); req_obj.SetActualTP(order.TakeProfit()); req_obj.SetActualTypeFilling(order.TypeFilling()); req_obj.SetActualTypeTime(order.TypeTime()); req_obj.SetActualExpiration(order.TimeExpiration()); } else { req_obj.SetActualVolume(request.volume); req_obj.SetActualPrice(request.price); req_obj.SetActualStopLimit(request.stoplimit); req_obj.SetActualSL(request.sl); req_obj.SetActualTP(request.tp); req_obj.SetActualTypeFilling(request.type_filling); req_obj.SetActualTypeTime(request.type_time); req_obj.SetActualExpiration(request.expiration); } //--- Выводим краткое описание созданного отложенного запроса if(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CREATED)," #",req_obj.ID(),":"); req_obj.PrintShort(); } //--- успешно return true; } //+------------------------------------------------------------------+
Забегая наперёд (раз уж мы сейчас правим основной торговый класс) скажу, что когда будем дорабатывать объект отложенного запроса, мы введём
методы установки его фактических значений, что даст нам возможность сравнивать состояние объекта — сменилось ли оно, а точнее — отработал
ли уже этот запрос — сравниваем состояние свойств ордера с теми значениями, которые прописываются в свойства объекта отложенного
запроса. При полном их равенстве считаем что отложенный запрос выполнил свою функцию.
Вот в методе создания отложенного торгового запроса мы и проверяем что
передано в качестве объекта-ордера. Если передан не NULL (объект-ордер существует), то в
объекте-отложенном запросе устанавливаются первоначальные параметры из объекта-ордера. Иначе
— параметры устанавливаются из структуры торгового запроса.
Это все изменения основного торгового класса.
Теперь займёмся доработкой класса объекта отложенного запроса.
У нас появились новые свойства объекта-отложенного запроса. Запишем их в файл Defines.mqh.
В целочисленные свойства отложенного запроса добавим новые свойства,
и изменим количество целочисленных свойств с 19 на 22:
//+------------------------------------------------------------------+ //| Целочисленные свойства отложенного торгового запроса | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_PROP_INTEGER { PEND_REQ_PROP_STATUS = 0, // Статус торгового запроса (из перечисления ENUM_PEND_REQ_STATUS) PEND_REQ_PROP_TYPE, // Тип торгового запроса (из перечисления ENUM_PEND_REQ_TYPE) PEND_REQ_PROP_ID, // Идентификатор торгового запроса PEND_REQ_PROP_RETCODE, // Результат, на основании которого создан запрос PEND_REQ_PROP_TIME_CREATE, // Время создания запроса PEND_REQ_PROP_TIME_ACTIVATE, // Время активации очередной попытки PEND_REQ_PROP_WAITING, // Продолжительность ожидания между запросами PEND_REQ_PROP_CURRENT_ATTEMPT, // Номер текущей попытки PEND_REQ_PROP_TOTAL, // Количество попыток PEND_REQ_PROP_ACTUAL_TYPE_FILLING, // Фактический тип заливки ордера PEND_REQ_PROP_ACTUAL_TYPE_TIME, // Фактический тип экспирации ордера PEND_REQ_PROP_ACTUAL_EXPIRATION, // Фактическое время жизни ордера //--- MqlTradeRequest PEND_REQ_PROP_MQL_REQ_ACTION, // Тип выполняемого действия в структуре запроса PEND_REQ_PROP_MQL_REQ_TYPE, // Тип ордера в структуре запроса PEND_REQ_PROP_MQL_REQ_MAGIC, // Штамп эксперта (идентификатор magic number) в структуре запроса PEND_REQ_PROP_MQL_REQ_ORDER, // Тикет ордера в структуре запроса PEND_REQ_PROP_MQL_REQ_POSITION, // Тикет позиции в структуре запроса PEND_REQ_PROP_MQL_REQ_POSITION_BY, // Тикет встречной позиции в структуре запроса PEND_REQ_PROP_MQL_REQ_DEVIATION, // Максимально приемлемое отклонение от запрашиваемой цены в структуре запроса PEND_REQ_PROP_MQL_REQ_EXPIRATION, // Срок истечения ордера (для ордеров типа ORDER_TIME_SPECIFIED) в структуре запроса PEND_REQ_PROP_MQL_REQ_TYPE_FILLING, // Тип ордера по исполнению в структуре запроса PEND_REQ_PROP_MQL_REQ_TYPE_TIME, // Тип ордера по времени действия в структуре запроса }; #define PEND_REQ_PROP_INTEGER_TOTAL (22) // Общее количество целочисленных свойств события #define PEND_REQ_PROP_INTEGER_SKIP (0) // Количество неиспользуемых в сортировке свойств запроса //+------------------------------------------------------------------+
В список вещественных свойств добавим новые свойства и изменим их количество с 6 до 11:
//+------------------------------------------------------------------+ //| Вещественные свойства отложенного торгового запроса | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_PROP_DOUBLE { PEND_REQ_PROP_PRICE_CREATE = PEND_REQ_PROP_INTEGER_TOTAL,// Цена при создании запроса PEND_REQ_PROP_ACTUAL_VOLUME, // Фактический объем PEND_REQ_PROP_ACTUAL_PRICE, // Фактическая цена установки ордера PEND_REQ_PROP_ACTUAL_STOPLIMIT, // Фактическая цена установки stoplimit-ордера PEND_REQ_PROP_ACTUAL_SL, // Фактическая цена установки stoploss-ордера PEND_REQ_PROP_ACTUAL_TP, // Фактическая цена установки takeprofit-ордера //--- MqlTradeRequest PEND_REQ_PROP_MQL_REQ_VOLUME, // Запрашиваемый объем сделки в лотах в структуре запроса PEND_REQ_PROP_MQL_REQ_PRICE, // Цена в структуре запроса PEND_REQ_PROP_MQL_REQ_STOPLIMIT, // Уровень StopLimit ордера в структуре запроса PEND_REQ_PROP_MQL_REQ_SL, // Уровень Stop Loss ордера в структуре запроса PEND_REQ_PROP_MQL_REQ_TP, // Уровень Take Profit ордера в структуре запроса }; #define PEND_REQ_PROP_DOUBLE_TOTAL (11) // Общее количество вещественных свойств события #define PEND_REQ_PROP_DOUBLE_SKIP (0) // Количество неиспользуемых в сортировке свойств события //+------------------------------------------------------------------+
В список возможных критериев сортировки добавим сортировку по новым свойствам:
//+------------------------------------------------------------------+ //| Возможные критерии сортировки отложенных запросов | //+------------------------------------------------------------------+ #define FIRST_PREQ_DBL_PROP (PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_INTEGER_SKIP) #define FIRST_PREQ_STR_PROP (PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_INTEGER_SKIP+PEND_REQ_PROP_DOUBLE_TOTAL-PEND_REQ_PROP_DOUBLE_SKIP) enum ENUM_SORT_PEND_REQ_MODE { //--- Сортировка по целочисленным свойствам SORT_BY_PEND_REQ_STATUS = 0, // Сортировать по статусу торгового запроса (из перечисления ENUM_PEND_REQ_STATUS) SORT_BY_PEND_REQ_TYPE, // Сортировать по типу торгового запроса (из перечисления ENUM_PEND_REQ_TYPE) SORT_BY_PEND_REQ_ID, // Сортировать по идентификатору торгового запроса SORT_BY_PEND_REQ_RETCODE, // Сортировать по результату, на основании которого создан запрос SORT_BY_PEND_REQ_TIME_CREATE, // Сортировать по времени создания запроса SORT_BY_PEND_REQ_TIME_ACTIVATE, // Сортировать по времени активации очередной попытки SORT_BY_PEND_REQ_WAITING, // Сортировать по продолжительности ожидания между запросами SORT_BY_PEND_REQ_CURENT, // Сортировать по номеру текущей попытки SORT_BY_PEND_REQ_TOTAL, // Сортировать по количеству попыток SORT_BY_PEND_REQ_ACTUAL_TYPE_FILLING, // Сортировать по фактическому типу заливки ордера SORT_BY_PEND_REQ_ACTUAL_TYPE_TIME, // Сортировать по фактическому типу экспирации ордера SORT_BY_PEND_REQ_ACTUAL_EXPIRATION, // Сортировать по фактическому времени жизни ордера //--- MqlTradeRequest SORT_BY_PEND_REQ_MQL_REQ_ACTION, // Сортировать по типу выполняемого действия в структуре запроса SORT_BY_PEND_REQ_MQL_REQ_TYPE, // Сортировать по типу ордера в структуре запроса SORT_BY_PEND_REQ_MQL_REQ_MAGIC, // Сортировать по штампу эксперта (идентификатору magic number) в структуре запроса SORT_BY_PEND_REQ_MQL_REQ_ORDER, // Сортировать по тикету ордера в структуре запроса SORT_BY_PEND_REQ_MQL_REQ_POSITION, // Сортировать по тикету позиции в структуре запроса SORT_BY_PEND_REQ_MQL_REQ_POSITION_BY, // Сортировать по тикету встречной позиции в структуре запроса SORT_BY_PEND_REQ_MQL_REQ_DEVIATION, // Сортировать по максимально приемлемому отклонению от запрашиваемой цены в структуре запроса SORT_BY_PEND_REQ_MQL_REQ_EXPIRATION, // Сортировать по сроку истечения ордера (для ордеров типа ORDER_TIME_SPECIFIED) в структуре запроса SORT_BY_PEND_REQ_MQL_REQ_TYPE_FILLING, // Сортировать по типу ордера по исполнению в структуре запроса SORT_BY_PEND_REQ_MQL_REQ_TYPE_TIME, // Сортировать по типу ордера по времени действия в структуре запроса //--- Сортировка по вещественным свойствам SORT_BY_PEND_REQ_PRICE_CREATE = FIRST_PREQ_DBL_PROP, // Сортировать по цене при создании запроса SORT_BY_PEND_REQ_ACTUAL_VOLUME, // Сортировать по начальному объему SORT_BY_PEND_REQ_ACTUAL_PRICE, // Сортировать по фактической цене установки ордера SORT_BY_PEND_REQ_ACTUAL_STOPLIMIT, // Сортировать по фактической цене установки stoplimit-ордера SORT_BY_PEND_REQ_ACTUAL_SL, // Сортировать по фактической цене установки stoploss-ордера SORT_BY_PEND_REQ_ACTUAL_TP, // Сортировать по фактической цене установки takeprofit-ордера //--- MqlTradeRequest SORT_BY_PEND_REQ_MQL_REQ_VOLUME, // Сортировать по запрашиваемому объему сделки в лотах в структуре запроса SORT_BY_PEND_REQ_MQL_REQ_PRICE, // Сортировать по цене в структуре запроса SORT_BY_PEND_REQ_MQL_REQ_STOPLIMIT, // Сортировать по уровню StopLimit ордера в структуре запроса SORT_BY_PEND_REQ_MQL_REQ_SL, // Сортировать по уровню StopLoss ордера в структуре запроса SORT_BY_PEND_REQ_MQL_REQ_TP, // Сортировать по уровню TakeProfit ордера в структуре запроса //--- Сортировка по строковым свойствам //--- MqlTradeRequest SORT_BY_PEND_REQ_MQL_SYMBOL = FIRST_PREQ_STR_PROP, // Сортировать по имени торгового инструмента в структуре запроса SORT_BY_PEND_REQ_MQL_COMMENT // Сортировать по комментарию к ордеру в структуре запроса }; //+------------------------------------------------------------------+
Теперь к объектам.
Откроем файл класса базового объекта абстрактного отложенного запроса \MQL5\Include\DoEasy\Objects\PendRequest\PendRequest.mqh
и внесём в него необходимые изменения.
В приватной секции класса объявим объект класса-паузы, а в защищённой — два перегруженных метода сравнения значения свойств объекта с переданным в метод значением и набор методов, возвращающих флаги завершения работы отложенного запроса по изменению каждого из параметров ордера/позиции:
//+------------------------------------------------------------------+ //| Класс абстрактного отложенного торгового запроса | //+------------------------------------------------------------------+ class CPendRequest : public CObject { private: MqlTradeRequest m_request; // Структура торгового запроса CPause m_pause; // Объект класса "Пауза" //--- Копирует данные торгового запроса void CopyRequest(const MqlTradeRequest &request); //--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство запроса int IndexProp(ENUM_PEND_REQ_PROP_DOUBLE property)const { return(int)property-PEND_REQ_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_PEND_REQ_PROP_STRING property)const { return(int)property-PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_DOUBLE_TOTAL; } protected: int m_digits; // Количество знаков после запятой в котировке символа int m_digits_lot; // Количество знаков после запятой в значении лота символа bool m_is_hedge; // Флаг счёта с типом "хедж" long m_long_prop[PEND_REQ_PROP_INTEGER_TOTAL]; // Целочисленные свойства запроса double m_double_prop[PEND_REQ_PROP_DOUBLE_TOTAL]; // Вещественные свойства запроса string m_string_prop[PEND_REQ_PROP_STRING_TOTAL]; // Строковые свойства запроса //--- Защищённый параметрический конструктор CPendRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode); //--- Возвращает (1) заданный в настройках магический номер, (2) флаг счёта с типом "хедж", (3) флаг равенства вещественного свойства значению ushort GetMagicID(void) const { return ushort(this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC) & 0xFFFF);} bool IsHedge(void) const { return this.m_is_hedge; } bool IsEqualByMode(const int mode,const double value) const; bool IsEqualByMode(const int mode,const long value) const; //--- Возвращает флаги завершения работы отложенного запроса по изменению каждого из параметров ордера/позиции bool IsCompletedVolume(void) const; bool IsCompletedPrice(void) const; bool IsCompletedStopLimit(void) const; bool IsCompletedStopLoss(void) const; bool IsCompletedTakeProfit(void) const; bool IsCompletedTypeFilling(void) const; bool IsCompletedTypeTime(void) const; bool IsCompletedExpiration(void) const; public:
В публичной секции класса объявим виртуальный метод, возвращающий флаг
завершения работы отложенного запроса, метод, возвращающий текущий
объект-запрос полностью, и методы для установки и возврата параметров
объекта-пауза:
public: //--- Конструктор по умолчанию CPendRequest(){;} //--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство запроса void SetProperty(ENUM_PEND_REQ_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_PEND_REQ_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_PEND_REQ_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value; } //--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство запроса long GetProperty(ENUM_PEND_REQ_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_PEND_REQ_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_PEND_REQ_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Возвращает флаг поддержания запросом данного свойства virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property) { return true; } //--- Возвращает флаг завершения работы отложенного запроса virtual bool IsCompleted(void) const { return false;} //--- Возвращает себя CPendRequest *GetObject(void) { return &this;} //--- Сравнивает объекты CPendRequest между собой по заданному свойству (для сортировки списков по указанному свойству объекта-запроса) virtual int Compare(const CObject *node,const int mode=0) const; //--- Сравнивает объекты CPendRequest между собой по всем свойствам (для поиска равных объектов-запросов) bool IsEqual(CPendRequest* compared_obj); //--- Возвращает из объекта-паузы (1) прошедшее количество милисекунд, (2) завершение времени ожидания, //--- время в милисекундах (3) начала отсчёта паузы, (4) времени ожидания ulong PausePassed(void) const { return this.m_pause.Passed(); } bool PauseIsCompleted(void) const { return this.m_pause.IsCompleted(); } ulong PauseTimeBegin(void) const { return this.m_pause.TimeBegin(); } ulong PauseTimeWait(void) const { return this.m_pause.TimeWait(); } //--- Возвращает описание (1) прошедшего количества секунд ожидания паузы, //--- (2) времени начала отсчёта паузы, времени ожидания (3) в милисекундах, (4) в секундах string PausePassedDescription(void) const { return this.m_pause.PassedDescription(); } string PauseTimeBeginDescription(void) const { return this.m_pause.TimeBeginDescription(); } string PauseWaitingMSCDescription(void) const { return this.m_pause.WaitingMSCDescription(); } string PauseWaitingSecDescription(void) const { return this.m_pause.WaitingSECDescription(); } //+------------------------------------------------------------------+
Виртуальный метод IsCompleted() здесь — в родительском объекте — всегда возвращает ложь, и будет реализован для каждого объекта-наследника индивидуально. Методы работы с объектом-паузой просто вызывают соответствующие методы этого объекта, рассмотренные нами в самом начале статьи.
В блок методов упрощённого доступа к свойствам объекта-отложенного запроса добавим
методы, возвращающие фактические свойства ордера:
//+------------------------------------------------------------------+ //| Методы упрощённого доступа к свойствам объекта-запроса | //+------------------------------------------------------------------+ //--- Возвращает (1) структуру запроса, (2) статус, (3) тип запроса, (4) цену при создании запроса, //--- (5) время создания запроса, (6) время активации очередной попытки, //--- (7) продолжительность ожидания между запросами, (8) номер текущей попытки, //--- (9) количество попыток, (10) идентификатор запроса //--- (11) результат, на основании которого создан запрос, //--- (12) тикет ордера, (13) тикет позиции, (14) тип торговой операции MqlTradeRequest MqlRequest(void) const { return this.m_request; } ENUM_PEND_REQ_STATUS Status(void) const { return (ENUM_PEND_REQ_STATUS)this.GetProperty(PEND_REQ_PROP_STATUS); } ENUM_PEND_REQ_TYPE TypeRequest(void) const { return (ENUM_PEND_REQ_TYPE)this.GetProperty(PEND_REQ_PROP_TYPE); } double PriceCreate(void) const { return this.GetProperty(PEND_REQ_PROP_PRICE_CREATE); } ulong TimeCreate(void) const { return this.GetProperty(PEND_REQ_PROP_TIME_CREATE); } ulong TimeActivate(void) const { return this.GetProperty(PEND_REQ_PROP_TIME_ACTIVATE); } ulong WaitingMSC(void) const { return this.GetProperty(PEND_REQ_PROP_WAITING); } uchar CurrentAttempt(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_CURRENT_ATTEMPT); } uchar TotalAttempts(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_TOTAL); } uchar ID(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_ID); } int Retcode(void) const { return (int)this.GetProperty(PEND_REQ_PROP_RETCODE); } ulong Order(void) const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_ORDER); } ulong Position(void) const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_POSITION); } ENUM_TRADE_REQUEST_ACTIONS Action(void) const { return (ENUM_TRADE_REQUEST_ACTIONS)this.GetProperty(PEND_REQ_PROP_MQL_REQ_ACTION); } //--- Возвращает фактический (1) объём, (2) цену установки ордера, (3) цену установки limit-ордера, //--- (4) цену установки stoploss-ордера, (5) цену установки takeprofit-ордера, (6) тип заливки ордера, //--- (7) тип экспирации ордера, (8) время жизни ордера double ActualVolume(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_VOLUME); } double ActualPrice(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_PRICE); } double ActualStopLimit(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT); } double ActualSL(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_SL); } double ActualTP(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_TP); } ENUM_ORDER_TYPE_FILLING ActualTypeFilling(void) const { return (ENUM_ORDER_TYPE_FILLING)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING); } ENUM_ORDER_TYPE_TIME ActualTypeTime(void) const { return (ENUM_ORDER_TYPE_TIME)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME); } datetime ActualExpiration(void) const { return (datetime)this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION); }
Каждый раз при проверке очередного объекта-отложенного запроса, в эти его свойства будут записываться соответствующие свойства ордера/позиции, для работы с которым создан объект-запрос. Эти методы позволяют получить из объекта-запроса текущие свойства соответствующего ему ордера.
В этом же блоке доработаем метод установки времени создания отложенного
запроса и метод установки его времени ожидания:
//--- Устанавливает (1) цену при создании запроса, (2) время создания запроса, //--- (3) время текущей попытки, (4) продолжительность ожидания между запросами, //--- (5) номер текущей попытки, (6) количество попыток, (7) идентификатор, //--- (8) тикет ордера, (9) тикет позиции void SetPriceCreate(const double price) { this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price); } void SetTimeCreate(const ulong time) { this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time); this.m_pause.SetTimeBegin(time); } void SetTimeActivate(const ulong time) { this.SetProperty(PEND_REQ_PROP_TIME_ACTIVATE,time); } void SetWaitingMSC(const ulong miliseconds) { this.SetProperty(PEND_REQ_PROP_WAITING,miliseconds); this.m_pause.SetWaitingMSC(miliseconds); }
Теперь эти методы одновременно с установкой передаваемых в них значений в объект-отложенный запрос, будут устанавливать эти значения и в объект-паузу.
И в том же блоке напишем методы установки в объект-запрос фактических свойств объекта-ордера:
//--- Устанавливает фактический (1) объём, (2) цену установки ордера, (3) цену установки limit-ордера, //--- (4) цену установки stoploss-ордера, (5) цену установки takeprofit-ордера, (6) тип заливки ордера, //--- (7) тип экспирации ордера, (8) время жизни ордера void SetActualVolume(const double volume) { this.SetProperty(PEND_REQ_PROP_ACTUAL_VOLUME,volume); } void SetActualPrice(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_PRICE,price); } void SetActualStopLimit(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT,price); } void SetActualSL(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_SL,price); } void SetActualTP(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TP,price); } void SetActualTypeFilling(const ENUM_ORDER_TYPE_FILLING type) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING,type); } void SetActualTypeTime(const ENUM_ORDER_TYPE_TIME type) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME,type); } void SetActualExpiration(const datetime expiration) { this.SetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION,expiration); } //+------------------------------------------------------------------+
В блок вывода описания свойств объекта добавим определение методов,
возвращающих описания фактических свойств ордера, и в самом конце добавим
виртуальный метод, возвращающий "заголовок" (краткое описание объекта), описывающий объект-отложенный запрос:
//+------------------------------------------------------------------+ //| Описания свойств объекта-запроса | //+------------------------------------------------------------------+ //--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства запроса string GetPropertyDescription(ENUM_PEND_REQ_PROP_INTEGER property); string GetPropertyDescription(ENUM_PEND_REQ_PROP_DOUBLE property); string GetPropertyDescription(ENUM_PEND_REQ_PROP_STRING property); //--- Возвращает наименования параметров объекта-отложенного запроса string StatusDescription(void) const; string TypeRequestDescription(void) const; string IDDescription(void) const; string RetcodeDescription(void) const; string TimeCreateDescription(void) const; string TimeActivateDescription(void) const; string TimeWaitingDescription(void) const; string CurrentAttemptDescription(void) const; string TotalAttemptsDescription(void) const; string PriceCreateDescription(void) const; string TypeFillingActualDescription(void) const; string TypeTimeActualDescription(void) const; string ExpirationActualDescription(void) const; string VolumeActualDescription(void) const; string PriceActualDescription(void) const; string StopLimitActualDescription(void) const; string StopLossActualDescription(void) const; string TakeProfitActualDescription(void) const; //--- Возвращает наименования параметров структуры торгового запроса в объекте-запросе string MqlReqActionDescription(void) const; string MqlReqMagicDescription(void) const; string MqlReqOrderDescription(void) const; string MqlReqSymbolDescription(void) const; string MqlReqVolumeDescription(void) const; string MqlReqPriceDescription(void) const; string MqlReqStopLimitDescription(void) const; string MqlReqStopLossDescription(void) const; string MqlReqTakeProfitDescription(void) const; string MqlReqDeviationDescription(void) const; string MqlReqTypeOrderDescription(void) const; string MqlReqTypeFillingDescription(void) const; string MqlReqTypeTimeDescription(void) const; string MqlReqExpirationDescription(void) const; string MqlReqCommentDescription(void) const; string MqlReqPositionDescription(void) const; string MqlReqPositionByDescription(void) const; //--- Выводит в журнал (1) описание свойств запроса (full_prop=true - все свойства, false - только поддерживаемые), //--- (2) краткое сообщение о запросе, (3) краткое наименование запроса (2 и 3 - реализация в потомках класса) void Print(const bool full_prop=false); virtual void PrintShort(void){;} virtual string Header(void){return NULL;} }; //+------------------------------------------------------------------+
Методы описания фактических свойств объекта-ордера рассмотрим чуть ниже, а виртуальный метод описания заголовка отложенного запроса здесь
возвращает NULL, и будет реализован в объектах-наследниках.
В конструкторе класса впишем инициализацию объекта-паузы значениями
времени создания объекта-запроса и его временем ожидания:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CPendRequest::CPendRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) { this.CopyRequest(request); this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; this.m_digits=(int)::SymbolInfoInteger(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL),SYMBOL_DIGITS); int dg=(int)DigitsLots(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL)); this.m_digits_lot=(dg==0 ? 1 : dg); this.SetProperty(PEND_REQ_PROP_STATUS,status); this.SetProperty(PEND_REQ_PROP_ID,id); this.SetProperty(PEND_REQ_PROP_RETCODE,retcode); this.SetProperty(PEND_REQ_PROP_TYPE,this.GetProperty(PEND_REQ_PROP_RETCODE)>0 ? PEND_REQ_TYPE_ERROR : PEND_REQ_TYPE_REQUEST); this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time); this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price); this.m_pause.SetTimeBegin(this.GetProperty(PEND_REQ_PROP_TIME_CREATE)); this.m_pause.SetWaitingMSC(this.GetProperty(PEND_REQ_PROP_WAITING)); } //+------------------------------------------------------------------+
Таким образом, сразу же при создании нового объекта-отложенного запроса в его объект-паузу будет вписано время начала ожидания и
продолжительность паузы.
За пределами тела класса напишем реализацию методов, возвращающих флаги равенства вещественного и целочисленного свойств
объекта-запроса и переданного в методы значения:
//+------------------------------------------------------------------+ //| Возвращает флаг равенства вещественного свойства значению | //+------------------------------------------------------------------+ bool CPendRequest::IsEqualByMode(const int mode,const double value) const { CPendRequest *req=new CPendRequest(); if(req==NULL) return false; req.SetProperty((ENUM_PEND_REQ_PROP_DOUBLE)mode,value); bool res=!this.Compare(req,mode); delete req; return res; } //+------------------------------------------------------------------+ //| Возвращает флаг равенства целочисленного свойства значению | //+------------------------------------------------------------------+ bool CPendRequest::IsEqualByMode(const int mode,const long value) const { CPendRequest *req=new CPendRequest(); if(req==NULL) return false; req.SetProperty((ENUM_PEND_REQ_PROP_INTEGER)mode,value); bool res=!this.Compare(req,mode); delete req; return res; } //+------------------------------------------------------------------+
В метод передаётся режим, по которому сравниваем свойство
объекта и значение, переданное в метод.
Режимом является
указание одного из значений свойства объекта из перечисления его вещественных (ENUM_PEND_REQ_PROP_DOUBLE) или целочисленных свойств
(ENUM_PEND_REQ_PROP_INTEGER). По этому свойству и будет проводиться сравнение с переданным в метод значением.
В методе создаём
новый временный объект-отложенный запрос и устанавливаем для его соответствующего
свойства переданное в метод значение.
Метод Compare()
базового объекта CObject стандартной библиотеки возвращает 0, что означает равенство. Но метод виртуальный, и реализуется в
потомках класса. Здесь метод Compare() реализован так, что возвращает 1 если значение текущего объекта больше такого же значения у
сравниваемого, -1 при значении текущего меньше значения сравниваемого, и 0 при равенстве.
Таким образом, если сравниваемые
значения равны, то в переменную res записывается false.
Но
мы туда записываем логическое отрицание результата проверки.
Таким образом, если метод сравнения возвращает 0, то !false равно true.
А если 1 или -1, то !true равно false.
После записи результата сравнения, временный объект удаляется и возвращается
результат сравнения.
Методы, возвращающие флаги сравнения фактических значений свойств ордера и объекта-запроса по их типам:
//+------------------------------------------------------------------+ //| Возвращает флаг успешного изменения объёма | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedVolume(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_VOLUME,this.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME)); } //+------------------------------------------------------------------+ //| Возвращает флаг успешной модификации цены установки | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedPrice(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_PRICE,this.GetProperty(PEND_REQ_PROP_MQL_REQ_PRICE)); } //+------------------------------------------------------------------+ //| Возвращает флаг успешной модификации цены StopLimit-ордера | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedStopLimit(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_STOPLIMIT,this.GetProperty(PEND_REQ_PROP_MQL_REQ_STOPLIMIT)); } //+------------------------------------------------------------------+ //| Возвращает флаг успешной модификации StopLoss-ордера | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedStopLoss(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_SL,this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL)); } //+------------------------------------------------------------------+ //| Возвращает флаг успешной модификации TakeProfit-ордера | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedTakeProfit(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_TP,this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP)); } //+------------------------------------------------------------------+ //| Возвращает флаг успешной модификации типа заливки ордера | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedTypeFilling(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_TYPE_FILLING,this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE_FILLING)); } //+------------------------------------------------------------------+ //| Возвращает флаг успешной модификации типа экспирации ордера | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedTypeTime(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_TYPE_TIME,this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE_TIME)); } //+------------------------------------------------------------------+ //| Возвращает флаг успешной модификации времени жизни ордера | //+------------------------------------------------------------------+ bool CPendRequest::IsCompletedExpiration(void) const { return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_EXPIRATION,this.GetProperty(PEND_REQ_PROP_MQL_REQ_EXPIRATION)); } //+------------------------------------------------------------------+
Методы возвращают результат сравнения двух соответствующих свойств — фактического и записанного в объекте, при помощи только что
рассмотренного метода сравнения.
В реализацию методов, возвращающих описания свойств объекта, впишем новые свойства:
//+------------------------------------------------------------------+ //| Возвращает описание целочисленного свойства запроса | //+------------------------------------------------------------------+ string CPendRequest::GetPropertyDescription(ENUM_PEND_REQ_PROP_INTEGER property) { return ( property==PEND_REQ_PROP_STATUS ? this.StatusDescription() : property==PEND_REQ_PROP_TYPE ? this.TypeRequestDescription() : property==PEND_REQ_PROP_ID ? this.IDDescription() : property==PEND_REQ_PROP_RETCODE ? this.RetcodeDescription() : property==PEND_REQ_PROP_TIME_CREATE ? this.TimeCreateDescription() : property==PEND_REQ_PROP_TIME_ACTIVATE ? this.TimeActivateDescription() : property==PEND_REQ_PROP_WAITING ? this.TimeWaitingDescription() : property==PEND_REQ_PROP_CURRENT_ATTEMPT ? this.CurrentAttemptDescription() : property==PEND_REQ_PROP_TOTAL ? this.TotalAttemptsDescription() : property==PEND_REQ_PROP_ACTUAL_TYPE_FILLING ? this.TypeFillingActualDescription() : property==PEND_REQ_PROP_ACTUAL_TYPE_TIME ? this.TypeTimeActualDescription() : property==PEND_REQ_PROP_ACTUAL_EXPIRATION ? this.ExpirationActualDescription() : //--- MqlTradeRequest property==PEND_REQ_PROP_MQL_REQ_ACTION ? this.MqlReqActionDescription() : property==PEND_REQ_PROP_MQL_REQ_TYPE ? this.MqlReqTypeOrderDescription() : property==PEND_REQ_PROP_MQL_REQ_MAGIC ? this.MqlReqMagicDescription() : property==PEND_REQ_PROP_MQL_REQ_ORDER ? this.MqlReqOrderDescription() : property==PEND_REQ_PROP_MQL_REQ_POSITION ? this.MqlReqPositionDescription() : property==PEND_REQ_PROP_MQL_REQ_POSITION_BY ? this.MqlReqPositionByDescription() : property==PEND_REQ_PROP_MQL_REQ_DEVIATION ? this.MqlReqDeviationDescription() : property==PEND_REQ_PROP_MQL_REQ_EXPIRATION ? this.MqlReqExpirationDescription() : property==PEND_REQ_PROP_MQL_REQ_TYPE_FILLING ? this.MqlReqTypeFillingDescription() : property==PEND_REQ_PROP_MQL_REQ_TYPE_TIME ? this.MqlReqTypeTimeDescription() : ::EnumToString(property) ); } //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| Возвращает описание вещественного свойства запроса | //+------------------------------------------------------------------+ string CPendRequest::GetPropertyDescription(ENUM_PEND_REQ_PROP_DOUBLE property) { return ( property==PEND_REQ_PROP_PRICE_CREATE ? this.PriceCreateDescription() : property==PEND_REQ_PROP_ACTUAL_VOLUME ? this.VolumeActualDescription() : property==PEND_REQ_PROP_ACTUAL_PRICE ? this.PriceActualDescription() : property==PEND_REQ_PROP_ACTUAL_STOPLIMIT ? this.StopLimitActualDescription() : property==PEND_REQ_PROP_ACTUAL_SL ? this.StopLossActualDescription() : property==PEND_REQ_PROP_ACTUAL_TP ? this.TakeProfitActualDescription() : //--- MqlTradeRequest property==PEND_REQ_PROP_MQL_REQ_VOLUME ? this.MqlReqVolumeDescription() : property==PEND_REQ_PROP_MQL_REQ_PRICE ? this.MqlReqPriceDescription() : property==PEND_REQ_PROP_MQL_REQ_STOPLIMIT ? this.MqlReqStopLimitDescription() : property==PEND_REQ_PROP_MQL_REQ_SL ? this.MqlReqStopLossDescription() : property==PEND_REQ_PROP_MQL_REQ_TP ? this.MqlReqTakeProfitDescription() : ::EnumToString(property) ); } //+------------------------------------------------------------------+
В методах проверяется переданное в него свойство объекта, и возвращается его описание при помощи соответствующих свойству методов.
Методы, возвращающие описания фактических свойств ордера, записанные в объекте-запросе:
//+------------------------------------------------------------------+ //| Возвращает описание фактического режима заливки ордера | //+------------------------------------------------------------------+ string CPendRequest::TypeFillingActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_FILLING)+": "+OrderTypeFillingDescription((ENUM_ORDER_TYPE_FILLING)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING)); } //+------------------------------------------------------------------+ //| Возвращает описание фактического типа срока действия ордера | //+------------------------------------------------------------------+ string CPendRequest::TypeTimeActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_TIME)+": "+OrderTypeTimeDescription((ENUM_ORDER_TYPE_TIME)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME)); } //+------------------------------------------------------------------+ //| Возвращает описание фактического срока истечения ордера | //+------------------------------------------------------------------+ string CPendRequest::ExpirationActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_EXPIRATION)+": "+ (this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION)>0 ? ::TimeToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION)) : CMessage::Text(MSG_LIB_PROP_NOT_SET)); } //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| Возвращает описание значения фактического объёма ордера | //+------------------------------------------------------------------+ string CPendRequest::VolumeActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_VOLUME)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_VOLUME),this.m_digits_lot); } //+------------------------------------------------------------------+ //| Возвращает описание значения фактической цены ордера | //+------------------------------------------------------------------+ string CPendRequest::PriceActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_PRICE)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_PRICE),this.m_digits); } //+------------------------------------------------------------------+ //| Возвращает описание значения фактической цены StopLimit-ордера | //+------------------------------------------------------------------+ string CPendRequest::StopLimitActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_STOPLIMIT)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT),this.m_digits); } //+------------------------------------------------------------------+ //| Возвращает описание значения фактической цены StopLoss-ордера | //+------------------------------------------------------------------+ string CPendRequest::StopLossActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_SL)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_SL),this.m_digits); } //+------------------------------------------------------------------+ //| Возвращает описание значения фактической цены TaleProfit-ордера | //+------------------------------------------------------------------+ string CPendRequest::TakeProfitActualDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TP)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_TP),this.m_digits); } //+------------------------------------------------------------------+
В методах просто составляется и возвращается текст сообщения, соответствующий свойству, возвращаемому методом.
Это все изменения базового объекта абстрактного отложенного запроса.
Теперь внесём изменения в классы-наследники базового объекта-запроса.
Откроем файл класса объекта отложенного запроса на открытие позиции \MQL5\Include\DoEasy\Objects\PendRequest\PendReqOpen.mqh и внесём правки.
Впишем определение виртуального метода, возвращающего краткое наименование
запроса:
//+------------------------------------------------------------------+ //| Отложенный запрос на открытие позиции | //+------------------------------------------------------------------+ class CPendReqOpen : public CPendRequest { public: //--- Конструктор CPendReqOpen(const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) : CPendRequest(PEND_REQ_STATUS_OPEN,id,price,time,request,retcode) {} //--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property); //--- Выводит в журнал краткое сообщениес данными запроса, (2) краткое наименование запроса virtual void PrintShort(void); virtual string Header(void); }; //+------------------------------------------------------------------+
И его реализацию:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование запроса | //+------------------------------------------------------------------+ string CPendReqOpen::Header(void) { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Класс отложенного запроса на модификацию стоп-приказов позиции (файл PendReqSLTP.mqh):
//+------------------------------------------------------------------+ //| Отложенный запрос на мидификацию стоп-приказов позиции | //+------------------------------------------------------------------+ class CPendReqSLTP : public CPendRequest { public: //--- Конструктор CPendReqSLTP(const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) : CPendRequest(PEND_REQ_STATUS_SLTP,id,price,time,request,retcode) {} //--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property); //--- Возвращает флаг завершения работы отложенного запроса virtual bool IsCompleted(void) const; //--- Выводит в журнал краткое сообщениес данными запроса, (2) краткое наименование запроса virtual void PrintShort(void); virtual string Header(void); }; //+------------------------------------------------------------------+
В методы, возвращающие флаг поддержания объектом некоторых его свойств, допишем новые свойства:
//+------------------------------------------------------------------+ //| Возвращает истину, если ордер поддерживает переданное | //| целочисленное свойство, возвращает ложь в противном случае | //+------------------------------------------------------------------+ bool CPendReqSLTP::SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property) { if(property==PEND_REQ_PROP_MQL_REQ_POSITION_BY || property==PEND_REQ_PROP_MQL_REQ_ORDER || property==PEND_REQ_PROP_MQL_REQ_EXPIRATION || property==PEND_REQ_PROP_MQL_REQ_DEVIATION || property==PEND_REQ_PROP_MQL_REQ_TYPE_FILLING || property==PEND_REQ_PROP_MQL_REQ_TYPE_TIME || property==PEND_REQ_PROP_ACTUAL_EXPIRATION || property==PEND_REQ_PROP_ACTUAL_TYPE_TIME || property==PEND_REQ_PROP_ACTUAL_TYPE_FILLING ) return false; return true; } //+------------------------------------------------------------------+ //| Возвращает истину, если ордер поддерживает переданное | //| вещественное свойство, возвращает ложь в противном случае | //+------------------------------------------------------------------+ bool CPendReqSLTP::SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property) { if(property==PEND_REQ_PROP_PRICE_CREATE || property==PEND_REQ_PROP_ACTUAL_SL || property==PEND_REQ_PROP_ACTUAL_TP || property==PEND_REQ_PROP_MQL_REQ_SL || property==PEND_REQ_PROP_MQL_REQ_TP ) return true; return false; } //+------------------------------------------------------------------+
Виртуальный метод, возвращающий флаг завершения работы отложенного запроса:
//+------------------------------------------------------------------+ //| Возвращает флаг завершения работы отложенного запроса | //+------------------------------------------------------------------+ bool CPendReqSLTP::IsCompleted(void) const { bool res=true; res &= this.IsCompletedStopLoss(); res &= this.IsCompletedTakeProfit(); return res; } //+------------------------------------------------------------------+
Здесь: в переменную res добавляем результаты проверки завершения модификации StopLoss и TakeProfit. Если хоть одна из проверок вернёт false, то общий результат будет false. Итоговый результат возвращаем из метода.
Метод, возвращающий краткое наименование запроса:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование запроса | //+------------------------------------------------------------------+ string CPendReqSLTP::Header(void) { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_SLTP)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Класс отложенного запроса на закрытие позиции (файл PendReqClose.mqh):
//+------------------------------------------------------------------+ //| Отложенный запрос на закрытие позиции | //+------------------------------------------------------------------+ class CPendReqClose : public CPendRequest { public: //--- Конструктор CPendReqClose(const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) : CPendRequest(PEND_REQ_STATUS_CLOSE,id,price,time,request,retcode) {} //--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property); //--- Возвращает флаг завершения работы отложенного запроса virtual bool IsCompleted(void) const; //--- Выводит в журнал краткое сообщениес данными запроса, (2) краткое наименование запроса virtual void PrintShort(void); virtual string Header(void); }; //+------------------------------------------------------------------+
Метод, возвращающий флаг завершения работы запроса:
//+------------------------------------------------------------------+ //| Возвращает флаг завершения работы отложенного запроса | //+------------------------------------------------------------------+ bool CPendReqClose::IsCompleted(void) const { return this.IsCompletedVolume(); } //+------------------------------------------------------------------+
Метод, возвращающий краткое наименование запроса:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование запроса | //+------------------------------------------------------------------+ string CPendReqClose::Header(void) { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_CLOSE)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Класс отложенного запроса на установку отложенного ордера (файл PendReqPlace.mqh):
//+------------------------------------------------------------------+ //| Отложенный запрос на установку отложенного ордера | //+------------------------------------------------------------------+ class CPendReqPlace : public CPendRequest { public: //--- Конструктор CPendReqPlace(const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) : CPendRequest(PEND_REQ_STATUS_PLACE,id,price,time,request,retcode) {} //--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property); //--- Выводит в журнал краткое сообщениес данными запроса, (2) краткое наименование запроса virtual void PrintShort(void); virtual string Header(void); }; //+------------------------------------------------------------------+
Метод, возвращающий краткое наименование запроса:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование запроса | //+------------------------------------------------------------------+ string CPendReqPlace::Header(void) { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_PLACE)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Класс отложенного запроса на изменение параметров отложенного ордера (файл PendReqModify.mqh):
//+------------------------------------------------------------------+ //| Отложенный запрос на мидификацию параметров отложенного ордера | //+------------------------------------------------------------------+ class CPendReqModify : public CPendRequest { public: //--- Конструктор CPendReqModify(const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) : CPendRequest(PEND_REQ_STATUS_MODIFY,id,price,time,request,retcode) {} //--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property); //--- Возвращает флаг завершения работы отложенного запроса virtual bool IsCompleted(void) const; //--- Выводит в журнал краткое сообщениес данными запроса, (2) краткое наименование запроса virtual void PrintShort(void); virtual string Header(void); }; //+------------------------------------------------------------------+
Метод, возвращающий флаг завершения работы запроса:
//+------------------------------------------------------------------+ //| Возвращает флаг завершения работы отложенного запроса | //+------------------------------------------------------------------+ bool CPendReqModify::IsCompleted(void) const { bool res=true; res &= this.IsCompletedPrice(); res &= this.IsCompletedStopLimit(); res &= this.IsCompletedStopLoss(); res &= this.IsCompletedTakeProfit(); res &= this.IsCompletedTypeFilling(); res &= this.IsCompletedTypeTime(); res &= this.IsCompletedExpiration(); return res; } //+------------------------------------------------------------------+
Метод, возвращающий краткое наименование запроса:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование запроса | //+------------------------------------------------------------------+ string CPendReqModify::Header(void) { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Класс отложенного запроса на удаление отложенного ордера (файл PendReqRemove.mqh):
//+------------------------------------------------------------------+ //| Отложенный запрос на удаление отложенного ордера | //+------------------------------------------------------------------+ class CPendReqRemove : public CPendRequest { public: //--- Конструктор CPendReqRemove(const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) : CPendRequest(PEND_REQ_STATUS_REMOVE,id,price,time,request,retcode) {} //--- Поддерживаемые свойства сделки (1) вещественные, (2) целочисленные virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property); //--- Выводит в журнал краткое сообщениес данными запроса, (2) краткое наименование запроса virtual void PrintShort(void); virtual string Header(void); }; //+------------------------------------------------------------------+
Метод, возвращающий краткое наименование запроса:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование запроса | //+------------------------------------------------------------------+ string CPendReqRemove::Header(void) { return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID); } //+------------------------------------------------------------------+
Как видим из листингов классов и их соответствующих методов, здесь всё достаточно банально — каждый метод использует только присущие ему свойства объекта абстрактного отложенного запроса, и в кратком наименовании запроса составляется текст из соответствующих запросу текстовых сообщений.
На этом доработка классов-наследников базового класса объекта абстрактного отложенного торгового запроса завершена.
Класс управления объектами-отложенными запросами
Теперь займёмся наконец классом управления объектами-отложенными запросами.
Как уже писалось выше, данный класс у нас будет унаследован от торгового класса CTrading ввиду того, что в классах CTrading, CPendRequest и в
новом классе CTradingControl все данные и методы достаточно тесно взаимосвязаны. Класс в данной реализации будет совсем небольшим,
работать будет в таймере, и весь код в таймер будет перенесён из кода таймера класса CTrading с небольшими доработками.
Создадим новый класс CTradingControl в файле \MQL5\Include\DoEasy\TradingControl.mqh. Базовым классом при создании этого класса укажем CTrading, и не забываем сразу же после создания класса подключить к нему файл Trading.mqh:
//+------------------------------------------------------------------+ //| PendReqControl.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 "Trading.mqh" //+------------------------------------------------------------------+ //| Класс управления отложенными торговыми запросами | //+------------------------------------------------------------------+ class CTradingControl : public CTrading { private: //--- Устнанавливает в объект-отложенный запрос фактические данные ордера/позиции void SetActualProperties(CPendRequest *req_obj,const COrder *order); public: //--- Возвращает себя CTradingControl *GetObject(void) { return &this; } //--- Таймер virtual void OnTimer(void); //--- Конструктор CTradingControl(); }; //+------------------------------------------------------------------+
Что мы тут видим:
класс имеет приватный метод SetActualProperties(), устанавливающий в объект-отложенный
запрос фактические данные ордера/позиции.
В публичной секции метод GetObject() возвращает указатель на весь объект
управления отложенными запросами, а виртуальный метод OnTimer() является таймером класса управления отложенными запросами,
где всё и будет работать.
В конструкторе класса очищаем список отложенных запросов и устанавливаем ему флаг сортированного списка:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CTradingControl::CTradingControl() { this.m_list_request.Clear(); this.m_list_request.Sort(); } //+------------------------------------------------------------------+
Данный список принадлежит классу CTrading, так как именно в торговом классе создаются отложенные торговые запросы.
За пределами тела класса напишем реализацию метода установки фактических данных ордера в объект-запрос:
//+------------------------------------------------------------------+ //| Устнанавливает в объект-отложенный запрос данные ордера/позиции | //+------------------------------------------------------------------+ void CTradingControl::SetActualProperties(CPendRequest *req_obj,const COrder *order) { req_obj.SetActualExpiration(order.TimeExpiration()); req_obj.SetActualPrice(order.PriceOpen()); req_obj.SetActualSL(order.StopLoss()); req_obj.SetActualStopLimit(order.PriceStopLimit()); req_obj.SetActualTP(order.TakeProfit()); req_obj.SetActualTypeFilling(order.TypeFilling()); req_obj.SetActualTypeTime(order.TypeTime()); req_obj.SetActualVolume(order.Volume()); } //+------------------------------------------------------------------+
В метод передаются указатель на объект-отложенный запрос и указатель на объект-ордер. А далее в объекте-запросе при помощи соответствующих методов, рассмотренных нами выше, устанавливаются соответствующие значения свойств ордера.
В таймере класса организовано отслеживание всех имеющихся объектов-запросов в списке отложенных запросов. Проверяется время жизни каждого отложенного запроса, и если оно истекло, то запрос удаляется. Если же запрос уже отработан — есть соответствующее ему торговое событие в истории счёта, или существует ордер/позиция с идентификатором отложенного запроса в магике, соответствующем идентификатору отложенного запроса, то такие запросы считаются полностью отработанными, и удаляются из списка.
Если же запрос ещё не отработан, и наступило время его активации, то на сервер отправляется записанный в отложенном запросе торговый приказ.
В методе организована проверка разрешения на проведение торговых операций со стороны терминала — кнопка "Авто-торговля" и
галочка в настройках советника "Разрешить авто-торговлю". И если отложенный торговый запрос создан по коду ошибки 10027 — запрет
автоторговли со стороны терминала, то при устранении причины ошибки пользователем (включение кнопки "Авто-торговля" или установка в
настройках советника галочки "Разрешить авто-торговлю") этот отложенный запрос сразу же обрабатывается — устанавливается новое время
активации отложенного запроса так, чтобы на следующем тике оно наступило — ведь пользователь исправил ошибку, и уже не нужно ожидать, а
сразу же стоит отправить приказ на сервер во-избежание последующих реквот.
Так как метод достаточно объёмный, то все действия, проводимые внутри таймера класса управления отложенными запросами, я постарался подробно расписать в комментариях к коду, и надеюсь, они понятны для самостоятельного изучения.
Таймер класса управления отложенными запросами:
//+------------------------------------------------------------------+ //| Таймер | //+------------------------------------------------------------------+ void CTradingControl::OnTimer(void) { //--- В цикле по списку отложенных запросов int total=this.m_list_request.Total(); for(int i=total-1;i>WRONG_VALUE;i--) { //--- получаем очередной объект-запрос CPendRequest *req_obj=this.m_list_request.At(i); if(req_obj==NULL) continue; //--- получаем структуру запроса и из неё объект-символ, на котором должна быть проведена торговая операция MqlTradeRequest request=req_obj.MqlRequest(); CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol); if(symbol_obj==NULL || !symbol_obj.RefreshRates()) continue; //--- Устанавливаем флаг запрета торговли со стороны терминала одновременно с двух свойств //--- (кнопка Авто-торговля в терминале и галочка "Разрешить автоматическую торговлю" в настройках советника) //--- Если любое из двух свойств имеет значение false, то флаг будет иметь значение false bool terminal_trade_allowed=::TerminalInfoInteger(TERMINAL_TRADE_ALLOWED); terminal_trade_allowed &=::MQLInfoInteger(MQL_TRADE_ALLOWED); //--- Если объект-запрос создан по коду ошибки if(req_obj.TypeRequest()==PEND_REQ_TYPE_ERROR) { //--- если ошибка - запрет торговли со стороны терминала, и её причина устранена if(req_obj.Retcode()==10027 && terminal_trade_allowed) { //--- если текущая попытка ещё не превысила установленное количество торговых попыток if(req_obj.CurrentAttempt()<req_obj.TotalAttempts()+1) { //--- Установим время создания запроса равным времени его создания минус время ожидания - т.е., отослать запрос немедленно //--- И уменьшим номер текущей попытки, так как при последующем запросе номер попытки будет увеличен, и если это последняя попытка, //--- то она уже исполнена не будет. Но это исправление причины ошибки пользователем, а значит - нужно дать ещё время на последнюю попытку req_obj.SetTimeCreate(req_obj.TimeCreate()-req_obj.WaitingMSC()); req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()>0 ? req_obj.CurrentAttempt()-1 : 0)); } } } //--- если текущая попытка превысила установленное количество торговых попыток, //--- или текущее время больше времени ожидания всех попыток //--- удаляем текущий объект-запрос и переходим к следующему if(req_obj.CurrentAttempt()>req_obj.TotalAttempts() || req_obj.CurrentAttempt()>=UCHAR_MAX || (long)symbol_obj.Time()>long(req_obj.TimeCreate()+req_obj.WaitingMSC()*req_obj.TotalAttempts())) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DELETED)); this.m_list_request.Delete(i); continue; } //--- Если это открытие позиции или установка отложенного ордера if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()==0) || req_obj.Action()==TRADE_ACTION_PENDING) { //--- Получаем идентификатор отложенного запроса uchar id=this.GetPendReqID((uint)request.magic); //--- Получаем список ордеров/позиций, содержащий ордер/позицию с таким идентификатором отложенного запроса CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL); if(::CheckPointer(list)==POINTER_INVALID) continue; //--- Если есть такой ордер/позиция - запрос отработан: удаляем его и переходим к следующему if(list.Total()>0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(i); continue; } } //--- Иначе: полное и частичное закрытие позиции, удаление ордера, модификация параметров ордера и стоп приказов позиции else { CArrayObj *list=NULL; //--- если это закрытие позиции, в том числе и встречным if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()>0) || req_obj.Action()==TRADE_ACTION_CLOSE_BY) { //--- Получаем из списка открытых позиций позицию с нужным тикетом list=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Position(),EQUAL); if(::CheckPointer(list)==POINTER_INVALID) continue; //--- Если в рынке нет такой позиции - запрос отработан: удаляем его и переходим к следующему if(list.Total()==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(i); continue; } //--- Иначе, если позиция ещё существует, то это частичное закрытие else { //--- Получаем список всех торговых событий аккаунта list=this.m_events.GetList(); if(list==NULL) continue; //--- В цикле с конца списка торговых событий аккаунта int events_total=list.Total(); for(int j=events_total-1; j>WRONG_VALUE; j--) { //--- получаем очередное торговое событие CEvent *event=list.At(j); if(event==NULL) continue; //--- Если это событие - частичное закрытие, или при закрытии встречным было частичное закрытие if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL || event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS) { //--- Если тикет позиции в торговом событии совпадает с тикетом в отложенном торговом запросе if(event.PositionID()==req_obj.Position()) { //--- Получаем объект-позицию из списка рыночных позиций CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL); if(list_orders==NULL || list_orders.Total()==0) break; COrder *order=list_orders.At(list_orders.Total()-1); if(order==NULL) break; //--- Устанавливаем фактические данные позиции в объект-отложенный запрос this.SetActualProperties(req_obj,order); //--- Если (исполненный объём заявки + неисполненный объём заявки) равен запрашиваемому объёму в отложенном запросе - //--- запрос отработан: удаляем его и прерываем цикл по списку торговых событий аккаунта if(req_obj.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME)==event.VolumeOrderExecuted()+event.VolumeOrderCurrent()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(i); break; } } } } //--- Если в цикле по списку торговых событий был удалён отработанный объект-отложенный запрос - переходим к следующему if(::CheckPointer(req_obj)==POINTER_INVALID) continue; } } //--- Если это модификация стоп-приказов позиции if(req_obj.Action()==TRADE_ACTION_SLTP) { //--- Получаем список всех торговых событий аккаунта list=this.m_events.GetList(); if(list==NULL) continue; //--- В цикле с конца списка торговых событий аккаунта int events_total=list.Total(); for(int j=events_total-1; j>WRONG_VALUE; j--) { //--- получаем очередное торговое событие CEvent *event=list.At(j); if(event==NULL) continue; //--- Если это событие - любое изменение стоп-приказов позиции if(event.TypeEvent()>TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT) { //--- Если тикет позиции в торговом событии совпадает с тикетом в отложенном торговом запросе if(event.PositionID()==req_obj.Position()) { //--- Получаем объект-позицию из списка рыночных позиций CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL); if(list_orders==NULL || list_orders.Total()==0) break; COrder *order=list_orders.At(list_orders.Total()-1); if(order==NULL) break; //--- Устанавливаем фактические данные позиции в объект-отложенный запрос this.SetActualProperties(req_obj,order); //--- Если все модификации отработаны - //--- запрос отработан: удаляем его и прерываем цикл по списку торговых событий аккаунта if(req_obj.IsCompleted()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(i); break; } } } } //--- Если в цикле по списку торговых событий был удалён отработанный объект-отложенный запрос - переходим к следующему if(::CheckPointer(req_obj)==POINTER_INVALID) continue; } //--- Если это удаление отложенного ордера if(req_obj.Action()==TRADE_ACTION_REMOVE) { //--- Получаем список удалённых отложенных ордеров из исторического списка list=this.m_history.GetList(ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL); if(::CheckPointer(list)==POINTER_INVALID) continue; //--- Оставляем в списке только один ордер с нужным тикетом list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,req_obj.Order(),EQUAL); //--- Если есть такой ордер - запрос отработан: удаляем его и переходим к следующему if(list.Total()>0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(i); continue; } } //--- Если это модификация отложенного ордера if(req_obj.Action()==TRADE_ACTION_MODIFY) { //--- Получаем список всех торговых событий аккаунта list=this.m_events.GetList(); if(list==NULL) continue; //--- В цикле с конца списка торговых событий аккаунта int events_total=list.Total(); for(int j=events_total-1; j>WRONG_VALUE; j--) { //--- получаем очередное торговое событие CEvent *event=list.At(j); if(event==NULL) continue; //--- Если это событие - любое изменение модифицируемых параметров отложенного ордера if(event.TypeEvent()>TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER && event.TypeEvent()<TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT) { //--- Если тикет ордера в торговом событии совпадает с тикетом в отложенном торговом запросе if(event.TicketOrderEvent()==req_obj.Order()) { //--- Получаем объект-ордер из списка CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Order(),EQUAL); if(list_orders==NULL || list_orders.Total()==0) break; COrder *order=list_orders.At(0); if(order==NULL) break; //--- Устанавливаем фактические данные ордера в объект-отложенный запрос this.SetActualProperties(req_obj,order); //--- Если все модификации отработаны - //--- запрос отработан: удаляем его и прерываем цикл по списку торговых событий аккаунта if(req_obj.IsCompleted()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(i); break; } } } } } } //--- Если после проверки отработки запроса объект-отложенный запрос удалён - выходим if(::CheckPointer(req_obj)==POINTER_INVALID) return; //--- Устанавливаем время активации запроса в объекте-запросе req_obj.SetTimeActivate(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.CurrentAttempt()+1)); //--- Если текущее время меньше времени активации запроса, //--- значит время запроса не наступило - идём к следующему запросу в списке if((long)symbol_obj.Time()<(long)req_obj.TimeActivate()) continue; //--- Устанавливаем номер попытки в объекте-запросе req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1)); //--- Выводим в журнал номер торговой попытки if(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_RE_TRY_N)+(string)req_obj.CurrentAttempt()+":"); req_obj.PrintShort(); } //--- В зависимости от типа выполняемого действия в торговом запросе switch(request.action) { //--- Открытие/закрытие позиции case TRADE_ACTION_DEAL : //--- Если тикета нет в структуре запроса - это открытие позиции if(request.position==0) this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling); //--- Если тикет есть в структуре запроса - это закрытие позиции else this.ClosePosition(request.position,request.volume,request.comment,request.deviation); break; //--- Модификация StopLoss/TakeProfit позиции case TRADE_ACTION_SLTP : this.ModifyPosition(request.position,request.sl,request.tp); break; //--- Закрытие встречным case TRADE_ACTION_CLOSE_BY : this.ClosePositionBy(request.position,request.position_by); break; //--- //--- Установка отложенного ордера case TRADE_ACTION_PENDING : this.PlaceOrder(request.type,request.volume,request.symbol,request.price,request.stoplimit,request.sl,request.tp,request.magic,request.comment,request.expiration,request.type_time,request.type_filling); break; //--- Модификация отложенного ордера case TRADE_ACTION_MODIFY : this.ModifyOrder(request.order,request.price,request.sl,request.tp,request.stoplimit,request.expiration,request.type_time,request.type_filling); break; //--- Удаление отложенного ордера case TRADE_ACTION_REMOVE : this.DeleteOrder(request.order); break; //--- default: break; } } } //+------------------------------------------------------------------+
На сегодня это всё, что нужно было сделать в классе управления отложенными торговыми запросами. Если что-либо не понятно в работе таймера класса, то все вопросы можно озвучить в обсуждении к статье.
Внесём изменения в класс базового объекта библиотеки CEngine.
Так как теперь вместо торгового класса будем использовать его наследника — класс управления отложенными запросами, то
вместо
подключения к листингу файла торгового класса:
//+------------------------------------------------------------------+ //| Engine.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\TimerCounter.mqh" #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Collections\SymbolsCollection.mqh" #include "Collections\ResourceCollection.mqh" #include "Trading.mqh" //+------------------------------------------------------------------+
подключим файл класса управления отложенными торговыми запросами:
//+------------------------------------------------------------------+ //| Engine.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\TimerCounter.mqh" #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Collections\SymbolsCollection.mqh" #include "Collections\ResourceCollection.mqh" #include "TradingControl.mqh" //+------------------------------------------------------------------+
А вместо объекта торгового класса
//+------------------------------------------------------------------+ //| Класс-основа библиотеки | //+------------------------------------------------------------------+ class CEngine { private: CHistoryCollection m_history; // Коллекция исторических ордеров и сделок CMarketCollection m_market; // Коллекция рыночных ордеров и сделок CEventsCollection m_events; // Коллекция событий CAccountsCollection m_accounts; // Коллекция аккаунтов CSymbolsCollection m_symbols; // Коллекция символов CResourceCollection m_resource; // Список ресурсов CTrading m_trading; // Объект торгового класса CArrayObj m_list_counters; // Список счётчиков таймера
будем использовать объект класса управления торговыми запросами:
//+------------------------------------------------------------------+ //| Класс-основа библиотеки | //+------------------------------------------------------------------+ class CEngine { private: CHistoryCollection m_history; // Коллекция исторических ордеров и сделок CMarketCollection m_market; // Коллекция рыночных ордеров и сделок CEventsCollection m_events; // Коллекция событий CAccountsCollection m_accounts; // Коллекция аккаунтов CSymbolsCollection m_symbols; // Коллекция символов CResourceCollection m_resource; // Список ресурсов CTradingControl m_trading; // Объект управления торговлей CArrayObj m_list_counters; // Список счётчиков таймера
И на сегодня это все изменения в библиотеке.
Тестирование
Для тестирования новой версии библиотеки возьмём советник из прошлой статьи и сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part30\ под новым именем TestDoEasyPart30.mq5.
Скомпилируем советник и запустим его на графике демо-счёта.
Нам нужно проверить как отрабатывают объекты отложенных запросов при отключенной кнопке Авто-торговля в терминале.
В
настройках советника установим дистанцию установки StopLoss и TakeProfit в значения 0.
За это отвечают два настраиваемых
параметра:
- StopLoss in points
- TakeProfit in points.
А далее сделаем так:
сначала отключим автоторговлю кнопкой
"Авто-торговля" в терминале, пытаемся отправить торговый запрос, например, на установку отложенного ордера SellLimit кнопкой с
торговой панели советника.
Соответственно, получим ошибку запрета торговли
со стороны терминала,
при этом создастся отложенный запрос.
Далее сразу же нажимаем кнопку "Авто-торговля" чтобы исправить ошибку.
И по замыслу — тут же должен быть активирован только что созданный
торговый запрос, который отошлёт торговый запрос на сервер,
и после его
исполнения
объект-запрос будет удалён как исполненный:
2019.12.27 04:16:28.894 automated trading is disabled 2019.12.27 04:16:33.177 CTrading::PlaceOrder<uint,int,uint,uint>: Invalid request: 2019.12.27 04:16:33.177 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled) 2019.12.27 04:16:33.177 Correction of trade request parameters ... 2019.12.27 04:16:33.178 Pending request created #1: 2019.12.27 04:16:33.178 Pending request to place a pending order: 2019.12.27 04:16:33.178 - GBPUSD 0.10 Pending order Sell Limit, Price 1.30088 2019.12.27 04:16:33.178 - Pending request ID: #1, Created 2019.12.26 23:16:30.003, Attempts 5, Wait 00:00:20, End 2019.12.26 23:18:10.003 2019.12.27 04:16:33.178 2019.12.27 04:16:37.397 automated trading is enabled 2019.12.27 04:16:37.472 Retry trading attempt #1: 2019.12.27 04:16:37.472 Pending request to place a pending order: 2019.12.27 04:16:37.472 - GBPUSD 0.10 Pending order Sell Limit, Price 1.30088 2019.12.27 04:16:37.472 - Pending request ID: #1, Created 2019.12.26 23:16:10.003, Attempts 5, Wait 00:00:20, End 2019.12.26 23:17:50.003 2019.12.27 04:16:37.472 2019.12.27 04:16:37.977 - Pending order placed: 2019.12.26 23:16:38.325 - 2019.12.27 04:16:37.977 GBPUSD Placed 0.10 Pending order Sell Limit #500442708 at price 1.30088, Magic number 26148987 (123), G1: 15, G2: 8, ID: 1 2019.12.27 04:16:37.979 OnDoEasyEvent: Pending order placed 2019.12.27 04:16:38.024 Pending request to place a pending order, ID #1: Deleted due completed
Теперь необходимо проверить возможность модификации стоп-приказов только что установленного отложенного ордера
(он был установлен без них).
Опять отключим автоторговлю и
нажмём кнопку "Set StopLoss" на торговой панели тестового советника.
Получим ошибку запрета проведения торговых операций со стороны терминала и
будет создан отложенный запрос на изменение параметров отложенного ордера.
После очередной торговой попытки, которую уже
отсылает отложенный запрос,
разрешим в терминале автоторговлю,
и
отложенному ордеру будет выставлен StopLoss,
а сам отложенный
запрос удалён как отработавший:
2019.12.27 04:24:11.671 automated trading is disabled 2019.12.27 04:24:16.653 CTrading::ModifyOrder<int,int,double,int>: Invalid request: 2019.12.27 04:24:16.653 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled) 2019.12.27 04:24:16.653 Correction of trade request parameters ... 2019.12.27 04:24:16.653 Pending request created #1: 2019.12.27 04:24:16.653 Pending request to modify pending order parameters: 2019.12.27 04:24:16.653 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208 2019.12.27 04:24:16.653 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:24:16.653 - Order expiration type: Good till cancel order 2019.12.27 04:24:16.653 - Pending request ID: #1, Created 2019.12.26 23:24:00.270, Attempts 5, Wait 00:00:20, End 2019.12.26 23:25:40.270 2019.12.27 04:24:16.653 2019.12.27 04:24:25.803 Retry trading attempt #1: 2019.12.27 04:24:25.803 Pending request to modify pending order parameters: 2019.12.27 04:24:25.803 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208 2019.12.27 04:24:25.803 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:24:25.803 - Order expiration type: Good till cancel order 2019.12.27 04:24:25.803 - Pending request ID: #1, Created 2019.12.26 23:24:00.270, Attempts 5, Wait 00:00:20, End 2019.12.26 23:25:40.270 2019.12.27 04:24:25.803 2019.12.27 04:24:29.770 automated trading is enabled 2019.12.27 04:24:30.022 Retry trading attempt #1: 2019.12.27 04:24:30.022 Pending request to modify pending order parameters: 2019.12.27 04:24:30.022 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208 2019.12.27 04:24:30.022 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:24:30.022 - Order expiration type: Good till cancel order 2019.12.27 04:24:30.022 - Pending request ID: #1, Created 2019.12.26 23:23:40.270, Attempts 5, Wait 00:00:20, End 2019.12.26 23:25:20.270 2019.12.27 04:24:30.022 2019.12.27 04:24:30.405 - Modified order StopLoss: 2019.12.26 23:16:38.325 - 2019.12.27 04:24:30.405 GBPUSD Pending order Sell Limit #500442708: Modified order StopLoss: [0.00000 --> 1.30208], Magic number 26148987 (123), G1: 15, G2: 8, ID: 1 2019.12.27 04:24:30.405 OnDoEasyEvent: Modified order StopLoss 2019.12.27 04:24:30.601 Pending request to modify pending order parameters, ID #1: Deleted due completed
Здесь видно, что после разрешения автоторговли, повторный запрос от объекта-отложенного запроса имеет тот же номер, что и при первой попытке повторного торгового запроса - т.е, логика добавления ещё одной попытки при устранении причины ошибки пользователем (разрешение автоторговли в терминале) сработала верно.
И теперь сделаем то же самое, но для установки TakeProfit.
И опять всё отработало (здесь после второй торговой
попытки, проведённой объектом-отложенным запросом),
TakeProfit был выставлен объектом-отложенным запросом и этот запрос был
удалён:
2019.12.27 04:32:46.843 automated trading is disabled 2019.12.27 04:32:50.810 CTrading::ModifyOrder<int,int,int,double>: Invalid request: 2019.12.27 04:32:50.810 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled) 2019.12.27 04:32:50.810 Correction of trade request parameters ... 2019.12.27 04:32:50.810 Pending request created #1: 2019.12.27 04:32:50.810 Pending request to modify pending order parameters: 2019.12.27 04:32:50.810 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888 2019.12.27 04:32:50.810 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:32:50.810 - Order expiration type: Good till cancel order 2019.12.27 04:32:50.810 - Pending request ID: #1, Created 2019.12.26 23:32:47.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:27.943 2019.12.27 04:32:50.810 2019.12.27 04:33:08.782 Retry trading attempt #1: 2019.12.27 04:33:08.782 Pending request to modify pending order parameters: 2019.12.27 04:33:08.782 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888 2019.12.27 04:33:08.782 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:33:08.782 - Order expiration type: Good till cancel order 2019.12.27 04:33:08.782 - Pending request ID: #1, Created 2019.12.26 23:32:47.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:27.943 2019.12.27 04:33:08.782 2019.12.27 04:33:29.984 Retry trading attempt #2: 2019.12.27 04:33:29.984 Pending request to modify pending order parameters: 2019.12.27 04:33:29.984 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888 2019.12.27 04:33:29.984 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:33:29.984 - Order expiration type: Good till cancel order 2019.12.27 04:33:29.984 - Pending request ID: #1, Created 2019.12.26 23:32:47.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:27.943 2019.12.27 04:33:29.984 2019.12.27 04:33:31.999 automated trading is enabled 2019.12.27 04:33:32.250 Retry trading attempt #2: 2019.12.27 04:33:32.250 Pending request to modify pending order parameters: 2019.12.27 04:33:32.250 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888 2019.12.27 04:33:32.250 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return) 2019.12.27 04:33:32.250 - Order expiration type: Good till cancel order 2019.12.27 04:33:32.250 - Pending request ID: #1, Created 2019.12.26 23:32:27.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:07.943 2019.12.27 04:33:32.250 2019.12.27 04:33:32.352 - Modified order TakeProfit: 2019.12.26 23:24:26.509 - 2019.12.27 04:33:32.352 GBPUSD Pending order Sell Limit #500442708: Modified order TakeProfit: [0.00000 --> 1.29888], Magic number 26148987 (123), G1: 15, G2: 8, ID: 1 2019.12.27 04:33:32.352 OnDoEasyEvent: Modified order TakeProfit 2019.12.27 04:33:32.754 Pending request to modify pending order parameters, ID #1: Deleted due completed
Из проведённых тестов видим, что теперь мы можем делать разные торговые операции с одним и тем же ордером или позицией (торговые операции с одним и тем же тикетом). В прошлых версиях мы могли провести только одну торговую операцию с одним тикетом, а далее объект-отложенный запрос считал всегда, что он уже отработал. Сейчас мы такое поведение исправили.
Остальные торговые операции при работе с отложенными запросами мы постепенно проверим и отладим при дальнейшей разработке библиотеки, так как
всё требует тщательного и длительного тестирования для выявления и устранения различных нестандартных ситуаций.
Что дальше
В следующей статье продолжим развитие концепции отложенных торговых запросов.
Ниже прикреплены все файлы текущей версии библиотеки и файлы тестового советника. Их можно скачать и протестировать всё
самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.
Статьи этой серии:
Часть 1. Концепция, организация данныхЧасть 2. Коллекция исторических ордеров и сделок
Часть 3. Коллекция рыночных ордеров и позиций, организация поиска
Часть 4. Торговые события. Концепция
Часть 5. Классы и коллекция торговых событий. Отправка событий в программу
Часть 6. События на счёте с типом неттинг
Часть 7. События срабатывания StopLimit-ордеров, подготовка функционала для регистрации событий модификации ордеров и позиций
Часть 8. События модификации ордеров и позиций
Часть 9. Совместимость с MQL4 — Подготовка данных
Часть 10. Совместимость с MQL4 — События открытия позиций и активации отложенных ордеров
Часть 11. Совместимость с MQL4 — События закрытия позиций
Часть 12. Класс объекта "аккаунт", коллекция объектов-аккаунтов
Часть 13. События объекта "аккаунт"
Часть 14. Объект "Символ"
Часть 15. Коллекция объектов-символов
Часть 16. События коллекции символов
Часть 17. Интерактивность объектов библиотеки
Часть 18. Интерактивность объекта-аккаунт и любых других объектов библиотеки
Часть 19. Класс сообщений библиотеки
Часть 20. Создание и хранение ресурсов программы
Часть 21. Торговые классы — Базовый кроссплатформенный торговый объект
Часть 22. Торговые классы — Основной торговый класс, контроль ограничений
Часть 23. Торговые классы — Основной торговый класс, контроль допустимых параметров
Часть 24. Торговые классы — Основной торговый класс, автоматическая коррекция ошибочных параметров
Часть 25. Торговые классы — Основной торговый класс, обработка ошибок, возвращаемых торговым сервером
Часть 26. Работа с отложенными торговыми запросами - первая реализация (открытие позиций)
Часть 27. Работа с отложенными торговыми запросами - выставление отложенных ордеров
Часть 28. Работа с отложенными торговыми запросами - закрытие, удаление, модификации
Часть 29. Работа с отложенными торговыми запросами - классы объектов-запросов
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Артём, добрый день. Ваша работа бесценна! Благодарю за возможность развиваться и время, которое вы потратили, чтобы такие, как я, могли его качественно экономить.
Я с предложением: может опубликуем репозиторий с вашей либой на гитхаб? Что это даст:
Например, в процессе изучения библиотеки я нашел и исправил несколько ошибок, а также оптимизировал часть кода. И в таком случае я мог бы теперь отправить вам специальный запрос в рамках системы контроля версий, по которому вы приняли бы решение: принимать в актуальную версию библиотеки мои доработки или нет.
Вы делаете большое дело, и мы вместе (таких желающих, как я немало) могли бы вложить собственное время ради общего блага с целью доработки DoEasy. Готов помочь в реализации предложенного.
Здравствуйте. Спасибо за отзыв.
В данный момент рано говорить и думать о репозитории - библиотека находится в активной разработке, и пока я не создам и не опубликую всё задуманное, я не хочу отступать от намеченного плана. А многочисленные её исправления и доработки сторонними пользователями, лишь будут отвлекать автора от следования намеченным планам.
А вот про найденные ошибки и способы их устранения лучше сообщать прямо в обсуждениях тех статей, где были найдены ошибки - это как раз поможет развитию библиотеки и устранению найденных ошибок.
При компилировании файла TradingControl.mqh выпадают две ошибки:
'CTrading::OpenPosition<double,d…' - cannot access private member function TradingControl.mqh 328 21
see declaration of 'CTrading::OpenPosition<double,double>' Trading.mqh 146 24
'CTrading::PlaceOrder<double,dou…' - cannot access private member function TradingControl.mqh 344 18
see declaration of 'CTrading::PlaceOrder<double,double,double,double>' Trading.mqh 156 26
Эти методы находятся в приватной секции класса CTrading. Ошибки уходят, если их перенести в публичную секцию этого класса. Но в прикреплённых файлах (а как я понимаю, они рабочие) эти методы тоже в приватной секции класса CTrading, и тоже при компилировании файла TradingControl.mqh выпадают эти две ошибки.
Артём, а как же тогда у вас работает? Либо здесь ошибка, либо я чего-то недопонимаю.
P.S. Я скачал прикреплённые файлы к следующей части - 31, и там тоже эти методы находятся в приватной секции класса CTrading и тоже выпадают эти две ошибки при компилировании.
При компилировании файла TradingControl.mqh выпадают две ошибки:
'CTrading::OpenPosition<double,d…' - cannot access private member function TradingControl.mqh 328 21
see declaration of 'CTrading::OpenPosition<double,double>' Trading.mqh 146 24
'CTrading::PlaceOrder<double,dou…' - cannot access private member function TradingControl.mqh 344 18
see declaration of 'CTrading::PlaceOrder<double,double,double,double>' Trading.mqh 156 26
Эти методы находятся в приватной секции класса CTrading. Ошибки уходят, если их перенести в публичную секцию этого класса. Но в прикреплённых файлах (а как я понимаю, они рабочие) эти методы тоже в приватной секции класса CTrading, и тоже при компилировании файла TradingControl.mqh выпадают эти две ошибки.
Артём, а как же тогда у вас работает? Либо здесь ошибка, либо я чего-то недопонимаю.
P.S. Я скачал прикреплённые файлы к следующей части - 31, и там тоже эти методы находятся в приватной секции класса CTrading и тоже выпадают эти две ошибки при компилировании.
Это стало так после недавнего обновления. Попробуйте их в защищённой (protected) секции сделать - чтобы не было к ним доступа извне. Публичные - слишком нехорошо
Это стало так после недавнего обновления. Попробуйте их в защищённой (protected) секции сделать - чтобы не было к ним доступа извне. Публичные - слишком нехорошо
Хорошо, так и сделаю.
И ещё есть вопрос: есть методы в public-секции ClosePosition, PlaceBuyStop, PlaceBuyLimit, и т.д. Для них не критично, что они находятся в public-секции?
Хорошо, так и сделаю.
И ещё есть вопрос: есть методы в public-секции ClosePosition, PlaceBuyStop, PlaceBuyLimit, и т.д. Для них не критично, что они находятся в public-секции?
Она там и должны быть - это одни из методов для работы с ордерами и позициями