
Работа с таймсериями в библиотеке DoEasy (Часть 35): Объект "Бар" и список-таймсерия символа
Содержание
С этой статьи мы начинаем новый раздел в описании создания библиотеки для удобного написания программ для терминалов MetaTrader5 и 4. Первая серия статей (34 части) была посвящена разработке концепции объектов библиотеки и их взаимосвязей, и на основании разработанной концепции был создан функционал для работы со счётом — его текущим состоянием и историей.
На текущий момент библиотека обладает следующим функционалом:
- умеет искать, сортировать и сравнивать данные любого ордера или позиции,
- предоставляет быстрый и удобный доступ к любым свойствам ордеров и позиций,
- отслеживает любые события, происходящие на счёте,
- позволяет получать и сравнивать данные аккаунта и символов,
- реагирует на изменения свойств всех имеющихся объектов в своих базах данных (коллекциях) и отсылает уведомления в программу о зарегистрированных ею событиях.
Также мы можем указать библиотеке на какие события она должна реагировать и отсылать уведомления о произошедших отслеживаемых событиях.
Кроме того, организована работа с торговыми функциями терминалов, плюс разработаны новые типы торговых запросов — отложенные торговые
запросы, которые позволят нам в будущем прямо из своей программы создавать логику её поведения в тех или иных торговых условиях, а также
предоставляют полный набор возможностей для создания новых типов отложенных ордеров.
Всё это, и многое другое было создано и описано в
предыдущей серии описания создания библиотеки.
Во второй серии описания библиотеки будут рассмотрены многие аспекты работы с ценовыми данными, графиками символов, стаканами,
индикаторами и т.д. В общем — новая серия статей будет посвящена разработке функционала библиотеки для быстрого доступа, поиска,
сравнения и сортировки любых массивов ценовых данных и объектов их хранения и источников.
Концепция
Концепция устройства объектов библиотеки, способ их хранения и работы с ними были подробно описаны в первых статьях прошлой серии. Сегодня мы вкратце повторим базовые принципы устройства и хранения объектов библиотеки.
Любой набор однотипных данных можно представить как список объектов, наделённых однотипными свойствами, присущими типу этих данных. Т.е. у каждого объекта из списка объектов одного типа есть одинаковый набор свойств, но разных по своим значениям у каждого из объекта одного и того же сортированного списка.
Каждый такой список, хранящий набор объектов одного типа, можно сортировать по любому из свойств, которыми наделены объекты данного списка.Свойства объекта имеют три основных типа — целочисленные, вещественные и строковые свойства.
Хранение таких объектов в сортированных списках позволяет очень быстро находить любой из объектов, получать от него любые данные и использовать их в своих программах.Почти все объекты библиотеки имеют возможность самостоятельного отслеживания собственных свойств — изменение их значений на определённую
величину, которую можно задать каждому интересуемому нас объекту. При изменении свойства объекта на заданную величину, отслеживаемый
объект самостоятельно отправляет сообщение в управляющую программу о достижении своего порогового значения, которое установлено для
отслеживания.
Основой всех объектов библиотеки является базовый объект стандартной библиотеки, поставляемой вместе с торговой платформой, а списком объектов (коллекцией объектов) является динамический массив указателей на экземпляры класса CObject и его наследников Стандартной библиотеки.
Сегодня мы создадим объект "Бар", который будет хранить всю информацию, присущую одному бару на графике символа, и список всех объектов-баров для одного символа и таймфрейма.
Каждый бар графика символа имеет определённый набор параметров, описанный в структуре MqlRates:
- время начала периода
- цена открытия
- наивысшая цена за период
- наименьшая цена за период
- цена закрытия
- тиковый объем
- спред
- биржевой объем
Помимо этих основных свойств объекта-бара мы можем задать дополнительные его свойства прямо в момент создания объекта:
- Год, к которому относится бар.
- Месяц, к которому относится бар.
- День недели бара.
- Порядковый номер бара в году.
- День месяца (число).
- Час бара.
- Минута бара.
- Индекс бара в таймсерии символа.
- Размер бара (от High до Low).
- Верх тела свечи (Max(Open,Close)).
- Низ тела свечи (Min(Open,Close)).
- Размер тела свечи (от верха до низа тела свечи).
- Размер верхней тени свечи (от High до верха тела свечи).
- Размер нижней тени свечи (от низа тела свечи до Low).
Все эти свойства позволят нам искать любые комбинации баров и свечей в пределах диапазона, хранящихся в списке баров. Нужный диапазон хранимых данных можно будет устанавливать для списка объектов-баров. По умолчанию будем хранить в списке тысячу баров (или всю доступную историю баров символа, если она меньше тысячи баров или иного заданного диапазона)
Объект "Бар"
Создадим перечисления всех свойств объекта-бара. Для этого откроем файл Defines.mqh, хранящийся по пути \MQL5\Include\DoEasy\Defines.mqh и впишем в конец файла перечисления типа бара, целочисленных, вещественных и строковых свойств объекта-бара и методы сортировки списка объектов-баров:
//+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Данные для работы с серийными данными | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Тип бара (тело свечи) | //+------------------------------------------------------------------+ enum ENUM_BAR_BODY_TYPE { BAR_BODY_TYPE_BULLISH, // Бычий бар BAR_BODY_TYPE_BEARISH, // Медвежий бар BAR_BODY_TYPE_NULL, // Нулевой бар BAR_BODY_TYPE_CANDLE_ZERO_BODY, // Свеча с нулевым телом }; //+------------------------------------------------------------------+ //| Целочисленные свойства бара | //+------------------------------------------------------------------+ enum ENUM_BAR_PROP_INTEGER { BAR_PROP_INDEX = 0, // Индекс бара в таймсерии BAR_PROP_TYPE, // Тип бара (из перечисления ENUM_BAR_BODY_TYPE) BAR_PROP_PERIOD, // Период бара (таймфрейм) BAR_PROP_SPREAD, // Спред бара BAR_PROP_VOLUME_TICK, // Тиковый объём бара BAR_PROP_VOLUME_REAL, // Биржевой объём бара BAR_PROP_TIME, // Время начала периода бара BAR_PROP_TIME_DAY_OF_YEAR, // Порядковый номер дня бара в году BAR_PROP_TIME_YEAR, // Год, к которому относится бар BAR_PROP_TIME_MONTH, // Месяц, к которому относится бар BAR_PROP_TIME_DAY_OF_WEEK, // День недели бара BAR_PROP_TIME_DAY, // День месяца бара (число) BAR_PROP_TIME_HOUR, // Час бара BAR_PROP_TIME_MINUTE, // Минута бара }; #define BAR_PROP_INTEGER_TOTAL (14) // Общее количество целочисленных свойств бара #define BAR_PROP_INTEGER_SKIP (0) // Количество неиспользуемых в сортировке свойств бара //+------------------------------------------------------------------+ //| Вещественные свойства бара | //+------------------------------------------------------------------+ enum ENUM_BAR_PROP_DOUBLE { //--- данные бара BAR_PROP_OPEN = BAR_PROP_INTEGER_TOTAL, // Цена открытия бара BAR_PROP_HIGH, // Наивысшая цена за период бара BAR_PROP_LOW, // Наименьшая цена за период бара BAR_PROP_CLOSE, // Цена закрытия бара //--- данные свечи BAR_PROP_CANDLE_SIZE, // Размер свечи BAR_PROP_CANDLE_SIZE_BODY, // Размер тела свечи BAR_PROP_CANDLE_BODY_TOP, // Верх тела свечи BAR_PROP_CANDLE_BODY_BOTTOM, // Низ тела свечи BAR_PROP_CANDLE_SIZE_SHADOW_UP, // Размер верхней тени свечи BAR_PROP_CANDLE_SIZE_SHADOW_DOWN, // Размер нижней тени свечи }; #define BAR_PROP_DOUBLE_TOTAL (10) // Общее количество вещественных свойств бара #define BAR_PROP_DOUBLE_SKIP (0) // Количество неиспользуемых в сортировке свойств бара //+------------------------------------------------------------------+ //| Строковые свойства бара | //+------------------------------------------------------------------+ enum ENUM_BAR_PROP_STRING { BAR_PROP_SYMBOL = (BAR_PROP_INTEGER_TOTAL+BAR_PROP_DOUBLE_TOTAL), // Символ бара }; #define BAR_PROP_STRING_TOTAL (1) // Общее количество строковых свойств бара //+------------------------------------------------------------------+ //| Возможные критерии сортировки баров | //+------------------------------------------------------------------+ #define FIRST_BAR_DBL_PROP (BAR_PROP_INTEGER_TOTAL-BAR_PROP_INTEGER_SKIP) #define FIRST_BAR_STR_PROP (BAR_PROP_INTEGER_TOTAL-BAR_PROP_INTEGER_SKIP+BAR_PROP_DOUBLE_TOTAL-BAR_PROP_DOUBLE_SKIP) enum ENUM_SORT_BAR_MODE { //--- Сортировка по целочисленным свойствам SORT_BY_BAR_INDEX = 0, // Сортировать по индексу бара SORT_BY_BAR_TYPE, // Сортировать по типу бара (из перечисления ENUM_BAR_BODY_TYPE) SORT_BY_BAR_PERIOD, // Сортировать по периоду бара (таймфрейму) SORT_BY_BAR_SPREAD, // Сортировать по спреду бара SORT_BY_BAR_VOLUME_TICK, // Сортировать по тиковому объёму бара SORT_BY_BAR_VOLUME_REAL, // Сортировать по биржевому объёму бара SORT_BY_BAR_TIME, // Сортировать по времени начала периода бара SORT_BY_BAR_TIME_DAY_OF_YEAR, // Сортировать по порядковому номеру дня бара в году SORT_BY_BAR_TIME_YEAR, // Сортировать по году, к которому относится бар SORT_BY_BAR_TIME_MONTH, // Сортировать по месяцу, к которому относится бар SORT_BY_BAR_TIME_DAY_OF_WEEK, // Сортировать по дню недели бара SORT_BY_BAR_TIME_DAY, // Сортировать по дню бара SORT_BY_BAR_TIME_HOUR, // Сортировать по часу бара SORT_BY_BAR_TIME_MINUTE, // Сортировать по минуте бара //--- Сортировка по вещественным свойствам SORT_BY_BAR_OPEN = FIRST_BAR_DBL_PROP, // Сортировать по цене открытия бара SORT_BY_BAR_HIGH, // Сортировать по наивысшей цене за период бара SORT_BY_BAR_LOW, // Сортировать по наименьшей цене за период бара SORT_BY_BAR_CLOSE, // Сортировать по цене закрытия бара SORT_BY_BAR_CANDLE_SIZE, // Сортировать по размеру свечи SORT_BY_BAR_CANDLE_SIZE_BODY, // Сортировать по размеру тела свечи SORT_BY_BAR_CANDLE_BODY_TOP, // Сортировать по верху тела свечи SORT_BY_BAR_CANDLE_BODY_BOTTOM, // Сортировать по низу тела свечи SORT_BY_BAR_CANDLE_SIZE_SHADOW_UP, // Сортировать по размеру верхней тени свечи SORT_BY_BAR_CANDLE_SIZE_SHADOW_DOWN, // Сортировать по размеру нижней тени свечи //--- Сортировка по строковым свойствам SORT_BY_BAR_SYMBOL = FIRST_BAR_STR_PROP, // Сортировать по символу бара }; //+------------------------------------------------------------------+
Тип бара может быть бычьим, медвежьим и нулевым. Отдельным свойством типа бара мы задали тип тела свечи — ведь для определения различных свечных формаций используется в частности и размер тела свечи, поэтому нулевой размер тела свечи не должен приравниваться к нулевому бару — нулевой бар имеет всего лишь одну единственную цену для всех его четырёх цен OHLC, тогда как свеча с нулевым телом может иметь тени, и расположение и величина теней свечи будет иметь значение для определения свечных формаций.
Для вывода описаний свойств баров и некоторых иных сообщений библиотеки нам потребуются новые текстовые сообщения.
Допишем в
файл Datas.mqh, хранящийся по пути \MQL5\Include\DoEasy\Datas.mqh, индексы
новых сообщений:
MSG_LIB_SYS_ERROR_CODE_OUT_OF_RANGE, // Код возврата вне заданного диапазона кодов ошибок MSG_LIB_SYS_FAILED_CREATE_PAUSE_OBJ, // Не удалось создать объект \"Пауза\" MSG_LIB_SYS_FAILED_CREATE_BAR_OBJ, // Не удалось создать объект \"Бар\" MSG_LIB_SYS_FAILED_SYNC_DATA, // Не удалось синхронизировать данные с сервером
...
MSG_LIB_TEXT_TIME_UNTIL_THE_END_DAY, // Будет использоваться время действия ордера до конца текущего дня MSG_LIB_TEXT_JANUARY, // Январь MSG_LIB_TEXT_FEBRUARY, // Февраль MSG_LIB_TEXT_MARCH, // Март MSG_LIB_TEXT_APRIL, // Апрель MSG_LIB_TEXT_MAY, // Май MSG_LIB_TEXT_JUNE, // Июнь MSG_LIB_TEXT_JULY, // Июль MSG_LIB_TEXT_AUGUST, // Август MSG_LIB_TEXT_SEPTEMBER, // Сентябрь MSG_LIB_TEXT_OCTOBER, // Октябрь MSG_LIB_TEXT_NOVEMBER, // Ноябрь MSG_LIB_TEXT_DECEMBER, // Декабрь MSG_LIB_TEXT_SUNDAY, // Воскресение
...
MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS, // Добавлены условия активации отложенного запроса //--- CBar MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA, // Не удалось получить данные бара MSG_LIB_TEXT_BAR_FAILED_GET_SERIES_DATA, // Не удалось получить данные таймсерии MSG_LIB_TEXT_BAR_FAILED_ADD_TO_LIST, // Не удалось добавить объект-бар в список MSG_LIB_TEXT_BAR, // Бар MSG_LIB_TEXT_BAR_PERIOD, // Таймфрейм MSG_LIB_TEXT_BAR_SPREAD, // Спред MSG_LIB_TEXT_BAR_VOLUME_TICK, // Тиковый объём MSG_LIB_TEXT_BAR_VOLUME_REAL, // Биржевой объём MSG_LIB_TEXT_BAR_TIME, // Время начала периода MSG_LIB_TEXT_BAR_TIME_YEAR, // Год MSG_LIB_TEXT_BAR_TIME_MONTH, // Месяц MSG_LIB_TEXT_BAR_TIME_DAY_OF_YEAR, // Порядковый номер дня в году MSG_LIB_TEXT_BAR_TIME_DAY_OF_WEEK, // День недели MSG_LIB_TEXT_BAR_TIME_DAY, // День месяца MSG_LIB_TEXT_BAR_TIME_HOUR, // Час MSG_LIB_TEXT_BAR_TIME_MINUTE, // Минута MSG_LIB_TEXT_BAR_INDEX, // Индекс в таймсерии MSG_LIB_TEXT_BAR_HIGH, // Наивысшая цена за период MSG_LIB_TEXT_BAR_LOW, // Наименьшая цена за период MSG_LIB_TEXT_BAR_CANDLE_SIZE, // Размер свечи MSG_LIB_TEXT_BAR_CANDLE_SIZE_BODY, // Размер тела свечи MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_UP, // Размер верхней тени свечи MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_DOWN, // Размер нижней тени свечи MSG_LIB_TEXT_BAR_CANDLE_BODY_TOP, // Верх тела свечи MSG_LIB_TEXT_BAR_CANDLE_BODY_BOTTOM, // Низ тела свечи MSG_LIB_TEXT_BAR_TYPE_BULLISH, // Бычий бар MSG_LIB_TEXT_BAR_TYPE_BEARISH, // Медвежий бар MSG_LIB_TEXT_BAR_TYPE_NULL, // Нулевой бар MSG_LIB_TEXT_BAR_TYPE_CANDLE_ZERO_BODY, // Свеча с нулевым телом MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA, // Сначала нужно установить требуемое количество данных при помощи SetAmountUsedData() }; //+------------------------------------------------------------------+
и текстовые сообщения, соответствующие вновь добавленным индексам:
{"Код возврата вне заданного диапазона кодов ошибок","Return code out of range of error codes"}, {"Не удалось создать объект \"Пауза\"","Failed to create object \"Pause\""}, {"Не удалось создать объект \"Бар\"","Failed to create object \"Bar\""}, {"Не удалось синхронизировать данные с сервером","Failed to sync data with server"},
...
{"Будет использоваться время действия ордера до конца текущего дня","The order validity time until the end of the current day will be used"}, {"Январь","January"}, {"Февраль","February"}, {"Март","March"}, {"Апрель","April"}, {"Май","May"}, {"Июнь","June"}, {"Июль","July"}, {"Август","August"}, {"Сентябрь","September"}, {"Октябрь","October"}, {"Ноябрь","November"}, {"Декабрь","December"}, {"Воскресение","Sunday"},
...
{"Добавлены условия активации отложенного запроса","Pending request activation conditions added"}, {"Не удалось получить данные бара","Failed to get bar data"}, {"Не удалось получить данные таймсерии","Failed to get timeseries data"}, {"Не удалось добавить объект-бар в список","Failed to add the bar object to the list"}, {"Бар","Bar"}, {"Таймфрейм","Timeframe"}, {"Спред","Spread"}, {"Тиковый объём","Tick volume"}, {"Биржевой объём","Real volume"}, {"Время начала периода","Period start time"}, {"Год","Year"}, {"Месяц","Month"}, {"Порядковый номер дня в году","Sequence day number in the year"}, {"День недели","Day of week"}, {"День месяца","Day od month"}, {"Час","Hour"}, {"Минута","Minute"}, {"Индекс в таймсерии","Timeseries index"}, {"Наивысшая цена за период","Highest price for the period"}, {"Наименьшая цена за период","Lowest price for the period"}, {"Размер свечи","Candle size"}, {"Размер тела свечи","Candle body size"}, {"Размер верхней тени свечи","Candle upper shadow size"}, {"Размер нижней тени свечи","Candle lower shadow size"}, {"Верх тела свечи","Top of the candle body"}, {"Низ тела свечи","Bottom of the candle body"}, {"Бычий бар","Bullish bar"}, {"Медвежий бар","Bearish bar"}, {"Нулевой бар","Zero-bar"}, {"Свеча с нулевым телом","Candle with zero body"}, {"Сначала нужно установить требуемое количество данных при помощи SetAmountUsedData()","First you need to set the required amount of data using SetAmountUsedData()"}, }; //+---------------------------------------------------------------------+
В файле сервисных функций DELib.mqh, расположенном по адресу \MQL5\Include\DoEasy\Services\DELib.mqh впишем функцию, возвращающую наименование месяца, и функцию, возвращающую описание таймфрейма:
//+------------------------------------------------------------------+ //| Возвращает наименования месяца | //+------------------------------------------------------------------+ string MonthDescription(const int month) { return ( month==1 ? CMessage::Text(MSG_LIB_TEXT_JANUARY) : month==2 ? CMessage::Text(MSG_LIB_TEXT_FEBRUARY) : month==3 ? CMessage::Text(MSG_LIB_TEXT_MARCH) : month==4 ? CMessage::Text(MSG_LIB_TEXT_APRIL) : month==5 ? CMessage::Text(MSG_LIB_TEXT_MAY) : month==6 ? CMessage::Text(MSG_LIB_TEXT_JUNE) : month==7 ? CMessage::Text(MSG_LIB_TEXT_JULY) : month==8 ? CMessage::Text(MSG_LIB_TEXT_AUGUST) : month==9 ? CMessage::Text(MSG_LIB_TEXT_SEPTEMBER) : month==10 ? CMessage::Text(MSG_LIB_TEXT_OCTOBER) : month==11 ? CMessage::Text(MSG_LIB_TEXT_NOVEMBER) : month==12 ? CMessage::Text(MSG_LIB_TEXT_DECEMBER) : (string)month ); } //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| Возвращает описание таймфрейма | //+------------------------------------------------------------------+ string TimeframeDescription(const ENUM_TIMEFRAMES timeframe) { return StringSubstr(EnumToString(timeframe),7); } //+------------------------------------------------------------------+
В функцию, возвращающую наименования месяца, передаётся номер месяца, и в соответствии с номером возвращается его текстовое описание.
В функцию, возвращающую наименование таймфрейма, передаётся таймфрейм, далее из текстового
представления значения перечисления таймфрейма извлекается подстрока,
начинающаяся с позиции 7 и до конца строки. Полученный результат возвращается в виде текста. Таким образом, например, из текстового
представления часового таймфрейма PERIOD_H1 извлекается значение H1.
Для хранения классов объектов-баров создадим новую папку в каталоге объектов библиотеки \MQL5\Include\DoEasy\Objects\Series\, а в ней новый файл Bar.mqh класса CBar.
Рассмотрим листинг тела класса, а далее реализацию его методов:
//+------------------------------------------------------------------+ //| Bar.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" #property strict // Нужно для mql4 //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "..\..\Services\DELib.mqh" //+------------------------------------------------------------------+ //| Класс "Бар" | //+------------------------------------------------------------------+ class CBar : public CObject { private: MqlDateTime m_dt_struct; // Структура даты int m_digits; // Значение Digits символа string m_period_description; // Строковое описание таймфрейма long m_long_prop[BAR_PROP_INTEGER_TOTAL]; // Целочисленные свойства double m_double_prop[BAR_PROP_DOUBLE_TOTAL]; // Вещественные свойства string m_string_prop[BAR_PROP_STRING_TOTAL]; // Строковые свойства //--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство бара int IndexProp(ENUM_BAR_PROP_DOUBLE property) const { return(int)property-BAR_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_BAR_PROP_STRING property) const { return(int)property-BAR_PROP_INTEGER_TOTAL-BAR_PROP_DOUBLE_TOTAL; } //--- Возвращает тип бара (бычий/медвежий/нулевой) ENUM_BAR_BODY_TYPE BodyType(void) const; //--- Рассчитывает и возвращает размер (1) свечи, (2) тела свечи, //--- (3) верхней, (4) нижней тени свечи, //--- (5) верх, (6) низ тела свечи double CandleSize(void) const { return(this.High()-this.Low()); } double BodySize(void) const { return(this.BodyHigh()-this.BodyLow()); } double ShadowUpSize(void) const { return(this.High()-this.BodyHigh()); } double ShadowDownSize(void) const { return(this.BodyLow()-this.Low()); } double BodyHigh(void) const { return ::fmax(this.Close(),this.Open()); } double BodyLow(void) const { return ::fmin(this.Close(),this.Open()); } //--- Возвращает (1) год, (2) месяц, к которому относится бар, (3) день недели, //--- (4) порядковый номер в году, (5) день, (6) час, (7) минуту бара, int TimeYear(void) const { return this.m_dt_struct.year; } int TimeMonth(void) const { return this.m_dt_struct.mon; } int TimeDayOfWeek(void) const { return this.m_dt_struct.day_of_week; } int TimeDayOfYear(void) const { return this.m_dt_struct.day_of_year; } int TimeDay(void) const { return this.m_dt_struct.day; } int TimeHour(void) const { return this.m_dt_struct.hour; } int TimeMinute(void) const { return this.m_dt_struct.min; } public: //--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство бара void SetProperty(ENUM_BAR_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_BAR_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_BAR_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value; } //--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство бара long GetProperty(ENUM_BAR_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_BAR_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_BAR_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Возвращает флаг поддержания баром данного свойства virtual bool SupportProperty(ENUM_BAR_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_BAR_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_BAR_PROP_STRING property) { return true; } //--- Возвращает себя CBar *GetObject(void) { return &this;} //--- Устанавливает (1) символ, таймфрейм и индекс бара, (2) параметры объекта-бар void SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index); void SetProperties(const MqlRates &rates); //--- Сравнивает объекты CBar между собой по всем возможным свойствам (для сортировки списков по указанному свойству объекта-бара) virtual int Compare(const CObject *node,const int mode=0) const; //--- Сравнивает объекты CBar между собой по всем свойствам (для поиска равных объектов-баров) bool IsEqual(CBar* compared_bar) const; //--- Конструкторы CBar(){;} CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index); CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const MqlRates &rates); //+------------------------------------------------------------------+ //| Методы упрощённого доступа к свойствам объекта-ордера | //+------------------------------------------------------------------+ //--- Возвращает (1) тип, (2) период, (3) спред, (4) тиковый, (5) биржевой объём, //--- (6) время начала периода бара, (7) год, (8) месяц, к которому относится бар //--- (9) номер недели от начала года, (10) номер недели от начала месяца //--- (11) день, (12) час, (13) минута, (14) индекс бара ENUM_BAR_BODY_TYPE TypeBody(void) const { return (ENUM_BAR_BODY_TYPE)this.GetProperty(BAR_PROP_TYPE); } ENUM_TIMEFRAMES Period(void) const { return (ENUM_TIMEFRAMES)this.GetProperty(BAR_PROP_PERIOD); } int Spread(void) const { return (int)this.GetProperty(BAR_PROP_SPREAD); } long VolumeTick(void) const { return this.GetProperty(BAR_PROP_VOLUME_TICK); } long VolumeReal(void) const { return this.GetProperty(BAR_PROP_VOLUME_REAL); } datetime Time(void) const { return (datetime)this.GetProperty(BAR_PROP_TIME); } long Year(void) const { return this.GetProperty(BAR_PROP_TIME_YEAR); } long Month(void) const { return this.GetProperty(BAR_PROP_TIME_MONTH); } long DayOfWeek(void) const { return this.GetProperty(BAR_PROP_TIME_DAY_OF_WEEK); } long DayOfYear(void) const { return this.GetProperty(BAR_PROP_TIME_DAY_OF_YEAR); } long Day(void) const { return this.GetProperty(BAR_PROP_TIME_DAY); } long Hour(void) const { return this.GetProperty(BAR_PROP_TIME_HOUR); } long Minute(void) const { return this.GetProperty(BAR_PROP_TIME_MINUTE); } long Index(void) const { return this.GetProperty(BAR_PROP_INDEX); } //--- Возвращает цену (1) Open, (2) High, (3) Low, (4) Close бара, //--- размер (5) свечи, (6) тела, (7) верх, (8) низ свечи, //--- размер (9) верхней, (10) нижней тени свечи double Open(void) const { return this.GetProperty(BAR_PROP_OPEN); } double High(void) const { return this.GetProperty(BAR_PROP_HIGH); } double Low(void) const { return this.GetProperty(BAR_PROP_LOW); } double Close(void) const { return this.GetProperty(BAR_PROP_CLOSE); } double Size(void) const { return this.GetProperty(BAR_PROP_CANDLE_SIZE); } double SizeBody(void) const { return this.GetProperty(BAR_PROP_CANDLE_SIZE_BODY); } double TopBody(void) const { return this.GetProperty(BAR_PROP_CANDLE_BODY_TOP); } double BottomBody(void) const { return this.GetProperty(BAR_PROP_CANDLE_BODY_BOTTOM); } double SizeShadowUp(void) const { return this.GetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_UP); } double SizeShadowDown(void) const { return this.GetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_DOWN); } //--- Возвращает символ бара string Symbol(void) const { return this.GetProperty(BAR_PROP_SYMBOL); } //+------------------------------------------------------------------+ //| Описания свойств объекта-бара | //+------------------------------------------------------------------+ //--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства бара string GetPropertyDescription(ENUM_BAR_PROP_INTEGER property); string GetPropertyDescription(ENUM_BAR_PROP_DOUBLE property); string GetPropertyDescription(ENUM_BAR_PROP_STRING property); //--- Возвращает описание типа бара string BodyTypeDescription(void) const; //--- Выводит в журнал описание свойств бара (full_prop=true - все свойства, false - только поддерживаемые) void Print(const bool full_prop=false); //--- Выводит в журнал краткое описание бара virtual void PrintShort(void); //--- Возвращает краткое наименование объекта-бара virtual string Header(void); //--- }; //+------------------------------------------------------------------+
В качестве повторения ранее пройденного материала вкратце рассмотрим состав класса.
В приватной секции класса расположены:
Три массива, в которых хранятся соответствующие свойства
объекта-бара — целочисленные, вещественные и строковые.
Методы,
рассчитывающие истинный индекс свойства объекта в соответствующем массиве.
Методы,
рассчитывающие и возвращающие дополнительные свойства объекта-бара.
В публичной секции класса расположены:
Методы, записывающие в массивы целочисленных, вещественных и строковых
свойств переданное значение свойства объекта.
Методы,
возвращающие из массивов значение запрашиваемого целочисленного, вещественного или строкового свойства.
Виртуальные
методы, возвращающие для каждого из свойств флаг поддержания объектом данного свойства. Методы предназначены для
реализации в объектак-потомках объекта-бара, и должны возвращать false в случае,
если объект-наследник не поддерживает указанное свойство. В объекте "Бар" все свойства поддерживаются, и методы возвращают true.
Подробно всё устройство объектов библиотеки мы обсуждали в первой статье, а здесь вкратце рассмотрим реализацию остальных методов класса.
Класс имеет три конструктора:
1. Конструктор по умолчанию без параметров служит для простого объявления объекта класса с последующей установкой необходимых параметров созданному объекту.
2. Первый параметрический конструктор получает три параметра — символ, таймфрейм и индекс бара, и на их основе получает из таймсерии все свойства одного объекта-бара посредством первой формы функции CopyRates():
//+------------------------------------------------------------------+ //| Конструктор 1 | //+------------------------------------------------------------------+ CBar::CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index) { MqlRates rates_array[1]; this.SetSymbolPeriod(symbol,timeframe,index); ::ResetLastError(); //--- Если не получилось записать в данные бара в массив по индексу, или произошла ошибка установки времени в структуру времени //--- выводим сообщение об ошибке, создаём и заполняем структуру нулями и записываем её в массив rates_array if(::CopyRates(symbol,timeframe,index,1,rates_array)<1 || !::TimeToStruct(rates_array[0].time,this.m_dt_struct)) { int err_code=::GetLastError(); ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA),". ",CMessage::Text(MSG_LIB_SYS_ERROR)," ",CMessage::Text(err_code)," ",CMessage::Retcode(err_code)); MqlRates err={0}; rates_array[0]=err; } //--- Устанавливаем свойства бара this.SetProperties(rates_array[0]); } //+------------------------------------------------------------------+
Этот конструктор служит для одноразового получения данных из таймсерии сразу при создании объекта-бара.
3. Второй параметрический конструктор служит для создания объекта-бара из уже готового массива структур MqlRates.
Т.е. подразумевается проход в цикле по массиву структур MqlRates и созданию объекта по индексу этого массива:
//+------------------------------------------------------------------+ //| Конструктор 2 | //+------------------------------------------------------------------+ CBar::CBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index,const MqlRates &rates) { this.SetSymbolPeriod(symbol,timeframe,index); ::ResetLastError(); //--- Если произошла ошибка установки времени в структуру времени, выводим сообщение об ошибке, //--- создаём и заполняем структуру нулями, устанавливаем свойства бара из этой структуры и выходим if(!::TimeToStruct(rates.time,this.m_dt_struct)) { int err_code=::GetLastError(); ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_BAR_DATA),". ",CMessage::Text(MSG_LIB_SYS_ERROR)," ",CMessage::Text(err_code)," ",CMessage::Retcode(err_code)); MqlRates err={0}; this.SetProperties(err); return; } //--- Устанавливаем свойства бара this.SetProperties(rates); } //+------------------------------------------------------------------+
Здесь в конструктор помимо символа, таймфрейма и индекса передаётся ссылка на структуру MqlRates. На основании этих данных и создаётся объект-бар.
Виртуальный метод Compare() предназначен для сравнения двух объектов по указанному свойству. Определён в классе базового объекта стандартной библиотеки CObject, и должен возвращать ноль, если значения равны, и 1/-1 если одно из сравниваемых значений больше/меньше. Для поиска и сортировки используется в методе Search() Стандартной библиотеки, и должен переопределяться в классах-наследниках:
//+------------------------------------------------------------------+ //| Сравнивает объекты CBar между собой по всем возможным свойствам | //+------------------------------------------------------------------+ int CBar::Compare(const CObject *node,const int mode=0) const { const CBar *bar_compared=node; //--- сравнение целочисленных свойств двух баров if(mode<BAR_PROP_INTEGER_TOTAL) { long value_compared=bar_compared.GetProperty((ENUM_BAR_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_BAR_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- сравнение вещественных свойств двух баров else if(mode<BAR_PROP_DOUBLE_TOTAL+BAR_PROP_INTEGER_TOTAL) { double value_compared=bar_compared.GetProperty((ENUM_BAR_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_BAR_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- сравнение строковых свойств двух баров else if(mode<BAR_PROP_DOUBLE_TOTAL+BAR_PROP_INTEGER_TOTAL+BAR_PROP_STRING_TOTAL) { string value_compared=bar_compared.GetProperty((ENUM_BAR_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_BAR_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+
Метод для определения двух одинаковых объектов-баров служит для сравнения двух объектов-баров, и возвращает true только в случае, если все поля двух сравниваемых объектов равны:
//+------------------------------------------------------------------+ //| Сравнивает объекты CBar между собой по всем свойствам | //+------------------------------------------------------------------+ bool CBar::IsEqual(CBar *compared_bar) const { int beg=0, end=BAR_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_BAR_PROP_INTEGER prop=(ENUM_BAR_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_bar.GetProperty(prop)) return false; } beg=end; end+=BAR_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_BAR_PROP_DOUBLE prop=(ENUM_BAR_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_bar.GetProperty(prop)) return false; } beg=end; end+=BAR_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_BAR_PROP_STRING prop=(ENUM_BAR_PROP_STRING)i; if(this.GetProperty(prop)!=compared_bar.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
Метод для установки символа, таймфрейма и индекса объекта-бара в таймсерии:
//+------------------------------------------------------------------+ //| Устанавливает символ, таймфрейм и индекс бара | //+------------------------------------------------------------------+ void CBar::SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const int index) { this.SetProperty(BAR_PROP_INDEX,index); this.SetProperty(BAR_PROP_SYMBOL,symbol); this.SetProperty(BAR_PROP_PERIOD,timeframe); this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS); this.m_period_description=TimeframeDescription(timeframe); } //+------------------------------------------------------------------+
Помимо установки трёх перечисленных свойств, метод устанавливает количество знаков после запятой в значении цены символа в переменную m_digits и текстовое описание таймфрейма в переменную m_period_description — достаточно их указать один раз при создании объекта-бара.
Метод для установки всех параметров объекта-бар просто записывает в свойства объекта значения из переданной в метод структуры MqlRates, а также рассчитывает параметры дополнительных свойств объекта при помощи соответствующих методов:
//+------------------------------------------------------------------+ //| Устанавливает параметры объекта-бар | //+------------------------------------------------------------------+ void CBar::SetProperties(const MqlRates &rates) { this.SetProperty(BAR_PROP_SPREAD,rates.spread); this.SetProperty(BAR_PROP_VOLUME_TICK,rates.tick_volume); this.SetProperty(BAR_PROP_VOLUME_REAL,rates.real_volume); this.SetProperty(BAR_PROP_TIME,rates.time); this.SetProperty(BAR_PROP_TIME_YEAR,this.TimeYear()); this.SetProperty(BAR_PROP_TIME_MONTH,this.TimeMonth()); this.SetProperty(BAR_PROP_TIME_DAY_OF_YEAR,this.TimeDayOfYear()); this.SetProperty(BAR_PROP_TIME_DAY_OF_WEEK,this.TimeDayOfWeek()); this.SetProperty(BAR_PROP_TIME_DAY,this.TimeDay()); this.SetProperty(BAR_PROP_TIME_HOUR,this.TimeHour()); this.SetProperty(BAR_PROP_TIME_MINUTE,this.TimeMinute()); //--- this.SetProperty(BAR_PROP_OPEN,rates.open); this.SetProperty(BAR_PROP_HIGH,rates.high); this.SetProperty(BAR_PROP_LOW,rates.low); this.SetProperty(BAR_PROP_CLOSE,rates.close); this.SetProperty(BAR_PROP_CANDLE_SIZE,this.CandleSize()); this.SetProperty(BAR_PROP_CANDLE_SIZE_BODY,this.BodySize()); this.SetProperty(BAR_PROP_CANDLE_BODY_TOP,this.BodyHigh()); this.SetProperty(BAR_PROP_CANDLE_BODY_BOTTOM,this.BodyLow()); this.SetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_UP,this.ShadowUpSize()); this.SetProperty(BAR_PROP_CANDLE_SIZE_SHADOW_DOWN,this.ShadowDownSize()); //--- this.SetProperty(BAR_PROP_TYPE,this.BodyType()); } //+------------------------------------------------------------------+
Метод, выводящий в журнал описания всех свойств объекта-бара:
//+------------------------------------------------------------------+ //| Выводит в журнал свойства бара | //+------------------------------------------------------------------+ void CBar::Print(const bool full_prop=false) { ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") ============="); int beg=0, end=BAR_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_BAR_PROP_INTEGER prop=(ENUM_BAR_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=BAR_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_BAR_PROP_DOUBLE prop=(ENUM_BAR_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=BAR_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_BAR_PROP_STRING prop=(ENUM_BAR_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n"); } //+------------------------------------------------------------------+
В трёх циклах по массивам свойств объекта выводятся описания каждого очередного свойства. Если свойство не поддерживается, то оно
не выводится в журнал в случае, если входной параметр метода full_prop имеет значение false (по
умолчанию).
Метод, выводящий в журнал краткое описание объекта-бара:
//+------------------------------------------------------------------+ //| Выводит в журнал краткое описание бара | //+------------------------------------------------------------------+ void CBar::PrintShort(void) { int dg=(this.m_digits>0 ? this.m_digits : 1); string params= ( ::TimeToString(this.Time(),TIME_DATE|TIME_MINUTES|TIME_SECONDS)+", "+ "O: "+::DoubleToString(this.Open(),dg)+", "+ "H: "+::DoubleToString(this.High(),dg)+", "+ "L: "+::DoubleToString(this.Low(),dg)+", "+ "C: "+::DoubleToString(this.Close(),dg)+", "+ "V: "+(string)this.VolumeTick()+", "+ (this.VolumeReal()>0 ? "R: "+(string)this.VolumeReal()+", " : "")+ this.BodyTypeDescription() ); ::Print(this.Header(),": ",params); } //+------------------------------------------------------------------+
Метод выводит описание бара в формате
Bar "SYMBOL" H4[INDEX]: YYYY.MM.DD HH:MM:SS, O: X.XXXXX, H: X.XXXXX, L: X.XXXXX, C: X.XXXXX, V: XXXX, BAR_TYPE
Например:
Бар "EURUSD" H4[6]: 2020.02.06 20:00:00, O: 1.09749, H: 1.09828, L: 1.09706, C: 1.09827, V: 3323, Бычий бар
Метод, выводящий в журнал краткое наименование бара:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование объекта-бара | //+------------------------------------------------------------------+ string CBar::Header(void) { return ( CMessage::Text(MSG_LIB_TEXT_BAR)+" \""+this.GetProperty(BAR_PROP_SYMBOL)+"\" "+ TimeframeDescription((ENUM_TIMEFRAMES)this.GetProperty(BAR_PROP_PERIOD))+"["+(string)this.GetProperty(BAR_PROP_INDEX)+"]" ); } //+------------------------------------------------------------------+
Выводит наименование бара в формате
Bar "SYMBOL" H4[INDEX]
Например:
Бар "EURUSD" H4[6]
Метод, возвращающий описание целочисленного свойства объекта-бара:
//+------------------------------------------------------------------+ //| Возвращает описание целочисленного свойства бара | //+------------------------------------------------------------------+ string CBar::GetPropertyDescription(ENUM_BAR_PROP_INTEGER property) { return ( property==BAR_PROP_INDEX ? CMessage::Text(MSG_LIB_TEXT_BAR_INDEX)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BAR_PROP_TYPE ? CMessage::Text(MSG_ORD_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.BodyTypeDescription() ) : property==BAR_PROP_PERIOD ? CMessage::Text(MSG_LIB_TEXT_BAR_PERIOD)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.m_period_description ) : property==BAR_PROP_SPREAD ? CMessage::Text(MSG_LIB_TEXT_BAR_SPREAD)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BAR_PROP_VOLUME_TICK ? CMessage::Text(MSG_LIB_TEXT_BAR_VOLUME_TICK)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BAR_PROP_VOLUME_REAL ? CMessage::Text(MSG_LIB_TEXT_BAR_VOLUME_REAL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BAR_PROP_TIME ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : property==BAR_PROP_TIME_YEAR ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_YEAR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.Year() ) : property==BAR_PROP_TIME_MONTH ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_MONTH)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+MonthDescription((int)this.Month()) ) : property==BAR_PROP_TIME_DAY_OF_YEAR ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_DAY_OF_YEAR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)::IntegerToString(this.DayOfYear(),3,'0') ) : property==BAR_PROP_TIME_DAY_OF_WEEK ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_DAY_OF_WEEK)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+DayOfWeekDescription((ENUM_DAY_OF_WEEK)this.DayOfWeek()) ) : property==BAR_PROP_TIME_DAY ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_DAY)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)::IntegerToString(this.Day(),2,'0') ) : property==BAR_PROP_TIME_HOUR ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_HOUR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)::IntegerToString(this.Hour(),2,'0') ) : property==BAR_PROP_TIME_MINUTE ? CMessage::Text(MSG_LIB_TEXT_BAR_TIME_MINUTE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)::IntegerToString(this.Minute(),2,'0') ) : "" ); } //+------------------------------------------------------------------+
В метод передаётся целочисленное свойство, и в зависимости от его значения возвращается его текстовое описание, заданное в файле Datas.mqh.
Методы, возвращающие описания вещественного и строкового свойств объекта-бара устроены аналогично методу,
возвращающему описание целочисленного свойства объекта-бара:
//+------------------------------------------------------------------+ //| Возвращает описание вещественного свойства бара | //+------------------------------------------------------------------+ string CBar::GetPropertyDescription(ENUM_BAR_PROP_DOUBLE property) { int dg=(this.m_digits>0 ? this.m_digits : 1); return ( property==BAR_PROP_OPEN ? CMessage::Text(MSG_ORD_PRICE_OPEN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_HIGH ? CMessage::Text(MSG_LIB_TEXT_BAR_HIGH)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_LOW ? CMessage::Text(MSG_LIB_TEXT_BAR_LOW)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CLOSE ? CMessage::Text(MSG_ORD_PRICE_CLOSE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CANDLE_SIZE ? CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_SIZE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CANDLE_SIZE_BODY ? CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_SIZE_BODY)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CANDLE_SIZE_SHADOW_UP ? CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_UP)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CANDLE_SIZE_SHADOW_DOWN ? CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_SIZE_SHADOW_DOWN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CANDLE_BODY_TOP ? CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_BODY_TOP)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==BAR_PROP_CANDLE_BODY_BOTTOM ? CMessage::Text(MSG_LIB_TEXT_BAR_CANDLE_BODY_BOTTOM)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : "" ); } //+------------------------------------------------------------------+ //| Возвращает описание строкового свойства бара | //+------------------------------------------------------------------+ string CBar::GetPropertyDescription(ENUM_BAR_PROP_STRING property) { return(property==BAR_PROP_SYMBOL ? CMessage::Text(MSG_LIB_PROP_SYMBOL)+": \""+this.GetProperty(property)+"\"" : ""); } //+------------------------------------------------------------------+
Метод, возвращающий тип бара:
//+------------------------------------------------------------------+ //| Возвращает тип бара (бычий/медвежий/нулевой) | //+------------------------------------------------------------------+ ENUM_BAR_BODY_TYPE CBar::BodyType(void) const { return ( this.Close()>this.Open() ? BAR_BODY_TYPE_BULLISH : this.Close()<this.Open() ? BAR_BODY_TYPE_BEARISH : (this.ShadowUpSize()+this.ShadowDownSize()==0 ? BAR_BODY_TYPE_NULL : BAR_BODY_TYPE_CANDLE_ZERO_BODY) ); } //+------------------------------------------------------------------+
Здесть всё просто: если цена закрытия бара больше цены открытия, то это
бычий бар, если цена закрытия бара меньше цены открытия, то это
медвежий бар, иначе если обе тени свечи нулевые, то это бар с нулевым
телом, иначе —
это свеча с нулевым телом.
Метод, возвращающий описание типа бара:
//+------------------------------------------------------------------+ //| Возвращает описание типа бара | //+------------------------------------------------------------------+ string CBar::BodyTypeDescription(void) const { return ( this.BodyType()==BAR_BODY_TYPE_BULLISH ? CMessage::Text(MSG_LIB_TEXT_BAR_TYPE_BULLISH) : this.BodyType()==BAR_BODY_TYPE_BEARISH ? CMessage::Text(MSG_LIB_TEXT_BAR_TYPE_BEARISH) : this.BodyType()==BAR_BODY_TYPE_CANDLE_ZERO_BODY ? CMessage::Text(MSG_LIB_TEXT_BAR_TYPE_CANDLE_ZERO_BODY) : CMessage::Text(MSG_LIB_TEXT_BAR_TYPE_NULL) ); } //+------------------------------------------------------------------+
В зависимости от типа бара метод возвращает его текстовое описание, прописанное в файле Datas.mqh.
Класс объекта "Бар" готов. Теперь мы можем для каждого требуемого бара нужной таймсерии создать объект-бар. Но сам по себе он не может дать нам какого-либо значимого преимущества перед обычным получением данных при помощи запроса бара таймсерии посредством CopyRates().
Для того, чтобы мы могли спокойно оперировать данными таймсерий как нам хочется, нам нужно создать список объектов-баров, соответствующих нужной таймсерии, и в требуемом количестве. Тогда мы сможем уже анализировать данные списка и искать в нём всю необходимую для анализа информацию.
А значит — нам необходимо создать список-таймсерию, в котором будут храниться объекты-бары.
Помимо этого нам
потребуется знать момент и фиксировать факт открытия нового бара — чтобы дописать очередной объект-бар в список, да и всегда иметь под
рукой инструмент, сигнализирующий нам об открытии нового бара на любом символе и любом таймфрейме — в любом количестве символов и
таймфреймов.
Прежде чем создадим список для хранения объектов-баров, напишем класс "Новый бар", так как объект этого класса будет являться одним из свойств списка баров.
Объект "Новый бар"
Для определения факта открытия нового бара достаточно сравнить время открытия текущего бара с запомненным прошлым временем его открытия. Если эти времена не совпадают — имеем факт открытия нового бара. В этом случае нужно сохранить новое время открытия как прошлое для последующего сравнения:
NewBar = false; if(PrevTime != Time) { NewBar = true; PrevTime = Time; }
Такой вариант покажет один раз событие открытия нового бара, и все последующие команды будут выполняться уже на новом баре.
Но иногда требуется исполнение определённых команд именно после события "Новый бар", и до тех пор, пока все команды не будут выполнены, такое событие должно быть актуальным. Для этого нужно выполнить все команды, которые должны быть обязательно завершены в момент рождения нового бара, до того как присвоить прошлому времени новое значение:
NewBar = false; if(PrevTime != Time) { NewBar = true; // ... команды, которые // ... должны быть выполнены // ... в момент рождения нового бара PrevTime = Time; }
Таким образом, первый вариант может быть выполнен как самостоятельныя функция, возвращающая флаг открытия нового бара. Второй же вариант в представленном исполнении должен быть в составе обработчика OnTick(), желательно в самом его начале — чтобы сначала исполнились все команды, которые должы быть выполнены в момент рождения нового бара, а уже затем — всё остальное, выполняемое всегда.
В самом простом случае этого достаточно для контроля открытия нового бара.
Но для нужд библиотеки этого
недостаточно — нам требуется раздельное определение нового бара для каждого символа и каждого таймфрейма.
При этом — в двух
озвученных вариантах:
- автоматический контроль и сохранение времени для заданного символа и таймфрейма (возврат флага события "Новый бар" раздельно для каждого символа и таймфрейма),
- контроль времени для заданного символа и таймфрейма с ручным управлением сохранения его нового значения
(определение факта события "Новый бар" и предоставление пользователю определить когда сохранить новое время для последующего контроля очередного нового бара раздельно для каждого символа и таймфрейма).
Ещё одним важным моментом является особенность: не
рекомендуется из индикаторов обращаться к функциям обновления таймсерий в случае, если данные запрашиваются на текущем символе и
таймфрейме. Такой запрос с большой долей вероятности может привести к клинчу, так как обновление исторических данных
производится в том же потоке, в котором работает индикатор. Поэтому необходимо обязательно проверять для запросов из
иникаторов "а не с текущего ли символа и таймфрейма мы пытаемся получить данные таймсерии", и если это так, то для
такого запроса необходимо использовать иные способы, нежели получение при помощи SeriesInfoInteger()
и других функций, возвращающих серийные данные, и обращение к которым
инициирует подгрузку истории.
И такой способ есть, и он достаточно прост:
В индикаторах в параметрах обработчика OnCalculate() уже есть нужные нам предопределённые переменные:
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[])
- rates_total — количество доступной истории в таймсериях (аналог функции Bars() без параметров),
- prev_calculated — количество уже посчитанных данных на прошлом вызове,
- time[] — массив-таймсерия с данными о времени баров.
В индикаторах можно отслеживать изменения исторических данных при помощи простого расчёта:
- если (rates_total - prev_calculated) больше 1, то это означает подгрузку истории и индикатор нужно перерисовать полностью,
- если (rates_total - prev_calculated) равно 1 — это означает открытие нового бара на текущем символе-таймфрейме.
- в обычном состоянии на каждом новом тике значение выражения (rates_total - prev_calculated) равно 0.
В индикаторах для текущего символа и таймфрейма для определения нового бара и указания его времени в методы класса будем передавать
время бара из массива time[], а в остальных случаях — получать время внутри методов класса.
В папке библиотеки \MQL5\Include\DoEasy\Objects\Series\ создадим новый файл NewBarObj.mqh класса CNewBarObj:
//+------------------------------------------------------------------+ //| NewBarObj.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" #property strict // Нужно для mql4 //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Класс объекта "Новый бар" | //+------------------------------------------------------------------+ class CNewBarObj { private: string m_symbol; // Символ ENUM_TIMEFRAMES m_timeframe; // Таймфрейм datetime m_new_bar_time; // Время нового бара для автоматического управления временем datetime m_prev_time; // Прошлое время для автоматического управления временем datetime m_new_bar_time_manual; // Время нового бара для ручного управления временем datetime m_prev_time_manual; // Прошлое время для ручного управления временем //--- Возвращает дату текущего бара datetime GetLastBarDate(const datetime time); public: //--- Устанавливает (1) символ, (2) таймфрейм void SetSymbol(const string symbol) { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); } void SetPeriod(const ENUM_TIMEFRAMES timeframe) { this.m_timeframe=(timeframe==PERIOD_CURRENT ? (ENUM_TIMEFRAMES)::Period() : timeframe); } //--- Сохраняет время нового бара при ручном управлении временем void SaveNewBarTime(const datetime time) { this.m_prev_time_manual=this.GetLastBarDate(time); } //--- Возвращает (1) символ, (2) таймфрейм string Symbol(void) const { return this.m_symbol; } ENUM_TIMEFRAMES Period(void) const { return this.m_timeframe; } //--- Возвращает (1) время нового бара datetime TimeNewBar(void) const { return this.m_new_bar_time; } //--- Возвращает флаг открытия нового бара при (1) автоматическом, (2) ручном управлении временем bool IsNewBar(const datetime time); bool IsNewBarManual(const datetime time); //--- Конструкторы CNewBarObj(void) : m_symbol(::Symbol()), m_timeframe((ENUM_TIMEFRAMES)::Period()), m_prev_time(0),m_new_bar_time(0), m_prev_time_manual(0),m_new_bar_time_manual(0) {} CNewBarObj(const string symbol,const ENUM_TIMEFRAMES timeframe); }; //+------------------------------------------------------------------+
Мне кажется тут совсем всё просто:
в приватной секции объявлены
переменные-члены класса для хранения символа и таймфрейма, для которых объект будет определять событие "Новый бар",
переменные для хранения времени открытия нового бара и прошлго времени открытия — раздельные для
автоматического и ручного управления временем (для
чего нужно — обсуждалось выше).
Метод GetLastBarDate() возвращает время открытия нового бара и будет рассмотрен
ниже.
В публичной секции класса объявлен и реализован метод сохранения времени нового бара как предыдущее для ручного
управления временем SaveNewBarTime() — позволяет пользователю библиотеки самостоятельно сохранять время нового бара
после того, как все необходимые действия на новом баре будут завершены.
Остальные методы говорят сами за себя, и описывать здесь
мы их не будем.
В классе реализованы два конструктора — первый конструктор не имеет параметров, в его списке инициализации прописываются текущий
символ и таймфрейм и обнуляются все значения времени нового бара и прошлого времени открытия бара. После создания такого объекта
необходимо самостоятельно вызвать методы установки нужных символа и таймфрейма для созданного объекта класса.
Второй
конструктор — параметрический, в него сразу же передаются нужные символ и таймфрейм:
//+------------------------------------------------------------------+ //| Параметрический конструктор | //+------------------------------------------------------------------+ CNewBarObj::CNewBarObj(const string symbol,const ENUM_TIMEFRAMES timeframe) : m_symbol(symbol),m_timeframe(timeframe) { this.m_prev_time=this.m_prev_time_manual=this.m_new_bar_time=this.m_new_bar_time_manual=0; } //+------------------------------------------------------------------+
В его списке инициализации устанавливаются символ и таймфрейм, переданные в параметрах создаваемого объекта класса, а затем в теле класса всем переменным времени устанавливаются нулевые значения.
Метод, возвращающий флаг открытия нового бара при автоматическом управлении временем:
//+------------------------------------------------------------------+ //| Возвращает флаг открытия нового бара | //+------------------------------------------------------------------+ bool CNewBarObj::IsNewBar(const datetime time) { //--- Получаем время текущего бара datetime tm=this.GetLastBarDate(time); //--- Если прошлое и текущее время равны нулю - это первый запуск if(this.m_prev_time==0 && this.m_new_bar_time==0) { //--- устанавливаем время открытия нового бара, //--- устанавливаем время прошлого бара как текущее и возвращаем false this.m_new_bar_time=this.m_prev_time=tm; return false; } //--- Если прошлое время не равно времени открытия текущего бара - это новый бар if(this.m_prev_time!=tm) { //--- устанавливаем время открытия нового бара, //--- устанавливаем прошлое время как текущее и возвращаем true this.m_new_bar_time=this.m_prev_time=tm; return true; } //--- в остальных случаях возвращаем false return false; } //+------------------------------------------------------------------+
Метод единожды при каждом открытии нового бара на символе и таймфрейме, заданными для объекта, возвращает true.
Метод, возвращающий флаг открытия нового бара при ручном управлении временем:
//+------------------------------------------------------------------+ //| Возвращает флаг открытия нового бара при ручном управлении | //+------------------------------------------------------------------+ bool CNewBarObj::IsNewBarManual(const datetime time) { //--- Получаем время текущего бара datetime tm=this.GetLastBarDate(time); //--- Если прошлое и текущее время равны нулю - это первый запуск if(this.m_prev_time_manual==0 && this.m_new_bar_time_manual==0) { //--- устанавливаем время открытия нового бара, //--- устанавливаем время прошлого бара как текущее и возвращаем false this.m_new_bar_time_manual=this.m_prev_time_manual=tm; return false; } //--- Если прошлое время не равно времени открытия текущего бара - это новый бар if(this.m_prev_time_manual!=tm) { //--- устанавливаем время открытия нового бара и возвращаем true //--- Предыдущее время сохранять как текущее необходимо самостоятельно из программы методом SaveNewBarTime() //--- До тех пор, пока принудительно из программы не будет установлено прошлое время как текущее, //--- метод будет возвращать флаг нового бара, позволяя завершить все необходимые действия на новом баре. this.m_new_bar_time=tm; return true; } //--- в остальных случаях возвращаем false return false; } //+------------------------------------------------------------------+
В отличии от метода с автоматическим управлением временем, данный метод не записывает в переменную, хранящую прошлое время бара,
значение текущего времени открытия нового бара. Это даёт возможность каждый раз на новом тике после фиксации события "Новый бар"
отправлять флаг открытия нового бара до тех пор, пока пользователь сам не решит что все действия, которые должны быть сделаны на
открытии нового бара, завершились и нужно сохранить время открытия нового бара как прошлое.
В оба метода в их входных параметрах
передаётся время нового бара, затем при помощи метода GetLastBarDate() решается какое время использовать:
//+------------------------------------------------------------------+ //| Возвращает время текущего бара | //+------------------------------------------------------------------+ datetime CNewBarObj::GetLastBarDate(const datetime time) { return ( ::MQLInfoInteger(MQL_PROGRAM_TYPE)==PROGRAM_INDICATOR && this.m_symbol==::Symbol() && this.m_timeframe==::Period() ? time : (datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_LASTBAR_DATE) ); } //+------------------------------------------------------------------+
Если это индикатор и
символ и таймфрейм объекта "Новый бар" совпадают с текущими символом и таймфреймом, то
возвращается время, переданное в метод (в индикаторах это время есть в параметрах OnCalculate() в массиве time[], и именно из
этого массива нужно передавать время в методы определения нового бара), иначе —
получаем время последнего бара при помощи SeriesInfoInteger() — в данном случае вместо времени можно передать любое
значение.
Для текущих потребностей класс объекта "Новый бар" готов. Приступим к созданию списка объектов-баров.
Список объектов-баров это по сути отрезок исторических данных таймсерий, входящих в состав MqlRates. Для чего нужно создавать отдельный
список?
Во-первых — для возможности быстрой сортировки, поиска и сравнений, и во-вторых — объект-бар, заданное число которых
будет храниться в списке, предоставляют помимо полей структуры MqlRates, дополнительные поля со значениями, которые облегчат
поиск различных свечных формаций в будущем.
Список объектов-баров, поиск и сортировка
Для списка-таймсерии (список объектов-баров) будем использовать класс динамического массива указателей на экземпляры класса CObject и его наследников стандартной библиотеки. Сегодня создадим единственный объект-список объектов-баров, в котором будут храниться бары только одной таймсерии по символу и таймфрейму. В последующих статьях на основе данного списка создадим коллекцию таймсерий по таймфреймам для каждого отдельного символа, используемого в программе пользователя. Таким образом, мы будем иметь множество однотипных коллекций списков-таймсерий, в которых возможно будет проводить быстрый поиск нужной информации для анализа и сравнения с другими списками-коллекциями таймсерий, имеющимися в распоряжении пользователя библиотеки.
Каждый список объектов-баров будет иметь определённое пользователем количество объектов-баров (глубину истории). По умолчанию глубина
истории всех списков будет иметь размерность 1000 баров. И каждый список, прежде чем будет построен, должен учитывать факт синхронизации
данных с торговым сервером. Каждый список каждой таймсерии будет иметь значение, указывающее количество доступных баров истории. Эту
величину возвращает функция Bars() без параметров. Если она вернула ноль, это значит, что история ещё не синхронизирована. И мы будем
делать несколько попыток с небольшим ожиданием между ними в ожидании синхронизации данных с сервером.
Создадим в файле Defines.mqh макроподстановку, определяющую
глубину используемой истории, количество милисекунд паузы между попытками
синхронизации истории с сервером и количество попыток получения факта
синхронизации:
//--- Идентификаторы типов отложенных запросов #define PENDING_REQUEST_ID_TYPE_ERR (1) // Тип отложенного запроса, созданного по коду возврата сервера #define PENDING_REQUEST_ID_TYPE_REQ (2) // Тип отложенного запроса, созданного по запросу //--- Параметры таймсерий #define SERIES_DEFAULT_BARS_COUNT (1000) // Требуемое количество данных таймсерий по умолчанию #define PAUSE_FOR_SYNC_ATTEMPTS (16) // Количество милисекунд паузы между попытками синхронизации #define ATTEMPTS_FOR_SYNC (5) // Количество попыток получения факта синхронизации с сервером //+------------------------------------------------------------------+ //| Структуры | //+------------------------------------------------------------------+
Для быстрого поиска и сортировки списков-коллекций мы уже создавали функционал в классе CSelect,
описанный в папке
сервисных функций и классов \MQL5\Include\DoEasy\Services\ в файле Select.mqh.
Добавим в класс методы для поиска и сортировки в списках объектов-баров.
Подключим
к листингу файл класса объекта "Бар":
//+------------------------------------------------------------------+ //| Select.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 <Arrays\ArrayObj.mqh> #include "..\Objects\Orders\Order.mqh" #include "..\Objects\Events\Event.mqh" #include "..\Objects\Accounts\Account.mqh" #include "..\Objects\Symbols\Symbol.mqh" #include "..\Objects\PendRequest\PendRequest.mqh" #include "..\Objects\Series\Bar.mqh" //+------------------------------------------------------------------+
Впишем в тело класса определения методов поиска и сортировки по свойствам объекта "Бар":
//+------------------------------------------------------------------+ //| Класс для выборки объектов, удовлетворяющих критерию | //+------------------------------------------------------------------+ class CSelect { private: //--- Метод сравнения двух величин template<typename T> static bool CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode); public: //+------------------------------------------------------------------+ //| Методы работы с ордерами | //+------------------------------------------------------------------+ //--- Возвращает список ордеров, у которых одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Возвращает индекс ордера в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера static int FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property); static int FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property); static int FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property); //--- Возвращает индекс ордера в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера static int FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property); static int FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property); static int FindOrderMin(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property); //+------------------------------------------------------------------+ //| Методы работы с событиями | //+------------------------------------------------------------------+ //--- Возвращает список событий, у которых одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByEventProperty(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Возвращает индекс события в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства события static int FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property); static int FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property); static int FindEventMax(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property); //--- Возвращает индекс события в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства события static int FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_INTEGER property); static int FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_DOUBLE property); static int FindEventMin(CArrayObj *list_source,ENUM_EVENT_PROP_STRING property); //+------------------------------------------------------------------+ //| Методы работы с аккаунтами | //+------------------------------------------------------------------+ //--- Возвращает список аккаунтов, у которых одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByAccountProperty(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Возвращает индекс события в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства события static int FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property); static int FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property); static int FindAccountMax(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property); //--- Возвращает индекс события в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства события static int FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_INTEGER property); static int FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_DOUBLE property); static int FindAccountMin(CArrayObj *list_source,ENUM_ACCOUNT_PROP_STRING property); //+------------------------------------------------------------------+ //| Методы работы с символами | //+------------------------------------------------------------------+ //--- Возвращает список символов, у которых одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *BySymbolProperty(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Возвращает индекс символа в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера static int FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property); static int FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property); static int FindSymbolMax(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property); //--- Возвращает индекс символа в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера static int FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_INTEGER property); static int FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_DOUBLE property); static int FindSymbolMin(CArrayObj *list_source,ENUM_SYMBOL_PROP_STRING property); //+------------------------------------------------------------------+ //| Методы работы с отложенными запросами | //+------------------------------------------------------------------+ //--- Возвращает список отложенных запросов, у которых одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию static CArrayObj *ByPendReqProperty(CArrayObj *list_source,ENUM_PEND_REQ_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByPendReqProperty(CArrayObj *list_source,ENUM_PEND_REQ_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByPendReqProperty(CArrayObj *list_source,ENUM_PEND_REQ_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Возвращает индекс отложенного запроса в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера static int FindPendReqMax(CArrayObj *list_source,ENUM_PEND_REQ_PROP_INTEGER property); static int FindPendReqMax(CArrayObj *list_source,ENUM_PEND_REQ_PROP_DOUBLE property); static int FindPendReqMax(CArrayObj *list_source,ENUM_PEND_REQ_PROP_STRING property); //--- Возвращает индекс отложенного запроса в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера static int FindPendReqMin(CArrayObj *list_source,ENUM_PEND_REQ_PROP_INTEGER property); static int FindPendReqMin(CArrayObj *list_source,ENUM_PEND_REQ_PROP_DOUBLE property); static int FindPendReqMin(CArrayObj *list_source,ENUM_PEND_REQ_PROP_STRING property); //+------------------------------------------------------------------+ //| Методы работы с барами таймсерии | //+------------------------------------------------------------------+ //--- Возвращает список баров, у которых одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Возвращает индекс отложенного запроса в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера static int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property); static int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property); static int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_STRING property); //--- Возвращает индекс отложенного запроса в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера static int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property); static int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property); static int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_STRING property); //--- }; //+------------------------------------------------------------------+
И за пределами тела класса напишем реализацию добавленных методов:
//+------------------------------------------------------------------+ //| Методы работы со списками баров таймсерии | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Возвращает список баров, у которых одно из целочисленных | //| свойств удовлетворяет заданному критерию | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); int total=list_source.Total(); for(int i=0; i<total; i++) { CBar *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; long obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Возвращает список баров, у которых одно из вещественных | //| свойств удовлетворяет заданному критерию | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); for(int i=0; i<list_source.Total(); i++) { CBar *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; double obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Возвращает список баров, у которых одно из строковых | //| свойств удовлетворяет заданному критерию | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); for(int i=0; i<list_source.Total(); i++) { CBar *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; string obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Возвращает индекс бара в списке | //| с максимальным значением целочисленного свойства | //+------------------------------------------------------------------+ int CSelect::FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CBar *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBar *obj=list_source.At(i); long obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); long obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Возвращает индекс бара в списке | //| с максимальным значением вещественного свойства | //+------------------------------------------------------------------+ int CSelect::FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CBar *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBar *obj=list_source.At(i); double obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); double obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Возвращает индекс бара в списке | //| с максимальным значением строкового свойства | //+------------------------------------------------------------------+ int CSelect::FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_STRING property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CBar *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBar *obj=list_source.At(i); string obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); string obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Возвращает индекс бара в списке | //| с минимальным значением целочисленного свойства | //+------------------------------------------------------------------+ int CSelect::FindBarMin(CArrayObj* list_source,ENUM_BAR_PROP_INTEGER property) { int index=0; CBar *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBar *obj=list_source.At(i); long obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); long obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+ //| Возвращает индекс бара в списке | //| с минимальным значением вещественного свойства | //+------------------------------------------------------------------+ int CSelect::FindBarMin(CArrayObj* list_source,ENUM_BAR_PROP_DOUBLE property) { int index=0; CBar *min_obj=NULL; int total=list_source.Total(); if(total== 0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBar *obj=list_source.At(i); double obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); double obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+ //| Возвращает индекс бара в списке | //| с минимальным значением строкового свойства | //+------------------------------------------------------------------+ int CSelect::FindBarMin(CArrayObj* list_source,ENUM_BAR_PROP_STRING property) { int index=0; CBar *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBar *obj=list_source.At(i); string obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); string obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+
Работу подобных методов мы рассматривали в третьей статье предыдущей серии описания библиотеки, поэтому здесь останавливаться не будем — всегда есть возможность повторить материал и попытаться разобраться в нём самостоятельно. Ну и конечно же, все вопросы можно озвучить в обсуждении к статье.
Создадим в папке \MQL5\Include\DoEasy\Objects\Series\ новый файл Series.mqh класса CSeries и подключим к нему файл класса CSelect и созданные новые классы объекта "Новый Бар" и объекта "Бар":
//+------------------------------------------------------------------+ //| Series.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" #property strict // Нужно для mql4 //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "..\..\Services\Select.mqh" #include "NewBarObj.mqh" #include "Bar.mqh" //+------------------------------------------------------------------+
Теперь пропишем в теле класса все необходимые переменные-члены класса и объявим методы класса:
//+------------------------------------------------------------------+ //| Series.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" #property strict // Нужно для mql4 //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "..\..\Services\Select.mqh" #include "NewBarObj.mqh" #include "Bar.mqh" //+------------------------------------------------------------------+ //| Класс "Таймсерия" | //+------------------------------------------------------------------+ class CSeries : public CObject { private: ENUM_PROGRAM_TYPE m_program; // Тип программы ENUM_TIMEFRAMES m_timeframe; // Таймфрейм string m_symbol; // Символ uint m_amount; // Количество используемых данных таймсерии uint m_bars; // Количество баров в истории по символу и таймфрейму bool m_sync; // Флаг синхронизированности данных CArrayObj m_list_series; // Список-таймсерия CNewBarObj m_new_bar_obj; // Объект "Новый бар" public: //--- Возвращает список-таймсерию CArrayObj* GetList(void) { return &m_list_series;} //--- Возвращает список баров по выбранному (1) double, (2) integer и (3) string свойству, удовлетворяющему сравниваемому условию CArrayObj* GetList(ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByBarProperty(this.GetList(),property,value,mode); } CArrayObj* GetList(ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByBarProperty(this.GetList(),property,value,mode); } CArrayObj* GetList(ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByBarProperty(this.GetList(),property,value,mode); } //--- Устанавливает (1) символ и таймфрейм, (2) количество используемых данных таймсерии void SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe); bool SetAmountUsedData(const uint amount,const uint rates_total); //--- Возвращает (1) символ, (2) таймфрейм, (3) количество используемых данных таймсерии, //--- (4) количество баров в таймсерии, флаг нового бара с (5) автоматическим, (6) ручным управлением временем string Symbol(void) const { return this.m_symbol; } ENUM_TIMEFRAMES Period(void) const { return this.m_timeframe; } uint AmountUsedData(void) const { return this.m_amount; } uint Bars(void) const { return this.m_bars; } bool IsNewBar(const datetime time) { return this.m_new_bar_obj.IsNewBar(time); } bool IsNewBarManual(const datetime time) { return this.m_new_bar_obj.IsNewBarManual(time); } //--- Возвращает объект-бар по индексу (1) в списке, (2) в таймсерии CBar *GetBarByListIndex(const uint index); CBar *GetBarBySeriesIndex(const uint index); //--- Возвращает (1) Open, (2) High, (3) Low, (4) Close, (5) время, (6) тиковый объём, (7) реальный объём, (8) спред бара по индексу double Open(const uint index,const bool from_series=true); double High(const uint index,const bool from_series=true); double Low(const uint index,const bool from_series=true); double Close(const uint index,const bool from_series=true); datetime Time(const uint index,const bool from_series=true); long TickVolume(const uint index,const bool from_series=true); long RealVolume(const uint index,const bool from_series=true); int Spread(const uint index,const bool from_series=true); //--- Сохраняет время нового бара при ручном управлении временем void SaveNewBarTime(const datetime time) { this.m_new_bar_obj.SaveNewBarTime(time); } //--- Синхронизирует данные по символу с данными на сервере bool SyncData(const uint amount,const uint rates_total); //--- (1) Создаёт, (2) обновляет список-таймсерию int Create(const uint amount=0); void Refresh(const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0); //--- Конструкторы CSeries(void); CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint amount=0); }; //+------------------------------------------------------------------+
Методы получения списка у нас есть во всех классах-коллекциях
объектов, разработка этих методов рассматривалась в третьей части предыдущей
серии.
Пройдёмся по списку объявленных методов и разберём их реализации.
Первый конструктор класса не имеет параметров и служит для создания списка для текущих символа и таймфрейма:
//+------------------------------------------------------------------+ //| Конструктор 1 (таймсерия текущего символа и периода) | //+------------------------------------------------------------------+ CSeries::CSeries(void) : m_bars(0),m_amount(0),m_sync(false) { this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE); this.m_list_series.Clear(); this.m_list_series.Sort(SORT_BY_BAR_INDEX); this.SetSymbolPeriod(::Symbol(),(ENUM_TIMEFRAMES)::Period()); } //+------------------------------------------------------------------+
В списке инициализации обнуляются значения количества доступных баров
таймсерии, количества баров, хранящихся в списке, и флаг синхронизированности данных с сервером.
Затем устанавливается
тип программы, очищается список объектов-баров и ему
задаётся флаг сортировки по индексу баров, после чего для списка
устанавливаются текущие символ и таймфрейм.
После создания списка объектов-баров обязательно нужно установить для него
количество используемых баров при помощи метода SetAmountUsedData() или SyncData(), в состав которого входит метод
SetAmountUsedData(). Для индикаторов в метод обязательно вторым параметром нужно передать rates_total.
Второй конструктор класса имеет три входных парамета: символ, таймфрейм и размер списка объектов-баров, и служит для создания списка для указанных символа и таймфрейма на нужную глубину истории:
//+------------------------------------------------------------------+ //| Конструктор 2 (таймсерия указанных символа и периода) | //+------------------------------------------------------------------+ CSeries::CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint amount=0) : m_bars(0), m_amount(0),m_sync(false) { this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE); this.m_list_series.Clear(); this.m_list_series.Sort(SORT_BY_BAR_INDEX); this.SetSymbolPeriod(symbol,timeframe); this.m_sync=this.SetAmountUsedData(amount,0); } //+------------------------------------------------------------------+
В списке инициализации обнуляются значения количества доступных баров
таймсерии, количества баров, хранящихся в списке, и флаг синхронизированности данных с сервером.
Затем устанавливается
тип программы, очищается список объектов-баров и ему
задаётся флаг сортировки по индексу баров,
после чего для списка
устанавливаются текущие символ и таймфрейм.
И напоследок флагу
синхронизированности устанавливается результат работы метода установки количества требуемых баров для списка объектов-баров
SetAmountUsedData(), в который передаётся требуемая глубина истории, указанная входным параметром amount
конструктора.
После создания списка объектов-баров обязательно из программы нужно проконтролировать синхронизацию с сервером
при помощи метода SyncData().
Метод для установки символа и таймфрейма:
//+------------------------------------------------------------------+ //| Устанавливает символ и таймфрейм | //+------------------------------------------------------------------+ void CSeries::SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe) { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_timeframe=(timeframe==PERIOD_CURRENT ? (ENUM_TIMEFRAMES)::Period() : timeframe); this.m_new_bar_obj.SetSymbol(this.m_symbol); this.m_new_bar_obj.SetPeriod(this.m_timeframe); } //+------------------------------------------------------------------+
В метод передаются символ и таймфрейм, проверяется корректность переданных значений и устанавливается либо текущий символ и таймфрейм, либо переданные в метод. Затем объекту "Новый бар" списка баров устанавливаются сохранённые только что в переменных символ и таймфрейм.
Метод, устанавливающий количество используемых данных таймсерии для списка объектов-баров:
//+------------------------------------------------------------------+ //| Устанавливает количество требуемых данных | //+------------------------------------------------------------------+ bool CSeries::SetAmountUsedData(const uint amount,const uint rates_total) { //--- Установим количество доступных баров таймсерии this.m_bars= ( //--- Если это индикатор и работа на текущем символе и таймфрейме, //--- то записываем переданное в метод значение rates_total, //--- иначе - получаем количество из окружения this.m_program==PROGRAM_INDICATOR && this.m_symbol==::Symbol() && this.m_timeframe==::Period() ? rates_total : ::Bars(this.m_symbol,this.m_timeframe) ); //--- Если удалось записать количество доступных баров истории - установим количество данных в списке: if(this.m_bars>0) { //--- если передано нулевое значение amount, //--- то используем либо значение по умолчанию (1000 баров), либо количество доступных баров истории - меньшее из них //--- если передано не нулевое значение amount, //--- то используем либо значение amount, либо количество доступных баров истории - меньшее из них this.m_amount=(amount==0 ? ::fmin(SERIES_DEFAULT_BARS_COUNT,this.m_bars) : ::fmin(amount,this.m_bars)); return true; } return false; } //+------------------------------------------------------------------+
В метод передаются необходимое количество данных для списка объектов-баров
и общее количество баров текущей таймсерии (для индикаторов).
Затем проверяется тип программы и выбирается, откуда получаем количество доступной истории в переменную m_bars — либо из
переданного в метод значения (для индикатора на текущем символе и таймфрейме), либо из
окружения. Далее определяем, какое значение должно быть установлено для переменной m_amount — исходя из
размера доступной и требуемой истории.
Метод синхронизации данных по символу и таймфрейму с данными на сервере:
//+------------------------------------------------------------------+ //|Синхронизирует данные по символу и таймфрейму с данными на сервере| //+------------------------------------------------------------------+ bool CSeries::SyncData(const uint amount,const uint rates_total) { //--- Если удалось получить доступное количество баров в таймсерии //--- и установить размер списка объектов-баров - возвращаем true this.m_sync=this.SetAmountUsedData(amount,rates_total); if(this.m_sync) return true; //--- Данные ещё не синхронизированы с сервером //--- Создаём объект-паузу CPause *pause=new CPause(); if(pause==NULL) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_PAUSE_OBJ)); return false; } //--- Устанавливаем длительность паузы в 16 милисекунд (PAUSE_FOR_SYNC_ATTEMPTS) и инициализируем счётчик тиков pause.SetWaitingMSC(PAUSE_FOR_SYNC_ATTEMPTS); pause.SetTimeBegin(0); //--- Сделаем пять (ATTEMPTS_FOR_SYNC) попыток получить доступное количество баров в таймсерии //--- и установить размер списка объектов-баров int attempts=0; while(attempts<ATTEMPTS_FOR_SYNC && !::IsStopped()) { //--- Если данные синхронизированы с сервером на данный момент if(::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_SYNCHRONIZED)) { //--- если удалось получить доступное количество баров в таймсерии //--- и установить размер списка объектов-баров - прерываем цикл this.m_sync=this.SetAmountUsedData(amount,rates_total); if(this.m_sync) break; } //--- Данные ещё не синхронизированы. //--- Если пауза в 16 мсек завершена if(pause.IsCompleted()) { //--- устанавливаем объекту-паузе новое начало следующего ожидания //--- и увеличиваем счётчик попыток pause.SetTimeBegin(0); attempts++; } } //--- Удаляем объект-паузу и возвращаем значение m_sync delete pause; return this.m_sync; } //+------------------------------------------------------------------+
Логика метода описана в комментариях к коду, думаю, там всё понятно.
Методы создания и обновления списка объектов-баров тоже подробно прокомментированы в листинге, и чтобы не занимать много места для их описания, просто рассмотрим их целиком. Если возникнут какие-либо вопросы по этим методам, то их всегда можно задать в обсуждении к статье:
//+------------------------------------------------------------------+ //| Создаёт список-таймсерию | //+------------------------------------------------------------------+ int CSeries::Create(const uint amount=0) { //--- Если ещё не установлена требуемая глубина истории для списка //--- выводим об этом сообщение и возвращаем ноль, if(this.m_amount==0) { ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA)); return 0; } //--- иначе, если переданное значение amount больше нуля, не равно уже установленному, //--- и при этом переданное значение amount меньше доступного количества баров, //--- устанавливаем новое значение требуемой глубины истории для списка else if(amount>0 && this.m_amount!=amount && amount<this.m_bars) { //--- Если не удалось установить новое значение - возвращаем ноль if(!this.SetAmountUsedData(amount,0)) return 0; } //--- Для массива rates[], в который будем получать исторические данные, //--- установим признак направленности как в таймсерии, //--- очистим список объектов-баров и установим ему флаг сортировки по индексу бара MqlRates rates[]; ::ArraySetAsSeries(rates,true); this.m_list_series.Clear(); this.m_list_series.Sort(SORT_BY_BAR_INDEX); ::ResetLastError(); //--- Получим в массив rates[] исторические данные структуры MqlRates, начиная от текущего бара в количестве m_amount, //--- и если получить данные не удалось - выводим об этом сообщение и возвращаем ноль int copied=::CopyRates(this.m_symbol,this.m_timeframe,0,this.m_amount,rates),err=ERR_SUCCESS; if(copied<1) { err=::GetLastError(); ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_GET_SERIES_DATA)," ",this.m_symbol," ",TimeframeDescription(this.m_timeframe),". ", CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(err),CMessage::Retcode(err)); return 0; } //--- Исторические данные получены в массив rates[] //--- В цикле по массиву rates[] for(int i=0; i<copied; i++) { //--- создаём новый обект-бар из данных текущей структуры MqlRates из массива rates[] по индексу цикла ::ResetLastError(); CBar* bar=new CBar(this.m_symbol,this.m_timeframe,i,rates[i]); if(bar==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_BAR_OBJ),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(::GetLastError())); continue; } //--- Если не удалось добавить новый объект-бар в список //--- выволим об этом сообщение в журнал с описанием ошибки if(!this.m_list_series.Add(bar)) { err=::GetLastError(); ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_FAILED_ADD_TO_LIST)," ",bar.Header(),". ", CMessage::Text(MSG_LIB_SYS_ERROR),": ",CMessage::Text(err),CMessage::Retcode(err)); } } //--- Возвращаем размер созданного списка объектов-баров return this.m_list_series.Total(); } //+------------------------------------------------------------------+ //| Обновляет список и данные тайм-серии | //+------------------------------------------------------------------+ void CSeries::Refresh(const datetime time=0, const double open=0, const double high=0, const double low=0, const double close=0, const long tick_volume=0, const long volume=0, const int spread=0) { MqlRates rates[1]; //--- Устанавливаем флаг сортировки списка баров по индексу this.m_list_series.Sort(SORT_BY_BAR_INDEX); //--- Если есть новый бар на символе и периоде if(this.IsNewBarManual(time)) { //--- создаём новый объект-бар и добавляем его в конец списка CBar *new_bar=new CBar(this.m_symbol,this.m_timeframe,0); if(new_bar==NULL) return; if(!this.m_list_series.Add(new_bar)) { delete new_bar; return; } //--- если установленный размер таймсерии больше одного бара - удаляем самый ранний бар if(this.m_list_series.Total()>1) this.m_list_series.Delete(0); //--- сохраняем новое время бара как прошлое для последующей проверки на новый бар this.SaveNewBarTime(time); } //--- Получаем индекс последнего бара в списке и объект-бар по этому индексу int index=this.m_list_series.Total()-1; CBar *bar=this.m_list_series.At(index); //--- если работа в индикаторе, и таймсерия принадлежит текущему символу и таймфрейму, //--- копируем в структуру цен бара переданные в метод извне параметры цен int copied=1; if(this.m_program==PROGRAM_INDICATOR && this.m_symbol==::Symbol() && this.m_timeframe==::Period()) { rates[0].time=time; rates[0].open=open; rates[0].high=high; rates[0].low=low; rates[0].close=close; rates[0].tick_volume=tick_volume; rates[0].real_volume=volume; rates[0].spread=spread; } //--- иначе - получаем данные в структуру цен бара из окружения else copied=::CopyRates(this.m_symbol,this.m_timeframe,0,1,rates); //--- Если цены получены - устанавливаем объекту-бару новые свойства из структуры цен if(copied==1) bar.SetProperties(rates[0]); } //+------------------------------------------------------------------+
Для создания списка-таймсерии сначала нужно установить размер требуемой истории и получить флаг синхронизированности с сервером методом SyncData(), а уже затем вызывать метод Create(). Для обновления данных списка объектов-баров достаточно на каждом тике вызывать метод Refresh(). Метод сам определит факт открытия нового бара и добавит новый объект-бар в список-таймсерию, и при этом самый ранний объект-бар будет удалён из списка объектов-баров — чтобы размер списка всегда оставался таким, каким он был установлен при помощи метода SyncData().
Нам необходимо будет получать из списка-таймсерии объекты "Бар" для совершения с их данными различных манипуляций. Если списку-таймсерии установлен флаг сортировки по индексу (SORT_BY_BAR_INDEX), то последовательность расположения объектов-баров в списке будет соответствовать расположению реальных баров в таймсерии. Но если списку установить другой флаг сортировки, то очерёдность расположения объектов в списке уже не будет соответствовать расположению реальных баров в таймсерии — они будут расположены по возрастанию значения свойства, по которому сортирован список. Поэтому для выбора объектов из списка объектов-баров у нас есть два метода: метод, возвращающий объект-бар по его индексу в таймсерии, и метод, возвращающий объект-бар по его индексу в списке объектов-баров.
Рассмотрим два этих метода.
Метод, возвращающий объект-бар по его индексу в списке объектов-баров:
//+------------------------------------------------------------------+ //| Возвращает объект-бар по индексу в списке | //+------------------------------------------------------------------+ CBar *CSeries::GetBarByListIndex(const uint index) { return this.m_list_series.At(this.m_list_series.Total()-index-1); } //+------------------------------------------------------------------+
В метод передаётся индекс нужного объекта-бара. Передаваемый индекс подразумевает направление как в таймсерии: нулевой индекс
указывает на последний объект в списке. Но объекты в списке сохраняются от нулевого индекса до list.Total()-1, т.е., чтобы получить самый
последний бар из списка, нужно запросить его по индексу list.Total()-1, а чтобы получить самый правый бар на графике, нужно запросить его
по индексу 0 — здесь обратная индексация.
Поэтому в методе для получения верного индекса производится его перерасчёт: от размера
списка отнимается номер переданного индекса-1, и возвращается объект-бар по рассчитанному индексу в соответствии с направлением
индексации как в таймсерии.
Метод, возвращающий объект-бар по его индексу в таймсерии:
//+------------------------------------------------------------------+ //| Возвращает объект-бар по индексу в таймсерии | //+------------------------------------------------------------------+ CBar *CSeries::GetBarBySeriesIndex(const uint index) { CArrayObj *list=this.GetList(BAR_PROP_INDEX,index); return(list==NULL || list.Total()==0 ? NULL : list.At(0)); } //+------------------------------------------------------------------+
В метод передаётся индекс нужного объекта-бара. Передаваемый индекс подразумевает направление как в таймсерии.
Для получения объекта с таким индексом бара в таймсерии, его нужно выбрать по свойству
BAR_PROP_INDEX. Если в списке объектов-баров есть такой бар с искомым индексом, то в списке list будет
находиться один единственный объект — его и возвращаем.
Если
же такого объекта нет, то будет возвращён NULL. Впрочем, оба этих метода при ошибке
возвращают значение NULL.
Методы, возвращающие базовые свойства объекта-бара из списка объектов-баров по индексу:
//+------------------------------------------------------------------+ //| Возвращает Open бара по индексу | //+------------------------------------------------------------------+ double CSeries::Open(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.Open() : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает High бара по индексу таймсерии или списка баров | //+------------------------------------------------------------------+ double CSeries::High(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.High() : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает Low бара по индексу таймсерии или списка баров | //+------------------------------------------------------------------+ double CSeries::Low(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.Low() : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает Close бара по индексу таймсерии или списка баров | //+------------------------------------------------------------------+ double CSeries::Close(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.Close() : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает время бара по индексу таймсерии или списка баров | //+------------------------------------------------------------------+ datetime CSeries::Time(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.Time() : 0); } //+-------------------------------------------------------------------+ //|Возвращает тиковый объём бара по индексу таймсерии или списка баров| //+-------------------------------------------------------------------+ long CSeries::TickVolume(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.VolumeTick() : WRONG_VALUE); } //+--------------------------------------------------------------------+ //|Возвращает реальный объём бара по индексу таймсерии или списка баров| //+--------------------------------------------------------------------+ long CSeries::RealVolume(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.VolumeReal() : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает спред бара по индексу таймсерии или списка баров | //+------------------------------------------------------------------+ int CSeries::Spread(const uint index,const bool from_series=true) { CBar *bar=(from_series ? this.GetBarBySeriesIndex(index) : this.GetBarByListIndex(index)); return(bar!=NULL ? bar.Spread() : WRONG_VALUE); } //+------------------------------------------------------------------+
В методы передаются индекс бара и флаг,
указывающий на то, что запрашиваемый индекс соответствует (true) направлению
индексации как в таймсерии.
Исходя из значения этого флагаполучаем
объект-бар либо при помощи метода GetBarBySeriesIndex(), либо при
помощи метода GetBarByListIndex(), а затем возвращаем значение
запрашиваемого в методе свойства.
Остальные, не рассмотренные здесь методы класса CSeries, служат для простой установки или возврата значений переменных-членов класса.
Чтобы проверить то, что сегодня сделали, нам необходимо сделать так, чтобы внешняя программа знала о созданных классах. Для этого достаточно подключить
файл класса CSeries к файлу главного объекта библиотеки CEngine:
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, 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" #include "Objects\Series\Series.mqh" //+------------------------------------------------------------------+
Теперь в тестовом советнике можно определить переменную с типом класса CSeries чтобы создать и использовать (в данной реализации — не
полноценно, а только для тестирования) списки таймсерий с заданным количеством объектов-баров. Что сейчас мы и проверим.
Тест
Для тестирования возьмём советник из последней статьи предыдущей серии и
сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part35\ под новым именем TestDoEasyPart35.mq5.
Как будем тестировать...
Создадим в советнике два временных (для теста) объекта класса CSeries — один для минутного таймфрейма
(2 бара), а второй — для текущего (10 баров). В обработчике OnInit() установим для них все необходимые параметры и выведем три списка:
- список всех баров текущей таймсерии, отсортированный по размеру свечей (от High до Low бара) — выведем короткие описания объектов-баров;
- список всех баров текущей таймсерии, отсортированный по индексам баров (от бара 0 до бара 9 ) — выведем короткие описания объектов-баров;
- список всех свойств объекта-бара, соответствующего индексу 1 (предыдущий бар) текущей таймсерии — выведем полный список всех свойств
бара.
А в обработчике OnTick() на каждом тике будем обновлять обе таймсерии и выводить в журнал запись об открытии нового бара на каждом из таймфреймов этих двух списков-таймсерий — на таймфрейме М1 и на текущем. Для текущей таймсерии при открытии нового бара, помимо вывода записи в журнал, проиграем стандартный звук "news.wav".
В списке глобальных переменных советника определим две переменные с типом класса CSeries — переменная списка-таймсерии текущего таймфрейма и переменная списка-таймсерии таймфрейма М1:
//+------------------------------------------------------------------+ //| TestDoEasyPart35.mq5 | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> //--- enums enum ENUM_BUTTONS { BUTT_BUY, BUTT_BUY_LIMIT, BUTT_BUY_STOP, BUTT_BUY_STOP_LIMIT, BUTT_CLOSE_BUY, BUTT_CLOSE_BUY2, BUTT_CLOSE_BUY_BY_SELL, BUTT_SELL, BUTT_SELL_LIMIT, BUTT_SELL_STOP, BUTT_SELL_STOP_LIMIT, BUTT_CLOSE_SELL, BUTT_CLOSE_SELL2, BUTT_CLOSE_SELL_BY_BUY, BUTT_DELETE_PENDING, BUTT_CLOSE_ALL, BUTT_SET_STOP_LOSS, BUTT_SET_TAKE_PROFIT, BUTT_PROFIT_WITHDRAWAL, BUTT_TRAILING_ALL }; #define TOTAL_BUTT (20) //--- structures struct SDataButt { string name; string text; }; //--- input variables input ushort InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 150; // StopLoss in points input uint InpTakeProfit = 150; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpDistancePReq = 50; // Distance for Pending Request's activate (points) input uint InpBarsDelayPReq = 5; // Bars delay for Pending Request's activate (current timeframe) input uint InpSlippage = 5; // Slippage in points input uint InpSpreadMultiplier = 1; // Spread multiplier for adjusting stop-orders by StopLevel input uchar InpTotalAttempts = 5; // Number of trading attempts sinput double InpWithdrawal = 10; // Withdrawal funds (in tester) sinput uint InpButtShiftX = 0; // Buttons X shift sinput uint InpButtShiftY = 10; // Buttons Y shift input uint InpTrailingStop = 50; // Trailing Stop (points) input uint InpTrailingStep = 20; // Trailing Step (points) input uint InpTrailingStart = 0; // Trailing Start (points) input uint InpStopLossModify = 20; // StopLoss for modification (points) input uint InpTakeProfitModify = 60; // TakeProfit for modification (points) sinput ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; // Mode of used symbols list sinput string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) sinput bool InpUseSounds = true; // Use sounds //--- global variables CEngine engine; CSeries series; CSeries series_m1; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ushort magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint distance_pending_request; uint bars_delay_pending_request; uint slippage; bool trailing_on; bool pressed_pending_buy; bool pressed_pending_buy_limit; bool pressed_pending_buy_stop; bool pressed_pending_buy_stoplimit; bool pressed_pending_close_buy; bool pressed_pending_close_buy2; bool pressed_pending_close_buy_by_sell; bool pressed_pending_sell; bool pressed_pending_sell_limit; bool pressed_pending_sell_stop; bool pressed_pending_sell_stoplimit; bool pressed_pending_close_sell; bool pressed_pending_close_sell2; bool pressed_pending_close_sell_by_buy; bool pressed_pending_delete_all; bool pressed_pending_close_all; bool pressed_pending_sl; bool pressed_pending_tp; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; int used_symbols_mode; string used_symbols; string array_used_symbols[]; bool testing; uchar group1; uchar group2; double g_point; int g_digits; //+------------------------------------------------------------------+
В обработчике OnInit() советника установим нужные свойства для обеих переменных объектов-таймсерий, и сразу же выведем всю информацию по созданному списку объектов-баров текущего таймфрейма. Для таймфрейма М1 просто выведем сообщение об успешном создании списка:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Вызов данной функции выводит в журнал список констант перечисления, //--- заданного в файле DELib.mqh в строках 22 и 25, для проверки корректности констант //EnumNumbersTest(); //--- Установка глобальных переменных советника prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_"; testing=engine.IsTester(); for(int i=0;i<TOTAL_BUTT;i++) { butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i); butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i); } lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0)); magic_number=InpMagic; stoploss=InpStopLoss; takeprofit=InpTakeProfit; distance_pending=InpDistance; distance_stoplimit=InpDistanceSL; slippage=InpSlippage; trailing_stop=InpTrailingStop*Point(); trailing_step=InpTrailingStep*Point(); trailing_start=InpTrailingStart; stoploss_to_modify=InpStopLossModify; takeprofit_to_modify=InpTakeProfitModify; distance_pending_request=(InpDistancePReq<5 ? 5 : InpDistancePReq); bars_delay_pending_request=(InpBarsDelayPReq<1 ? 1 : InpBarsDelayPReq); g_point=SymbolInfoDouble(NULL,SYMBOL_POINT); g_digits=(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS); //--- Инициализация случайных номеров групп group1=0; group2=0; srand(GetTickCount()); //--- Инициализация библиотеки DoEasy OnInitDoEasy(); //--- Проверка и удаление неудалённых графических объектов советника if(IsPresentObects(prefix)) ObjectsDeleteAll(0,prefix); //--- Создание панели кнопок if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- Установка состояния кнопки активизации трейлингов ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on); //--- Сброс состояний кнопок активизации работы отложенными запросами for(int i=0;i<14;i++) { ButtonState(butt_data[i].name+"_PRICE",false); ButtonState(butt_data[i].name+"_TIME",false); } //--- Проверка воспроизведения стандартного звука по макроподстановке и пользовательского звука по описанию engine.PlaySoundByDescription(SND_OK); Sleep(600); engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","The sound of a falling coin 2")); //--- Установка параметров объекта-таймсерии М1 series_m1.SetSymbolPeriod(Symbol(),PERIOD_M1); //--- Если данные по символу и минутному таймфрейму синхронизированы if(series_m1.SyncData(2,0)) { //--- создадим список-таймсерию из двух баров (текущий и прошлый), //--- и если таймсерия создана, выведем об этом сообщение в журнал int total=series_m1.Create(2); if(total>0) Print(TextByLanguage("Создана таймсерия М1 с размером ","Created timeseries M1 with size "),(string)total); } //--- Проверка заполнения ценовых данных по текущему символу и таймфрейму series.SetSymbolPeriod(Symbol(),(ENUM_TIMEFRAMES)Period()); //--- Если данные по символу и текущему таймфрейму синхронизированы if(series.SyncData(10,0)) { //--- создадим список-таймсерию из десяти баров (бары 0 - 9), //--- и если таймсерия создана, выведем три списка: //--- 1. список баров, отсортированных по размеру свечей (от High до Low баров) //--- 2. список баров, отсортированных по индексам баров (в порядке следования их в таймсерии) //--- 3. полный список всех свойств предыдущего объекта-бара (свойства бара с индексом таймсерии 1) int total=series.Create(10); if(total>0) { CArrayObj *list=series.GetList(); CBar *bar=NULL; //--- Вывод кратких свойств списка баров по размеру свечи Print("\n",TextByLanguage("Бары, сортированные по размеру свечи от High до Low:","Bars, sorted by size candle from High to Low:")); list.Sort(SORT_BY_BAR_CANDLE_SIZE); for(int i=0;i<total;i++) { bar=series.GetBarByListIndex(i); if(bar==NULL) continue; bar.PrintShort(); } //--- Вывод кратких свойств списка баров по индексу в таймсерии Print("\n",TextByLanguage("Бары, сортированные по индексу таймсерии:","Bars, sorted by timeseries index:")); list.Sort(SORT_BY_BAR_INDEX); for(int i=0;i<total;i++) { bar=series.GetBarByListIndex(i); if(bar==NULL) continue; bar.PrintShort(); } //--- Вывод всех свойств бара 1 Print(""); list=CSelect::ByBarProperty(list,BAR_PROP_INDEX,1,EQUAL); if(list.Total()==1) { bar=list.At(0); bar.Print(); } } } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
В обработчике OnTick() на каждом тике будем обновлять списки-таймсерии объектов класса CSeries, и на событии "Новый бар" для каждого из двух списков будем выводить сообщения об этом событии. Дополнительно проиграем звук на событии открытия нового бара на текущем таймфрейме:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Если работа в тестере if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(); // Работа в таймере PressButtonsControl(); // Контроль нажатия кнопок EventsHandling(); // Работа с событиями } //--- Если установлен флаг трейлинга if(trailing_on) { TrailingPositions(); // Трейлинг позиций TrailingOrders(); // Трейлинг отложенных ордеров } //--- Проверка обновления текущей таймсерии и таймсерии М1 series.Refresh(); if(series.IsNewBar(0)) { Print("New bar on ",series.Symbol()," ",TimeframeDescription(series.Period())," ",TimeToString(series.Time(0))); engine.PlaySoundByDescription(SND_NEWS); } series_m1.Refresh(); if(series_m1.IsNewBar(0)) { Print("New bar on ",series_m1.Symbol()," ",TimeframeDescription(series_m1.Period())," ",TimeToString(series_m1.Time(0))); } } //+------------------------------------------------------------------+
Скомпилируем советник и запустим его на графике символа.
В журнал будут выведены записи сначала
о создании списка-таймсерии для таймфрейма М1 в количестве двух баров, затем
будет выведен список баров, сортированный по размеру свечей, далее список
баров, сортированный в порядке индексов баров в таймсерии, и наконец — все
свойства объекта-бара с индексом 1 в таймсерии:
Account 15585535: Artyom Trishkin (MetaQuotes Software Corp.) 9999.40 USD, 1:100, Demo account MetaTrader 5 Work only with the current symbol. The number of symbols used: 1 Created timeseries M1 with size 2 Bars, sorted by size candle from High to Low: Bar "EURUSD" H1[2]: 2020.02.12 10:00:00, O: 1.09145, H: 1.09255, L: 1.09116, C: 1.09215, V: 2498, Bullish bar Bar "EURUSD" H1[3]: 2020.02.12 09:00:00, O: 1.09057, H: 1.09150, L: 1.09022, C: 1.09147, V: 1773, Bullish bar Bar "EURUSD" H1[1]: 2020.02.12 11:00:00, O: 1.09215, H: 1.09232, L: 1.09114, C: 1.09202, V: 1753, Bearish bar Bar "EURUSD" H1[9]: 2020.02.12 03:00:00, O: 1.09130, H: 1.09197, L: 1.09129, C: 1.09183, V: 1042, Bullish bar Bar "EURUSD" H1[4]: 2020.02.12 08:00:00, O: 1.09108, H: 1.09108, L: 1.09050, C: 1.09057, V: 581, Bearish bar Bar "EURUSD" H1[8]: 2020.02.12 04:00:00, O: 1.09183, H: 1.09197, L: 1.09146, C: 1.09159, V: 697, Bearish bar Bar "EURUSD" H1[5]: 2020.02.12 07:00:00, O: 1.09122, H: 1.09143, L: 1.09096, C: 1.09108, V: 591, Bearish bar Bar "EURUSD" H1[6]: 2020.02.12 06:00:00, O: 1.09152, H: 1.09159, L: 1.09121, C: 1.09122, V: 366, Bearish bar Bar "EURUSD" H1[7]: 2020.02.12 05:00:00, O: 1.09159, H: 1.09177, L: 1.09149, C: 1.09152, V: 416, Bearish bar Bar "EURUSD" H1[0]: 2020.02.12 12:00:00, O: 1.09202, H: 1.09204, L: 1.09181, C: 1.09184, V: 63, Bearish bar Bars, sorted by timeseries index: Bar "EURUSD" H1[9]: 2020.02.12 03:00:00, O: 1.09130, H: 1.09197, L: 1.09129, C: 1.09183, V: 1042, Bullish bar Bar "EURUSD" H1[8]: 2020.02.12 04:00:00, O: 1.09183, H: 1.09197, L: 1.09146, C: 1.09159, V: 697, Bearish bar Bar "EURUSD" H1[7]: 2020.02.12 05:00:00, O: 1.09159, H: 1.09177, L: 1.09149, C: 1.09152, V: 416, Bearish bar Bar "EURUSD" H1[6]: 2020.02.12 06:00:00, O: 1.09152, H: 1.09159, L: 1.09121, C: 1.09122, V: 366, Bearish bar Bar "EURUSD" H1[5]: 2020.02.12 07:00:00, O: 1.09122, H: 1.09143, L: 1.09096, C: 1.09108, V: 591, Bearish bar Bar "EURUSD" H1[4]: 2020.02.12 08:00:00, O: 1.09108, H: 1.09108, L: 1.09050, C: 1.09057, V: 581, Bearish bar Bar "EURUSD" H1[3]: 2020.02.12 09:00:00, O: 1.09057, H: 1.09150, L: 1.09022, C: 1.09147, V: 1773, Bullish bar Bar "EURUSD" H1[2]: 2020.02.12 10:00:00, O: 1.09145, H: 1.09255, L: 1.09116, C: 1.09215, V: 2498, Bullish bar Bar "EURUSD" H1[1]: 2020.02.12 11:00:00, O: 1.09215, H: 1.09232, L: 1.09114, C: 1.09202, V: 1753, Bearish bar Bar "EURUSD" H1[0]: 2020.02.12 12:00:00, O: 1.09202, H: 1.09204, L: 1.09181, C: 1.09184, V: 63, Bearish bar ============= The beginning of the event parameter list (Bar "EURUSD" H1[1]) ============= Timeseries index: 1 Type: Bearish bar Timeframe: H1 Spread: 1 Tick volume: 1753 Real volume: 0 Period start time: 2020.02.12 11:00:00 Sequence day number in the year: 042 Year: 2020 Month: February Day of week: Wednesday Day od month: 12 Hour: 11 Minute: 00 ------ Price open: 1.09215 Highest price for the period: 1.09232 Lowest price for the period: 1.09114 Price close: 1.09202 Candle size: 0.00118 Candle body size: 0.00013 Top of the candle body: 1.09215 Bottom of the candle body: 1.09202 Candle upper shadow size: 0.00017 Candle lower shadow size: 0.00088 ------ Symbol: "EURUSD" ============= End of the parameter list (Bar "EURUSD" H1[1]) =============
Теперь запустим советник в визуальном режиме тестера на таймфрейме М5 и посмотрим на сообщения журнала тестера об открытии новых баров:
Как видим каждое пятое сообщение — об открытии нового бара на М5, а между ними — сообщения об открытии нового бара на М1.
Что дальше
В следующей статье создадим класс-коллекцию списков баров.
Ниже прикреплены все файлы текущей версии библиотеки и файлы тестового советника. Их можно скачать и протестировать всё
самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.
Статьи предыдущей серии:
Часть 1. Концепция, организация данных
Часть
2. Коллекция исторических ордеров и сделок
Часть 3. Коллекция рыночных ордеров и
позиций, организация поиска
Часть 4. Торговые события. Концепция
Часть 5. Классы и коллекция торговых событий. Отправка событий в программу
Часть 6. События на счёте с типом неттинг
Часть
7. События срабатывания StopLimit-ордеров, подготовка функционала для регистрации событий модификации ордеров и позиций
Часть
8. События модификации ордеров и позиций
Часть 9. Совместимость с MQL4 —
Подготовка данных
Часть 10. Совместимость с MQL4 — События открытия позиций и
активации отложенных ордеров
Часть 11. Совместимость с MQL4 — События закрытия
позиций
Часть 12. Класс объекта "аккаунт", коллекция объектов-аккаунтов
Часть 13. События объекта "аккаунт"
Часть
14. Объект "Символ"
Часть 15. Коллекция объектов-символов
Часть
16. События коллекции символов
Часть 17. Интерактивность объектов библиотеки
Часть 18. Интерактивность объекта-аккаунт и любых других объектов библиотеки
Часть
19. Класс сообщений библиотеки
Часть 20. Создание и хранение ресурсов программы
Часть 21. Торговые классы — Базовый кроссплатформенный торговый объект
Часть 22. Торговые классы — Основной торговый класс, контроль ограничений
Часть 23. Торговые классы — Основной торговый класс, контроль допустимых параметров
Часть 24. Торговые классы — Основной торговый класс, автоматическая коррекция ошибочных
параметров
Часть 25. Торговые классы — Основной торговый класс, обработка
ошибок, возвращаемых торговым сервером
Часть 26. Работа с отложенными торговыми
запросами - первая реализация (открытие позиций)
Часть 27. Работа с отложенными
торговыми запросами - выставление отложенных ордеров
Часть 28. Работа с
отложенными торговыми запросами - закрытие, удаление, модификации
Часть 29.
Работа с отложенными торговыми запросами - классы объектов-запросов
Часть 30.
Работа с отложенными запросами - управление объектами-запросами
Часть 31.
Работа с отложенными запросами - открытие позиций по условиям
Часть 32. Работа с
отложенными запросами - установка отложенных ордеров по условиям
Часть 33.
Работа с отложенными запросами - закрытие позиций (полное, частичное и встречное) по условиям
Часть
34. Работа с отложенными запросами - удаление ордеров, модификация ордеров и позиций по условиям





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Как-то некошерно иметь новый класс CSeries, когда есть одноимённый в СБ.
Как-то некошерно иметь новый класс CSeries, когда есть одноимённый в СБ.
Согласен. Спасибо, поправлю.
Очень важным параметром каждого бара является время цена High и время цена Low, или по крайней мере флаг, указывающий какой из двух цен High или Low произошел первым во времени. Это необходимо для организации правильного зигзага. Эта информация может быть получена путем анализа баров с более низкого таймфрейма или непосредственно из M1. Прошу добавить такой функционал.
Очень важным параметром каждого бара является время цена High и время цена Low, или по крайней мере флаг, указывающий какой из двух цен High или Low произошел первым во времени. Это необходимо для организации правильного зигзага. Эта информация может быть получена путем анализа баров с более низкого таймфрейма или непосредственно из M1. Прошу добавить такой функционал.
Такой функционал можно сделать самостоятельно - есть все данные по младшим таймфреймам, и есть все данные по их барам. Это не последняя статья, и в последующих есть полный список всех таймсерий любого символа - так что лишним функционалом не стоит перегружать тот, что уже есть. Тем более, что это вызовет увеличение времени обновления таймсерий. Поэтому лучше получать данные из уже имеющихся по запросу, чем делать такой анализ там, где он не требуется.