Создание торговой панели администратора на MQL5 (Часть IX): Организация кода (II): Модуляризация
Содержание
- Введение
- Обзор обсуждения
- Модуляризация
- Реализация кода
- Новая торговая панель администратора
- Тестирование
- Заключение
Введение
Статья знаменует собой прорыв в создании обслуживаемой панели администратора (Admin Panel) в виде советника. Представленная в предыдущей статье организация кода значительно улучшила наш основной код, и сегодня мы делаем шаг вперед, модулируя ключевые компоненты во внешних файлах. Такой подход гарантирует, что будущие обновления будут направлены на улучшение отдельных компонентов, не нарушая работу других частей кода.
Наглядной демонстрацией пользы такого подхода стал случай, когда мне потребовалось доработать панель коммуникации. Прокрутка большой монолитной базы кодов в поисках нужного раздела оказалась утомительной. Разбивая код на структурированные модули, мы упрощаем навигацию, делая разработку и сопровождение значительно более эффективными.
Я черпал вдохновение из хорошо структурированных проектов, которые являются примерами передового опыта в организации кода. Сегодня мы реализуем модуляризацию путем введения пользовательских классов для основных функций, определяющих нашу программу. Ниже мы составили таблицу полного списка потенциальных модулей, которые мы планируем разработать.
| Файл модуля | Описание |
|---|---|
| AdminHomeDialog.mqh | Объявляет центральный раздел торговой панели администратора, предоставляющий доступ к другим утилитам программы. |
| Aunthentication.mqh | Управляет аутентификацией пользователя, включая проверку пароля и двухфакторную аутентификацию. |
| ThemeManager.mqh | Отвечает за управление внешним видом и стилем панели. |
| Telegram.mqh | Содержит функции и классы для взаимодействия с Telegram, обычно для отправки сообщений, уведомлений. |
| CommunicationsDialog.mqh | Отвечает за обработку пользовательского интерфейса (UI) и взаимодействия, связанного с функциями коммуникации в панели администратора |
| AnalyticsDialog.mqh | Для отображения и управления аналитическими данными, такими как торговая статистика, показатели производительности или графики в диалоговой панели. |
| TradeManagementDialog.mqh | Позволяет создать пользовательский интерфейс для торговых задач, где пользователи смогут эффективно совершать и управлять сделками. |
После успешного создания этих файлов их можно включить в основной код.
#include <Telegram.mqh> #include <Authentication.mqh> #include <AdminHomeDialog.mqh> #include <AnalyticsDialog.mqh> #include <TradeManagementDialog.mqh> #include <CommunicationDialog.mqh>
Все объявления компонентов панели будут помещены во включаемые файлы, в то время как основной код будет в первую очередь содержать определения. Поскольку определения, как правило, меньше объявлений, такой подход сохраняет основную программу чистой и не перегруженной, улучшая ее читаемость и удобство обслуживания.
Я уверен, вы уже можете себе представить, как развивается наш проект с этими нововведениями. В следующем разделе мы дадим подробное объяснение модуляризации, а затем ее реализации в данном проекте.

