Creación de un asesor experto integrado de MQL5 y Telegram (Parte 4): Modular las funciones del código para mejorar su reutilización
Introducción
En el artículo anterior de esta serie, profundizamos en el proceso de envío de instantáneas de gráficos con títulos desde MetaTrader 5 a Telegram. Nuestro enfoque, aunque eficaz, fue bastante directo y algo inflexible. Hemos encadenado los componentes necesarios para capturar una pantalla, convertirla o codificarla en un formato apto para mensajes y enviarla a Telegram. Aunque esta configuración funcionaba, resultaba en una buena cantidad de código repetitivo y no del todo manejable. Entonces, ¿qué podemos hacer para mejorar esta aplicación? Pasar a una base de código más modular. Es el primer paso hacia un sistema más flexible y fácil de mantener.
En esta cuarta parte de nuestra serie, nos centraremos en mejorar la reusabilidad de nuestro programa a través de la modularización del código. Analizaremos en detalle los principios de la modularización del código y, más concretamente, cómo se aplican a nuestro proyecto. A continuación, presentaremos instrucciones paso a paso para reorganizar nuestro script MQL5 existente en funciones separadas y bien definidas. Al final, tendrá la opción de utilizar el programa antiguo y monolítico o un nuevo Asesor Experto (EA) modular con el mismo resultado.
A continuación, cambiaremos metódicamente nuestro código actual para que ocupe un nuevo espacio en la implementación de nuestro programa. Dividiremos el código en funciones discretas, cada una de las cuales realizará una única tarea: enviar mensajes, tomar capturas de pantalla y codificar datos en la forma necesaria para su transmisión. Demostraremos cómo encaja cada pieza en la nueva estructura y, lo que es más importante, cómo cada función lleva a cabo su tarea sin repeticiones innecesarias y de una manera que nos permitirá actualizar y ampliar el programa fácilmente.
Para concluir, hablaremos sobre los procesos de prueba y verificación del código de estrategia modularizado. Esto incluirá verificar el funcionamiento correcto de cada función y el rendimiento general del sistema y comparar los resultados con el código anterior. Aunque estemos utilizando el término modularización, sólo estamos hablando de hacer nuestros Asesores Expertos más comprensibles y mantenibles. Al final de este artículo, sabrá por qué este es un paso importante en el desarrollo y tendrá una idea clara de cómo lo hicimos y qué beneficios esperamos obtener. Estos son los temas que seguiremos para crear el Asesor Experto (EA):
- Comprender la necesidad de la modularización
- Refactorización del código de envío de mensajes
- Modularización de funciones de captura de pantalla
- Prueba e implementación de funciones modulares
- Conclusión
Al final, habremos producido un ordenado y eficiente Asesor experto MetaQuotes Language 5 (MQL5)-Telegram. Este código modular y bien estructurado facilita la integración, es muy flexible y requiere mucho menos esfuerzo para actualizarlo y añadirle nuevas funciones que si el código se escribiera de la forma habitual. No sólo gestiona las tareas de envío de mensajes, sino que también se encarga de las funciones de captura de pantalla y almacenamiento de datos, y lo hace de forma manejable y escalable, sentando así una base sólida para futuras mejoras. Empecemos pues.
Comprender la necesidad de la modularización
Examinaremos la idea de modularización del código, que es una técnica fundamental de la ingeniería del software para organizar y gestionar grandes bases de código. Cuando un programa se modulariza, se divide en partes más pequeñas y manejables que son casi autónomas. Cada una de estas partes (o módulos) realiza una función específica e interactúa con otros módulos de una manera bien definida. Por supuesto, la mayor parte del programa aún necesita escribirse, por lo que también veremos algunos principios y conceptos básicos que rigen cómo se puede hacer un programa modular.
Tanto la fase de desarrollo como la de mantenimiento muestran los beneficios de la modularización. Cuando llega el momento de escribir el código, los desarrolladores pueden concentrarse en un módulo a la vez. El código de cada módulo se implementa, se prueba y se depura antes de pasar al siguiente. Una vez que el sistema está en funcionamiento, si es necesario realizar un cambio, éste generalmente se puede realizar en un solo módulo. Los cambios en un módulo tienen muy poco efecto en otros módulos. El resultado general es un sistema mucho más estable con un menor coste de cambios o reparaciones. Como ilustración, nuestro MQL5 Asesor Experto puede enviar mensajes cuando ocurren ciertos eventos. Uno de nuestros módulos hace eso. Otro módulo puede tomar capturas de pantalla y guardarlas. Si quisiéramos ampliar la función de captura de pantalla, podríamos hacerlo sin estropear el módulo que envía mensajes.
Garantizar la mantenibilidad de un programa es engañosamente simple; puede parecer simple porque en realidad se trata de mantener todo lo que hace que un programa funcione en un orden limpio y bien organizado. Casi todo lo que hemos dicho hasta ahora es un preludio a este párrafo esencial, ya que nada es más propicio para la mantenibilidad que crear módulos que contengan todas las piezas necesarias para mantener a las partes contentas: una pieza para ti, una pieza para mí y una pieza para esa función de allí. Al garantizar todo esto, también garantizamos la reutilización, ya que una vez que un módulo hace lo que se supone que debe hacer, es lo mejor que puede conseguir.
Demostraremos la modularización del código del Asesor Experto MQL5 dividiéndolo en funciones y clases bien definidas. Veremos cómo unir estos módulos para realizar trabajos bien especificados, como enviar mensajes de texto, tomar capturas de pantalla y reestructurar los datos que ingresamos en nuestro sistema para que haga lo que queremos. Al final, esperamos entender cómo estos cambios hacen que nuestro Asesor Experto sea más eficiente, mantenible y escalable.
Refactorización del código de envío de mensajes
Lo primero que haremos es crear una función o módulo personalizado donde podremos ingresar la lógica que encapsularemos y organizaremos para su máxima reutilización. La primera función será la encargada de enviar un mensaje sencillo.
//+------------------------------------------------------------------+ //| FUNCTION TO SEND SIMPLE MESSAGE | //+------------------------------------------------------------------+ void sendSimpleMessage(){ //... }
Aquí, encapsulamos el proceso de envío de un mensaje de Telegram dentro de una función llamada "sendSimpleMessage". Esta estructura modular simplifica el mantenimiento, la gestión y la reutilización del código dentro del Asesor Experto (Expert Advisor, EA). Utilizamos una función void; es decir, una función que no devuelve ningún valor. En su lugar, funciona enviando un mensaje a Telegram. La función también es capaz de gestionar el éxito y el fracaso de la operación «bajo el capó», por lo que el código no se complica demasiado con todo tipo de sentencias "if". Esta encapsulación permite que el programa principal llame a la función cuando quiere enviar un mensaje, sin empantanarse en cómo hacerlo con la API de Telegram.
Para permitir flexibilidad en la operación de envío de mensajes, necesitamos incluir parámetros que podamos reutilizar automáticamente para permitir que se envíen diferentes textos cuando sea necesario, como la URL de la API, el token del bot y el ID del chat.
void sendSimpleMessage(string custom_message,const string api_url, const string bot_token,const string chat_id, int timeout=10000){ //... }
Aquí, definimos una función void llamada «sendSimpleMessage», que refleja su propósito: enviar un mensaje plano a Telegram sin ningún adjunto complejo o procesamiento de datos. Luego se completa con cuatro parámetros de entrada obligatorios: "custom_message", "api_url", "bot_token" y "chat_id", y un parámetro de entrada opcional: "timeout". Desglosemos los parámetros de forma estructurada para facilitar su comprensión.
- custom_message: Este es un parámetro de cadena que contiene el mensaje de texto real que queremos enviar a Telegram.
- api_url: Este es un parámetro de cadena que contiene la URL base de la API del Bot de Telegram. Esta URL se utiliza para solicitar el punto final correcto de la API.
- bot_token: Otro parámetro de cadena que contiene el token único del bot, necesario para autenticarlo y autorizarlo a enviar mensajes.
- chat_id: Este parámetro de cadena especifica el identificador único del chat o canal de Telegram donde se enviará el mensaje.
- timeout: Este es un parámetro int opcional que establece la cantidad de tiempo (en milisegundos) que la función debe esperar una respuesta de la API de Telegram antes de considerar la solicitud como agotada. El valor predeterminado es de 10.000 milisegundos (10 segundos), pero el usuario puede proporcionar un valor de tiempo de espera personalizado si es necesario.
Te habrás dado cuenta de que utilizamos la palabra clave const en algunos de los argumentos de entrada. Significa que los valores pasados son finales y no pueden ser cambiados, alterados o reemplazados dentro del cuerpo de la función, lo que asegura que no haya errores de sobreescritura en la función. A continuación, sólo tenemos que transferir los fragmentos de código responsables del envío de mensajes sencillos desde el formulario predeterminado a la función.
char data[]; // Array to hold data to be sent in the web request (empty in this case) char res[]; // Array to hold the response data from the web request string resHeaders; // String to hold the response headers from the web request string message = custom_message; const string url = api_url + "/bot" + bot_token + "/sendmessage?chat_id=" + chat_id + "&text=" + message; // Send the web request to the Telegram API int send_res = WebRequest("POST", url, "", timeout, data, res, resHeaders);
Empezamos declarando un par de arrays: "data" y "res". La matriz "data" está vacía, ya que no enviamos datos a nadie en nuestra solicitud web; solo enviamos el mensaje como parámetro de URL. La matriz "res" contendrá los datos de respuesta del servidor después de que realicemos la solicitud. Además, declaramos un string llamado «resHeaders», que contendrá cualquier cabecera de respuesta enviada de vuelta por la API de Telegram.
A continuación, tomamos el "custom_message" del parámetro de entrada y lo asignamos a la variable del mensaje. Básicamente, esto nos da la capacidad de trabajar con el mensaje o transmitirlo dentro de la función si necesitamos hacerlo.
Creamos la URL de solicitud de API uniendo varios componentes: la "api_url" básica, el punto final "/bot", el "bot_token" de autenticación y el "chat_id" del chat del destinatario. Para ello, añadimos el texto del mensaje como parámetro de URL: "&text=". El resultado es una URL completa que contiene todos los datos necesarios para la llamada API.
Por último, pasamos la lógica de petición web a la función WebRequest. Esta función es responsable de enviar una petición HTTP POST a la API de Telegram. Utiliza la URL que acabamos de crear para la API. El valor del tiempo de espera de la petición, por defecto 10 segundos (u otro valor especificado por el usuario), determina cuánto tiempo esperará la petición una respuesta antes de darse por vencida y seguir con su vida. La solicitud se envía con una matriz de datos vacía (que también podría ser simplemente un objeto vacío en formato JavaScript Object Notation (JSON)), y cualquier respuesta que la API nos devuelva se almacena en la matriz de resultados y en la cadena de encabezados de resultados.
Por último, simplemente agregamos la lógica de verificación para el estado de respuesta de la solicitud web.
// Check the response status of the web request if (send_res == 200) { // If the response status is 200 (OK), print a success message Print("TELEGRAM MESSAGE SENT SUCCESSFULLY"); } else if (send_res == -1) { // If the response status is -1 (error), check the specific error code if (GetLastError() == 4014) { // If the error code is 4014, it means the Telegram API URL is not allowed in the terminal Print("PLEASE ADD THE ", api_url, " TO THE TERMINAL"); } // Print a general error message if the request fails Print("UNABLE TO SEND THE TELEGRAM MESSAGE"); } else if (send_res != 200) { // If the response status is not 200 or -1, print the unexpected response code and error code Print("UNEXPECTED RESPONSE ", send_res, " ERR CODE = ", GetLastError()); }
El código de función final para enviar un mensaje simple desde MetaTrader 5 a Telegram es el siguiente:
//+------------------------------------------------------------------+ //| FUNCTION TO SEND SIMPLE MESSAGE | //+------------------------------------------------------------------+ void sendSimpleMessage(string custom_message,const string api_url, const string bot_token,const string chat_id, int timeout=10000){ char data[]; // Array to hold data to be sent in the web request (empty in this case) char res[]; // Array to hold the response data from the web request string resHeaders; // String to hold the response headers from the web request string message = custom_message; const string url = api_url + "/bot" + bot_token + "/sendmessage?chat_id=" + chat_id + "&text=" + message; // Send the web request to the Telegram API int send_res = WebRequest("POST", url, "", timeout, data, res, resHeaders); // Check the response status of the web request if (send_res == 200) { // If the response status is 200 (OK), print a success message Print("TELEGRAM MESSAGE SENT SUCCESSFULLY"); } else if (send_res == -1) { // If the response status is -1 (error), check the specific error code if (GetLastError() == 4014) { // If the error code is 4014, it means the Telegram API URL is not allowed in the terminal Print("PLEASE ADD THE ", api_url, " TO THE TERMINAL"); } // Print a general error message if the request fails Print("UNABLE TO SEND THE TELEGRAM MESSAGE"); } else if (send_res != 200) { // If the response status is not 200 or -1, print the unexpected response code and error code Print("UNEXPECTED RESPONSE ", send_res, " ERR CODE = ", GetLastError()); } }
Para comprobar que esto funciona perfectamente, vayamos al manejador de eventos OnInit, comentemos los fragmentos de código innecesarios y llamemos a la función. Para llamar a la función, deberá escribir su nombre y proporcionar los parámetros necesarios.
string msg = "EA INITIALIZED ON CHART " + _Symbol; // Message to send, including the chart symbol sendSimpleMessage(msg,TG_API_URL,botTkn,chatID,10000);
Aquí, invocamos la función "sendSimpleMessage" para enviar un mensaje al chat en Telegram. Primero construimos una cadena, "msg", que es una mera concatenación de las palabras "EA INITIALIZED ON CHART" con el símbolo del gráfico actual (_Symbol). Al próximo destinatario de este mensaje se le notificará que el Asesor Experto se ha inicializado en algún gráfico en particular.
Una vez que hemos definido el mensaje de texto que queremos enviar, llamamos a la función "sendSimpleMessage". Pasamos los cuatro argumentos de la función. El primer argumento es simplemente el mensaje de texto que queremos enviar, por lo que se llama "msg". El segundo argumento es una constante llamada "TG_API_URL", que es la URL base de la API de bots de Telegram. El tercer argumento es el token de acceso del bot ("botTkn"), y el cuarto argumento ("chatID") es el ID del chat o canal en Telegram donde el bot enviará el mensaje. Por último, especificamos un valor de tiempo de espera de 10 segundos (10000 milisegundos). Si el servidor de Telegram no responde después de ese tiempo, lo consideramos una falla y la función devolverá un código de error. Al realizar la prueba recibimos el siguiente mensaje:

Ha sido un éxito. Ahora puede ver que no necesitamos fragmentos de código demasiado largos para realizar acciones similares. Basta con llamar a la función responsable y pasarle los argumentos correspondientes. Enviemos otro mensaje sencillo que informe al usuario del marco temporal o periodo del gráfico.
string new_msg = "THE CURRENT TIMEFRAME IS "+EnumToString(_Period); sendSimpleMessage(new_msg,TG_API_URL,botTkn,chatID,10000);
Aquí, definimos una nueva variable de cadena llamada «new_msg». La nueva variable se establece fusionando el texto "THE CURRENT TIMEFRAME IS" con la versión de cadena del valor de_Period (el marco temporal del gráfico actual). Para ello se utiliza la función EnumToString, que traduce el valor de _Period a un formato legible por humanos. Por ejemplo, si el gráfico está ajustado a un marco temporal de 1 hora, «new_msg» contendrá el texto «THE CURRENT TIMEFRAME IS PERIOD_H1». Después se llama a la misma función para enviar mensajes simples y eso es todo. Puedes ver lo fácil que es. Al ejecutar una prueba, obtenemos el siguiente resultado:

Podemos ver lo fácil que fue enviar el código. Usamos sólo dos líneas de código para lograrlo. A continuación, pasamos a enviar un mensaje codificado complejo. No habrá muchos cambios en la función. Hereda la misma lógica. Sin embargo, dado que enviamos mensajes complejos, queremos gestionar los errores que puedan surgir. Así, en lugar de simplemente declarar una función void, tendremos una función de tipo de datos integer, que devolverá códigos específicos que ilustran un fallo o un éxito al llamar a la función. Por tanto, en el ámbito global, tendremos que definir los códigos de error.
#define FAILED_CODE -1 #define SUCCEEDED_CODE +1
Aquí definimos dos constantes, «FAILED_CODE» y «SUCCEEDED_CODE», utilizando la directiva del preprocesador #define. Asignamos a las constantes valores integer específicos para representar los resultados de las operaciones: «FAILED_CODE» tiene el valor -1 y representa un fallo, mientras que “SUCCEED_CODE” tiene el valor +1 y representa un resultado satisfactorio. Estas constantes pueden ser cualquier cosa que consideres adecuada. Luego de su declaración, procedemos a construir nuestra función.
int sendEncodedMessage(string custom_message,const string api_url, const string bot_token,const string chat_id, int timeout=10000){ //... }
Aquí, definimos una función entera llamada "sendEncodedMessage", lo que significa que la función devolverá valores enteros. Los datos de la solicitud web seguirán siendo los mismos. Sin embargo, necesitaremos verificar el éxito o el fracaso del estado de la respuesta y tomar las medidas necesarias.
// Check the response status of the web request if (send_res == 200) { // If the response status is 200 (OK), print a success message Print("TELEGRAM MESSAGE SENT SUCCESSFULLY"); return (SUCCEEDED_CODE); }
Aquí, si el estado de respuesta es exitoso y el mensaje se envió correctamente, devolvemos "SUCCEEDED_CODE". De lo contrario, si el estado de la respuesta es un error, devolvemos "FAILED_CODE".
else if (send_res == -1) { // If the response status is -1 (error), check the specific error code if (GetLastError() == 4014) { // If the error code is 4014, it means the Telegram API URL is not allowed in the terminal Print("PLEASE ADD THE ", api_url, " TO THE TERMINAL"); } // Print a general error message if the request fails Print("UNABLE TO SEND THE TELEGRAM MESSAGE"); return (FAILED_CODE); } else if (send_res != 200) { // If the response status is not 200 or -1, print the unexpected response code and error code Print("UNEXPECTED RESPONSE ", send_res, " ERR CODE = ", GetLastError()); return (FAILED_CODE); }
Por último, debemos devolver el código exitoso si todo pasa hasta este punto de la siguiente manera:
return (SUCCEEDED_CODE);
La devolución de éxito al final es muy importante, ya que podría darse la posibilidad de que no se comprobara ninguna de las subfunciones, y la función necesita devolver un número entero. Si intentamos compilar el programa sin el código de retorno, recibiremos un error como el siguiente:

Así, el código de función completo responsable del envío de mensajes complejos es el siguiente:
//+------------------------------------------------------------------+ //| FUNCTION TO SEND ENCODED MESSAGE | //+------------------------------------------------------------------+ //#define FAILED_CODE -1 //#define SUCCEEDED_CODE +1 int sendEncodedMessage(string custom_message,const string api_url, const string bot_token,const string chat_id, int timeout=10000){ char data[]; // Array to hold data to be sent in the web request (empty in this case) char res[]; // Array to hold the response data from the web request string resHeaders; // String to hold the response headers from the web request string message = custom_message; const string url = api_url + "/bot" + bot_token + "/sendmessage?chat_id=" + chat_id + "&text=" + message; // Send the web request to the Telegram API int send_res = WebRequest("POST", url, "", timeout, data, res, resHeaders); // Check the response status of the web request if (send_res == 200) { // If the response status is 200 (OK), print a success message Print("TELEGRAM MESSAGE SENT SUCCESSFULLY"); return (SUCCEEDED_CODE); } else if (send_res == -1) { // If the response status is -1 (error), check the specific error code if (GetLastError() == 4014) { // If the error code is 4014, it means the Telegram API URL is not allowed in the terminal Print("PLEASE ADD THE ", api_url, " TO THE TERMINAL"); } // Print a general error message if the request fails Print("UNABLE TO SEND THE TELEGRAM MESSAGE"); return (FAILED_CODE); } else if (send_res != 200) { // If the response status is not 200 or -1, print the unexpected response code and error code Print("UNEXPECTED RESPONSE ", send_res, " ERR CODE = ", GetLastError()); return (FAILED_CODE); } return (SUCCEEDED_CODE); }
Enviemos ahora la información de la cuenta en segmentos concatenados con caracteres emoji a Telegram y veamos la respuesta. Para ello se adoptará la misma estructura de código, pero explicaremos brevemente lo que hacemos exactamente.
////--- Account Status Update: double accountEquity = AccountInfoDouble(ACCOUNT_EQUITY); double accountFreeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FREE); string complex_msg = "\xF680 EA INITIALIZED ON CHART " + _Symbol + "\xF680" +"\n\xF4CA Account Status \xF4CA" +"\nEquity: $" +DoubleToString(accountEquity,2) +"\nFree Margin: $" +DoubleToString(accountFreeMargin,2); string encloded_msg = UrlEncode(complex_msg); complex_msg = encloded_msg; sendEncodedMessage(complex_msg,TG_API_URL,botTkn,chatID,10000);
Comenzamos accediendo a la equidad de la cuenta y al margen libre, utilizando AccountInfoDouble y pasando los argumentos ACCOUNT_EQUITY y ACCOUNT_MARGIN_FREE respectivamente. Estos recuperan el capital de la cuenta y el margen libre en tiempo real. Construimos un mensaje detallado, "complex_msg", en el que usamos el símbolo del gráfico, junto con el capital de la cuenta y el margen libre, y lo formateamos con emojis. Enviamos el mensaje utilizando la API de Telegram, pero primero debemos asegurarnos de que sea seguro para la transmisión a través de HTTP. Lo hacemos codificando el mensaje con la función "UrlEncode". Después de enviar el mensaje, esto es lo que obtenemos en la aplicación de Telegram.

Puedes ver que tenemos el mensaje complejo recibido exitosamente en Telegram. Entonces hemos terminado por completo con las funciones de envío de mensajes. Está bastante claro que al encapsular la funcionalidad anterior dentro de una función, hacemos que el código sea más limpio y permitimos que el proceso de envío de mensajes se reutilice sin esfuerzo en múltiples ubicaciones. Esto resultará especialmente útil a medida que procedamos a modularizar aún más el código, como por ejemplo al agregar funciones para enviar imágenes y manejar errores. Esto se hace en la siguiente sección.
Modularización de funciones de captura de pantalla
Aquí, necesitamos construir una función que tome los parámetros necesarios y envíe las imágenes del gráfico a Telegram. Su estructura de código será idéntica a la que usamos para enviar mensajes codificados.
int sendScreenShot(string screenshot_name,const string telegram_url, const string bot_token,const string chat_id, string caption=""){ //... }
Declaramos una función entera llamada "sendScreenShot", lo que significa que devolverá un valor entero. La función toma varios parámetros, garantizando flexibilidad y modularidad.
- El parámetro "screenshot_name" se refiere al nombre del archivo de captura de pantalla que se enviará, lo que nos permite especificar diferentes capturas de pantalla.
- "telegram_url", "bot_token" y "chat_id" son las entradas principales necesarias para comunicarse con la API de Telegram, lo que hace que la función sea adaptable a varias configuraciones de bots y cuentas de Telegram.
- Un parámetro opcional, "caption", nos permite adjuntar texto descriptivo a la captura de pantalla, mejorando la funcionalidad al permitirnos anotar las capturas de pantalla antes de enviarlas.
Dado que se mantiene la misma estructura de código, nos concentraremos únicamente en la lógica de retorno. El primero será en la instancia donde intentamos abrir y leer el contenido de la imagen.
int screenshot_Handle = INVALID_HANDLE; screenshot_Handle = FileOpen(screenshot_name,FILE_READ|FILE_BIN); if(screenshot_Handle == INVALID_HANDLE){ Print("INVALID SCREENSHOT HANDLE. REVERTING NOW!"); return(FAILED_CODE); }
Aquí, si no podemos abrir los datos de captura de pantalla que se enviarán a Telegram, devolvemos "FAILED_CODE" desde la función, lo que indica que la operación para enviar la captura de pantalla no puede continuar debido al problema de acceso al archivo. A continuación, simplemente heredamos la misma lógica para verificar el estado de respuesta y los respectivos registros de mensajes y códigos de estado como se muestra a continuación:
// Send the web request to the Telegram API int send_res = WebRequest("POST",URL,HEADERS,10000, DATA, res, resHeaders); // Check the response status of the web request if (send_res == 200) { // If the response status is 200 (OK), print a success message Print("TELEGRAM SCREENSHOT FILE SENT SUCCESSFULLY"); return (SUCCEEDED_CODE); } else if (send_res == -1) { // If the response status is -1 (error), check the specific error code if (GetLastError() == 4014) { // If the error code is 4014, it means the Telegram API URL is not allowed in the terminal Print("PLEASE ADD THE ", telegram_url, " TO THE TERMINAL"); } // Print a general error message if the request fails Print("UNABLE TO SEND THE TELEGRAM SCREENSHOT FILE"); return (FAILED_CODE); } else if (send_res != 200) { // If the response status is not 200 or -1, print the unexpected response code and error code Print("UNEXPECTED RESPONSE ", send_res, " ERR CODE = ", GetLastError()); return (FAILED_CODE); } return (SUCCEEDED_CODE);
La función completa responsable de enviar archivos de captura de pantalla de gráficos es la siguiente:
//+------------------------------------------------------------------+ //| FUNCTION TO SEND CHART SCREENSHOT FILES | //+------------------------------------------------------------------+ int sendScreenShot(string screenshot_name,const string telegram_url, const string bot_token,const string chat_id, string caption=""){ int screenshot_Handle = INVALID_HANDLE; screenshot_Handle = FileOpen(screenshot_name,FILE_READ|FILE_BIN); if(screenshot_Handle == INVALID_HANDLE){ Print("INVALID SCREENSHOT HANDLE. REVERTING NOW!"); return(FAILED_CODE); } else if (screenshot_Handle != INVALID_HANDLE){ Print("SCREENSHOT WAS SAVED & OPENED SUCCESSFULLY FOR READING."); Print("HANDLE ID = ",screenshot_Handle,". IT IS NOW READY FOR ENCODING."); } int screenshot_Handle_Size = (int)FileSize(screenshot_Handle); if (screenshot_Handle_Size > 0){ Print("CHART SCREENSHOT FILE SIZE = ",screenshot_Handle_Size); } uchar photoArr_Data[]; ArrayResize(photoArr_Data,screenshot_Handle_Size); FileReadArray(screenshot_Handle,photoArr_Data,0,screenshot_Handle_Size); if (ArraySize(photoArr_Data) > 0){ Print("READ SCREENSHOT FILE DATA SIZE = ",ArraySize(photoArr_Data)); } FileClose(screenshot_Handle); //ArrayPrint(photoArr_Data); //--- create boundary: (data -> base64 -> 1024 bytes -> md5) //Encodes the photo data into base64 format //This is part of preparing the data for transmission over HTTP. uchar base64[]; uchar key[]; CryptEncode(CRYPT_BASE64,photoArr_Data,key,base64); if (ArraySize(base64) > 0){ Print("Transformed BASE-64 data = ",ArraySize(base64)); //Print("The whole data is as below:"); //ArrayPrint(base64); } //Copy the first 1024 bytes of the base64-encoded data into a temporary array uchar temporaryArr[1024]= {0}; //Print("FILLED TEMPORARY ARRAY WITH ZERO (0) IS AS BELOW:"); //ArrayPrint(temporaryArr); ArrayCopy(temporaryArr,base64,0,0,1024); //Print("FIRST 1024 BYTES OF THE ENCODED DATA IS AS FOLLOWS:"); //ArrayPrint(temporaryArr); //Create an MD5 hash of the temporary array //This hash will be used as part of the boundary in the multipart/form-data uchar md5[]; CryptEncode(CRYPT_HASH_MD5,temporaryArr,key,md5); if (ArraySize(md5) > 0){ Print("SIZE OF MD5 HASH OF TEMPORARY ARRAY = ",ArraySize(md5)); Print("MD5 HASH boundary in multipart/form-data is as follows:"); ArrayPrint(md5); } //Format MD5 hash as a hexadecimal string & //truncate it to 16 characters to create the boundary. string HexaDecimal_Hash=NULL;//Used to store the hexadecimal representation of MD5 hash int total=ArraySize(md5); for(int i=0; i<total; i++){ HexaDecimal_Hash+=StringFormat("%02X",md5[i]); } Print("Formatted MD5 Hash String is: \n",HexaDecimal_Hash); HexaDecimal_Hash=StringSubstr(HexaDecimal_Hash,0,16);//truncate HexaDecimal_Hash string to its first 16 characters //done to comply with a specific length requirement for the boundary //in the multipart/form-data of the HTTP request. Print("Final Truncated (16 characters) MD5 Hash String is: \n",HexaDecimal_Hash); //--- WebRequest char DATA[]; string URL = NULL; URL = telegram_url+"/bot"+bot_token+"/sendPhoto"; //--- add chart_id //Append a carriage return and newline character sequence to the DATA array. //In the context of HTTP, \r\n is used to denote the end of a line //and is often required to separate different parts of an HTTP request. ArrayAdd(DATA,"\r\n"); //Append a boundary marker to the DATA array. //Typically, the boundary marker is composed of two hyphens (--) //followed by a unique hash string and then a newline sequence. //In multipart/form-data requests, boundaries are used to separate //different pieces of data. ArrayAdd(DATA,"--"+HexaDecimal_Hash+"\r\n"); //Add a Content-Disposition header for a form-data part named chat_id. //The Content-Disposition header is used to indicate that the following data //is a form field with the name chat_id. ArrayAdd(DATA,"Content-Disposition: form-data; name=\"chat_id\"\r\n"); //Again, append a newline sequence to the DATA array to end the header section //before the value of the chat_id is added. ArrayAdd(DATA,"\r\n"); //Append the actual chat ID value to the DATA array. ArrayAdd(DATA,chat_id); //Finally, Append another newline sequence to the DATA array to signify //the end of the chat_id form-data part. ArrayAdd(DATA,"\r\n"); // EXAMPLE OF USING CONVERSIONS //uchar array[] = { 72, 101, 108, 108, 111, 0 }; // "Hello" in ASCII //string output = CharArrayToString(array,0,WHOLE_ARRAY,CP_ACP); //Print("EXAMPLE OUTPUT OF CONVERSION = ",output); // Hello Print("CHAT ID DATA:"); ArrayPrint(DATA); string chatID_Data = CharArrayToString(DATA,0,WHOLE_ARRAY,CP_UTF8); Print("SIMPLE CHAT ID DATA IS AS FOLLOWS:",chatID_Data); //--- Caption string CAPTION_STRING = NULL; CAPTION_STRING = caption; if(StringLen(CAPTION_STRING) > 0){ ArrayAdd(DATA,"--"+HexaDecimal_Hash+"\r\n"); ArrayAdd(DATA,"Content-Disposition: form-data; name=\"caption\"\r\n"); ArrayAdd(DATA,"\r\n"); ArrayAdd(DATA,CAPTION_STRING); ArrayAdd(DATA,"\r\n"); } //--- ArrayAdd(DATA,"--"+HexaDecimal_Hash+"\r\n"); ArrayAdd(DATA,"Content-Disposition: form-data; name=\"photo\"; filename=\"Upload_ScreenShot.jpg\"\r\n"); ArrayAdd(DATA,"\r\n"); ArrayAdd(DATA,photoArr_Data); ArrayAdd(DATA,"\r\n"); ArrayAdd(DATA,"--"+HexaDecimal_Hash+"--\r\n"); Print("FINAL FULL PHOTO DATA BEING SENT:"); ArrayPrint(DATA); string final_Simple_Data = CharArrayToString(DATA,0,WHOLE_ARRAY,CP_ACP); Print("FINAL FULL SIMPLE PHOTO DATA BEING SENT:",final_Simple_Data); string HEADERS = NULL; HEADERS = "Content-Type: multipart/form-data; boundary="+HexaDecimal_Hash+"\r\n"; Print("SCREENSHOT SENDING HAS BEEN INITIATED SUCCESSFULLY."); //char data[]; // Array to hold data to be sent in the web request (empty in this case) char res[]; // Array to hold the response data from the web request string resHeaders; // String to hold the response headers from the web request //string msg = "EA INITIALIZED ON CHART " + _Symbol; // Message to send, including the chart symbol //const string url = TG_API_URL + "/bot" + botTkn + "/sendmessage?chat_id=" + chatID + // "&text=" + msg; // Send the web request to the Telegram API int send_res = WebRequest("POST",URL,HEADERS,10000, DATA, res, resHeaders); // Check the response status of the web request if (send_res == 200) { // If the response status is 200 (OK), print a success message Print("TELEGRAM SCREENSHOT FILE SENT SUCCESSFULLY"); return (SUCCEEDED_CODE); } else if (send_res == -1) { // If the response status is -1 (error), check the specific error code if (GetLastError() == 4014) { // If the error code is 4014, it means the Telegram API URL is not allowed in the terminal Print("PLEASE ADD THE ", telegram_url, " TO THE TERMINAL"); } // Print a general error message if the request fails Print("UNABLE TO SEND THE TELEGRAM SCREENSHOT FILE"); return (FAILED_CODE); } else if (send_res != 200) { // If the response status is not 200 or -1, print the unexpected response code and error code Print("UNEXPECTED RESPONSE ", send_res, " ERR CODE = ", GetLastError()); return (FAILED_CODE); } return (SUCCEEDED_CODE); }
Ahora tenemos la función para enviar una captura de pantalla, pero no tenemos una función para obtener los archivos de captura de pantalla. Primero, crearemos una función para obtener el archivo de imagen del gráfico donde está adjunto el programa, ya que no requerirá muchos parámetros.
//+------------------------------------------------------------------+ //| FUNCTION TO GET SCREENSHOT OF CURRENT CHART | //+------------------------------------------------------------------+ int getScreenshot_of_Current_Chart(string screenshot_name){ //--- First delete an instance of the screenshot file if it already exists if(FileIsExist(screenshot_name)){ FileDelete(screenshot_name); Print("Chart Screenshot was found and deleted."); ChartRedraw(0); } ChartScreenShot(0,screenshot_name,1366,768,ALIGN_RIGHT); // Wait for 30 secs to save screenshot if not yet saved int wait_loops = 60; while(!FileIsExist(screenshot_name) && --wait_loops > 0){ Sleep(500); } if(!FileIsExist(screenshot_name)){ Print("THE SPECIFIED SCREENSHOT DOES NOT EXIST (WAS NOT SAVED). REVERTING NOW!"); return (FAILED_CODE); } else if(FileIsExist(screenshot_name)){ Print("THE CHART SCREENSHOT WAS SAVED SUCCESSFULLY TO THE DATA-BASE."); return (SUCCEEDED_CODE); } return (SUCCEEDED_CODE); }
Aquí, declaramos una función entera, "getScreenshot_of_Current_Chart", para llevar a cabo el proceso de capturar y guardar una captura de pantalla del gráfico actual en MetaTrader 5. La función toma un parámetro, "screenshot_name", que contiene el nombre deseado del archivo en el que se guardará la captura de pantalla. Comenzamos la función comprobando si ya existe un archivo con el nombre "screenshot_name". Si es así, eliminamos el archivo preexistente. Este paso es crucial porque si no eliminamos el archivo preexistente, inevitablemente tendríamos un problema de sobrescritura, una situación en la que una captura de pantalla guardada terminaría con el mismo nombre que un archivo que se había eliminado recientemente.
También invocamos la función ChartRedraw para refrescar la visualización del gráfico antes de tomar la captura de pantalla. A continuación, para obtener la captura de pantalla del gráfico en su estado actual, llamamos a la función ChartScreenShot, indicándole el nombre que queremos darle al archivo, las dimensiones deseadas y la alineación que queremos. Después de esto, tuvimos que usar un bucle while para verificar la existencia del archivo. Esperamos hasta 30 segundos para que apareciera el archivo antes de continuar con el siguiente paso, y de ninguna manera queríamos ralentizar el proceso para la aparición de una captura de pantalla en el siguiente paso.
Si el archivo continúa sin existir después de este intervalo, generamos un mensaje de error que comunica que la captura de pantalla no se guardó y devolvemos un "FAILED_CODE" definitivo. Sin embargo, si se encuentra el archivo, emitimos un mensaje de éxito y devolvemos un "SUCCEEDED_CODE" claro. En esencia, permitimos dos resultados posibles para nuestra operación y los etiquetamos sin ambigüedades. La función para abrir un gráfico personalizado y tomar una instantánea hereda la misma lógica.
//+------------------------------------------------------------------+ //| FUNCTION TO GET SCREENSHOT OF A NEW CHART | //+------------------------------------------------------------------+ int getScreenshot_of_New_Chart(string screenshot_name,string symbol_name, ENUM_TIMEFRAMES period_name){ //--- First delete an instance of the screenshot file if it already exists if(FileIsExist(screenshot_name)){ FileDelete(screenshot_name); Print("Chart Screenshot was found and deleted."); ChartRedraw(0); } long chart_id=ChartOpen(symbol_name,period_name); ChartSetInteger(chart_id,CHART_BRING_TO_TOP,true); // update chart int wait=60; while(--wait>0){//decrease the value of wait by 1 before loop condition check if(SeriesInfoInteger(symbol_name,period_name,SERIES_SYNCHRONIZED)){ break; // if prices up to date, terminate the loop and proceed } } ChartRedraw(chart_id); ChartSetInteger(chart_id,CHART_SHOW_GRID,false); ChartSetInteger(chart_id,CHART_SHOW_PERIOD_SEP,false); ChartSetInteger(chart_id,CHART_COLOR_CANDLE_BEAR,clrRed); ChartSetInteger(chart_id,CHART_COLOR_CANDLE_BULL,clrBlue); ChartSetInteger(chart_id,CHART_COLOR_BACKGROUND,clrLightSalmon); ChartScreenShot(chart_id,screenshot_name,1366,768,ALIGN_RIGHT); Print("OPENED CHART PAUSED FOR 10 SECONDS TO TAKE SCREENSHOT."); Sleep(10000); // sleep for 10 secs to see the opened chart ChartClose(chart_id); // Wait for 30 secs to save screenshot if not yet saved int wait_loops = 60; while(!FileIsExist(screenshot_name) && --wait_loops > 0){ Sleep(500); } if(!FileIsExist(screenshot_name)){ Print("THE SPECIFIED SCREENSHOT DOES NOT EXIST (WAS NOT SAVED). REVERTING NOW!"); return (FAILED_CODE); } else if(FileIsExist(screenshot_name)){ Print("THE CHART SCREENSHOT WAS SAVED SUCCESSFULLY TO THE DATA-BASE."); return (SUCCEEDED_CODE); } return (SUCCEEDED_CODE); }
Aquí, declaramos una función entera que toma 3 parámetros: el nombre de la imagen, el nombre del símbolo y el período del gráfico que se abrirá. Hasta este punto tenemos las funciones que necesitamos para obtener capturas de pantalla y enviarlas a Telegram. Procedamos a obtener y enviar la captura de pantalla del gráfico actual y veamos qué obtenemos. Para lograrlo, se aplicará el siguiente fragmento de código.
getScreenshot_of_Current_Chart(SCREENSHOT_FILE_NAME);
sendScreenShot(SCREENSHOT_FILE_NAME,TG_API_URL,botTkn,chatID,NULL);
Aquí, simplemente llamamos a las dos funciones responsables de obtener y enviar la captura de pantalla guardada respectivamente, y pasar los parámetros de entrada requeridos. Puedes ver cuán pequeña es la lógica ahora. Sólo dos líneas de código son suficientes para hacer maravillas. Tras la compilación, obtenemos el siguiente resultado.

Podemos ver que podemos recibir el archivo de captura de pantalla del gráfico actual en el chat de Telegram. Hasta este punto, casi todo lo necesario está cubierto y así podemos ver cómo se pueden utilizar los códigos de retorno para contrarrestar el fallo o el éxito de las funciones. Para esta práctica, utilizaremos la lógica del gráfico nuevo.
int get_screenshot_new_chart_result = getScreenshot_of_New_Chart(SCREENSHOT_FILE_NAME,_Symbol,_Period); if (get_screenshot_new_chart_result == FAILED_CODE){ string result_msg = "NEW CHART SCREENSHOT COULDN'T BE SAVED. REVERT NOW."; Print(result_msg); sendSimpleMessage(result_msg,TG_API_URL,botTkn,chatID,10000); return (INIT_FAILED); } else if (get_screenshot_new_chart_result == SUCCEEDED_CODE){ string result_msg = "SUCCESS. NEW CHART SCREENSHOT WAS SAVED. CONTINUE NOW."; Print(result_msg); sendSimpleMessage(result_msg,TG_API_URL,botTkn,chatID,10000); string sending_msg = "\x2705\SCREENSHOT SENDING HAS BEEN INITIATED SUCCESSFULLY."; sending_msg += "\n\x270C\YOU SHOULD RECEIVE THE IMAGE FILE WITHIN 10 SECONDS"; string encoded_sending_msg = UrlEncode(sending_msg); Print(encoded_sending_msg); sendEncodedMessage(encoded_sending_msg,TG_API_URL,botTkn,chatID,10000); }
Aquí, gestionamos la salida de la función "getScreenshot_of_New_Chart". Esta función realiza el trabajo de tomar una captura de pantalla del nuevo gráfico y guardarla. Llamamos a la función con parámetros claros: el nombre que queremos para el archivo de captura de pantalla, el símbolo actual del gráfico y el período de tiempo. El resultado de la función se almacena en una variable que llamamos "get_screenshot_new_chart_result". Si este resultado es un éxito, pasamos a la siguiente parte de nuestro programa. Si falla, manejamos el fracaso de una manera que parezca sensata.
Cuando recibimos un resultado de "FAILED_CODE", indica que no se pudo guardar la captura de pantalla. En esta situación, generamos un mensaje de error que deja claro al usuario que algo salió mal en el proceso de guardar la captura de pantalla. Este mensaje se imprime en el terminal y también se envía a nuestro chat de Telegram mediante la función "sendSimpleMessage". Luego devolvemos "INIT_FAILED" como nuestro código de retorno, informando al usuario que la operación no tuvo éxito y que ni siquiera debe intentarse la siguiente operación. Esto finaliza el proceso de inicialización.
Por otro lado, si el resultado es "SUCCEEDED_CODE", significa que la captura de pantalla se guardó correctamente. Preparamos e imprimimos un mensaje que dice que la terminal envió un comando de éxito después de tomar la captura de pantalla. Luego procedemos a utilizar la función "sendSimpleMessage" para informar al usuario que su captura de pantalla se ha guardado y que debe esperar recibir el archivo pronto. El proceso de envío del mensaje al usuario es claro, conciso y se ejecuta correctamente. El comando para enviar la captura de pantalla al usuario fue exitoso y debería recibir el archivo en aproximadamente 10 segundos. Al diario le llega el siguiente registro:

En el chat de Telegram, recibimos el siguiente resultado:

Como puedes ver, ahora mismo es fácil enviar varias instancias de mensajes a Telegram para chatear sin esfuerzo. Todo lo que tenemos que hacer ahora es llamar a la función para que la lógica de envío de captura de pantalla transmita el archivo de imagen. Esto se logra mediante la siguiente lógica:
sendScreenShot(SCREENSHOT_FILE_NAME,TG_API_URL,botTkn,chatID,NULL);
Al ejecutar el código, obtenemos los siguientes resultados:

Ha sido un éxito. Puedes notar que la imagen que recibimos no tiene título. Esto se debe a que elegimos tener el campo de título como NULL, lo que significa que no se considerará ningún título. Para incluir el título solo tenemos que definir el campo título y pasarlo a la función. Nuestro título predeterminado se utilizará para la ilustración como se muestra a continuación:
//--- Caption string CAPTION = NULL; CAPTION = "Screenshot of Symbol: "+Symbol()+ " ("+EnumToString(ENUM_TIMEFRAMES(_Period))+ ") @ Time: "+TimeToString(TimeCurrent()); sendScreenShot(SCREENSHOT_FILE_NAME,TG_API_URL,botTkn,chatID,CAPTION);
Tras la compilación, recibimos una captura de pantalla del gráfico personalizado recién abierto con los parámetros predeterminados tal como se describe, pero lo que es más importante, con un título que ilustra el símbolo de la captura de pantalla, el período de tiempo y el momento en que se capturó y se transmitió a Telegram.

Ha sido un éxito. Ahora tenemos un programa completamente funcional que utiliza funciones para comunicarnos con el chat de Telegram. Ahora solo nos queda realizar algunas pruebas e implementar las funciones modulares que hemos creado para enviar señales comerciales basadas en cruces de medias móviles desde la plataforma MetaTrader 5 a los chats de Telegram. Este tema se abordará explícitamente en la siguiente sección.
Prueba e implementación de funciones modulares
En esta sección, cambiaremos el enfoque de la construcción de funciones individuales a la aplicación de esas funciones en situaciones comerciales reales, donde las confirmaciones de señales provocan respuestas específicas. Nuestro propósito ahora es verificar que las funciones que modularizamos (como las que permiten capturar capturas de pantalla o enviar mensajes) funcionarán juntas en el marco más amplio que estamos construyendo. Al poner la lógica de nuestro Asesor Experto (EA) en funciones reutilizables, podemos cumplir mejor el propósito de enviar capturas de pantalla de gráficos o actualizar estados de cuenta, de una manera que mantenga la lógica del EA, un EA que funciona según el principio de activar ciertas funciones cuando se cumplen condiciones específicas.
Este tema mostrará cómo convocamos estas funciones modulares cuando se confirman las señales comerciales, asegurando que todos los componentes funcionen de manera eficiente en escenarios del mundo real. Comprobaremos rigurosamente la confiabilidad de estas funciones, las ejecutaremos en el tablero de pruebas de ejecuciones repetidas y veremos si pueden soportar los rigores de la gestión de errores y, al mismo tiempo, cumplir con su tarea principal de enviar mensajes de confirmación de señal a Telegram de manera precisa y oportuna. Al hacer esto, no solo verificaremos la funcionalidad de nuestro código, sino que también lo usaremos como un paso hacia la creación de un sistema de gestión de señales comerciales que sea casi completamente robusto y maneje las condiciones de error con elegancia. Ahora solo tenemos que trasladar el fragmento de código de inicialización a la sección de generación de señal. Para una señal de compra, su fragmento de código será el siguiente:
// BUY POSITION OPENED. GET READY TO SEND MESSAGE TO TELEGRAM Print("BUY POSITION OPENED. SEND MESSAGE TO TELEGRAM NOW."); //char data[]; // Array to hold data to be sent in the web request (empty in this case) //char res[]; // Array to hold the response data from the web request //string resHeaders; // String to hold the response headers from the web request ushort MONEYBAG = 0xF4B0; string MONEYBAG_Emoji_code = ShortToString(MONEYBAG); string msg = "\xF680 Opened Buy Position." +"\n====================" +"\n"+MONEYBAG_Emoji_code+"Price = "+DoubleToString(openPrice,_Digits) +"\n\xF412\Time = "+TimeToString(iTime(_Symbol,_Period,0),TIME_SECONDS) +"\n\xF551\Time Current = "+TimeToString(TimeCurrent(),TIME_SECONDS) +"\n\xF525 Lotsize = "+DoubleToString(lotSize,2) +"\n\x274E\Stop loss = "+DoubleToString(stopLoss,_Digits) +"\n\x2705\Take Profit = "+DoubleToString(takeProfit,_Digits) +"\n_________________________" +"\n\xF5FD\Time Local = "+TimeToString(TimeLocal(),TIME_DATE) +" @ "+TimeToString(TimeLocal(),TIME_SECONDS) ; string encloded_msg = UrlEncode(msg); msg = encloded_msg; sendEncodedMessage(msg,TG_API_URL,botTkn,chatID,10000); int get_screenshot_current_chart_result = getScreenshot_of_Current_Chart(SCREENSHOT_FILE_NAME); if (get_screenshot_current_chart_result == FAILED_CODE){ string result_msg = "CURRENT CHART SCREENSHOT COULDN'T BE SAVED. REVERT NOW."; Print(result_msg); sendSimpleMessage(result_msg,TG_API_URL,botTkn,chatID,10000); return; } else if (get_screenshot_current_chart_result == SUCCEEDED_CODE){ string result_msg = "SUCCESS. CURRENT CHART SCREENSHOT WAS SAVED. CONTINUE NOW."; Print(result_msg); sendSimpleMessage(result_msg,TG_API_URL,botTkn,chatID,10000); string sending_msg = "\x2705\SCREENSHOT SENDING HAS BEEN INITIATED SUCCESSFULLY."; sending_msg += "\n\x270C\YOU SHOULD RECEIVE THE IMAGE FILE WITHIN 10 SECONDS"; string encoded_sending_msg = UrlEncode(sending_msg); Print(encoded_sending_msg); sendEncodedMessage(encoded_sending_msg,TG_API_URL,botTkn,chatID,10000); } //--- Caption string CAPTION = NULL; CAPTION = "Screenshot of Symbol: "+Symbol()+ " ("+EnumToString(ENUM_TIMEFRAMES(_Period))+ ") @ Time: "+TimeToString(TimeCurrent()); sendScreenShot(SCREENSHOT_FILE_NAME,TG_API_URL,botTkn,chatID,CAPTION);
Aquí, nos concentramos únicamente en informar sobre la señal generada y enviar una captura de pantalla del gráfico actual, mostrando los niveles comerciales. Para confirmar una señal de venta se adapta una lógica similar de la siguiente manera:
// SELL POSITION OPENED. GET READY TO SEND MESSAGE TO TELEGRAM Print("SELL POSITION OPENED. SEND MESSAGE TO TELEGRAM NOW."); //char data[]; // Array to hold data to be sent in the web request (empty in this case) //char res[]; // Array to hold the response data from the web request //string resHeaders; // String to hold the response headers from the web request ushort MONEYBAG = 0xF4B0; string MONEYBAG_Emoji_code = ShortToString(MONEYBAG); string msg = "\xF680 Opened Sell Position." +"\n====================" +"\n"+MONEYBAG_Emoji_code+"Price = "+DoubleToString(openPrice,_Digits) +"\n\xF412\Time = "+TimeToString(iTime(_Symbol,_Period,0),TIME_SECONDS) +"\n\xF551\Time Current = "+TimeToString(TimeCurrent(),TIME_SECONDS) +"\n\xF525 Lotsize = "+DoubleToString(lotSize,2) +"\n\x274E\Stop loss = "+DoubleToString(stopLoss,_Digits) +"\n\x2705\Take Profit = "+DoubleToString(takeProfit,_Digits) +"\n_________________________" +"\n\xF5FD\Time Local = "+TimeToString(TimeLocal(),TIME_DATE) +" @ "+TimeToString(TimeLocal(),TIME_SECONDS) ; string encloded_msg = UrlEncode(msg); msg = encloded_msg; sendEncodedMessage(msg,TG_API_URL,botTkn,chatID,10000); int get_screenshot_current_chart_result = getScreenshot_of_Current_Chart(SCREENSHOT_FILE_NAME); if (get_screenshot_current_chart_result == FAILED_CODE){ string result_msg = "CURRENT CHART SCREENSHOT COULDN'T BE SAVED. REVERT NOW."; Print(result_msg); sendSimpleMessage(result_msg,TG_API_URL,botTkn,chatID,10000); return; } else if (get_screenshot_current_chart_result == SUCCEEDED_CODE){ string result_msg = "SUCCESS. CURRENT CHART SCREENSHOT WAS SAVED. CONTINUE NOW."; Print(result_msg); sendSimpleMessage(result_msg,TG_API_URL,botTkn,chatID,10000); string sending_msg = "\x2705\SCREENSHOT SENDING HAS BEEN INITIATED SUCCESSFULLY."; sending_msg += "\n\x270C\YOU SHOULD RECEIVE THE IMAGE FILE WITHIN 10 SECONDS"; string encoded_sending_msg = UrlEncode(sending_msg); Print(encoded_sending_msg); sendEncodedMessage(encoded_sending_msg,TG_API_URL,botTkn,chatID,10000); } //--- Caption string CAPTION = NULL; CAPTION = "Screenshot of Symbol: "+Symbol()+ " ("+EnumToString(ENUM_TIMEFRAMES(_Period))+ ") @ Time: "+TimeToString(TimeCurrent()); sendScreenShot(SCREENSHOT_FILE_NAME,TG_API_URL,botTkn,chatID,CAPTION);
Para confirmar que se generan señales, cambiemos a un marco de tiempo de 1 minuto y esperemos las respuestas de las señales. La primera señal que recibimos es una configuración de venta. Se genera como se muestra a continuación en la terminal comercial:

Registro de señales de venta de MetaTrader 5:

Mensaje de venta recibido en el campo de chat de Telegram:

De las imágenes proporcionadas anteriormente, podemos ver que la configuración de la señal de venta se confirma en la terminal comercial, los mensajes importantes se registran en el diario y los datos de posición respectivos y las capturas de pantalla se envían al grupo de chat de Telegram. Ahora esperamos que cuando haya un cruce alcista, cerremos la posición de venta existente, abramos una posición de compra y enviemos la información al grupo de Telegram también. La configuración de confirmación del terminal comercial es la siguiente:

Registro de señales de compra de MetaTrader 5:

Mensaje de compra recibido en el campo de chat de Telegram:

Para visualizar el hito de forma más eficiente, aquí tienes un vídeo detallado basado en el funcionamiento del Asesor Experto desde su compilación, pasando por su inicialización y apertura de operaciones en base a las señales generadas, y cómo se transmiten los metadatos desde MQL5 a Telegram.
Hasta este punto, podemos ver que la modularización del código de la función es un éxito. La integración de MQL5 y Telegram ya funciona sin problemas. Gracias al uso de funciones modulares, que utilizamos para confirmar señales, hemos logrado automatizar la mensajería y la toma de capturas de pantalla, de modo que cada evento o actualización clave se publique en el chat de Telegram tan pronto como sucede. Y lo hemos probado lo suficiente como para estar seguros de que funciona. En términos de actuar como un puente entre MQL5 y Telegram, esta implementación es confiable y flexible: un buen diseño modular sobre el cual podemos desarrollar integraciones más complejas más adelante.
Conclusión
Este artículo describe la integración de MetaQuotes Language 5 (MQL5) con Telegram, centrándose en la creación de funciones modulares para enviar mensajes y operar con capturas de pantalla de gráficos. El diseño modular mejora la eficiencia, la escalabilidad y la capacidad de mantenimiento del Asesor Experto (EA) sin complejidad innecesaria.
El próximo artículo explorará un sistema de comunicación bidireccional que permitirá a Telegram enviar comandos a MetaTrader 5 y controlar las acciones del EA. Esta integración permitirá interacciones más avanzadas, como solicitar datos comerciales en vivo o capturas de pantalla directamente desde Telegram, ampliando los límites de la integración MQL5-Telegram. Esté atento a los próximos avances.
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/15706
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.
Ejemplo de análisis de redes de causalidad (Causality Network Analysis, CNA) y modelo de autoregresión vectorial para la predicción de eventos de mercado
Reimaginando las estrategias clásicas (Parte VII): Análisis de los mercados Forex y la deuda soberana en el USDJPY
Creación de un modelo de restricción de tendencia de velas (Parte 8): Desarrollo de un asesor experto (II)
Características del Wizard MQL5 que debe conocer (Parte 35): Regresión de vectores de soporte
- 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
enviar mensajes funciona pero sendPhoto no, ¿por qué?
Muchas gracias por su esfuerzo y amabilidad de compartir, esta serie de artículos es tan útil para mí y para otros. Le deseo lo mejor de la paz, la salud y la riqueza.