Прочие классы в библиотеке DoEasy (Часть 69): Класс-коллекция объектов-чартов
Содержание
Концепция
Итак, мы подготовили функционал для работы с объектами-чартами, у нас есть объект-чарт, который содержит в себе списки объектов-окон чарта, в которых в свою очередь содержатся списки прикреплённых к ним индикаторов. Настало время соединить всё это в одну общую базу — коллекцию объектов-чартов, из которой к любому графику, открытому в терминале, мы сможем иметь удобный доступ для работы с ним и его свойствами. Коллекция позволит нам сортировать, искать и получать списки объектов-чартов по любому их свойству и удобно работать с выбранным чартом или одновременно с несколькими.
Доработка классов библиотеки
В первую очередь добавим в библиотеку новые сообщения.
В файл \MQL5\Include\DoEasy\Data.mqh впишем индексы новых сообщений:
MSG_CHART_OBJ_CHART_WINDOW, // Главное окно графика MSG_CHART_OBJ_CHART_SUBWINDOW, // Подокно графика MSG_CHART_OBJ_CHART_SUBWINDOWS_NUM, // Подокон MSG_CHART_OBJ_INDICATORS_MW_NAME_LIST, // Индикаторы в главном окне графика MSG_CHART_OBJ_INDICATORS_SW_NAME_LIST, // Индикаторы в окне графика MSG_CHART_OBJ_INDICATOR, // Индикатор MSG_CHART_OBJ_INDICATORS_TOTAL, // Индикаторов MSG_CHART_OBJ_WINDOW_N, // Окно MSG_CHART_OBJ_INDICATORS_NONE, // Отсутствуют //--- CChartObjCollection MSG_CHART_COLLECTION_TEXT_CHART_COLLECTION, // Коллекция чартов MSG_CHART_COLLECTION_ERR_FAILED_CREATE_CHART_OBJ, // Не удалось создать новый объект-чарт MSG_CHART_COLLECTION_ERR_FAILED_ADD_CHART, // Не удалось добавить объект-чарт в коллекцию }; //+------------------------------------------------------------------+
и тексты сообщений, соответствующие вновь добавленным индексам:
{"Главное окно графика","Main chart window"}, {"Подокно графика","Chart subwindow"}, {"Подокон","Subwindows"}, {"Индикаторы в главном окне графика","Indicators in the main chart window"}, {"Индикаторы в окне графика","Indicators in the chart window"}, {"Индикатор","Indicator"}, {"Индикаторов","Indicators total"}, {"Окно","Window"}, {"Отсутствуют","No indicators"}, //--- CChartObjCollection {"Коллекция чартов","Chart collection"}, {"Не удалось создать новый объект-чарт","Failed to create new chart object"}, {"Не удалось добавить объект-чарт в коллекцию","Failed to add chart object to collection"}, }; //+---------------------------------------------------------------------+
Коллекция объектов-чартов у нас должна отслеживать изменения в количестве открытых графиков и изменения в количестве открытых окон на каждом графике — чтобы вовремя вносить изменения в свои списки чартов и окон, открытых в них. Некоторые изменения возможно отслеживать в обработчике OnChartEvent(), но тесты показали, что в основном в этом обработчике указывается, что с графиком произошли некие изменения — событие изменения графика (CHARTEVENT_CHART_CHANGE), т. е. ни о какой конкретике тут речи быть не может (если нам нужно знать количество графиков и окон). Поэтому мы будем работать в таймере программы и самостоятельно отслеживать изменения в количестве открытых графиков и окон на них. Иные же изменения, которые могут происходить на графике, можно отслеживать при помощи либо вышеупомянутого обработчика OnChartEvent(), либо при помощи наследования объекта-чарта и объекта-окна чарта от объекта библиотеки CBaseObjExt, который в свою очередь является наследником базового объекта всех объектов библиотеки CBaseObj и даёт дополнительный событийный функционал своим объектам-наследникам. Это в случае, если нам в дальнейшем потребуется такой функционал при работе с графиками.
Так как работа с чартами в основном проводится в полуавтоматическом режиме, то нам для определения изменений в количестве открытых графиков и окон на них будет вполне достаточно два раза в секунду проверять текущее количество графиков и окон и сравнивать их с прошлым количеством. Если никаких изменений не было, то и делать ничего не нужно. При обнаружении изменений в количестве окон и графиков мы будем обновлять данные в нашей коллекции.
Для работы объектов-чартов в таймере нам будет необходим ещё один счётчик таймера коллекции объектов-чартов. Для каждой коллекции, работающей в таймере, у нас создан свой счётчик таймера, позволяющий отслеживать заданную для коллекции частоту её обновления. Помимо параметров счётчика нам нужно будет добавить идентификатор новой коллекции, так как у каждой коллекции объектов есть свой идентификатор, по которому мы можем определить, к какой коллекции принадлежит тот или иной список объектов.
В файле \MQL5\Include\DoEasy\Defines.mqh в разделе "Макроподстановки" пропишем параметры счётчика таймера коллекции объектов-чартов и идентификатор списка коллекции объектов-чартов:
//+------------------------------------------------------------------+ //| Макроподстановки | //+------------------------------------------------------------------+ //--- "Описание функции с номером строки ошибки" #define DFUN_ERR_LINE (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Стр. " : ", Line ")+(string)__LINE__+": ") #define DFUN (__FUNCTION__+": ") // "Описание функции" #define COUNTRY_LANG ("Russian") // Язык страны #define END_TIME (D'31.12.3000 23:59:59') // Конечная дата для запросов данных истории счёта #define TIMER_FREQUENCY (16) // Минимальная частота таймера библиотеки в миллисекундах #define TOTAL_TRADE_TRY (5) // Количество торговых попыток по умолчанию #define IND_COLORS_TOTAL (64) // Максимально возможное количество цветов индикаторного буфера #define IND_BUFFERS_MAX (512) // Максимально возможное количество буферов индикатора //--- Стандартные звуки #define SND_ALERT "alert.wav" #define SND_ALERT2 "alert2.wav" #define SND_CONNECT "connect.wav" #define SND_DISCONNECT "disconnect.wav" #define SND_EMAIL "email.wav" #define SND_EXPERT "expert.wav" #define SND_NEWS "news.wav" #define SND_OK "ok.wav" #define SND_REQUEST "request.wav" #define SND_STOPS "stops.wav" #define SND_TICK "tick.wav" #define SND_TIMEOUT "timeout.wav" #define SND_WAIT "wait.wav" //--- Параметры таймера коллекции ордеров и сделок #define COLLECTION_ORD_PAUSE (250) // Пауза таймера коллекции ордеров и сделок в миллисекундах #define COLLECTION_ORD_COUNTER_STEP (16) // Шаг приращения счётчика таймера коллекции ордеров и сделок #define COLLECTION_ORD_COUNTER_ID (1) // Идентификатор счётчика таймера коллекции ордеров и сделок //--- Параметры таймера коллекции аккаунтов #define COLLECTION_ACC_PAUSE (1000) // Пауза таймера коллекции аккаунтов в миллисекундах #define COLLECTION_ACC_COUNTER_STEP (16) // Шаг приращения счётчика таймера аккаунтов #define COLLECTION_ACC_COUNTER_ID (2) // Идентификатор счётчика таймера аккаунтов //--- Параметры таймера1 коллекции символов #define COLLECTION_SYM_PAUSE1 (100) // Пауза таймера1 коллекции символов в миллисекундах (для сканирования символов в обзоре рынка) #define COLLECTION_SYM_COUNTER_STEP1 (16) // Шаг приращения счётчика таймера1 символов #define COLLECTION_SYM_COUNTER_ID1 (3) // Идентификатор счётчика таймера1 символов //--- Параметры таймера2 коллекции символов #define COLLECTION_SYM_PAUSE2 (300) // Пауза таймера2 коллекции символов в миллисекундах (для событий списка символов в обзоре рынка) #define COLLECTION_SYM_COUNTER_STEP2 (16) // Шаг приращения счётчика таймера2 символов #define COLLECTION_SYM_COUNTER_ID2 (4) // Идентификатор счётчика таймера2 символов //--- Параметры таймера торгового класса #define COLLECTION_REQ_PAUSE (300) // Пауза таймера торгового класса в миллисекундах #define COLLECTION_REQ_COUNTER_STEP (16) // Шаг приращения счётчика таймера торгового класса #define COLLECTION_REQ_COUNTER_ID (5) // Идентификатор счётчика таймера торгового класса //--- Параметры таймера коллекции таймсерий #define COLLECTION_TS_PAUSE (64) // Пауза таймера коллекции таймсерий в миллисекундах #define COLLECTION_TS_COUNTER_STEP (16) // Шаг приращения счётчика таймера таймсерий #define COLLECTION_TS_COUNTER_ID (6) // Идентификатор счётчика таймера таймсерий //--- Параметры таймера коллекции таймсерий индикаторных данных #define COLLECTION_IND_TS_PAUSE (64) // Пауза таймера коллекции таймсерий индикаторных данных в миллисекундах #define COLLECTION_IND_TS_COUNTER_STEP (16) // Шаг приращения счётчика таймера таймсерий индикаторных данных #define COLLECTION_IND_TS_COUNTER_ID (7) // Идентификатор счётчика таймера таймсерий индикаторных данных //--- Параметры таймера коллекции тиковых серий #define COLLECTION_TICKS_PAUSE (64) // Пауза таймера коллекции тиковых серий в миллисекундах #define COLLECTION_TICKS_COUNTER_STEP (16) // Шаг приращения счётчика таймера тиковых серий #define COLLECTION_TICKS_COUNTER_ID (8) // Идентификатор счётчика таймера тиковых серий //--- Параметры таймера коллекции чартов #define COLLECTION_CHARTS_PAUSE (500) // Пауза таймера коллекции чартов в миллисекундах #define COLLECTION_CHARTS_COUNTER_STEP (16) // Шаг приращения счётчика таймера чартов #define COLLECTION_CHARTS_COUNTER_ID (9) // Идентификатор счётчика таймера чартов //--- Идентификаторы списков коллекций #define COLLECTION_HISTORY_ID (0x777A) // Идентификатор списка исторической коллекции #define COLLECTION_MARKET_ID (0x777B) // Идентификатор списка рыночной коллекции #define COLLECTION_EVENTS_ID (0x777C) // Идентификатор списка коллекции событий #define COLLECTION_ACCOUNT_ID (0x777D) // Идентификатор списка коллекции аккаунтов #define COLLECTION_SYMBOLS_ID (0x777E) // Идентификатор списка коллекции символов #define COLLECTION_SERIES_ID (0x777F) // Идентификатор списка коллекции таймсерий #define COLLECTION_BUFFERS_ID (0x7780) // Идентификатор списка коллекции индикаторных буферов #define COLLECTION_INDICATORS_ID (0x7781) // Идентификатор списка коллекции индикаторов #define COLLECTION_INDICATORS_DATA_ID (0x7782) // Идентификатор списка коллекции индикаторных данных #define COLLECTION_TICKSERIES_ID (0x7783) // Идентификатор списка коллекции тиковых серий #define COLLECTION_MBOOKSERIES_ID (0x7784) // Идентификатор списка коллекции серий стаканов цен #define COLLECTION_MQL5_SIGNALS_ID (0x7785) // Идентификатор списка коллекции mql5-сигналов #define COLLECTION_CHARTS_ID (0x7786) // Идентификатор списка коллекции чартов //--- Параметры данных для файловых операций
В перечислении целочисленных свойств чарта удалим константу CHART_PROP_WINDOW_IS_VISIBLE, так как я не нашёл практического применения данного свойства для объекта. Соответственно, уменьшим на 1 значение количества целочисленных свойств (с 67 до 66):
#define CHART_PROP_INTEGER_TOTAL (66) // Общее количество целочисленных свойств
Поправим критерии сортировки объектов-чартов по их свойствам, дописав пропущенные мной ранее свойства для сортировки объектов:
//+------------------------------------------------------------------+ //| Возможные критерии сортировки чартов | //+------------------------------------------------------------------+ #define FIRST_CHART_DBL_PROP (CHART_PROP_INTEGER_TOTAL-CHART_PROP_INTEGER_SKIP) #define FIRST_CHART_STR_PROP (CHART_PROP_INTEGER_TOTAL-CHART_PROP_INTEGER_SKIP+CHART_PROP_DOUBLE_TOTAL-CHART_PROP_DOUBLE_SKIP) enum ENUM_SORT_CHART_MODE { //--- Сортировка по целочисленным свойствам SORT_BY_CHART_ID = 0, // Сортировать по идентификатору графика SORT_BY_CHART_TIMEFRAME, // Сортировать по таймфрейму графика SORT_BY_CHART_SHOW, // Сортировать по признаку отрисовки ценового графика SORT_BY_CHART_IS_OBJECT, // Сортировать по признаку идентификации объекта "График" (OBJ_CHART)
И удалим из списка констант этого перечисления константу SORT_BY_CHART_WINDOW_IS_VISIBLE, так как это свойство мы в объекте использовать не будем.
Все объекты, коллекции которых у нас есть в библиотеке, имеют свои списки, которые в свою очередь могут сортироваться по различным свойствам объектов. Список коллекции объектов-чартов тоже будет иметь возможность сортировки — для поиска и выбора объектов с требуемыми значениями свойств. Для каждого такого объекта мы делаем свои методы их сортировки, прописанные в файле \MQL5\Include\DoEasy\Services\Select.mqh.
Добавим новые методы в файл класса CSelect для организации поиска и сортировки объектов-чартов.
Подключим к файлу класса CSelect файл класса объекта-чарта:
//+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #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\SeriesDE.mqh" #include "..\Objects\Indicators\Buffer.mqh" #include "..\Objects\Indicators\IndicatorDE.mqh" #include "..\Objects\Indicators\DataInd.mqh" #include "..\Objects\Ticks\DataTick.mqh" #include "..\Objects\Book\MarketBookOrd.mqh" #include "..\Objects\MQLSignalBase\MQLSignal.mqh" #include "..\Objects\Chart\ChartObj.mqh" //+------------------------------------------------------------------+
В конце листинга тела класса объявим новые методы:
//+------------------------------------------------------------------+ //| Методы работы с данными чартов | //+------------------------------------------------------------------+ //--- Возвращает список чартов, где одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию static CArrayObj *ByChartProperty(CArrayObj *list_source,ENUM_CHART_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByChartProperty(CArrayObj *list_source,ENUM_CHART_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByChartProperty(CArrayObj *list_source,ENUM_CHART_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Возвращает индекс чарта в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства static int FindChartMax(CArrayObj *list_source,ENUM_CHART_PROP_INTEGER property); static int FindChartMax(CArrayObj *list_source,ENUM_CHART_PROP_DOUBLE property); static int FindChartMax(CArrayObj *list_source,ENUM_CHART_PROP_STRING property); //--- Возвращает индекс чарта в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства static int FindChartMin(CArrayObj *list_source,ENUM_CHART_PROP_INTEGER property); static int FindChartMin(CArrayObj *list_source,ENUM_CHART_PROP_DOUBLE property); static int FindChartMin(CArrayObj *list_source,ENUM_CHART_PROP_STRING property); //--- }; //+------------------------------------------------------------------+
За пределами тела класса напишем их реализацию:
//+------------------------------------------------------------------+ //| Методы работы с данными чартов | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Возвращает список чартов, где одно из целочисленных | //| свойств удовлетворяет заданному критерию | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByChartProperty(CArrayObj *list_source,ENUM_CHART_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++) { CChartObj *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::ByChartProperty(CArrayObj *list_source,ENUM_CHART_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++) { CChartObj *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::ByChartProperty(CArrayObj *list_source,ENUM_CHART_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++) { CChartObj *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::FindChartMax(CArrayObj *list_source,ENUM_CHART_PROP_INTEGER property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CChartObj *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CChartObj *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::FindChartMax(CArrayObj *list_source,ENUM_CHART_PROP_DOUBLE property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CChartObj *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CChartObj *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::FindChartMax(CArrayObj *list_source,ENUM_CHART_PROP_STRING property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CChartObj *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CChartObj *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::FindChartMin(CArrayObj* list_source,ENUM_CHART_PROP_INTEGER property) { int index=0; CChartObj *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CChartObj *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::FindChartMin(CArrayObj* list_source,ENUM_CHART_PROP_DOUBLE property) { int index=0; CChartObj *min_obj=NULL; int total=list_source.Total(); if(total== 0) return WRONG_VALUE; for(int i=1; i<total; i++) { CChartObj *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::FindChartMin(CArrayObj* list_source,ENUM_CHART_PROP_STRING property) { int index=0; CChartObj *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CChartObj *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; } //+------------------------------------------------------------------+
Работа этих методов подробно рассматривалась нами в статье 3 в разделе "Организация поиска".
При изменении количества окон, присоединённых к графику, индексы окон меняются, поэтому в некоторых случаях нам может потребоваться устанавливать новый индекс окна. В файле класса объекта-окна чарта \MQL5\Include\DoEasy\Objects\Chart\ChartWnd.mqh в публичной секции класса напишем новый метод для установки индекса окна:
//+------------------------------------------------------------------+ //| Класс объекта-индикатора окна графика | //+------------------------------------------------------------------+ class CWndInd : public CObject { private: long m_chart_id; // Идентификатор графика string m_name; // Короткое имя индикатора int m_index; // Индекс окна на графике int m_handle; // Хэндл индикатора public: //--- Возвращает себя CWndInd *GetObject(void) { return &this; } //--- Возвращает (1) имя индикатора, (2) индекс окна, (3) хэндл индикатора string Name(void) const { return this.m_name; } int Index(void) const { return this.m_index; } int Handle(void) const { return this.m_handle; } //--- Устанавливает индекс окна на графике void SetIndex(const int index) { this.m_index=index; } //--- Выводит в журнал описание свойств объекта (dash=true - дефис перед описанием, false - только описание) void Print(const bool dash=false) { ::Print((dash ? "- " : "")+this.Header()); } //--- Возвращает краткое наименование объекта string Header(void) const { return CMessage::Text(MSG_CHART_OBJ_INDICATOR)+" "+this.Name(); } //--- Сравнивает объекты CWndInd между собой по указанному свойству virtual int Compare(const CObject *node,const int mode=0) const; //--- Конструкторы CWndInd(void); CWndInd(const int handle,const string name,const int index) : m_handle(handle),m_name(name),m_index(index) {} }; //+------------------------------------------------------------------+
Немного доработаем класс объекта-чарта, расположенный в файле \MQL5\Include\DoEasy\Objects\Chart\ChartObj.mqh.
Из списка приватных методов для установки свойств объекта удалим объявление метода SetVisible(), так как мы решили избавиться от свойства, устанавливаемого этим методом, не несущего нам никакой полезной нагрузки:
//--- Методы установки значений свойств bool SetMode(const string source,const ENUM_CHART_MODE mode,const bool redraw=false); bool SetScale(const string source,const int scale,const bool redraw=false); bool SetModeVolume(const string source,const ENUM_CHART_VOLUME_MODE mode,const bool redraw=false); void SetVisibleBars(void); void SetWindowsTotal(void); void SetVisible(void); void SetFirstVisibleBars(void); void SetWidthInBars(void); void SetWidthInPixels(void); void SetMaximizedFlag(void); void SetMinimizedFlag(void); void SetExpertName(void); void SetScriptName(void);
За пределами тела класса найдём его реализацию и тоже удалим:
//+------------------------------------------------------------------+ //| Устанавливает свойство | //| "Общее количество окон графика, включая подокна индикаторов" | //+------------------------------------------------------------------+ void CChartObj::SetWindowsTotal(void) { this.SetProperty(CHART_PROP_WINDOWS_TOTAL,::ChartGetInteger(this.ID(),CHART_WINDOWS_TOTAL)); } //+------------------------------------------------------------------+ //| Устанавливает свойство "Видимость подокна" | //+------------------------------------------------------------------+ void CChartObj::SetVisible(void) { this.SetProperty(CHART_PROP_WINDOW_IS_VISIBLE,::ChartGetInteger(this.ID(),CHART_WINDOW_IS_VISIBLE,0)); } //+------------------------------------------------------------------+ //| Устанавливает свойство "Номер первого видимого бара на графике" | //+------------------------------------------------------------------+ void CChartObj::SetFirstVisibleBars(void) { this.SetProperty(CHART_PROP_FIRST_VISIBLE_BAR,::ChartGetInteger(this.ID(),CHART_FIRST_VISIBLE_BAR)); } //+------------------------------------------------------------------+
Метод просто устанавливал свойство видимости главного окна графика в уже удалённое выше целочисленное свойство. Его мы использовать не будем, поэтому и избавимся от всех методов, с ним связанных. В любом случае, значение этого свойства окна мы всё ещё можем получить прямо из окружения, а не из свойств объекта-окна графика.
В приватной секции класса объявим метод для создания списка окон, прикреплённых к чарту:
//--- Методы установки значений свойств bool SetMode(const string source,const ENUM_CHART_MODE mode,const bool redraw=false); bool SetScale(const string source,const int scale,const bool redraw=false); bool SetModeVolume(const string source,const ENUM_CHART_VOLUME_MODE mode,const bool redraw=false); void SetVisibleBars(void); void SetWindowsTotal(void); void SetFirstVisibleBars(void); void SetWidthInBars(void); void SetWidthInPixels(void); void SetMaximizedFlag(void); void SetMinimizedFlag(void); void SetExpertName(void); void SetScriptName(void); //--- Создаёт список окон графика void CreateWindowsList(void); public:
В публичной секции класса объявим метод для обновления свойств объекта-чарта и его подокон:
//--- Обновляет объект-чарт и его список окон индикаторов void Refresh(void); //--- Конструкторы CChartObj(){;} CChartObj(const long chart_id);
При организации работы с методом, возвращающим общее количество окон графика, столкнулся с тем, что всегда приходится после запроса этих данных, сразу же их устанавливать в свойства объекта. Поэтому решил объединить в методе запрос этих данных и их запись в свойство объекта.
В методе WindowsTotal() впишем сначала запрос и установку свойства количества окон графика, осуществляемых в методе SetWindowsTotal(), а затем возврат только что полученного и сохранённого в свойствах объекта значения. Реализацию метода Visible() удалим:
//--- Возвращает количество баров на графике, доступных для отображения int VisibleBars(void) const { return (int)this.GetProperty(CHART_PROP_VISIBLE_BARS); } //--- Возвращает общее количество окон графика, включая подокна индикаторов int WindowsTotal(void) { this.SetWindowsTotal(); return (int)this.GetProperty(CHART_PROP_WINDOWS_TOTAL); } //--- Возвращает видимость окна bool Visible(void) const { return (bool)this.GetProperty(CHART_PROP_WINDOW_IS_VISIBLE); }
Добавим ещё один метод, возвращающий флаг того, что данный объект-чарт является главным — тем чартом, к которому присоединена программа, созданная на основе библиотеки:
//--- Эмулирует тик (обновления графика - аналогично команде Refresh в терминале) void EmulateTick(void) { ::ChartSetSymbolPeriod(this.ID(),this.Symbol(),this.Timeframe());} //--- Возвращает флаг, что объект-чарт принадлежит графику программы bool IsMainChart(void) const { return(this.m_chart_id==CBaseObj::GetMainChartID()); } //--- Возвращает указанное по индексу окно графика CChartWnd *GetWindowByIndex(const int index) const { return this.m_list_wnd.At(index); }
В базовом объекте всех объектов библиотеки CBaseObj есть переменная m_chart_id_main, хранящая идентификатор графика, на котором запущена программа. В его конструкторе этой переменной устанавливается значение, возвращаемое функцией ChartID(), и возвращается значение идентификатора текущего графика методом GetMainChartID() класса CBaseObj, который возвращает значение, записанное в переменную m_chart_id_main. Таким образом мы возвращаем флаг того, что идентификатор текущего графика совпадает с идентификатором главного окна программы. При совпадении идентификаторов метод возвращает true, иначе — false.
Из параметрического конструктора удалим строку, в которой устанавливается значение видимости окна текущего графика:
//+------------------------------------------------------------------+ //| Параметрический конструктор | //+------------------------------------------------------------------+ CChartObj::CChartObj(const long chart_id) { //--- Установка идентификатора графика в базовый объект CBaseObj::SetChartID(chart_id); //--- Установка целочисленных свойств this.SetProperty(CHART_PROP_ID,chart_id); // Идентификатор графика this.SetProperty(CHART_PROP_TIMEFRAME,::ChartPeriod(this.ID())); // Таймфрейм графика this.SetProperty(CHART_PROP_SHOW,::ChartGetInteger(this.ID(),CHART_SHOW)); // Признак отрисовки ценового графика this.SetProperty(CHART_PROP_IS_OBJECT,::ChartGetInteger(this.ID(),CHART_IS_OBJECT)); // Признак идентификации объекта "График" this.SetProperty(CHART_PROP_BRING_TO_TOP,false); // Показ графика поверх всех других this.SetProperty(CHART_PROP_CONTEXT_MENU,::ChartGetInteger(this.ID(),CHART_CONTEXT_MENU)); // Доступ к контекстному меню по нажатию правой клавиши мышки this.SetProperty(CHART_PROP_CROSSHAIR_TOOL,::ChartGetInteger(this.ID(),CHART_CROSSHAIR_TOOL)); // Доступ к инструменту "Перекрестие" по нажатию средней клавиши мышки this.SetProperty(CHART_PROP_MOUSE_SCROLL,::ChartGetInteger(this.ID(),CHART_MOUSE_SCROLL)); // Прокрутка графика левой кнопкой мышки по горизонтали this.SetProperty(CHART_PROP_EVENT_MOUSE_WHEEL,::ChartGetInteger(this.ID(),CHART_EVENT_MOUSE_WHEEL)); // Отправка всем mql5-программам на графике сообщений о событиях колёсика мышки this.SetProperty(CHART_PROP_EVENT_MOUSE_MOVE,::ChartGetInteger(this.ID(),CHART_EVENT_MOUSE_MOVE)); // Отправка всем mql5-программам на графике сообщений о событиях перемещения и нажатия кнопок мышки this.SetProperty(CHART_PROP_EVENT_OBJECT_CREATE,::ChartGetInteger(this.ID(),CHART_EVENT_OBJECT_CREATE)); // Отправка всем mql5-программам на графике сообщений о событии создания графического объекта this.SetProperty(CHART_PROP_EVENT_OBJECT_DELETE,::ChartGetInteger(this.ID(),CHART_EVENT_OBJECT_DELETE)); // Отправка всем mql5-программам на графике сообщений о событии уничтожения графического объекта this.SetProperty(CHART_PROP_MODE,::ChartGetInteger(this.ID(),CHART_MODE)); // Тип графика (свечи, бары или линия this.SetProperty(CHART_PROP_FOREGROUND,::ChartGetInteger(this.ID(),CHART_FOREGROUND)); // Ценовой график на переднем плане this.SetProperty(CHART_PROP_SHIFT,::ChartGetInteger(this.ID(),CHART_SHIFT)); // Режим отступа ценового графика от правого края this.SetProperty(CHART_PROP_AUTOSCROLL,::ChartGetInteger(this.ID(),CHART_AUTOSCROLL)); // Режим автоматического перехода к правому краю графика this.SetProperty(CHART_PROP_KEYBOARD_CONTROL,::ChartGetInteger(this.ID(),CHART_KEYBOARD_CONTROL)); // Разрешение на управление графиком с помощью клавиатуры this.SetProperty(CHART_PROP_QUICK_NAVIGATION,::ChartGetInteger(this.ID(),CHART_QUICK_NAVIGATION)); // Разрешение на перехват графиком нажатий клавиш Space и Enter для активации строки быстрой навигации this.SetProperty(CHART_PROP_SCALE,::ChartGetInteger(this.ID(),CHART_SCALE)); // Масштаб this.SetProperty(CHART_PROP_SCALEFIX,::ChartGetInteger(this.ID(),CHART_SCALEFIX)); // Режим фиксированного масштаба this.SetProperty(CHART_PROP_SCALEFIX_11,::ChartGetInteger(this.ID(),CHART_SCALEFIX_11)); // Режим масштаба 1:1 this.SetProperty(CHART_PROP_SCALE_PT_PER_BAR,::ChartGetInteger(this.ID(),CHART_SCALE_PT_PER_BAR)); // Режим указания масштаба в пунктах на бар this.SetProperty(CHART_PROP_SHOW_TICKER,::ChartGetInteger(this.ID(),CHART_SHOW_TICKER)); // Отображение в левом верхнем углу тикера символа this.SetProperty(CHART_PROP_SHOW_OHLC,::ChartGetInteger(this.ID(),CHART_SHOW_OHLC)); // Отображение в левом верхнем углу значений OHLC this.SetProperty(CHART_PROP_SHOW_BID_LINE,::ChartGetInteger(this.ID(),CHART_SHOW_BID_LINE)); // Отображение значения Bid горизонтальной линией на графике this.SetProperty(CHART_PROP_SHOW_ASK_LINE,::ChartGetInteger(this.ID(),CHART_SHOW_ASK_LINE)); // Отображение значения Ask горизонтальной линией на графике this.SetProperty(CHART_PROP_SHOW_LAST_LINE,::ChartGetInteger(this.ID(),CHART_SHOW_LAST_LINE)); // Отображение значения Last горизонтальной линией на графике this.SetProperty(CHART_PROP_SHOW_PERIOD_SEP,::ChartGetInteger(this.ID(),CHART_SHOW_PERIOD_SEP)); // Отображение вертикальных разделителей между соседними периодами this.SetProperty(CHART_PROP_SHOW_GRID,::ChartGetInteger(this.ID(),CHART_SHOW_GRID)); // Отображение сетки на графике this.SetProperty(CHART_PROP_SHOW_VOLUMES,::ChartGetInteger(this.ID(),CHART_SHOW_VOLUMES)); // Отображение объемов на графике this.SetProperty(CHART_PROP_SHOW_OBJECT_DESCR,::ChartGetInteger(this.ID(),CHART_SHOW_OBJECT_DESCR)); // Отображение текстовых описаний объектов this.SetProperty(CHART_PROP_VISIBLE_BARS,::ChartGetInteger(this.ID(),CHART_VISIBLE_BARS)); // Количество баров на графике, доступных для отображения this.SetProperty(CHART_PROP_WINDOWS_TOTAL,::ChartGetInteger(this.ID(),CHART_WINDOWS_TOTAL)); // Общее количество окон графика, включая подокна индикаторов this.SetProperty(CHART_PROP_WINDOW_IS_VISIBLE,::ChartGetInteger(this.ID(),CHART_WINDOW_IS_VISIBLE,0));// Видимость окна this.SetProperty(CHART_PROP_WINDOW_HANDLE,::ChartGetInteger(this.ID(),CHART_WINDOW_HANDLE)); // Хэндл окна графика this.SetProperty(CHART_PROP_WINDOW_YDISTANCE,::ChartGetInteger(this.ID(),CHART_WINDOW_YDISTANCE,0)); // Дистанция в пикселях по вертикальной оси Y между верхней рамкой подокна индикатора и верхней рамкой главного окна графика this.SetProperty(CHART_PROP_FIRST_VISIBLE_BAR,::ChartGetInteger(this.ID(),CHART_FIRST_VISIBLE_BAR)); // Номер первого видимого бара на графике this.SetProperty(CHART_PROP_WIDTH_IN_BARS,::ChartGetInteger(this.ID(),CHART_WIDTH_IN_BARS)); // Ширина графика в барах this.SetProperty(CHART_PROP_WIDTH_IN_PIXELS,::ChartGetInteger(this.ID(),CHART_WIDTH_IN_PIXELS)); // Ширина графика в пикселях this.SetProperty(CHART_PROP_HEIGHT_IN_PIXELS,::ChartGetInteger(this.ID(),CHART_HEIGHT_IN_PIXELS,0)); // Высота графика в пикселях this.SetProperty(CHART_PROP_COLOR_BACKGROUND,::ChartGetInteger(this.ID(),CHART_COLOR_BACKGROUND)); // Цвет фона графика this.SetProperty(CHART_PROP_COLOR_FOREGROUND,::ChartGetInteger(this.ID(),CHART_COLOR_FOREGROUND)); // Цвет осей, шкалы и строки OHLC this.SetProperty(CHART_PROP_COLOR_GRID,::ChartGetInteger(this.ID(),CHART_COLOR_GRID)); // Цвет сетки this.SetProperty(CHART_PROP_COLOR_VOLUME,::ChartGetInteger(this.ID(),CHART_COLOR_VOLUME)); // Цвет объемов и уровней открытия позиций this.SetProperty(CHART_PROP_COLOR_CHART_UP,::ChartGetInteger(this.ID(),CHART_COLOR_CHART_UP)); // Цвет бара вверх, тени и окантовки тела бычьей свечи this.SetProperty(CHART_PROP_COLOR_CHART_DOWN,::ChartGetInteger(this.ID(),CHART_COLOR_CHART_DOWN)); // Цвет бара вниз, тени и окантовки тела медвежьей свечи this.SetProperty(CHART_PROP_COLOR_CHART_LINE,::ChartGetInteger(this.ID(),CHART_COLOR_CHART_LINE)); // Цвет линии графика и японских свечей "Доджи" this.SetProperty(CHART_PROP_COLOR_CANDLE_BULL,::ChartGetInteger(this.ID(),CHART_COLOR_CANDLE_BULL)); // Цвет тела бычьей свечи this.SetProperty(CHART_PROP_COLOR_CANDLE_BEAR,::ChartGetInteger(this.ID(),CHART_COLOR_CANDLE_BEAR)); // Цвет тела медвежьей свечи this.SetProperty(CHART_PROP_COLOR_BID,::ChartGetInteger(this.ID(),CHART_COLOR_BID)); // Цвет линии Bid-цены this.SetProperty(CHART_PROP_COLOR_ASK,::ChartGetInteger(this.ID(),CHART_COLOR_ASK)); // Цвет линии Ask-цены this.SetProperty(CHART_PROP_COLOR_LAST,::ChartGetInteger(this.ID(),CHART_COLOR_LAST)); // Цвет линии цены последней совершенной сделки (Last) this.SetProperty(CHART_PROP_COLOR_STOP_LEVEL,::ChartGetInteger(this.ID(),CHART_COLOR_STOP_LEVEL)); // Цвет уровней стоп-ордеров (Stop Loss и Take Profit) this.SetProperty(CHART_PROP_SHOW_TRADE_LEVELS,::ChartGetInteger(this.ID(),CHART_SHOW_TRADE_LEVELS)); // Отображение на графике торговых уровней (уровни открытых позиций, Stop Loss, Take Profit и отложенных ордеров) this.SetProperty(CHART_PROP_DRAG_TRADE_LEVELS,::ChartGetInteger(this.ID(),CHART_DRAG_TRADE_LEVELS)); // Разрешение на перетаскивание торговых уровней на графике с помощью мышки this.SetProperty(CHART_PROP_SHOW_DATE_SCALE,::ChartGetInteger(this.ID(),CHART_SHOW_DATE_SCALE)); // Отображение на графике шкалы времени this.SetProperty(CHART_PROP_SHOW_PRICE_SCALE,::ChartGetInteger(this.ID(),CHART_SHOW_PRICE_SCALE)); // Отображение на графике ценовой шкалы this.SetProperty(CHART_PROP_SHOW_ONE_CLICK,::ChartGetInteger(this.ID(),CHART_SHOW_ONE_CLICK)); // Отображение на графике панели быстрой торговли this.SetProperty(CHART_PROP_IS_MAXIMIZED,::ChartGetInteger(this.ID(),CHART_IS_MAXIMIZED)); // Окно графика развернуто this.SetProperty(CHART_PROP_IS_MINIMIZED,::ChartGetInteger(this.ID(),CHART_IS_MINIMIZED)); // Окно графика свернуто this.SetProperty(CHART_PROP_IS_DOCKED,::ChartGetInteger(this.ID(),CHART_IS_DOCKED)); // Окно графика закреплено this.SetProperty(CHART_PROP_FLOAT_LEFT,::ChartGetInteger(this.ID(),CHART_FLOAT_LEFT)); // Левая координата открепленного графика относительно виртуального экрана this.SetProperty(CHART_PROP_FLOAT_TOP,::ChartGetInteger(this.ID(),CHART_FLOAT_TOP)); // Верхняя координата открепленного графика относительно виртуального экрана this.SetProperty(CHART_PROP_FLOAT_RIGHT,::ChartGetInteger(this.ID(),CHART_FLOAT_RIGHT)); // Правая координата открепленного графика относительно виртуального экрана this.SetProperty(CHART_PROP_FLOAT_BOTTOM,::ChartGetInteger(this.ID(),CHART_FLOAT_BOTTOM)); // Нижняя координата открепленного графика относительно виртуального экрана //--- Установка вещественных свойств this.SetProperty(CHART_PROP_SHIFT_SIZE,::ChartGetDouble(this.ID(),CHART_SHIFT_SIZE)); // Размер отступа нулевого бара от правого края в процентах this.SetProperty(CHART_PROP_FIXED_POSITION,::ChartGetDouble(this.ID(),CHART_FIXED_POSITION)); // Положение фиксированной позиции графика от левого края в процентах this.SetProperty(CHART_PROP_FIXED_MAX,::ChartGetDouble(this.ID(),CHART_FIXED_MAX)); // Фиксированный максимум графика this.SetProperty(CHART_PROP_FIXED_MIN,::ChartGetDouble(this.ID(),CHART_FIXED_MIN)); // Фиксированный минимум графика this.SetProperty(CHART_PROP_POINTS_PER_BAR,::ChartGetDouble(this.ID(),CHART_POINTS_PER_BAR)); // Значение масштаба в пунктах на бар this.SetProperty(CHART_PROP_PRICE_MIN,::ChartGetDouble(this.ID(),CHART_PRICE_MIN)); // Минимум графика this.SetProperty(CHART_PROP_PRICE_MAX,::ChartGetDouble(this.ID(),CHART_PRICE_MAX)); // Максимум графика //--- Установка строковых свойств this.SetProperty(CHART_PROP_COMMENT,::ChartGetString(this.ID(),CHART_COMMENT)); // Текст комментария на графике this.SetProperty(CHART_PROP_EXPERT_NAME,::ChartGetString(this.ID(),CHART_EXPERT_NAME)); // Имя эксперта, запущенного на графике this.SetProperty(CHART_PROP_SCRIPT_NAME,::ChartGetString(this.ID(),CHART_SCRIPT_NAME)); // Имя скрипта, запущенного на графике this.SetProperty(CHART_PROP_SYMBOL,::ChartSymbol(this.ID())); // Символ графика this.m_digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS);
А вместо цикла, в котором создаются объекты-окна, принадлежащие этому чарту
this.m_digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS); int total=this.WindowsTotal(); for(int i=0;i<total;i++) { CChartWnd *wnd=new CChartWnd(m_chart_id,i); if(wnd==NULL) continue; m_list_wnd.Sort(); if(!m_list_wnd.Add(wnd)) delete wnd; }
впишем вызов метода, в котором создаётся список окон, принадлежащих этому чарту:
this.m_digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS); this.CreateWindowsList();
В итоге параметрический конструктор теперь будет таким:
//+------------------------------------------------------------------+ //| Параметрический конструктор | //+------------------------------------------------------------------+ CChartObj::CChartObj(const long chart_id) { //--- Установка идентификатора графика в базовый объект CBaseObj::SetChartID(chart_id); //--- Установка целочисленных свойств this.SetProperty(CHART_PROP_ID,chart_id); // Идентификатор графика this.SetProperty(CHART_PROP_TIMEFRAME,::ChartPeriod(this.ID())); // Таймфрейм графика this.SetProperty(CHART_PROP_SHOW,::ChartGetInteger(this.ID(),CHART_SHOW)); // Признак отрисовки ценового графика this.SetProperty(CHART_PROP_IS_OBJECT,::ChartGetInteger(this.ID(),CHART_IS_OBJECT)); // Признак идентификации объекта "График" this.SetProperty(CHART_PROP_BRING_TO_TOP,false); // Показ графика поверх всех других this.SetProperty(CHART_PROP_CONTEXT_MENU,::ChartGetInteger(this.ID(),CHART_CONTEXT_MENU)); // Доступ к контекстному меню по нажатию правой клавиши мышки this.SetProperty(CHART_PROP_CROSSHAIR_TOOL,::ChartGetInteger(this.ID(),CHART_CROSSHAIR_TOOL)); // Доступ к инструменту "Перекрестие" по нажатию средней клавиши мышки this.SetProperty(CHART_PROP_MOUSE_SCROLL,::ChartGetInteger(this.ID(),CHART_MOUSE_SCROLL)); // Прокрутка графика левой кнопкой мышки по горизонтали this.SetProperty(CHART_PROP_EVENT_MOUSE_WHEEL,::ChartGetInteger(this.ID(),CHART_EVENT_MOUSE_WHEEL)); // Отправка всем mql5-программам на графике сообщений о событиях колёсика мышки this.SetProperty(CHART_PROP_EVENT_MOUSE_MOVE,::ChartGetInteger(this.ID(),CHART_EVENT_MOUSE_MOVE)); // Отправка всем mql5-программам на графике сообщений о событиях перемещения и нажатия кнопок мышки this.SetProperty(CHART_PROP_EVENT_OBJECT_CREATE,::ChartGetInteger(this.ID(),CHART_EVENT_OBJECT_CREATE)); // Отправка всем mql5-программам на графике сообщений о событии создания графического объекта this.SetProperty(CHART_PROP_EVENT_OBJECT_DELETE,::ChartGetInteger(this.ID(),CHART_EVENT_OBJECT_DELETE)); // Отправка всем mql5-программам на графике сообщений о событии уничтожения графического объекта this.SetProperty(CHART_PROP_MODE,::ChartGetInteger(this.ID(),CHART_MODE)); // Тип графика (свечи, бары или линия this.SetProperty(CHART_PROP_FOREGROUND,::ChartGetInteger(this.ID(),CHART_FOREGROUND)); // Ценовой график на переднем плане this.SetProperty(CHART_PROP_SHIFT,::ChartGetInteger(this.ID(),CHART_SHIFT)); // Режим отступа ценового графика от правого края this.SetProperty(CHART_PROP_AUTOSCROLL,::ChartGetInteger(this.ID(),CHART_AUTOSCROLL)); // Режим автоматического перехода к правому краю графика this.SetProperty(CHART_PROP_KEYBOARD_CONTROL,::ChartGetInteger(this.ID(),CHART_KEYBOARD_CONTROL)); // Разрешение на управление графиком с помощью клавиатуры this.SetProperty(CHART_PROP_QUICK_NAVIGATION,::ChartGetInteger(this.ID(),CHART_QUICK_NAVIGATION)); // Разрешение на перехват графиком нажатий клавиш Space и Enter для активации строки быстрой навигации this.SetProperty(CHART_PROP_SCALE,::ChartGetInteger(this.ID(),CHART_SCALE)); // Масштаб this.SetProperty(CHART_PROP_SCALEFIX,::ChartGetInteger(this.ID(),CHART_SCALEFIX)); // Режим фиксированного масштаба this.SetProperty(CHART_PROP_SCALEFIX_11,::ChartGetInteger(this.ID(),CHART_SCALEFIX_11)); // Режим масштаба 1:1 this.SetProperty(CHART_PROP_SCALE_PT_PER_BAR,::ChartGetInteger(this.ID(),CHART_SCALE_PT_PER_BAR)); // Режим указания масштаба в пунктах на бар this.SetProperty(CHART_PROP_SHOW_TICKER,::ChartGetInteger(this.ID(),CHART_SHOW_TICKER)); // Отображение в левом верхнем углу тикера символа this.SetProperty(CHART_PROP_SHOW_OHLC,::ChartGetInteger(this.ID(),CHART_SHOW_OHLC)); // Отображение в левом верхнем углу значений OHLC this.SetProperty(CHART_PROP_SHOW_BID_LINE,::ChartGetInteger(this.ID(),CHART_SHOW_BID_LINE)); // Отображение значения Bid горизонтальной линией на графике this.SetProperty(CHART_PROP_SHOW_ASK_LINE,::ChartGetInteger(this.ID(),CHART_SHOW_ASK_LINE)); // Отображение значения Ask горизонтальной линией на графике this.SetProperty(CHART_PROP_SHOW_LAST_LINE,::ChartGetInteger(this.ID(),CHART_SHOW_LAST_LINE)); // Отображение значения Last горизонтальной линией на графике this.SetProperty(CHART_PROP_SHOW_PERIOD_SEP,::ChartGetInteger(this.ID(),CHART_SHOW_PERIOD_SEP)); // Отображение вертикальных разделителей между соседними периодами this.SetProperty(CHART_PROP_SHOW_GRID,::ChartGetInteger(this.ID(),CHART_SHOW_GRID)); // Отображение сетки на графике this.SetProperty(CHART_PROP_SHOW_VOLUMES,::ChartGetInteger(this.ID(),CHART_SHOW_VOLUMES)); // Отображение объемов на графике this.SetProperty(CHART_PROP_SHOW_OBJECT_DESCR,::ChartGetInteger(this.ID(),CHART_SHOW_OBJECT_DESCR)); // Отображение текстовых описаний объектов this.SetProperty(CHART_PROP_VISIBLE_BARS,::ChartGetInteger(this.ID(),CHART_VISIBLE_BARS)); // Количество баров на графике, доступных для отображения this.SetProperty(CHART_PROP_WINDOWS_TOTAL,::ChartGetInteger(this.ID(),CHART_WINDOWS_TOTAL)); // Общее количество окон графика, включая подокна индикаторов this.SetProperty(CHART_PROP_WINDOW_HANDLE,::ChartGetInteger(this.ID(),CHART_WINDOW_HANDLE)); // Хэндл окна графика this.SetProperty(CHART_PROP_WINDOW_YDISTANCE,::ChartGetInteger(this.ID(),CHART_WINDOW_YDISTANCE,0)); // Дистанция в пикселях по вертикальной оси Y между верхней рамкой подокна индикатора и верхней рамкой главного окна графика this.SetProperty(CHART_PROP_FIRST_VISIBLE_BAR,::ChartGetInteger(this.ID(),CHART_FIRST_VISIBLE_BAR)); // Номер первого видимого бара на графике this.SetProperty(CHART_PROP_WIDTH_IN_BARS,::ChartGetInteger(this.ID(),CHART_WIDTH_IN_BARS)); // Ширина графика в барах this.SetProperty(CHART_PROP_WIDTH_IN_PIXELS,::ChartGetInteger(this.ID(),CHART_WIDTH_IN_PIXELS)); // Ширина графика в пикселях this.SetProperty(CHART_PROP_HEIGHT_IN_PIXELS,::ChartGetInteger(this.ID(),CHART_HEIGHT_IN_PIXELS,0)); // Высота графика в пикселях this.SetProperty(CHART_PROP_COLOR_BACKGROUND,::ChartGetInteger(this.ID(),CHART_COLOR_BACKGROUND)); // Цвет фона графика this.SetProperty(CHART_PROP_COLOR_FOREGROUND,::ChartGetInteger(this.ID(),CHART_COLOR_FOREGROUND)); // Цвет осей, шкалы и строки OHLC this.SetProperty(CHART_PROP_COLOR_GRID,::ChartGetInteger(this.ID(),CHART_COLOR_GRID)); // Цвет сетки this.SetProperty(CHART_PROP_COLOR_VOLUME,::ChartGetInteger(this.ID(),CHART_COLOR_VOLUME)); // Цвет объемов и уровней открытия позиций this.SetProperty(CHART_PROP_COLOR_CHART_UP,::ChartGetInteger(this.ID(),CHART_COLOR_CHART_UP)); // Цвет бара вверх, тени и окантовки тела бычьей свечи this.SetProperty(CHART_PROP_COLOR_CHART_DOWN,::ChartGetInteger(this.ID(),CHART_COLOR_CHART_DOWN)); // Цвет бара вниз, тени и окантовки тела медвежьей свечи this.SetProperty(CHART_PROP_COLOR_CHART_LINE,::ChartGetInteger(this.ID(),CHART_COLOR_CHART_LINE)); // Цвет линии графика и японских свечей "Доджи" this.SetProperty(CHART_PROP_COLOR_CANDLE_BULL,::ChartGetInteger(this.ID(),CHART_COLOR_CANDLE_BULL)); // Цвет тела бычьей свечи this.SetProperty(CHART_PROP_COLOR_CANDLE_BEAR,::ChartGetInteger(this.ID(),CHART_COLOR_CANDLE_BEAR)); // Цвет тела медвежьей свечи this.SetProperty(CHART_PROP_COLOR_BID,::ChartGetInteger(this.ID(),CHART_COLOR_BID)); // Цвет линии Bid-цены this.SetProperty(CHART_PROP_COLOR_ASK,::ChartGetInteger(this.ID(),CHART_COLOR_ASK)); // Цвет линии Ask-цены this.SetProperty(CHART_PROP_COLOR_LAST,::ChartGetInteger(this.ID(),CHART_COLOR_LAST)); // Цвет линии цены последней совершенной сделки (Last) this.SetProperty(CHART_PROP_COLOR_STOP_LEVEL,::ChartGetInteger(this.ID(),CHART_COLOR_STOP_LEVEL)); // Цвет уровней стоп-ордеров (Stop Loss и Take Profit) this.SetProperty(CHART_PROP_SHOW_TRADE_LEVELS,::ChartGetInteger(this.ID(),CHART_SHOW_TRADE_LEVELS)); // Отображение на графике торговых уровней (уровни открытых позиций, Stop Loss, Take Profit и отложенных ордеров) this.SetProperty(CHART_PROP_DRAG_TRADE_LEVELS,::ChartGetInteger(this.ID(),CHART_DRAG_TRADE_LEVELS)); // Разрешение на перетаскивание торговых уровней на графике с помощью мышки this.SetProperty(CHART_PROP_SHOW_DATE_SCALE,::ChartGetInteger(this.ID(),CHART_SHOW_DATE_SCALE)); // Отображение на графике шкалы времени this.SetProperty(CHART_PROP_SHOW_PRICE_SCALE,::ChartGetInteger(this.ID(),CHART_SHOW_PRICE_SCALE)); // Отображение на графике ценовой шкалы this.SetProperty(CHART_PROP_SHOW_ONE_CLICK,::ChartGetInteger(this.ID(),CHART_SHOW_ONE_CLICK)); // Отображение на графике панели быстрой торговли this.SetProperty(CHART_PROP_IS_MAXIMIZED,::ChartGetInteger(this.ID(),CHART_IS_MAXIMIZED)); // Окно графика развернуто this.SetProperty(CHART_PROP_IS_MINIMIZED,::ChartGetInteger(this.ID(),CHART_IS_MINIMIZED)); // Окно графика свернуто this.SetProperty(CHART_PROP_IS_DOCKED,::ChartGetInteger(this.ID(),CHART_IS_DOCKED)); // Окно графика закреплено this.SetProperty(CHART_PROP_FLOAT_LEFT,::ChartGetInteger(this.ID(),CHART_FLOAT_LEFT)); // Левая координата открепленного графика относительно виртуального экрана this.SetProperty(CHART_PROP_FLOAT_TOP,::ChartGetInteger(this.ID(),CHART_FLOAT_TOP)); // Верхняя координата открепленного графика относительно виртуального экрана this.SetProperty(CHART_PROP_FLOAT_RIGHT,::ChartGetInteger(this.ID(),CHART_FLOAT_RIGHT)); // Правая координата открепленного графика относительно виртуального экрана this.SetProperty(CHART_PROP_FLOAT_BOTTOM,::ChartGetInteger(this.ID(),CHART_FLOAT_BOTTOM)); // Нижняя координата открепленного графика относительно виртуального экрана //--- Установка вещественных свойств this.SetProperty(CHART_PROP_SHIFT_SIZE,::ChartGetDouble(this.ID(),CHART_SHIFT_SIZE)); // Размер отступа нулевого бара от правого края в процентах this.SetProperty(CHART_PROP_FIXED_POSITION,::ChartGetDouble(this.ID(),CHART_FIXED_POSITION)); // Положение фиксированной позиции графика от левого края в процентах this.SetProperty(CHART_PROP_FIXED_MAX,::ChartGetDouble(this.ID(),CHART_FIXED_MAX)); // Фиксированный максимум графика this.SetProperty(CHART_PROP_FIXED_MIN,::ChartGetDouble(this.ID(),CHART_FIXED_MIN)); // Фиксированный минимум графика this.SetProperty(CHART_PROP_POINTS_PER_BAR,::ChartGetDouble(this.ID(),CHART_POINTS_PER_BAR)); // Значение масштаба в пунктах на бар this.SetProperty(CHART_PROP_PRICE_MIN,::ChartGetDouble(this.ID(),CHART_PRICE_MIN)); // Минимум графика this.SetProperty(CHART_PROP_PRICE_MAX,::ChartGetDouble(this.ID(),CHART_PRICE_MAX)); // Максимум графика //--- Установка строковых свойств this.SetProperty(CHART_PROP_COMMENT,::ChartGetString(this.ID(),CHART_COMMENT)); // Текст комментария на графике this.SetProperty(CHART_PROP_EXPERT_NAME,::ChartGetString(this.ID(),CHART_EXPERT_NAME)); // Имя эксперта, запущенного на графике this.SetProperty(CHART_PROP_SCRIPT_NAME,::ChartGetString(this.ID(),CHART_SCRIPT_NAME)); // Имя скрипта, запущенного на графике this.SetProperty(CHART_PROP_SYMBOL,::ChartSymbol(this.ID())); // Символ графика this.m_digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS); this.CreateWindowsList(); } //+------------------------------------------------------------------+
Из метода GetPropertyDescription(), возвращающего описание целочисленного свойства объекта, удалим блок кода, в котором создаётся описание параметра видимости окна — это свойство мы удалили из объекта:
property==CHART_PROP_WINDOWS_TOTAL ? CMessage::Text(MSG_CHART_OBJ_WINDOWS_TOTAL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==CHART_PROP_WINDOW_IS_VISIBLE ? CMessage::Text(MSG_CHART_OBJ_WINDOW_IS_VISIBLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : property==CHART_PROP_WINDOW_HANDLE ? CMessage::Text(MSG_CHART_OBJ_WINDOW_HANDLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) :
В методах, выводящих в журнал данные всех индикаторов всех окон графика и свойства всех окон графика, заменим размер цикла на количество объектов-окон графика в списках:
//+------------------------------------------------------------------+ //| Выводит в журнал данные всех индикаторов всех окон графика | //+------------------------------------------------------------------+ void CChartObj::PrintWndIndicators(void) { for(int i=0;i<this.m_list_wnd.Total();i++) { CChartWnd *wnd=m_list_wnd.At(i); if(wnd==NULL) continue; wnd.PrintIndicators(true); } } //+------------------------------------------------------------------+ //| Выводит в журнал свойства всех окон графика | //+------------------------------------------------------------------+ void CChartObj::PrintWndParameters(void) { for(int i=0;i<this.m_list_wnd.Total();i++) { CChartWnd *wnd=m_list_wnd.At(i); if(wnd==NULL) continue; wnd.PrintParameters(true); } } //+------------------------------------------------------------------+
Ранее циклы считались до значения, возвращаемого методом WindowsTotal(), что не совсем верно — этот метод возвращает реальное значение, полученное от графика, в то время как в списках может быть либо меньше, либо больше объектов, чем есть на самом деле. Это происходит при изменении их количества на графике в терминале. Поэтому цикл по неправильному количеству объектов в списке чреват ошибками, что мы здесь и исправили.
За пределами тела класса напишем реализацию метода, обновляющего объект-чарт и список его окон:
//+------------------------------------------------------------------+ //| Обновляет объект-чарт и список его окон | //+------------------------------------------------------------------+ void CChartObj::Refresh(void) { int change=this.WindowsTotal()-this.m_list_wnd.Total(); if(change==0) return; this.CreateWindowsList(); } //+------------------------------------------------------------------+
Здесь мы считаем разницу между реальным количеством окон графика и количеством объектов-окон в списке. Если разница нулевая, то никаких изменений нет — уходим из метода, иначе — просто заново создаём полный список всех объектов-окон графика, принадлежащих данному объекту-чарту. Пересоздать список гораздо проще, чем искать разницу между объектами, искать отсутствующие/недостающие объекты-окна графика и удалять ненужные или добавлять новые в список.
Напишем реализацию метода, создающего список объектов-окон графика, принадлежащих чарту:
//+------------------------------------------------------------------+ //| Создаёт список окон графика | //+------------------------------------------------------------------+ void CChartObj::CreateWindowsList(void) { this.m_list_wnd.Clear(); int total=this.WindowsTotal(); for(int i=0;i<total;i++) { CChartWnd *wnd=new CChartWnd(m_chart_id,i); if(wnd==NULL) continue; this.m_list_wnd.Sort(); if(!m_list_wnd.Add(wnd)) delete wnd; } } //+------------------------------------------------------------------+
Здесь мы очищаем список объектов-окон, получаем общее количество окон графика из его параметров в терминале и в цикле по полученному количеству окон создаём новый объект-окно графика и добавляем его в список. Если объект в список добавить не удалось — удаляем его для избежания утечки памяти.
Доработка классов библиотеки завершена.
Приступим к созданию класса-коллекции объектов-чартов.
Класс-коллекция объектов-чартов
Точно так же, как и у объекта-чарта, где мы проверяем изменение количества окон графика для активации обновления их количества в объекте, в коллекции объектов-чартов мы будем проверять изменение количества открытых графиков для того, чтобы запустить процесс перестроения списка-коллекции объектов-чартов.
Сначала будем проверять количество окон у уже существующих объектов-чартов, затем — проверять изменение количества открытых графиков, и в случае ненулевого результата проверки будем запускать перестроение коллекции объектов-чартов.
В папке библиотеки \MQL5\Include\DoEasy\Collections\ChartObjCollection.mqh создадим новый файл класса CChartObjCollection. Класс должен быть унаследован от класса базового объекта всех объектов библиотеки CBaseObj, а файл объекта-чарта должен быть подключен к файлу класса-коллекции объектов-чартов:
//+------------------------------------------------------------------+ //| ChartObjCollection.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\Chart\ChartObj.mqh" //+------------------------------------------------------------------+ //| Коллекция объектов-mql5-сигналов | //+------------------------------------------------------------------+ class CChartObjCollection : public CBaseObj { }
В приватной секции класса объявим список CListObj, в котором будут храниться объекты-чарты, переменную для хранения прошлого значения количества открытых графиков в терминале и вспомогательные методы для организации работы класса:
//+------------------------------------------------------------------+ //| Коллекция объектов-mql5-сигналов | //+------------------------------------------------------------------+ class CChartObjCollection : public CBaseObj { private: CListObj m_list; // Список объектов-чартов int m_charts_total_prev; // Прошлое количество чартов в терминале //--- Возвращает количество чартов в терминале int ChartsTotal(void) const; //--- Возвращает флаг существования (1) объекта-чарта, (2) чарта bool IsPresentChartObj(const long chart_id); bool IsPresentChart(const long chart_id); //--- Создаёт новый объект-чарт и добавляет его в список bool CreateNewChartObj(const long chart_id,const string source); //--- Находит отсутствующий объект-чарт, создаёт его и добавляет в список-коллекцию bool FindAndCreateMissingChartObj(void); //--- Находит и удаляет из списка объект-чарт, отсутствующий в терминале void FindAndDeleteExcessChartObj(void); public: }
В публичной секции класса расположим стандартные для объектов библиотеки методы:
public: //--- Возвращает (1) себя, (2) список-коллекцию объектов-чартов CChartObjCollection *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list; } //--- Возвращает список по выбранному (1) целочисленному, (2) вещественному и (3) строковому свойству, удовлетворяющему сравниваемому критерию CArrayObj *GetList(ENUM_CHART_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByChartProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_CHART_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByChartProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_CHART_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByChartProperty(this.GetList(),property,value,mode); } //--- Возвращает количество объектов-чартов в списке int DataTotal(void) const { return this.m_list.Total(); } //--- Выводит в журнал (1) полное, (2) краткое описание коллекции void Print(void); void PrintShort(void); //--- Конструктор CChartObjCollection();
и добавим вспомогательные методы:
//--- Возвращает указатель на объект-чарт (1) по идентификатору, (2) по индексу в списке CChartObj *GetChart(const long id); CChartObj *GetChart(const int index) { return this.m_list.At(index); } //--- Возвращает список объектов-чартов по (1) символу, (2) таймфрейму CArrayObj *GetChartsList(const string symbol) { return this.GetList(CHART_PROP_SYMBOL,symbol,EQUAL); } CArrayObj *GetChartsList(const ENUM_TIMEFRAMES timeframe) { return this.GetList(CHART_PROP_TIMEFRAME,timeframe,EQUAL); } //--- Возвращает идентификатор графика с программой long GetMainChartID(void) const { return CBaseObj::GetMainChartID(); } //--- Создаёт список-коллекцию объектов-чартов bool CreateCollection(void); //--- Обновляет (1) список-коллекцию объектов-чартов, (2) указанный объект-чарт void Refresh(void); void Refresh(const long chart_id); }; //+------------------------------------------------------------------+
Рассмотрим реализацию некоторых методов класса.
В конструкторе класса очистим список объектов коллекции, установим списку флаг сортированного списка, присвоим списку идентификатор коллекции объектов-чартов и сохраним значение количества текущих открытых графиков в терминале:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CChartObjCollection::CChartObjCollection() { this.m_list.Clear(); this.m_list.Sort(); this.m_list.Type(COLLECTION_CHARTS_ID); this.m_charts_total_prev=this.ChartsTotal(); } //+------------------------------------------------------------------+
В MQL нет функции, возвращающей количество открытых графиков, и нет возможности в цикле пройтись по готовому массиву открытых графиков и получить каждый очередной график по индексу цикла. Но есть функция, возвращающая идентификатор самого первого открытого графика ChartFirst() и функция, возвращающая идентификатор следующего графика после указанного ChartNext(). Таким образом мы можем создать цикл по всем открытым графикам, поочерёдно получая каждый последующий график на основании идентификатора предыдущего. В справке по функции ChartNext() есть пример организации такого цикла:
//--- переменные для идентификаторов графиков long currChart,prevChart=ChartFirst(); int i=0,limit=100; Print("ChartFirst = ",ChartSymbol(prevChart)," ID = ",prevChart); while(i<limit)// у нас наверняка не больше 100 открытых графиков { currChart=ChartNext(prevChart); // на основании предыдущего получим новый график if(currChart<0) break; // достигли конца списка графиков Print(i,ChartSymbol(currChart)," ID =",currChart); prevChart=currChart;// запомним идентификатор текущего графика для ChartNext() i++;// не забудем увеличить счетчик }
Вот на основании такого цикла мы и сделаем наши методы, в которых требуется работать с открытыми графиками клиентского терминала.
Метод, создающий список-коллекцию объектов-чартов:
//+------------------------------------------------------------------+ //| Создаёт список-коллекцию объектов-чартов | //+------------------------------------------------------------------+ bool CChartObjCollection::CreateCollection(void) { //--- Очистим список и установим ему флаг сортировки по идентификатору графика m_list.Clear(); m_list.Sort(SORT_BY_CHART_ID); //--- Объявим переменные и получим идентификатор первого графика long curr_chart,prev_chart=::ChartFirst(); int i=0; //--- Создадим первый объект-чарт и добавим его в список if(!this.CreateNewChartObj(prev_chart,DFUN)) return false; //--- В цикле по общему количеству графиков терминала (не более 100) while(i<CHARTS_MAX) { //--- на основании предыдущего получим новый график curr_chart=::ChartNext(prev_chart); //--- Если достигли конца списка графиков - завершаем цикл if(curr_chart<0) break; //--- Создаём объект-чарт на основании идентификатора текущего графика в цикле и добавим его в список if(!this.CreateNewChartObj(curr_chart,DFUN)) return false; //--- запомним идентификатор текущего графика для ChartNext() и увеличим счётчик цикла prev_chart=curr_chart; i++; } //--- Успешно заполнили список return true; } //+------------------------------------------------------------------+
Здесь вся логика метода расписана в комментариях к коду. Если вкратце, то сначала очищаем список-коллекцию от ранее добавленных в него объектов, затем в вышерассмотренном цикле получаем каждый очередной открытый график, на основании его идентификатора создаём новый объект-чарт и добавляем его в список-коллекцию.
Метод, обновляющий список-коллекцию объектов-чартов:
//+------------------------------------------------------------------+ //| Обновляет список-коллекцию объектов-чартов | //+------------------------------------------------------------------+ void CChartObjCollection::Refresh(void) { //--- Получаем количество открытых графиков в терминале и int charts_total=this.ChartsTotal(); //--- рассчитываем разницу между количеством открытых графиков в терминале //--- и объектов-чартов в списке-коллекции, эти значения выводим в комментарии на графике int change=charts_total-this.m_list.Total(); Comment(DFUN,", list total=",DataTotal(),", charts total=",charts_total,", change=",change); //--- Если нет изменений - уходим if(change==0) return; //--- Если добавлен график в терминале if(change>0) { //--- Находим недостающий объект-чарт, создаём и добавляем его в список-коллекцию this.FindAndCreateMissingChartObj(); //--- Получаем текущий график и возвращаемся к нему т. к. //--- добавление нового графика переключает фокус на него CChartObj *chart=this.GetChart(GetMainChartID()); if(chart!=NULL) chart.SetBringToTopON(true); } //--- Если удалён график в терминале else if(change<0) { //--- Находим лишний объект-чарт в списке-коллекции и удаляем его из списка this.FindAndDeleteExcessChartObj(); } //--- В цикле по количеству объектов-чартов в списке for(int i=0;i<this.m_list.Total();i++) { //--- получаем очередной объект-чарт и CChartObj *chart=this.m_list.At(i); if(chart==NULL) continue; //--- обновляем его chart.Refresh(); } } //+------------------------------------------------------------------+
Вся логика метода расписана в комментариях к коду. Вкратце: сначала проверяем изменение количества открытых графиков в клиентском терминале. Если график добавлен, то вызываем метод FindAndCreateMissingChartObj(), в котором ищется недостающий объект-чарт, создаётся и добавляется в список-коллекцию, после чего фокус переключается на текущий график с программой (так как добавление нового графика в терминал переключает фокус на него). Если график удалён из клиентского терминала, то вызываем метод, в котором ищется лишний объект-чарт и удаляется из списка. В завершение все объекты-чарты обновляются — метод Refresh() объекта-чарта проверяет изменение количества окон, присоединённых к чарту, и изменяет их количество в своём списке окон, если были найдены какие-либо изменения.
Метод, обновляющий указанный по идентификатору графика объект-чарт:
//+------------------------------------------------------------------+ //| Обновляет указанный объект-чарт | //+------------------------------------------------------------------+ void CChartObjCollection::Refresh(const long chart_id) { CChartObj *chart=this.GetChart(chart_id); if(chart==NULL) return; chart.Refresh(); } //+------------------------------------------------------------------+
Здесь: получаем из списка-коллекции объект-чарт по идентификатору и обновляем его.
Метод, выводящий в журнал полное описание коллекции:
//+------------------------------------------------------------------+ //| Выводит в журнал полное описание коллекции | //+------------------------------------------------------------------+ void CChartObjCollection::Print(void) { //--- Выводим в журнал заголовок и делаем полную распечатку всех объектов-чартов ::Print(CMessage::Text(MSG_CHART_COLLECTION_TEXT_CHART_COLLECTION),":"); for(int i=0;i<this.m_list.Total();i++) { CChartObj *chart=this.m_list.At(i); if(chart==NULL) continue; chart.Print(); } } //+------------------------------------------------------------------+
Здесь: сначала распечатываем заголовок, затем в цикле по общему количеству объектов в списке-коллекции получаем очередной объект-чарт и выводим его полное описание.
Метод, выводящий в журнал краткое описание коллекции:
//+------------------------------------------------------------------+ //| Выводит в журнал краткое описание коллекции | //+------------------------------------------------------------------+ void CChartObjCollection::PrintShort(void) { //--- Выводим в журнал заголовок и делаем краткую распечатку всех объектов-чартов ::Print(CMessage::Text(MSG_CHART_COLLECTION_TEXT_CHART_COLLECTION),":"); for(int i=0;i<this.m_list.Total();i++) { CChartObj *chart=this.m_list.At(i); if(chart==NULL) continue; chart.PrintShort(true); } } //+------------------------------------------------------------------+
Логика метода идентична вышерассмотренному, за исключением того, что в журнал выводятся краткие описания объектов-чартов.
Метод, возвращающий количество чартов в терминале:
//+------------------------------------------------------------------+ //| Возвращает количество чартов в терминале | //+------------------------------------------------------------------+ int CChartObjCollection::ChartsTotal(void) const { //--- Объявим переменные и получим идентификатор первого графика long currChart,prevChart=::ChartFirst(); int res=1; // Один чарт точно есть - текущий //--- В цикле по общему количеству графиков терминала (не более 100) while(res<CHARTS_MAX) { //--- на основании предыдущего получим новый график currChart=::ChartNext(prevChart); //--- Если достигли конца списка графиков - завершаем цикл if(currChart<0) break; prevChart=currChart; res++; } //--- Возвращаем полученный счётчик цикла return res; } //+------------------------------------------------------------------+
Логика метода расписана в комментариях к коду. Вкратце: один график у нас точно открыт — на нём запущена наша программа. Поэтому счётчик цикла начинается с единицы. Далее в цикле получаем каждый последующий график на основании предыдущего, наращивая при этом счётчик. В итоге после завершения цикла возвращаем полученное в цикле значение счётчика.
Метод, создающий новый объект-чарт и добавляющий его в список-коллекцию:
//+------------------------------------------------------------------+ //| Создаёт новый объект-чарт и добавляет его в список | //+------------------------------------------------------------------+ bool CChartObjCollection::CreateNewChartObj(const long chart_id,const string source) { ::ResetLastError(); CChartObj *chart_obj=new CChartObj(chart_id); if(chart_obj==NULL) { CMessage::ToLog(source,MSG_CHART_COLLECTION_ERR_FAILED_CREATE_CHART_OBJ,true); return false; } this.m_list.Sort(SORT_BY_CHART_ID); if(!this.m_list.InsertSort(chart_obj)) { CMessage::ToLog(source,MSG_CHART_COLLECTION_ERR_FAILED_ADD_CHART,true); delete chart_obj; return false; } return true; } //+------------------------------------------------------------------+
Здесь: в метод передаётся идентификатор графика (chart_id), объект-чарт которого необходимо создать, и наименование (source) метода, из которого вызван данный метод. Создаём новый объект-чарт, и если создать его не удалось — выводим об этом сообщение и возвращаем false. Если объект успешно создан, то устанавливаем списку-коллекции флаг сортированного по идентификаторам графиков списка и пытаемся добавить объект в сортированный список. Если добавить объект в список не удалось, то выводим об этом сообщение, удаляем вновь созданный объект и возвращаем false. Если всё прошло успешно — возвращаем true.
Метод, возвращающий указатель на объект-чарт по идентификатору графика:
//+------------------------------------------------------------------+ //| Возвращает указатель на объект-чарт по идентификатору | //+------------------------------------------------------------------+ CChartObj *CChartObjCollection::GetChart(const long id) { CArrayObj *list=CSelect::ByChartProperty(GetList(),CHART_PROP_ID,id,EQUAL); return(list!=NULL ? list.At(0) : NULL); } //+------------------------------------------------------------------+
Здесь: получаем список объектов-чартов, у которых свойство "Идентификатор графика" равно значению, переданному в метод (в списке может быть только один объект-чарт, так как идентификаторы графиков уникальны). Если объект получен в новый список из одного элемента — возвращаем его. Во всех иных случаях возвращаем NULL — объект не получен.
Метод, возвращающий флаг существования объекта-чарта:
//+------------------------------------------------------------------+ //| Возвращает флаг существования объекта-чарта | //+------------------------------------------------------------------+ bool CChartObjCollection::IsPresentChartObj(const long chart_id) { return(this.GetChart(chart_id)!=NULL); } //+------------------------------------------------------------------+
Здесь: если объект с указанным идентификатором получить из списка-коллекции возможно (результат запроса не равен NULL), то возвращается true. Иначе — возвращается false.
Метод, возвращающий флаг существования чарта в клиентском терминале:
//+------------------------------------------------------------------+ //| Возвращает флаг существования чарта | //+------------------------------------------------------------------+ bool CChartObjCollection::IsPresentChart(const long chart_id) { //--- Объявим переменные и получим идентификатор первого графика long curr_chart,prev_chart=::ChartFirst(); //--- Если идентификаторы совпадают - возвращаем true if(prev_chart==chart_id) return true; int i=0; //--- В цикле по общему количеству графиков терминала (не более 100) while(i<CHARTS_MAX) { //--- на основании предыдущего получим новый график curr_chart=::ChartNext(prev_chart); //--- Если достигли конца списка графиков - завершаем цикл if(curr_chart<0) break; //--- Если идентификаторы совпадают - возвращаем true if(curr_chart==chart_id) return true; //--- запомним идентификатор текущего графика для ChartNext() и увеличим счётчик цикла prev_chart=curr_chart; i++; } return false; } //+------------------------------------------------------------------+
Здесь в цикле по графикам терминала, получаемым на основании идентификатора предыдущего графика, проверяем совпадение идентификатора текущего выбранного в цикле графика, со значением, переданным в метод. Если значения совпадают — график с таким идентификатором есть, возвращаем true.
По окончании цикла возвращаем false — график с запрошенным идентификатором найден не был.
Метод, ищущий отсутствующий объект-чарт, создающий и добавляющий его в список-коллекцию:
//+------------------------------------------------------------------+ //| Находит отсутствующий объект-чарт, | //| создаёт его и добавляет в список-коллекцию | //+------------------------------------------------------------------+ bool CChartObjCollection::FindAndCreateMissingChartObj(void) { //--- Объявим переменные и получим идентификатор первого графика long curr_chart,prev_chart=::ChartFirst(); int i=0; //--- Если объекта первого графика нет в списке - пытаемся создать и добавить его в список if(!this.IsPresentChartObj(prev_chart) && !this.CreateNewChartObj(prev_chart,DFUN)) return false; //--- В цикле по общему количеству графиков терминала (не более 100) ищем остальные графики while(i<CHARTS_MAX) { //--- на основании предыдущего получим новый график curr_chart=::ChartNext(prev_chart); //--- Если достигли конца списка графиков - завершаем цикл if(curr_chart<0) break; //--- Если объекта нет в списке - пытаемся создать и добавить его в список if(!this.IsPresentChartObj(curr_chart) && !this.CreateNewChartObj(curr_chart,DFUN)) return false; //--- запомним идентификатор текущего графика для ChartNext() и увеличим счётчик цикла prev_chart=curr_chart; i++; } return true; } //+------------------------------------------------------------------+
Здесь так же используем цикл по графикам на основании идентификатора предыдущего графика. Сначала определяем наличие/отсутствие объекта-чарта с программой в списке-коллекции, а затем по всем остальным графикам в цикле так же ищем отсутствующие объекты существующих в терминале графиков и добавляем их в список-коллекцию.
Метод, ищущий и удаляющий из списка объект-чарт, отсутствующий в терминале, но присутствующий в списке-коллекции:
//+------------------------------------------------------------------+ //|Находит и удаляет из списка объект-чарт, отсутствующий в терминале| //+------------------------------------------------------------------+ void CChartObjCollection::FindAndDeleteExcessChartObj(void) { for(int i=this.m_list.Total()-1;i>WRONG_VALUE;i--) { CChartObj *chart=this.m_list.At(i); if(chart==NULL) continue; if(!this.IsPresentChart(chart.ID())) { m_list.Delete(i); } } } //+------------------------------------------------------------------+
Здесь: в обратном цикле по всем объектам-чартам в списке коллекции получаем очередной объект-чарт, и если графика с идентификатором как у объекта-чарта нет в клиентском терминале, то удаляем этот объект из списка-коллекции.
На этом создание первой версии класса-коллекции объектов-чартов завершено.
Теперь нам необходимо подключить к главному классу библиотеки CEngine файл созданного класса-коллекции объектов-чартов и создать в нём методы для доступа к методам класса-коллекции объектов-чартов — чтобы мы могли работать с ними из наших программ.
В файле \MQL5\Include\DoEasy\Engine.mqh класса 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 "Collections\TimeSeriesCollection.mqh" #include "Collections\BuffersCollection.mqh" #include "Collections\IndicatorsCollection.mqh" #include "Collections\TickSeriesCollection.mqh" #include "Collections\BookSeriesCollection.mqh" #include "Collections\MQLSignalsCollection.mqh" #include "Collections\ChartObjCollection.mqh" #include "TradingControl.mqh" //+------------------------------------------------------------------+
В приватной секции класса объявим объект класса-коллекции чартов:
//+------------------------------------------------------------------+ //| Класс-основа библиотеки | //+------------------------------------------------------------------+ class CEngine { private: CHistoryCollection m_history; // Коллекция исторических ордеров и сделок CMarketCollection m_market; // Коллекция рыночных ордеров и сделок CEventsCollection m_events; // Коллекция событий CAccountsCollection m_accounts; // Коллекция аккаунтов CSymbolsCollection m_symbols; // Коллекция символов CTimeSeriesCollection m_time_series; // Коллекция таймсерий CBuffersCollection m_buffers; // Коллекция индикаторных буферов CIndicatorsCollection m_indicators; // Коллекция индикаторов CTickSeriesCollection m_tick_series; // Коллекция тиковых серий CMBookSeriesCollection m_book_series; // Коллекция серий стаканов цен CMQLSignalsCollection m_signals_mql5; // Коллекция сигналов сервиса сигналов mql5.com CChartObjCollection m_charts; // Коллекция чартов CResourceCollection m_resource; // Список ресурсов CTradingControl m_trading; // Объект управления торговлей CPause m_pause; // Объект "Пауза" CArrayObj m_list_counters; // Список счётчиков таймера
В публичной секции класса расположим методы для доступа к классу-коллекции объектов-чартов:
//--- Выводит в журнал (1) полное, (2) краткое описание коллекции, (3) параметры настроек копирования сигналов void SignalsMQL5Print(void) { m_signals_mql5.Print(); } void SignalsMQL5PrintShort(const bool list=false,const bool paid=true,const bool free=true) { m_signals_mql5.PrintShort(list,paid,free); } void SignalsMQL5CurrentSubscriptionParameters(void) { this.m_signals_mql5.CurrentSubscriptionParameters();} //--- Создаёт коллекцию чартов bool ChartCreateCollection(void) { return this.m_charts.CreateCollection(); } //--- Возвращает (1) коллекцию чартов, (2) список чартов из коллекции чартов CChartObjCollection *GetChartObjCollection(void) { return &this.m_charts; } CArrayObj *GetListCharts(void) { return this.m_charts.GetList(); } //--- Возвращает (1) указанный объект-чарт, (2) объект-чарт с программой CChartObj *ChartGetChartObj(const long chart_id) { return this.m_charts.GetChart(chart_id); } CChartObj *ChartGetMainChart(void) { return this.m_charts.GetChart(this.m_charts.GetMainChartID());} //--- Обновляет (1) указанный по идентификатору чарт, (2) всех чарты void ChartRefresh(const long chart_id) { this.m_charts.Refresh(chart_id); } void ChartsRefreshAll(void) { this.m_charts.Refresh(); } //--- Возвращает список объектов-чартов по (1) символу, (2) таймфрейму CArrayObj *ChartGetChartsList(const string symbol) { return this.m_charts.GetChartsList(symbol); } CArrayObj *ChartGetChartsList(const ENUM_TIMEFRAMES timeframe) { return this.m_charts.GetChartsList(timeframe); } //--- Возвращает (1) коллекцию буферов, (2) список буферов из коллекции
Все только что добавленные методы просто возвращают результат вызова соответствующих методов коллекции объектов-чартов, только что нами рассмотренных выше. Все эти методы будут видны и доступны для использования в наших программах, созданных на основе библиотеки.
В конструкторе класса добавим создание нового счётчика — для класса-коллекции объектов-чартов:
//+------------------------------------------------------------------+ //| CEngine конструктор | //+------------------------------------------------------------------+ CEngine::CEngine() : m_first_start(true), m_last_trade_event(TRADE_EVENT_NO_EVENT), m_last_account_event(WRONG_VALUE), m_last_symbol_event(WRONG_VALUE), m_global_error(ERR_SUCCESS) { this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; this.m_is_tester=::MQLInfoInteger(MQL_TESTER); this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE); this.m_name=::MQLInfoString(MQL_PROGRAM_NAME); this.m_list_counters.Sort(); this.m_list_counters.Clear(); this.CreateCounter(COLLECTION_ORD_COUNTER_ID,COLLECTION_ORD_COUNTER_STEP,COLLECTION_ORD_PAUSE); this.CreateCounter(COLLECTION_ACC_COUNTER_ID,COLLECTION_ACC_COUNTER_STEP,COLLECTION_ACC_PAUSE); this.CreateCounter(COLLECTION_SYM_COUNTER_ID1,COLLECTION_SYM_COUNTER_STEP1,COLLECTION_SYM_PAUSE1); this.CreateCounter(COLLECTION_SYM_COUNTER_ID2,COLLECTION_SYM_COUNTER_STEP2,COLLECTION_SYM_PAUSE2); this.CreateCounter(COLLECTION_REQ_COUNTER_ID,COLLECTION_REQ_COUNTER_STEP,COLLECTION_REQ_PAUSE); this.CreateCounter(COLLECTION_TS_COUNTER_ID,COLLECTION_TS_COUNTER_STEP,COLLECTION_TS_PAUSE); this.CreateCounter(COLLECTION_IND_TS_COUNTER_ID,COLLECTION_IND_TS_COUNTER_STEP,COLLECTION_IND_TS_PAUSE); this.CreateCounter(COLLECTION_TICKS_COUNTER_ID,COLLECTION_TICKS_COUNTER_STEP,COLLECTION_TICKS_PAUSE); this.CreateCounter(COLLECTION_CHARTS_COUNTER_ID,COLLECTION_CHARTS_COUNTER_STEP,COLLECTION_CHARTS_PAUSE); ::ResetLastError(); #ifdef __MQL5__ if(!::EventSetMillisecondTimer(TIMER_FREQUENCY)) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError()); this.m_global_error=::GetLastError(); } //---__MQL4__ #else if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY)) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_TIMER),(string)::GetLastError()); this.m_global_error=::GetLastError(); } #endif //--- } //+------------------------------------------------------------------+
В таймере класса добавим блок кода для работы с коллекцией объектов-чартов:
//+------------------------------------------------------------------+ //| CEngine таймер | //+------------------------------------------------------------------+ void CEngine::OnTimer(SDataCalculate &data_calculate) { //--- Если это не тестер - работаем с событиями коллекций по таймеру if(!this.IsTester()) { //--- Таймер коллекций исторических ордеров и сделок и рыночных ордеров и позиций int index=this.CounterIndex(COLLECTION_ORD_COUNTER_ID); CTimerCounter* cnt1=this.m_list_counters.At(index); if(cnt1!=NULL) { //--- Если пауза завершилась - работаем с событиями коллекций ордеров, сделок и позиций if(cnt1.IsTimeDone()) this.TradeEventsControl(); } //--- Таймер коллекции аккаунтов index=this.CounterIndex(COLLECTION_ACC_COUNTER_ID); CTimerCounter* cnt2=this.m_list_counters.At(index); if(cnt2!=NULL) { //--- Если пауза завершилась - работаем с событиями коллекции аккаунтов if(cnt2.IsTimeDone()) this.AccountEventsControl(); } //--- Таймер1 коллекции символов (обновление котировочных данных символов в коллекции) index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID1); CTimerCounter* cnt3=this.m_list_counters.At(index); if(cnt3!=NULL) { //--- Если пауза завершилась - обновляем котировочные данные всех символов в коллекции if(cnt3.IsTimeDone()) this.m_symbols.RefreshRates(); } //--- Таймер2 коллекции символов (обновление всех данных всех символов в коллекции и отслеживание событий символов и списка символов в окне обзора рынка) index=this.CounterIndex(COLLECTION_SYM_COUNTER_ID2); CTimerCounter* cnt4=this.m_list_counters.At(index); if(cnt4!=NULL) { //--- Если пауза завершилась if(cnt4.IsTimeDone()) { //--- обновляем данные и работаем с событиями всех символов в коллекции this.SymbolEventsControl(); //--- Если работаем со списком из обзора рынка - проверяем события окна обзора рынка if(this.m_symbols.ModeSymbolsList()==SYMBOLS_MODE_MARKET_WATCH) this.MarketWatchEventsControl(); } } //--- Таймер торгового класса index=this.CounterIndex(COLLECTION_REQ_COUNTER_ID); CTimerCounter* cnt5=this.m_list_counters.At(index); if(cnt5!=NULL) { //--- Если пауза завершилась - работаем со списком отложенных запросов if(cnt5.IsTimeDone()) this.m_trading.OnTimer(); } //--- Таймер коллекции таймсерий index=this.CounterIndex(COLLECTION_TS_COUNTER_ID); CTimerCounter* cnt6=this.m_list_counters.At(index); if(cnt6!=NULL) { //--- Если пауза завершилась - работаем со списком таймсерий (обновляем все кроме текущей) if(cnt6.IsTimeDone()) this.SeriesRefreshAllExceptCurrent(data_calculate); } //--- Таймер коллекции таймсерий данных индикаторных буферов index=this.CounterIndex(COLLECTION_IND_TS_COUNTER_ID); CTimerCounter* cnt7=this.m_list_counters.At(index); if(cnt7!=NULL) { //--- Если пауза завершилась - работаем со списком таймсерий индикаторных данных (обновляем все кроме текущей) if(cnt7.IsTimeDone()) this.IndicatorSeriesRefreshAll(); } //--- Таймер коллекции тиковых серий index=this.CounterIndex(COLLECTION_TICKS_COUNTER_ID); CTimerCounter* cnt8=this.m_list_counters.At(index); if(cnt8!=NULL) { //--- Если пауза завершилась - работаем со списком тиковых серий (обновляем все кроме текущей) if(cnt8.IsTimeDone()) this.TickSeriesRefreshAllExceptCurrent(); } //--- Таймер коллекции чартов index=this.CounterIndex(COLLECTION_CHARTS_COUNTER_ID); CTimerCounter* cnt9=this.m_list_counters.At(index); if(cnt9!=NULL) { //--- Если пауза завершилась - работаем со списком чартов if(cnt9.IsTimeDone()) this.ChartsRefreshAll(); } } //--- Если тестер - работаем с событиями коллекций по тику else { //--- работаем с событиями коллекций ордеров, сделок и позиций по тику this.TradeEventsControl(); //--- работаем с событиями коллекции аккаунтов по тику this.AccountEventsControl(); //--- обновляем котировочные данные всех символов в коллекции по тику this.m_symbols.RefreshRates(); //--- работаем с событиями всех символов в коллекции по тику this.SymbolEventsControl(); //--- работаем со списком отложенных запросов по тику this.m_trading.OnTimer(); //--- работаем со списком таймсерий по тику this.SeriesRefresh(data_calculate); //--- работаем со списком таймсерий индикаторных буферов по тику this.IndicatorSeriesRefreshAll(); //--- работаем со списком тиковых серий по тику this.TickSeriesRefreshAll(); } } //+------------------------------------------------------------------+
Как только счётчик таймера коллекции отсчитает полсекунды, будет вызван метод обновления всех объектов-чартов в их коллекции. Таким образом мы автоматизировали отслеживание изменений в количестве графиков в терминале и окон, присоединённых к каждому открытому графику.
Протестируем работу созданного сегодня класса.
Тестирование
Для тестирования возьмём советник из прошлой статьи и сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part69\ под новым именем TestDoEasyPart69.mq5.
По сути, мы не будем особо ничего изменять в логике предыдущего советника — мы точно так же выведем краткие описания всех объектов-чартов, графики которых открыты в терминале, а для основного графика выведем его полное описание. Помимо этого у нас в классе-коллекции объектов-чартов в методе обновления списка коллекции добавлен вывод комментария на график, сообщающего нам сколько объектов-чартов у нас есть в коллекции, сколько соответствующих им графиков открыто в терминале, и какова разница сравнения их количества. Впоследствии мы уберём этот отладочный комментарий. А сегодня проверим по нему правильность работы класса-коллекции объектов-чартов.
В первую очередь удалим подключение файла класса объекта-чарта из кода советника:
//+------------------------------------------------------------------+ //| TestDoEasyPart68.mq5 | //| Copyright 2021, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> #include <DoEasy\Objects\Chart\ChartObj.mqh> //--- enums
Теперь у нас класс-коллекция объектов-чартов подключен к главному объекту библиотеки и все классы чартов и их коллекции доступны из него.
В блоке входных переменных добавим переменную, указывающую нам флаг необходимости использования в программе класса-коллекции объектов-чартов:
//--- 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 ENUM_TIMEFRAMES_MODE InpModeUsedTFs = TIMEFRAMES_MODE_LIST; // Mode of used timeframes list sinput string InpUsedTFs = "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator) sinput ENUM_INPUT_YES_NO InpUseBook = INPUT_YES; // Use Depth of Market sinput ENUM_INPUT_YES_NO InpUseMqlSignals = INPUT_YES; // Use signal service sinput ENUM_INPUT_YES_NO InpUseCharts = INPUT_YES; // Use Charts control sinput ENUM_INPUT_YES_NO InpUseSounds = INPUT_YES; // Use sounds //--- global variables
Из обработчика OnTick() советника удалим блок кода, в котором мы создавали список объектов-чартов и выводили их описания в журнал:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Обработка события NewTick в библиотеке engine.OnTick(rates_data); //--- Если работа в тестере if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(rates_data); // Работа в таймере PressButtonsControl(); // Контроль нажатия кнопок engine.EventsHandling(); // Работа с событиями } //--- Если установлен флаг трейлинга if(trailing_on) { TrailingPositions(); // Трейлинг позиций TrailingOrders(); // Трейлинг отложенных ордеров } //--- Если первый запуск static bool done=false; if(!done) { //--- Создадим объект-список для хранения объектов-чартов CArrayObj *list=new CArrayObj(); if(list==NULL) return; //--- Объявим переменные и получим идентификатор первого графика long currChart,prevChart=ChartFirst(); int i=0; //--- Создадим объект-чарт и добавим его в список CChartObj *chart_first=new CChartObj(prevChart); list.Add(chart_first); //--- В цикле по общему количеству графиков терминала (не более 100) while(i<CHARTS_MAX) { //--- на основании предыдущего получим новый график currChart=ChartNext(prevChart); //--- Если достигли конца списка графиков - завершаем цикл if(currChart<0) break; //--- Создаём объект-чарт на основании идентификатора текущего графика в цикле и добавим его в список CChartObj *chart=new CChartObj(currChart); list.Add(chart); //--- запомним идентификатор текущего графика для ChartNext() и увеличим счётчик цикла prevChart=currChart; i++; } Print(""); //--- Из заполненного списка в цикле получим очередной объект-чарт и выведем его краткое описание int total=list.Total(); for(int j=0;j<total;j++) { CChartObj *chart_obj=list.At(j); if(chart_obj!=NULL) chart_obj.PrintShort(); } Print(""); //--- Выведем полное описание текущего графика: в цикле по всем объектам созданного списка for(int j=0;j<total;j++) { //--- получим очередной объект-чарт и CChartObj *chart_obj=list.At(j); //--- если его символ совпадает с символом текущего графика - выведем в журнал его полное описание if(chart_obj!=NULL && chart_obj.Symbol()==Symbol()) chart_obj.Print(); } //--- Уничтожим список объектов-чартов delete list; done=true; } //--- } //+------------------------------------------------------------------+
Теперь всё то же самое у нас будет выполняться этими строчками кода:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Обработка события NewTick в библиотеке engine.OnTick(rates_data); //--- Если работа в тестере if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(rates_data); // Работа в таймере PressButtonsControl(); // Контроль нажатия кнопок engine.EventsHandling(); // Работа с событиями } //--- Если установлен флаг трейлинга if(trailing_on) { TrailingPositions(); // Трейлинг позиций TrailingOrders(); // Трейлинг отложенных ордеров } //--- Если первый запуск static bool done=false; if(!done) { //--- Получим из коллекции чарт с советником и выведем его полное описание CChartObj *chart_main=engine.ChartGetMainChart(); Print(""); chart_main.Print(); done=true; } //--- } //+------------------------------------------------------------------+
Здесь у нас выводится только полное описание чарта, на котором запущен советник. Краткие же описания всех объектов-чартов коллекции мы будем выводить в функции OnInitDoEasy() в этом блоке кода:
//--- Если работа с сигналами не разрешена или не удалось создать коллекцию сигналов - //--- устанавливаем запрет на копирование сделок по подписке else engine.SignalsMQL5CurrentSetSubscriptionEnableOFF(); //--- Создание коллекции чартов //--- Если работа с чартами и коллекция чартов создана if(InpUseCharts && engine.ChartCreateCollection()) { //--- Проверка созданных объектов-чартов - выводим в журнал короткое описание коллекции engine.GetChartObjCollection().PrintShort(); } //--- Создание тестовых файлов ресурсов
В обработчике OnBookEvent() советника закомментируем вывод отладочной информации на график от класса-коллекции стаканов цен, так как сейчас нам не нужно видеть эту информацию (пока она присутствует ввиду иногда странного поведения терминала при работе со стаканом цен — советник иногда достаточно длительное время не откликается, и эта информация — часто меняющая свои значения — позволяет визуально видеть работу советника), да и нам будет мешать вывод этого комментария видеть изменения, регистрируемые классом-коллекцией объектов-чартов.
//+------------------------------------------------------------------+ //| OnBookEvent function | //+------------------------------------------------------------------+ void OnBookEvent(const string& symbol) { static bool first=true; //--- Если не удалось обновить серию снимков стакана символа - уходим if(!engine.OnBookEvent(symbol)) return; //--- Работаем по текущему символу if(symbol==Symbol()) { //--- Получаем серию снимков стакана цен текущего символа CMBookSeries *book_series=engine.GetMBookSeries(symbol); if(book_series==NULL) return; //--- Получаем последний объект-снимок стакана цен из объекта-серии снимков стакана цен CMBookSnapshot *book=book_series.GetLastMBook(); if(book==NULL) return; //--- Получаем самый первый и самый последний объекты-заявки стакана цен из объекта-снимка стакана цен CMarketBookOrd *ord_0=book.GetMBookByListIndex(0); CMarketBookOrd *ord_N=book.GetMBookByListIndex(book.DataTotal()-1); if(ord_0==NULL || ord_N==NULL) return; //--- Выводим на график в комментарии время текущего снимка стакана цен, //--- максимальное количество показываемых заявок в стакане для символа, //--- полученное количество заявок в текущем снимке стакана цен, //--- общее количество снимков стакана цен, записанных в список-серию и //--- максимальную и минимальную по цене заявки текущего снимка стакана цен //Comment // ( // DFUN,book.Symbol(),": ",TimeMSCtoString(book.Time()), // //", symbol book size=",sym.TicksBookdepth(), // ", last book data total: ",book.DataTotal(), // ", series books total: ",book_series.DataTotal(), // "\nMax: ",ord_N.Header(),"\nMin: ",ord_0.Header() // ); //--- Выведем в журнал первый снимок стакана цен if(first) { //--- описание серии book_series.Print(); //--- описание снимка book.Print(); first=false; } } } //+------------------------------------------------------------------+
Скомпилируем советник. Откроем в терминале четыре любых графика (на первом запустим советник) и создадим такое же окружение, какое было при тестировании в прошлой статье:
На чарт с советником в его главное окно добавим индикатор фракталов + добавим окно индикатора, например, DeMarker, в который разместим ещё один, например, AMA, рассчитываемый на данных DeMarker.
На втором графике расположим окно стохастика...
После запуска советника в журнал будут выведены данные о созданных объектах коллекции объектов-чартов и о чарте с программой:
Chart collection: - Main chart window EURUSD H4 ID: 131733844391938630, HWND: 920114, Subwindows: 1 - Main chart window GBPUSD H4 ID: 131733844391938633, HWND: 592798, Subwindows: No - Main chart window AUDUSD H1 ID: 131733844391938634, HWND: 527424, Subwindows: 2 - Main chart window USDRUB H4 ID: 131733844391938635, HWND: 920468, Subwindows: No Subscribed to Depth of Market EURUSD Library initialization time: 00:00:05.922 ============= The beginning of the parameter list (Main chart window EURUSD H4) ============= Chart ID: 131733844391938630 Timeframe: H4 Drawing attributes of a price chart: Yes Object "Chart": No Chart on top of other charts: No Accessing the context menu by pressing the right mouse button: Yes Accessing the "Crosshair tool" by pressing the middle mouse button: Yes Scrolling the chart horizontally using the left mouse button: Yes Sending messages about mouse wheel events to all mql5 programs on a chart: No Send notifications of mouse move and mouse click events to all mql5 programs on a chart: No Send a notification of an event of new object creation to all mql5-programs on a chart: No Send a notification of an event of object deletion to all mql5-programs on a chart: No Chart type: Display as Japanese candlesticks Price chart in the foreground: No Price chart indent from the right border: Yes Automatic moving to the right border of the chart: Yes Managing the chart using a keyboard: Yes Allowed to intercept Space and Enter key presses on the chart to activate the quick navigation bar: Yes Scale: 2 Fixed scale mode: No Scale 1:1 mode: No Scale to be specified in points per bar: No Display a symbol ticker in the upper left corner: Yes Display OHLC values in the upper left corner: Yes Display Bid values as a horizontal line in a chart: Yes Display Ask values as a horizontal line in a chart: Yes Display Last values as a horizontal line in a chart: No Display vertical separators between adjacent periods: No Display grid in the chart: No Display volume in the chart: Trade volumes Display textual descriptions of objects: Yes The number of bars on the chart that can be displayed: 137 The total number of chart windows, including indicator subwindows: 2 Chart window handle: 920114 Number of the first visible bar in the chart: 136 Chart width in bars: 168 Chart width in pixels: 670 Main chart window: - Chart height in pixels: 293 Chart subwindow 1: - The distance between the upper frame of the indicator subwindow and the upper frame of the main chart window: 295 - Chart height in pixels: 21 Chart background color: clrWhite Color of axes, scales and OHLC line: clrBlack Grid color: clrSilver Color of volumes and position opening levels: clrGreen Color for the up bar, shadows and body borders of bull candlesticks: clrBlack Color for the down bar, shadows and body borders of bear candlesticks: clrBlack Line chart color and color of "Doji" Japanese candlesticks: clrBlack Body color of a bull candlestick: clrWhite Body color of a bear candlestick: clrBlack Bid price level color: clrLightSkyBlue Ask price level color: clrCoral Line color of the last executed deal price (Last): clrSilver Color of stop order levels (Stop Loss and Take Profit): clrOrangeRed Displaying trade levels in the chart (levels of open positions, Stop Loss, Take Profit and pending orders): Yes Permission to drag trading levels on a chart with a mouse: Yes Showing the time scale on a chart: Yes Showing the price scale on a chart: Yes Showing the "One click trading" panel on a chart: No Chart window is maximized: Yes Chart window is minimized: No The chart window is docked: Yes The left coordinate of the undocked chart window relative to the virtual screen: 2028 The top coordinate of the undocked chart window relative to the virtual screen: 100 The right coordinate of the undocked chart window relative to the virtual screen: 2654 The bottom coordinate of the undocked chart window relative to the virtual screen: 329 ------ The size of the zero bar indent from the right border in percents: 18.93 Chart fixed position from the left border in percent value: 0.00 Fixed chart maximum: 1.21320 Fixed chart minimum : 1.16950 Scale in points per bar: 1.00 Chart minimum: 1.16950 Chart maximum: 1.21320 ------ Text of a comment in a chart: "" The name of the Expert Advisor running on the chart: "TestDoEasyPart69" The name of the script running on the chart: "" Indicators in the main chart window: - Indicator Fractals Indicators in the chart window 1: - Indicator DeM(14) - Indicator AMA(14,2,30) Symbol: "EURUSD" ============= End of the parameter list (Main chart window EURUSD H4) =============
Затем сначала добавим несколько графиков, а потом удалим добавленные. В комментарии на графике будут отражаться происходящие изменения в количестве объектов-чартов в коллекции, количестве открытых в терминале графиков и значение разницы сравнения количества графиков и объектов:
Что дальше
В следующей статье продолжим работу над коллекцией объектов-чартов для расширения её функционала.
Ниже прикреплены все файлы текущей версии библиотеки и файл тестового советника для MQL5. Их можно скачать и протестировать всё самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.
*Статьи этой серии:
Прочие классы в библиотеке DoEasy (Часть 67): Класс объекта-чарта
Прочие классы в библиотеке DoEasy (Часть 68): Класс объекта-окна графика и классы объектов-индикаторов в окне графика
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования