
Трейдинг с экономическим календарем MQL5 (Часть 2): Создание новостной панели
Введение
Мы продолжаем нашу работу с экономическим календарем MQL5, начатую в предыдущей статье серии, в которой мы сосредоточились на освоении функций, необходимых для поиска и анализа экономических новостных событий. Теперь мы сделаем следующий шаг и создадим новостную панель, которая предоставит трейдерам удобный интерфейс для доступа к важнейшим экономическим данным в реальном времени. Панель поможет оптимизировать процессы принятия решений, выделяя важные новости, которые могут повлиять на движение рынка. Мы рассмотрим следующие темы:
- Создание панели
- Настройка панели в MQL5
- Заключение
С помощью этих компонентов мы стремимся улучшить процесс торговли, предоставив эффективный инструмент для мониторинга экономических событий в реальном времени для экономических новостей на языке MQL5.
Создание панели
Разработка панели является важным шагом в создании эффективного инструмента для мониторинга экономических новостей с использованием экономического календаря MQL5. Наша цель — создать удобный и визуально привлекательный интерфейс, представляющий важную информацию четко и лаконично. Хорошо структурированная панель инструментов позволит нам быстро оценить актуальность и влияние экономических событий на наши торговые стратегии.
При проектировании панели управления нам необходимо начать с определения ключевых компонентов, которые необходимо отобразить. Эти компоненты обычно включают название события, запланированное время, валюту, уровень важности и краткое описание события. Для повышения удобства мы организуем эту информацию в виде таблицы, где каждая строка будет представлять отдельное экономическое событие. Мы намерены сделать таблицу легко читаемой, используя контрастные цвета для разных уровней важности, чтобы можно было быстро обнаруживать важные события.
Чтобы сделать панель более привлекательной, мы выберем визуальные элементы, такие как границы, фоны и шрифты, чтобы создать чистый и профессиональный вид. Компоновка обеспечивает легкую навигацию, гарантируя, что мы сможем быстро найти нужную информацию, не перегружая себя излишними подробностями. Сохраняя интуитивно понятный и простой дизайн, мы позволяем трейдерам сосредоточиться на принятии обоснованных решений на основе экономических событий, отображаемых на панели мониторинга. Панель будет организована, как описано выше, и будет включать в себя компоненты, показанные ниже:
Теперь, когда мы четко определились с целями, давайте займемся автоматизацией. В предыдущей статье мы сосредоточились на освоении функций экономического календаря MQL5 для эффективного поиска и анализа экономических новостей. Если вы еще этого не сделали, ознакомьтесь с первой частью, прежде чем продолжить.
Настройка панели в MQL5
В этом разделе мы сосредоточимся на настройке панели, создав необходимые элементы с помощью MQL5. Сначала нам нужно будет создать функции для трех элементов, которые нам понадобятся: прямоугольной метки, кнопки и текстовых меток. Такой подход будет чрезвычайно полезен, поскольку он позволяет нам повторно использовать одни и те же функции при создании аналогичных объектов, устраняя необходимость повторять весь процесс для каждого нового объекта. Поступая таким образом, мы экономим время и место, делая процесс быстрым и простым, а также сохраняя краткость фрагментов кода.
Чтобы создать прямоугольную метку, мы создадим функцию, которая принимает десять аргументов или параметров. Эта функция определит свойства прямоугольника, такие как его положение, размер, цвет и стиль, что позволит нам настроить внешний вид метки в соответствии с требованиями дизайна нашей панели.
//+------------------------------------------------------------------+ //| Function to create rectangle label | //+------------------------------------------------------------------+ bool createRecLabel(string objName, int xD, int yD, int xS, int yS, color clrBg, int widthBorder, color clrBorder = clrNONE, ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID) { ... }
Сигнатура функции говорит сама за себя. Это логическая функция с именем createRecLabel, возвращающая два логических флага, true или false, в случае успеха или неудачи соответственно. Чтобы легче понять ее параметры, давайте опишем и объясним их по отдельности ниже.
- objName - уникальное имя объекта метки прямоугольника. Служит идентификатором создаваемого графического элемента.
- xD и yD - расстояния X и Y от угла, где будет расположена метка прямоугольника. Это координаты, определяющие верхний левый угол прямоугольника относительно диаграммы.
- xS и yS - ширина и высота прямоугольника. Значение xS определяет ширину прямоугольника по горизонтали, а yS — по вертикали.
- clrBg - цвет фона прямоугольной метки. Выберите цвет, который хорошо контрастирует с фоном графика или дополняет другие элементы.
- widthBorder - параметр определяет ширину границы вокруг прямоугольника. Если вам нужна граница, задайте положительное значение. В противном случае используйте ноль, чтобы не отображать границу.
- clrBorder - необязательный параметр для цвета границы. Если вам нужна рамка, укажите цвет (например, clrNONE для отсутствия рамки).
- borderType - тип границы прямоугольника. Варианты включают плоские, приподнятые и другие стили. Для простой плоской границы используйте BORDER_FLAT.
- borderStyle - стиль линии при выборе плоской границы (например, сплошная, пунктирная). Используйте STYLE_SOLID для непрерывной линии.
Возможно, вы заметили, что некоторые аргументы уже инициализированы тем или иным значение в сигнатуре функции. Значение инициализации представляет собой значение по умолчанию, которое будет присвоено этому параметру в случае, если он будет проигнорирован во время вызова функции. Например, если значение цвета границы не указано во время вызова функции, то к границе нашей прямоугольной метки цвет применен не будет.
Процедуры создания объектов определяются внутри тела функции, заключенного в фигурные скобки ({}).
// Create a rectangle label object if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError); return (false); // Return false if object creation fails }
Начнем с использования оператора if, чтобы проверить, не создан ли объект. Используется логическая функция ObjectCreate, принимающая 6 аргументов. Эта функция создает объект с указанным именем, типом и начальными координатами в указанном подокне графика. Сначала мы указываем окно графика, 0 означает, что объект будет создан в главном окне. Затем указываем имя объекта. Это уникальное имя, которое будет присвоено конкретному объекту. Тип объекта, который мы хотим создать — OBJ_RECTANGLE_LABEL, что означает объект для создания и проектирования пользовательского интерфейса. Затем мы переходим к предоставлению подокна, 0 - текущее подокно. Наконец, мы указываем значения времени и цены как ноль (0), поскольку мы будем прикреплять их не к графику, а к координатам окна графика. Для настройки отображения используются пиксели.
Если не удается создать объект, функция ObjectCreate в конечном итоге возвращает false. Очевидно, что нет смысла продолжать, возвращаемся с ошибкой. В этом случае мы сообщаем об ошибке, указывая ее в журнале вместе с кодом и возвращая false. Возможно, была предыдущая ошибка, и поэтому, чтобы получить текущую ошибку, нам нужно очистить предыдущую. Это делается с помощью вызова функции ResetLastError, которая является встроенной функцией MQL5, расположенной непосредственно перед нашей логикой создания объекта.
ResetLastError(); // Reset any previous error codes
Цель — установить значение предопределенной переменной _LastError, которая хранит код ошибки последней операции, равным нулю. Вызывая ее, мы гарантируем, что все предыдущие коды ошибок будут очищены перед продолжением операций. Этот шаг важен, поскольку он позволяет нам обрабатывать новые ошибки без риска нарваться на предыдущие.
Достижение этого этапа означает, что мы создали объект, и, таким образом, мы можем продолжить обновление свойств объекта. Встроенная функция ObjectSet... устанавливает значение соответствующего свойства объекта. Свойство объекта должно иметь тип datetime, integer, color, boolean или character.
// Set properties for the rectangle label ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the rectangle ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the rectangle ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Rectangle background color ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); // Border type ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); // Border style (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_WIDTH, widthBorder); // Border width (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_COLOR, clrBorder); // Border color (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Not a background object ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected
Сосредоточимся на логике первого свойства.
ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner
Здесь мы используем встроенную функцию ObjectSetInteger и передаем параметры. Параметры описаны ниже.
- Chart id - идентификатор графика. 0 означает текущий график (chart ID). Редактируем свойства объекта в этом графике.
- Name - имя объекта. objName - уникальное имя, присвоенное объекту метки прямоугольника.
- Property id - идентификатор свойства объекта. Значение равно одному из значений перечисления ENUM_OBJECT_PROPERTY_INTEGER. OBJPROP_XDISTANCE указывает, что мы изменяем свойство расстояния X.
- Property value - значение свойства. Значение, присвоенное xD, определяет, насколько далеко вправо (или влево, если значение отрицательное) будет располагаться верхний левый угол нашей прямоугольной метки по горизонтали от левого края графика.
Остальные свойства используют тот же формат. OBJPROP_YDISTANCE - свойство расстояния Y метки прямоугольника. Значение yD определяет, насколько далеко верхний левый угол прямоугольной метки будет расположен по вертикали от верхнего края графика. Другими словами, параметр управляет вертикальным расположением метки в области графика. Задается расстояние Y от угла. OBJPROP_XSIZE и OBJPROP_YSIZE - ширина и высота прямоугольника соответственно.
Чтобы расположить наш объект, мы используем свойство OBJPROP_CORNER, чтобы определить угол, в котором должен располагаться наш объект в окне графика.
ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner
Имеется 4 возможных типа свойства:
- CORNER_LEFT_UPPER - центр координат в левом верхнем углу графика.
- CORNER_LEFT_LOWER - центр координат в левом нижнем углу графика.
- CORNER_RIGHT_LOWER - центр координат в правом нижнем углу графика.
- CORNER_RIGHT_UPPER - центр координат в правом верхнем углу графика.
На рисунке это выглядит так:
Остальные свойства просты. Они снабжены комментариями для более легкого понимания. Перерисуем график с помощью ChartRedraw, чтобы изменения вступали в силу автоматически, без необходимости ждать изменений котировок или событий графика.
ChartRedraw(0); // Redraw the chart
Наконец мы возвращаем true, означая, что создание и обновление свойств объекта прошло успешно.
return (true); // Return true if object creation and property settings are successful
Ниже приведен полный код функции, отвечающей за создание объекта прямоугольника в окне графика.
bool createRecLabel(string objName, int xD, int yD, int xS, int yS, color clrBg, int widthBorder, color clrBorder = clrNONE, ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID) { ResetLastError(); // Reset any previous error codes // Create a rectangle label object if (!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { Print(__FUNCTION__, ": failed to create rec label! Error code = ", _LastError); return (false); // Return false if object creation fails } // Set properties for the rectangle label ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the rectangle ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the rectangle ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Rectangle background color ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); // Border type ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); // Border style (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_WIDTH, widthBorder); // Border width (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_COLOR, clrBorder); // Border color (only if borderType is flat) ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Not a background object ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected ChartRedraw(0); // Redraw the chart return (true); // Return true if object creation and property settings are successful }
Для создания объекта кнопки используется тот же функциональный подход. Код для создания пользовательской функции кнопки приведен ниже.
//+------------------------------------------------------------------+ //| Function to create button | //+------------------------------------------------------------------+ bool createButton(string objName, int xD, int yD, int xS, int yS, string txt = "", color clrTxt = clrBlack, int fontSize = 12, color clrBg = clrNONE, color clrBorder = clrNONE, string font = "Arial Rounded MT Bold") { // Reset any previous errors ResetLastError(); // Attempt to create the button object if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) { // Print an error message if creation fails Print(__FUNCTION__, ": failed to create the button! Error code = ", _LastError); return (false); } // Set properties for the button ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Width of the button ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Height of the button ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Text displayed on the button ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Background color ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Border color ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Button state (not pressed) ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected // Redraw the chart to display the button ChartRedraw(0); return (true); // Button creation successful }
Различия в коде заключаются в том, что объект прямоугольника не может содержать текст, а кнопка включает в себя текст с описанием функциональности кнопки на случай, если это потребуется. Поэтому в качестве входных параметров мы рассматриваем свойства текста, в нашем случае — текстовое значение, цвет, размер шрифта и название шрифта. Тип границы нашей кнопки статический, поэтому мы избавляемся от ее свойств и оставляем только цвет границы.
Тип создаваемого объекта - OBJ_BUTTON, что означает, что мы создаем графический объект кнопки. Его точки привязки задаются в пикселях. Свойство границы, которое мы сохраняем, — это цвет. Все остальное заменяем свойствами ввода текста.
Наконец, нам нужна текстовая метка. Текстовая метка устраняет необходимость в фоновом объекте, и поэтому ее реализация гораздо проще, чем у остальных функций. Нам нужен только текст, поэтому мы концентрируемся на свойствах текста. Его код выглядит следующим образом.
//+------------------------------------------------------------------+ //| Function to create text label | //+------------------------------------------------------------------+ bool createLabel(string objName, int xD, int yD, string txt, color clrTxt = clrBlack, int fontSize = 12, string font = "Arial Rounded MT Bold") { // Reset any previous errors ResetLastError(); // Attempt to create the label object if (!ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0)) { // Print an error message if creation fails Print(__FUNCTION__, ": failed to create the label! Error code = ", _LastError); return (false); } // Set properties for the label ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // X distance from the corner ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Y distance from the corner ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); // Positioning corner ObjectSetString(0, objName, OBJPROP_TEXT, txt); // Text displayed on the label ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Text color ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Font size ObjectSetString(0, objName, OBJPROP_FONT, font); // Font name ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Transparent background ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Label state (not active) ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Not selectable ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Not selected // Redraw the chart to display the label ChartRedraw(0); return (true); // Label creation successful }
Основными отличиями этой структуры кода от функции кнопки являются размер объекта и свойства границы. В сигнатуре функции мы избавляемся от размеров объекта, а также от свойств границ. Мы определяем наш тип объекта как OBJ_LABEL, обозначая, что мы рисуем метки в соответствии с определенными координатами меток в окне графика. Наконец, мы избавляемся от параметров размера и границ, и на этом все заканчивается. Все очень просто.
Теперь, когда у нас есть функции, необходимые для создания графического интерфейса пользователя, давайте используем их для создания панели. Нам понадобятся имена объектов. Определим макросы, чтобы легко управлять взаимодействием имен объектов.
#define MAIN_REC "MAIN_REC"
Мы используем ключевое слово #define для определения макроса MAIN_REC со значением MAIN_REC для простого хранения базового имени нашего основного прямоугольника, вместо необходимости повторно вводить имя при каждом создании уровня, что значительно экономит наше время и снижает вероятность неправильного указания имени. По сути, макросы используются для подстановки текста во время компиляции.
Наш код будет в основном основан на разделе инициализации советника, поскольку мы хотим создать панель в момент инициализации. Таким образом, обработчик событий OnInit будет содержать большую часть структуры кода.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... return(INIT_SUCCEEDED); }
Функция OnInit представляет собой обработчик событий, который вызывается для экземпляра инициализации советника при выполнении необходимых инициализаций.
Затем вызываем функцию для создания прямоугольной метки путем ввода ее имени и указания ее параметров.
//--- Create main rectangle label for the dashboard panel createRecLabel(MAIN_REC,50,50,740,410,clrSeaGreen,1);
Здесь имя нашего прямоугольника — MAIN_REC, как следует из определения макроса. Наше расстояние по оси X, шкале времени и даты, от левого верхнего угла окна графика составляет 50 пикселей, а расстояние по оси Y, шкале цен, составляет 50 пикселей. Ширина составляет 740 пикселей, а высота — 410 пикселей соответственно. Мы выбираем цвет фона — морской волны (sea green), ширину границы — 1, а остальные параметры — по умолчанию. Чтобы получить приблизительный диапазон пикселей, можно уменьшить масштаб диаграммы до 0, а количество полос между двумя координатами перекрестия будет равно количеству пикселей на горизонтальной шкале. Ниже показано, что мы имеем в виду.
Остальные параметры опущены, то есть значения по умолчанию будут применены автоматически. То есть тип границы будет ровным, а стиль линии — непрерывной сплошной линией. Вот что мы получаем после компиляции.
Для создания внутренних рамок мы снова явно объявляем соответствующие макросы.
#define SUB_REC1 "SUB_REC1" #define SUB_REC2 "SUB_REC2"
Затем мы вызываем ту же функцию для создания внутренних рамок. Мы хотим, чтобы наши рамки располагались внутри рамки базовой панели, поэтому нам потребуется использовать немного другой цвет. Для этого мы использовали белый и зеленый цвета и поля в 3 и 5 пикселей.
//--- Create sub-rectangle labels within the main panel for different sections createRecLabel(SUB_REC1,50+3,50+30,740-3-3,410-30-3,clrWhite,1); createRecLabel(SUB_REC2,50+3+5,50+30+50+27,740-3-3-5-5,410-30-3-50-27-10,clrGreen,1);
Здесь мы создаем два дополнительных подраздела, SUB_REC1 и SUB_REC2, на главной панели управления, чтобы помочь организовать и визуально разделить контент. Используя функцию createRecLabel, мы позиционируем SUB_REC1, добавляя смещение в 3 пикселя от левого и правого краев и 30 пикселей от верха основного прямоугольника MAIN_REC, фактически создавая рамочную секцию внутри основной панели. Мы определяем его ширину как "740-3-3", чтобы он вписывался в уменьшенные поля, а его высоту как "410-30-3", чтобы оставить место сверху и снизу, что позволит ему аккуратно вписаться в основной прямоугольник. Этот подраздел выполнен в белом цвете, что создает нейтральный фон, контрастирующий с цветом морской волны основной панели, что повышает визуальную четкость.
Далее мы используем createRecLabel для добавления SUB_REC2, дополнительного раздела внутри SUB_REC1, и размещаем его с более точными смещениями для организованной многослойной компоновки. Для этого мы устанавливаем начальную координату X как "50+3+5", размещая ее дальше внутри "SUB_REC1", чтобы визуально определить ее как отдельную область внутри этого подраздела. Мы устанавливаем координату Y как "50+30+50+27", чтобы учесть вертикальные смещения как основного, так и первого подпрямоугольника. Ширина, "740-3-3-5-5", точно вписывается в SUB_REC2 в оставшееся горизонтальное пространство, тогда как высота, "410-30-3-50-27-10", позволяет создать сбалансированную и разделенную область. Установка зеленого цвета для SUB_REC2 добавляет сильный контраст, указывая на область, в которой будут отображаться критически важные данные. Такое тщательное наложение прямоугольников имеет важное значение для создания структурированной и визуально удобной для навигации панели управления. После компиляции получаем следующие результаты:
На этом настройка рамок, полей и границ нашей панели завершена. Переходим к добавлению других утилит панели, их свойств и эффектов. Для начала дадим название панели.
#define HEADER_LABEL "HEADER_LABEL" //--- //--- Create the header label with text "MQL5 Economic Calendar" createLabel(HEADER_LABEL,50+3+5,50+5,"MQL5 Economic Calendar",clrWhite,15);
Здесь мы определяем идентификатор метки HEADER_LABEL со значением HEADER_LABEL для единообразия и простоты ссылки на эту конкретную метку во всем нашем коде. Эта метка будет служить заголовком для нашей панели мониторинга, на ней будет отображаться заголовок MQL5 Economic Calendar (экономический календарь MQL5).
Затем с помощью функции createLabel мы создаем заголовок в указанной позиции. Мы устанавливаем его координату X как "50+3+5", располагая его немного правее края основной панели, чтобы он был выровнен внутри прямоугольника SUB_REC1 и не перекрывался никакими полями. Координата Y, "50+5", размещает его на несколько пикселей ниже верхнего края основного прямоугольника, обеспечивая читаемость. Для наглядности мы установили белый цвет текста и размер шрифта "15", создав жирный и заметный заголовок, отмечающий цель панели. Этот заголовок закрепит визуальный дизайн панели управления, сразу же сообщая пользователям о ее назначении. Вот что мы получаем.
Всё работает как надо. Теперь мы можем приступить к созданию заголовков панелей. Для этого мы воспользуемся самым простым методом, который заключается в определении заголовков и размещении их в массиве, а затем использовании цикла для их динамического размещения, поскольку они находятся всего в одной строке. Однако нам также придется по-разному определять размеры кнопок, поскольку они будут иметь разную ширину из-за индивидуальной длины заголовков. Ниже приведена логика, которую мы будем использовать для достижения этой цели.
string array_calendar[] = {"Date","Time","Cur.","Imp.","Event","Actual","Forecast","Previous"}; int buttons[] = {80,50,50,40,281,60,70,70};
Мы определяем два массива для организации надписей и размеров кнопок на панели управления. Первый массив, array_calendar, содержит строки для каждого заголовка столбца, который мы будем отображать, указывая тип информации: Date (дата), Time (время), Cur. (валюта), Imp. (важность), Event (событие), Actual (актуальное), Forecast (прогноз) и Previous (предыдущее). Каждая строка представляет собой метку категории данных, помогая нам понять, что будет отображаться в каждом разделе панели управления.
Второй массив, buttons (кнопки), содержит целые числа, представляющие ширину (в пикселях) каждой кнопки, связанной с соответствующими столбцами в array_calendar. Эти значения ширины подбираются индивидуально для каждого типа данных, чтобы гарантировать, что макет остается выровненным и визуально организованным. Например, значение "80" пикселей для столбца Date соответствует более длинным форматам даты, в то время как для столбцов Time и Cur. установлена более узкая ширина, например "50" пикселей, для которых требуется меньше места. Вместе эти массивы помогают упростить создание заголовков столбцов и кнопок панели мониторинга, создавая структурированную основу для дополнительных элементов пользовательского интерфейса. Здесь мы можем использовать цикл для динамического создания заголовков.
#define ARRAY_CALENDAR "ARRAY_CALENDAR" //--- //--- Initialize starting x-coordinate for button positioning int startX = 59; //--- Loop through the array_calendar elements to create buttons for (int i=0; i<ArraySize(array_calendar); i++){ //--- Create each button for calendar categories createButton(ARRAY_CALENDAR+IntegerToString(i),startX,132,buttons[i],25,array_calendar[i],clrWhite,13,clrGreen,clrNONE,"Calibri Bold"); startX += buttons[i]+3; //--- Update x-coordinate for the next button }
Здесь мы инициализируем и позиционируем кнопки на основе элементов array_calendar для маркировки каждой категории на нашей панели мониторинга. Сначала мы определяем идентификатор ARRAY_CALENDAR для серии кнопок календаря. Затем мы устанавливаем startX на "59" в качестве начальной координаты x, что позволит расположить первую кнопку на панели горизонтально.
Затем мы используем цикл for для итерации по каждому элементу в array_calendar для создания кнопки. Для каждой итерации мы вызываем функцию createButton и передаем уникальный идентификатор для каждой кнопки, добавляя индекс цикла к ARRAY_CALENDAR. Это гарантирует уникальность идентификатора каждой кнопки и ссылки на такие категории, как Date, Time, Cur. и другие. Мы указываем позицию startX и используем значения из buttons для определения ширины каждой кнопки, обеспечивая соответствие необходимой категории данных. Каждая кнопка также получает свойства стиля, включая цвет шрифта (белый), размер шрифта ("13") и цвета фона (зеленый для кнопки и никакой для границы), заданные шрифтом Calibri Bold. После создания каждой кнопки мы корректируем startX, добавляя ширину текущей кнопки и отступ в "3" пикселя, равномерно распределяя кнопки для следующей итерации. После компиляции мы получаем следующий результат.
После создания заголовков нам необходимо создать другой подраздел для отображения времени, количества выявленных новостей и уровней значимости. Для начала рассмотрим новости из предыдущей части серии.
//--- Declare variables for tracking news events and status int totalNews = 0; bool isNews = false; MqlCalendarValue values[]; //--- Array to store calendar values //--- Define start and end time for calendar event retrieval datetime startTime = TimeTradeServer() - PeriodSeconds(PERIOD_H12); datetime endTime = TimeTradeServer() + PeriodSeconds(PERIOD_H12); //--- Set a specific country code filter (e.g., "US" for USD) string country_code = "US"; string currency_base = SymbolInfoString(_Symbol,SYMBOL_CURRENCY_BASE); //--- Retrieve historical calendar values within the specified time range int allValues = CalendarValueHistory(values,startTime,endTime,NULL,NULL); //--- Print the total number of values retrieved and the array size Print("TOTAL VALUES = ",allValues," || Array size = ",ArraySize(values));
Это дает нам исторические значения событий, извлеченные из экономического календаря MQL5, и, таким образом, вместо того, чтобы просто распечатать их, мы можем отобразить их на панели управления. Вот какую логику мы применяем.
#define TIME_LABEL "TIME_LABEL" //--- //--- Create label displaying server time and total number of news events found createLabel(TIME_LABEL,70,85,"Server Time: "+TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS)+" ||| Total News: "+ IntegerToString(allValues),clrBlack,14,"Times new roman bold");
Здесь мы создаем метку для отображения текущего времени сервера, а также общего количества полученных новостей. Сначала мы определяем идентификатор TIME_LABEL для уникальной ссылки на эту метку на нашей панели мониторинга.
Далее мы вызываем функцию createLabel для генерации самой метки. Мы указываем положение метки, указывая координаты "70" и "85", которые определяют, где метка будет отображаться на панели. Текст метки динамически конструируется с использованием функции TimeToString, которая форматирует текущее время сервера, полученное функцией TimeCurrent в формате даты и секунд. Мы объединяем это отформатированное время со строкой "||| Total News: " (всего новостей) и конвертируем переменную allValues, которая содержит количество новостей, в строку, используя функцию IntegerToString. Это создает комплексную метку, которая показывает как время сервера, так и общее количество найденных новостей. Мы оформляем метку черным цветом, используем шрифт размером 14 и Times New Roman Bold для лучшей видимости. По той же логике мы создаем и метку важности.
#define IMPACT_LABEL "IMPACT_LABEL" //--- //--- Create label for displaying "Impact" category header createLabel(IMPACT_LABEL,70,105,"Impact: ",clrBlack,14,"Times new roman bold");
После компиляции получаем следующий результат.
Всё работает как надо. Теперь нам нужно перейти к отображению соответствующих кнопок важности с соответствующими метками и цветами, чтобы пользователи могли знать, что представляет собой каждый уровень важности.
//--- Define labels for impact levels and size of impact display areas string impact_labels[] = {"None", "Low", "Medium", "High"}; int impact_size = 100; //--- Loop through impact levels to create buttons for each level for (int i=0; i<ArraySize(impact_labels); i++){ color impact_color = clrBlack, label_color = clrBlack; //--- Default colors for label and button //--- Assign color based on impact level if (impact_labels[i] == "None"){label_color = clrWhite;} else if (impact_labels[i] == "Low"){impact_color = clrYellow;} else if (impact_labels[i] == "Medium"){impact_color = clrOrange;} else if (impact_labels[i] == "High"){impact_color = clrRed;} //--- Create button for each impact level createButton(IMPACT_LABEL+string(i),140+impact_size*i,105,impact_size,25,impact_labels[i],label_color,12,impact_color,clrBlack); }
Здесь мы определяем метки для различных уровней важности, связанных с экономическими событиями, и размер областей отображения для этих показателей важности. Сначала мы объявляем массив impact_labels, содержащий строки, представляющие различные уровни воздействия: None (нет), Low (низкий), Medium (средний) и High (высокий). Кроме того, мы инициализируем целочисленную переменную impact_size значением 100, которое определяет ширину кнопок, которые будут созданы для каждого уровня важности.
Далее мы входим в цикл, который выполняет итерацию по массиву impact_labels, используя функцию ArraySize для определения общего количества уровней важности. В этом цикле мы сначала устанавливаем цвета по умолчанию для кнопки и метки, используя черный цвет. Затем мы используем условные операторы для назначения определенных цветов на основе текущего уровня важности. При None меняем label_color на белый. Если уровень Low, устанавливаем impact_color на желтый. Для Medium назначаем impact_color оранжевый, а для High - красный. Наконец, вызываем функцию createButton, чтобы создать кнопку для каждого уровня воздействия, позиционируя ее с использованием динамической координаты X, рассчитанной по формуле 140 + impact_size * i, сохраняя фиксированную координату Y, равную 105 и предоставляя соответствующие размеры и цвета. Вот текущие результаты.
Всё работает как надо. Теперь мы можем добавить реальные данные календаря на панель. Однако перед этим нам нужно будет разделить вторую внутреннюю рамку, чтобы придать панели более профессиональный вид, а не просто разместить на ней данные. Сделаем это с помощью следующей логики.
//--- Limit the total number of values to display int valuesTotal = (allValues <= 11) ? allValues : 11; //--- Initialize starting y-coordinate for displaying news data int startY = 162; //--- Loop through each calendar value up to the maximum defined total for (int i = 0; i < valuesTotal; i++){ //--- Set alternating colors for each data row holder color holder_color = (i % 2 == 0) ? C'213,227,207' : clrWhite; //--- Create rectangle label for each data row holder createRecLabel(DATA_HOLDERS+string(i),62,startY-1,716,26,holder_color,1,clrBlack); //--- Increment y-coordinate for the next row of data startY += 25; Print(startY); //--- Print current y-coordinate for debugging }
Мы ограничиваем общее количество значений, отображаемых на нашей панели, путем определения целочисленной переменной valuesTotal. Мы используем условный (тернарный) оператор для проверки того, что allValues меньше или равно "11". Если это так, устанавливаем valuesTotal на allValues. В противном случае мы устанавливаем его на "11". Такой подход гарантирует, что мы не будем пытаться отображать более "11" новостных событий, сохраняя при этом порядок и управляемость нашей панели управления.
Далее мы инициализируем целочисленную переменную startY значением "162", которая служит начальной координатой Y для позиционирования новостей на панели. Затем мы входим в цикл, который повторяется от "0" до valuesTotal, эффективно обрабатывая каждое календарное значение, которое мы намерены отобразить. В этом цикле мы определяем цвет для каждого держателя строки, используя чередующийся шаблон на основе текущего индекса i. Если i четное, мы устанавливаем holder_color на светло-серый цвет, представленный как C'213,227,207'. Если i нечетное, мы устанавливаем его белым. После определения цвета вызываем функцию createRecLabel для создания прямоугольной метки для каждого держателя строки данных, расположенной в точке "62" по оси X, startY - 1 по оси Y, с шириной "716", высотой "26" и границей черного цвета. Наконец, мы увеличиваем startY на "25", чтобы скорректировать координату Y для следующей строки данных, гарантируя, что каждая запись будет отображаться последовательно. В целях отладки мы выводим текущее значение startY, что позволяет нам отслеживать вертикальное положение каждой строки данных по мере ее создания. Вот текущие результаты.
Вы могли заметить, что при создании внутренних рамок-держателей мы использовали макропеременную DATA_HOLDERS. Вот как мы ее определили.
#define DATA_HOLDERS "DATA_HOLDERS" #define ARRAY_NEWS "ARRAY_NEWS"
Мы также определили макрос ARRAY_NEWS, чтобы упростить создание соответствующих данных, которые должны быть сопоставлены внутри держателей данных относительно заголовков столбцов. Чтобы заполнить данные, нам потребуется, чтобы для каждого выбранного держателя мы перебрали все данные для определенного значения события и получили его данные, которые мы отобразим. Это будет сделано внутри первого цикла и будет иметь следующую логику.
//--- Initialize starting x-coordinate for each data entry int startX = 65; //--- Loop through calendar data columns for (int k=0; k<ArraySize(array_calendar); k++){ MqlCalendarEvent event; //--- Declare event structure CalendarEventById(values[i].event_id,event); //--- Retrieve event details by ID MqlCalendarCountry country; //--- Declare country structure CalendarCountryById(event.country_id,country); //--- Retrieve country details by event's country ID //--- Print event details for debugging Print("Name = ",event.name,", IMP = ",EnumToString(event.importance),", COUNTRY = ",country.name,", TIME = ",values[i].time); //--- Skip event if currency does not match the selected country code // if (StringFind(_Symbol,country.currency) < 0) continue; //--- Prepare news data array with time, country, and other event details string news_data[ArraySize(array_calendar)]; news_data[0] = TimeToString(values[i].time,TIME_DATE); //--- Event date news_data[1] = TimeToString(values[i].time,TIME_MINUTES); //--- Event time news_data[2] = country.currency; //--- Event country currency //--- Determine importance color based on event impact color importance_color = clrBlack; if (event.importance == CALENDAR_IMPORTANCE_LOW){importance_color=clrYellow;} else if (event.importance == CALENDAR_IMPORTANCE_MODERATE){importance_color=clrOrange;} else if (event.importance == CALENDAR_IMPORTANCE_HIGH){importance_color=clrRed;} //--- Set importance symbol for the event news_data[3] = ShortToString(0x25CF); //--- Set event name in the data array news_data[4] = event.name; MqlCalendarValue value; //--- Declare calendar value structure CalendarValueById(values[i].id,value); //--- Retrieve actual, forecast, and previous values //--- Populate actual, forecast, and previous values in the news data array news_data[5] = DoubleToString(value.GetActualValue(),3); news_data[6] = DoubleToString(value.GetForecastValue(),3); news_data[7] = DoubleToString(value.GetPreviousValue(),3); //--- Create label for each news data item if (k == 3){ createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY-(22-12),news_data[k],importance_color,22,"Calibri"); } else { createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY,news_data[k],clrBlack,12,"Calibri"); } //--- Increment x-coordinate for the next column startX += buttons[k]+3; }
Здесь мы инициализируем целочисленную переменную startX значением 65, которое будет служить начальной координатой X для позиционирования каждой записи данных, связанной с событиями календаря. Затем мы входим в цикл, который проходит по каждому столбцу в array_calendar, используя индекс k. В этом цикле мы объявляем структурную переменную event типа MqlCalendarEvent, которая будет использоваться для хранения сведений о конкретном календарном событии. Мы получаем детали события, вызывая функцию CalendarEventById, передающую идентификатор события из массива values и сохраняющую результаты в event.
Далее мы объявляем еще одну структурную переменную country типа MqlCalendarCountry для хранения информации о стране, связанной с календарным событием. Мы используем функцию CalendarCountryById для заполнения country данными на основе идентификатора страны события. В целях отладки мы выводим на печать ключевые сведения о событии, такие как название события, уровень его важности (преобразованный в строку с помощью функции EnumToString), название страны и время события, хранящееся в values[i].time.
Затем мы подготавливаем строковый массив news_data и размером, равным array_calendar, для хранения информации, связанной с событием. Первый элемент news_data задается как дата события, отформатированная как строка с использованием функции TimeToString с флагом TIME_DATE. Второй элемент фиксирует время события, отформатированное с использованием флага TIME_MINUTES. Третий элемент хранит валюту страны события.
Далее мы определяем цвет важности события, инициализируя переменную importance_colo» черным цветом. Мы проверяем event.importance и на основе его значения (низкое, среднее или высокое) назначаем соответствующий цвет: желтый для низкого, оранжевый для среднего и красный для высокого.
Мы также устанавливаем четвертый элемент news_data для символа, представляющего уровень важности события, используя ShortToString(0x25CF) для создания закрашенного круга. Пятому элементу присваивается имя события, извлеченное из event.name.
Чтобы получить фактические, прогнозируемые и предыдущие значения для события, мы объявляем еще одну структурную переменную value типа MqlCalendarValue и используем функцию CalendarValueById для заполнения этой структуры на основе идентификатора события, сохраненного в values[i].id. Шестой, седьмой и восьмой элементы news_data заполняются фактическими, прогнозируемыми и предыдущими значениями соответственно, отформатированными до трех знаков после запятой с использованием функции DoubleToString.
Наконец, мы создаем метку для каждого элемента данных новостей с помощью функции createLabel. Если k равно "3", мы применяем цвет важности. В противном случае мы используем черный цвет по умолчанию. Координата X для каждой метки определяется значением startX, которое затем увеличивается путем добавления ширины кнопки из массива buttons, гарантируя правильное расположение каждого столбца данных для четкого отображения. После компиляции получаем следующий результат.
Всё работает как надо. Мы создали панель экономического календаря MQL5, которая отображает новости на графике для удобства использования. В следующих частях серии мы улучшим панель, интегрировав фильтры и обновления данных в реальном времени, а также используя новости в торговле.
Заключение
Мы успешно заложили основу нашего экономического календаря MQL5, создав интерактивную панель, которая отображает важнейшие экономические события в удобном для пользователя формате. Реализуя такие функции, как извлечение данных календаря, визуальная категоризация событий по степени их важности и интуитивно понятная маркировка, мы можем быть в курсе существенных изменений на рынке. Проведенная работа не только улучшает пользовательский интерфейс, но и обеспечивает надежную основу для дальнейших улучшений, которые выведут нашу панель на новый уровень.
В следующих частях этой серии мы интегрируем дополнительные функции, такие как фильтры новостей, которые помогут нам сосредоточиться на наиболее важной информации для наших стратегий в MQL5. Мы также внедрим обновления в реальном времени, чтобы гарантировать, что наша панель отражает последние экономические данные по мере их поступления. Кроме того, мы сосредоточимся на том, чтобы сделать панель адаптивной, что позволит ей легко адаптироваться к различным размерам экрана и взаимодействию с пользователем. В конечном итоге мы стремимся использовать эти данные для принятия обоснованных торговых решений, превращая наш экономический календарь в мощный инструмент для трейдеров, стремящихся извлечь выгоду из волатильности рынка. Оставайтесь с нами!
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/16301





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования