
Erstellen eines integrierten MQL5-Telegram Expert Advisors (Teil 4): Modularisierung von Codefunktionen für bessere Wiederverwendbarkeit
Einführung
Im vorangegangenen Artikel dieser Serie haben wir uns mit dem Senden von Schnappschüssen von Charts mit Legende von MetaTrader 5 an Telegram. Unser Ansatz war zwar effektiv, aber recht einfach und etwas unflexibel. Wir haben die Komponenten miteinander verknüpft, die erforderlich sind, um ein Screenshot zu erfassen, ihn in eine nachrichtenfreundliche Form zu konvertieren oder zu kodieren und ihn an Telegram zu senden. Obwohl dieser Aufbau funktionierte, führte er zu einer Menge Code, der sich wiederholte und nicht ganz so einfach zu handhaben war. Was können wir also tun, um diese Umsetzung zu verbessern? Umstellung auf eine modularere Codebasis! Dies ist der erste Schritt zu einem flexibleren und besser wartbaren System.
In diesem vierten Teil unserer Serie werden wir uns darauf konzentrieren, die Wiederverwendbarkeit unseres Programms durch den Code Modularisierung zu verbessern. Wir werden eine ausführliche Diskussion über die Prinzipien der Code-Modularisierung führen und insbesondere darüber, wie diese Prinzipien auf unser Projekt anwendbar sind. Im Anschluss daran werden wir Schritt für Schritt erklären, wie wir unser bestehendes mql5-Skript in separate, klar definierte Funktionen umstrukturieren können. Am Ende haben Sie die Wahl zwischen dem alten, monolithischen Programm und einem neuen, modularen Expert Advisor (EA) mit demselben Ergebnis.
Anschließend werden wir unseren aktuellen Code methodisch so ändern, dass er einen neuen Platz in der Verkörperung unseres Programms einnehmen wird. Wir werden den Code in einzelne Funktionen aufteilen, von denen jede eine einzige Aufgabe ausführt: das Senden von Nachrichten, das Erstellen von Screenshots und das Kodieren von Daten in die für die Übertragung erforderliche Form. Wir werden zeigen, wie die einzelnen Teile in der neuen Struktur zusammenpassen und, was noch wichtiger ist, wie jede Funktion ihre Aufgabe ohne unnötige Wiederholungen und auf eine Weise erfüllt, die es uns ermöglicht, das Programm problemlos zu aktualisieren und zu erweitern.
Abschließend werden wir über die Test- und Verifikationsprozesse für den modularisierten Strategiecode sprechen. Dabei werden die korrekte Funktionsweise der einzelnen Funktionen und die Gesamtleistung des Systems überprüft und die Ergebnisse mit dem alten Code verglichen. Obwohl wir den Begriff der Modularisierung verwenden, geht es uns nur darum, unsere Expert Advisors verständlicher und wartbarer zu machen. Am Ende dieses Artikels werden Sie wissen, warum dies ein wichtiger Schritt in der Entwicklung ist, und Sie werden ein klares Bild davon haben, wie wir es gemacht haben und welchen Nutzen wir uns davon versprechen. Hier sind die Themen, die wir verfolgen werden, um den Expert Advisor (EA) zu erstellen:
- Verständnis für die Notwendigkeit der Modularisierung
- Überarbeiten des Codes zum Versenden von Nachrichten
- Modularisierung der -Funktionen für die Screenshots
- Testen und Implementieren modularer Funktionen
- Schlussfolgerung
Am Ende werden wir einen ordentlichen und effizienten MetaQuotes Language 5 (MQL5)-Telegram Expert Advisor erstellt haben. Dieser modulare und gut strukturierte Code ermöglicht eine einfache Integration, ist äußerst flexibel und erfordert weitaus weniger Aufwand bei der Aktualisierung und Hinzufügung neuer Funktionen, als wenn der Code auf die übliche Weise geschrieben würde. Es übernimmt nicht nur die Aufgaben des Nachrichtenversands, sondern auch die Screenshot-Erfassung und die Datenhaltung, und zwar auf überschaubare und skalierbare Weise, wodurch eine solide Grundlage für zukünftige Erweiterungen geschaffen wird. Fangen wir also an.
Verständnis für die Notwendigkeit der Modularisierung
Wir werden uns mit der Idee der Code-Modularisierung befassen, einer grundlegenden Technik der Softwareentwicklung zur Organisation und Verwaltung großer Codebasen. Wenn ein Programm modularisiert wird, wird es in kleinere, überschaubare Teile zerlegt, die nahezu in sich geschlossen sind. Jedes dieser Teile (oder Module) erfüllt eine bestimmte Funktion und interagiert mit anderen Modulen auf eine genau definierte Weise. Natürlich muss der Großteil des Programms noch geschrieben werden, daher werden wir uns auch einige grundlegende Prinzipien und Konzepte ansehen, die bestimmen, wie man ein Programm modular gestalten kann.
Sowohl in der Entwicklungs- als auch in der Wartungsphase zeigt sich, dass sich die Modularisierung auszahlt. Wenn es an der Zeit ist, den Code zu schreiben, können sich die Entwickler auf ein Modul nach dem anderen konzentrieren. Der Code in jedem Modul wird implementiert, getestet und debuggt, bevor das nächste Modul in Angriff genommen wird. Wenn das System einmal in Betrieb ist und eine Änderung erforderlich ist, kann diese Änderung in der Regel in nur einem Modul vorgenommen werden. Änderungen in einem Modul haben nur geringe Auswirkungen auf andere Module. Das Gesamtergebnis ist ein wesentlich stabileres System mit geringeren Kosten für Änderungen oder Reparaturen. Zur Veranschaulichung: Unser MQL5 Expert Advisor kann Nachrichten senden, wenn bestimmte Ereignisse eintreten. Eines unserer Module tut dies. Ein anderes Modul kann Screenshots aufnehmen und speichern. Wenn wir die Screenshot-Funktion erweitern wollten, könnten wir das tun, ohne das Modul, das Nachrichten sendet, durcheinander zu bringen.
Die Gewährleistung der Wartbarkeit eines Programms ist trügerisch einfach; sie kann einfach erscheinen, weil es in Wirklichkeit darum geht, alles, was ein Programm zum Funktionieren bringt, in einer sauberen, gut organisierten Ordnung zu halten. Fast alles, was wir bisher gesagt haben, ist ein Vorspiel zu diesem wichtigen Absatz, denn nichts ist der Wartungsfreundlichkeit zuträglicher als Module, die alle Teile enthalten, die erforderlich sind, um die Parteien glücklich zu machen - ein Teil für Sie, ein Teil für mich und ein Teil für die Funktion dort drüben. Indem wir all dies sicherstellen, gewährleisten wir auch die Wiederverwendbarkeit, denn wenn ein Modul einmal tut, was es tun soll, ist es so gut wie möglich.
Wir werden die Modularisierung des Codes des MQL5 Expert Advisors demonstrieren, indem wir ihn in wohldefinierte Funktionen und Klassen unterteilen. Wir werden uns ansehen, wie diese Module zusammengefügt werden können, um genau definierte Aufgaben zu erfüllen, wie z. B. das Senden von Textnachrichten, das Erstellen von Screenshots und die Umstrukturierung der Daten, die wir in unser System eingeben, damit es das tut, was wir wollen. Wir hoffen, dass wir am Ende verstehen werden, wie diese Änderungen unseren Expert Advisor effizienter, wartbarer und skalierbarer machen.
Überarbeiten des Codes zum Versenden von Nachrichten
Als erstes werden wir eine nutzerdefinierte Funktion oder ein Modul erstellen, in das wir die Logik eingeben können, die wir kapseln und organisieren werden, um eine maximale Wiederverwendung zu erreichen. Die erste Funktion ist für das Senden einer einfachen Nachricht zuständig.
//+------------------------------------------------------------------+ //| FUNCTION TO SEND SIMPLE MESSAGE | //+------------------------------------------------------------------+ void sendSimpleMessage(){ //... }
Hier kapseln wir den Prozess des Sendens einer Telegram-Nachricht in einer Funktion namens „sendSimpleMessage“. Diese modulare Struktur vereinfacht die Wartung, Verwaltung und Wiederverwendung des Codes innerhalb des Expert Advisors (EA). Wir verwenden eine void Funktion, d. h. eine Funktion, die keinen Wert zurückgibt. Stattdessen wird eine Nachricht an Telegram gesendet. Die Funktion ist auch in der Lage, den Erfolg und Misserfolg der Operation „unter der Haube“ zu behandeln, sodass der Code nicht zu unübersichtlich wird mit allen Arten von if-Anweisungen. Diese Kapselung ermöglicht es dem Hauptprogramm, die Funktion aufzurufen, wenn es eine Nachricht senden möchte, ohne sich mit der Telegram-API zu beschäftigen.
Um den Vorgang des Nachrichtenversands flexibel zu gestalten, müssen wir Parameter einfügen, die wir automatisch wiederverwenden können, damit bei Bedarf verschiedene Texte gesendet werden können, sowie die API-URL, das Token des Bots und die Chat-ID.
void sendSimpleMessage(string custom_message,const string api_url, const string bot_token,const string chat_id, int timeout=10000){ //... }
Hier definieren wir eine void Funktion namens „sendSimpleMessage“, die ihren Zweck widerspiegelt: Senden einer einfachen Nachricht an Telegram, ohne komplexe Anhänge oder Datenverarbeitung. Es wird dann mit vier obligatorischen Eingabeparametern gefüllt: „custom_message“, „api_url“, „bot_token“ und „chat_id“ sowie einem optionalen Eingabeparameter: „timeout“. Lassen Sie uns die Parameter zum besseren Verständnis in eine strukturierte Form bringen.
- custom_message: Dies ist ein String-Parameter, der die eigentliche Textnachricht enthält, die wir an Telegram senden wollen.
- api_url: Dies ist ein String-Parameter, der die Basis-URL der Telegram Bot API enthält. Diese URL wird verwendet, um den richtigen API-Endpunkt anzufordern.
- bot_token: Ein weiterer String-Parameter, der das eindeutige Token des Bots enthält, das für die Authentifizierung des Bots und seine Autorisierung zum Senden von Nachrichten erforderlich ist.
- chat_id: Dieser String-Parameter gibt den eindeutigen Bezeichner des Telegram-Chats oder -Kanals an, an den die Nachricht gesendet werden soll.
- timeout: Dies ist ein optionaler int-Parameter, der die Zeitspanne (in Millisekunden) festlegt, die die Funktion auf eine Antwort von der Telegram-API warten soll, bevor sie die Anfrage als abgelaufen betrachtet. Der Standardwert ist auf 10.000 Millisekunden (10 Sekunden) eingestellt, aber der Nutzer kann bei Bedarf einen eigenen Timeout-Wert angeben.
Sie haben vielleicht bemerkt, dass wir das Schlüsselwort const in einigen der Eingabeargumente verwenden. Das bedeutet, dass die übergebenen Werte endgültig sind und innerhalb des Funktionskörpers nicht geändert, modifiziert oder ersetzt werden können, wodurch sichergestellt wird, dass es keine Überschreibungsfehler in der Funktion gibt. Als Nächstes müssen wir nur noch die Codeabschnitte, die für das Senden einfacher Nachrichten verantwortlich sind, aus dem Standardformular in die Funktion übertragen.
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);
Wir beginnen mit der Deklaration von zwei Arrays: „data“ und „res“. Das Array „data“ ist leer, da wir in unserer Webanforderung keine Daten senden, sondern nur die Nachricht als URL-Parameter. Das Array „res“ enthält die Antwortdaten des Servers, nachdem wir die Anfrage gestellt haben. Außerdem deklarieren wir eine Zeichenkette namens „resHeaders“, der alle Antwort-Header zugewiesen werden, die von der Telegram-API zurückgeschickt werden.
Als Nächstes nehmen wir die „custom_message“ aus dem Eingabeparameter und weisen sie der Nachrichtenvariablen zu. Dies gibt uns im Wesentlichen die Möglichkeit, mit der Nachricht innerhalb der Funktion zu arbeiten oder sie weiterzugeben, wenn wir das tun müssen.
Die URL der API-Anforderung wird aus mehreren Komponenten zusammengesetzt: der Basis-“api_url“, dem „/bot“-Endpunkt, dem Authentifizierungs-“bot_token“ und der „chat_id“ des Empfängerchats. Dazu fügen wir den Nachrichtentext als URL-Parameter hinzu: „&text=“. Das Ergebnis ist eine vollständige URL, die alle erforderlichen Daten für den API-Aufruf enthält.
Schließlich übergeben wir die Webanforderungslogik an die Funktion WebRequest. Diese Funktion ist für das Senden einer HTTP-POST-Anfrage an die Telegram-API verantwortlich. Sie verwendet die URL, die wir gerade für die API erstellt haben. Der Timeout-Wert der Anfrage, der standardmäßig 10 Sekunden beträgt (oder einen anderen vom Nutzer angegebenen Wert), bestimmt, wie lange die Anfrage auf eine Antwort wartet, bevor sie aufgibt und ihr Leben weiterführt. Die Anfrage wird mit einem leeren Datenarray gesendet (das auch einfach ein leeres Objekt im JavaScript Object Notation (JSON-Format) sein kann), und jede Antwort, die die API an uns zurückschickt, wird im Ergebnisarray und in der Ergebnis-Header-Zeichenkette gespeichert.
Zum Schluss fügen wir nur noch die Prüflogik für den Antwortstatus der Webanfrage hinzu.
// 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()); }
Der endgültige Funktionscode zum Senden einer einfachen Nachricht von MetaTrader 5 an Telegram lautet wie folgt:
//+------------------------------------------------------------------+ //| 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()); } }
Um sicherzustellen, dass dies einwandfrei funktioniert, gehen wir zuOnInit, kommentieren die unnötigen Codeabschnitte aus und rufen die Funktion auf. Der Aufruf der Funktion erfolgt durch die Eingabe des Funktionsnamens und die Angabe der erforderlichen Parameter.
string msg = "EA INITIALIZED ON CHART " + _Symbol; // Message to send, including the chart symbol sendSimpleMessage(msg,TG_API_URL,botTkn,chatID,10000);
Hier rufen wir die Funktion „sendSimpleMessage“ auf, um eine Nachricht an den Chat auf Telegram zu übermitteln. Zunächst konstruieren wir eine Zeichenkette, „msg“, die eine einfache Verkettung der Worte „EA INITIALIZED ON CHART“ mit dem Symbol des aktuellen Charts (_Symbol) ist. Der künftige Empfänger dieser Nachricht wird darüber informiert, dass der Expert Advisor für einen bestimmten Chart initialisiert wurde.
Nachdem wir die Textnachricht, die wir senden wollen, definiert haben, rufen wir die Funktion „sendSimpleMessage“ auf. Wir übergeben die vier Argumente der Funktion. Das erste Argument ist einfach die Textnachricht, die wir senden wollen, also heißt es „msg“. Das zweite Argument ist eine Konstante namens „TG_API_URL“, die die Basis-URL für die Telegram Bot API ist. Das dritte Argument ist das Zugriffs-Token des Bots („botTkn“), und das vierte Argument („chatID“) ist die ID des Chats oder Kanals in Telegram, an den der Bot die Nachricht senden wird. Schließlich geben wir einen Timeout-Wert von 10 Sekunden (10000 Millisekunden) an. Wenn der Telegram-Server nach dieser Zeit nicht antwortet, betrachten wir dies als Fehlschlag, und die Funktion gibt einen Fehlercode zurück. Beim Testen erhalten wir die folgende Meldung:
Das war ein Erfolg. Sie sehen nun, dass wir keine allzu langen Codeabschnitt benötigen, um ähnliche Aktionen durchzuführen. Wir brauchen nur die zuständige Funktion aufzurufen und die entsprechenden Argumente zu übergeben. Lassen Sie uns eine weitere einfache Nachricht senden, die den Nutzer über den Zeitrahmen oder die Periode des Charts informiert.
string new_msg = "THE CURRENT TIMEFRAME IS "+EnumToString(_Period); sendSimpleMessage(new_msg,TG_API_URL,botTkn,chatID,10000);
Hier definieren wir eine neue String-Variable namens „new_msg“. Die neue Variable wird durch Verschmelzen des Textes „THE CURRENT TIMEFRAME IS “ mit der String-Version des Wertes von _Period (dem Zeitrahmen des aktuellen Charts) erstellt. Dies geschieht mit der Funktion EnumToString, die den Wert von _Period in eine für Menschen lesbare Form umwandelt. Wenn das Chart zum Beispiel auf einen 1-Stunden-Zeitrahmen eingestellt ist, enthält „new_msg“ den Text „THE CURRENT TIMEFRAME IS PERIOD_H1“. Danach wird dieselbe Funktion zum Senden einfacher Nachrichten aufgerufen, und das war's. Sie können sehen, wie einfach das ist. Nach der Durchführung eines Tests erhalten wir die folgende Ausgabe:
Wir können sehen, wie einfach das Senden des Codes war. Wir haben nur zwei Zeilen Code verwendet, um dies zu erreichen. Als Nächstes gehen wir nun dazu über, eine komplexe verschlüsselte Nachricht zu versenden. An der Funktion wird sich nicht viel ändern. Sie folgt der gleichen Logik. Da wir jedoch komplexe Nachrichten senden, sollten wir mit Fehlern umgehen, die daraus resultieren könnten. Anstatt also einfach eine ungültige Funktion zu deklarieren, werden wir eine ganzzahlige Funktion haben, die bestimmte Codes zurückgibt, die einen Fehler oder einen Erfolg beim Aufruf der Funktion anzeigen. Daher müssen wir auf globaler Ebene die Fehlercodes definieren.
#define FAILED_CODE -1 #define SUCCEEDED_CODE +1
Hier definieren wir zwei Konstanten, „FAILED_CODE“ und „SUCCEED_CODE“, unter Verwendung der Präprozessoranweisung #define. Wir weisen den Konstanten bestimmte ganzzahlige Werte zu, um die Ergebnisse der Operationen darzustellen: „FAILED_CODE“ wird auf -1 gesetzt und steht für einen Fehlschlag, während „SUCCEEDED_CODE“ auf +1 gesetzt wird und für ein erfolgreiches Ergebnis steht. Diese Konstanten können alles sein, was Sie für richtig halten. Nach ihrer Deklaration fahren wir fort, unsere Funktion zu konstruieren.
int sendEncodedMessage(string custom_message,const string api_url, const string bot_token,const string chat_id, int timeout=10000){ //... }
Hier definieren wir eine Integer-Funktion mit dem Namen „sendEncodedMessage“, was bedeutet, dass die Funktion Integer-Werte zurückgeben wird. Die Daten der Webanforderung bleiben unverändert. Wir müssen jedoch prüfen, ob der Antwortstatus erfolgreich war oder nicht, und die erforderlichen Maßnahmen ergreifen.
// 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); }
Wenn der Antwortstatus ein Erfolg ist und die Nachricht erfolgreich gesendet wurde, geben wir hier den „SUCCEEDED_CODE“ zurück. Andernfalls, wenn der Antwortstatus ein Fehler ist, geben wir den „FAILED_CODE“ zurück.
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); }
Schließlich müssen wir den erfolgreichen Code zurückgeben, wenn alles bis zu diesem Punkt wie folgt funktioniert:
return (SUCCEEDED_CODE);
Die Rückgabe des Erfolgs am Ende ist sehr wichtig, da es möglich sein könnte, dass keine der Unterfunktionen geprüft wird und die Funktion eine ganze Zahl zurückgeben muss. Wenn wir versuchen, das Programm ohne den Rückgabecode zu kompilieren, erhalten wir eine Fehlermeldung wie unten:
Der vollständige Funktionscode, der für das Senden komplexer Nachrichten zuständig ist, lautet also wie folgt:
//+------------------------------------------------------------------+ //| 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); }
Senden wir nun die Kontoinformationen in Segmenten, die mit Emoji-Zeichen verkettet sind, an Telegram und sehen wir uns die Antwort an. Hierfür wird die gleiche Codestruktur verwendet, aber wir werden kurz erklären, was wir genau tun.
////--- 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);
Wir beginnen mit dem Zugriff auf das Kapital und die freie Marge des Kontos, indem wir AccountInfoDouble verwenden und die Argumente ACCOUNT_EQUITY bzw. ACCOUNT_MARGIN_FREE übergeben. Diese rufen das Kontokapital und die freie Marge in Echtzeit ab. Wir erstellen eine ausführliche Nachricht, „complex_msg“, in der wir das Symbol des Charts zusammen mit dem Eigenkapital und der freien Marge des Kontos verwenden und diese mit Emojis formatieren. Wir senden die Nachricht über die Telegram-API, aber zuerst müssen wir sicherstellen, dass sie sicher über HTTP übertragen werden kann. Dazu kodieren wir die Nachricht mit der Funktion „UrlEncode“. Nachdem wir die Nachricht gesendet haben, sehen wir dies in der Telegram-App.
Sie können sehen, dass wir die komplexe Nachricht erfolgreich in Telegram empfangen haben. Damit sind wir mit den Funktionen zum Versenden von Nachrichten vollständig fertig. Es liegt auf der Hand, dass wir durch die Kapselung der früheren Funktionalität in einer Funktion den Code sauberer machen und es ermöglichen, dass der Prozess des Nachrichtenversands mühelos an mehreren Stellen wiederverwendet werden kann. Dies wird sich als besonders nützlich erweisen, wenn wir den Code weiter modularisieren, z. B. beim Hinzufügen von Funktionen zum Senden von Bildern und zur Fehlerbehandlung. Dies geschieht im nächsten Abschnitt.
Modularisierung der -Funktionen für die Screenshots
Hier müssen wir eine Funktion konstruieren, die die notwendigen Parameter annimmt und die Chartbilder an Telegram sendet. Seine Codestruktur ist identisch mit derjenigen, die wir für das Senden verschlüsselter Nachrichten verwendet haben.
int sendScreenShot(string screenshot_name,const string telegram_url, const string bot_token,const string chat_id, string caption=""){ //... }
Wir deklarieren eine Integer-Funktion mit dem Namen „sendScreenShot“, was bedeutet, dass sie einen Integer-Wert zurückgeben wird. Die Funktion nimmt mehrere Parameter auf, was Flexibilität und Modularität gewährleistet.
- Der Parameter „screenshot_name“ bezieht sich auf den Namen der Screenshot-Datei, die gesendet wird, sodass verschiedene Screenshots angegeben werden können.
- „telegram_url“, „bot_token“ und „chat_id“ sind die Haupteingaben, die für die Kommunikation mit der Telegram-API erforderlich sind, wodurch die Funktion an verschiedene Bot-Konfigurationen und Telegram-Konten angepasst werden kann.
- Ein optionaler Parameter, „caption", ermöglicht es uns, dem Screenshot einen beschreibenden Text beizufügen und so die Funktionalität zu erweitern, indem wir die Screenshots vor dem Senden mit Anmerkungen versehen können.
Da die gleiche Codestruktur beibehalten wird, konzentrieren wir uns nur auf die Rückkehrlogik. Die erste wird in der Instanz sein, in der wir versuchen, den Bildinhalt zu öffnen und zu lesen.
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); }
Wenn die an Telegram zu sendenden Screenshot-Daten nicht geöffnet werden können, gibt die Funktion „FAILED_CODE“ zurück und signalisiert damit, dass der Vorgang zum Senden des Screenshots aufgrund eines Dateizugriffsproblems nicht ausgeführt werden kann. Als Nächstes übernehmen wir einfach die gleiche Logik zur Überprüfung des Antwortstatus und der entsprechenden Meldungsprotokolle und Statuscodes wie unten:
// 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);
Die vollständige Funktion, die für das Senden von Screenshot-Dateien zuständig ist, wird im Folgenden beschrieben:
//+------------------------------------------------------------------+ //| 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); }
Wir haben jetzt die Funktion, ein Screenshot zu senden, aber wir haben keine Funktion, um die Screenshot-Dateien zu erhalten. Als erstes werden wir eine Funktion erstellen, die die Bilddatei des Charts abruft, an das das Programm angehängt ist, da sie nicht viele Parameter benötigt.
//+------------------------------------------------------------------+ //| 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); }
Hier deklarieren wir eine Integer-Funktion, „getScreenshot_of_Current_Chart“, um ein Screenshot des aktuellen Charts in MetaTrader 5 zu erstellen und zu speichern. Die Funktion benötigt einen Parameter, „screenshot_name“, der den gewünschten Namen der Datei enthält, in der das Screenshot gespeichert werden soll. Wir beginnen die Funktion mit der Überprüfung, ob bereits eine Datei mit dem Namen „screenshot_name“ existiert. Ist dies der Fall, löschen wir die bereits vorhandene Datei. Dieser Schritt ist von entscheidender Bedeutung, denn wenn wir die bereits vorhandene Datei nicht löschen würden, käme es unweigerlich zu einem Überschreibungsproblem - eine Situation, in der ein gespeicherter Screenshot denselben Namen hätte wie eine Datei, die erst kürzlich gelöscht wurde.
Wir rufen auch die Funktion ChartRedraw auf, um die Anzeige des Charts zu aktualisieren, bevor wir den Screenshot aufnehmen. Um ein Screenshot des Charts in seinem aktuellen Zustand zu erhalten, rufen wir die Funktion ChartScreenShot auf und teilen ihr den Namen, den wir der Datei geben wollen, die gewünschten Abmessungen und die gewünschte Ausrichtung mit. Danach mussten wir eine while-Schleife verwenden, um zu prüfen, ob die Datei vorhanden ist. Wir haben bis zu 30 Sekunden gewartet, bis die Datei erschien, bevor wir zum nächsten Schritt übergingen, und wir wollten auf keinen Fall den Prozess verlangsamen, um im nächsten Schritt ein Screenshot zu erhalten.
Sollte die Datei nach diesem Intervall immer noch nicht existieren, wird eine Fehlermeldung ausgegeben, die besagt, dass der Screenshot nicht gespeichert wurde, und ein eindeutiger „FAILED_CODE“ zurückgegeben. Wenn die Datei jedoch gefunden wird, geben wir eine Erfolgsmeldung aus und liefern einen eindeutigen „SUCCEEDED_CODE“. Im Wesentlichen lassen wir zwei mögliche Ergebnisse für unseren Vorgang zu und kennzeichnen sie eindeutig. Die Funktion zum Öffnen eines nutzerdefinierten Charts und zum Erstellen eines Schnappschusses folgt der gleichen Logik.
//+------------------------------------------------------------------+ //| 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); }
Hier deklarieren wir eine Integer-Funktion, die 3 Parameter benötigt: den Bildnamen, den Symbolnamen und den Zeitraum des zu öffnenden Charts. Bis zu diesem Punkt haben wir die Funktionen, die wir brauchen, um Screenshots zu erhalten und sie an Telegram zu senden. Lassen Sie uns nun ein Screenshot des aktuellen Charts erstellen und versenden und sehen, was wir erhalten. Um dies zu erreichen, wird der folgende Codeabschnitt verwendet.
getScreenshot_of_Current_Chart(SCREENSHOT_FILE_NAME);
sendScreenShot(SCREENSHOT_FILE_NAME,TG_API_URL,botTkn,chatID,NULL);
Hier rufen wir lediglich die beiden Funktionen auf, die für das Abrufen bzw. Senden des gespeicherten Screenshots zuständig sind, und übergeben die erforderlichen Eingabeparameter. Sie können sehen, wie klein die Logik jetzt ist. Nur zwei Zeilen Code genügen, um Wunder zu vollbringen. Nach dem Kompilieren erhalten wir die folgende Ausgabe.
Wir können sehen, dass wir die Screenshot-Datei des aktuellen Charts im Telegram-Chat empfangen können. Bis zu diesem Punkt ist fast alles Notwendige abgedeckt, und so können wir sehen, wie die Rückgabecodes verwendet werden können, um das Scheitern oder den Erfolg der Funktionen zu ermitteln. Für diese Übung werden wir die Logik des neuen Charts verwenden.
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); }
Hier verwalten wir die Ausgabe der Funktion „getScreenshot_of_New_Chart“. Mit dieser Funktion können Sie ein Screenshot des neuen Charts erstellen und speichern. Wir rufen die Funktion mit klaren Parametern auf: den Namen, den wir für die Screenshot-Datei haben wollen, das aktuelle Symbol des Charts und den Zeitrahmen. Das Ergebnis der Funktion wird in einer Variablen namens „get_screenshot_new_chart_result“ gespeichert. Wenn dieses Ergebnis ein Erfolg ist, gehen wir zum nächsten Teil unseres Programms über. Wenn es scheitert, gehen wir mit dem Fehler so um, wie es sinnvoll erscheint.
Wenn wir das Ergebnis „FAILED_CODE“ erhalten, bedeutet dies, dass das Speichern des Screenshots fehlgeschlagen ist. In diesem Fall generieren wir eine Fehlermeldung, die dem Nutzer deutlich macht, dass beim Speichern der Screenshots etwas schief gelaufen ist. Diese Nachricht wird auf dem Terminal ausgedruckt und mit der Funktion „sendSimpleMessage“ auch an unseren Telegram-Chat gesendet. Wir geben dann „INIT_FAILED“ als Rückgabewert zurück, um dem Nutzer mitzuteilen, dass der Vorgang nicht erfolgreich war und dass der nächste Vorgang gar nicht erst versucht werden sollte. Damit wird der Initialisierungsprozess beendet.
Lautet das Ergebnis hingegen „SUCCEEDED_CODE“, bedeutet dies, dass der Screenshot erfolgreich gespeichert wurde. Wir bereiten eine Meldung vor und drucken sie aus, die besagt, dass das Terminal einen erfolgreichen Befehl gesendet hat, nachdem der Screenshot aufgenommen wurde. Anschließend verwenden wir die Funktion „sendSimpleMessage“, um den Nutzer darüber zu informieren, dass sein Screenshot gespeichert wurde und er die Datei bald erhalten soll. Der Prozess der Übermittlung der Nachricht an den Nutzer ist klar, präzise und ordnungsgemäß ausgeführt. Der Befehl zum Senden des Screenshots an den Nutzer war erfolgreich, und er sollte die Datei in etwa 10 Sekunden erhalten. Für das Journal erhalten wir das folgende Protokoll:
Im Telegram-Chat erhalten wir die folgende Ausgabe:
Sie können sehen, dass es jetzt einfach ist, mehrere Instanzen von Nachrichten an Telegram zu senden, um mühelos zu chatten. Jetzt müssen wir nur noch die Funktion für die Logik des Screenshot-Versands aufrufen, um die Bilddatei zu übermitteln. Dies wird durch die nachstehende Logik erreicht:
sendScreenShot(SCREENSHOT_FILE_NAME,TG_API_URL,botTkn,chatID,NULL);
Wenn wir den Code ausführen, erhalten wir die folgenden Ergebnisse:
Das war ein Erfolg. Sie können feststellen, dass das Bild, das wir erhalten, keine Legende hat. Das liegt daran, dass wir für das Feld „caption“ für die Legende den Wert NULL gewählt haben, was bedeutet, dass keine Legende berücksichtigt wird. Um die Legende einzubinden, müssen wir nur das Legendenfeld definieren und es an die Funktion übergeben. Für die Abbildung wird unsere Standardlegende wie unten dargestellt verwendet:
//--- 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);
Nach dem Kompilieren erhalten wir ein Screenshot des neu geöffneten, nutzerdefinierten Charts mit den Standardparametern wie beschrieben, aber vor allem mit einer Legende, die das Symbol des Screenshots, den Zeitrahmen und die Zeit, zu der er aufgenommen und an Telegram übermittelt wurde, zeigt.
Das war ein Erfolg. Wir haben jetzt ein voll funktionsfähiges Programm, das Funktionen zur Kommunikation mit dem Telegram-Chat nutzt. Jetzt müssen wir nur noch einige Tests durchführen und die modularen Funktionen implementieren, die wir entwickelt haben, um Handelssignale auf der Grundlage von gleitenden Durchschnittsübergängen von der MetaTrader 5-Plattform an Telegram-Chats zu senden. Dies soll im nächsten Abschnitt ausdrücklich behandelt werden.
Testen und Implementieren modularer Funktionen
In diesem Abschnitt verlagern wir den Schwerpunkt von der Konstruktion einzelner Funktionen auf die Anwendung dieser Funktionen in echten Handelssituationen, in denen Signalbestätigungen bestimmte Reaktionen hervorrufen. Unser Ziel ist es nun, zu überprüfen, ob die Funktionen, die wir modularisiert haben - wie z. B. die Funktionen für die Aufnahme von Screenshots oder das Senden von Nachrichten - in dem größeren Framework, das wir aufbauen, zusammenarbeiten werden. Indem wir die Logik unseres Expert Advisors (EA) in wiederverwendbare Funktionen packen, können wir den Zweck des Versendens von Chart-Screenshots oder des Aktualisierens von Kontoständen besser erfüllen, und zwar auf eine Art und Weise, die die Logik des EA beibehält - ein EA, der nach dem Prinzip arbeitet, bestimmte Funktionen auszulösen, wenn bestimmte Bedingungen erfüllt sind.
In diesem Thema wird gezeigt, wie wir diese modularen Funktionen aufrufen, wenn Handelssignale bestätigt werden, um sicherzustellen, dass alle Komponenten in realen Szenarien effizient arbeiten. Wir werden die Zuverlässigkeit dieser Funktionen rigoros prüfen, sie durch wiederholte Ausführungen laufen lassen und sehen, ob sie den Schwierigkeiten des Fehlermanagements standhalten können, während sie weiterhin ihre primäre Aufgabe erfüllen, Signalbestätigungsnachrichten genau und zeitnah an Telegram zu senden. Dabei werden wir nicht nur die Funktionalität unseres Codes überprüfen, sondern auch einen Schritt in Richtung eines Systems zur Verwaltung von Handelssignalen machen, das fast vollständig robust ist und Fehlerzustände zuverlässig behandelt. Jetzt müssen wir also nur noch den Initialisierungscode in den Abschnitt zur Signalerzeugung verschieben. Für ein Kaufsignal sieht der Codeabschnitt wie folgt aus:
// 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);
Hier beschränken wir uns darauf, über das generierte Signal zu informieren und ein Screenshot des aktuellen Charts zu senden, der die Handelsstufen zeigt. Für die Bestätigung eines Verkaufssignals wird eine ähnliche Logik wie folgt angepasst:
// 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);
Um zu bestätigen, dass Signale generiert werden, wechseln wir zu einem 1-Minuten-Zeitrahmen und warten auf Signalreaktionen. Das erste Signal, das wir erhalten, ist ein Verkaufssituation. Sie wird wie unten dargestellt im Handelsterminal generiert:
MetaTrader 5 Verkaufssignal-Protokoll:
Die im Telegram-Chatfeld empfangene Verkaufsnachricht:
Aus den oben gezeigten Bildern ist ersichtlich, dass das Verkaufssignal im Handelsterminal bestätigt wird, wichtige Nachrichten im Journal protokolliert werden und die entsprechenden Positionsdaten und Screenshots an die Telegram-Chatgruppe gesendet werden. Wir erwarten nun, dass wir bei einem steigenden Kreuzen die bestehende Verkaufsposition schließen, eine Kaufposition eröffnen und die Information auch an die Telegram-Gruppe senden. Die Bestätigung des Handelsterminals sieht wie folgt aus:
MetaTrader 5 Kaufsignal-Protokoll:
Kaufnachricht im Telegram-Chatfeld erhalten:
Zur besseren Veranschaulichung des Meilensteins finden Sie hier ein detailliertes Video, das die Leistung des Expert Advisors von seiner Zusammenstellung über seine Initialisierung und die Eröffnung von Geschäften auf der Grundlage der erzeugten Signale bis hin zur Übertragung der Metadaten von MQL5 an Telegram zeigt.
Bis zu diesem Punkt können wir sehen, dass die Modularisierung des Funktionscodes ein Erfolg ist. Die Integration von MQL5 und Telegram funktioniert jetzt reibungslos. Mithilfe modularer Funktionen, die wir zur Bestätigung von Signalen herangezogen haben, konnten wir die Nachrichtenübermittlung und das Erstellen von Screenshots automatisieren, sodass jedes wichtige Ereignis oder Update ähnlich wie Dana White in den Telegram-Chat „geflascht“ wird, sobald es eintritt. Und wir haben es so oft getestet, dass wir überzeugt sind, dass es funktioniert. In Bezug auf die Funktion als Brücke zwischen MQL5 und Telegram ist diese Implementierung sowohl zuverlässig als auch flexibel - ein gutes modulares Design, auf dem wir später für komplexere Integrationen aufbauen können.
Schlussfolgerung
Dieser Artikel beschreibt die Integration von MetaQuotes Language 5 (MQL5) mit Telegram, wobei der Schwerpunkt auf der Erstellung modularer Funktionen für das Senden von Nachrichten und den Handel mit Chart-Screenshots liegt. Der modulare Aufbau erhöht die Effizienz, Skalierbarkeit und Wartbarkeit des Expert Advisors (EA) ohne unnötige Komplexität.
Der nächste Artikel befasst sich mit einem Zwei-Wege-Kommunikationssystem, das es Telegram ermöglicht, Befehle an MetaTrader 5 zu senden und die Aktionen des EAs zu steuern. Diese Integration ermöglicht fortschrittlichere Interaktionen, wie z. B. die Abfrage von Live-Handelsdaten oder Screenshots direkt von Telegram, wodurch die Grenzen der MQL5-Telegram-Integration verschoben werden. Bleiben Sie dran für weitere Entwicklungen.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/15706





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.
Das Senden von Nachrichten funktioniert, aber sendPhoto funktioniert nicht, warum?
Vielen Dank für Ihre Mühe und Freundlichkeit zu teilen, ist dieser Artikel-Serie so nützlich für mich und andere. Wünsche Ihnen am besten von friedlichen, Gesundheit und Reichtum.