Работа с ценами в библиотеке DoEasy (Часть 59): Объект для хранения данных одного тика
Содержание
Концепция
С этой статьи начнём разработку функционала библиотеки для работы с тиковыми данными.
Концепция хранения и использования тиковых данных будет похожа на концепцию хранения данных таймсерий, где минимальной единицей данных является бар. Объекты-бары хранятся в списках, принадлежащих соответствующим таймфреймам, которые в свою очередь хранятся в списках, принадлежащих символам.
В концепции хранения тиковых данных минимальной величиной объёма данных будут значения структуры цены на одном тике. Такие значения описываются при помощи структуры для хранения последних цен по символу MqlTick. Объект для хранения таких значений будет иметь в своём составе дополнительные свойства: спред — разница цен Ask и Bid и символ, данные одного тика которого описываются объектом.
Для каждого из символов будут создаваться свои списки объектов с тиковыми данными — для большего удобства структуризации списков. Естественно, каждый из списков объектов тиковых данных будет автоматически обновляться, в него будут добавляться новые данные вновь поступающих тиков, а размер списков будет поддерживаться в определённом пользователем библиотеки объёме.
Концепцией хранения данных в библиотеке предусмотрен поиск и сортировка по любым из свойств объектов, хранящихся в списках, что позволяет получать требуемые данные для последующего их использования или проведения аналитических исследований. А значит, и тиковые данные будут наделены такой же возможностью. Это позволит пользователю быстро анализировать тиковые потоки данных для отслеживания, например, каких-либо изменений в характере их поступления, или для поиска заданных паттернов, и т.д.
Подготовка данных
Как и для любых объектов библиотеки, нам необходимо добавить текстовые сообщения для вывода описания их параметров и создать перечисления свойств объекта, по которым будет возможно искать их в списках или сортировать объекты по этим свойствам.
В файл \MQL5\Include\DoEasy\Data.mqh впишем индексы новых сообщений:
//--- CSeriesDataInd MSG_LIB_TEXT_METHOD_NOT_FOR_INDICATORS, // Метод не предназначен для работы с программами-индикаторами MSG_LIB_TEXT_IND_DATA_FAILED_GET_SERIES_DATA, // Не удалось получить таймсерию индикаторных данных MSG_LIB_TEXT_IND_DATA_FAILED_GET_CURRENT_DATA, // Не удалось получить текущие данные буфера индикатора MSG_LIB_SYS_FAILED_CREATE_IND_DATA_OBJ, // Не удалось создать объект индикаторных данных MSG_LIB_TEXT_IND_DATA_FAILED_ADD_TO_LIST, // Не удалось добавить объект индикаторных данных в список //--- CTick MSG_TICK_TEXT_TICK, // Тик MSG_TICK_TIME_MSC, // Время последнего обновления цен в миллисекундах MSG_TICK_TIME, // Время последнего обновления цен MSG_TICK_VOLUME, // Объем для текущей цены Last MSG_TICK_FLAGS, // Флаги MSG_TICK_VOLUME_REAL, // Объем для текущей цены Last c повышенной точностью MSG_TICK_SPREAD, // Спред MSG_LIB_TEXT_TICK_CHANGED_DATA, // Изменённые данные на тике: MSG_LIB_TEXT_TICK_FLAG_BID, // Изменение цены Bid MSG_LIB_TEXT_TICK_FLAG_ASK, // Изменение цены Ask MSG_LIB_TEXT_TICK_FLAG_LAST, // Изменение цены последней сделки MSG_LIB_TEXT_TICK_FLAG_VOLUME, // Изменение объема }; //+------------------------------------------------------------------+
и тексты сообщений, соответствующие вновь добавленным индексам:
//--- CSeriesDataInd {"Метод не предназначен для работы с программами-индикаторами","The method is not intended for working with indicator programs"}, {"Не удалось получить таймсерию индикаторных данных","Failed to get indicator data timeseries"}, {"Не удалось получить текущие данные буфера индикатора","Failed to get the current data of the indicator buffer"}, {"Не удалось создать объект индикаторных данных","Failed to create indicator data object"}, {"Не удалось добавить объект индикаторных данных в список","Failed to add indicator data object to the list"}, //--- CTick {"Тик","Tick"}, {"Время последнего обновления цен в миллисекундах","Last price update time in milliseconds"}, {"Время последнего обновления цен","Last price update time"}, {"Объем для текущей цены Last","Volume for the current Last price"}, {"Флаги","Flags"}, {"Объем для текущей цены Last c повышенной точностью","Volume for the current \"Last\" price with increased accuracy"}, {"Спред","Spread"}, {"Изменённые данные на тике:","Changed data on a tick:"}, {"Изменение цены Bid","Bid price change"}, {"Изменение цены Ask","Ask price change"}, {"Изменение цены последней сделки","Last price change"}, {"Изменение объема","Volume change"}, }; //+---------------------------------------------------------------------+
В файл \MQL5\Include\DoEasy\Defines.mqh впишем перечисления для указания целочисленных, вещественных и строковых свойств объекта тиковых данных:
//+------------------------------------------------------------------+ //| Данные для работы с тиковыми данными | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Целочисленные свойства тика | //+------------------------------------------------------------------+ enum ENUM_TICK_PROP_INTEGER { TICK_PROP_TIME_MSC = 0, // Время последнего обновления цен в миллисекундах TICK_PROP_TIME, // Время последнего обновления TICK_PROP_VOLUME, // Объем для текущей цены Last TICK_PROP_FLAGS, // Флаги тика }; #define TICK_PROP_INTEGER_TOTAL (4) // Общее количество целочисленных свойств тика #define TICK_PROP_INTEGER_SKIP (0) // Количество неиспользуемых в сортировке свойств тика //+------------------------------------------------------------------+ //| Вещественные свойства тика | //+------------------------------------------------------------------+ enum ENUM_TICK_PROP_DOUBLE { TICK_PROP_BID = TICK_PROP_INTEGER_TOTAL, // Цена Bid тика TICK_PROP_ASK, // Цена Ask тика TICK_PROP_LAST, // Текущая цена последней сделки (Last) TICK_PROP_VOLUME_REAL, // Объем для текущей цены Last c повышенной точностью TICK_PROP_SPREAD, // Спред тика (Ask - Bid) }; #define TICK_PROP_DOUBLE_TOTAL (5) // Общее количество вещественных свойств тика #define TICK_PROP_DOUBLE_SKIP (0) // Количество неиспользуемых в сортировке свойств тика //+------------------------------------------------------------------+ //| Строковые свойства тика | //+------------------------------------------------------------------+ enum ENUM_TICK_PROP_STRING { TICK_PROP_SYMBOL = (TICK_PROP_INTEGER_TOTAL+TICK_PROP_DOUBLE_TOTAL), // Символ тика }; #define TICK_PROP_STRING_TOTAL (1) // Общее количество строковых свойств тика //+------------------------------------------------------------------+
Для каждого из перечислений укажем общее количество используемых и неиспользуемых в сортировке свойств.
Для возможности поиска и сортировки по перечисленным выше свойствам добавим ещё одно перечисление, где будут указаны возможные критерии сортировки тиковых данных:
//+------------------------------------------------------------------+ //| Возможные критерии сортировки тиков | //+------------------------------------------------------------------+ #define FIRST_TICK_DBL_PROP (TICK_PROP_INTEGER_TOTAL-TICK_PROP_INTEGER_SKIP) #define FIRST_TICK_STR_PROP (TICK_PROP_INTEGER_TOTAL-TICK_PROP_INTEGER_SKIP+TICK_PROP_DOUBLE_TOTAL-TICK_PROP_DOUBLE_SKIP) enum ENUM_SORT_TICK_MODE { //--- Сортировка по целочисленным свойствам SORT_BY_TICK_TIME_MSC = 0, // Сортировать по времени последнего обновления цен в миллисекундах SORT_BY_TICK_TIM, // Сортировать по времени последнего обновления цен SORT_BY_TICK_VOLUME, // Сортировать по объему для текущей цены Last SORT_BY_TICK_FLAGS, // Сортировать по флагам тика //--- Сортировка по вещественным свойствам SORT_BY_TICK_BID = FIRST_TICK_DBL_PROP, // Сортировать по цене Bid тика SORT_BY_TICK_ASK, // Сортировать по цене Ask тика SORT_BY_TICK_LAST, // Сортировать по текущей цене последней сделки (Last) SORT_BY_TICK_VOLUME_REAL, // Сортировать по объему для текущей цены Last c повышенной точностью SORT_BY_TICK_SPREAD, // Сортировать по спреду тика //--- Сортировка по строковым свойствам SORT_BY_TICK_SYMBOL = FIRST_TICK_STR_PROP, // Сортировать по символу тика }; //+------------------------------------------------------------------+
Ранее мы уже создавали объект "Новый тик" в статье 38, позволяющий отслеживать поступление нового тика на указанном символе.
Объект расположен в папке \MQL5\Include\DoEasy\Objects\Ticks\ каталога библиотеки в файле NewTickObj.mqh.
Доработаем класс объекта так, чтобы мы могли передавать в метод установки символа значение NULL (или пустую строку) для указания текущего символа объекту:
//--- Устанавливает символ void SetSymbol(const string symbol) { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); }
И в конструкторе класса, в его списке инициализации установим значение false для переменной m_new_tick, хранящей флаг нового тика:
//+------------------------------------------------------------------+ //| Параметрический конструктор CNewTickObj | //+------------------------------------------------------------------+ CNewTickObj::CNewTickObj(const string symbol) : m_symbol(symbol),m_new_tick(false) { //--- Обнуляем структуры нового и прошлого тиков ::ZeroMemory(this.m_tick); ::ZeroMemory(this.m_tick_prev); //--- Если удалось получить текущие цены в структуру тика - //--- копируем данные полученного тика в данные прошлого тика и сбрасываем флаг первого запуска if(::SymbolInfoTick(this.m_symbol,this.m_tick)) { this.m_tick_prev=this.m_tick; this.m_first_start=false; } } //+------------------------------------------------------------------+
Ранее эта переменная нигде не инициализировалась начальным значением, что неправильно.
Класс объекта данных тика
В папке расположения класса объекта "Новый тик" \MQL5\Include\DoEasy\Objects\Ticks\ создадим новый файл класса объекта тиковых данных DataTick.mqh.
Объект не будет из себя представлять чего-то нового для классов библиотеки — всё стандартно. Рассмотрим тело класса:
//+------------------------------------------------------------------+ //| DataTick.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 "..\BaseObj.mqh" #include "..\..\Services\DELib.mqh" //+------------------------------------------------------------------+ //| Класс "Тик" | //+------------------------------------------------------------------+ class CDataTick : public CBaseObj { private: MqlTick m_tick; // Структура для получения текущих цен int m_digits; // Значение Digits символа long m_long_prop[TICK_PROP_INTEGER_TOTAL]; // Целочисленные свойства double m_double_prop[TICK_PROP_DOUBLE_TOTAL]; // Вещественные свойства string m_string_prop[TICK_PROP_STRING_TOTAL]; // Строковые свойства //--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство тика int IndexProp(ENUM_TICK_PROP_DOUBLE property) const { return(int)property-TICK_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_TICK_PROP_STRING property) const { return(int)property-TICK_PROP_INTEGER_TOTAL-TICK_PROP_DOUBLE_TOTAL;} public: //--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство тика void SetProperty(ENUM_TICK_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_TICK_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_TICK_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство тика long GetProperty(ENUM_TICK_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_TICK_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_TICK_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Возвращает флаг поддержания тиком данного свойства virtual bool SupportProperty(ENUM_TICK_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_TICK_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_TICK_PROP_STRING property) { return true; } //--- Возвращает себя CDataTick *GetObject(void) { return &this;} //--- Сравнивает объекты CDataTick между собой по указанному свойству (для сортировки списков по указанному свойству объекта) virtual int Compare(const CObject *node,const int mode=0) const; //--- Сравнивает объекты CDataTick между собой по всем свойствам (для поиска равных объектов) bool IsEqual(CDataTick* compared_obj) const; //--- Конструкторы CDataTick(){;} CDataTick(const string symbol,const MqlTick &tick); //+------------------------------------------------------------------+ //| Описания свойств объекта-тиковых данных | //+------------------------------------------------------------------+ //--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства тика string GetPropertyDescription(ENUM_TICK_PROP_INTEGER property); string GetPropertyDescription(ENUM_TICK_PROP_DOUBLE property); string GetPropertyDescription(ENUM_TICK_PROP_STRING property); //--- Выводит в журнал описание свойств тика (full_prop=true - все свойства, false - только поддерживаемые) void Print(const bool full_prop=false); //--- Выводит в журнал краткое описание тика virtual void PrintShort(void); //--- Возвращает (1) краткое наименование, (2) описание флагов объекта тиковых данных virtual string Header(void); string FlagsDescription(void); //+------------------------------------------------------------------+ //| Методы упрощённого доступа к свойствам объекта тиковых данных | //+------------------------------------------------------------------+ //--- Возвращает (1) Время (2) время в милисекундах, (3) объём, (4) флаги тика datetime Time(void) const { return (datetime)this.GetProperty(TICK_PROP_TIME); } long TimeMSC(void) const { return this.GetProperty(TICK_PROP_TIME_MSC); } long Volume(void) const { return this.GetProperty(TICK_PROP_VOLUME); } uint Flags(void) const { return (uint)this.GetProperty(TICK_PROP_FLAGS); } //--- Возвращает цену тика (1) Bid, (2) Ask, (3) Last, (4) объем c повышенной точностью, (5) спред тика //--- размер (9) верхней, (10) нижней тени свечи double Bid(void) const { return this.GetProperty(TICK_PROP_BID); } double Ask(void) const { return this.GetProperty(TICK_PROP_ASK); } double Last(void) const { return this.GetProperty(TICK_PROP_LAST); } double VolumeReal(void) const { return this.GetProperty(TICK_PROP_VOLUME_REAL); } double Spread(void) const { return this.GetProperty(TICK_PROP_SPREAD); } //--- Возвращает символ тика string Symbol(void) const { return this.GetProperty(TICK_PROP_SYMBOL); } //--- Возвращает (1) время, (2) индекс бара на указанном таймфрейме, в который попадает время тика datetime TimeBar(const ENUM_TIMEFRAMES timeframe)const { return ::iTime(this.Symbol(),timeframe,this.Index(timeframe)); } int Index(const ENUM_TIMEFRAMES timeframe) const { return ::iBarShift(this.Symbol(),(timeframe==PERIOD_CURRENT ? ::Period() : timeframe),this.Time()); } //--- Возвращает флаг смены цены (1) Bid, (2) Ask, (3) Last, (4) объёма, сделка на (5) покупку, (6) продажу bool IsChangeBid() const { return((this.Flags() & TICK_FLAG_BID)==TICK_FLAG_BID); } bool IsChangeAsk() const { return((this.Flags() & TICK_FLAG_ASK)==TICK_FLAG_ASK); } bool IsChangeLast() const { return((this.Flags() & TICK_FLAG_LAST)==TICK_FLAG_LAST); } bool IsChangeVolume() const { return((this.Flags() & TICK_FLAG_VOLUME)==TICK_FLAG_VOLUME); } bool IsChangeBuy() const { return((this.Flags() & TICK_FLAG_BUY)==TICK_FLAG_BUY); } bool IsChangeSell() const { return((this.Flags( )& TICK_FLAG_SELL)==TICK_FLAG_SELL); } //--- }; //+------------------------------------------------------------------+
Подробно о составе объектов библиотеки и об их создании мы говорили в самой первой статье. Сейчас же лишь кратко пробежимся по основным переменным и методам, а далее рассмотрим их реализацию в классе.
В приватной секции класса расположены вспомогательные переменные, массивы для хранения значений целочисленных, вещественных и строковых свойств объекта, а также методы, возвращающие фактические индексы свойств, по которым они физически располагаются в массивах.
В публичной секции класса расположены методы для установки и возврата значений указанных свойств в соответствующие массивы свойств объекта, методы, возвращающие флаги поддержания объектом того, или иного свойства, методы для поиска, сравнения и сортировки объектов и конструкторы класса.
Там же расположены методы для вывода описаний свойств объекта и методы для упрощённого доступа к свойствам объекта.
Теперь рассмотрим реализацию методов класса.
В параметрический конструктор класса передаются символ и заполненная структура с данными текущего тика:
//+------------------------------------------------------------------+ //| Параметрический конструктор | //+------------------------------------------------------------------+ CDataTick::CDataTick(const string symbol,const MqlTick &tick) { //--- Сохраняем Digits символа this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS); //--- Сохраняем целочисленные свойства тика this.SetProperty(TICK_PROP_TIME,tick.time); this.SetProperty(TICK_PROP_TIME_MSC,tick.time_msc); this.SetProperty(TICK_PROP_VOLUME,tick.volume); this.SetProperty(TICK_PROP_FLAGS,tick.flags); //--- Сохраняем вещественные свойства тика this.SetProperty(TICK_PROP_BID,tick.bid); this.SetProperty(TICK_PROP_ASK,tick.ask); this.SetProperty(TICK_PROP_LAST,tick.last); this.SetProperty(TICK_PROP_VOLUME_REAL,tick.volume_real); //--- Сохраняем дополнительные свойства тика this.SetProperty(TICK_PROP_SPREAD,tick.ask-tick.bid); this.SetProperty(TICK_PROP_SYMBOL,(symbol==NULL || symbol=="" ? ::Symbol() : symbol)); } //+------------------------------------------------------------------+
Внутри конструктора мы просто заполняем свойства объекта соответствующими значениями из структуры тика и символ, с которого получены данные тика.
Метод сравнения двух параметров объектов по указанному свойству:
//+------------------------------------------------------------------+ //| Сравнивает объекты CDataTick между собой по указанному свойству | //+------------------------------------------------------------------+ int CDataTick::Compare(const CObject *node,const int mode=0) const { const CDataTick *obj_compared=node; //--- сравнение целочисленных свойств двух объектов if(mode<TICK_PROP_INTEGER_TOTAL) { long value_compared=obj_compared.GetProperty((ENUM_TICK_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_TICK_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- сравнение вещественных свойств двух объектов else if(mode<TICK_PROP_DOUBLE_TOTAL+TICK_PROP_INTEGER_TOTAL) { double value_compared=obj_compared.GetProperty((ENUM_TICK_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_TICK_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- сравнение строковых свойств двух объектов else if(mode<TICK_PROP_DOUBLE_TOTAL+TICK_PROP_INTEGER_TOTAL+TICK_PROP_STRING_TOTAL) { string value_compared=obj_compared.GetProperty((ENUM_TICK_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_TICK_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+
В метод передаётся указатель на объект, с которым нужно сравнить текущий, и тип свойства, по которому два объекта будут сравниваться.
В зависимости от переданного режима сравнения объектов сравниваем эти свойства у двух объектов и возвращаем 1/-1/0, если у текущего объекта значение его свойства больше/меньше или равно значению свойства сравниваемого объекта, соответственно.
Метод для сравнения двух объектов на идентичность:
//+------------------------------------------------------------------+ //| Сравнивает объекты CDataTick между собой по всем свойствам | //+------------------------------------------------------------------+ bool CDataTick::IsEqual(CDataTick *compared_obj) const { int beg=0, end=TICK_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_TICK_PROP_INTEGER prop=(ENUM_TICK_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=TICK_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_TICK_PROP_DOUBLE prop=(ENUM_TICK_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=TICK_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_TICK_PROP_STRING prop=(ENUM_TICK_PROP_STRING)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
Здесь: в метод передаётся указатель на объект для сравнения. Затем в трёх циклах по каждой группе свойств сравниваем каждое очередное свойство двух объектов. Если хоть одно из свойств не равно такому же у сравниваемого объекта, то возвращается false. По окончании всех трёх циклов возвращается true — все свойства двух объектов равны, а значит — объекты идентичны.
Метод для вывода в журнал всех свойств объекта:
//+------------------------------------------------------------------+ //| Выводит в журнал свойства тика | //+------------------------------------------------------------------+ void CDataTick::Print(const bool full_prop=false) { ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") ============="); int beg=0, end=TICK_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_TICK_PROP_INTEGER prop=(ENUM_TICK_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=TICK_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_TICK_PROP_DOUBLE prop=(ENUM_TICK_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=TICK_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_TICK_PROP_STRING prop=(ENUM_TICK_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"); } //+------------------------------------------------------------------+
Здесь: в трёх циклах по каждой из групп свойств объекта выводится в журнал описание каждого очередного свойства при помощи соответствующего метода GetPropertyDescription(). Если в параметрах метода передано значение false в переменной full_prop, то выводятся только поддерживаемые объектом свойства. Для неподдерживаемого свойства в журнал пишется, что свойство не поддерживается, хотя в данном объекте поддерживаются все свойства, но это можно изменить в объектах-наследниках класса.
Методы, возвращающие описание указанного целочисленного, вещественного и строкового свойства объекта:
//+------------------------------------------------------------------+ //| Возвращает описание целочисленного свойства тика | //+------------------------------------------------------------------+ string CDataTick::GetPropertyDescription(ENUM_TICK_PROP_INTEGER property) { return ( property==TICK_PROP_TIME_MSC ? CMessage::Text(MSG_TICK_TIME_MSC)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+TimeMSCtoString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : property==TICK_PROP_TIME ? CMessage::Text(MSG_TICK_TIME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : property==TICK_PROP_VOLUME ? CMessage::Text(MSG_TICK_VOLUME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==TICK_PROP_FLAGS ? CMessage::Text(MSG_TICK_FLAGS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property)+"\n"+CMessage::Text(MSG_LIB_TEXT_TICK_CHANGED_DATA)+this.FlagsDescription() ) : "" ); } //+------------------------------------------------------------------+ //| Возвращает описание вещественного свойства тика | //+------------------------------------------------------------------+ string CDataTick::GetPropertyDescription(ENUM_TICK_PROP_DOUBLE property) { int dg=(this.m_digits>0 ? this.m_digits : 1); return ( property==TICK_PROP_BID ? CMessage::Text(MSG_LIB_PROP_BID)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==TICK_PROP_ASK ? CMessage::Text(MSG_LIB_PROP_ASK)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==TICK_PROP_LAST ? CMessage::Text(MSG_LIB_PROP_LAST)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==TICK_PROP_VOLUME_REAL ? CMessage::Text(MSG_TICK_VOLUME_REAL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==TICK_PROP_SPREAD ? CMessage::Text(MSG_TICK_SPREAD)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : "" ); } //+------------------------------------------------------------------+ //| Возвращает описание строкового свойства тика | //+------------------------------------------------------------------+ string CDataTick::GetPropertyDescription(ENUM_TICK_PROP_STRING property) { return(property==TICK_PROP_SYMBOL ? CMessage::Text(MSG_LIB_PROP_SYMBOL)+": \""+this.GetProperty(property)+"\"" : ""); } //+------------------------------------------------------------------+
Здесь: в зависимости от переданного в метод свойства возвращается его строковое описание.
Метод, возвращающий строку с описанием всех флагов тика:
//+------------------------------------------------------------------+ //| Выводит описание флагов | //+------------------------------------------------------------------+ string CDataTick::FlagsDescription(void) { string flags= ( (this.IsChangeAsk() ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_ASK) : "")+ (this.IsChangeBid() ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_BID) : "")+ (this.IsChangeLast() ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_LAST) : "")+ (this.IsChangeVolume() ? "\n - "+CMessage::Text(MSG_LIB_TEXT_TICK_FLAG_VOLUME) : "")+ (this.IsChangeBuy() ? "\n - "+CMessage::Text(MSG_DEAL_TO_BUY) : "")+ (this.IsChangeSell() ? "\n - "+CMessage::Text(MSG_DEAL_TO_SELL) : "") ); return flags; } //+------------------------------------------------------------------+
У каждого тика в его свойствах есть переменная, хранящая набор флагов. Эти флаги описывают события, повлёкшие за собой этот тик:
Из справки по MqlTick
Чтобы узнать, какие именно данные изменились с текущим тиком, анализируйте его флаги:
- TICK_FLAG_BID — тик изменил цену бид
- TICK_FLAG_ASK — тик изменил цену аск
- TICK_FLAG_LAST — тик изменил цену последней сделки
- TICK_FLAG_VOLUME — тик изменил объем
- TICK_FLAG_BUY — тик возник в результате сделки на покупку
- TICK_FLAG_SELL — тик возник в результате сделки на продажу
У нас есть шесть методов (по количеству флагов), возвращающих флаг наличия в составе переменной каждого из флагов:
//--- Возвращает флаг смены цены (1) Bid, (2) Ask, (3) Last, (4) объёма, сделка на (5) покупку, (6) продажу bool IsChangeBid() const { return((this.Flags() & TICK_FLAG_BID)==TICK_FLAG_BID); } bool IsChangeAsk() const { return((this.Flags() & TICK_FLAG_ASK)==TICK_FLAG_ASK); } bool IsChangeLast() const { return((this.Flags() & TICK_FLAG_LAST)==TICK_FLAG_LAST); } bool IsChangeVolume() const { return((this.Flags() & TICK_FLAG_VOLUME)==TICK_FLAG_VOLUME); } bool IsChangeBuy() const { return((this.Flags() & TICK_FLAG_BUY)==TICK_FLAG_BUY); } bool IsChangeSell() const { return((this.Flags() & TICK_FLAG_SELL)==TICK_FLAG_SELL); }
В описываемом методе сначала проверяется флаг наличия флага в составе переменной, и, в зависимости от того, есть такое изменение, или его нет, в результирующую текстовую строку вписывается перевод строки + описание флага, либо пустая строка. После проверки всех флагов в итоге имеем составленную строку, в которой содержатся описания присутствующих в переменной флагов. Если флагов несколько, то описание каждого флага будет начинаться с новой строки в журнале.
Метод, выводящий в журнал краткое описание объекта тиковых данных:
//+------------------------------------------------------------------+ //| Выводит в журнал краткое описание тика | //+------------------------------------------------------------------+ void CDataTick::PrintShort(void) { ::Print(this.Header()); } //+------------------------------------------------------------------+
Метод просто выводит в журнал краткое наименование самого себя, возвращаемое следующим методом:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование объекта тиковых данных | //+------------------------------------------------------------------+ string CDataTick::Header(void) { return ( CMessage::Text(MSG_TICK_TEXT_TICK)+" \""+this.Symbol()+"\" "+TimeMSCtoString(TimeMSC()) ); } //+------------------------------------------------------------------+
На этом создание объекта тиковых данных завершено.
Тестирование объекта данных тика
Для тестирования возьмём советник из прошлой статьи
и сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part59\ под новым именем TestDoEasyPart59.mq5.
В этом советнике нам уже не нужны индикаторы и их данные с их таймсериями — здесь мы просто создадим объект тиковых данных, выведем его полное описание в журнал и краткое описание в комментарии на графике. С каждым новым тиком его описание на графике будет меняться, тогда как в журнал мы выведем первый пришедший тик после запуска советника.
Так как пока ещё нет никакой связи советника с этим новым объектом библиотеки, то мы просто подключим файл его класса к советнику:
//+------------------------------------------------------------------+ //| TestDoEasyPart59.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> #include <DoEasy\Objects\Ticks\DataTick.mqh>
В области глобальных переменных советника вместо массивов параметров пользовательских индикаторов
//--- Массивы параметров пользовательских индикаторов MqlParam param_ma1[]; MqlParam param_ma2[]; //+------------------------------------------------------------------+
объявим объект класса "Новый тик" — он нам нужен будет для имитации работы в составе будущих классов-коллекций тиковых данных библиотеки:
//--- global variables CEngine engine; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ushort magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint distance_pending_request; uint bars_delay_pending_request; uint slippage; bool trailing_on; bool 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 array_used_symbols[]; string array_used_periods[]; bool testing; uchar group1; uchar group2; double g_point; int g_digits; //--- Объект "Новый тик" CNewTickObj check_tick; //+------------------------------------------------------------------+
В обработчике OnInit() удалим блок создания индикаторов:
//--- Создание индикаторов
ArrayResize(param_ma1,4);
//--- Имя индикатора 1
param_ma1[0].type=TYPE_STRING;
param_ma1[0].string_value="Examples\\Custom Moving Average.ex5";
//--- Период расчёта
param_ma1[1].type=TYPE_INT;
param_ma1[1].integer_value=13;
//--- Горизонтальное смещение
param_ma1[2].type=TYPE_INT;
param_ma1[2].integer_value=0;
//--- Метод сглаживания
param_ma1[3].type=TYPE_INT;
param_ma1[3].integer_value=MODE_SMA;
//--- Создаём индикатор 1
engine.GetIndicatorsCollection().CreateCustom(NULL,PERIOD_CURRENT,MA1,1,INDICATOR_GROUP_TREND,param_ma1);
ArrayResize(param_ma2,5);
//--- Имя индикатора 2
param_ma2[0].type=TYPE_STRING;
param_ma2[0].string_value="Examples\\Custom Moving Average.ex5";
//--- Период расчёта
param_ma2[1].type=TYPE_INT;
param_ma2[1].integer_value=13;
//--- Горизонтальное смещение
param_ma2[2].type=TYPE_INT;
param_ma2[2].integer_value=0;
//--- Метод сглаживания
param_ma2[3].type=TYPE_INT;
param_ma2[3].integer_value=MODE_SMA;
//--- Цена расчёта
param_ma2[4].type=TYPE_INT;
param_ma2[4].integer_value=PRICE_OPEN;
//--- Создаём индикатор 2
engine.GetIndicatorsCollection().CreateCustom(NULL,PERIOD_CURRENT,MA2,1,INDICATOR_GROUP_TREND,param_ma2);
//--- Создаём индикатор 3
engine.GetIndicatorsCollection().CreateAMA(NULL,PERIOD_CURRENT,AMA1);
//--- Создаём индикатор 4
engine.GetIndicatorsCollection().CreateAMA(NULL,PERIOD_CURRENT,AMA2,14);
//--- Выводим описания созданных индикаторов
engine.GetIndicatorsCollection().Print();
engine.GetIndicatorsCollection().PrintShort();
В самом конце OnInit() установим объекту "Новый тик" текущий символ в качестве рабочего:
//--- Установим текущий символ объекту "Новый тик" check_tick.SetSymbol(Symbol()); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
В обработчике OnTick() советника впишем блок кода для определения нового тика (в качестве имитации работы в составе будущего класса-коллекции тиков) и создания нового объекта тиковых данных с выводом его описания на график и в журнал:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Обработка события NewTick в библиотеке engine.OnTick(rates_data); //--- Если работа в тестере if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(rates_data); // Работа в таймере PressButtonsControl(); // Контроль нажатия кнопок engine.EventsHandling(); // Работа с событиями } //--- Создаём временный список для хранения объектов "Данные тика", //--- переменную для получения данных тика и //--- переменную для подсчёта поступающих тиков static int tick_count=0; CArrayObj list; MqlTick tick_struct; //--- Проверяем новый тик на текущем символе if(check_tick.IsNewTick()) { //--- Если цены получить не удалось - уходим if(!SymbolInfoTick(Symbol(),tick_struct)) return; //--- Создаём новый объект тиковых данных CDataTick *tick_obj=new CDataTick(Symbol(),tick_struct); if(tick_obj==NULL) return; //--- Увеличиваем счётчик тиков (просто для вывода на экран - другого смысла нету) tick_count++; //--- Ограничиваем количество тиков в подсчёте ста тысячами (тоже без какого-либо смысла) if(tick_count>100000) tick_count=1; //--- Выводим в комментарии на графике номер тика и его краткое описание Comment("--- №",IntegerToString(tick_count,5,'0'),": ",tick_obj.Header()); //--- Если это первый тик (следующий за первым запуском советника), выведем его полное описание в журнал if(tick_count==1) tick_obj.Print(); //--- Если созданный объект тиковых данных не удалось разместить в списке - удалим его if(!list.Add(tick_obj)) delete tick_obj; } //--- Если установлен флаг трейлинга if(trailing_on) { TrailingPositions(); // Трейлинг позиций TrailingOrders(); // Трейлинг отложенных ордеров } } //+------------------------------------------------------------------+
Вся логика подробно расписана в комментариях в листинге и, думаю, там всё понятно и просто.
Скомпилируем советник и запустим его на графике, предварительно выставив в настройках использовать текущие символ и таймфрейм. После запуска и прихода нового тика, в журнал будет выведено описание объекта тиковых данных (пришедшего тика):
Счёт 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10426.13 USD, 1:100, Hedge, Демонстрационный счёт MetaTrader 5 --- Инициализация библиотеки "DoEasy" --- Работа только с текущим символом: "EURUSD" Работа только с текущим таймфреймом: H1 Таймсерия символа EURUSD: - Таймсерия "EURUSD" H1: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 5153 Время инициализации библиотеки: 00:00:00.000 ============= Начало списка параметров (Тик "EURUSD" 2020.12.16 13:22:32.822) ============= Время последнего обновления цен в миллисекундах: 2020.12.16 13:22:32.822 Время последнего обновления цен: 2020.12.16 13:22:32 Объем для текущей цены Last: 0 Флаги: 6 Изменённые данные на тике: - Изменение цены Ask - Изменение цены Bid ------ Цена Bid: 1.21927 Цена Ask: 1.21929 Цена Last: 0.00000 Объем для текущей цены Last c повышенной точностью: 0.00 Спред: 0.00002 ------ Символ: "EURUSD" ============= Конец списка параметров (Тик "EURUSD" 2020.12.16 13:22:32.822) =============
а на графике с каждым новым тиком будет выводиться комментарий с его кратким описанием:
Что дальше
В следующей статье начнём создавать коллекцию тиковых данных для одного символа.
Ниже прикреплены все файлы текущей версии библиотеки и файл тестового советника для MQL5. Их можно скачать и протестировать всё самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.
Статьи этой серии:
Работа с таймсериями в библиотеке DoEasy (Часть 35): Объект "Бар" и список-таймсерия символа
Работа с таймсериями в библиотеке DoEasy (Часть 36): Объект таймсерий всех используемых периодов символа
Работа с таймсериями в библиотеке DoEasy (Часть 37): Коллекция таймсерий - база данных таймсерий по символам и периодам
Работа с таймсериями в библиотеке DoEasy (Часть 38): Коллекция таймсерий - реалтайм обновление и доступ к данным из программы
Работа с таймсериями в библиотеке DoEasy (Часть 39): Индикаторы на основе библиотеки - подготовка данных и события таймсерий
Работа с таймсериями в библиотеке DoEasy (Часть 40): Индикаторы на основе библиотеки - реалтайм обновление данных
Работа с таймсериями в библиотеке DoEasy (Часть 41): Пример мультисимвольного мультипериодного индикатора
Работа с таймсериями в библиотеке DoEasy (Часть 42): Класс объекта абстрактного индикаторного буфера
Работа с таймсериями в библиотеке DoEasy (Часть 43): Классы объектов индикаторных буферов
Работа с таймсериями в библиотеке DoEasy (Часть 44): Класс-коллекция объектов индикаторных буферов
Работа с таймсериями в библиотеке DoEasy (Часть 45): Мультипериодные индикаторные буферы
Работа с таймсериями в библиотеке DoEasy (Часть 46): Мультипериодные, мультисимвольные индикаторные буферы
Работа с таймсериями в библиотеке DoEasy (Часть 47): Мультипериодные мультисимвольные стандартные индикаторы
Работа с таймсериями в библиотеке DoEasy (Часть 48): Мультипериодные мультисимвольные индикаторы на одном буфере в подокне
Работа с таймсериями в библиотеке DoEasy (Часть 49): Мультипериодные мультисимвольные многобуферные стандартные индикаторы
Работа с таймсериями в библиотеке DoEasy (Часть 50): Мультипериодные мультисимвольные стандартные индикаторы со смещением
Работа с таймсериями в библиотеке DoEasy (Часть 51): Составные мультипериодные мультисимвольные стандартные индикаторы
Работа с таймсериями в библиотеке DoEasy (Часть 52): Кроссплатформенность мультипериодных мультисимвольных однобуферных стандартных индикаторов
Работа с таймсериями в библиотеке DoEasy (Часть 53): Класс абстрактного базового индикатора
Работа с таймсериями в библиотеке DoEasy (Часть 54): Классы-наследники абстрактного базового индикатора
Работа с таймсериями в библиотеке DoEasy (Часть 55): Класс-коллекция индикаторов
Работа с таймсериями в библиотеке DoEasy (Часть 56): Объект пользовательского индикатора, получение данных от объектов-индикаторов в коллекции
Работа с таймсериями в библиотеке DoEasy (Часть 57): Объект данных буфера индикатора
Работа с таймсериями в библиотеке DoEasy (Часть 58): Таймсерии данных буферов индикаторов
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Осталось добавить, когда война клоническая пойдет ?