
Управление рисками (Часть 2): Реализация расчета лотов в графическом интерфейсе
- Введение
- Улучшения функций получения лота и стоп-лосса
- Изучение библиотек элементов управления и панелей в MQL5
- Функции для создания компонентов панели (меток, кнопок и т. д.)
- Создание объектов в клиентской области
- Функции динамического обновления Combobox и других элементов
- Обработка событий клавиатуры: реализация OnCharEvent
- Инициализация панели: использование события OnInit и общая настройка
- Тестирование и проверка панели
- Заключение
Введение
Добро пожаловать! В этой статье, являющейся продолжением предыдущей, мы реализуем на практике все, что было ранее упомянуто, а также улучшим некоторые функции включаемого файла. Чтобы облегчить себе работу, мы воспользуемся мощными библиотеками графических элементов управления MQL5. Цель состоит в том, чтобы более эффективно применить изученное на практике, продемонстрировав, как сочетать графические интерфейсы с нашими функциями управления рисками. В итоге вы получите надежный инструмент, который позволит точно и эффективно рассчитывать объем лота и стоп-лосс (SL).
Улучшения функций получения лота и стоп-лосса
Мы начнем эту статью с улучшения ранее созданных функций, сосредоточившись на упрощении и оптимизации их работы. Основные изменения включают добавление отладочных сообщений (PrintFormat и Print), которые помогают выявлять ошибки в режиме реального времени, а также создание новых функций для более эффективного расчета идеального лота и расстояния до стоп-лосса.
Улучшения функции GetMaxLote
Эта функция рассчитывает максимальный объем лота, которым можно торговать, исходя из доступной свободной маржи и специфики символа.
//+----------------------------------------------------------------------------------------------+ //| Calculates the maximum lot size that can be traded based on free margin and symbol specifics | //+----------------------------------------------------------------------------------------------+ double GetMaxLote(ENUM_ORDER_TYPE type, double DEVIATION = 100, double STOP_LIMIT = 50) { double VOLUME = 1.0; // Initial volume size ENUM_ORDER_TYPE new_type = MarketOrderByOrderType(type); double price = PriceByOrderType(_Symbol, type, DEVIATION, STOP_LIMIT); // Price for the given order type double volume_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Volume step for the symbol double margin = EMPTY_VALUE; // Required margin, initialized as empty ResetLastError(); if(!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin)) { Print("OrderCalcMargin() failed. Error ", GetLastError()); return 0; // Exit the function if margin calculation fails } if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0) { PrintFormat("Free margin of %+.2f is invalid, you cannot open trades right now",AccountInfoDouble(ACCOUNT_MARGIN_FREE)); return 0; } double result = MathFloor((AccountInfoDouble(ACCOUNT_MARGIN_FREE) / margin) / volume_step) * volume_step; return result; // Return the calculated maximum lot size }
Реализованные улучшения:
- Отладочные сообщения: теперь пользователь получает уведомление, если расчет маржи не удался, или свободная маржа недостаточна.
- Проверка свободной маржи: добавлена проверка для того, чтобы избежать вычислений, если свободная маржа меньше или равна нулю.
Функция GetIdealLot
Эта функция рассчитывает идеальный объем лота, исходя из максимально допустимого риска на сделку и текущих рыночных условий.
//+---------------------------------------------------------------------+ //| Determine the optimal lot size based on risk and current conditions | //+---------------------------------------------------------------------+ void GetIdealLot(double& nlot, double glot, double max_risk_per_operation, double& new_risk_per_operation, long StopLoss) { if(StopLoss <= 0) { Print("[ERROR SL] Stop Loss distance is less than or equal to zero, now correct the stoploss distance: ", StopLoss); nlot = 0.0; return; } Print("Max Lot: ", glot, " | RiskPerOperation: ", max_risk_per_operation); new_risk_per_operation = 0; long spread = (long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); double rpo = (glot * (spread + 1 + (StopLoss * tick_value))); if(rpo > max_risk_per_operation) { if(max_risk_per_operation <= 0) return; double new_lot = (max_risk_per_operation / rpo) * glot; new_lot = MathFloor(new_lot / step) * step; new_risk_per_operation = new_lot * (spread + 1 + (StopLoss * tick_value)); nlot = new_lot; } else { new_risk_per_operation = rpo; nlot = glot; } if(nlot <= 0) PrintFormat("The lot %.2f is invalid, the risk %.2f increases or the sl %i decreases",nlot,max_risk_per_operation,StopLoss); }
Улучшения этой функции:
- Проверка стоп-лосса: перед выполнением важно проверить корректность расстояния до стоп-лосса.
- Понятные сообщения: информируют, если рассчитанный лот недействителен, или если необходимо скорректировать риск.
Новая функция GetLotByRiskPerOperation
Позволяет рассчитать оптимальный объем лота, основываясь исключительно на риске на сделку (в долларах США) и типе ордера, исключая необходимость указывать расстояние до стоп-лосса.
//+--------------------------------------------------------------------+ //| Function to obtain the ideal lot based on your risk per operation | //+--------------------------------------------------------------------+ // risk_per_operation in USD, not % double GetLotByRiskPerOperation(double risk_per_operation, const ENUM_ORDER_TYPE order_type, double DEVIATION = 100, double STOP_LIMIT = 50) { double VOLUME = 1.0; // Initial volume size ENUM_ORDER_TYPE new_type = MarketOrderByOrderType(order_type); double price = PriceByOrderType(_Symbol, order_type, DEVIATION, STOP_LIMIT); // Price for the given order type double volume_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Volume step for the symbol double margin = EMPTY_VALUE; // Required margin, initialized as empty ResetLastError(); if(!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin)) { Print("OrderCalcMargin() failed. Error ", GetLastError()); return 0; // Exit the function if margin calculation fails } double result = MathFloor((risk_per_operation / margin) / volume_step) * volume_step; if(result <= 0) { PrintFormat("The lot %.2f is invalid, the risk %.2f increases",result,risk_per_operation); return 0; } PrintFormat("The ideal lot for %.2f risk per trade is %.2f lots",risk_per_operation,result); return result; // Return the calculated maximum lot size } //+------------------------------------------------------------------+
Основные характеристики:
- Упрощение расчетов: используется только риск в USD и устраняется зависимость от дополнительных параметров.
- Отладка: понятные сообщения о недопустимых объемах лота или чрезмерных рисках.
Улучшение функции CalculateSL
Позволяет рассчитать идеальное расстояние до стоп-лосса в пунктах, согласно риску на сделку и выбранному объему лота.
//+-----------------------------------------------------------------------+ //| Calculate the stop loss distance in points based on risk and lot size | //+-----------------------------------------------------------------------+ long CalculateSL(const ENUM_ORDER_TYPE type, double risk_per_operation, double &chosen_lot, double DEVIATION = 100, double STOP_LIMIT = 50) { double lot = GetLotByRiskPerOperation(risk_per_operation,type,DEVIATION,STOP_LIMIT); chosen_lot = lot; long spread = (long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double result = ((risk_per_operation / lot) - spread - 1) / tick_value; long ideal_sl = long(MathRound(result)); if(ideal_sl <= 0) { PrintFormat("Stop loss %i invalid, please increase the risk per trade %.2f",ideal_sl,risk_per_operation); return 0; } return ideal_sl; }
Улучшения:
- Интеграция с GetLotByRiskPerOperation: используется рассчитанный объем лота для определения стоп-лосса.
- Улучшенный расчет: гарантируется, что стоп-лосс всегда положительный и корректный.
Благодаря этим улучшениям, функции стали более надежными, более гибкими и простыми в отладке. Сообщения об ошибках позволяют быстро выявлять проблемы, а новые реализации упрощают процесс расчета как объема лота, так и стоп-лосса.
Изучение библиотек элементов управления и панелей в MQL5
Прежде чем мы начнем углубляться в библиотеки элементов управления MQL5, необходимо понять базовые принципы работы объектов в MQL5.
Графические объекты в MQL5
В MQL5 графические объекты — это визуальные элементы, которые можно размещать на графике для отображения информации или взаимодействия с пользователем. Эти объекты позиционируются с помощью двух основных координат:
- X (горизонталь) — определяет положение объекта по горизонтальной оси.
- Y (вертикаль) — определяет положение по вертикальной оси.
Эти координаты точно определяют, где именно будет отображаться объект на графике или панели.
Основные свойства графических объектов
Помимо своего расположения, графические объекты обладают такими характеристиками, как:
- высота (height) — пространство, занимаемое объектом по вертикали,
- ширина (width) — пространство, занимаемое им по горизонтали.
Эти свойства важны для того, чтобы убедиться, что объект не выйдет за пределы области, в которой должен отображаться. Например, если вы проектируете интерфейс, эти размеры гарантируют, что элементы будут оставаться упорядоченными и в пределах видимых границ.
Угол привязки
Точка привязки — это место на объекте, которое берется за точку отсчета при использовании координат X и Y. Эта точка определяет, как интерпретируется положение объекта. К тому же, существует 4 типа точек привязки:
Идентификатор | Описание |
---|---|
CORNER_LEFT_UPPER | Центр координат находится в левом верхнем углу графика. |
CORNER_LEFT_LOWER | Центр координат находится в левом нижнем углу графика. |
CORNER_RIGHT_LOWER | Центр координат находится в правом нижнем углу графика. |
CORNER_RIGHT_UPPER | Центр координат находится в правом верхнем углу графика. |
Эти 4 типа привязки можно представить на изображении следующим образом:
Примечание: как правило, по умолчанию наиболее часто используется тип привязки CORNER_LEFT_UPPER. Выбор типа может меняться в зависимости от задачи или того, какой вариант кажется проще. В этой статье мы будем работать только с указанным типом привязки.
Библиотека элементов управления MQL5
Библиотека элементов управления MQL5 — это библиотека классов для создания элементов управления, панелей и т.д. Чтобы ознакомиться с описанием этих элементов, можно обратиться к официальной документации.
Для этого откройте документацию, нажав клавишу F1 в MetaEditor. Оказавшись там, перейдите в раздел "Содержание" и кликните на книгу под названием «Справочное руководство по MQL5». Прокрутив страницу вниз до конца, вы найдете раздел под названием "Стандартная библиотека". Кликнув на нее, вы увидите список различных библиотек для разных приложений. Нас интересует библиотека Controls.
При выборе этой библиотеки, вы получите доступ к разным таблицам, описывающим назначение и функции каждого элемента управления, а также структуру их классификации по типам. Чтобы помочь вам лучше понять структуру и взаимосвязь между классами элементов управления, я подготовил схему, показывающую, как устроено наследование и классы, связанные с этими элементами управления.
Как видим, основным классом является CObject, от которого наследуется класс CWnd. От класса CWnd наследуются два дополнительных класса: CWndContainer, предназначенный для сложных элементов управления, и CWndObj, используемый для более простых. В наших панелях мы будем использовать оба типа элементов управления в зависимости от конкретных потребностей для каждого случая. Однако сейчас мы сосредоточимся на элементе управления CAppDialog — классе, отвечающем за создание главной панели, подобной той, которую мы разрабатываем.
На эту панель мы начнем добавлять основные элементы управления, такие как метки для отображения текста, выпдающие списки для выбора типа ордеров, кнопки для сохранения переменных и получения результатов, а также поля ввода, чтобы пользователь мог редактировать значения, например, уровень риска на сделку, который он хочет установить.
Проектирование панели в MQL5: планирование и графические элементы
Перед тем как приступить к программированию панели в MQL5, необходимо продумать ее дизайн. Лучший способ сделать это — создать визуальный макет, как показано на приложенном изображении, используя простые инструменты, такие как Paint, Canva и т. д. Этот шаг гарантирует, что каждый необходимый элемент будет включен и правильно размещен, что позволит избежать пропусков и упростит реализацию. Я сделал это так:
Анализируя изображение панели, мы можем увидеть несколько элементов управления, выполняющих разные функции. Эти элементы дают возможность пользователю взаимодействовать с интерфейсом понятным и эффективным образом. Ниже приведено подробное описание основных элементов и того, как они будут использоваться в нашем классе:
Элемент управления | Функция | Пример на панели | Базовый объект |
---|---|---|---|
CLabel | Используется для отображения статического текста на панели, например описаний или инструкций для пользователя. | Такие метки, как «Risk Per Operation %», «Deviation (Points)» и «Stop Limit (Points)». | Этот элемент управления является производным от графического объекта OBJ_LABEL. |
CComboBox | Дает возможность пользователю выбрать один вариант из раскрывающегося списка. | «Get Lote» и «Get SL», которые позволяют выбрать тип ордера для соответствующего лота и расчета стоп-лосса. | Не основан на одном объекте, поскольку является составным объектом. |
CButton | Интерактивные кнопки, выполняющие действия при нажатии. | Кнопка рядом с «SL Point», которая выполняет действие, например, вычисление или подтверждение введенного значения. Также включает кнопку «Get ideal sl». | Этот элемент управления является производным от графического объекта OBJ_BUTTON. |
CEdit | Дает возможность пользователю вводить или редактировать данные вручную. | Поля, такие как «Risk Per Operation %», «Deviation (Points)» и «Stop Limit (Points)», где пользователь вводит конкретные значения. | Соответствует графическому объекту OBJ_EDIT |
Функции для создания компонентов панели (меток, кнопок и т. д.)
Прежде чем приступить к созданию функций, нам необходимо создать советник (шаблон) в MetaEditor. Этот файл изначально будет пустым, и как только он будет готов, мы сможем начать подключать необходимые библиотеки для работы с компонентами нашего графического интерфейса.
Для этого добавьте следующие строки кода в начало файла:
#include <Controls\Dialog.mqh> #include <Controls\Edit.mqh> #include <Controls\Label.mqh> #include <Controls\Button.mqh> #include <Risk Management.mqh> #include <Controls\ComboBox.mqh>
Создание основного класса
Начнем с определения параметров, задающих размеры различным элементам управления. Эти значения позволят нам настраивать размеры компонентов интерфейса, таких как кнопки, текстовые поля и раскрывающиеся списки.
//+------------------------------------------------------------------+ //| defines | //+------------------------------------------------------------------+ //--- for edits #define EDIT_HEIGHT (20) // edit height //--- for buttons #define BUTTON_WIDTH (80) // size by X coordinate #define BUTTON_HEIGHT (20) // size by Y coordinate //--- for combo box #define COMBO_BOX_WIDTH (200) // size by X coordinate #define COMBO_BOX_HEIGHT (20) // size by Y coordinate
Далее мы создадим массив типа string вместимостью в 8 элементов. Этот массив будет использоваться для хранения различных типов ордеров в текстовом формате.
string elements_order_type[8] = { "ORDER_TYPE_BUY", "ORDER_TYPE_SELL", "ORDER_TYPE_BUY_LIMIT", "ORDER_TYPE_SELL_LIMIT", "ORDER_TYPE_BUY_STOP", "ORDER_TYPE_SELL_STOP", "ORDER_TYPE_BUY_STOP_LIMIT", "ORDER_TYPE_SELL_STOP_LIMIT" };
Создание класса для панели управления рисками
Теперь мы создадим новый класс, который унаследует публичную часть класса CAppDialog, являющегося основным классом для панелей. Этот класс станет основой нашей панели управления рисками.
//+-------------------------------------------------------------------+ //| Class CRiskManagementPanel | //| This class inherits from CAppDialog and will define the panel for | //| managing risk parameters. | //+-------------------------------------------------------------------+ class CRiskManagementPanel : public CAppDialog { // Declare private members here. };
Добавление методов и атрибутов
Далее мы начнем добавлять методы и атрибуты в наш класс. Как уже упоминалось, нам понадобятся различные графические элементы управления, и, как видно на предыдущем изображении, эти элементы будут добавлены в приватную часть класса.
private:
CLabel m_label_risk_per_operation;
CEdit m_edit_risk_per_operation;
CLabel m_label_deviaiton;
CEdit m_edit_deviaiton;
CLabel m_label_stop_limit;
CEdit m_edit_stop_limit;
CLabel m_label_get_lote;
CComboBox m_combobox_odertype_get_lot;
CLabel m_label_sl;
CEdit m_edit_sl;
CButton m_buttom_get_lote;
CLabel m_label_result_lote;
CLabel m_label_the_result_lote;
CLabel m_label_get_sl;
CComboBox m_combobox_odertype_get_sl;
CLabel m_label_lote;
CButton m_buttom_get_sl;
CLabel m_label_result_sl;
CLabel m_label_the_result_sl;
Приватные переменные для хранения данных
Помимо элементов управления, нам понадобятся приватные переменные, которые будут хранить данные, введенные пользователем через элементы Edit и ComboBox. Эти переменные будут содержать такие значения, как риск на сделку, величина отклонения, тип ордера и значения стоп-лосса.
// Variables to store the data entered by the user double deviation; // Stores deviation entered by the user double stop_limit; // Stores stop limit entered by the user double risk_per_operation; // Stores risk per operation entered by the user long sl; // Stores stop loss value entered by the user ENUM_ORDER_TYPE order_type_get_sl; // Stores the selected order type for stop loss ENUM_ORDER_TYPE order_type_get_lot; // Stores the selected order type for lot size
Чтобы завершить приватную часть нашего класса, мы объявим все функции, необходимые для управления графическими элементами и взаимодействия с пользователем. Эти функции будут включать: создание объектов, функции, которые будут выполняться при изменении пользователем выбранного элемента в ComboBox, а также функции для обновления значений Labels и другие.
Объявление приватных функций
//--- create labels and buttons bool CreateAreaClientPanel(); //--- functions to edit labels dynamically void EditLabelResultSL(string text); void EditLabelResultLote(string text); //--- create controls (buttons, labels, edits, combo boxes) bool CreateEdit(CEdit &m_edit, const string name, const int x1, const int y1, string initial_Text = ""); bool CreateLabel(CLabel &label, const string name, const string text, const int x1, const int y1); bool CreateButton(CButton &button, const string name, const string text, const int x1, const int y1, int x2_ = BUTTON_WIDTH, int y2_ = BUTTON_HEIGHT); bool CreateComboBox(CComboBox &combo_box, const string name, string &elements[], string initial_text, const int x1, const int y1); //--- combo box functions for handling user input void OnChangeComBoxOrderTypeGetLote(); void OnChangeComBoxOrderTypeGetSL();
Объявление публичных методов
В публичной части класса мы добавляем две функции для создания панели и элементов управления в клиентской области. Также мы объявляем событие OnEvent для обработки событий графика и функцию для преобразования строки в ENUM_ORDER_TYPE, а также конструкторы и деструкторы.
public: CRiskManagementPanel(void); ~CRiskManagementPanel(void); //--- create panel and controls virtual bool Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2); //--- chart event handler virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam); //--- function to convert string to ENUM_ORDER_TYPE static ENUM_ORDER_TYPE StringOrderTypeToEnum(const string OrderType);
Благодаря этим объявлениям, у нас есть все необходимое для создания панели, элементов управления и обработки событий на графике.
Создание методов для добавления элементов управления
В этом разделе мы подробно рассмотрим методы, необходимые для создания базовых элементов управления, таких как метки (labels), кнопки (buttons), поля ввода (edits) и раскрывающиеся списки (comboBoxes), которые будут частью нашей панели управления рисками. Кроме того, будет объяснен ключевой метод, обеспечивающий корректную работу панели путем привязки элементов управления к клиентской области. Это гарантирует, что элементы управления будут оставаться совмещенными с панелью при ее перемещении или изменении размера.
Привязка элементов управления к клиентской области
Чтобы элементы управления (такие как кнопки, метки и т.д.) перемещались вместе с панелью при взаимодействии с ней, они должны быть привязаны к клиентской области панели. Для этого используется метод Add класса CDialog. Данный метод позволяет регистрировать элементы управления в списке объектов клиентской области, обеспечивая их синхронизацию с панелью. Ниже представлено его определение в CDialog:
//+------------------------------------------------------------------+ //| Add control to the client area (by pointer) | //+------------------------------------------------------------------+ bool CDialog::Add(CWnd *control) { return(m_client_area.Add(control)); } //+------------------------------------------------------------------+ //| Add control to the client area (by reference) | //+------------------------------------------------------------------+ bool CDialog::Add(CWnd &control) { return(m_client_area.Add(control)); }
Метод Add имеет две версии: одна принимает указатель на элемент управления, а другая использует ссылку на него. Обе версии добавляют элемент управления в контейнер m_client_area, который управляет всеми элементами, связанными с панелью.
Если пропустить эту привязку, элементы управления не будут следовать за перемещением панели, что может привести к визуальным несоответствиям или проблемам при взаимодействии.
Методы создания базовых элементов управления
1. Функция для создания Labels
Метки (Labels) используются для отображения статического текста на панели, например заголовков или инструкций. Функция создания метки включает:
- определение координат элемента управления;
- создание объекта label с помощью метода Create;
- настройку текста label с помощью Text;
- привязку label к клиентской области с помощью Add.
Пример кода:
//+------------------------------------------------------------------+ //| Function to create labels | //+------------------------------------------------------------------+ bool CRiskManagementPanel::CreateLabel(CLabel &label,const string name, const string text, const int x1, const int y1) { //--- coordinates int x2=x1+50; int y2=y1+20; //--- create if(!label.Create(m_chart_id,name+"Label",m_subwin,x1,y1,x2,y2)) return(false); if(!label.Text(text)) return(false); if(!Add(label)) return(false); //--- succeed return(true); }
- label.Create — создает графический элемент управления по заданным координатам,
- label.Text — задает текст, который будет отображаться в элементе Label,
- Add — добавляет метку на панель, для управления ею совместно с другими элементами управления.
2. Функция создания кнопок:
Кнопки дают возможность пользователю напрямую взаимодействовать с панелью. Функция включает параметры для текста, размеров и начальных координат. Кроме того, она позволяет настраивать размер кнопок с помощью предустановленных значений.
Пример кода:
//+------------------------------------------------------------------+ //| Function to create buttons | //+------------------------------------------------------------------+ bool CRiskManagementPanel::CreateButton(CButton &buttom,const string name, const string text, const int x1, const int y1,int x2_= BUTTON_WIDTH,int y2_ = BUTTON_HEIGHT) { int x2=x1+x2_; int y2=y1+y2_; //--- create if(!buttom.Create(m_chart_id,name,m_subwin,x1,y1,x2,y2)) return(false); if(!buttom.Text(text)) return(false); if(!Add(buttom)) return(false); //--- succeed return(true); }
Размеры кнопки:
- x2_ и y2_ — необязательные параметры, определяющие ширину и высоту кнопки. Если они не указаны, используются значения по умолчанию (BUTTON_WIDTH и BUTTON_HEIGHT).
Используемые методы:
- button.Create — создает кнопку по заданным координатам;
- button.Text — задает текст, который будет отображаться на кнопке;
- Add — регистрирует кнопку в списке элементов панели.
3.Функция для создания полей ввода (edits)
Редактируемые поля дают возможность пользователю вводить текст на панель. Эта функция включает необязательный параметр для установки начального текста.
Пример кода:
//+------------------------------------------------------------------+ //| Function to create edits | //+------------------------------------------------------------------+ bool CRiskManagementPanel::CreateEdit(CEdit &m_edit, const string name, const int x1, const int y1, string initial_Text = "") { //--- coordinates int y2=y1+EDIT_HEIGHT; int x2 =x1 +100; //--- create if(!m_edit.Create(m_chart_id,name+"Edit",m_subwin,x1,y1,x2,y2)) return(false); //--- allow editing the content if(!m_edit.ReadOnly(false)) return(false); if(!Add(m_edit)) return(false); m_edit.Text(initial_Text); //--- succeed return(true); }
Размеры текстового поля:
- Фиксированная ширина 100 и высота, определяемая EDIT_HEIGHT, задают размеры поля.
Используемые методы:
- m_edit.ReadOnly(false) — разрешает пользователю редактировать поле,
- m_edit.Text — задает начальный текст, который будет отображаться в поле.
4. Функция создания ComboBoxes
ComboBox — это сложный элемент управления, который дает возможность пользователю выбрать один элемент из раскрывающегося списка. Эта функция также включает возможность задать начальный текст и добавить несколько элементов.
Пример кода:
//+-------------------------------------------------------------------+ //| Function to create the complex object: combo box | //| This function creates a combo box with multiple selectable items. | //+-------------------------------------------------------------------+ bool CRiskManagementPanel::CreateComboBox(CComboBox &combo_box, const string name, string &elements[], string initial_text, const int x1, const int y1) { //--- calculate coordinates for the combo box int x2 = x1 + COMBO_BOX_WIDTH; int y2 = y1 + COMBO_BOX_HEIGHT; //--- create the combo box control if (!combo_box.Create(m_chart_id, name, m_subwin, x1, y1, x2, y2)) return (false); //--- add items to the combo box for (int i = 0; i < ArraySize(elements); i++) { if (!combo_box.AddItem(elements[i], i)) return (false); } //--- select the initial text combo_box.SelectByText(initial_text); //--- add the combo box to the panel if (!Add(combo_box)) return (false); //--- successfully created the combo box return (true); }
Элементы ComboBox:
- «elements[]» — массив строк, содержащий элементы, которые будут отображаться в раскрывающемся списке,
- "combo_box.AddItem" — добавляет каждый элемент в ComboBox, связывая его с уникальным индексом.
Начальный выбор:
- "combo_box.SelectByText(initial_text)" — определяет, какой элемент будет отображаться в ComboBox при его создании.
Используемые методы:
- "combo_box.Create" — инициализирует ComboBox по заданным координатам,
- "Add" — добавляет ComboBox на панель.
Создание объектов в клиентской области
В этом разделе мы приступаем к проектированию и созданию элементов клиентской области в нашем пользовательском интерфейсе. Цель — предоставить пользователю функциональное пространство для настройки и управления параметрами, связанными с риском и торговыми ордерами. Ниже описано, как разделяется эта область, и как мы создаем каждый компонент.
Панель клиента делится на три основные секции:
Общая секция: в этой части пользователь сможет редактировать общие параметры, такие как риск на сделку, отклонение и предел стопа.
Расчет лота: вторая часть панели предназначена для расчета размера лота на основе риска на сделку и стоп-лосса в пунктах. Здесь выполняется автоматическая оценка того, какой уровень риска готов принять пользователь.
Расчет стоп-лосса: в нижней части панели можно рассчитать стоп-лосс на основе приемлемого процента убытка на сделку.
bool CRiskManagementPanel::CreateAreaClientPanel(void) { int x1 = 11; // Initial X coordinate int y1 = 15; // Initial Y coordinate // --- General Section: Risk Per Operation Configuration --- if (!CreateLabel(m_label_risk_per_operation, "L-Risk-Per-operation", "Risk per operation %: ", x1, y1)) return false; // Create the label for risk per operation if (!CreateEdit(m_edit_risk_per_operation, "Risk-Per-operation", x1 + 150, y1)) return false; // Create the editable field for risk per operation y1 += 30; // Move the Y coordinate down for the next section if (!CreateLabel(m_label_deviation, "L-Deviation", "Deviation (Points):", x1, y1)) return false; // Create the label for deviation if (!CreateEdit(m_edit_deviation, "Deviation", x1 + 150, y1, "100")) return false; // Create the editable field for deviation this.deviation = 100; // Default value for deviation y1 += 30; if (!CreateLabel(m_label_stop_limit, "L-StopLimit", "Stop Limit (Points):", x1, y1)) return false; // Create the label for stop limit if (!CreateEdit(m_edit_stop_limit, "Stop Limit", x1 + 150, y1, "50")) return false; // Create the editable field for stop limit this.stop_limit = 50; // Default value for stop limit y1 += 50; // --- Lot Calculation Section --- if (!CreateLabel(m_label_get_lote, "L-Get-Lote-Title", "Get Lote", x1, y1)) return false; // Create the label for lot calculation section if (!CreateComboBox(m_combobox_order_type_get_lot, "ORDER_TYPE_LOT", elements_order_type, "ORDER_TYPE_BUY", x1 + 60, y1)) return false; // Create the combo box to select order type for lot calculation this.order_type_get_lot = ORDER_TYPE_BUY; // Default order type y1 += 30; if (!CreateLabel(m_label_sl, "L-SL", "SL Point: ", x1, y1)) return false; // Create the label for SL point if (!CreateEdit(m_edit_sl, "WRITE-SL", x1 + 60, y1)) return false; // Create the editable field for SL if (!CreateButton(m_button_get_lote, "GET-LOTE", "Save", x1 + 160 + 5, y1)) return false; // Create the button to save the lot calculation y1 += 25; if (!CreateLabel(m_label_result_lote, "L-Result-Lote", "Ideal Lot: ", x1, y1)) return false; // Create the label for displaying the ideal lot if (!CreateLabel(m_label_the_result_lote, "L-The-Result-lot", " ", x1 + 65, y1)) return false; // Create a label to display the calculated ideal lot y1 += 50; // --- Stop Loss Calculation Section --- if (!CreateLabel(m_label_get_sl, "L-Get-SL-Title", "Get SL", x1, y1)) return false; // Create the label for stop loss calculation section if (!CreateComboBox(m_combobox_order_type_get_sl, "ORDER_TYPE_SL", elements_order_type, "ORDER_TYPE_BUY", x1 + 50, y1)) return false; // Create the combo box to select order type for stop loss calculation this.order_type_get_sl = ORDER_TYPE_BUY; // Default order type y1 += 30; if (!CreateLabel(m_label_lote, "L-LOTE", "Get ideal sl:", x1, y1)) return false; // Create the label for getting the ideal stop loss if (!CreateButton(m_button_get_sl, "GET-SL", "Get", x1 + 90, y1)) return false; // Create the button to get the stop loss value y1 += 25; if (!CreateLabel(m_label_result_sl, "L-Result-sl", "Ideal SL:", x1, y1)) return false; // Create the label for displaying the ideal stop loss if (!CreateLabel(m_label_the_result_sl, "L-The-result-sl", " ", x1 + 65, y1)) return false; // Create a label to display the calculated ideal stop loss return true; // If all components are successfully created }
Основная функция для создания панели:
Функция Create является ядром для инициализации графической панели в пользовательском интерфейсе. Эта функция объединяет несколько ключевых элементов, необходимых для установки и настройки панели, обеспечивающих ее корректную связь с графиком и правильное назначение размеров и координат.
Входные параметры
chart (long):
- представляет ID графика, на котором будет создана панель,
- по умолчанию в MQL5 текущий график имеет идентификатор, равный 0.
name (string):
- имя, идентифицирующее панель,
- оно уникально и используется для ссылки на панель в других операциях.
subwin (int):
- номер подокна графика, в котором будет размещена панель,
- если панель находится в главном окне, это значение будет равно 0. Для дополнительных подокон назначаются последовательные значения (1, 2 и т. д.).
x1 y y1 (int):
- начальные координаты панели (верхний левый угол) на графике.
x2 y y2 (int):
- Определяют размеры панели:
- x2 — определяет ширину,
- y2 — определяет высоту.
- Эти размеры задаются в пикселях.
- Определяют размеры панели:
//+------------------------------------------------------------------+ //| function to create the interface | //+------------------------------------------------------------------+ bool CRiskManagementPanel::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) { if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); if(!CreateAreaClientPanel()) return(false); //--- succeed return(true); }
Функции динамического обновления Combobox и других элементов
Теперь, продолжая создание функций, мы завершим реализацию динамических функций, которые изменяют значения элементов пользовательского интерфейса, таких как ComboBox и Label, отображающих результаты расчетов размера лота (lot size) и стоп-лосса (SL). Эти функции позволяют быстро изменять текст меток с результатами и реагировать на изменения в ComboBox, которые может сделать пользователь.
1. Функции редактирования меток
Эти функции позволяют изменять текст меток с результатами, отображаемых в интерфейсе, в частности меток размера лота (lot size) и стоп-лосса (SL), на основе выполненных расчетов.
//+------------------------------------------------------------------+ //| Function to edit the text of the lot size label | //+------------------------------------------------------------------+ void CRiskManagementPanel::EditLabelResultLote(string text) { // This function updates the text of the label that shows the ideal lot size. this.m_label_the_result_lote.Text(text); // Set the new text to the label }Эта функция обновляет текст метки, отображающей рассчитанный размер лота. В качестве параметра передается текстовое значение, которым и обновляется соответствующая метка.
//+------------------------------------------------------------------+ //| Function to edit the text of the stop loss label | //+------------------------------------------------------------------+ void CRiskManagementPanel::EditLabelResultSL(string text) { // This function updates the text of the label that shows the ideal stop loss value. this.m_label_the_result_sl.Text(text); // Set the new text to the stop loss label }
Аналогично предыдущей функции, эта функция отвечает за обновление текста метки, отображающей идеальное значение стоп-лосса. Она также получает текст в качестве параметра и обновляет соответствующую метку.
2. Функции для обработки изменений в ComboBox
Эти функции выполняются каждый раз, когда пользователь выбирает новое значение в ComboBox, который управляет типом ордера (например, покупка или продажа). Они обновляют внутреннюю переменную, в которой хранится выбранный тип ордера.
//+------------------------------------------------------------------+ //| Function to update the variable that stores | //| the order type to obtain the ideal sl | //+------------------------------------------------------------------+ void CRiskManagementPanel::OnChangeComBoxOrderTypeGetSL(void) { // Iterate through the order types array to find the selected type for(int i = 0; i < ArraySize(elements_order_type); i++) { // If the selected order type matches the one in the array if(m_combobox_order_type_get_sl.Select() == elements_order_type[i]) { // Update the order type variable for stop loss this.order_type_get_sl = StringOrderTypeToEnum(m_combobox_order_type_get_sl.Select()); Print("New order type for sl: ", EnumToString(this.order_type_get_sl)); // Log the selected order type break; } } }
Функция выполняется, когда пользователь изменяет тип ордера в ComboBox для получения стоп-лосса. Она проходит по массиву типов ордеров и проверяет, какой из них был выбран в ComboBox. Затем обновляет внутреннюю переменную order_type_get_sl соответствующим типом ордера, используя функцию StringOrderTypeToEnum.
//+------------------------------------------------------------------+ //| Function to update the variable that stores | //| the order type to obtain the ideal lot | //+------------------------------------------------------------------+ void CRiskManagementPanel::OnChangeComBoxOrderTypeGetLote(void) { // Iterate through the order types array to find the selected type for(int i = 0; i < ArraySize(elements_order_type); i++) { // If the selected order type matches the one in the array if(m_combobox_order_type_get_lot.Select() == elements_order_type[i]) { // Update the order type variable for lot size this.order_type_get_lot = StringOrderTypeToEnum(m_combobox_order_type_get_lot.Select()); Print("New order type for lot: ", EnumToString(this.order_type_get_lot)); // Log the selected order type break; } } }
Аналогично предыдущей функции, эта выполняет ту же задачу, но для размера лота. Когда пользователь изменяет тип ордера в ComboBox для расчета идеального лота, функция обновляет переменную order_type_get_lot выбранным типом ордера.
3. Дополнительная функция для преобразования строки (string) в перечисление ENUM_ORDER_TYPE
В заключение, эта функция преобразует текстовую строку, представляющую тип ордера (например, «ORDER_TYPE_BUY»), в соответствующее перечисляемое значение типа ENUM_ORDER_TYPE.
//+------------------------------------------------------------------+ //| Function to convert a string into an order type | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE CRiskManagementPanel::StringOrderTypeToEnum(const string OrderType) { // Convert the string order type to its corresponding enum value if(OrderType == "ORDER_TYPE_BUY") return ORDER_TYPE_BUY; if(OrderType == "ORDER_TYPE_SELL") return ORDER_TYPE_SELL; if(OrderType == "ORDER_TYPE_BUY_LIMIT") return ORDER_TYPE_BUY_LIMIT; if(OrderType == "ORDER_TYPE_SELL_LIMIT") return ORDER_TYPE_SELL_LIMIT; if(OrderType == "ORDER_TYPE_BUY_STOP") return ORDER_TYPE_BUY_STOP; if(OrderType == "ORDER_TYPE_SELL_STOP") return ORDER_TYPE_SELL_STOP; if(OrderType == "ORDER_TYPE_BUY_STOP_LIMIT") return ORDER_TYPE_BUY_STOP_LIMIT; if(OrderType == "ORDER_TYPE_SELL_STOP_LIMIT") return ORDER_TYPE_SELL_STOP_LIMIT; // Return WRONG_VALUE if no match is found return WRONG_VALUE; }
Обработка событий клавиатуры: реализация OnCharEvent
Завершая эту статью, мы определим метод OnChartEvent, ранее уже объявленный в нашем классе. Этот метод будет отвечать за выполнение функций, созданных в предыдущем разделе, а также за расчет размера лота и стоп-лосса при нажатии необходимых кнопок, за обновление полей ввода и так далее.
Но прежде чем приступить к реализации указанного, нам нужно понять принцип работы событий в библиотеке элементов управления MQL5.
Важно прояснить, что вместо использования предустановленных событий, таких как CHARTEVENT_OBJECT_CLICK (которые распространены во многих средах программирования), мы используем пользовательские события. Эти события определены в специальном файле под названием Defines.mqh, который находится в папке includes\Controls\Defines.mqh нашего проекта.
Файл Defines.mqh содержит перечисления и определения, необходимые для работы элементов управления. Помимо предустановленных значений, таких как цвета элементов управления или панели, наиболее важными, в нашем случае, являются определения событий, которые находятся в конце файла. Ниже я объясню, как они организованы, и как мы их используем.
Определение событий
В файле Defines.mqh есть несколько кастомных (пользовательских) событий, которые можно использовать в элементах управления.
//+------------------------------------------------------------------+ //| Events | //+------------------------------------------------------------------+ #define ON_CLICK (0) // clicking on control event #define ON_DBL_CLICK (1) // double clicking on control event #define ON_SHOW (2) // showing control event #define ON_HIDE (3) // hiding control event #define ON_CHANGE (4) // changing control event #define ON_START_EDIT (5) // start of editing event #define ON_END_EDIT (6) // end of editing event #define ON_SCROLL_INC (7) // increment of scrollbar event #define ON_SCROLL_DEC (8) // decrement of scrollbar event #define ON_MOUSE_FOCUS_SET (9) // the "mouse cursor entered the control" event #define ON_MOUSE_FOCUS_KILL (10) // the "mouse cursor exited the control" event #define ON_DRAG_START (11) // the "control dragging start" event #define ON_DRAG_PROCESS (12) // the "control is being dragged" event #define ON_DRAG_END (13) // the "control dragging end" event #define ON_BRING_TO_TOP (14) // the "mouse events priority increase" event #define ON_APP_CLOSE (100) // "closing the application" event //+------------------------------------------------------------------+
Использование событий на панели
В нашем конкретном случае нам не нужно использовать все доступные события, лучше сосредоточиться на трех ключевых:
- ON_CLICK — это событие срабатывает при клике на элемент управления, например, на кнопку;
- ON_CHANGE — это событие используется при изменении значения элемента управления, например, в случае с ComboBox;
- ON_END_EDIT — это событие выполняется, когда пользователь завершает редактирование поля, например, элемента Edit.
Пример реализации события в OnChartEvent
События, определенные в Defines.mqh, присваиваются параметру ID внутри функции OnChartEvent. Эта функция отвечает за обработку событий, происходящих на графике или панели. ID комбинируется со значением CHARTEVENT_CUSTOM для создания уникального идентификатора для каждого события.
Например, событие ON_CLICK будет использоваться следующим образом:
if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_lote.Id()) { // Acción a ejecutar cuando el botón de obtener lote es presionado // Aquí se llamaría a la función correspondiente para gestionar la acción del botón }
Создание функции OnEvent
1. Объявление функции
bool CRiskManagementPanel::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
Функция OnEvent — это обработчик событий, который принимает несколько параметров:
- id — идентификатор произошедшего события;
- lparam — дополнительный параметр, который обычно содержит информацию, характерную для какого-то конкретного элемента управления или компонента;.
- dparam — параметр типа double, используемый для передачи числовых значений;
- sparam — параметр типа string, используемый для передачи с строковых данных.
Целью этой функции является обработка событий интерфейса, таких как изменения в текстовых полях или клики по кнопкам.
2. Проверка изменения в ComboBox, отвечающего за ордер
Элементы ComboBox дают возможность пользователю выбрать значение из раскрывающегося списка. На этом этапе мы проверяем, было ли изменено пользователем значение в ComboBox.
Для ComboBox, отвечающего за лот:
if(id == ON_CHANGE + CHARTEVENT_CUSTOM && lparam == m_combobox_order_type_get_lot.Id()) { OnChangeComBoxOrderTypeGetLote(); }
Для ComboBox, отвечающего за стоп-лосс (SL):
if(id == ON_CHANGE + CHARTEVENT_CUSTOM && lparam == m_combobox_order_type_get_sl.Id()) { OnChangeComBoxOrderTypeGetSL(); }
- ON_CHANGE + CHARTEVENT_CUSTOM — событие, указывающее на то, что значение элемента управления изменилось.
- В условии "lparam == m_combobox_order_type_get_lot.Id()" проверяется, что событие соответствует нужному элементу ComboBox с использованием его ID.
В этих условиях выполняются функции OnChangeComBoxOrderTypeGetLote и OnChangeComBoxOrderTypeGetSL, которые отвечают за изменение внутренней переменной ENUM_ORDER_TYPE, которая представляет собой тип выбранного ордера.
3. Edits
Поля ввода (edits) дают возможность пользователю вводить значения вручную. Здесь проверяются и обновляются переменные, связанные с каждым редактируемым текстовым полем.
Для поля "Риск на сделку":
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_risk_per_operation.Id()) { this.risk_per_operation = StringToDouble(m_edit_risk_per_operation.Text()); this.risk_per_operation = NormalizeDouble((this.risk_per_operation / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE), 2); Print("Edit Risk Per Operation: ", this.risk_per_operation); }
Для поля "SL":
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_sl.Id()) { this.sl = StringToInteger(m_edit_sl.Text()); Print("Edit SL: ", this.sl); }
И для других полей, таких как "Deviation" и "Stop Limit":
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_deviation.Id()) { this.deviation = StringToDouble(m_edit_deviation.Text()); Print("Edit Deviation: ", this.deviation); } if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_stop_limit.Id()) { this.stop_limit = StringToDouble(m_edit_stop_limit.Text()); Print("Edit Stop Limit: ", this.stop_limit); }
- ON_END_EDIT + CHARTEVENT_CUSTOM — это событие срабатывает, когда пользователь завершает редактирование поля.
- Каждый оператор if проверяет, совпадает ли lparam с ID соответствующего элемента управления (например, edit), и если это так, соответствующая внутренняя переменная обновляется новым значением, введенным пользователем. Значения преобразуются в соответствующие типы (double, int и т.д.) и нормализуются при необходимости.
4. Получение идеального лота и стоп-лосса
Здесь мы проверяем, кликнул ли пользователь на кнопки для получения идеального лота или идеального стоп-лосса. В зависимости от того, какая кнопка была нажата, производятся соответствующие вычисления.
Для кнопки "Get Lot":
if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_lote.Id()) { Print("Risk Per operation: ", this.risk_per_operation); Print("SL in points: ", this.sl); Print("Order type get lot: ", EnumToString(this.order_type_get_lot)); double new_lot; double new_risk_per_operation; GetIdealLot(new_lot, GetMaxLote(this.order_type_get_lot), this.risk_per_operation, new_risk_per_operation, this.sl); PrintFormat("Loss in case the following operation fails, with the parameters: lot %.2f and stop loss of %i points will be %.2f ", new_lot, this.sl, new_risk_per_operation); EditLabelResultLote(DoubleToString(new_lot, 2)); m_button_get_lote.Pressed(false); }
Для кнопки "Get SL":
if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_sl.Id()) { Print("Risk Per operation: ", this.risk_per_operation); Print("Order type get sl: ", EnumToString(this.order_type_get_lot)); double new_lot; long new_sl = CalculateSL(this.order_type_get_sl, this.risk_per_operation, new_lot, this.deviation, this.stop_limit); PrintFormat("For the risk per operation %.2f the chosen lot is %.2f and the ideal stop loss in points is %i", this.risk_per_operation, new_lot, new_sl); EditLabelResultSL(IntegerToString(new_sl)); m_button_get_sl.Pressed(false); }
- ON_CLICK + CHARTEVENT_CUSTOM — проверяется, что событие клика соответствует определенным кнопкам (m_button_get_lote или m_button_get_sl).
- Выполняются вычисления с использованием функций GetIdealLot и CalculateSL для определения идеального лота и идеального стоп-лосса.
- Результаты отображаются в метках панели (EditLabelResultLote и EditLabelResultSL).
- В завершение, кнопка деактивируется путем установки ее в состояние false (m_button_get_lote. Pressed(false)).
5. Возврат события OnChartEvent
Наконец, после обработки событий, функция возвращает событие базовому классу CAppDialog, чтобы базовая функция могла также обработать любые другие события, которые могли произойти:
return(CAppDialog::OnEvent(id, lparam, dparam, sparam));
Этот шаг важен для того, чтобы гарантировать, что другие важные события, которые не обрабатываются напрямую в OnEvent, не будут потеряны.
Инициализация панели: использование события OnInit и общая настройка
В этом разделе мы настроим панель управления рисками с помощью пользовательского класса CRiskManagementPanel. Эта панель будет отвечать за управление расчетами лота, стоп-лосса и другими важными функциями для трейдера, представленными через графический интерфейс. Далее мы рассмотрим, как объявить, инициализировать и управлять этой панелью в программе.
1. Объявление глобального объекта
Сначала мы объявляем объект класса CRiskManagementPanel в глобальной области программы. Это делает панель доступной из различных методов кода, таких как OnInit, OnDeinit и OnChartEvent.
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ CRiskManagementPanel risk_panel; // Declare the panel object globally
2. Настройка панели в событии OnInit
В событии OnInit мы настраиваем панель с помощью метода Create. Этот метод инициализирует графическое окно панели и определяет его положение и размер. Если создание панели не удалось, программа возвращает статус INIT_FAILED, указывающий на ошибку инициализации. Затем мы запускаем панель с помощью метода run, который запускает графический интерфейс для взаимодействия с пользователем.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Create the risk management panel if(!risk_panel.Create(0, "Test Risk Management", 0, 40, 40, 380, 380)) return(INIT_FAILED); // If panel creation fails, return an error code //--- Run the panel risk_panel.Run(); // Start the graphical interface //--- Return success return(INIT_SUCCEEDED); }
- 0 (chart_id) — указывает, что панель будет создана в главном окне графика.
- "Test Risk Management" (имя) — задает заголовок, который будет отображаться в окне панели.
- 0 (subwin) — указывает подокно, где будет отображаться панель. Поскольку мы создаем панель в боте, ставим 0.
- 40, 40 (координаты) — задают начальное положение панели в пикселях, координаты x(40) и y(40).
- 380, 380 (размеры) — определяют ширину и высоту панели в пикселях.
3. Удаление панели в событии OnDeinit
Когда программа завершается или закрывается, необходимо освободить ресурсы, используемые панелью. Для этого мы используем метод Destroy внутри события OnDeinit. Это гарантирует отсутствие остаточных графических элементов, которые могли бы помешать работе будущих программ.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Destroy the risk management panel risk_panel.Destroy(reason); // Free resources and remove the panel }
4. Обработка событий с помощью OnChartEvent
Метод OnChartEvent является ключевым для перехвата и интерпретации взаимодействий пользователя с панелью, таких как клики по кнопкам, ввод данных в текстовые поля или выбор вариантов в раскрывающихся списках. Эти события обрабатываются методом ChartEvent класса CRiskManagementPanel.
//+------------------------------------------------------------------+ //| Expert chart event function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // Event ID const long& lparam, // Event parameter of type long const double& dparam, // Event parameter of type double const string& sparam) // Event parameter of type string { //--- Pass the chart event to the risk panel for processing risk_panel.ChartEvent(id, lparam, dparam, sparam); }
Тестирование и валидация панели
Теперь советника с именем, которое ему было присвоено, мы будем запускать на одном символе; я буду запускать его на символе XAUUSD, то есть на золоте. График будет выглядеть примерно так:
Чтобы все было более прозрачным и можно было увидеть возможные ошибки, баланс моего счета выглядит следующим образом:
Как видим, в данный момент у меня нет ни одной открытой позиции.
Сейчас я покажу, как вы можете это использовать:
1. Нужно заполнить поле "Risk Per Operation" процентным значением (оно будет применено к текущему балансу). На следующем изображении я выберу 3% от своего счета.
Примечание: при желании можно изменить поля ввода (edits) для отклонения (deviation) и предела стопа (stop limit).
2. Выберите, что вы хотите рассчитать. Для начала я воспользуюсь функцией получения идеального лота для своей сделки на основе стоп-лосса в пунктах символа, риске на сделку и типе ордера, выбрав стоп-лосс в 500 пунктов и ордер на покупку (ORDER_TYPE_BUY).
Нажимаем левой кнопкой мыши на кнопку "Save" в строке "SL Point", и вот результат:
Результатом стал лот 0,03, как можно увидеть на панели, кроме того, это появилось и в области сообщений в разделе экспертов:
В сообщении содержится информация о максимальном размере лота, который составит 0,45, риске на сделку, а также о том, что если сделка закроется по рассчитанным лоту и стоп-лоссу, убыток составит 15.45 USD.
3. Теперь протестируем метод расчета лота на основе риска на сделку: кликаем левой кнопкой мыши на кнопку "get" (она находится в той же строке, что и метка "Get ideal sl"):
В результате мы получили стоп-лосс в 1856 пунктов, а также:
Размер лота 0,01, как идеальный для риска в 3,0%.
Заключение
В этой статье мы изучили реализацию "калькулятора" лота и стоп-лосса — чрезвычайно полезного инструмента для любого трейдера. Кроме того, мы рассмотрели, как использовать классы и библиотеки, предлагаемые MQL5, для создания пользовательских панелей. Также мы углубились в структуру классов в MQL5, включая наследование и использование таких объектов, как поля ввода (edits), раскрывающиеся списки (comboboxes) и кнопки (buttons), что позволяет нам создавать динамичные и функциональные графические интерфейсы.
Такой подход не только оптимизирует рабочий процесс, но и открывает целый мир возможностей для разработки пользовательских инструментов в MQL5. В следующей части серии мы начнем работать над основным классом, объединяя концепции, представленные в этой статье. Надеюсь, это руководство было полезным и мотивирует вас продолжать изучать и создавать продвинутые графические интерфейсы для улучшения своего трейдингового опыта.
Все файлы, созданные и использованные в этой статье, представлены в следующей таблице:Имя файла | Описание |
---|---|
"Risk_Management_Panel.mq5" | Файл экспертного советника, содержащий панель управления рисками, разработанную в этой статье |
"Risk_Management.mqh" | Включаемый файл, который определяет функции для расчета размера лота, стоп-лосса и другие функции управления рисками |
Перевод с испанского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/es/articles/16985
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.




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