Связь между основным кодом и заголовочным файлом
Обзор обсуждения
После краткого обзора мы теперь более подробно рассмотрим модуляризацию, прежде чем погрузиться в разработку и реализацию наших компонентов кода. Каждый модуль будет подробно объяснен с подробным описанием функциональности каждой строки кода.
Наконец, мы интегрируем все модули в новую основную базу кодов панели, по сути перестроив ее с нуля с улучшенной структурой и эффективностью.
К концу этого обсуждения мы разработаем, интегрируем и протестируем следующие файлы:
- AdminHomeDialog.mqh
- Authentication.mqh
- Telegram.mqh
Модуляризация
В MQL5-программировании модуляризация относится к практике разбиения программы на более мелкие, независимые и повторно используемые части, в основном посредством использования классов, функций и включаемых файлов. Такой подход позволяет разработчикам инкапсулировать определенные функции в модули или классы, например, создавать компоненты пользовательского интерфейса или торговую логику, которые могут быть включены или реализованы по мере необходимости в различных частях приложения или даже в нескольких приложениях. Благодаря этому код становится более управляемым, простым в обслуживании и менее подверженным ошибкам, поскольку изменения в одном модуле не обязательно влияют на другие, что способствует повторному использованию кода, улучшает читаемость и облегчает совместную разработку в среде MetaTrader 5.
В этом контексте мы уже обрисовали подкомпоненты нашей новой программы во введении выше. Кроме того, существуют и другие ресурсы для дальнейшего изучения этой темы, и в разных статьях я встречал различные подходы к применению модуляризации.
На следующих этапах я подробно проведу вас через процесс разработки каждого модуля, обеспечив четкое понимание их реализации и интеграции.
Реализация кода
Сейчас самое время применить наши знания MQL5 для разработки ключевых компонентов нашего советника Trading Administrator Panel. Хорошая новость заключается в том, что эти файлы легко адаптируются и интегрируются в ваши собственные проекты.
Основная структура заголовочного файла в MQL5:
Заголовочный файл в MQL5, обычно с расширением .mqh, служит местом для определения классов, констант, перечислений и прототипов функций, которые могут быть включены в другие MQL5-скрипты или советники. Вот типичная структура, основанная на другом встроенном коде заголовочного файла:
- Метаданные файла: информация об авторских правах, ссылки и контроль версий.
- Операторы включения: другие заголовочные файлы или библиотеки, от которых зависит текущий заголовок.
- Определения/константы: определяют макросы или константы, используемые внутри класса или другими частями кода для получения согласованных значений.
- Объявление класса: объявляет класс с его наследованием (если таковое имеется), закрытыми членами, открытыми методами и защищенными методами.
- Сопоставление событий: использует макросы для определения того, как события сопоставляются с функциями-членами для программирования на основе событий.
- Реализации методов: Хотя это не является строго обязательным для всех заголовочных файлов, в данном случае реализации методов включены, что характерно для небольших классов, где инкапсуляция не имеет решающего значения. По соображениям производительности в MQL5 включение реализации может сократить расходы на вызов функций.
- Конструкторы и деструкторы: являются частью определения класса и указывают, как создаются и уничтожаются объекты класса.
На основе вышеизложенного, вот пример шаблона кода:
// Meta Data here on top. #include // Include other necessary libraries or headers #include //+------------------------------------------------------------------+ //| Defines | //+------------------------------------------------------------------+ #define CONSTANT_NAME1 (value1) // Constants or macro definitions #define CONSTANT_NAME2 (value2) //+------------------------------------------------------------------+ //| Class CClass | //| Usage: Description of class purpose | //+------------------------------------------------------------------+ class CClass : public CParentClass // Inherits from another class if needed { private: // Private member variables CSomeControl m_control; // Example control member public: CClass(void); // Constructor ~CClass(void); // Destructor virtual bool Create(/* parameters */); // Virtual method for polymorphism virtual bool OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); protected: // Protected methods or members bool CreateSomeControl(void); void SomeEventHandler(void); }; //+------------------------------------------------------------------+ //| Event Handling | //+------------------------------------------------------------------+ EVENT_MAP_BEGIN(CClass) ON_EVENT(SOME_EVENT,m_control,SomeEventHandler) EVENT_MAP_END(CParentClass) // Constructor implementation CClass::CClass(void) { } // Destructor implementation CClass::~CClass(void) { } // Method implementations if included in the header bool CClass::Create(/* parameters */) { // Implementation of create method } // Event handler examples void CClass::SomeEventHandler(void) { // Handle the event } //+------------------------------------------------------------------+
Чтобы создать заголовочные файлы в MetaEditor, откройте новый файл, нажав Ctrl + N или вручную перемещаясь по меню. Во всплывающем окне выберите "Включаемый файл (*.mqh)" и приступайте к редактированию. По умолчанию сгенерированный шаблон включает в себя некоторые комментарии в качестве примечаний.
Смотрите изображение ниже:
Создание нового заголовочного файла в MetaEditor
Ниже приведен шаблон файла заголовка по умолчанию, который включает некоторые примечания с комментариями.
//+------------------------------------------------------------------+ //| Telegram.mqh | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" //+------------------------------------------------------------------+ //| defines | //+------------------------------------------------------------------+ // #define MacrosHello "Hello, world!" // #define MacrosYear 2010 //+------------------------------------------------------------------+ //| DLL imports | //+------------------------------------------------------------------+ // #import "user32.dll" // int SendMessageA(int hWnd,int Msg,int wParam,int lParam); // #import "my_expert.dll" // int ExpertRecalculate(int wParam,int lParam); // #import //+------------------------------------------------------------------+ //| EX5 imports | //+------------------------------------------------------------------+ // #import "stdlib.ex5" // string ErrorDescription(int error_code); // #import //+------------------------------------------------------------------+
Заголовочный файл домашней панели администратора (Admin Home)
В этом разделе мы разрабатываем класс CAdminHomeDialog, который служит основным интерфейсом для панели администратора в нашей программе MQL5. Он интегрирует необходимые файлы заголовков для диалоговых элементов управления и кнопок, используя предопределенные константы для поддержания согласованных размеров панелей и расстояния между ними.
//+------------------------------------------------------------------+ //| AdminHomeDialog.mqh | //+------------------------------------------------------------------+ #include <Controls\Dialog.mqh> #include <Controls\Button.mqh> //+------------------------------------------------------------------+ //| Defines | //+------------------------------------------------------------------+ #define ADMIN_PANEL_WIDTH (335) #define ADMIN_PANEL_HEIGHT (350) #define INDENT_LEFT (11) #define INDENT_TOP (11) #define INDENT_RIGHT (11) #define INDENT_BOTTOM (11) #define CONTROLS_GAP_X (5) #define CONTROLS_GAP_Y (5) #define BUTTON_WIDTH (250) #define BUTTON_HEIGHT (40)
Класс CAdminHomeDialog расширяет CAppDialog и включает в себя четыре ключевые кнопки - m_tradeMgmtButton, m_commButton, m_analyticsButton и m_showAllButton, - которые обеспечивают удобную навигацию по различным разделам панели администратора. Структура класса остается упрощенной, с минимальным конструктором и деструктором, в то время как метод Create обеспечивает правильную инициализацию всех кнопок для удобства пользователя.
//+------------------------------------------------------------------+ //| CAdminHomeDialog class | //+------------------------------------------------------------------+ class CAdminHomeDialog : public CAppDialog { private: CButton m_tradeMgmtButton; CButton m_commButton; CButton m_analyticsButton; CButton m_showAllButton; public: CAdminHomeDialog(void) {} ~CAdminHomeDialog(void) {} virtual bool Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2); virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam); }; //+------------------------------------------------------------------+ //| Create | //+------------------------------------------------------------------+ bool CAdminHomeDialog::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(!CreateTradeMgmtButton()) return false; if(!CreateCommButton()) return false; if(!CreateAnalyticsButton()) return false; if(!CreateShowAllButton()) return false; return true; }
Взаимодействие с пользователем осуществляется в рамках метода OnEvent, в котором нажатия кнопок вызывают отладочные сообщения и соответствующие обработчики событий: OnClickTradeManagement, OnClickCommunications, OnClickAnalytics и OnClickShowAll. В настоящее время эти обработчики регистрируют взаимодействия, но будут расширены по мере улучшения функциональности.
//+------------------------------------------------------------------+ //| Event Handling (Enhanced Debugging) | //+------------------------------------------------------------------+ bool CAdminHomeDialog::OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id == CHARTEVENT_OBJECT_CLICK) { Print("Clicked object: ", sparam); // Debug which object was clicked if(sparam == m_tradeMgmtButton.Name()) { Print("Trade Management button detected"); OnClickTradeManagement(); return true; } else if(sparam == m_commButton.Name()) { Print("Communications button detected"); OnClickCommunications(); return true; } else if(sparam == m_analyticsButton.Name()) { Print("Analytics button detected"); OnClickAnalytics(); return true; } else if(sparam == m_showAllButton.Name()) { Print("Show All button detected"); OnClickShowAll(); return true; } } return CAppDialog::OnEvent(id, lparam, dparam, sparam); } //+------------------------------------------------------------------+ //| Button Click Handlers | //+------------------------------------------------------------------+ void CAdminHomeDialog::OnClickTradeManagement() { Print("Trade Management Panel clicked"); } void CAdminHomeDialog::OnClickCommunications() { Print("Communications Panel clicked"); } void CAdminHomeDialog::OnClickAnalytics() { Print("Analytics Panel clicked"); } void CAdminHomeDialog::OnClickShowAll() { Print("Show All clicked"); }
Методы создания кнопок — CreateTradeMgmtButton, CreateCommButton, CreateAnalyticsButton и CreateShowAllButton — динамически генерируют кнопки с уникальными идентификаторами, точным позиционированием и четко определенными надписями. Кнопка Show All (показать все) даже включает в себя эмодзи для улучшения пользовательского интерфейса. По мере продолжения разработки будут вводиться дополнительные улучшения и усовершенствования для оптимизации производительности и удобства использования.
//+------------------------------------------------------------------+ //| Control Creation Methods | //+------------------------------------------------------------------+ bool CAdminHomeDialog::CreateTradeMgmtButton() { int x = INDENT_LEFT; int y = INDENT_TOP; return m_tradeMgmtButton.Create(m_chart_id, m_name+"_TradeBtn", m_subwin, x, y, x+BUTTON_WIDTH, y+BUTTON_HEIGHT) && m_tradeMgmtButton.Text("Trade Management Panel") && Add(m_tradeMgmtButton); } bool CAdminHomeDialog::CreateCommButton() { int x = INDENT_LEFT; int y = INDENT_TOP + BUTTON_HEIGHT + CONTROLS_GAP_Y; return m_commButton.Create(m_chart_id, m_name+"_CommBtn", m_subwin, x, y, x+BUTTON_WIDTH, y+BUTTON_HEIGHT) && m_commButton.Text("Communications Panel") && Add(m_commButton); } bool CAdminHomeDialog::CreateAnalyticsButton() { int x = INDENT_LEFT; int y = INDENT_TOP + (BUTTON_HEIGHT + CONTROLS_GAP_Y) * 2; return m_analyticsButton.Create(m_chart_id, m_name+"_AnalyticsBtn", m_subwin, x, y, x+BUTTON_WIDTH, y+BUTTON_HEIGHT) && m_analyticsButton.Text("Analytics Panel") && Add(m_analyticsButton); } bool CAdminHomeDialog::CreateShowAllButton() { int x = INDENT_LEFT; int y = INDENT_TOP + (BUTTON_HEIGHT + CONTROLS_GAP_Y) * 3; return m_showAllButton.Create(m_chart_id, m_name+"_ShowAllBtn", m_subwin, x, y, x+BUTTON_WIDTH, y+BUTTON_HEIGHT) && m_showAllButton.Text("Show All 💥") && Add(m_showAllButton); }
Реализация AdminHomeDialog.mqh в основной программе:
1. Добавляем с помощью #include"AdminHomeDialog.mqh"
#include "AdminHomeDialog.mqh"
Добавление AdminHomeDialog.mqh делает класс CAdminHomeDialog доступным в основном скрипте. Без этого включения компилятор не распознал бы CAdminHomeDialog, что приводит к ошибкам. Такой модульный подход позволяет сохранить основной сценарий чистым, сохраняя при этом реализацию диалога в отдельном файле для лучшей организации и удобства обслуживания.
2. Объявляем как CAdminHomeDialog ExtDialog;
CAdminHomeDialog ExtDialog;
Объявление ExtDialog как экземпляра CAdminHomeDialog позволяет скрипту ссылаться на домашнюю панель администратора и управлять ею на протяжении всей программы. Этот объект управляет созданием, видимостью и событиями панели, делая ее доступной для различных функций.
3. Создаем с помощью ExtDialog внутри CreateHiddenPanels()
bool CreateHiddenPanels() { bool success = ExtDialog.Create(0, "Admin Home", 0, MAIN_DIALOG_X, MAIN_DIALOG_Y, MAIN_DIALOG_X + MAIN_DIALOG_WIDTH, MAIN_DIALOG_Y + MAIN_DIALOG_HEIGHT); if(success) { ExtDialog.Hide(); ChartRedraw(); } return success; }
Метод Create() инициализирует панель с определенными размерами и правильно размещает ее на графике. Расположение внутри CreateHiddenPanels() обеспечивает создание панели только один раз во время инициализации, что позволяет упорядочить процесс настройки и избежать ненужной повторной инициализации.
4. Показываем или скрываем в зависимости от статуса аутентификации в OnChartEvent()
if(authManager.IsAuthenticated()) { if(!ExtDialog.IsVisible()) { ExtDialog.Show(); ChartRedraw(); } ExtDialog.ChartEvent(id, lparam, dparam, sparam); } else { if(ExtDialog.IsVisible()) { ExtDialog.Hide(); } }
Домашняя панель администратора должна быть доступна только после успешной аутентификации. Проверка authManager. IsAuthenticated() не дает неавторизованным пользователям взаимодействовать с панелью. Если аутентификация верна, панель отображается; в противном случае она остается скрытой, что повышает безопасность и обеспечивает контроль доступа.
5. Уничтожаем в OnDeinit() при удалении скрипта
void OnDeinit(const int reason) { ExtDialog.Destroy(reason); }
Когда советник удаляется с графика, вызов ExtDialog. Destroy() обеспечивает тщательную очистку ресурсов, выделяемых для панели. Это предотвращает потенциальные утечки памяти или появление потерянных графических объектов, которые могут помешать выполнению будущих экземпляров скрипта.
Заголовочный файл Telegram
При создании заголовочного файла Telegram, функция Telegram копируется непосредственно в исходный файл заголовка из-за ее простоты и понятности работы. Однако этот подход может отличаться для других файлов, которым требуется более структурированная настройка, включающая классы, методы, конструкторы и деструкторы, как упоминалось ранее. Таким образом, это самый простой заголовочный файл в нашем списке создаваемых элементов. Модулируя функцию, мы сокращаем длину основного кода, и функцию можно легко использовать повторно в других проектах.
//+------------------------------------------------------------------+ //| Telegram.mqh | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" //+------------------------------------------------------------------+ //| Telegram.mqh - Telegram Communication Include File | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Send the message to Telegram | //+------------------------------------------------------------------+ bool SendMessageToTelegram(string message, string chatId, string botToken) { string url = "https://api.telegram.org/bot" + botToken + "/sendMessage"; string jsonMessage = "{\"chat_id\":\"" + chatId + "\", \"text\":\"" + message + "\"}"; char postData[]; ArrayResize(postData, StringToCharArray(jsonMessage, postData) - 1); int timeout = 5000; char result[]; string responseHeaders; int responseCode = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, postData, result, responseHeaders); if (responseCode == 200) { Print("Message sent successfully: ", message); return true; } else { Print("Failed to send message. HTTP code: ", responseCode, " Error code: ", GetLastError()); Print("Response: ", CharArrayToString(result)); return false; } } //+------------------------------------------------------------------+
Реализация заголовка Telegram в основном коде
Это делается просто - в 2 шага:
1. Включаем файл в основной код следующим образом.
#include<Telegram.mqh> Вышеуказанное работает только в том случае, если элементы хранятся в каталоге MQL5/Include, в противном случае имя подпапки должно быть указано следующим образом:
#include <FolderName\Telegram.mqh> // Replace FolderName with actual location name
2. Наконец, нам нужно вызвать функцию в основном коде, когда это необходимо, следующим образом:
SendMessageToTelegram("Your verification code: " + ActiveTwoFactorAuthCode,
TwoFactorAuthChatId, TwoFactorAuthBotToken) Создание файла заголовка аутентификации
В предыдущих частях мы рассматривали эволюцию логики запросов безопасности, которая последовательно использовалась в каждой версии панели администратора. Эту логику можно адаптировать и для других проектов, связанных с панелью, особенно при разработке классифицированного модуля для его функциональности. На данном этапе мы разрабатываем Authentication.mqh, который объединяет всю логику безопасности, использовавшуюся в предыдущей панели администратора. Рассмотрим код и особенности его работы.
//+------------------------------------------------------------------+ //| authentication.mqh | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.0" #property strict // Authentication Dialog Coordinates #define AUTH_DIALOG_X 100 #define AUTH_DIALOG_Y 100 #define AUTH_DIALOG_WIDTH 300 #define AUTH_DIALOG_HEIGHT 200 #define PASS_INPUT_X 20 #define PASS_INPUT_Y 50 #define PASS_INPUT_WIDTH 260 // Wider input field #define PASS_INPUT_HEIGHT 30 #define PASS_LABEL_X 20 #define PASS_LABEL_Y 20 #define PASS_LABEL_WIDTH 200 #define PASS_LABEL_HEIGHT 20 #define FEEDBACK_LABEL_X 20 #define FEEDBACK_LABEL_Y 100 #define FEEDBACK_LABEL_WIDTH 260 #define FEEDBACK_LABEL_HEIGHT 40 // Button spacing adjustments #define LOGIN_BTN_X 20 #define LOGIN_BTN_Y 130 #define LOGIN_BTN_WIDTH 120 #define LOGIN_BTN_HEIGHT 30 #define CANCEL_BTN_X 160 // Added 20px spacing from login button #define CANCEL_BTN_Y 130 #define CANCEL_BTN_WIDTH 120 #define CANCEL_BTN_HEIGHT 30 // Two-Factor Authentication Dialog Coordinates #define TWOFA_DIALOG_X 100 #define TWOFA_DIALOG_Y 100 #define TWOFA_DIALOG_WIDTH 300 #define TWOFA_DIALOG_HEIGHT 200 #define TWOFA_INPUT_X 20 #define TWOFA_INPUT_Y 50 #define TWOFA_INPUT_WIDTH 180 #define TWOFA_INPUT_HEIGHT 30 #define TWOFA_LABEL_X 20 #define TWOFA_LABEL_Y 20 #define TWOFA_LABEL_WIDTH 260 #define TWOFA_LABEL_HEIGHT 20 #define TWOFA_FEEDBACK_X 20 #define TWOFA_FEEDBACK_Y 100 #define TWOFA_FEEDBACK_WIDTH 260 #define TWOFA_FEEDBACK_HEIGHT 40 #define TWOFA_VERIFY_BTN_X 60 #define TWOFA_VERIFY_BTN_Y 130 #define TWOFA_VERIFY_WIDTH 120 #define TWOFA_VERIFY_HEIGHT 30 #define TWOFA_CANCEL_BTN_X 140 #define TWOFA_CANCEL_BTN_Y 130 #define TWOFA_CANCEL_WIDTH 60 #define TWOFA_CANCEL_HEIGHT 30 #include <Controls\Dialog.mqh> #include <Controls\Button.mqh> #include <Controls\Edit.mqh> #include <Controls\Label.mqh> #include <Telegram.mqh> class CAuthenticationManager { private: CDialog m_authDialog; CDialog m_2faDialog; CEdit m_passwordInput; CEdit m_2faCodeInput; CLabel m_passwordLabel; CLabel m_feedbackLabel; CLabel m_2faLabel; CLabel m_2faFeedback; CButton m_loginButton; CButton m_closeAuthButton; CButton m_2faLoginButton; CButton m_close2faButton; string m_password; string m_2faChatId; string m_2faBotToken; int m_failedAttempts; bool m_isAuthenticated; string m_active2faCode; public: CAuthenticationManager(string password, string twoFactorChatId, string twoFactorBotToken) : m_password(password), m_2faChatId(twoFactorChatId), m_2faBotToken(twoFactorBotToken), m_failedAttempts(0), m_isAuthenticated(false), m_active2faCode("") { } ~CAuthenticationManager() { m_authDialog.Destroy(); m_2faDialog.Destroy(); } bool Initialize() { if(!CreateAuthDialog() || !Create2FADialog()) { Print("Authentication initialization failed"); return false; } m_2faDialog.Hide(); // Ensure 2FA dialog starts hidden return true; } bool IsAuthenticated() const { return m_isAuthenticated; } void HandleEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id == CHARTEVENT_OBJECT_CLICK) { if(sparam == "LoginButton") HandleLoginAttempt(); else if(sparam == "2FALoginButton") Handle2FAAttempt(); else if(sparam == "CloseAuthButton") m_authDialog.Hide(); else if(sparam == "Close2FAButton") m_2faDialog.Hide(); } } private: bool CreateAuthDialog() { if(!m_authDialog.Create(0, "Authentication", 0, AUTH_DIALOG_X, AUTH_DIALOG_Y, AUTH_DIALOG_X + AUTH_DIALOG_WIDTH, AUTH_DIALOG_Y + AUTH_DIALOG_HEIGHT)) return false; if(!m_passwordInput.Create(0, "PasswordInput", 0, PASS_INPUT_X, PASS_INPUT_Y, PASS_INPUT_X + PASS_INPUT_WIDTH, PASS_INPUT_Y + PASS_INPUT_HEIGHT) || !m_passwordLabel.Create(0, "PasswordLabel", 0, PASS_LABEL_X, PASS_LABEL_Y, PASS_LABEL_X + PASS_LABEL_WIDTH, PASS_LABEL_Y + PASS_LABEL_HEIGHT) || !m_feedbackLabel.Create(0, "AuthFeedback", 0, FEEDBACK_LABEL_X, FEEDBACK_LABEL_Y, FEEDBACK_LABEL_X + FEEDBACK_LABEL_WIDTH, FEEDBACK_LABEL_Y + FEEDBACK_LABEL_HEIGHT) || !m_loginButton.Create(0, "LoginButton", 0, LOGIN_BTN_X, LOGIN_BTN_Y, LOGIN_BTN_X + LOGIN_BTN_WIDTH, LOGIN_BTN_Y + LOGIN_BTN_HEIGHT) || !m_closeAuthButton.Create(0, "CloseAuthButton", 0, CANCEL_BTN_X, CANCEL_BTN_Y, CANCEL_BTN_X + CANCEL_BTN_WIDTH, CANCEL_BTN_Y + CANCEL_BTN_HEIGHT)) return false; m_passwordLabel.Text("Enter Password:"); m_feedbackLabel.Text(""); m_feedbackLabel.Color(clrRed); m_loginButton.Text("Login"); m_closeAuthButton.Text("Cancel"); m_authDialog.Add(m_passwordInput); m_authDialog.Add(m_passwordLabel); m_authDialog.Add(m_feedbackLabel); m_authDialog.Add(m_loginButton); m_authDialog.Add(m_closeAuthButton); m_authDialog.Show(); return true; } bool Create2FADialog() { if(!m_2faDialog.Create(0, "2FA Verification", 0, TWOFA_DIALOG_X, TWOFA_DIALOG_Y, TWOFA_DIALOG_X + TWOFA_DIALOG_WIDTH, TWOFA_DIALOG_Y + TWOFA_DIALOG_HEIGHT)) return false; if(!m_2faCodeInput.Create(0, "2FAInput", 0, TWOFA_INPUT_X, TWOFA_INPUT_Y, TWOFA_INPUT_X + TWOFA_INPUT_WIDTH, TWOFA_INPUT_Y + TWOFA_INPUT_HEIGHT) || !m_2faLabel.Create(0, "2FALabel", 0, TWOFA_LABEL_X, TWOFA_LABEL_Y, TWOFA_LABEL_X + TWOFA_LABEL_WIDTH, TWOFA_LABEL_Y + TWOFA_LABEL_HEIGHT) || !m_2faFeedback.Create(0, "2FAFeedback", 0, TWOFA_FEEDBACK_X, TWOFA_FEEDBACK_Y, TWOFA_FEEDBACK_X + TWOFA_FEEDBACK_WIDTH, TWOFA_FEEDBACK_Y + TWOFA_FEEDBACK_HEIGHT) || !m_2faLoginButton.Create(0, "2FALoginButton", 0, TWOFA_VERIFY_BTN_X, TWOFA_VERIFY_BTN_Y, TWOFA_VERIFY_BTN_X + TWOFA_VERIFY_WIDTH, TWOFA_VERIFY_BTN_Y + TWOFA_VERIFY_HEIGHT) || !m_close2faButton.Create(0, "Close2FAButton", 0, TWOFA_CANCEL_BTN_X, TWOFA_CANCEL_BTN_Y, TWOFA_CANCEL_BTN_X + TWOFA_CANCEL_WIDTH, TWOFA_CANCEL_BTN_Y + TWOFA_CANCEL_HEIGHT)) return false; m_2faLabel.Text("Enter verification code:"); m_2faFeedback.Text(""); m_2faFeedback.Color(clrRed); m_2faLoginButton.Text("Verify"); m_close2faButton.Text("Cancel"); m_2faDialog.Add(m_2faCodeInput); m_2faDialog.Add(m_2faLabel); m_2faDialog.Add(m_2faFeedback); m_2faDialog.Add(m_2faLoginButton); m_2faDialog.Add(m_close2faButton); return true; } void HandleLoginAttempt() { if(m_passwordInput.Text() == m_password) { m_isAuthenticated = true; m_authDialog.Hide(); m_2faDialog.Hide(); // Ensure both dialogs are hidden } else { if(++m_failedAttempts >= 3) { Generate2FACode(); m_authDialog.Hide(); m_2faDialog.Show(); } else { m_feedbackLabel.Text(StringFormat("Invalid password (%d attempts left)", 3 - m_failedAttempts)); } } } void Handle2FAAttempt() { if(m_2faCodeInput.Text() == m_active2faCode) { m_isAuthenticated = true; m_2faDialog.Hide(); m_authDialog.Hide(); // Hide both dialogs on success } else { m_2faFeedback.Text("Invalid code - please try again"); m_2faCodeInput.Text(""); } } void Generate2FACode() { m_active2faCode = StringFormat("%06d", MathRand() % 1000000); SendMessageToTelegram("Your verification code: " + m_active2faCode, m_2faChatId, m_2faBotToken); } }; //+------------------------------------------------------------------+
Класс CAuthenticationManager в модуле обрабатывает многоэтапный процесс аутентификации пользователя для приложения на базе MQL5. Он управляет как аутентификацией по паролю, так и двухфакторной аутентификацией (2FA) через диалоговый интерфейс. Процесс аутентификации начинается с того, что пользователь вводит пароль в диалоговое окно. Если пароль правильный, доступ предоставляется немедленно. Однако если пароль неверен, система отслеживает неудачные попытки и после трех неверных вводов запускает второй диалог для 2FA. Процесс 2FA включает в себя генерацию шестизначного проверочного кода, который отправляется через Telegram пользователю, который должен ввести правильный код для продолжения.
Модуль использует предопределенные координаты для элементов диалога, чтобы обеспечить единообразие макета, а также включает механизмы обратной связи для информирования пользователей об ошибках или успешной аутентификации. Класс также интегрирует библиотеку Telegram.mqh для обработки отправки кодов 2FA. Он разработан с учетом расширяемости, что позволяет легко изменять параметры пароля и 2FA, а также гарантирует, что оба диалоговых окна будут скрыты после успешной аутентификации. Такая конструкция упрощает работу пользователя, сохраняя при этом надежные функции безопасности.
Новая торговая панель администратора
На данном этапе мы интегрируем все ранее разработанные модули для создания более структурированной и эффективной панели администратора. В этой версии улучшена организация и модульность, что позволяет легко совместно использовать ее компоненты и повторно использовать их в других приложениях внутри терминала.
Новая панель администратора включает в себя AdminHomeDialog.mqh для графического интерфейса и Authentication.mqh для управления аутентификацией. Советник определяет входные параметры для идентификатора чата Telegram и токена бота для проверки 2FA. Во время инициализации (OnInit), он пытается инициализировать аутентификацию и создать скрытую панель (CreateHiddenPanels), вызывая сбой, если какой-либо из процессов завершился неудачей.
Функция OnChartEvent обрабатывает события графика, выполняя аутентификацию перед отображением или скрытием панели AdminHomeDialog на основе статуса аутентификации. В случае аутентификации панель становится видимой, и события пересылаются на нее. В противном случае панель будет скрыта. Функция деинициализации (OnDeinit) обеспечивает корректное уничтожение диалога. Такая конструкция обеспечивает безопасный доступ к главной странице панели администратора, требуя аутентификации перед предоставлением управления ее функциями.
Вот полный код новой программы:
//+------------------------------------------------------------------+ //| New Admin Panel.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.00" // Panel coordinate defines #define MAIN_DIALOG_X 30 #define MAIN_DIALOG_Y 80 #define MAIN_DIALOG_WIDTH 335 #define MAIN_DIALOG_HEIGHT 350 #include "AdminHomeDialog.mqh" #include <Authentication.mqh> // Input parameters for authentication input string TwoFactorChatID = "YOUR_CHAT_ID"; input string TwoFactorBotToken = "YOUR_BOT_TOKEN"; string AuthPassword = "2024"; CAdminHomeDialog ExtDialog; CAuthenticationManager authManager(AuthPassword, TwoFactorChatID, TwoFactorBotToken); //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { if(!authManager.Initialize() || !CreateHiddenPanels()) { Print("Initialization failed"); return INIT_FAILED; } return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ExtDialog.Destroy(reason); } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) { authManager.HandleEvent(id, lparam, dparam, sparam); if(authManager.IsAuthenticated()) { if(!ExtDialog.IsVisible()) { ExtDialog.Show(); ChartRedraw(); } // Handle dialog events only when authenticated ExtDialog.ChartEvent(id, lparam, dparam, sparam); } else { if(ExtDialog.IsVisible()) { ExtDialog.Hide(); } } } //+------------------------------------------------------------------+ //| Create hidden panels | //+------------------------------------------------------------------+ bool CreateHiddenPanels() { bool success = ExtDialog.Create(0, "Admin Home", 0, MAIN_DIALOG_X, MAIN_DIALOG_Y, MAIN_DIALOG_X + MAIN_DIALOG_WIDTH, MAIN_DIALOG_Y + MAIN_DIALOG_HEIGHT); if(success) { ExtDialog.Hide(); ChartRedraw(); } return success; }
Тестирование
Рассмотрим результаты тестирования после разработки отдельных компонентов и их логической интеграции в новую панель администратора. Мы успешно скомпилировали и запустили программу на графике, как показано ниже.

Тестирование новой панели администратора на графике EURUSD
Заключение
Я уверен, вы оцените, как мы оптимизировали организацию нашей панели администратора. Наследовав CAppDialog, мы значительно улучшили отзывчивость приложения, впервые позволив свободно перетаскивать панель по графику. Кроме того, кнопка сворачивания, унаследованная от базового класса, позволяет нам сворачивать приложение, обеспечивая беспрепятственный просмотр графика, при этом приложение продолжает работать в фоновом режиме. Панель можно развернуть в любой момент при необходимости, что обеспечивает удобство использования.
Наше внимание к читаемости, масштабируемости и модульности побудило нас разрабатывать каждый компонент отдельно. Такой подход повышает возможность повторного использования кода, позволяя нам интегрировать эти модули в другие программы, просто включая соответствующие файлы и вызывая необходимые методы. В дальнейшем мы планируем завершить работу над оставшимися компонентами, которые сделают новую панель администратора еще более мощной. К ним относятся CommunicationsDialog.mqh, TradeManagementDialog.mqh и AnalyticsDialog.mqh, каждый из которых разработан для повторного использования в различных приложениях.
В качестве отправной точки вы можете попробовать реализовать Telegram.mqh в своей собственной программе, чтобы увидеть, насколько легко он интегрируется. Я прикрепил все необходимые файлы ниже. Вы можете тестировать и модифицировать их.
| Файл | Описание |
|---|---|
| New Admin Panel .mqh | В последней версии панели администратора реализована концепция модульности. |
| Telegram.mqh | Для передачи сообщений и уведомлений через Telegram. |
| Authentication.mqh | Этот файл содержит все декларации безопасности и полную логику. |
| AdminHomeDialog.mqh | Для создания диалогового окна администратора. Он содержит все объявления координат. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/16562
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Нейросети в трейдинге: Агрегация движения по времени (Окончание)
Разработка динамического советника на нескольких парах (Часть 2): Диверсификация и оптимизация портфеля
От новичка до эксперта: Индикатор Market Periods Synchronizer
Команда ИИ-агентов с ротацией по прибыли: Эволюция живой торговой системы в MQL5
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования