Взгляни на рынок через готовые классы
Введение
Главные цели, которые ставились - получить удобный в пользовании и одновременно расширяемый инструмент для представления произвольной текстовой информации на экране торгового терминала. Это может быть, например, текущее состояние настроек эксперта или результаты его работы за заданный интервал, представленные в виде таблицы. Это могут быть ценовые значения или таблица значений индикаторов, используемых трейдером в работе, или собственный лог торгующего эксперта. Вся эта информация может динамически выводиться на экран в процессе работы эксперта/индикатора, использующего данную библиотеку.
В качестве основы для разработки библиотеки взят набор стандартных классов, поставляемых вместе с терминалом MetaTrader 5. Для начала, рассмотрим этот набор и затем решим, как нужно его дополнить и расширить, чтобы выполнить поставленную задачу.
Стандартные классы для создания графических объектов
Интересующий нас набор классов находится в директориях Include и Include\ChartObjects.
Это следующие файлы:
- Object.mqh - содержит один базовый класс CObject для построения всех остальных, связанных с графическими объектами.
- ChartObject.mqh - также содержит один класс CChartObject, порожденный от CObject, расширяющий функциональность и инкапсулирующий общие данные и методы всех графических объектов.
- ChartObjectsTxtControls.mqh - содержит ряд классов, предназначенных для вывода на экран различных графических объектов, содержащих текст. Это базовый для них класс CChartObjectText и классы-потомки CChartObjectLabel, CChartObjectEdit и CChartObjectButton.
Рассмотрим эти классы подробней.
Базовый класс общего назначения - CObject
Поскольку текст описания класса небольшой, приведем его полностью:
class CObject { protected: CObject *m_prev; CObject *m_next; public: CObject(); CObject *Prev() { return(m_prev); } void Prev(CObject *node) { m_prev=node; } CObject *Next() { return(m_next); } void Next(CObject *node) { m_next=node; } virtual bool Save(int file_handle) { return(true); } virtual bool Load(int file_handle) { return(true); } virtual int Type() const { return(0); } protected: virtual int Compare(const CObject *node,int mode=0) const { return(0); } }; //+------------------------------------------------------------------+ void CObject::CObject() { m_prev=NULL; m_next=NULL; }
Как видим, данный класс содержит только данные и методы общего назначения, не имеющие прямого отношения к выводу на экран терминала. Зато он обладает очень важным свойством - его можно использовать для построения односвязных и двусвязных списков. Такое свойство ему дают поля-данные CObject::m_prev и CObject::m_next типа CObject* и методы их чтения/записи. Поле CObject::m_prev указывает на предыдущий элемент списка, а CObject::m_next - на следующий. Более подробно о построении списков будет рассказано дальше.
Кроме того, есть метод сравнения двух объектов типа CObject* - это метод CObject::Compare, который может использоваться при сортировке элементов внутри списка. Есть еще два полезных метода, позволяющих сохранять/считывать поля данных в файлах - это методы CObject::Save и CObject::Load, соответственно. Данные методы нужно переопределять в классах-потомках для получения нужной функциональности. Еще один метод - идентификация типа объекта - CObject::Type. Данный метод бывает полезен при манипулировании списком, содержащим объекты разного типа.
Итак, базовый класс CObject (и его экземпляры) обладает следующими свойствами:
- идентификация своего положения относительно соседних элементов внутри списка.
- идентификация своего типа.
- сохранение в файле полей-данных и восстановление их из файла.
- сравнение себя с заданным объектом своего типа.
Большинство описанных выше методов не имеют полезной реализации в базовом классе, поскольку базовый класс не имеет реальных свойств, несущих физический смысл. Как и положено в ООП, потомок должен реализовать ту функциональность, которая является новой по отношению к предкам.
Базовый класс для построения графических объектов - CChartObject
CChartObject - это потомок класса CObject. Как можно определить уже по его названию - это класс для описания некоего абстрактного графического объекта. Тем не менее, этот абстрактный объект уже содержит реальные физические свойства и методы работы с этими свойствами. Эти свойства являются общими для всех графических объектов, существующих в терминале MetaTrader 5, поэтому логично их поместить в данном классе.
Рассмотрим их более подробно. Для привязки графического объекта в заданном окне используются следующие данные:
protected: long m_chart_id; // идентификатор графика, на котором лежит // данный графический объект (0 - текущий график) int m_window; // номер подокна ( 0 - главное окно ) string m_name; // уникальное (в пределах графика) имя объекта int m_num_points; // число точек для привязки объекта
Прежде чем задавать или считывать свойства реального графического объекта, он должен быть привязан к его программному "образу". Это делает метод CChartObject::Attach. В потомках данного класса он вызывается сразу после создания графического объекта на экране.
bool CChartObject::Attach(long chart_id,string name,int window,int points) { if(ObjectFind(chart_id,name)<0) { return(false); } if(chart_id==0) chart_id=ChartID(); m_chart_id =chart_id; m_window =window; m_name =name; m_num_points=points; return(true); }
Сначала проверяется существование реального графического объекта. Если он существует, то его свойства запоминаются во внутренних полях объекта класса CChartObject. После этого можно считывать и изменять свойства графического объекта (цвет, координаты и т.д.).
Здесь уже реализованы методы сохранения CChartObject::Save и считывания CChartObject::Load свойств графического объекта. В классе-потомке следует сначала вызывать родительские методы сохранения/чтения, затем собственные.
Класс CChartObject (и его экземпляры) обладает следующими новыми свойствами по сравнению с базовым:
- привязка реального графического объекта к своему программному "образу".
- чтение и модификация общих для всех графических объектов свойств.
- удаление графического объекта с экрана.
- перемещение графического объекта по экрану (позиционирование).
Класс для построения текстовых графических объектов - CChartObjectText
Обратимся теперь к файлу ChartObjectsTxtControls.mqh. Здесь находятся описания классов, предназначенных для вывода на экран различных графических объектов, содержащих текст. Рассмотрим их основные особенности.
Базовый класс для них - CChartObjectText. Он инкапсулирует в себе свойства и методы, связанные с отображением текста.
Приведем полный текст описания данного класса:
class CChartObjectText : public CChartObject { public: double Angle() const; bool Angle(double angle); string Font() const; bool Font(string font); int FontSize() const; bool FontSize(int size); ENUM_ANCHOR_POINT Anchor() const; bool Anchor(ENUM_ANCHOR_POINT anchor); bool Create(long chart_id,string name,int window,datetime time,double price); virtual int Type() const { return(OBJ_TEXT); } virtual bool Save(int file_handle); virtual bool Load(int file_handle); };
По сравнению с CChartObject, содержит методы чтения и модификации свойств именно текстовых графических объектов - угла ориентации текста на экране, названия шрифта при выводе текста, размера шрифта и способа (точки) привязки координат графического объекта. Появился новый метод CChartObjectText::Create, позволяющий создать на экране реальный графический объект типа OBJ_TEXT.
Его реализация следующая:
bool CChartObjectText::Create(long chart_id,string name,int window,datetime time,double price) { bool result = ObjectCreate( chart_id, name, OBJ_TEXT, window, time, price ); if(result) { result &= Attach(chart_id, name, window, 1 ); } return(result); }
В случае успешного создания графического объекта (функция ObjectCreate возвращает истинное значение), вызывается метод CChartObject::Attach, который мы рассмотрели ранее.
Итак, по сравнению с родительским классом, CChartObjectText содержит новые свойства:
- чтение и модификация свойств текстовых графических объектов.
- создание реального графического объекта на экране терминала типа OBJ_TEXT.
Класс для создания графического объекта типа "Текстовая метка" - CChartObjectLabel
Следующий класс в иерархии стандартных - CChartObjectLabel. Позволяет создавать на экране графический объект типа OBJ_LABEL (текстовая метка).
Приведем описание данного класса:
class CChartObjectLabel : public CChartObjectText { public: int X_Distance() const; bool X_Distance(int X); int Y_Distance() const; bool Y_Distance(int Y); int X_Size() const; int Y_Size() const; ENUM_BASE_CORNER Corner() const; bool Corner(ENUM_BASE_CORNER corner); bool Time(datetime time) { return(false); } bool Price(double price) { return(false); } bool Create(long chart_id,string name,int window,int X,int Y); virtual int Type() const { return(OBJ_LABEL); } virtual bool Save(int file_handle); virtual bool Load(int file_handle); };
Здесь надо отметить различие между графическим объектом типа OBJ_TEXT и объектом типа OBJ_LABEL. Первый привязывается к ценовому графику (в координатах цена-время) или к графику в подокне. Второй привязывается к экранным координатам окна или подокна (пикселям), в котором отображается график. Поэтому, объекты типа OBJ_TEXT перемещаются вместе с графиком при скроллировании, а объекты типа OBJ_LABEL остаются неподвижными относительно перемещаемого графика при его скроллировании. Соответственно решаемой задаче следует выбирать конкретный тип графического текстового объекта.
По сравнению с родительским, класс CChartObjectLabel содержит следующие отличительные свойства:
- позиционирование графического объекта производится в экранных координатах окна.
- чтение и модификация угла привязки, относительно которого будут задаваться координаты графического объекта. Фактически, это задание начала координат в одном из четырех углов окна графика.
- создание реального графического объекта на экране терминала типа OBJ_LABEL.
Класс для создания графического объекта типа "Поле ввода" - CChartObjectEdit
Следующий класс в иерархии - CChartObjectEdit. Это класс для создания графического объекта типа OBJ_EDIT (поле ввода). Поскольку объект данного типа привязывается также, как и объект типа OBJ_LABEL - к экранным координатам (пикселям), то логично наследовать его от класса CChartObjectLabel, что и сделано в библиотеке стандартных классов:
class CChartObjectEdit : public CChartObjectLabel { public: bool X_Size(int X); bool Y_Size(int Y); color BackColor() const; bool BackColor(color new_color); bool ReadOnly() const; bool ReadOnly(bool flag); bool Angle(double angle) { return(false); } bool Create(long chart_id,string name,int window,int X,int Y,int sizeX,int sizeY); virtual int Type() const { return(OBJ_EDIT); } virtual bool Save(int file_handle); virtual bool Load(int file_handle); };
Отличия поля ввода типа OBJ_EDIT от текстовой метки следующие:
- поле ввода имеет свойства ширина и высота (задается в экранных пикселях), которые ограничивают размер графического объекта на экране. Размер текстовой метки же подстраивается автоматически так, чтобы полностью был виден текст.
- имеются методы разрешения/запрещения модификации текста - CChartObjectEdit::ReadOnly.
- добавлен метод, позволяющий менять цвет фона участка, занимаемый графическим объектом.
- отсутствует возможность изменения угла, под которым отображается объект. Поле ввода может отображаться только в горизонтальном направлении.
- создание реального графического объекта на экране терминала типа OBJ_EDIT.
Класс для создания графического объекта типа "Кнопка" - CChartObjectButton
Еще один класс в иерархии текстовых графических объектов - CChartObjectButton. Этот объект называется кнопка, он предназначен для создания управляющего элемента на экране в виде нажимающейся кнопки. Этот класс является потомком класса CChartObjectEdit и наследует его функциональность:
class CChartObjectButton : public CChartObjectEdit { public: bool State() const; bool State(bool state); virtual int Type() const { return(OBJ_BUTTON); } virtual bool Save(int file_handle); virtual bool Load(int file_handle); };
Отличия кнопки типа OBJ_BUTTON от поля ввода следующие:
- внешнее отображение в виде нажимающейся кнопки, аналогичной используемых в диалоговых окнах Windows.
- новые методы для считывания/модификации состояния кнопки (нажата/отжата) - CChartObjectButton::State.
- создание реального графического объекта на экране терминала типа OBJ_BUTTON.
Общая структура стандартных классов для отображения графических текстовых объектов
В графическом виде структуру (иерархию) стандартных классов можно представить в следующем виде:
Рисунок 1. Общая структура стандартных классов
Класс CObject также является базовым для других стандартных классов, например, для рассматриваемого ниже класса управления списком CList и большинства других.
Расширение функциональности стандартных классов
Итак, мы кратко рассмотрели иерархию стандартных классов, предназначенных для создания на экране терминала текстовых графических объектов. Теперь расширим эту иерархию новыми классами. Прежде всего, надо решить, какая функциональность нужна для выполнения задуманного. Сформулируем требования. Поскольку мы имеем дело с выводом текстовой информации, то логично представить эту информацию в таком структурированном виде:
ЗАГОЛОВОК | ИНФОРМАЦИОННЫЙ ТЕКСТ |
Такая структура представления текстовой информации подходит для большинства простых случаев.
Например, информационный индикатор для вывода основных параметров инструмента на графике может иметь следующий вид:
Рисунок 2. Пример структурированного вывода текстовой информации
Здесь используется шесть информационных полей предложенной выше структуры. Некоторые элементы структуры могут отсутствовать. Например, на рисунке 1 в самом верхнем поле отсутствует заголовок. В остальных полях есть и заголовок, и информационный текст. Данный индикатор содержится в приложенном к статье файле PricelInfo.mq5.
Позиционирование графических объектов
Второй момент, который надо рассмотреть - это способ позиционирования графических текстовых объектов на экране. Принятый способ задания координат в экранных пикселях хотя и позволяет позиционировать объект в произвольное место экранного окна, но неудобен на практике при необходимости размещения множества текстовых объектов в разных местах экрана, поскольку приходится просчитывать все экранные координаты с точностью до пикселя. Кроме того, при изменении разрешения экрана, все пиксельные координаты нужно пересчитывать, чтобы относительное расположение объектов на экране не изменилось.
Пожертвовав незначительно функциональностью (позиционированием с точностью до отдельного пикселя), можно предложить следующий способ позиционирования. Если мысленно расчертить графическое окно терминала на прямоугольники (поля) одинакового размера и приписать каждому такому прямоугольнику горизонтальную и вертикальную координату, то мы получим универсальную систему позиционирования, не зависящую от разрешения экрана.
Пользователю остается задать в качестве параметров при создании графического текстового объекта максимальное число полей по вертикали и горизонтали и координаты объекта в таких полях. Функциональность, заложенная в соответствующем классе, автоматически подстроит координаты графических объектов при изменении разрешения экрана. При этом, координаты задаются один раз и подстройки не требуют.
Автоматическая генерация неповторяющихся имен объектов
Следующий вопрос, который необходимо решить - это автоматическая генерация имени графического текстового объекта. Главное требование к методу генерации - получение уникального имени в пределах данного окна. Это позволит размещать на экране достаточное для практической работы количество объектов, не заботясь о придумывании неповторяющихся имен.
Предлагается следующий способ генерации имени (строка, состоящая из полей):
Дата Время | Число миллисекунд |
Для получения части строки, содержащей дату и время, используем такой вызов:
TimeToString(TimeGMT(), TIME_DATE|TIME_MINUTES|TIME_SECONDS);
Для получения числа миллисекунд используем следующий вызов:
DoubleToString(GetTickCount(), 0);
Но, даже если измерить время с точностью до миллисекунд, вполне возможна ситуация, когда вызвав последовательно два или более раз функцию GetTickCount(), мы получим одно и тоже значение. Это обусловлено ограничением дискретности внутренних таймеров операционной системы и процессора. Таким образом, надо принять дополнительные меры для обнаружения такой ситуации.
Предлагаемый способ реализован в следующей функции:
string GetUniqName() { static uint prev_count = 0; uint count = GetTickCount(); while(1) { if(prev_count == UINT_MAX) { prev_count = 0; } if(count <= prev_count) { prev_count++; count = prev_count; } else { prev_count = count; } // Проверим, нет ли уже объекта с таким именем: string name = TimeToString(TimeGMT(), TIME_DATE|TIME_MINUTES|TIME_SECONDS)+" "+DoubleToString(count, 0); if(ObjectFind(0, name) < 0) { return(name); } } return(NULL); }
Ограничение такого способа: генерация не более 4 294 967 295 (UINT_MAX) уникальных имен за одну секунду. Видимо, этого должно быть вполне достаточно на практике. На всякий случай, дополнительно проверяется существование графического объекта с таким именем.
Отображение заголовка информационной структуры - класс TTitleDisplay
Класс для вывода заголовка на экран терминала представлен ниже:
class TTitleDisplay : public CChartObjectLabel { protected: long chart_id; int sub_window; long chart_width; // ширина графика в пикселях long chart_height; // высота графика в пикселях long chart_width_step; // шаг координатной сетки по горизонтали long chart_height_step; // шаг координатной сетки по вертикали int columns_number; // число колонок int lines_number; // число строк int curr_column; int curr_row; protected: void SetParams(long chart_id, int window, int cols, int lines);// задать параметры объекта protected: string GetUniqName(); // получить уникальное имя bool Create(long chart_id, int window, int cols, int lines, int col, int row); void RecalcAndRedraw();// пересчитать координаты и перерисовать public: void TTitleDisplay(); // конструктор void ~TTitleDisplay(); // деструктор };
Основной метод, создающий графический текстовый объект:
bool Create(long chart_id, int window, int _cols, int _lines, int _col, int _row);
Назначение входных параметров следующее:
- chart_id - идентификатор окна (0 - главное окно);
- window - номер подокна (0 - основное);
- cols - максимальное число графических текстовых объектов по горизонтали (число колонок);
- lines - максимальное число графических текстовых объектов по вертикали (число строк);
- col - координата графического объекта по горизонтали (может меняться в пределах от нуля до cols - 1);
- row - координата графического объекта по вертикали (может меняться в пределах от нуля до lines - 1);
Метод TTitleDisplay::SetParams рассчитывает параметры объекта, связанные с позиционированием на экране.
Реализация следующая:
void TTitleDisplay::SetParams(long _chart_id, int _window, int _cols, int _lines) { this.chart_id = _chart_id; this.sub_window = _window; this.columns_number = _cols; this.lines_number = _lines; // Определим размеры окна в пикселях: this.chart_width = GetSystemMetrics(SM_CXFULLSCREEN); this.chart_height = GetSystemMetrics(SM_CYFULLSCREEN); // Рассчитаем шаг координатной сетки: this.chart_width_step = this.chart_width/_cols; this.chart_height_step = this.chart_height/_lines; }
Здесь используется вызов функции WinAPI для получения текущих параметров экрана (разрешения рабочей области) - GetSystemMetrics. Эта функция импортируется из системной библиотеки Windows user32.dll.
Класс для создания информационного текста TFieldDisplay строится аналогично. Подробности можно посмотреть в приложенном к статье файле библиотеки TextDisplay.mqh.
Манипуляция объектами в списке - стандартный класс CList
Теперь рассмотрим еще один стандартный класс, который нам понадобится при реализации задуманного. Это класс, позволяющий группировать объекты в списки. Он находится в файле Include\Arrays\List.mqh. В этом файле описан и реализован стандартный класс CList, являющийся наследником базового класса CObject, который мы рассмотрели в начале статьи. Он содержит набор методов для манипуляции объектами в списке. Это добавление в список, удаление из списка, доступ к произвольному элементу списка и очистка списка.
Рассмотрим основные методы:
- добавление в список:
int Add(CObject *new_node); int Insert(CObject *new_node,int index);
Это два метода для добавления нового элемента в список. Первый из них CList::Add позволяет добавить новый элемент new_node в конец списка. Второй CList::Insert позволяет вставить новый элемент new_node в произвольное место списка, которое указывается индексом index.
- удаление из списка:
bool Delete(int index);
Метод позволяет удалить из списка элемент с заданным индексом index. При этом, кроме удаления элемента из списка, освобождается также память, занимаемая элементом типа CObject. То есть, происходит и "физическое" удаление объекта.
- доступ к произвольному элементу списка:
int IndexOf(CObject* node); CObject* GetNodeAtIndex(int index); CObject* GetFirstNode(); CObject* GetPrevNode(); CObject* GetNextNode(); CObject* GetLastNode();
Метод CList::IndexOf возвращает индекс заданного элемента в списке. Нумерация индексов идет с нуля - первый элемент имеет нулевой индекс. Метод, выполняющий обратную операцию - по индексу элемента возвращается указатель на элемент. Если элемента с таким индексом в списке нет, то возвращается -1.
Еще четыре метода выполняют навигацию по списку - CList::GetFirstNode возвращвет предыдущий элемент, CList::GetNextNode - следующий, CList::GetLastNode - последний элемент в списке.
- очистка списка:
void Clear();
Метод позволяет удалить все элементы из списка и также освобождается память, занимаемая объектами.
Это основные методы класса CList, остальные описаны в разделе справки, посвященном стандартной библиотеке.
Создание таблицы для вывода текста на экран - класс TableDisplay
Итак, у нас есть все необходимое для реализации задуманного. Вот простейший класс, позволяющий организовать текстовые графические объекты в виде таблицы произвольного размера:
class TableDisplay : public CList { protected: long chart_id; int sub_window; public: void SetParams(long _chart_id, int _window, ENUM_BASE_CORNER _corner = CORNER_LEFT_UPPER); int AddTitleObject(int _cols, int _lines, int _col, int _row, string _title, color _color, string _fontname = "Arial", int _fontsize = 8); int AddFieldObject(int _cols, int _lines, int _col, int _row, color _color, string _fontname = "Arial", int _fontsize = 8); bool SetColor(int _index, color _color); bool SetFont(int _index, string _fontname, int _fontsize); bool SetText(int _index, string _text); public: void TableDisplay(); void ~TableDisplay(); };
Прежде чем добавлять в таблицу графические объекты, необходимо задать параметры - идентификатор окна, номер подокна и угол для начала координат. Это делается вызовом метода TableDisplay::SetParams. После этого можно добавлять в таблицу произвольное число заголовков и текстовых полей.
Полный текст разработанной нами библиотеки находится в приложенном к статье файле TextDisplay.mqh.
3. Пример создания экранной таблицы
Рассмотрим пример создания экранной таблицы для вывода на график значений нескольких инструментов в виде индикатора.
Выглядеть должно примерно в таком виде:
Рисунок 3. Пример экранной таблицы
- Шаг 1 - включение библиотеки в исходный текст индикатора:
#include <TextDisplay.mqh>
- Шаг 2 - создание массивов с названиями и координатами для заголовков:
#define NUMBER 8 //--------------------------------------------------------------------- string names[NUMBER] = {"EURUSD", "GBPUSD", "AUDUSD", "NZDUSD", "USDCHF", "USDCAD", "USDJPY", "EURJPY"}; int coord_y[NUMBER] = {2, 3, 4, 5, 6, 7, 8, 9};Координаты отсчитываются начиная с нуля.
- Шаг 3 - создание табличного объекта типа TableDisplay для хранения всех отображаемых текстовых объектов:
TableDisplay Table1;
- Шаг 4 - добавление в таблицу объектов-заголовков и объектов-информационных полей:
int OnInit() { // Создание элемента отображения: Table1.SetParams(0, 0); for(int i=0; i<NUMBER; i++) { Table1.AddFieldObject(40, 40, 3, coord_y[i], Yellow); } for(int i=0; i<NUMBER; i++) { Table1.AddTitleObject(40, 40, 1, coord_y[i], names[i]+":", White); } ChartRedraw(0); EventSetTimer(1); return(0); }
Оптимально это делать в обработчике события инициализации OnInit. Сначала добавляются информационные поля с помощью метода TableDisplay::AddFieldObject. Затем добавляются заголовки для них методом TableDisplay::AddTitleObject.
Добавление происходит в отдельных циклах по двум причинам: во-первых, число заголовков в общем случае может не совпадать с числом информационных полей; во-вторых, доступ к обновляемым информационным полям проще организовать когда индексация их идет подряд (в данном случае, от нуля до значения NUMBER - 1).
- Шаг 5 - добавление кода для обновления динамической информации:
Обновление динамической информации организовано, в данном случае, по событию от таймера. Обработчик этого события OnTimer должен получать ценовые значения по заданным инструментам и выводить эти значения на экран.
Текст его представлен ниже:
//--------------------------------------------------------------------- double rates[NUMBER]; datetime times[NUMBER]; MqlTick tick; //--------------------------------------------------------------------- // Обработчик события от таймера: //--------------------------------------------------------------------- void OnTimer() { for(int i=0; i<NUMBER; i++) { // Получим ценовые данные: ResetLastError(); if(SymbolInfoTick(names[i], tick) != true) { Table1.SetText(i,"Err "+DoubleToString(GetLastError(),0)); Table1.SetColor(i,Yellow); continue; } if(tick.time>times[i]) { Table1.SetText(i, DoubleToString(tick.bid, (int)(SymbolInfoInteger(names[i], SYMBOL_DIGITS)))); if(tick.bid>rates[i]) { Table1.SetColor(i, Lime); } else if(tick.bid<rates[i]) { Table1.SetColor(i, Red); } else { Table1.SetColor(i, Yellow); } rates[i] = tick.bid; times[i] = tick.time; } } ChartRedraw(0); }
В начале цикла считываются тиковые данные по заданному инструменту, затем проверяется актуальность данных - если время прихода тика не изменилось по сравнению с предыдущим, то считаем данные неактуальными. Далее анализируется значение котировки по сравнению с предыдущим тиком. Если текущая цена больше предыдущей, то задается зеленый цвет информационного поля. Если меньше, то красный, в случае равенства - желтый. В конце цикла запоминаются текущие значение цены и время прихода тика для анализа на следующем цикле обработки события от таймера.
В общем случае код для обновления динамической информации определяется решаемой задачей. Фактически, пользователю нужно реализовать только этот участок кода. Предположим, мы решили добавить отображение спрэда справа от цены на рис. 2. Посмотрим, как это можно сделать.
Все, что нужно - это добавить дополнительные информационные поля в таблицу:
for(int i=0; i<NUMBER; i++) { Table1.AddFieldObject(40, 40, 5, coord_y[i], Yellow); }
и в обработчик события от таймера включить код, обновляющий значение спрэда:
Table1.SetText(i+NUMBER, DoubleToString((tick.ask-tick.bid)/SymbolInfoDouble(names[i], SYMBOL_POINT), 0));
В результате получаем следующую картинку:
Рисунок 4. Цены со спрэдом
Обратите внимание, что индекс полей для вывода спрэда начинается от значения NUMBER (при i, равном нулю).
- Шаг 6 - удаление созданных объектов:
void OnDeinit(const int _reason) { EventKillTimer(); // Удаление элемента отображения: Table1.Clear(); }
Здесь удаляется таймер и очищается таблица, созданная нами ранее. При этом все объекты также удаляются из памяти.
Полный текст данного индикатора находится в приложенном к статье файле PriceList.mq5. Приложена "усовершенствованная" версия индикатора, выводящая спрэд и имеющая ряд внешних параметров для задания цвета заголовков и положения таблицы в окне терминала.
Заключение
В приложенном файле MarketWatch.mq5 (и включаемом MarketWatch.mqh) находится индикатор для вывода основных параметров по торговым инструментам в виде сводной таблицы. Для каждого инструмента выводится информация, аналогичная рис.2. Кроме того, отображается процентное изменение цены за заданные временные интервалы. Набор инструментов (не более 16) и временных интервалов задаются в виде строки с элементами, разделенными точкой с запятой.
Результаты работы данного индикатора представлены на рисунке 5:
Рисунок 5. Индикатор обзора рынка
Мы рассмотрели один из способов вывода текстовой информации на экран терминала MetaTrader 5. Использовав стандартные классы, поставляемые вместе с терминалом, удалось достаточно просто и быстро разработать недостающую функциональность для представления текстовой информации в виде двухмерной таблицы. Такова сила объектно-ориентированного подхода и реализующего этот подход языка MQL5.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Какого-то особенного смысла в этом нет - была мысль наподобие ЕМА использовать. Сейчас уже использую просто СДД как среднее арифметическое - так общепринято и разница не существенная.
Понятно. Я в общем-то так и подумал, что это вроде сглаженного среднего. В качестве дополнения есть мысль добавить данные о среднем за последние N дней по текущему дню недели.
Нет ли подобного кода?
Спасибо!
Понятно. Я в общем-то так и подумал, что это вроде сглаженного среднего. В качестве дополнения есть мысль добавить данные о среднем за последние N дней по текущему дню недели.
Нет ли подобного кода?
Спасибо!
Есть, но только в виде гистограммы (индикатора) и только под МТ4.
Спасибо! Но я тяготею к использованию исключительно МТ5 для работы :)
Спасибо! Но я тяготею к использованию исключительно МТ5 для работы :)