Creación de un Panel de administración de operaciones en MQL5 (Parte IX): Organización del código (II): Modularización
Contenido:
- Introducción
- Descripción general de la discusión
- Modularización
- Implementación de código
- El nuevo panel de administración de trading
- Pruebas
- Conclusión
Introducción
Esta discusión marca un avance en la creación de un Asesor Experto del Panel de Administración que se pueda mantener. La organización del código presentada en el artículo anterior mejoró significativamente nuestro código principal y hoy lo llevamos un paso más allá al modularizar los componentes clave en archivos externos. Este enfoque garantiza que las actualizaciones futuras se centren en mejorar componentes individuales sin interrumpir otras partes del código.
Un claro ejemplo de por qué esto es beneficioso ocurrió cuando necesité refinar el Panel de Comunicación. Desplazarse a través de una base de código grande y monolítica para localizar la sección relevante fue abrumador. Al dividir el código en módulos estructurados, simplificamos la navegación, haciendo que el desarrollo y el mantenimiento sean mucho más eficientes.
Nuestra inspiración proviene de proyectos bien estructurados que ejemplifican las mejores prácticas en la organización del código. Hoy implementaremos la modularización introduciendo clases personalizadas para las funcionalidades esenciales que definen nuestro programa. A continuación, hemos tabulado la lista completa de posibles módulos que planeamos desarrollar.
| Archivo de módulo | Descripción |
|---|---|
| AdminHomeDialog.mqh | Esto declara la sección central del Panel de Administrador Comercial, brindando acceso a otras utilidades dentro del programa. |
| Aunthentication.mqh | Este módulo gestiona la autenticación de usuarios, incluida la verificación de contraseña y la autenticación de dos factores. |
| ThemeManager.mqh | Responsable de gestionar la apariencia y el estilo de su panel de administración. |
| Telegram.mqh | Contiene funciones y clases para interactuar con Telegram, generalmente para enviar mensajes, notificaciones. |
| CommunicationsDialog.mqh | Este será responsable de manejar la interfaz de usuario (UI) y las interacciones relacionadas con las funciones de comunicación dentro de su Panel de administración. |
| AnalyticsDialog.mqh | Para mostrar y administrar datos analíticos, como estadísticas comerciales, métricas de rendimiento o gráficos visuales dentro de un panel de diálogo. |
| TradeManagementDialog.mqh | Esto manejará la creación de la interfaz de usuario de tareas relacionadas con el comercio, donde los usuarios pueden ejecutar y administrar transacciones de manera eficiente. |
Después de una creación exitosa de estos archivos, se pueden incluir en el código principal.
#include <Telegram.mqh> #include <Authentication.mqh> #include <AdminHomeDialog.mqh> #include <AnalyticsDialog.mqh> #include <TradeManagementDialog.mqh> #include <CommunicationDialog.mqh>
Todas las declaraciones de los componentes del panel se colocarán en los archivos de inclusión, mientras que el código principal contendrá principalmente definiciones. Dado que las definiciones son generalmente más pequeñas que las declaraciones, este enfoque mantiene el programa principal limpio y ordenado, mejorando la legibilidad y la facilidad de mantenimiento.
Estoy seguro de que ya podéis imaginar cómo evoluciona nuestro proyecto con estas innovaciones. En la siguiente sección, proporcionaremos una explicación detallada de la modularización, seguida de su implementación en este proyecto.

Relación entre el código principal y el archivo de encabezado
Descripción general de la discusión
Con una breve descripción general de la introducción anterior, ahora exploraremos la modularización con más detalle antes de sumergirnos en el desarrollo y la implementación de nuestros componentes de código. Cada módulo se explicará detalladamente, con un desglose de la funcionalidad de cada línea de código.
Por último, integraremos todos los módulos en una nueva base de código principal para el Panel de administrador comercial, reconstruyéndolo esencialmente desde cero con una estructura y eficiencia mejoradas.
Al final de esta discusión, habremos desarrollado, integrado y probado los siguientes archivos:
- AdminHomeDialog.mqh
- Authentication.mqh
- Telegram.mqh
Modularización
En la programación MQL5, la modularización se refiere a la práctica de dividir un programa en partes más pequeñas, independientes y reutilizables, principalmente mediante el uso de clases, funciones y archivos de inclusión. Este enfoque permite a los desarrolladores encapsular funcionalidades específicas en módulos o clases, como la creación de componentes de UI o lógica comercial, que pueden incluirse o instanciarse según sea necesario en diferentes partes de una aplicación o incluso en múltiples aplicaciones. Al hacerlo, el código se vuelve más manejable, más fácil de mantener y menos propenso a errores, ya que los cambios en un módulo no necesariamente afectan a los demás, lo que promueve la reutilización del código, mejora la legibilidad y facilita el desarrollo colaborativo en el entorno MetaTrader 5.
En este contexto, ya hemos esbozado los subcomponentes de nuestro nuevo programa en la introducción anterior. Además, hay otros recursos disponibles para seguir leyendo sobre este tema, y he encontrado varios enfoques para aplicar la modularización en diferentes artículos.
En los siguientes pasos, lo guiaré a través del desarrollo de cada módulo en detalle, asegurando una comprensión clara de su implementación e integración.
Implementación de código
Ahora es el momento de aplicar nuestro conocimiento sobre MQL5 para desarrollar los componentes clave de nuestro Panel de Administrador Comercial EA. La buena noticia es que estos archivos están diseñados para adaptarse e integrarse fácilmente en sus propios proyectos.
Estructura principal de un archivo de encabezado en MQL5:
Un archivo de encabezado en MQL5, normalmente con el .mqh extensión, sirve como un lugar para definir clases, constantes, enumeraciones y prototipos de funciones que se pueden incluir en otros scripts MQL5 o asesores expertos. Aquí está la estructura típica basada en otro código de archivo de encabezado incorporado:
- Metadatos del archivo: incluye información de derechos de autor, enlaces y control de versiones.
- Declaraciones de inclusión: enumera otros archivos de encabezado o bibliotecas de los que depende el encabezado actual.
- Define/Constantes: define macros o constantes utilizadas dentro de la clase o por otras partes del código para obtener valores consistentes.
- Declaración de clase: declara la clase con su herencia (si la hay), miembros privados, métodos públicos y métodos protegidos.
- Mapeo de eventos: utiliza macros para definir cómo se asignan los eventos a las funciones miembro para la programación basada en eventos.
- Implementaciones de métodos: si bien no es estrictamente necesario para todos los archivos de encabezado, en este caso se incluyen implementaciones de métodos, lo que es común para clases más pequeñas donde la encapsulación no es crítica. Por razones de rendimiento en MQL5, incluida la implementación, se puede reducir la sobrecarga de llamadas de función.
- Constructores y destructores: son parte de la definición de la clase y especifican cómo se crean y destruyen los objetos de la clase.
Basándonos en el esquema anterior, aquí tenemos una plantilla de código de muestra:
// 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 } //+------------------------------------------------------------------+
Para crear archivos de encabezado en MetaEditor, abra un nuevo archivo presionando Ctrl + N o navegando manualmente a través del menú. En la ventana emergente, seleccione Incluir (*.mqh) y proceda a iniciar la edición. De forma predeterminada, la plantilla generada incluye algunos comentarios como notas para guiarlo.
Consulte la imagen a continuación:
Crear un nuevo archivo de encabezado en MetaEditor
Aquí se muestra la plantilla de archivo de encabezado predeterminada, que incluye algunas notas comentadas como orientación.
//+------------------------------------------------------------------+ //| 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 //+------------------------------------------------------------------+
Archivo de encabezado de la página de inicio del administrador
En esta sección, desarrollamos la clase CAdminHomeDialog, que sirve como interfaz principal para el panel de administración en nuestro programa MQL5. Integra archivos de encabezado esenciales para controles de diálogo y botones y utiliza constantes predefinidas para mantener dimensiones y espaciado del panel coherentes.
//+------------------------------------------------------------------+ //| 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)
La clase CAdminHomeDialog extiende CAppDialog e incluye cuatro botones clave, m_tradeMgmtButton, m_commButton, m_analyticsButton y m_showAllButton, que proporcionan una navegación fluida por las diferentes secciones del panel de administración. La estructura de clase permanece optimizada, con un constructor y destructor mínimos, mientras que el método Create garantiza que todos los botones se inicialicen correctamente para una experiencia de usuario fluida.
//+------------------------------------------------------------------+ //| 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; }
Las interacciones del usuario se gestionan dentro del método OnEvent, donde los clics en los botones activan mensajes de depuración y llaman a sus respectivos controladores de eventos: OnClickTradeManagement, OnClickCommunications, OnClickAnalytics y OnClickShowAll. Estos controladores actualmente registran interacciones, pero se ampliarán a medida que mejoremos la funcionalidad.
//+------------------------------------------------------------------+ //| 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"); }
Los métodos de creación de botones CreateTradeMgmtButton, CreateCommButton, CreateAnalyticsButton, y CreateShowAllButton generan dinámicamente botones con identificadores únicos, posicionamiento preciso y etiquetas bien definidas. TEl botón «Show All» incluso incorpora un emoji para mejorar la interfaz de usuario. A medida que continuamos con el desarrollo, se introducirán mejoras y refinamientos adicionales para optimizar el rendimiento y la usabilidad.
//+------------------------------------------------------------------+ //| 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); }
Implementación de AdminHomeDialog.mqh en el programa principal:
1. Incluir vía #include "AdminHomeDialog.mqh"
#include "AdminHomeDialog.mqh"
Al incluir AdminHomeDialog.mqh, la clase CAdminHomeDialog estará disponible en el script principal. Sin esta inclusión, el compilador no reconocería CAdminHomeDialog, lo que provocaría errores. Este enfoque modular permite que el script principal permanezca limpio mientras conserva la implementación del diálogo en un archivo separado para una mejor organización y capacidad de mantenimiento.
2. Declarar como CAdminHomeDialog ExtDialog;
CAdminHomeDialog ExtDialog;
Al declarar ExtDialog como una instancia de CAdminHomeDialog , el script puede hacer referencia y controlar el panel de inicio de Admin en todo el programa. Este objeto maneja la creación, visibilidad y gestión de eventos del panel, haciéndolo accesible a través de diferentes funciones.
3. Create() utilizando ExtDialog dentro de 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; }
El método Create() inicializa el panel con dimensiones específicas y lo coloca correctamente en el gráfico. Colocar esto dentro de CreateHiddenPanels() garantiza que el panel solo se cree una vez durante la inicialización, lo que mantiene el proceso de configuración organizado y evita reinicializaciones innecesarias.
4. Mostrado u oculto según el estado de autenticación en OnChartEvent()
if(authManager.IsAuthenticated()) { if(!ExtDialog.IsVisible()) { ExtDialog.Show(); ChartRedraw(); } ExtDialog.ChartEvent(id, lparam, dparam, sparam); } else { if(ExtDialog.IsVisible()) { ExtDialog.Hide(); } }
Solo se debe poder acceder al panel de inicio de administración tras una autenticación correcta. Comprobando authManager. IsAuthenticated() garantiza que los usuarios no autorizados no puedan interactuar con el panel. Si la autenticación es válida, se muestra el panel; de lo contrario, permanece oculto, lo que mejora la seguridad y el control de acceso.
5. Se destruye en OnDeinit() cuando se elimina el script.
void OnDeinit(const int reason) { ExtDialog.Destroy(reason); }
Cuando se elimina el experto del gráfico, se llama a ExtDialog. Destroy() garantiza que los recursos asignados al panel se limpien por completo. Esto evita posibles fugas de memoria u objetos gráficos huérfanos que podrían interferir con futuras instancias del script.
Archivo de encabezado de Telegram
Para crear un archivo de encabezado de Telegram, la función de Telegram se copia directamente en la fuente del encabezado debido a su simplicidad y funcionamiento directo. Sin embargo, este enfoque puede ser diferente para otros archivos que requieren una configuración más estructurada, que involucra clases, métodos, constructores y destructores, como se mencionó anteriormente. Por lo tanto, este es el archivo de encabezado más simple en nuestra lista de creaciones. Al modularizar la función, reducimos la longitud del código principal y la función se puede reutilizar fácilmente en otros proyectos donde sea necesaria.
//+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
Implementación del encabezado de Telegram en el código principal
Esto se hace simplemente en 2 pasos:
1. Incluyendo el archivo en el código principal de la siguiente manera.
#include<Telegram.mqh> Lo anterior solo funciona si se almacena en el directorio MQL5/Include; de lo contrario, el nombre de la subcarpeta debe indicarse de la siguiente manera
#include <FolderName\Telegram.mqh> // Replace FolderName with actual location name
2. Por último, debemos llamar a la función en el código principal cuando sea necesario, de la siguiente manera:
SendMessageToTelegram("Your verification code: " + ActiveTwoFactorAuthCode,
TwoFactorAuthChatId, TwoFactorAuthBotToken) Desarrollo de un archivo de encabezado de autenticación
A partir de desarrollos anteriores, ha visto la evolución de la lógica del mensaje de seguridad, que se ha utilizado de manera consistente en todas las versiones del Panel de administración. Esta lógica también se puede adaptar a otros proyectos relacionados con paneles, especialmente al desarrollar un módulo clasificado por su funcionalidad. En esta etapa, estamos desarrollando Authentication.mqh, que consolida toda la lógica de seguridad utilizada en el antiguo Panel de administración. Compartiré el código a continuación y luego proporcionaré una explicación de cómo funciona.
//+------------------------------------------------------------------+ //| 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); } }; //+------------------------------------------------------------------+
La clase CAuthenticationManager del módulo proporcionado gestiona un proceso de autenticación de usuario de varios pasos para una aplicación basada en MQL5. Gestiona tanto la autenticación de contraseña como la autenticación de dos factores (2FA) a través de una interfaz basada en diálogo. El proceso de autenticación comienza cuando el usuario ingresa una contraseña en un cuadro de diálogo. Si la contraseña es correcta, el acceso se concede inmediatamente. Sin embargo, si la contraseña falla, el sistema rastrea los intentos fallidos y después de tres entradas incorrectas, activa un segundo diálogo para 2FA. El proceso 2FA implica la generación de un código de verificación de seis dígitos, que se envía vía Telegram al usuario, quien debe ingresar el código correcto para continuar.
El módulo utiliza coordenadas predefinidas para los elementos de diálogo para garantizar un diseño consistente e incluye mecanismos de retroalimentación para informar a los usuarios sobre errores o autenticación exitosa. La clase también integra la biblioteca Telegram.mqh para gestionar el envío de códigos 2FA. Está diseñado con la extensibilidad en mente, lo que permite modificar fácilmente la contraseña y la configuración de 2FA, y garantiza que ambos cuadros de diálogo estén ocultos cuando la autenticación es exitosa. Este diseño optimiza la experiencia del usuario al tiempo que mantiene características de seguridad sólidas.
El nuevo panel de administración de trading
En esta etapa, estamos integrando todos los módulos desarrollados previamente para construir un Panel de Administración más estructurado y eficiente. Esta versión mejorada mejora la organización y la modularidad, permitiendo que sus componentes se compartan y reutilicen fácilmente en otras aplicaciones dentro de la terminal.
El nuevo panel de administración incluye AdminHomeDialog.mqh para la interfaz gráfica y Authentication.mqh para la gestión de la autenticación. La EA define los parámetros de entrada para un ID de chat de Telegram y un token de bot para la verificación 2FA. Durante la inicialización (OnInit), intenta inicializar la autenticación y crear un panel oculto (CreateHiddenPanels), fallando si alguno de los dos procesos no se realiza correctamente.
La función OnChartEvent procesa los eventos del gráfico y gestiona la autenticación antes de mostrar u ocultar el panel AdminHomeDialog en función del estado de la autenticación. Si se autentica, garantiza que el panel sea visible y reenvía eventos al panel. De lo contrario, oculta el panel. La función de desinicialización (OnDeinit) garantiza que el cuadro de diálogo se destruya correctamente. Este diseño garantiza un acceso seguro a la página de inicio del Panel de administración, requiriendo autenticación antes de otorgar control sobre sus funciones.
Aquí está el código completo para el nuevo programa:
//+------------------------------------------------------------------+ //| 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; }
Pruebas
Aquí presentamos los resultados de las pruebas después de desarrollar componentes individuales e integrarlos lógicamente en el nuevo Panel de administración. Hemos compilado y ejecutado con éxito el programa en el gráfico, como se muestra a continuación.

Prueba del nuevo panel de administración en el gráfico EURUSD
Conclusión
Estoy seguro de que podrá apreciar cómo hemos simplificado la organización de nuestro Panel de administración. Al heredar de CAppDialog, hemos mejorado significativamente la capacidad de respuesta de la aplicación, lo que nos permite arrastrar el panel libremente por el gráfico por primera vez. Además, el botón minimizar, heredado de la clase base, nos permite minimizar la aplicación, proporcionando una vista de gráfico sin obstrucciones mientras la mantenemos ejecutándose en segundo plano. El panel se puede maximizar en cualquier momento cuando sea necesario, lo que garantiza una usabilidad perfecta.
Nuestro enfoque en la legibilidad, escalabilidad y modularidad nos ha llevado a desarrollar cada componente por separado. Este enfoque mejora la reutilización del código, lo que nos permite integrar estos módulos en otros programas simplemente incluyendo los archivos relevantes y llamando a los métodos esenciales. En el futuro, planeamos completar los componentes restantes que harán que el nuevo Panel de administración sea aún más poderoso. Estos incluyen CommunicationsDialog.mqh, TradeManagementDialog.mqh y AnalyticsDialog.mqh, cada uno diseñado para ser reutilizado en diferentes aplicaciones.
Como punto de partida, puedes intentar implementar Telegram.mqh en tu propio programa para ver con qué facilidad se integra. He adjuntado todos los archivos necesarios a continuación: ¡disfruta probándolos y desarrollándolos aún más!
| Archivo | Descripción |
|---|---|
| New Admin Panel .mqh | El último Panel de administración incorpora el concepto de modularidad. |
| Telegram.mqh | Para transmitir mensajes y notificaciones vía Telegram. |
| Authentication.mqh | Este archivo contiene todas las declaraciones de seguridad y la lógica completa. |
| AdminHomeDialog.mqh | Para la creación del cuadro de diálogo de inicio del administrador. Contiene todas las declaraciones de coordenadas. |
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/16562
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Redes neuronales en el trading: Integración de la teoría del caos en la previsión de series temporales (Final)
Creación de una estrategia de retorno a la media basada en el aprendizaje automático
Algoritmo del restaurador de éxito — Successful Restaurateur Algorithm (SRA)
Creación de un indicador canal de Keltner con gráficos personalizados en Canvas en MQL5
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso