
Creación de un Panel de Administración de Operaciones en MQL5 (Parte V): Panel de Gestión de Operaciones (II)
Contenido:
- Introducción
- Mejoramos el panel de gestión comercial
- Ajustamos la disposición del panel de gestión comercial
- Función auxiliar e Integración de un Nuevo Botón
- Manejadores de Codificación de Botones
- Pruebas
-
Introducción
Construyendo sobre la base de nuestra capacidad para superar las pruebas de seguridad y acceder al panel de inicio de administrador, donde tenemos acceso principal a las funciones clave, nuestro objetivo de hoy consistirá en seguir promoviendo la implementación de MQL5 a medida que mejoramos el rendimiento de nuestra interfaz multifuncional en un proyecto activamente desarrollado. Nuestra publicación anterior ha servido de introducción a las múltiples características del panel, desvelando las últimas capacidades sin detallar la funcionalidad de cada botón dentro del Panel de Gestión Comercial. Nos aseguraremos de que cada botón del Panel de Gestión Comercial produzca una respuesta al hacer clic sobre él.
Además, querríamos abordar una cuestión: ¿Estamos limitados por la disposición actual de los paneles? Para explorar esto, consideraremos ideas alternativas de diseño que pueden ayudarnos a usar mejor las capacidades de la clase de diálogo en el renombrado lenguaje de desarrollo de algoritmos comerciales, MQL5. A continuación, hemos incluido una imagen que muestra la posibilidad de ajustar la escala vertical del gráfico, y que, a mi juicio, resulta útil para maximizar el espacio disponible para nuestro panel. También creo que sería beneficioso permitir que ambos paneles se muestren simultáneamente mientras se mantiene una visión clara del gráfico. Para facilitar esta función, añadiremos un botón en el panel de inicio del administrador que permita a los usuarios ver todos los paneles a la vez.
La escala vertical del gráfico de MetaTrader 5 crea espacios y hace más clara la visión del rango de velas
Antes de seguir adelante, querría dar a todos la oportunidad de reconectar con los distintos elementos de nuestra interfaz multifuncional. Esto ayudará al lector a abordar algunos de los desafíos tácitos que se encuentran al trabajar en esta serie de artículos.- Para aquellos que cometen errores de compilación, es probable que se deba a la ejecución del código con los archivos de biblioteca por defecto que no se han ampliado. Para resolver este problema, asegúrese de sobreescribir el contenido del directorio MQL5/Include/controls con los archivos de la carpeta Extended Header proporcionada en artículos anteriores.
- El otro reto consistirá en asegurarse de que el ID del chat y el token del bot se actualicen para que coincidan con los nuestros, de modo que el código de verificación se envíe a nuestra cuenta.
Para el segundo reto, la solución pasará por acceder a la app de Telegram para crear dos bots: uno dedicado a la autenticación durante el inicio de la aplicación y otro para la comunicación a través de chats individuales, grupos y canales dentro del Panel de Comunicaciones del programa de EA.
A continuación, introduciremos nuestro ID de chat real y nuestro token de bot. Asegúrese de que el identificador del chat no sea de un grupo o canal, ya que al hacerlo compartiría su código con varios usuarios; debería seguir siendo algo altamente personal. Para verificar que tiene el ID de chat correcto, considere iniciar un chat con el bot y comprobar la API para el ID del chat. Tenga en cuenta que estos valores se codificarán en el código fuente antes de la compilación y que diferirán del ID de chat y del token de bot que se introducen al iniciar la aplicación.
Ya hemos hablado de ello en nuestros artículos anteriores, y los pasos son fáciles de seguir.
PASO 1:
Asegúrese de que es un usuario registrado de Telegram. Para ello, descargue la aplicación de Telegram en su teléfono u ordenador y siga las instrucciones de la aplicación para registrarse.
PASO 2:
Inicie un chat con BotFather para crear dos bots: uno para la autenticación de dos factores (2FA) y otro para las comunicaciones. Recibirá un token de bot único para cada bot, que se podrá utilizar para acceder a la API de Telegram y obtener los IDs del chat. Cada bot tiene su propio token distintivo, y usted será libre de nombrarlos como quiera, asegurándose de que puede identificar fácilmente sus propósitos.
PASO 3:
Acceda a la API clicando en el siguiente enlace: https://api.telegram.org/botReplaceMeWithTheTokenFromTheBotFather/getUpdates.
Telegram API accedida con la ayuda del navegador Chrome
PASO 4:
Inicie una conversación con su bot dedicado encargado de entregar el código 2FA. También puede añadir el bot de comunicaciones a cualquier grupo o canal en el que desee compartir información sobre trading y configurarlo como administrador para garantizar que funcione correctamente. Una vez haya completado la configuración, se iniciará una conversación dentro de los grupos para recuperar el ID del chat. Después, actualice su navegador en la pestaña de la API para ver la información actualizada.
Chat de Telegram: Inicie una conversación con el bot (en este caso hemos llamado a mi bot Admin Panel)
API de Telegram : Aquí podemos obtener nuestro Chat ID para 2FA
En la imagen de arriba, hemos iniciado un chat personal, principalmente para obtener mi ID de chat para la entrega de 2FA. También podrá añadir el mismo bot a grupos y canales; cuando inicie conversaciones en ellos, los ID de chat aparecerán automáticamente en la API, tal y como se muestra arriba, normalmente debajo de la primera conversación. Por ejemplo, he añadido el bot a uno de mis canales educativos: el mensaje API que he recibido se muestra en la siguiente imagen.
En resumen, el mismo bot puede usarse para varios canales, cada uno con su propio identificador de chat. Si así lo prefiere, puede utilizar un bot diferente para cada canal.
Añada su bot como Admin en un Canal de Telegram
Por último, a continuación le mostramos el JSON de la API con el ID del chat del canal.
JASON generado por Channel Chat
Este fragmento de código es donde deberá introducir las credenciales del bot para la entrega del código 2FA:// Constants for 2FA. Here put the chat ID dedicated for verification code delivery. It must be kept secure always. const string Hardcoded2FAChatId = "REPLACE WITH YOUR CHAT ID"; const string Hardcoded2FABotToken = "REPLACE WITH YOU BOT TOKEN"; //Obtain your bot token from a telegram bot father. //If you don't already have a bot you can start the chat with the bot father to create one.
A continuación le mostramos una imagen que ilustra dónde introducir las credenciales de Telegram para las comunicaciones comerciales durante la inicialización de la aplicación. También podrá personalizar los mensajes rápidos según sus necesidades:
Explicación de los ajustes de entrada de inicialización
Tenga en cuenta que el fragmento de código y la imagen anteriores muestran dos ID de chat diferentes, como hemos mencionado anteriormente. Uno se utiliza para la entrega de códigos de verificación, mientras que el otro se usa para la difusión de comunicaciones administrativas a través de chats personales o grupos de Telegram. Cada usuario, grupo y canal conectado al bot posee un ID de chat único.
Por último, hemos estado usando el mismo PIN, 2024, a lo largo de estas discusiones; sin embargo, podrá personalizarlo para su propio uso dentro del código fuente. Para 2FA, el algoritmo generador de código de seis dígitos se encargará de esto automáticamente; solo necesitará asegurarse de que tiene su ID de chat único para la entrega segura del código.
Con esta breve recapitulación, espero que esté preparado y listo para el progreso de hoy. En resumen, nos centraremos en los manejadores de los Botones de Gestión de Comercio y después mejoraremos su disposición en cuanto a la posición y las dimensiones en el gráfico.
Mejoramos el panel de gestión comercial
En primer lugar, me gustaría que entendiera la diferencia entre los botones del Panel de Comunicación y el Panel de Gestión de Operaciones. Sé que la mayoría de nosotros estamos familiarizados con la funcionalidad de operaciones masivas disponible tanto en la versión de escritorio como en la versión móvil de MetaTrader 5.
Como sugieren los nombres de los paneles, sus funciones se explican por sí mismas; no obstante, puede que le resulten confusas debido a la similitud de sus títulos o descripciones. Por ejemplo, los botones del Panel de Comunicación sirven únicamente para comunicarse, mientras que los del Panel de Gestión de Operaciones están programados específicamente para gestionar operaciones.
Podemos implementar manejadores de operaciones en los mismos botones utilizados en el Panel de Comunicación, permitiendo que ambas tareas se ejecuten simultáneamente al hacer clic. Sin embargo, existen varias razones para mantener los botones de Comunicación diferenciados de los de Gestión de Operaciones. Una razón clave es que el administrador puede querer gestionar transacciones que no están necesariamente destinadas a la difusión.
Por favor, consulte las imágenes siguientes, en las que se destacan las diferencias más significativas.
Panel de comunicaciones: Botones de mensajería
Casi todos los botones están pensados para la comunicación, salvo los de navegación. Además, los mensajes rápidos que envían pueden personalizarse durante la inicialización. El botón de mensaje rápido "Cerrar todo" está diseñado únicamente para enviar el mensaje al cliente de Telegram designado y no realizará ninguna otra acción.
Panel de Gestión de Operaciones: (Estos botones deben ejecutar transacciones comerciales al hacer clic en ellos)
A partir de las imágenes anteriores, hemos logrado aclarar la finalidad de cada funcionalidad. Ahora, volviendo nuestra atención al Panel de Gestión de Operaciones, queda mucho trabajo por hacer. Nuestro panel tiene actualmente un número limitado de botones, y nuestro objetivo es mejorarlo para maximizar el espacio de visualización de los gráficos. Los nuevos botones son esenciales para algunas operaciones comerciales críticas, como el botón "Cerrar todas las órdenes".
Ajuste de la disposición de la gestión de operaciones
La nueva disposición de la interfaz supone un diseño innovador destinado a crear más espacio para la visibilidad de los gráficos sin dejar de permitir las operaciones administrativas habituales dentro de esta serie de desarrollos. Este enfoque aprovecha las coordenadas de posición de los paneles en relación con el gráfico de MetaTrader 5.
Actualmente, el panel de inicio de administrador resulta demasiado grande para su contenido, y también lo ajustaremos. Para que el proceso sea más presentable, lo vamos a dividir en tres secciones.
Ajuste del panel de gestión de operaciones
Durante la inicialización, nuestro panel se crea usando el siguiente fragmento de código:
if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0, 30, 30, 500, 500))
{
Print("Failed to create Communictions panel dialog");
return INIT_FAILED;
}
En el código anterior, nuestro:
x1 =30
x2 =500
Por tanto, la anchura = X2 -X1 = 500-30 = 470.
Si aumentamos nuestro valor X2, la nueva anchura se ampliará según el concepto de diseño. Por ejemplo, vamos a aumentar en un 50% de la anchura original. Mire el siguiente fragmento de código.
// Let's increase our panel width by 50% of the former panel and is likely to be 250px but we will add an extra 30px to cover for the initial px on x1 co-ordinate.
// For the y co-ordinate we will reduce so much to 150px
if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0, 30, 30, 780, 150))
{
Print("Failed to create Communictions panel dialog");
return INIT_FAILED;
}
El resultado después de la compilación y la prueba será el siguiente:
Nueva disposición del Panel de Operaciones
Los cambios recientes han dejado algunos de los botones fuera del orden deseado, haciendo que floten sobre el gráfico en lugar de permanecer dentro de los bordes del panel. Para resolver este problema, iremos a la función CreateTradeManagementControls() y ajustaremos las coordenadas del botón como se muestra en el siguiente fragmento de código
// Create the Trade Management Panel controls
// Here we adjusted our button coordinates to fit well in the new Trade Management Panel
bool CreateTradeManagementControls()
{
long chart_id = ChartID();
// Buy Button
if (!buyButton.Create(chart_id, "BuyButton", 0, 130, 5, 210, 40))
{
Print("Failed to create Buy button");
return false;
}
buyButton.Text("Buy");
tradeManagementPanel.Add(buyButton);
// Sell Button
if (!sellButton.Create(chart_id, "SellButton", 0, 220, 5, 320, 40))
{
Print("Failed to create Sell button");
return false;
}
sellButton.Text("Sell");
tradeManagementPanel.Add(sellButton);
// Close Position Button
if (!closePosButton.Create(chart_id, "ClosePosButton", 0, 130, 50, 260, 70))
{
Print("Failed to create Close Position button");
return false;
}
closePosButton.Text("Close Position");
tradeManagementPanel.Add(closePosButton);
// Modify Position Button
if (!modifyPosButton.Create(chart_id, "ModifyPosButton", 0, 270, 50, 410, 70))
{
Print("Failed to create Modify Position button");
return false;
}
modifyPosButton.Text("Modify Position");
tradeManagementPanel.Add(modifyPosButton);
// Set Stop-Loss Button
if (!setSLButton.Create(chart_id, "SetSLButton", 0, 330, 5, 430, 40))
{
Print("Failed to create Set Stop-Loss button");
return false;
}
setSLButton.Text("Set SL");
tradeManagementPanel.Add(setSLButton);
// Set Take-Profit Button
if (!setTPButton.Create(chart_id, "SetTPButton", 0, 440, 5, 540, 40))
{
Print("Failed to create Set Take-Profit button");
return false;
}
setTPButton.Text("Set TP");
tradeManagementPanel.Add(setTPButton);
return true;
}
Al compilar y ejecutar el código hemos obtenido la siguiente imagen, con todos los botones colocados correctamente.
Panel de Gestión de Operaciones: Botones bien dispuestos
Ahora que hemos completado los cambios en el diseño y organizado los botones, procederemos a editar los botones existentes y a añadir otros nuevos.
Otra consideración importante es evitar conflictos con los botones internos. Por ejemplo, eche un vistazo a la siguiente imagen, que ilustra cómo los botones nativos se solapan con nuestro panel de administración.
Botones rápidos superpuestos al panel de administración
Para solucionar este problema, aplicaremos un desplazamiento a nuestras coordenadas x, moviéndolas hacia la derecha para trasladar la posición del panel. Ahora puede consultar el valor actualizado en el siguiente fragmento de código:
//1
//2
//2
if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0, 260, 30, 1040, 150))
{
Print("Failed to create Communictions panel dialog");
return INIT_FAILED;
}
La compilación se ha efectuado correctamente y el problema ha sido resuelto. Vea la disposición más abajo:
Botones Rápidos bien dispuestos y sin solapamientos
Función auxiliar y creación de nuevos botones
Durante mis clases de C++, con frecuencia mi profesor insistía en la importancia de la función de repetición, considerándola un concepto fundamental para cualquiera que aspire a llamarse programador. Más tarde, mientras investigaba funciones de ayuda en MQL5, inicialmente pensé que había tropezado con algo similar a la función de repetición de C ++. No obstante, pronto me di cuenta de que, aunque estas funciones comparten ciertas ventajas, sirven para fines distintos.
En resumen, las funciones de ayuda de MQL5 pueden compararse con "objetos de función" que parametrizan el comportamiento, mientras que las utilidades de repetición de C++ consisten más bien en aplicar transformaciones o patrones coherentes.
He aquí algunas similitudes entre ambas:
- Ambos pretenden reducir el código repetitivo.
- Y ambos mejoran la claridad y la facilidad de mantenimiento del código.
En esta discusión, hemos introducido el concepto de Funciones Auxiliares mientras nos preparamos para implementar múltiples botones para que nuestro trabajo siga siendo inteligente y presentable. Permanezca atento a nuestra aplicación.
Una función que me gusta especialmente cuando gestiono varias transacciones es la posibilidad de agilizar el proceso, lo que me permite gestionar numerosas transacciones a la vez de forma eficaz.
Operaciones masivas: Se utiliza en MetaTrader 5 para gestionar muchas transacciones
Daremos prioridad a las transacciones comerciales que requieran un solo clic para ejecutarse. Más adelante, nos centraremos en las funciones de modificación de operaciones, como ajustar los niveles de stop y configurarlos directamente desde el panel de administración. Partiendo de la imagen anterior también podemos inspirarnos para los nombres de los botones que utilizaremos en nuestro proyecto.
A continuación verá un esquema de los botones:
- Cerrar todas las posiciones
- Cerrar posiciones rentables
- Cerrar posiciones perdedoras
- Cerrar posiciones de compra
- Cerrar posiciones de venta
- Borrar todas las órdenes
- Borrar órdenes límite
- Borrar órdenes stop
- Borrar órdenes stop límite
A estas alturas, ya estamos familiarizados con los botones gracias a nuestras discusiones anteriores. El procedimiento para crear botones individuales será prácticamente el mismo, y solo diferirá en sus coordenadas. Para minimizar las tareas repetitivas, usaremos una función de ayuda, creada a medida para agilizar el proceso. Este enfoque reduce el tamaño del código e incorpora todos los nuevos botones de forma eficaz.
En primer lugar, declaramos como variables globales nuestros botones:
// Button Declarations CButton buyButton; // Button for Buy operations CButton sellButton; // Button for Sell operations CButton closeAllButton; // Button for closing all positions CButton closeProfitButton; // Button for closing profitable positions CButton closeLossButton; // Button for closing losing positions CButton closeBuyButton; // Button for closing Buy positions CButton closeSellButton; // Button for closing Sell positions CButton deleteAllOrdersButton; // Button for deleting all orders CButton deleteLimitOrdersButton; // Button for deleting limit orders CButton deleteStopOrdersButton; // Button for deleting stop orders CButton deleteStopLimitOrdersButton; // Button for deleting stop limit orders
Con nuestras declaraciones completas, será hora de desplegar la función auxiliar para crear esos botones, tal y como ya la hemos implementado. La función de ayuda, CreateButton, es una utilidad racionalizada diseñada para simplificar la repetitiva tarea de crear y configurar botones en el Panel de Gestión de Operaciones.
Esta función toma los parámetros para la referencia del botón, el nombre, el texto de la etiqueta y las coordenadas, al tiempo que gestiona la lógica subyacente de creación y configuración, incluida la gestión de errores. Centralizando este proceso, eliminamos el código redundante y garantizamos que todos los botones se creen de forma coherente con el mínimo esfuerzo.
Esta modularidad resulta crucial porque mejora la legibilidad y el mantenimiento del código, facilitando la ampliación del panel con botones adicionales o el ajuste de la funcionalidad en una ubicación central en lugar de en varias instancias. Esencialmente, la función auxiliar actúa como puente entre el diseño del panel y el proceso de creación de botones, garantizando una integración perfecta.
Este es el código de nuestra función auxiliar:
//Helper Function For seamless Button creation bool CreateButton(CButton &button, const string name, const string text, int x1, int y1, int x2, int y2) { long chart_id = ChartID(); if (!button.Create(chart_id, name, 0, x1, y1, x2, y2)) { Print("Failed to create button: ", name); return false; } button.Text(text); tradeManagementPanel.Add(button); return true; }
El fragmento de código anterior encapsulará todo el proceso de creación de botones. La función CreateTradeManagementControls sirve como organizador maestro, llamando a CreateButton repetidamente para definir y posicionar cada botón lógicamente dentro del Panel de Gestión de Operaciones. En lugar de duplicar la lógica de creación de botones para cada control, esta función se centra exclusivamente en especificar detalles únicos como coordenadas, etiquetas y tipos de botón.
El diseño modular que permite CreateButton mantiene esta función de nivel superior concisa y centrada en su misión principal: estructurar el diseño del panel. Juntas, estas dos funciones trabajan en armonía: CreateTradeManagementControls maneja la estructura mientras delega las tareas repetitivas a CreateButton, para realizar una implementación limpia, eficiente y adaptable del Panel de Gestión de Operaciones. Y el código para todos los botones se encuentra aquí
//+------------------------------------------------------------------+ //| Create Trade Management Controls | //+------------------------------------------------------------------+ bool CreateTradeManagementControls() { // Coordinates for buttons (adjust as needed) const int Y1_TOP = 5, Y2_TOP = 40; const int Y1_MID = 50, Y2_MID = 70; const int Y1_BOTTOM = 80, Y2_BOTTOM = 100; // Buy Button if (!CreateButton(buyButton, "BuyButton", "Buy", 130, Y1_TOP, 210, Y2_TOP)) return false; // Sell Button if (!CreateButton(sellButton, "SellButton", "Sell", 220, Y1_TOP, 320, Y2_TOP)) return false; // Close All Positions Button if (!CreateButton(closeAllButton, "CloseAllButton", "Close All", 130, Y1_MID, 230, Y2_MID)) return false; // Close Profitable Positions Button if (!CreateButton(closeProfitButton, "CloseProfitButton", "Close Profitable", 240, Y1_MID, 380, Y2_MID)) return false; // Close Losing Positions Button if (!CreateButton(closeLossButton, "CloseLossButton", "Close Losing", 390, Y1_MID, 510, Y2_MID)) return false; // Close Buy Positions Button if (!CreateButton(closeBuyButton, "CloseBuyButton", "Close Buys", 520, Y1_MID, 620, Y2_MID)) return false; // Close Sell Positions Button if (!CreateButton(closeSellButton, "CloseSellButton", "Close Sells", 630, Y1_MID, 730, Y2_MID)) return false; // Delete All Orders Button if (!CreateButton(deleteAllOrdersButton, "DeleteAllOrdersButton", "Delete All Orders", 130, Y1_BOTTOM , 270, Y2_BOTTOM )) return false; // Delete Limit Orders Button if (!CreateButton(deleteLimitOrdersButton, "DeleteLimitOrdersButton", "Delete Limits", 275, Y1_BOTTOM , 385, Y2_BOTTOM )) return false; // Delete Stop Orders Button if (!CreateButton(deleteStopOrdersButton, "DeleteStopOrdersButton", "Delete Stops", 390, Y1_BOTTOM , 515, Y2_BOTTOM )) return false; // Delete Stop Limit Orders Button if (!CreateButton(deleteStopLimitOrdersButton, "DeleteStopLimitOrdersButton", "Delete Stop Limits", 520, Y1_BOTTOM , 660, Y2_BOTTOM )) return false; return true; // All buttons created successfully }
Este es el resultado del nuevo diseño
Nuevo diseño tras la integración de los nuevos botones
Durante la incorporación de los nuevos botones, hemos eliminado algunos antiguos para mantener la uniformidad. Por ahora, nos centraremos en las operaciones que requieren una ejecución instantánea, sin necesidad de introducir datos adicionales; por ejemplo, tareas como la modificación de órdenes.
Manejadores de Codificación de Botones
Para mejorar la funcionalidad del panel de gestión de operaciones, hemos implementado funciones de gestión específicas para cada botón con el fin de permitir operaciones comerciales concretas. He aquí una explicación para cada fragmento de código:
1. Manejador de botón de compra (OnBuyButtonClick)
La función OnBuyButtonClick permite crear una orden de mercado para comprar el activo especificado. Usando la clase CTrade , gestiona los parámetros esenciales de la operación, como el tamaño del lote, el deslizamiento, el stop loss y el take profit, garantizando una ejecución precisa. Esto resulta fundamental para los tráders que desean abrir rápidamente posiciones de compra en un entorno controlado mediante programación.
//+------------------------------------------------------------------+ //| Handle Buy button click | //+------------------------------------------------------------------+ void OnBuyButtonClick() { CTrade trade; double lotSize = 0.1; // Example lot size double slippage = 3; // Example slippage double stopLoss = 0; // Example stop loss (in points) double takeProfit = 0; // Example take profit (in points) // Open Buy order double askPrice; if (SymbolInfoDouble(Symbol(), SYMBOL_ASK, askPrice) && askPrice > 0) { if (trade.Buy(lotSize, Symbol(), askPrice, slippage, stopLoss, takeProfit)) { Print("Buy order executed successfully."); } else { Print("Failed to execute Buy order. Error: ", GetLastError()); } } else { Print("Failed to retrieve Ask price. Error: ", GetLastError()); } // Execute Buy order logic here Print("Executing Buy operation"); }
2. Manejador de botón de venta (OnSellButtonClick)
La función OnSellButtonClick supone una imagen invertida del manejador de compra, permitiendo al usuario vender un activo a través de una orden de mercado. Estructurando la lógica de venta, se garantiza una gestión coherente de los parámetros, como el tamaño del lote y el deslizamiento, lo que hace que el panel de operaciones resulte eficaz para iniciar órdenes de venta bajo demanda.
//+------------------------------------------------------------------+ //| Handle Sell button click | //+------------------------------------------------------------------+ void OnSellButtonClick() { CTrade trade; double lotSize = 0.1; // Example lot size double slippage = 3; // Example slippage double stopLoss = 0; // Example stop loss (in points) double takeProfit = 0; // Example take profit (in points) double bidPrice; if (SymbolInfoDouble(Symbol(), SYMBOL_BID, bidPrice) && bidPrice > 0) { // Open Sell order if (trade.Sell(lotSize, Symbol(), bidPrice, slippage, stopLoss, takeProfit)) { Print("Sell order opened successfully."); } else { Print("Error opening sell order: ", trade.ResultRetcode()); } } else { Print("Failed to retrieve Bid price. Error: ", GetLastError()); } }
3. Manejador de cierre todas las posiciones (OnCloseAllButtonClick)
Esta función automatiza el cierre de todas las posiciones activas, iterando a través de las transacciones abiertas y utilizando CTrade. PositionClose para la ejecución. Resulta especialmente útil para los tráders que desean salir rápidamente de todas las transacciones, protegerse de la volatilidad repentina del mercado o cumplir los requisitos de salida de una estrategia.
//+------------------------------------------------------------------+ //| Handle Close All button click | //+------------------------------------------------------------------+ void OnCloseAllButtonClick() { CPositionInfo position; for (int i = 0; i < PositionsTotal(); i++) { if (position.SelectByIndex(i)) { CTrade trade; if (position.Type() == POSITION_TYPE_BUY) trade.PositionClose(position.Ticket()); else if (position.Type() == POSITION_TYPE_SELL) trade.PositionClose(position.Ticket()); } } Print("All positions closed."); }
4. Manejador de cierre de posiciones rentables (OnCloseProfitButtonClick)
Con la función OnCloseProfitButtonClick, los tráders pueden asegurar las ganancias cerrando solo las posiciones rentables. Filtra las transacciones en función de su valor de beneficio y garantiza cierres selectivos, alineándose con las estrategias centradas en fijar los beneficios y manteniendo las transacciones con pérdidas para su posterior evaluación.
//+------------------------------------------------------------------+ //| Handle Close Profitable button click | //+------------------------------------------------------------------+ void OnCloseProfitButtonClick() { CPositionInfo position; for (int i = 0; i < PositionsTotal(); i++) { if (position.SelectByIndex(i) && position.Profit() > 0) { CTrade trade; if (position.Type() == POSITION_TYPE_BUY) trade.PositionClose(position.Ticket()); else if (position.Type() == POSITION_TYPE_SELL) trade.PositionClose(position.Ticket()); } } Print("Profitable positions closed."); }
5. Manejador de cierre de posiciones perdedoras (OnCloseLossButtonClick)
Este manejador supone una herramienta de gestión del riesgo pues cierra todas las posiciones que estén incurriendo en pérdidas. Al centrarse únicamente en las transacciones con beneficios negativos, ayuda a mitigar las caídas posteriores, lo que es vital para mantener el capital de la cuenta y respetar los límites de pérdidas predefinidos.
//+------------------------------------------------------------------+ //| Handle Close Losing button click | //+------------------------------------------------------------------+ void OnCloseLossButtonClick() { CPositionInfo position; for (int i = 0; i < PositionsTotal(); i++) { if (position.SelectByIndex(i) && position.Profit() < 0) { CTrade trade; if (position.Type() == POSITION_TYPE_BUY) trade.PositionClose(position.Ticket()); else if (position.Type() == POSITION_TYPE_SELL) trade.PositionClose(position.Ticket()); } } Print("Losing positions closed."); } void OnCloseBuyButtonClick() { // Close Buy positions logic Print("Closing Buy positions"); } void OnCloseSellButtonClick() { // Close Sell positions logic Print("Closing Sell positions"); }
6. Manejador de eliminación de todas las órdenes (OnDeleteAllOrdersButtonClick)
Esta función elimina todas las órdenes pendientes, asegurando que ninguna orden límite o stop residual afecte a la cuenta. Utilizando la clase COrderInfo para recuperar y cancelar órdenes, ayuda a mantener un libro de órdenes limpio y evita ejecuciones no deseadas.
//+------------------------------------------------------------------+ //| Handle Delete All Orders button click | //+------------------------------------------------------------------+ void OnDeleteAllOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i)) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All orders deleted."); }
7. Manejado de borrado de órdenes límite (OnDeleteLimitOrdersButtonClick)
La función OnDeleteLimitOrdersButtonClick se centra solo en la cancelación de órdenes límite. Esto resulta esencial para los tráders que necesitan ajustar su estrategia al tiempo que preservan órdenes stop o de otro tipo, lo que permite un control preciso de la gestión de las órdenes.
//+------------------------------------------------------------------+ //| Handle Delete Limit Orders button click | //+------------------------------------------------------------------+ void OnDeleteLimitOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_STOP_LIMIT) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All limit orders deleted."); }
8. Manejador de eliminación de órdenes stop (OnDeleteStopOrdersButtonClick)
Este gestor tiene como objetivo la eliminación de todas las órdenes stop, garantizando que no desencadenen transacciones no deseadas en mercados volátiles. Aislando las órdenes stop, ofrece a los tráders un nivel de control granular sobre la gestión de las órdenes pendientes.
//+------------------------------------------------------------------+ //| Handle Delete Stop Orders button click | //+------------------------------------------------------------------+ void OnDeleteStopOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_STOP && ORDER_TYPE_SELL_STOP) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All stop orders deleted."); }
9. Manejador de eliminación de órdenes stop límite (OnDeleteStopLimitOrdersButtonClick)
La función gestiona la eliminación de órdenes stop límite, y resulta útil para estrategias que implican tipos de órdenes híbridas. Refuerza la flexibilidad y adapta la gestión de órdenes a las perspectivas actualizadas del mercado o a los cambios de estrategia del tráder.
//+------------------------------------------------------------------+ //| Handle Delete Stop Limit Orders button click | //+------------------------------------------------------------------+ void OnDeleteStopLimitOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_LIMIT && ORDER_TYPE_SELL_STOP_LIMIT) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All stop limit orders deleted."); }
Integración en OnChartEvent:
Para conectar las pulsaciones de los botones a estas funciones, integraremos sus llamadas dentro de la función OnChartEvent. Vinculando el valor sparam del botón con su manejador correspondiente, el programa garantiza una interacción perfecta entre la interfaz gráfica de usuario y la lógica comercial de backend, lo que hace que el panel responda y sea fácil de usar.
//+------------------------------------------------------------------+ //| Handle chart events | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (id == CHARTEVENT_OBJECT_CLICK) { // Panel navigation buttons if (sparam == "HomeButtonComm") { adminHomePanel.Show(); communicationsPanel.Hide(); } else if (sparam == "HomeButtonTrade") { adminHomePanel.Show(); tradeManagementPanel.Hide(); } if (sparam == "TradeMgmtAccessButton") { tradeManagementPanel.Show(); adminHomePanel.Hide(); } else if (sparam == "CommunicationsPanelAccessButton") { communicationsPanel.Show(); adminHomePanel.Hide(); } // Control buttons for panel resizing and closing else if (sparam == "MinimizeButton") { OnMinimizeButtonClick(); } else if (sparam == "MaximizeButton") { OnMaximizeButtonClick(); } else if (sparam == "CloseButton") { ExpertRemove(); } { if (sparam == "LoginButton") { OnLoginButtonClick(); } else if (sparam == "CloseAuthButton") { OnCloseAuthButtonClick(); } else if (sparam == "TwoFALoginButton") { OnTwoFALoginButtonClick(); } else if (sparam == "Close2FAButton") { OnClose2FAButtonClick(); } } switch (id) { case CHARTEVENT_OBJECT_CLICK: if (sparam == "SendButton") OnSendButtonClick(); else if (sparam == "ClearButton") OnClearButtonClick(); else if (sparam == "ChangeFontButton") OnChangeFontButtonClick(); else if (sparam == "ToggleThemeButton") OnToggleThemeButtonClick(); else if (sparam == "MinimizeButton") OnMinimizeButtonClick(); else if (sparam == "MaximizeButton") OnMaximizeButtonClick(); else if (sparam == "CloseButton") OnCloseButtonClick(); else if (StringFind(sparam, "QuickMessageButton") != -1) { long index = StringToInteger(StringSubstr(sparam, 18)); OnQuickMessageButtonClick(index - 1); } break; case CHARTEVENT_OBJECT_ENDEDIT: if (sparam == "InputBox") OnInputChange(); break; } } // Trade management buttons if (sparam == "BuyButton") OnBuyButtonClick(); else if (sparam == "SellButton") OnSellButtonClick(); else if (sparam == "CloseAllButton") OnCloseAllButtonClick(); else if (sparam == "CloseProfitButton") OnCloseProfitButtonClick(); else if (sparam == "CloseLossButton") OnCloseLossButtonClick(); else if (sparam == "CloseBuyButton") OnCloseBuyButtonClick(); else if (sparam == "CloseSellButton") OnCloseSellButtonClick(); else if (sparam == "DeleteAllOrdersButton") OnDeleteAllOrdersButtonClick(); else if (sparam == "DeleteLimitOrdersButton") OnDeleteLimitOrdersButtonClick(); else if (sparam == "DeleteStopOrdersButton") OnDeleteStopOrdersButtonClick(); else if (sparam == "DeleteStopLimitOrdersButton") OnDeleteStopLimitOrdersButtonClick(); }
Bien, aquí podemos ver nuestro código final. Ahora tenemos muchas líneas:
//+------------------------------------------------------------------+ //| 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 description "A secure and responsive Admin Panel. Send messages to your telegram clients without leaving MT5" #property version "1.22" #include <Trade\Trade.mqh> #include <Controls\Dialog.mqh> #include <Controls\Button.mqh> #include <Controls\Edit.mqh> #include <Controls\Label.mqh> // Input parameters for quick messages input string QuickMessage1 = "Updates"; input string QuickMessage2 = "Close all"; input string QuickMessage3 = "In deep profits"; input string QuickMessage4 = "Hold position"; input string QuickMessage5 = "Swing Entry"; input string QuickMessage6 = "Scalp Entry"; input string QuickMessage7 = "Book profit"; input string QuickMessage8 = "Invalid Signal"; input string InputChatId = "YOUR_CHAT_ID"; input string InputBotToken = "YOUR_BOT_TOKEN"; // Constants for 2FA const string Hardcoded2FAChatId = "Replace chat ID with yours"; const string Hardcoded2FABotToken = "Replace with your bot token"; // Global variables CDialog adminHomePanel, tradeManagementPanel, communicationsPanel; CDialog authentication, twoFactorAuth; CButton homeButtonComm, homeButtonTrade; CButton sendButton, clearButton, changeFontButton, toggleThemeButton; CButton loginButton, closeAuthButton, twoFALoginButton, close2FAButton; CButton quickMessageButtons[8], minimizeButton, maximizeButton, closeButton; CButton tradeMgmtAccessButton, communicationsPanelAccessButton; CEdit inputBox, passwordInputBox, twoFACodeInput; CLabel charCounter, passwordPromptLabel, feedbackLabel, twoFAPromptLabel, twoFAFeedbackLabel; bool minimized = false; bool darkTheme = false; int MAX_MESSAGE_LENGTH = 4096; string availableFonts[] = { "Arial", "Courier New", "Verdana", "Times New Roman" }; int currentFontIndex = 0; string Password = "2024"; // Hardcoded password string twoFACode = ""; // Button Declarations for Trade Management CButton buyButton; // Button for Buy operations CButton sellButton; // Button for Sell operations CButton closeAllButton; // Button for closing all positions CButton closeProfitButton; // Button for closing profitable positions CButton closeLossButton; // Button for closing losing positions CButton closeBuyButton; // Button for closing Buy positions CButton closeSellButton; // Button for closing Sell positions CButton deleteAllOrdersButton; // Button for deleting all orders CButton deleteLimitOrdersButton; // Button for deleting limit orders CButton deleteStopOrdersButton; // Button for deleting stop orders CButton deleteStopLimitOrdersButton; // Button for deleting stop limit orders //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { if (!ShowAuthenticationPrompt()) { Print("Authorization failed. Exiting..."); return INIT_FAILED; } if (!adminHomePanel.Create(ChartID(), "Admin Home Panel", 0, 30, 30, 500, 500)) { Print("Failed to create Admin Home Panel"); return INIT_FAILED; } if (!CreateAdminHomeControls()) { Print("Home panel control creation failed"); return INIT_FAILED; } if (!communicationsPanel.Create(ChartID(), "Communications Panel", 0, 30, 30, 500, 500)) { Print("Failed to create Communications panel dialog"); return INIT_FAILED; } if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0,260, 30, 1040, 170)) { Print("Failed to create Trade Management panel dialog"); return INIT_FAILED; } if (!CreateControls()) { Print("Control creation failed"); return INIT_FAILED; } if (!CreateTradeManagementControls()) { Print("Trade management control creation failed"); return INIT_FAILED; } adminHomePanel.Hide(); // Hide home panel by default on initialization communicationsPanel.Hide(); // Hide the Communications Panel tradeManagementPanel.Hide(); // Hide the Trade Management Panel return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Handle chart events | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (id == CHARTEVENT_OBJECT_CLICK) { // Panel navigation buttons if (sparam == "HomeButtonComm") { adminHomePanel.Show(); communicationsPanel.Hide(); } else if (sparam == "HomeButtonTrade") { adminHomePanel.Show(); tradeManagementPanel.Hide(); } if (sparam == "TradeMgmtAccessButton") { tradeManagementPanel.Show(); adminHomePanel.Hide(); } else if (sparam == "CommunicationsPanelAccessButton") { communicationsPanel.Show(); adminHomePanel.Hide(); } // Control buttons for panel resizing and closing else if (sparam == "MinimizeButton") { OnMinimizeButtonClick(); } else if (sparam == "MaximizeButton") { OnMaximizeButtonClick(); } else if (sparam == "CloseButton") { ExpertRemove(); } { if (sparam == "LoginButton") { OnLoginButtonClick(); } else if (sparam == "CloseAuthButton") { OnCloseAuthButtonClick(); } else if (sparam == "TwoFALoginButton") { OnTwoFALoginButtonClick(); } else if (sparam == "Close2FAButton") { OnClose2FAButtonClick(); } } switch (id) { case CHARTEVENT_OBJECT_CLICK: if (sparam == "SendButton") OnSendButtonClick(); else if (sparam == "ClearButton") OnClearButtonClick(); else if (sparam == "ChangeFontButton") OnChangeFontButtonClick(); else if (sparam == "ToggleThemeButton") OnToggleThemeButtonClick(); else if (sparam == "MinimizeButton") OnMinimizeButtonClick(); else if (sparam == "MaximizeButton") OnMaximizeButtonClick(); else if (sparam == "CloseButton") OnCloseButtonClick(); else if (StringFind(sparam, "QuickMessageButton") != -1) { long index = StringToInteger(StringSubstr(sparam, 18)); OnQuickMessageButtonClick(index - 1); } break; case CHARTEVENT_OBJECT_ENDEDIT: if (sparam == "InputBox") OnInputChange(); break; } } // Trade management buttons if (sparam == "BuyButton") OnBuyButtonClick(); else if (sparam == "SellButton") OnSellButtonClick(); else if (sparam == "CloseAllButton") OnCloseAllButtonClick(); else if (sparam == "CloseProfitButton") OnCloseProfitButtonClick(); else if (sparam == "CloseLossButton") OnCloseLossButtonClick(); else if (sparam == "CloseBuyButton") OnCloseBuyButtonClick(); else if (sparam == "CloseSellButton") OnCloseSellButtonClick(); else if (sparam == "DeleteAllOrdersButton") OnDeleteAllOrdersButtonClick(); else if (sparam == "DeleteLimitOrdersButton") OnDeleteLimitOrdersButtonClick(); else if (sparam == "DeleteStopOrdersButton") OnDeleteStopOrdersButtonClick(); else if (sparam == "DeleteStopLimitOrdersButton") OnDeleteStopLimitOrdersButtonClick(); } //+------------------------------------------------------------------+ //| Trade management button handlers | //+------------------------------------------------------------------+ void OnBuyButtonClick() { CTrade trade; double lotSize = 0.1; // lot size double slippage = 3; // slippage double stopLoss = 0; // stop loss (in points) double takeProfit = 0; // take profit (in points) // Open Buy order double askPrice; if (SymbolInfoDouble(Symbol(), SYMBOL_ASK, askPrice) && askPrice > 0) { if (trade.Buy(lotSize, Symbol(), askPrice, slippage, stopLoss, takeProfit)) { Print("Buy order executed successfully."); } else { Print("Failed to execute Buy order. Error: ", GetLastError()); } } else { Print("Failed to retrieve Ask price. Error: ", GetLastError()); } // Execute Buy order logic here Print("Executing Buy operation"); } //+------------------------------------------------------------------+ //| Handle Sell button click | //+------------------------------------------------------------------+ void OnSellButtonClick() { CTrade trade; double lotSize = 0.1; // lot size double slippage = 3; // slippage double stopLoss = 0; // stop loss (in points) double takeProfit = 0; // take profit (in points) double bidPrice; if (SymbolInfoDouble(Symbol(), SYMBOL_BID, bidPrice) && bidPrice > 0) { // Open Sell order if (trade.Sell(lotSize, Symbol(), bidPrice, slippage, stopLoss, takeProfit)) { Print("Sell order opened successfully."); } else { Print("Error opening sell order: ", trade.ResultRetcode()); } } else { Print("Failed to retrieve Bid price. Error: ", GetLastError()); } } //+------------------------------------------------------------------+ //| Handle Close All button click | //+------------------------------------------------------------------+ void OnCloseAllButtonClick() { CPositionInfo position; for (int i = 0; i < PositionsTotal(); i++) { if (position.SelectByIndex(i)) { CTrade trade; if (position.Type() == POSITION_TYPE_BUY) trade.PositionClose(position.Ticket()); else if (position.Type() == POSITION_TYPE_SELL) trade.PositionClose(position.Ticket()); } } Print("All positions closed."); } //+------------------------------------------------------------------+ //| Handle Close Profitable button click | //+------------------------------------------------------------------+ void OnCloseProfitButtonClick() { CPositionInfo position; for (int i = 0; i < PositionsTotal(); i++) { if (position.SelectByIndex(i) && position.Profit() > 0) { CTrade trade; if (position.Type() == POSITION_TYPE_BUY) trade.PositionClose(position.Ticket()); else if (position.Type() == POSITION_TYPE_SELL) trade.PositionClose(position.Ticket()); } } Print("Profitable positions closed."); } //+------------------------------------------------------------------+ //| Handle Close Losing button click | //+------------------------------------------------------------------+ void OnCloseLossButtonClick() { CPositionInfo position; for (int i = 0; i < PositionsTotal(); i++) { if (position.SelectByIndex(i) && position.Profit() < 0) { CTrade trade; if (position.Type() == POSITION_TYPE_BUY) trade.PositionClose(position.Ticket()); else if (position.Type() == POSITION_TYPE_SELL) trade.PositionClose(position.Ticket()); } } Print("Losing positions closed."); } void OnCloseBuyButtonClick() { // Close Buy positions logic Print("Closing Buy positions"); } void OnCloseSellButtonClick() { // Close Sell positions logic Print("Closing Sell positions"); } //+------------------------------------------------------------------+ //| Handle Delete All Orders button click | //+------------------------------------------------------------------+ void OnDeleteAllOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i)) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All orders deleted."); } //+------------------------------------------------------------------+ //| Handle Delete Limit Orders button click | //+------------------------------------------------------------------+ void OnDeleteLimitOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_STOP_LIMIT) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All limit orders deleted."); } //+------------------------------------------------------------------+ //| Handle Delete Stop Orders button click | //+------------------------------------------------------------------+ void OnDeleteStopOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_STOP && ORDER_TYPE_SELL_STOP) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All stop orders deleted."); } //+------------------------------------------------------------------+ //| Handle Delete Stop Limit Orders button click | //+------------------------------------------------------------------+ void OnDeleteStopLimitOrdersButtonClick() { COrderInfo order; for (int i = 0; i < OrdersTotal(); i++) { if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_LIMIT && ORDER_TYPE_SELL_STOP_LIMIT) { CTrade trade; trade.OrderDelete(order.Ticket()); } } Print("All stop limit orders deleted."); } //+------------------------------------------------------------------+ //| Show authentication input dialog | //+------------------------------------------------------------------+ bool ShowAuthenticationPrompt() { if (!authentication.Create(ChartID(), "Authentication", 0, 100, 100, 500, 300)) { Print("Failed to create authentication dialog"); return false; } if (!passwordInputBox.Create(ChartID(), "PasswordInputBox", 0, 20, 70, 260, 95)) { Print("Failed to create password input box"); return false; } authentication.Add(passwordInputBox); if (!passwordPromptLabel.Create(ChartID(), "PasswordPromptLabel", 0, 20, 20, 260, 40)) { Print("Failed to create password prompt label"); return false; } passwordPromptLabel.Text("Enter password: Access Admin Panel"); authentication.Add(passwordPromptLabel); if (!feedbackLabel.Create(ChartID(), "FeedbackLabel", 0, 20, 140, 380, 160)) { Print("Failed to create feedback label"); return false; } feedbackLabel.Text(""); feedbackLabel.Color(clrRed); // Red color for incorrect attempts authentication.Add(feedbackLabel); if (!loginButton.Create(ChartID(), "LoginButton", 0, 20, 120, 100, 140)) { Print("Failed to create login button"); return false; } loginButton.Text("Login"); authentication.Add(loginButton); if (!closeAuthButton.Create(ChartID(), "CloseAuthButton", 0, 120, 120, 200, 140)) { Print("Failed to create close button for authentication"); return false; } closeAuthButton.Text("Close"); authentication.Add(closeAuthButton); authentication.Show(); ChartRedraw(); return true; } //+------------------------------------------------------------------+ //| Show two-factor authentication input dialog | //+------------------------------------------------------------------+ void ShowTwoFactorAuthPrompt() { if (!twoFactorAuth.Create(ChartID(), "Two-Factor Authentication", 0, 100, 100, 500, 300)) { Print("Failed to create 2FA dialog"); return; } if (!twoFACodeInput.Create(ChartID(), "TwoFACodeInput", 0, 20, 70, 260, 95)) { Print("Failed to create 2FA code input box"); return; } twoFactorAuth.Add(twoFACodeInput); if (!twoFAPromptLabel.Create(ChartID(), "TwoFAPromptLabel", 0, 20, 20, 380, 40)) { Print("Failed to create 2FA prompt label"); return; } twoFAPromptLabel.Text("Enter the 2FA code sent to your Telegram:"); twoFactorAuth.Add(twoFAPromptLabel); if (!twoFAFeedbackLabel.Create(ChartID(), "TwoFAFeedbackLabel", 0, 20, 140, 380, 160)) { Print("Failed to create 2FA feedback label"); return; } twoFAFeedbackLabel.Text(""); twoFAFeedbackLabel.Color(clrRed); // Red color for incorrect 2FA attempts twoFactorAuth.Add(twoFAFeedbackLabel); if (!twoFALoginButton.Create(ChartID(), "TwoFALoginButton", 0, 20, 120, 100, 140)) { Print("Failed to create 2FA login button"); return; } twoFALoginButton.Text("Verify"); twoFactorAuth.Add(twoFALoginButton); if (!close2FAButton.Create(ChartID(), "Close2FAButton", 0, 120, 120, 200, 140)) { Print("Failed to create close button for 2FA"); return; } close2FAButton.Text("Close"); twoFactorAuth.Add(close2FAButton); twoFactorAuth.Show(); ChartRedraw(); } //+------------------------------------------------------------------+ //| Admin Home Panel controls creation | //+------------------------------------------------------------------+ bool CreateAdminHomeControls() { long chart_id = ChartID(); if (!tradeMgmtAccessButton.Create(chart_id, "TradeMgmtAccessButton", 0, 50, 50, 250, 90)) { Print("Failed to create Trade Management Access button"); return false; } tradeMgmtAccessButton.Text("Trade Management Panel"); adminHomePanel.Add(tradeMgmtAccessButton); if (!communicationsPanelAccessButton.Create(chart_id, "CommunicationsPanelAccessButton", 0, 50, 100, 250, 140)) { Print("Failed to create Communications Panel Access button"); return false; } communicationsPanelAccessButton.Text("Communications Panel"); adminHomePanel.Add(communicationsPanelAccessButton); if (!minimizeButton.Create(chart_id, "MinimizeButton", 0, 375, -22, 405, 0)) { Print("Failed to create minimize button"); return false; } minimizeButton.Text("_"); adminHomePanel.Add(minimizeButton); if (!maximizeButton.Create(chart_id, "MaximizeButton", 0, 405, -22, 435, 0)) { Print("Failed to create maximize button"); return false; } maximizeButton.Text("[ ]"); adminHomePanel.Add(maximizeButton); if (!closeButton.Create(chart_id, "CloseButton", 0, 435, -22, 465, 0)) { Print("Failed to create close button"); return false; } closeButton.Text("X"); adminHomePanel.Add(closeButton); return true; } //Helper Function seamless Button creation bool CreateButton(CButton &button, const string name, const string text, int x1, int y1, int x2, int y2) { long chart_id = ChartID(); if (!button.Create(chart_id, name, 0, x1, y1, x2, y2)) { Print("Failed to create button: ", name); return false; } button.Text(text); tradeManagementPanel.Add(button); return true; } //+------------------------------------------------------------------+ //| Create Trade Management Controls (Buttons) | //+------------------------------------------------------------------+ bool CreateTradeManagementControls() { // Coordinates for buttons (adjust as needed) const int Y1_TOP = 5, Y2_TOP = 40; const int Y1_MID = 50, Y2_MID = 70; const int Y1_BOTTOM = 80, Y2_BOTTOM = 100; // Create Buttons if (!CreateButton(buyButton, "BuyButton", "Buy", 130, Y1_TOP, 210, Y2_TOP)) return false; if (!CreateButton(sellButton, "SellButton", "Sell", 220, Y1_TOP, 320, Y2_TOP)) return false; if (!CreateButton(closeAllButton, "CloseAllButton", "Close All", 130, Y1_MID, 230, Y2_MID)) return false; if (!CreateButton(closeProfitButton, "CloseProfitButton", "Close Profitable", 240, Y1_MID, 380, Y2_MID)) return false; if (!CreateButton(closeLossButton, "CloseLossButton", "Close Losing", 390, Y1_MID, 510, Y2_MID)) return false; if (!CreateButton(closeBuyButton, "CloseBuyButton", "Close Buys", 520, Y1_MID, 620, Y2_MID)) return false; if (!CreateButton(closeSellButton, "CloseSellButton", "Close Sells", 630, Y1_MID, 730, Y2_MID)) return false; if (!CreateButton(deleteAllOrdersButton, "DeleteAllOrdersButton", "Delete All Orders", 130, Y1_BOTTOM , 270, Y2_BOTTOM )) return false; if (!CreateButton(deleteLimitOrdersButton, "DeleteLimitOrdersButton", "Delete Limits", 275, Y1_BOTTOM , 385, Y2_BOTTOM )) return false; if (!CreateButton(deleteStopOrdersButton, "DeleteStopOrdersButton", "Delete Stops", 390, Y1_BOTTOM , 515, Y2_BOTTOM )) return false; if (!CreateButton(deleteStopLimitOrdersButton, "DeleteStopLimitOrdersButton", "Delete Stop Limits", 520, Y1_BOTTOM , 660, Y2_BOTTOM )) return false; return true; // All buttons created successfully } //+------------------------------------------------------------------+ //| Handle login button click | //+------------------------------------------------------------------+ void OnLoginButtonClick() { string enteredPassword = passwordInputBox.Text(); if (enteredPassword == Password) { twoFACode = GenerateRandom6DigitCode(); SendMessageToTelegram("A login attempt was made on the Admin Panel. Please use this code to verify your identity: " + twoFACode, Hardcoded2FAChatId, Hardcoded2FABotToken); authentication.Destroy(); ShowTwoFactorAuthPrompt(); Print("Password authentication successful. A 2FA code has been sent to your Telegram."); } else { feedbackLabel.Text("Wrong password. Try again."); passwordInputBox.Text(""); } ///Handlers for the trade management } //+------------------------------------------------------------------+ //| Handle 2FA login button click | //+------------------------------------------------------------------+ void OnTwoFALoginButtonClick() { // If 2FA is successful, show the trade management panel string enteredCode = twoFACodeInput.Text(); if (enteredCode == twoFACode) { twoFactorAuth.Destroy(); adminHomePanel.Show(); Print("2FA authentication successful. Access granted to Trade Management Panel."); } else { twoFAFeedbackLabel.Text("Wrong code. Try again."); twoFACodeInput.Text(""); } } //+------------------------------------------------------------------+ //| Handle close button for authentication | //+------------------------------------------------------------------+ void OnCloseAuthButtonClick() { authentication.Destroy(); ExpertRemove(); // Exit the expert Print("Authentication dialog closed."); } //+------------------------------------------------------------------+ //| Handle close button for 2FA | //+------------------------------------------------------------------+ void OnClose2FAButtonClick() { twoFactorAuth.Destroy(); ExpertRemove(); Print("2FA dialog closed."); } //+------------------------------------------------------------------+ //| Create necessary UI controls | //+------------------------------------------------------------------+ bool CreateControls() { long chart_id = ChartID(); if (!inputBox.Create(chart_id, "InputBox", 0, 5, 25, 460, 95)) { Print("Failed to create input box"); return false; } communicationsPanel.Add(inputBox); // Create Home Button for Communications Panel if (!homeButtonComm.Create(chart_id, "HomeButtonComm", 0, 20, 120, 120,150)) { Print("Failed to create Home button for Communications Panel"); return false; } homeButtonComm.Text("Home 🏠"); communicationsPanel.Add(homeButtonComm); // Create Home Button for Trade Management Panel if (!homeButtonTrade.Create(chart_id, "HomeButtonTrade", 0, 20, 10, 120, 30)) { Print("Failed to create Home button for Trade Management Panel"); return false; } homeButtonTrade.Text("Home 🏠"); tradeManagementPanel.Add(homeButtonTrade); if (!charCounter.Create(chart_id, "CharCounter", 0, 380, 5, 460, 25)) { Print("Failed to create character counter"); return false; } charCounter.Text("0/" + IntegerToString(MAX_MESSAGE_LENGTH)); communicationsPanel.Add(charCounter); if (!clearButton.Create(chart_id, "ClearButton", 0, 235, 95, 345, 125)) { Print("Failed to create clear button"); return false; } clearButton.Text("Clear"); communicationsPanel.Add(clearButton); if (!sendButton.Create(chart_id, "SendButton", 0, 350, 95, 460, 125)) { Print("Failed to create send button"); return false; } sendButton.Text("Send"); communicationsPanel.Add(sendButton); if (!changeFontButton.Create(chart_id, "ChangeFontButton", 0, 95, 95, 230, 115)) { Print("Failed to create change font button"); return false; } changeFontButton.Text("Font<>"); communicationsPanel.Add(changeFontButton); if (!toggleThemeButton.Create(chart_id, "ToggleThemeButton", 0, 5, 95, 90, 115)) { Print("Failed to create toggle theme button"); return false; } toggleThemeButton.Text("Theme<>"); communicationsPanel.Add(toggleThemeButton); if (!minimizeButton.Create(chart_id, "MinimizeButton", 0, 375, -22, 405, 0)) { Print("Failed to create minimize button"); return false; } minimizeButton.Text("_"); communicationsPanel.Add(minimizeButton); if (!maximizeButton.Create(chart_id, "MaximizeButton", 0, 405, -22, 435, 0)) { Print("Failed to create maximize button"); return false; } maximizeButton.Text("[ ]"); communicationsPanel.Add(maximizeButton); if (!closeButton.Create(chart_id, "CloseButton", 0, 435, -22, 465, 0)) { Print("Failed to create close button"); return false; } closeButton.Text("X"); communicationsPanel.Add(closeButton); return CreateQuickMessageButtons(); } //+------------------------------------------------------------------+ //| Create quick message buttons | //+------------------------------------------------------------------+ bool CreateQuickMessageButtons() { string quickMessages[] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 }; int startX = 5, startY = 160, width = 222, height = 65, spacing = 5; for (int i = 0; i < ArraySize(quickMessages); i++) { bool created = quickMessageButtons[i].Create(ChartID(), "QuickMessageButton" + IntegerToString(i + 1), 0, startX + (i % 2) * (width + spacing), startY + (i / 2) * (height + spacing), startX + (i % 2) * (width + spacing) + width, startY + (i / 2) * (height + spacing) + height); if (!created) { Print("Failed to create quick message button ", i + 1); return false; } quickMessageButtons[i].Text(quickMessages[i]); communicationsPanel.Add(quickMessageButtons[i]); } return true; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { communicationsPanel.Destroy(); Print("Deinitialization complete"); } //+------------------------------------------------------------------+ //| Handle custom message send button click | //+------------------------------------------------------------------+ void OnSendButtonClick() { string message = inputBox.Text(); if (StringLen(message) > 0) { if (SendMessageToTelegram(message, InputChatId, InputBotToken)) Print("Custom message sent: ", message); else Print("Failed to send custom message."); } else { Print("No message entered."); } } //+------------------------------------------------------------------+ //| Handle clear button click | //+------------------------------------------------------------------+ void OnClearButtonClick() { inputBox.Text(""); OnInputChange(); Print("Input box cleared."); } //+------------------------------------------------------------------+ //| Handle quick message button click | //+------------------------------------------------------------------+ void OnQuickMessageButtonClick(long index) { string quickMessages[] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 }; string message = quickMessages[(int)index]; if (SendMessageToTelegram(message, InputChatId, InputBotToken)) Print("Quick message sent: ", message); else Print("Failed to send quick message."); } //+------------------------------------------------------------------+ //| Update character counter | //+------------------------------------------------------------------+ void OnInputChange() { int currentLength = StringLen(inputBox.Text()); charCounter.Text(IntegerToString(currentLength) + "/" + IntegerToString(MAX_MESSAGE_LENGTH)); ChartRedraw(); } //+------------------------------------------------------------------+ //| Handle toggle theme button click | //+------------------------------------------------------------------+ void OnToggleThemeButtonClick() { darkTheme = !darkTheme; UpdateThemeColors(); Print("Theme toggled: ", darkTheme ? "Dark" : "Light"); } //+------------------------------------------------------------------+ //| Update theme colors for the panel | //+------------------------------------------------------------------+ void UpdateThemeColors() { color textColor = darkTheme ? clrWhite : clrBlack; color buttonBgColor = darkTheme ? clrDarkSlateGray : clrGainsboro; color borderColor = darkTheme ? clrSlateGray : clrGray; color bgColor = darkTheme ? clrDarkBlue : clrWhite; UpdateButtonTheme(clearButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(sendButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(toggleThemeButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(changeFontButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(minimizeButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(maximizeButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(closeButton, textColor, buttonBgColor, borderColor); for (int i = 0; i < ArraySize(quickMessageButtons); i++) { UpdateButtonTheme(quickMessageButtons[i], textColor, buttonBgColor, borderColor); } ChartRedraw(); } //+------------------------------------------------------------------+ //| Apply theme settings to a button | //+------------------------------------------------------------------+ void UpdateButtonTheme(CButton &button, color textColor, color bgColor, color borderColor) { button.SetTextColor(textColor); button.SetBackgroundColor(bgColor); button.SetBorderColor(borderColor); } //+------------------------------------------------------------------+ //| Handle change font button click | //+------------------------------------------------------------------+ void OnChangeFontButtonClick() { currentFontIndex = (currentFontIndex + 1) % ArraySize(availableFonts); SetFontForAll(availableFonts[currentFontIndex]); Print("Font changed to: ", availableFonts[currentFontIndex]); ChartRedraw(); } //+------------------------------------------------------------------+ //| Set font for all input boxes and buttons | //+------------------------------------------------------------------+ void SetFontForAll(string fontName) { inputBox.Font(fontName); clearButton.Font(fontName); sendButton.Font(fontName); toggleThemeButton.Font(fontName); changeFontButton.Font(fontName); minimizeButton.Font(fontName); maximizeButton.Font(fontName); closeButton.Font(fontName); for (int i = 0; i < ArraySize(quickMessageButtons); i++) { quickMessageButtons[i].Font(fontName); } } //+------------------------------------------------------------------+ //| Generate a random 6-digit code for 2FA | //+------------------------------------------------------------------+ string GenerateRandom6DigitCode() { int code = MathRand() % 1000000; // Produces a 6-digit number return StringFormat("%06d", code); // Ensures leading zeros } //+------------------------------------------------------------------+ //| Handle minimize button click | //+------------------------------------------------------------------+ void OnMinimizeButtonClick() { minimized = true; communicationsPanel.Hide(); minimizeButton.Hide(); maximizeButton.Show(); closeButton.Show(); Print("Panel minimized."); } //+------------------------------------------------------------------+ //| Handle maximize button click | //+------------------------------------------------------------------+ void OnMaximizeButtonClick() { if (minimized) { communicationsPanel.Show(); minimizeButton.Show(); maximizeButton.Hide(); closeButton.Hide(); minimized = false; Print("Panel maximized."); } } //+------------------------------------------------------------------+ //| Handle close button click for admin panel | //+------------------------------------------------------------------+ void OnCloseButtonClick() { ExpertRemove(); Print("Admin panel closed."); } //+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
Pruebas
Tras las pruebas realizadas, todos los botones funcionan correctamente y responden según lo programado, lo que convierte a nuestro Panel de Gestión de Operaciones en un valioso componente del conjunto de herramientas del Administrador de Operaciones. La integración de nuevos botones y la implementación de sus manejadores de eventos se han completado con éxito.
Funcionamiento de los manejadores de los botones del Panel de Gestión de Operaciones
Estos son los comentarios de los expertos que muestran la ejecución y el cierre satisfactorios de una orden
Conclusión
En esta discusión, revisamos la configuración de Telegram y mejoramos el Panel de Gestión de Operaciones con nuevos botones de gestión de operaciones. También hemos mejorado la disposición reduciendo la escala vertical y aumentando la horizontal, incluido un desplazamiento posicional en la dirección x. Este ajuste ha resuelto los problemas de solapamiento entre nuestro panel y los botones incorporados de negociación rápida, mejorando la visibilidad de los gráficos y manteniendo los botones de negociación fácilmente accesibles.
Una vez finalizado el diseño, hemos integrado los manejadores de eventos de los botones para asegurarnos de que responden adecuadamente cuando se hace clic en ellos.
Espero que esta discusión haya arrojado luz sobre las amplias posibilidades de la programación de GUI en MQL5, demostrando el potencial creativo a la hora de diseñar varios componentes de GUI. Con una mente hábil e imaginativa, tendremos mucho más que explorar más allá de esta base.
¡Feliz desarrollo, amigos tráders!
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/16328





- 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