Советник для размещения сделок на основе риска с графическим интерфейсом на графике (Часть 1): Проектирование пользовательского интерфейса
Введение
Многим трейдерам сложно рассчитывать правильный размер лота для каждой сделки, сохраняя при этом постоянный уровень риска. Выполнять такие расчеты вручную перед каждым входом — повторяющаяся и затратная по времени задача; даже небольшая ошибка может привести к нежелательным убыткам. Трейдерам нужен способ сделать этот процесс быстрее и надежнее прямо с графика.
В этой статье предлагается практическое решение: мы покажем, как спроектировать панель управления на графике для советника, размещающего сделки на основе риска. Интерфейс, который мы создадим, станет основой полноценной системы, которая позже автоматизирует расчет размера лота и размещение ордеров. В первой части мы сосредоточимся на создании статического графического макета, который станет визуальным ядром инструмента.
Цель этого проекта выходит за рамки простого дизайна. Задача — создать основу, которую в дальнейшем можно будет использовать в реальных торговых условиях. Читатели также узнают, как правильно размещать и оформлять графические объекты, чтобы позже создавать собственные профессиональные интерфейсы для пользовательских советников.
В начале статьи читателю важно понять, как создать упорядоченный и привлекательный графический интерфейс (GUI) в MQL5. К концу статьи у него будут знания и пример кода для создания полноценной статической панели на графике. Эта статья — первый шаг в серии из двух частей, которая ведет от визуального дизайна к полной функциональности.
Концепция и дизайн
Перед началом программирования важно четко представить, что именно мы хотим создать. Графический пользовательский интерфейс этого советника предназначен для того, чтобы сделать работу трейдера проще и быстрее. Он объединяет все ключевые входные данные, необходимые для расчета размера лота и размещения ордера, в одной панели на графике.

Интерфейс позволяет трейдеру выбрать тип ордера, который он хочет разместить. Можно ввести цену входа для отложенных ордеров и указать уровни Stop Loss и Take Profit. Также предусмотрено поле для задания процента риска на сделку. Доступны две основные кнопки: одна рассчитывает корректный размер лота на основе введенных пользователем данных, а другая выполняет расчет и сразу отправляет торговый запрос на сервер.
Панель также содержит небольшой блок, в котором отображается рассчитанный размер лота, чтобы пользователь мог увидеть результат до отправки сделки. Для удобства предусмотрена кнопка закрытия панели, когда она не нужна, и другая кнопка для ее повторного открытия. Благодаря этому интерфейс остается аккуратным и не мешает просмотру графика во время торговли.
Дизайн построен на простом и современном макете. Все элементы аккуратно выровнены, чтобы панель было легко читать и удобно использовать. Цветовая тема использует мягкий светлый фон и минимум акцентных цветов, чтобы внимание оставалось на информации. Заголовок крупнее и выделен другим цветом, поэтому пользователь сразу понимает назначение инструмента. Кнопки действий имеют яркие цвета, которые привлекают внимание и направляют трейдера к следующему шагу.
Уже с первого взгляда пользователь может понять, что делает панель и как с ней взаимодействовать. Макет дает четкое ощущение структуры и назначения, делая инструмент одновременно профессиональным и приятным в использовании.
Подготовка структуры советника
Начнем с создания рабочего файла. Откройте MetaEditor, перейдите в меню Файл --> Создать --> Советник (шаблон) и создайте пустой файл советника. Назовите его SmartRiskTrader. Мы выбираем это имя, потому что оно ясно отражает назначение нашего инструмента. Это интеллектуальный торговый помощник, который управляет размещением сделок на основе риска.
После создания файла удалите из него весь автоматически сгенерированный код, затем вставьте следующий исходный код:
//+------------------------------------------------------------------+ //| SmartRiskTrader.mq5 | //| Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- create timer EventSetTimer(60); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- destroy timer EventKillTimer(); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { } //+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) { } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int32_t id, const long &lparam, const double &dparam, const string &sparam) { } //+------------------------------------------------------------------+
Этот код служит шаблоном. Это чистая основа, на которой мы будем строить полную функциональность нашего советника. Кратко рассмотрим основные части:
- OnInit — Эта функция вызывается при первой загрузке советника на график. Внутри нее мы устанавливаем событие таймера, которое срабатывает через заданный интервал времени. В нашем случае — каждые 60 секунд. Обычно именно в этой функции выполняются операции инициализации советника.
- OnDeinit — Эта функция выполняется, когда советник удаляется с графика или когда терминал завершает работу. Она очищает таймеры и ресурсы, созданные советником.
- OnTick — Эта функция вызывается каждый раз, когда приходит новый ценовой тик. Позже мы будем использовать ее для обработки торговой логики.
- OnTimer — Эта функция выполняется периодически на основе таймера, который мы установили в OnInit. Она отлично подходит для фоновых задач, таких как обновление панелей или проверка торговых условий.
- OnTradeTransaction — Эта функция вызывается каждый раз, когда происходит торговое действие, например открытие, изменение или закрытие позиции. Мы можем использовать ее для мониторинга торговой активности и реакции на нее.
- OnChartEvent — Эта функция обрабатывает взаимодействия пользователя с графиком, например нажатия кнопок. Она необходима для создания нашей панели управления на графике.
Когда структура готова, у нас есть все необходимые обработчики и точки входа для дальнейшего добавления логики. Перейдем дальше и определим несколько служебных функций, которые упростят создание графических объектов на графике и управление ими. Добавьте следующие функции сразу под уже существующим исходным кодом. Они будут служить повторно используемыми строительными блоками при создании пользовательского интерфейса.
//--- UTILITY FUNCTIONS //+------------------------------------------------------------------+ //| Function to generate a unique object name with a given prefix | //+------------------------------------------------------------------+ string GenerateUniqueName(string prefix){ int attempt = 0; string uniqueName; while(true) { uniqueName = prefix + IntegerToString(MathRand() + attempt); if(ObjectFind(0, uniqueName) < 0){ break; } attempt++; } return uniqueName; } //--- Reusable GUI elements //+------------------------------------------------------------------+ //| 1. To create a Rectangular panel | //+------------------------------------------------------------------+ bool CREATE_OBJ_RECTANGLE_LABEL( string objName, int xDistance, int yDistance, int width, int height, color clrBackground, int borderWidth, color borderColor = clrNONE, ENUM_BORDER_TYPE borderType = BORDER_FLAT, ENUM_LINE_STYLE borderStyle = STYLE_SOLID ){ ResetLastError(); //--- Create a rectangular panel if(!ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, 0, 0, 0)){ Print("Error while creating a rectangular panel: ", GetLastError()); return false; } //--- Set values for corresponding object properties ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance); ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance); ObjectSetInteger(0, objName, OBJPROP_XSIZE, width); ObjectSetInteger(0, objName, OBJPROP_YSIZE, height); ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBackground); ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, borderType); ObjectSetInteger(0, objName, OBJPROP_STYLE, borderStyle); ObjectSetInteger(0, objName, OBJPROP_WIDTH, borderWidth); ObjectSetInteger(0, objName, OBJPROP_COLOR, borderColor); ObjectSetInteger(0, objName, OBJPROP_BACK, false); ObjectSetInteger(0, objName, OBJPROP_STATE, false); ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); ChartRedraw(); return true; } //+------------------------------------------------------------------+ //| 2. To create a Button Object | //+------------------------------------------------------------------+ bool CREATE_OBJ_BUTTON( string objName, int xDistance, int yDistance, int width, int height, string text = "Activate", color textColor = clrDarkGray, int fontSize = 12, int borderWidth = 0, color backgroundColor = clrWhiteSmoke, color borderColor = clrBlack, string font = "Tahoma" ){ ResetLastError(); //--- Create a button object if(!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)){ Print("Error while creating a button: ", GetLastError()); return false; } //--- Set values for corresponding object properties ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance); ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance); ObjectSetInteger(0, objName, OBJPROP_XSIZE, width); ObjectSetInteger(0, objName, OBJPROP_YSIZE, height); ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetString (0, objName, OBJPROP_TEXT, text); ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor); ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); ObjectSetString (0, objName, OBJPROP_FONT, font); ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, backgroundColor); ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, BORDER_FLAT); ObjectSetInteger(0, objName, OBJPROP_WIDTH, borderWidth); ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, borderColor); ObjectSetInteger(0, objName, OBJPROP_BACK, false); ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); ChartRedraw(); return true; } //+------------------------------------------------------------------+ //| 3. To create an Input field | //+------------------------------------------------------------------+ bool CREATE_OBJ_EDIT( string objName, int xDistance, int yDistance, int width, int height, string text = "Say sth'...", color textColor = clrGray, int fontSize = 12, color backgroundColor = clrWhite, color borderColor = clrBlack, string font = "Tahoma" ){ ResetLastError(); //--- Create an input field if(!ObjectCreate(0, objName, OBJ_EDIT, 0, 0, 0)){ Print("Error while creating a text input: ", GetLastError()); return false; } //--- Set values for corresponding object properties ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance); ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance); ObjectSetInteger(0, objName, OBJPROP_XSIZE, width); ObjectSetInteger(0, objName, OBJPROP_YSIZE, height); ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetString (0, objName, OBJPROP_TEXT, text); ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor); ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); ObjectSetString (0, objName, OBJPROP_FONT, font); ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, backgroundColor); ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, borderColor); ObjectSetInteger(0, objName, OBJPROP_ALIGN, ALIGN_LEFT); ObjectSetInteger(0, objName, OBJPROP_READONLY, false); ObjectSetInteger(0, objName, OBJPROP_BACK, false); ObjectSetInteger(0, objName, OBJPROP_STATE, false); ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); ChartRedraw(); return true; } //+------------------------------------------------------------------+ //| 4. To create a Text label | //+------------------------------------------------------------------+ bool CREATE_OBJ_LABEL( string objName, int xDistance, int yDistance, string text = "Name sth'...", color textColor = clrDarkGray, int fontSize = 12, string font = "Tahoma" ){ ResetLastError(); //--- Create a text label if(!ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0)){ Print("Error while creating a text label: ", GetLastError()); return false; } //--- Set values for corresponding object properties ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xDistance); ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yDistance); ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetString (0, objName, OBJPROP_TEXT, text); ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor); ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); ObjectSetString (0, objName, OBJPROP_FONT, font); ObjectSetInteger(0, objName, OBJPROP_BACK, false); ObjectSetInteger(0, objName, OBJPROP_STATE, false); ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); ChartRedraw(); return true; }
Теперь разберем, что делает каждая из этих функций.
1. GenerateUniqueName
Каждый графический объект на графике должен иметь уникальное имя. Эта функция создает уникальное имя с использованием префикса и случайного числа. Это помогает избежать конфликтов имен при динамическом создании нескольких объектов.
2. CREATE_OBJ_RECTANGLE_LABEL
Эта функция создает прямоугольную панель. Мы будем использовать ее как фон или контейнер для других элементов интерфейса, таких как метки и кнопки.
3. CREATE_OBJ_BUTTON
Эта функция создает кликабельную кнопку с настраиваемыми текстом, размером и цветом. Кнопки необходимы для пользовательских действий, таких как размещение сделок или расчет размера лота.
4. CREATE_OBJ_EDIT
Эта функция создает редактируемое текстовое поле, в которое пользователь может вводить значения. Позже мы будем использовать такие поля для получения входных данных, например цены входа и процента риска.
5. CREATE_OBJ_LABEL
Эта функция создает простую текстовую метку, которая отображает информацию или описания рядом с полями ввода и кнопками.
Вместе эти пять функций формируют основу нашего интерфейса на графике. Они упрощают создание объектов и делают код более организованным и читаемым. В следующем разделе мы используем их для построения основного макета интерфейса.
Пошаговая сборка пользовательского интерфейса
Теперь, когда все вспомогательные функции готовы, пора приступить к сборке самого интерфейса. Чтобы код оставался чистым и организованным, мы создадим одну функцию, которая будет отвечать за весь процесс отрисовки интерфейса. Назовем эту функцию CREATE_GUI. Такая модульность очень полезна, потому что позже нам может понадобиться уничтожать и заново создавать панель, когда пользователь нажимает кнопку закрытия интерфейса. Наличие одной функции, отвечающей за создание всех визуальных элементов, делает этот процесс простым и управляемым. Пока объявим эту функцию сразу под существующими функциями.
... //+------------------------------------------------------------------+ //| Function to render the main GUI | //+------------------------------------------------------------------+ void CREATE_GUI(){ } //+------------------------------------------------------------------+
Мы также вызовем ее внутри функции OnInit чтобы сразу видеть результат работы кода по мере пошагового построения интерфейса.
... //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- create timer EventSetTimer(60); //--- render the graphical user interface CREATE_GUI(); return(INIT_SUCCEEDED); } ...
Перед тем как двигаться дальше, нужно выполнить один важный шаг, чтобы интерфейс корректно отображался на графике. По умолчанию на графиках MetaTrader в левом верхнем углу показываются кнопки торговли в один клик. Они могут перекрывать наш пользовательский интерфейс и ухудшать видимость. Чтобы исправить это, мы настроим внешний вид графика, отключив кнопки торговли в один клик при запуске советника.
Мы сделаем это с помощью простой функции ConfigureChartAppearance, которую определим сразу под существующими служебными функциями. Эта функция устанавливает свойство графика, скрывающее кнопки торговли в один клик.
//+------------------------------------------------------------------+ //| 4. This function configures the chart's appearance. | | //+------------------------------------------------------------------+ bool ConfigureChartAppearance() { if(!ChartSetInteger(0, CHART_SHOW_ONE_CLICK, false)){ Print("Error while setting one click buttons, ", GetLastError()); return false; } return true; }
После определения этой функции мы вызовем ее внутри OnInit, чтобы она автоматически выполнялась при загрузке советника. Это гарантирует, что макет графика будет настроен до отрисовки интерфейса.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { ... //--- Configure chart appearance if(!ConfigureChartAppearance()){ Print("Error while customizing the chart's appearance"); return(INIT_FAILED); } return(INIT_SUCCEEDED); }
Хорошая практика — убедиться, что все графические объекты, созданные нашим советником, корректно удаляются при снятии программы с графика. Это предотвращает захламление и исключает появление оставшихся объектов от предыдущих запусков.
Для этого мы добавим простую строку кода внутри функции OnDeinit, которая очищает все графические объекты при удалении советника. Функция ObjectsDeleteAll удаляет каждый объект с текущего графика, оставляя его чистым и готовым к дальнейшему использованию.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ... //--- delete all graphical objects ObjectsDeleteAll(0); }
Прежде чем создавать отдельные объекты нашей панели, нужно убедиться, что каждый объект имеет уникальное имя. Это поможет легко идентифицировать объекты на графике и управлять ими. Для этого мы будем использовать макросы, в которых сохраним имена объектов. Для объектов, к которым не нужно часто обращаться или которые не нужно изменять, мы просто зададим короткий префикс, используемый для генерации их имен. Добавим следующий фрагмент кода сразу под секцией с директивами свойств советника.
... //+------------------------------------------------------------------+ //| Macros | //+------------------------------------------------------------------+ #define SmartRiskTrader "SmartRiskTrader" ...
Начнем интерфейс с создания фоновой панели, которая будет базовым контейнером для всех остальных элементов интерфейса. Представьте ее как холст, на котором будут размещаться все кнопки, метки и элементы управления. Для этого мы вызываем нашу пользовательскую функцию CREATE_OBJ_RECTANGLE_LABEL, которая рисует на графике объект типа прямоугольная метка.
... //+------------------------------------------------------------------+ //| Function to render the main GUI | //+------------------------------------------------------------------+ void CREATE_GUI(){ //--- Background panel CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 20, 20, 320, 380, clrWhiteSmoke, 1, clrDarkBlue, BORDER_FLAT, STYLE_DASHDOTDOT); } ...
Мы используем GenerateUniqueName(SmartRiskTrader) чтобы гарантировать уникальность имени объекта, и задаем ему аккуратный фон WhiteSmoke с темно-синей рамкой.
На этом этапе скомпилируйте исходный код. После успешной компиляции перейдите на график и запустите советник. Теперь вы должны увидеть аккуратную прямоугольную панель именно там, где вы ее разместили.

Это подтверждает, что функция создания интерфейса работает правильно. Всегда полезно тестировать код постепенно: так можно заранее заметить и устранить проблемы до того, как интерфейс станет более сложным.
Далее добавим еще одну прямоугольную панель поверх исходной фоновой панели. Эта вторичная панель не выполняет функциональной роли — она нужна только для визуальной привлекательности и помогает придать интерфейсу более чистый, многослойный вид. Мы просто наложим ее на первый прямоугольник, используя немного меньшие координаты, чтобы она аккуратно располагалась внутри рамки основной панели.
После добавления ваш код должен выглядеть так:
... //+------------------------------------------------------------------+ //| Function to render the main GUI | //+------------------------------------------------------------------+ void CREATE_GUI(){ //--- Background panel CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 20, 20, 320, 380, clrWhiteSmoke, 1, clrDarkBlue, BORDER_FLAT, STYLE_DASHDOTDOT); CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 30, 30, 300, 360, clrWhite, 1, clrDarkBlue, BORDER_FLAT, STYLE_SOLID); } ...
Это простое добавление придает интерфейсу более аккуратный и многослойный вид. Скомпилируйте и запустите код, чтобы оценить обновленный дизайн.

Теперь, когда фон готов, перейдем к созданию секции заголовка интерфейса. В этой области будет отображаться название инструмента и небольшая кнопка, позволяющая пользователю закрыть панель. Также мы добавим тонкую разделительную линию, чтобы визуально отделить заголовок от остальной части интерфейса.
Мы будем создавать ее пошагово, добавляя по одному компоненту и компилируя код после каждого добавления, чтобы наблюдать изменения в реальном времени.
Начнем с создания небольшого прямоугольника, в котором будет находиться кнопка закрытия:
... //+------------------------------------------------------------------+ //| Function to render the main GUI | //+------------------------------------------------------------------+ void CREATE_GUI(){ //--- Background panel ... CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 30, 30, 300, 360, clrWhite, 1, clrDarkBlue, BORDER_FLAT, STYLE_SOLID); //--- Header Section Components CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 300, 40, 20, 20, clrWhiteSmoke, 1, clrDarkBlue, BORDER_FLAT, STYLE_SOLID); } ...
После добавления этой строки скомпилируйте код и запустите советник, чтобы увидеть небольшое поле заголовка в правой верхней области панели.

Теперь поместим кнопку закрытия («X») прямо внутрь этого поля. В отличие от декоративных компонентов, которые мы создавали до сих пор, эта кнопка действительно будет реагировать на действия пользователя — при нажатии она будет закрывать наш интерфейс. Поэтому важно дать ей уникальное и легко узнаваемое имя, к которому мы позже сможем обращаться в коде.
... //+------------------------------------------------------------------+ //| Macros | //+------------------------------------------------------------------+ #define SmartRiskTrader "SmartRiskTrader" #define BTN_CLOSE_GUI "BTN_CLOSE_GUI" ...
Теперь добавим код, который фактически отображает кнопку.
... //+------------------------------------------------------------------+ //| Function to render the main GUI | //+------------------------------------------------------------------+ void CREATE_GUI(){ ... //--- Header Section Components CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 300, 40, 20, 20, clrWhiteSmoke, 1, clrDarkBlue, BORDER_FLAT, STYLE_SOLID); CREATE_OBJ_LABEL(BTN_CLOSE_GUI, 305, 40, "X", clrDarkBlue, 12); }
Скомпилируйте советник, и вы должны увидеть символ «X» в правой верхней части панели.

Теперь добавим метку заголовка для интерфейса. Это сделает панель более профессиональной и информативной.
... //+------------------------------------------------------------------+ //| Function to render the main GUI | //+------------------------------------------------------------------+ void CREATE_GUI(){ ... CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 37, "Smart Risk Trader", clrDarkBlue, 14, "Comic Sans Ms"); CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 37, "Smart Risk Trader", clrDarkBlue, 14, "Comic Sans Ms"); } ...
Снова скомпилируйте и запустите код. Теперь заголовок должен отображаться в верхней части панели приятным и удобочитаемым шрифтом.

Наконец, создадим горизонтальную линию, которая отделит заголовок от остальных компонентов ниже:
... //+------------------------------------------------------------------+ //| Function to render the main GUI | //+------------------------------------------------------------------+ void CREATE_GUI(){ ... CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 37, "Smart Risk Trader", clrDarkBlue, 14, "Comic Sans Ms"); CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 30, 70, 300, 1, clrDarkBlue, 1, clrDarkBlue, BORDER_FLAT); } ...
Скомпилируйте код еще раз, чтобы убедиться, что все на месте.

После этих шагов у интерфейса появилась простая, но элегантная секция заголовка, которая придает интерфейсу структуру и узнаваемость: небольшая кнопка закрытия, аккуратный заголовок и четкая разделительная линия снизу.
Теперь добавим первое поле ввода и его метку. Это поле позволит пользователю выбрать тип ордера, например Buy, Sell, Buy Limit и другие. Как и раньше, начнем с отображения текстовой метки, описывающей назначение поля.
Далее создадим саму кнопку, которая будет работать как наше поле ввода. Поскольку позже мы хотим обращаться к этой кнопке программно — например, чтобы определять нажатие, — нужно присвоить ей фиксированное имя вместо динамической генерации. Для этого сначала определим макрос следующим образом:
... //+------------------------------------------------------------------+ //| Macros | //+------------------------------------------------------------------+ ... #define BTN_ORDER_TYPES "BTN_ORDER_TYPES" ...
После определения макроса можно создать метку и поле ввода, которое в данном случае представлено кнопкой. Метка будет служить названием поля, а кнопка позволит пользователю взаимодействовать с интерфейсом и выбирать нужный тип ордера.
... //+------------------------------------------------------------------+ //| Function to render the main GUI | //+------------------------------------------------------------------+ void CREATE_GUI(){ ... //--- Order Types CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 90, "Order Type: ", C'20, 20, 20'); CREATE_OBJ_BUTTON(BTN_ORDER_TYPES, 140, 90, 140, 25, "Select Order Type", C'20, 20, 20', 12, 1, clrWhiteSmoke, clrDarkBlue); } ...
После добавления этого кода скомпилируйте и запустите советник, чтобы увидеть новое поле ввода на графике.

Перед созданием выпадающего меню для типов ордеров нам нужно определить несколько макросов. В них будут храниться уникальные идентификаторы каждого элемента выпадающего списка, чтобы позже мы могли легко идентифицировать их и взаимодействовать с ними. Поскольку эти элементы будут реагировать на действия пользователя, такие как щелчки, использование фиксированных имен вместо динамически сгенерированных упрощает обработку событий. Ниже приведены макросы, которые определяют группу выпадающего списка и каждый вариант типа ордера.
#define ORDER_TYPE_GROUP "ORDER_TYPE_GROUP" #define MARKET_BUY "ORDER_TYPE_GROUP_MARKET_BUY" #define MARKET_SELL "ORDER_TYPE_GROUP_MARKET_SELL" #define BUY_LIMIT "ORDER_TYPE_GROUP_BUY_LIMIT" #define SELL_LIMIT "ORDER_TYPE_GROUP_SELL_LIMIT" #define BUY_STOP "ORDER_TYPE_GROUP_BUY_STOP" #define SELL_STOP "ORDER_TYPE_GROUP_SELL_STOP"
Теперь, когда макросы определены, можно создать функцию, которая формирует выпадающий список. Эта функция с именем CREATE_ORDER_TYPE_DROPDOWN будет отрисовывать все элементы выпадающего списка, включая фоновую панель и отдельные метки типов ордеров. Выпадающий список даст пользователям аккуратный и упорядоченный способ выбрать тип ордера непосредственно из интерфейса.
... //+------------------------------------------------------------------+ //| Function to create the order types dropdown | //+------------------------------------------------------------------+ void CREATE_ORDER_TYPE_DROPDOWN(){ CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(ORDER_TYPE_GROUP), 140, 116, 140, 151, clrWhiteSmoke, 1, clrDarkBlue, BORDER_FLAT); CREATE_OBJ_LABEL(MARKET_BUY , 150, 120, "Market Buy ", C'20, 20, 20'); CREATE_OBJ_LABEL(MARKET_SELL, 150, 145, "Market Sell", C'20, 20, 20'); CREATE_OBJ_LABEL(BUY_LIMIT , 150, 170, "Buy Limit ", C'20, 20, 20'); CREATE_OBJ_LABEL(SELL_LIMIT , 150, 195, "Sell Limit ", C'20, 20, 20'); CREATE_OBJ_LABEL(BUY_STOP , 150, 220, "Buy Stop ", C'20, 20, 20'); CREATE_OBJ_LABEL(SELL_STOP , 150, 245, "Sell Stop ", C'20, 20, 20'); }
Чтобы протестировать эту функцию, можно временно вызвать ее внутри OnInit. Это позволит увидеть, как выпадающий список отображается на графике при запуске советника.
... //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- create timer EventSetTimer(60); //--- render the graphical user interface CREATE_GUI(); CREATE_ORDER_TYPE_DROPDOWN(); return(INIT_SUCCEEDED); } ...
Теперь скомпилируйте исходный код и посмотрите на график: вы должны увидеть аккуратное выпадающее меню, красиво отображенное в вашем интерфейсе.
Убедившись, что оно работает правильно, важно закомментировать вызов функции, потому что позже мы будем показывать выпадающий список только после нажатия пользователем кнопки «Select Order Type». Это обеспечит динамическое поведение выпадающего списка, как и должно быть в профессиональном интерфейсе.
... //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- create timer EventSetTimer(60); //--- render the graphical user interface CREATE_GUI(); //CREATE_ORDER_TYPE_DROPDOWN(); return(INIT_SUCCEEDED); } ...
Далее создадим поле цены входа вместе с его меткой. Поскольку позже нам понадобится обращаться к этому полю в коде, нужно присвоить ему фиксированное имя для удобной ссылки. Определим это имя как макрос, как показано ниже:
... //+------------------------------------------------------------------+ //| Macros | //+------------------------------------------------------------------+ ... #define FIELD_ENTRY_PRICE "FIELD_ENTRY_PRICE" ...
После определения макроса можно добавить метку и поле ввода в функцию CREATE_GUI. Эти две строки кода отобразят подпись «Entry Price» и редактируемое поле, в которое пользователь сможет ввести значение.
... //+------------------------------------------------------------------+ //| Function to render the main GUI | //+------------------------------------------------------------------+ void CREATE_GUI(){ ... //--- Entry Price CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 125, "Entry Price: ", C'20, 20, 20'); CREATE_OBJ_EDIT(FIELD_ENTRY_PRICE, 140, 125, 100, 25, "1.14030", C'20, 20, 20', 12, clrWhiteSmoke, clrDarkBlue); }
Теперь скомпилируйте исходный код и запустите советник на графике. Вы должны увидеть аккуратное и хорошо выровненное поле цены входа с меткой, готовое к вводу пользователя.

Теперь, когда мы добавили поле цены входа, повторим ту же процедуру для остальных полей ввода. Эти поля позволят пользователю вводить уровень Stop Loss, уровень Take Profit и процент риска. Как и раньше, мы определим макросы для каждого из этих полей, чтобы позже легко обращаться к ним в коде.
... //+------------------------------------------------------------------+ //| Macros | //+------------------------------------------------------------------+ ... #define FIELD_STOP_LOSS "FIELD_STOP_LOSS" #define FIELD_TAKE_PROFIT "FIELD_TAKE_PROFIT" #define RISK "RISK"
После определения этих макросов вернемся к функции CREATE_GUI и добавим код, создающий метки и поля ввода для каждого из них.
//+------------------------------------------------------------------+ //| Function to render the main GUI | //+------------------------------------------------------------------+ void CREATE_GUI(){ ... //--- Stop Loss CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 160, "Stop Loss: ", C'20, 20, 20'); CREATE_OBJ_EDIT(FIELD_STOP_LOSS, 140, 160, 100, 25, "1.13302", C'20, 20, 20', 12, clrWhiteSmoke, clrDarkBlue); //--- Take Profit CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 195, "Take Profit: ", C'20, 20, 20'); CREATE_OBJ_EDIT(FIELD_TAKE_PROFIT, 140, 195, 100, 25, "1.16302", C'20, 20, 20', 12, clrWhiteSmoke, clrDarkBlue); //--- Risk CREATE_OBJ_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 230, "Risk %: ", C'20, 20, 20'); CREATE_OBJ_EDIT(RISK, 140, 230, 100, 25, "2.0", C'20, 20, 20', 12, clrWhiteSmoke, clrDarkBlue); }
После добавления этих строк скомпилируйте исходный код и запустите советник на графике. Теперь вы должны увидеть аккуратный и организованный макет с тремя новыми полями ввода, каждое из которых правильно подписано и выровнено с остальным интерфейсом.

Далее добавим кнопки выполнения, которые будут обрабатывать такие действия, как расчет размера лота и отправка торговых ордеров. Поскольку позже нам нужно будет определять, когда пользователь нажимает эти кнопки, важно назначить каждой из них уникальное и легко узнаваемое имя. Мы сделаем это, определив макросы для кнопок, как показано ниже:
//+------------------------------------------------------------------+ //| Macros | //+------------------------------------------------------------------+ ... #define BTN_SEND_ORDER "BTN_SEND_ORDER" #define RESULTS_TEXT "RESULTS_TEXT" #define BTN_GUI_OPEN "BTN_GUI_OPEN"
После определения этих макросов можно перейти к созданию самих элементов кнопок внутри нашей функции CREATE_GUI. Следующий код добавляет две кнопки: одну для расчета размера лота, а другую для отправки торговых ордеров.
//+------------------------------------------------------------------+ //| Function to render the main GUI | //+------------------------------------------------------------------+ void CREATE_GUI(){ ... //--- Execution Buttons CREATE_OBJ_BUTTON(BTN_CALC_LOT, 40, 270, 140, 40, "CALCULATE LOT", clrWhite, 12, 1, clrDarkBlue, clrBlack); CREATE_OBJ_BUTTON(BTN_SEND_ORDER, 190, 270, 120, 40, "SEND ORDER", clrWhite, 12, 1, clrDarkGreen, clrBlack); }
Теперь скомпилируйте исходный код и прикрепите советник к графику. Вы должны увидеть две аккуратно выровненные кнопки, которые позже позволят пользователю рассчитывать размер лота и отправлять ордера прямо из интерфейса.

Чтобы завершить статический макет пользовательского интерфейса, создадим небольшую область отображения, где после выполнения расчета будет появляться рассчитанный размер лота. Эта область будет состоять из прямоугольной фоновой панели и текстовой метки внутри нее. Поскольку позже нам нужно будет программно обновлять значение, показанное в этой метке, мы зададим ей уникальное имя, определенное как макрос для удобной ссылки.
//+------------------------------------------------------------------+ //| Macros | //+------------------------------------------------------------------+ ... #define RESULTS_TEXT "RESULTS_TEXT"
После определения макроса можно перейти к созданию самих визуальных элементов. Внутри нашей функции CREATE_GUI мы добавим следующие строки кода, чтобы нарисовать прямоугольный контейнер и разместить внутри него текстовую метку:
//+------------------------------------------------------------------+ //| Function to render the main GUI | //+------------------------------------------------------------------+ void CREATE_GUI(){ ... //--- Execution Results CREATE_OBJ_RECTANGLE_LABEL(GenerateUniqueName(SmartRiskTrader), 40, 320, 270, 50, clrWhiteSmoke, 1, clrDarkBlue, BORDER_FLAT); CREATE_OBJ_LABEL(RESULTS_TEXT, 60, 333, "Result: Lot Size = 0.23", clrDarkGreen, 14); }
Скомпилируйте код и прикрепите советник к графику. Теперь в нижней части интерфейса должна появиться чистая и простая область с примером текста, представляющим рассчитанный размер лота.

С этим добавлением мы завершили статический дизайн интерфейса «Smart Risk Trader». Чтобы вам было проще следовать материалу, мы приложили полный исходный код для текущего этапа проекта. Вы можете открыть его в MetaEditor, изучить рассмотренные функции и убедиться, что все корректно компилируется. В следующем разделе мы сосредоточимся на том, чтобы сделать GUI интерактивным и реагирующим на действия пользователя — фактически оживим его.
Заключение
На этом этапе мы успешно создали аккуратный и функциональный графический интерфейс на графике для нашего советника Smart Risk Trader. Теперь вы понимаете, как использовать графические объекты MQL5 для создания панелей, меток, кнопок и полей ввода, а также как структурировать код модульным образом для простого сопровождения и будущего расширения. То, что начиналось как пустой шаблон советника, превратилось в визуально привлекательную и хорошо организованную основу профессионального торгового инструмента.
Следуя пошаговому руководству, вы не только научились создавать GUI в MQL5, но и получили ценное понимание того, как эти элементы взаимодействуют в среде MetaTrader 5. С этими знаниями вы теперь можете проектировать и создавать еще более продвинутые, стильные и динамические интерфейсы для собственных советников и индикаторов.
Во второй части мы оживим этот интерфейс, подключив каждый компонент к реальной функциональности. Вы узнаете, как обнаруживать действия пользователя и реагировать на них — например, нажатия кнопок и выбор пунктов в выпадающем списке, — а также как сделать Smart Risk Trader полностью интерактивным
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/19932
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Разрабатываем пользовательский индикатор рыночных настроений
TradeMux как Quant Backbone: Подключение институциональных Python-пайплайнов к разным терминалам и брокерам
От начального до среднего уровня: События в объектах (I)
Знакомство с языком MQL5 (Часть 42): Руководство для начинающих по работе с файлами в MQL5 (IV)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования