English Русский 日本語
preview
Senden von Nachrichten von MQL5 an Discord, Erstellen eines Discord-Bots für MetaTrader 5

Senden von Nachrichten von MQL5 an Discord, Erstellen eines Discord-Bots für MetaTrader 5

MetaTrader 5Integration |
13 0
Omega J Msigwa
Omega J Msigwa

Inhalt


Einführung

Wir leben nicht mehr in den Anfängen des Internets und des digitalen Urzeitalters; heute kann fast alles mit allem im Internet verbunden werden, es kommt nur darauf an, dass man bereit ist, die nötige Arbeit zu leisten.

Das Vorhandensein von APIs (Application Programming Interface), d. h. einer Reihe von Regeln und Protokollen, die es verschiedenen Softwareanwendungen ermöglichen, miteinander zu kommunizieren, hat es für mehr als eine Software oder Anwendung einfacher gemacht, sich mit einer anderen zu verbinden, und hat somit zu dem am meisten vernetzten Internet geführt, das wir heute sehen.

Um eine bereitgestellte API zu nutzen, müssen Sie sich an die Regeln und Protokolle halten, und ganz zu schweigen davon, dass Ihnen der API-Anbieter einen sicheren Zugang (falls vorhanden) gewähren muss.

Die Kommunikation zwischen MetaTrader 5 und externen Anwendungen ist nicht neu; sie wurde bereits bei mehreren Anwendungen durchgeführt, die zuverlässige API(s) anbieten, die MQL5-Entwickler zum Senden und Empfangen von Informationen über Web Requests verwenden. Die häufigste App, die MQL5-Händler für diese Kommunikation verwenden, ist Telegram.

In diesem Artikel werden wir besprechen, wie wir Nachrichten und Handelsinformationen (Signale) vom MetaTrader 5 an Discord senden können, indem wir die Programmiersprache MQL5 verwenden.


Erstellen eines Discord-Webhooks

Für diejenigen, die mit noch nicht vertraut sind:

Discord ist eine kostenlose Kommunikationsplattform, die es Nutzern ermöglicht, über Text, Sprache und Video zu chatten und Bildschirme gemeinsam zu nutzen. Es ist beliebt, um sich mit Freunden und Gemeinschaften zu verbinden, insbesondere im Bereich der Online-Spiele, wird aber auch für verschiedene andere Gruppen wie Buchclubs oder Nähkreise verwendet.

Ähnlich wie bei Telegram handelt es sich um eine Kommunikationsplattform, die es den Nutzern ermöglicht, miteinander zu kommunizieren. Beide Plattformen bieten APIs, die die Kommunikation auch außerhalb ihrer Plattformen ermöglichen, aber Discord ist Telegram weit voraus, wenn es um die Integration und Automatisierung geht.

Diese Plattform ermöglicht es den Nutzern, flexible Gemeinschaften zu schaffen und verschiedene Arten von Apps (auch als Bots bekannt) zu verwalten.

Es gibt verschiedene Möglichkeiten, Bots zu erstellen und die Kommunikation und den Prozess der Informationsweitergabe an Discord zu automatisieren, aber die einfachste Möglichkeit ist die Verwendung eines Webhooks in einem der Kanäle Ihrer Community. Hier erfahren Sie, wie Sie eine solche erstellen.

 

Abbildung 0.

Gehen Sie in Ihrer Community zu den Servereinstellungen.

 

Abbildung 02.

Gehen Sie in den Servereinstellungen zu Integrationen > Webhooks.

 

Abbildung 03.

Klicken Sie auf die Schaltfläche New Webhook, um einen Webhook zu erstellen. Es wird ein Webhook mit dem Standardnamen Captain Hook erstellt. Sie können diesen Namen nach Belieben ändern.

 

Abbildung 04.

Nachdem Sie den Namen geändert haben, können Sie den Kanal auswählen, auf den Sie diesen Webhook anwenden möchten. In diesem Server habe ich einen Kanal namens Handelssignale, auf den ich diesen Webhook anwenden möchte. Siehe Abbildung 01.

Schließlich müssen wir noch die Webhook-URL kopieren. Dies ist unser API-Gateway, das wir mit Web Requests in MQL5 verwenden können.

 

Abbildung 05.



Senden der ersten Nachricht von MetaTrader 5 an Discord

Als erstes müssen wir https://discord.com in die Liste der erlaubten URLs in MetaTrader 5 aufnehmen, da sonst alles, was wir danach besprechen werden, nicht funktionieren wird.

In MetaTrader 5 gehen Sie zu Extras > Options > Expert Advisors > WebRequest für folgende URLs erlauben, aktivieren es und fügen Sie dann die URL in das unten stehende Fenster ein.

Mit der Webhook-URL können wir eine POST-Anfrage an diesen API-Endpunkt mit JSON-formatierten Daten senden.

Dateiname: Diskurs EA.mq5 

#include <jason.mqh> //https://www.mql5.com/en/code/13663

string discord_webhook_url = "https://discord.com/api/webhooks/1384809399767269527/105Kp27yKnQDpKD01VdEb01GS5P-KH5o5rYKuJb_xD_D8O23GPkGLXGn9pHBB1aOt4wR";
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
     
   CJAVal js(NULL, jtUNDEF); //Creating a json object
   
   string raw_json = "{\"content\": \"Hello world from MetaTrader5!\"}";
   
   bool b = js.Deserialize(raw_json); //Parse JSON string into a structured object
   
   string json;
   js.Serialize(json); //Convert the object to a valid JSON string
   
//--- Sending a Post webrequest

   char data[]; 
   ArrayResize(data, StringToCharArray(json, data, 0, StringLen(json))); //--- serialize to string
   
//--- send data

   char res_data[]; //For storing the body of the response
   string res_headers=NULL; //For storing response headers (if needed)
   
   string headers="Content-Type: application/json; charset=utf-8";
   uint timeout=10000; //10 seconds timeout for the HTTP request
   
   int ret = WebRequest("POST", 
                         discord_webhook_url, 
                         headers, 
                         timeout, 
                         data, 
                         res_data, 
                         res_headers); //Send a post request
   
   if (ret==-1)
     {
       printf("func=%s line=%d, Failed to send a webrequest. Error = %s",__FUNCTION__,__LINE__,ErrorDescription(GetLastError()));
       return false;
     }
   
//--- Check if the post request was successful or not

   if (ret==204)
     {
       if (MQLInfoInteger(MQL_DEBUG))
         Print("Message sent to discord successfully");
     }
   else
     {
       printf("Failed to send message to discord. Json response Error = %s",CharArrayToString(res_data));
     }
  
//---
   return(INIT_SUCCEEDED);
  }

Alle Informationen und Nachrichten, die Sie an Discord senden möchten, müssen immer im JSON-Format vorliegen.

Alles, was unter dem Schlüssel content in einem JSON-Objekt steht, stellt den Haupttext der an Discord gesendeten Nachricht dar.

Die Ausführung des obigen Codes sendet eine einfache Nachricht an Discord.

Fantastisch! Das war jedoch ein mühsamer Prozess, um die JSON-Daten und alles andere zu handhaben, nur um eine einfache Nachricht an Discord zu senden. Daher sollten wir alles in eine Klasse packen, um das Senden von Nachrichten zu vereinfachen, ohne sich jedes Mal um die technischen Details kümmern zu müssen.


Erstellen einer Discord-Klasse in MQL5

Um sicherzustellen, dass wir über eine Standardfunktionalität verfügen, fügen wir eine Protokollierung hinzu, um die vom API-Endpunkt erzeugten Fehler und den gesamten Prozess des Sendens von Anfragen zu verfolgen.

Dateiname: Discord.mqh.

#include <errordescription.mqh>
#include <logging.mqh>
#include <jason.mqh>

class CDiscord
  {
protected:
   string m_webhook_url;
   string m_headers;
   uint m_timeout;
   CLogging logging;
   
   bool PostRequest(const string json);
   string GetFormattedJson(const string raw_json);
   
public:
                     CDiscord(const string webhook_url, const string headers="Content-Type: application/json; charset=utf-8", const uint timeout=10000);
                    ~CDiscord(void);
                     
                     bool SendMessage(const string message);
                     bool SendEmbeds(const string title, const string description_, color clr, const string footer_text, const datetime time);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CDiscord::CDiscord(const string webhook_url, string headers="Content-Type: application/json; charset=utf-8", uint timeout=10000):
 m_webhook_url(webhook_url),
 m_headers(headers),
 m_timeout(timeout)
 {
//--- Initialize the logger

   logging.Config("Discord server");
   if (!logging.init())
      return;
      
   logging.info("Initialized",MQLInfoString(MQL_PROGRAM_NAME),__LINE__);
 }

Wir verpacken die sich wiederholenden Prozesse zum Senden von POST-Anfragen und zum Konvertieren von Texten in das JSON-Format.

string CDiscord::GetFormattedJson(const string raw_json)
 {
   CJAVal js(NULL, jtUNDEF);
   bool b = js.Deserialize(raw_json);
   
   string json;
   js.Serialize(json); 
   
   return json;
 }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CDiscord::PostRequest(const string json)
 {
   char res[];
    
//--- serialize to string

   char data[]; 
   ArrayResize(data, StringToCharArray(json, data, 0, StringLen(json)));
   
//--- send data

   char res_data[];
   string res_headers=NULL;

   int ret = WebRequest("POST", m_webhook_url, m_headers, m_timeout, data, res_data, res_headers); //Send a post request
   
   if (ret==-1)
     {
       printf("func=%s line=%d, Failed to send a webrequest. Error = %s",__FUNCTION__,__LINE__,ErrorDescription(GetLastError()));
       return false;
     }
   
//--- Check if the post request was successful or not

   if (ret==204)
     {
       if (MQLInfoInteger(MQL_DEBUG))
         Print("Message sent to discord successfully");
         logging.info("Message sent to discord successfully",MQLInfoString(MQL_PROGRAM_NAME), __LINE__);
     }
   else
     {
       printf("Failed to send message to discord. Json response Error = %s",CharArrayToString(res_data));
       logging.error(CharArrayToString(res_data), MQLInfoString(MQL_PROGRAM_NAME), __LINE__);
     }

  return true;
 }

Im Folgenden finden Sie eine einfache Funktion zum Senden von Nachrichten an Discord.

bool CDiscord::SendMessage(const string message)
 {
   string raw_json = StringFormat("{\"content\": \"%s\"}",message);   
   string json = GetFormattedJson(raw_json); //Deserialize & Serialize the message in JSON format
   
   return PostRequest(json);
 }

So wird sie verwendet.

Dateiname: Discord EA.mq5 

#include <Discord.mqh>
CDiscord *discord;

string discord_webhook_url = "https://discord.com/api/webhooks/1384809399767269527/105Kp27yKnQDpKD01VdEb01GS5P-KH5o5rYKuJb_xD_D8O23GPkGLXGn9pHBB1aOt4wR";
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
     
    discord = new CDiscord(discord_webhook_url);  
    discord.SendMessage("Hello from MetaTrader5!");
  }

Diese einfache und leistungsstarke Funktion kann zum Versenden von Nachrichten verschiedener Art verwendet werden, da Discord einen in Markdown formatierten Text verwendet.

Beispiel Markdown-Nachricht

    discord.SendMessage(
                         "# h1 header\n"
                         "## h2 header\n"
                         "### h3 header\n\n"
                         "**bold text** normal text\n"
                         "[MQL5](https://www.mql5.com)\n"
                         "![image](https://i.imgur.com/4M34hi2.png)"
                       );

Ausgabe:

Und das ist noch nicht einmal das Coolste, was man mit dem von Discord bereitgestellten Markdown-Texteditor machen kann. Lassen Sie uns einige Codezeilen mit Discord austauschen.

    discord.SendMessage("```MQL5\n" //we put the type of language for markdown highlighters after three consecutive backticks
                        "if (CheckPointer(discord)!=POINTER_INVALID)\n"
                        "  delete discord;\n"
                        "```");
                        
    discord.SendMessage("```Python\n"
                        "while(True):\n"
                        "  break\n"
                        "```");

Ausgabe:

Damit wollte ich Ihnen zeigen, wie leistungsfähig Markdown-Texteditoren sind.

Hinzufügen von Emojis zu Ihren Nachrichten

Emojis sind in Textnachrichten sehr nützlich; sie verleihen ihnen Eleganz, verbessern die Lesbarkeit, und nicht zuletzt sorgen sie für eine Prise Humor.

Im Gegensatz zu anderen Kommunikationsplattformen mit Emojis, die aus bestimmten numerischen Codes bestehen, verwendet Discord eine einzigartige Syntax, um Emojis direkt in Textnachrichten zu identifizieren und darzustellen.

Alles, was Sie tun müssen, ist, den Namen des Emoji zwischen zwei Doppelpunkte zu setzen – einen Doppelpunkt am Anfang und den anderen am Ende des Emoji-Namens.

Wenn die Codes dieser Emojis in eine Textnachricht eingefügt werden, werden sie in Discord als Emojis dargestellt.

discord.SendMessage(":rotating_light: Trade Alert!");

Nachricht.

Es gibt Tausende von Emojis, und es ist schwierig, den Überblick über sie alle zu behalten. Hier ist ein Spickzettel: https://github.com/ikatyang/emoji-cheat-sheet/blob/master/README.md. Ich habe eine einfache Bibliotheksdatei mit einigen der Emoji-Codes und der Syntax erstellt. Bitte zögern Sie nicht, weitere Emoji-Codes hinzuzufügen, wenn es Ihnen gefällt.

Dateiname: diskord emojis.mqh

#define DISCORD_EMOJI_ROCKET                   ":rocket:"               
#define DISCORD_EMOJI_CHART_UP                 ":chart_with_upwards_trend:"   
#define DISCORD_EMOJI_CHART_DOWN               ":chart_with_downwards_trend:" 
#define DISCORD_EMOJI_BAR_CHART                ":bar_chart:"            
#define DISCORD_EMOJI_BOMB                     ":bomb:"                 
#define DISCORD_EMOJI_THUMBS_UP                ":thumbsup:"             
#define DISCORD_EMOJI_THUMBS_DOWN              ":thumbsdown:"           
#define DISCORD_EMOJI_WARNING                  ":warning:"              
#define DISCORD_EMOJI_JOY                      ":joy:"                  
#define DISCORD_EMOJI_SOB                      ":sob:"                  
#define DISCORD_EMOJI_SMILE                    ":smile:"                
#define DISCORD_EMOJI_FIRE                     ":fire:"                 
#define DISCORD_EMOJI_STAR                     ":star:"                 
#define DISCORD_EMOJI_BLUSH                    ":blush:"                
#define DISCORD_EMOJI_THINKING                 ":thinking:"             
#define DISCORD_EMOJI_ROTATING_LIGHT           ":rotating_light:"
#define DISCORD_EMOJI_X                        ":x:"
#define DISCORD_EMOJI_WHITE_CHECK_MARK         ":white_check_mark:"
#define DISCORD_EMOJI_BALLOT_BOX_WITH_CHECK    ":ballot_box_with_check:"

#define DISCORD_EMOJI_HASH                     ":hash:"               
#define DISCORD_EMOJI_ASTERISK                 ":asterisk:"           

#define DISCORD_EMOJI_ZERO                     ":zero:"               
#define DISCORD_EMOJI_ONE                      ":one:"                
#define DISCORD_EMOJI_TWO                      ":two:"                
#define DISCORD_EMOJI_THREE                    ":three:"              
#define DISCORD_EMOJI_FOUR                     ":four:"               
#define DISCORD_EMOJI_FIVE                     ":five:"               
#define DISCORD_EMOJI_SIX                      ":six:"                
#define DISCORD_EMOJI_SEVEN                    ":seven:"              
#define DISCORD_EMOJI_EIGHT                    ":eight:"              
#define DISCORD_EMOJI_NINE                     ":nine:"               
#define DISCORD_EMOJI_TEN                      ":keycap_ten:"       
  
#define DISCORD_EMOJI_RED_CIRCLE               ":red_circle:"               
#define DISCORD_EMOJI_GREEN_CIRCLE             ":green_circle:"       

Eine Nachricht senden.

discord.SendMessage(DISCORD_EMOJI_ROTATING_LIGHT " Trade Alert! " DISCORD_EMOJI_JOY);

Ergebnis der Nachricht.

Erwähnen von Nutzern und Rollen

Diese einfache Nachricht kann mit Erwähnungen und Rollen umgehen, die für eine wirksame Nachrichtenübermittlung entscheidend sind.

  • @everyone – Damit werden alle Nutzer im Kanal benachrichtigt, auch wenn sie offline sind.
  • @here – Dies benachrichtigt alle aktuell aktiven Nutzer im Chat. Alle Nutzer, die online sind.
  • <@user_id> – Dies benachrichtigt einen bestimmten Nutzer oder mehrere Nutzer.
  • <@&role_id> Damit werden alle Nutzer benachrichtigt, die einer bestimmten Rolle zugeordnet sind. Zum Beispiel das Senden von Handelssignalen an alle Nutzer, denen die Rolle eines manuellen Händlers zugewiesen wurde.

Beispiel für die Verwendung.

discord.SendMessage("@everyone This is an information about a trading account");
discord.SendMessage("@here This is a quick trading signals for all of you that are active");

Das Ergebnis einer Nachricht.


Die Identität des Discord-Bots

Das Gute an einem Webhook ist, dass er nicht auf das standardmäßige Erscheinungsbild (Name und Avatar) beschränkt ist; Sie können diese Werte jederzeit ändern, wenn Sie eine Postanforderung senden.

Diese Fähigkeit ist sehr praktisch, da Sie mehrere Handelsroboter haben können, die eine Art von Informationen an eine oder zwei Gemeinschaften senden, die sich alle einen Webhook teilen. Die Möglichkeit, eine unterschiedliche Identität zu verwenden, hilft dabei, die Absender und empfangenen Nachrichten zu unterscheiden.

Wir können diese Identität jedes Mal, wenn die Discord-Klasse aufgerufen (initiiert) wird, zu einer Notwendigkeit machen, um die Identität global zu machen.

class CDiscord
  {
protected:

//...
//...
   
public:
                     string m_name;
                     string m_avatar_url;
                     
                     CDiscord(const string webhook_url, 
                              const string name, 
                              const string avatar_url, 
                              const string headers="Content-Type: application/json; charset=utf-8", 
                              const uint timeout=10000);
 }

Wir deklarieren diese Variablen öffentlich, da ein Nutzer sie möglicherweise ändern oder auf sie zugreifen möchte (nach der Initialisierung der Klasse).

Das macht es noch einfacher, die Protokolle für jeden Nutzernamen zu verfolgen, der diesem Discord-Messenger zugewiesen wird.

CDiscord::CDiscord(const string webhook_url, 
                   const string name, 
                   const string avatar_url, 
                   const string headers="Content-Type: application/json; charset=utf-8", 
                   const uint timeout=10000):
                   
 m_webhook_url(webhook_url),
 m_headers(headers),
 m_timeout(timeout),
 m_name(name),
 m_avatar_url(avatar_url)
 {
//--- Initialize the logger

   logging.Config(m_name);
   if (!logging.init())
      return;
      
   logging.info("Initialized",MQLInfoString(MQL_PROGRAM_NAME),__LINE__);
 }

Nun müssen wir diese Identitätsinformationen (Name und Avatar) überall dort anfügen, wo ein JSON-String gefunden wird.

bool CDiscord::SendMessage(const string message)
 {
   string raw_json = StringFormat("{"
                                    "\"username\": \"%s\","
                                    "\"avatar_url\": \"%s\","
                                    "\"content\": \"%s\""
                                  "}",
                                  m_name, m_avatar_url, message);   
                                  
   string json = GetFormattedJson(raw_json); //Deserialize & Serialize the message in JSON format
   
   return PostRequest(json);
 }

In der Funktion SendJSON() müssen wir diese Identität optional machen, weil ein Nutzer eine andere Vorstellung haben könnte. Deshalb hat er sich für eine Funktion entschieden, die einen rohen JSON-String annimmt, der ihm die Kontrolle darüber gibt, welche Informationen er senden möchte.

bool CDiscord::SendJSON(const string raw_json, bool use_id=true)
 {
   CJAVal js;                                 
   
   js.Deserialize(raw_json);
   
   if (use_id) //if a decides to use the ids assigned to the class constructor, we append that information to the json object
     {
       js["username"] = m_name;
       js["avatar_url"] = m_avatar_url;
     }
     
   string json;
   js.Serialize(json);
   
   return PostRequest(json);
 }

Lassen Sie uns eine andere Identität für unseren Bot festlegen.

#include <Discord.mqh>
CDiscord *discord;

input string discord_webhook_url = "https://discord.com/api/webhooks/1384809399767269527/105Kp27yKnQDpKD01VdEb01GS5P-KH5o5rYKuJb_xD_D8O23GPkGLXGn9pHBB1aOt4wR";
input string avatar_url_ = "https://imgur.com/m7sVf51.jpeg";
input string bots_name = "Signals Bot";
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
     
    discord = new CDiscord(discord_webhook_url,
                           bots_name,
                           avatar_url_);  
    
    discord.SendMessage("Same webhook but, different Identity :smile: cheers!");
            
//---
   return(INIT_SUCCEEDED);
  }

Das Ergebnis.


Arbeiten mit Einbettungen und Bilddateien

Auch wenn Sie das Bild an die Nachricht angehängt haben, die im vorherigen Abschnitt beim Erstellen einer Discord-Klasse besprochen wurde, gibt es einen richtigen Weg, um Dateien anzuhängen und Elemente in eine Nachricht einzubetten.

Wir können dies mit einer unabhängigen JSON-Anfrage erreichen.

discord.SendJSON(
    "{"
        "\"content\": \"Here's the latest chart update:\","
        "\"embeds\": ["
            "{"
                "\"title\": \"Chart Update\","
                "\"image\": {"
                    "\"url\": \"https://imgur.com/SBsomI7.png\""
                "}"
            "}"
        "]"
    "}"
);

Ergebnisse.

Leider ist es nicht möglich, ein Bild direkt von MQL5 über Web Request zu senden, obwohl Discord die Dateien empfangen kann.  Das Beste, was Sie derzeit tun können, ist, ein Bild über einen Link weiterzugeben, über den die Datei online gehostet wird.

Im vorherigen Beispiel musste ich imgur.com verwenden.


Senden von Handelsbenachrichtigungen von MetaTrader 5 an Discord

Händler nutzen häufig die Möglichkeit, Informationen von MetaTrader 5 an externe Plattformen zu senden, um Updates über ihre Handelsaktivitäten zu übermitteln. Lassen Sie uns diesen Bot (Expert Advisor) für eine solche Aufgabe verwenden.

Angefangen bei den Benachrichtigungen zur Eröffnung eines Handelsgeschäfts.

01: Senden von Benachrichtigungen über die Eröffnung von Handelsgeschäften

Die Funktion OnTradeTransaction ist für diese Aufgabe sehr nützlich.

Nachfolgend finden Sie Konditionsprüfer, mit denen Sie den Zustand einer aktuellen Position und eines Geschäfts überprüfen können.

#define IS_TRANSACTION_POSITION_OPENED         (trans.type == TRADE_TRANSACTION_DEAL_ADD && HistoryDealSelect(trans.deal) && (ENUM_DEAL_ENTRY)HistoryDealGetInteger(trans.deal, DEAL_ENTRY) == DEAL_ENTRY_IN)
#define IS_TRANSACTION_POSITION_CLOSED         (trans.type == TRADE_TRANSACTION_DEAL_ADD && HistoryDealSelect(trans.deal) && (ENUM_DEAL_ENTRY)HistoryDealGetInteger(trans.deal, DEAL_ENTRY) == DEAL_ENTRY_OUT && ((ENUM_DEAL_REASON)HistoryDealGetInteger(trans.deal, DEAL_REASON) != DEAL_REASON_SL && (ENUM_DEAL_REASON)HistoryDealGetInteger(trans.deal, DEAL_REASON) != DEAL_REASON_TP))
#define IS_TRANSACTION_POSITION_MODIFIED       (trans.type == TRADE_TRANSACTION_POSITION)
void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
  {
     ulong deal_ticket = trans.deal;
     ulong position_ticket = trans.position;

//---
     
     m_deal.Ticket(deal_ticket); //select a deal by it's ticket
     
     if (IS_TRANSACTION_POSITION_OPENED)
       {
         if (deal_ticket==0)
           return;
          
         if (!m_position.SelectByTicket(position_ticket)) //select a position by ticket
            return;
              
         if (!m_symbol.Name(m_deal.Symbol())) //select a symbol from a position
           {
             printf("line=%d Failed to select symbol %s. Error = %s",__LINE__,m_deal.Symbol(),ErrorDescription(GetLastError()));
             return;
           }
         
         string message = 
            DISCORD_EMOJI_ROTATING_LIGHT " **TRADE OPENED ALERT** \n\n"
            "- **Symbol:**"+m_position.Symbol()+"\n"
            "- **Trade Type:** "+ (m_position.PositionType()==POSITION_TYPE_BUY ? DISCORD_EMOJI_GREEN_CIRCLE: DISCORD_EMOJI_RED_CIRCLE) +" "+m_position.TypeDescription()+"\n"
            "- **Entry Price:** `"+DoubleToString(m_deal.Price(), m_symbol.Digits())+"`\n" 
            "- **Stop Loss:** `"+DoubleToString(m_position.StopLoss(), m_symbol.Digits())+"`\n"
            "- **Take Profit:** `"+DoubleToString(m_position.TakeProfit(), m_symbol.Digits())+"`\n"
            "- **Time UTC:** `"+TimeToString(TimeGMT())+"`\n"
            "- **Position Ticket:** `"+(string)position_ticket+"`\n"
            "> "DISCORD_EMOJI_WARNING" *Risk what you can afford to lose.*";
               
         discord.SendMessage(message);
       }

  //...
  //... Other lines of code
  //...     
 }

Anmerkung:

Bei der Formatierung von Marktwerten wie Einstiegskurs, Stop-Loss und Take-Profit usw. umschließe ich jeden mit einem einzelnen Backtick (`). Dadurch wird in Discord ein Inline-Code-Block erzeugt, der die Zahlen optisch voneinander trennt und das Kopieren für die Nutzer erleichtert.

Verwenden von Discord.

  • Ein einzelner Backtick (`) zur Formatierung von Inline-Code – ideal für kurze Werte wie Preise.
  • Dreifache Backticks (```) zur Formatierung von mehrzeiligen Codeblöcken – wird für größere Codeblöcke oder Nachrichten verwendet.

Ich habe zwei gegensätzliche Handelsgeschäfte manuell mit dem One-Click-Trading in MetaTrader 5 eröffnet, hier das Ergebnis.

02: Versenden von Benachrichtigungen über Handelsänderungen

if (IS_TRANSACTION_POSITION_MODIFIED)
  {
    if (!m_position.SelectByTicket(position_ticket))
      {
        printf("Failed to modify a position. Erorr = %s",ErrorDescription(GetLastError()));
        return;
      }
              
    if (!m_symbol.Name(m_deal.Symbol()))
      {
        printf("line=%d Failed to select symbol %s. Error = %s",__LINE__,m_deal.Symbol(),ErrorDescription(GetLastError()));
        return;
      }
         
    string message = 
       DISCORD_EMOJI_BAR_CHART " **TRADE MODIFIED ALERT** \n\n"
       "- **Symbol:** `"+m_position.Symbol()+"`\n"
       "- **Trade Type:** "+ (m_position.PositionType()==POSITION_TYPE_BUY ? DISCORD_EMOJI_GREEN_CIRCLE: DISCORD_EMOJI_RED_CIRCLE) +" "+m_position.TypeDescription()+"\n"
       "- **Entry Price:** `"+DoubleToString(m_position.PriceOpen(), m_symbol.Digits())+"`\n" 
       "- **New Stop Loss:** `"+DoubleToString(m_position.StopLoss(), m_symbol.Digits())+"`\n"
       "- **New Take Profit:** `"+DoubleToString(m_position.TakeProfit(), m_symbol.Digits())+"`\n"
       "- **Time UTC:** `"+TimeToString(TimeGMT())+"`\n"
       "- **Position Ticket:** `"+(string)position_ticket+"`\n"
       "> "DISCORD_EMOJI_WARNING" *Risk what you can afford to lose.*";
            
    discord.SendMessage(message);      
  }

In dieser Nachricht ist alles gleich wie bei den Handelsbenachrichtigungen, nur die Namen der Felder für Stop Loss und Take Profit wurden geändert.

03: Senden von Benachrichtigungen über den Abschluss von Handelsgeschäften

if (IS_TRANSACTION_POSITION_CLOSED)
   {         
    if (!m_symbol.Name(m_deal.Symbol()))
      {
        printf("line=%d Failed to select symbol %s. Error = %s",__LINE__,m_deal.Symbol(),ErrorDescription(GetLastError()));
        return;
      }
         
    m_symbol.RefreshRates(); //Get recent ask and bid prices
         
    long reason_integer;
    m_deal.InfoInteger(DEAL_REASON, reason_integer);
         
    string reason_text = GetDealReasonText(reason_integer);
            
    string message = 
          DISCORD_EMOJI_X " **TRADE CLOSED ALERT**\n\n"
          "- **Symbol:** `" + m_deal.Symbol() + "`\n"
          "- **Trade Type:** " + (m_position.PositionType() == POSITION_TYPE_BUY ? DISCORD_EMOJI_GREEN_CIRCLE : DISCORD_EMOJI_RED_CIRCLE) + " " + m_position.TypeDescription() + "\n"
          "- **Entry Price:** `" + DoubleToString(m_deal.Price(), m_symbol.Digits()) + "`\n"
          "- **Exit Price:** `" + (m_position.PositionType() == POSITION_TYPE_BUY ? (DoubleToString(m_symbol.Bid(), m_symbol.Digits())) : (DoubleToString(m_symbol.Ask(), m_symbol.Digits()))) + "`\n"
          "- **Profit:** " + (m_deal.Profit() >= 0 ? DISCORD_EMOJI_THUMBS_UP : DISCORD_EMOJI_THUMBS_DOWN) + " `" + DoubleToString(m_deal.Profit(), 2) + "`\n"
          "- **Close Reason:** `" + reason_text+ "`\n"
          "- **Commission:** `" + DoubleToString(m_position.Commission(), 2) + "`\n"
          "- **Swap:** `" + DoubleToString(m_position.Swap(), 2)+ "`\n"
          "- **Time (UTC):** `" + TimeToString(TimeGMT()) + "`\n"
          "- **Deal Ticket:** `" + string(deal_ticket) + "`\n"
          "> "DISCORD_EMOJI_WARNING" *Risk what you can afford to lose.*";
               
    discord.SendMessage(message);
  }

In der Nachricht über den Abschluss eines Handelsgeschäfts wird ein Emoji mit dem Daumen nach oben angezeigt, wenn das Handelsgeschäft mit Gewinn abgeschlossen wurde, und ein Emoji mit dem Daumen nach unten, wenn es mit Verlust abgeschlossen wurde. Anders als beim Öffnen und Ändern der Position, wo wir eine Ticketnummer für die Position hatten. Eine geschlossene Position ist keine Position mehr – es handelt sich um ein Handelsgeschäft, daher verwenden wir dafür die Ticketnummer.

Dieses Handelsgeschäft wurde manuell abgeschlossen, daher lautet der Grund Client (Desktop Terminal), dies entspricht ENUM_DEAL_REASON.

string GetDealReasonText(long reason)
{
   switch((int)reason)
   {
      case DEAL_REASON_CLIENT:            return "Client (Desktop Terminal)";
      case DEAL_REASON_MOBILE:            return "Mobile App";
      case DEAL_REASON_WEB:               return "Web Platform";
      case DEAL_REASON_EXPERT:            return "Expert Advisor";
      case DEAL_REASON_SL:                return "Stop Loss Hit";
      case DEAL_REASON_TP:                return "Take Profit Hit";
      case DEAL_REASON_SO:                return "Stop Out (Margin)";
      case DEAL_REASON_ROLLOVER:          return "Rollover Execution";
      case DEAL_REASON_VMARGIN:           return "Variation Margin Charged";
      case DEAL_REASON_SPLIT:             return "Stock Split Adjustment";
      case DEAL_REASON_CORPORATE_ACTION:  return "Corporate Action";
      default:                            return "Unknown Reason";
   }
}


Fazit

Discord Webhooks sind ein guter Ausgangspunkt für die Integration von MetaTrader 5 und Discord und ermöglichen die Kommunikation zwischen diesen beiden leistungsstarken Plattformen. Wie ich jedoch bereits sagte, haben alle APIs Regeln und Einschränkungen – die Discord Webhook-API ist nicht anders, denn Webhook-Anfragen sind begrenzt.

  • Sie können nur 5 Nachrichten alle 2 Sekunden mit einem einzigen Webhook senden.
  • Für einen einzelnen Webhook sind maximal 30 Nachrichten pro Minute und Kanal zulässig.

Bei Überschreitung dieser Begrenzung gibt Discord HTTP 429 (Too Many Requests) zurück. Um dieses Problem zu lösen, können Sie in Ihrem MQL5-Code Verzögerungen oder Warteschlangen hinzufügen.

Außerdem spreche ich in diesem Artikel von Discord-Bots, aber ein Webhook unterscheidet sich sehr von einem Discord-Bot. Der gesamte Arbeitsablauf zwischen MQL5 und Discord ist das, was ich als Bot bezeichne.

Bots sind im Wesentlichen Programme, die innerhalb von Discord laufen und eine breite Palette von Funktionen bieten, von einfachen Befehlen bis hin zu komplexen Automatisierungen. Webhooks hingegen sind einfache URLs, die es externen Anwendungen ermöglichen, automatisierte Nachrichten an einen bestimmten Discord-Kanal zu senden

Im Folgenden wird der Unterschied zwischen einem Discord-Webhook und einem Discord-Bot tabellarisch dargestellt.


Webhooks Bots
Funktion
  • Kann Nachrichten nur an einen bestimmten Kanal senden
  • Sie können nur Nachrichten senden, aber keine anzeigen.
  • Sie können bis zu 10 Einbettungen pro Nachricht senden.

  • Sie sind viel flexibler, da sie komplexere Aktionen durchführen können, die denen eines normalen Nutzers ähneln.
  • Bots können Nachrichten anzeigen und senden.
  • Pro Nachricht ist nur eine Einbettung erlaubt.
Personalisierung
  • Es können 10 Webhooks pro Server erstellt werden, wobei jeder Avatar und Name individuell angepasst werden kann.
  • Hyperlink zu beliebigem Text außerhalb einer Einbettung

  • Öffentliche Bots haben oft einen voreingestellten Avatar und Namen, die von Endnutzern nicht geändert werden können.
  • Kein Hyperlink in einer normalen Nachricht möglich, muss eine Einbettung verwenden
Belastung und Sicherheit
  • Es handelt sich lediglich um einen Endpunkt, an den Daten gesendet werden, es ist kein eigentliches Hosting erforderlich.
  • Keine Authentifizierung, dass die an den Webhook gesendeten Daten aus einer vertrauenswürdigen Quelle stammen.
  • Keine Authentifizierung, dass die an den Webhook gesendeten Daten aus einer vertrauenswürdigen Quelle stammen. Wenn die Webhook-URL durchgesickert ist, können nur nicht dauerhafte Probleme auftreten (z. B. Spamming).
  • Einfache Änderung der Webhook-URL bei Bedarf.

  • Bots müssen in einer sicheren Umgebung gehostet werden, die ständig online gehalten werden muss, was mehr Ressourcen kostet.
  • Bots werden über ein Token authentifiziert; ein kompromittiertes Token kann aufgrund ihrer Fähigkeiten schweren Schaden anrichten, wenn sie über die vom Serverbesitzer gewährten Berechtigungen verfügen.
  • Sie können das Bot-Token jedoch bei Bedarf zurücksetzen.

Ein Discord-Bot ist ziemlich komplex und für MQL5-Programmierer nicht sehr praktisch, da wir oft nur eine Funktion wünschen, um unsere Mithändler über den Handelsfortschritt zu informieren. Ganz zu schweigen davon, dass man zur Erstellung eines Discord-Bots einige Python Module benötigt.

Sie können jedoch diesen vollwertigen Discord-Bot entwickeln, der mit MetaTrader 5 funktioniert, wenn Sie das möchten.

Da wir ein MetaTrader 5-Python-Paket haben, können Sie sich von der Python-Umgebung aus zu einer vollständigen Integration zwischen diesen beiden Plattformen vorarbeiten.

Mit freundlichen Grüßen.


Tabelle der Anhänge

Dateiname Beschreibung und Verwendung
Include\discord emojis.mqh Enthält Emoji-Codes und eine mit Discord kompatible Syntax.
Include\discord.mqh Es enthält die CDiscord-Klasse zum Senden von Nachrichten im JSON-Format an die Discord-Plattform.
Include\errordescription.mqh Enthält Beschreibungen aller von MetaTrader 5 erzeugten Fehlercodes in der Sprache MQL5.
Include\jason.mqh Eine Bibliothek zur Serialisierung und Deserialisierung des JSON-Protokolls.
Include\logging.mqh  Eine Bibliothek zur Protokollierung aller Informationen und Fehler, die von der Klasse CDiscord (Webhook-Sender) erzeugt werden. 
Experts\Discord EA.mq5  Ein Expert Advisor (EA) zum Testen des Discord-Webhooks und zum Senden von Handelsalarmen an die zugewiesene Discord-Webhook-URL. 

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/18550

Beigefügte Dateien |
Attachments.zip (19.26 KB)
Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
Selbstoptimierende Expert Advisors in MQL5 (Teil 8): Analyse mehrerer Strategien (2) Selbstoptimierende Expert Advisors in MQL5 (Teil 8): Analyse mehrerer Strategien (2)
Nehmen Sie an unserer Folgediskussion teil, in der wir unsere ersten beiden Handelsstrategien zu einer Gesamthandelsstrategie zusammenführen werden. Wir werden die verschiedenen Schemata demonstrieren, die für die Kombination mehrerer Strategien möglich sind, und wir werden auch zeigen, wie man den Parameterraum kontrollieren kann, um sicherzustellen, dass eine effektive Optimierung möglich bleibt, selbst wenn unsere Parametergröße wächst.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Vom Neuling zum Experten: Animierte Nachrichten-Schlagzeile mit MQL5 (III) – Indicator Insights Vom Neuling zum Experten: Animierte Nachrichten-Schlagzeile mit MQL5 (III) – Indicator Insights
In diesem Artikel werden wir den News Headline EA weiterentwickeln, indem wir eine spezielle Indikator-Insight-Lane einführen – eine kompakte, auf dem Chart angezeigte Darstellung der wichtigsten technischen Signale, die von beliebten Indikatoren wie RSI, MACD, Stochastic und CCI generiert werden. Dieser Ansatz macht mehrere Unterfenster für Indikatoren auf dem MetaTrader 5-Terminal überflüssig, wodurch Ihr Arbeitsbereich übersichtlich und effizient bleibt. Indem wir die MQL5-API nutzen, um im Hintergrund auf Indikatordaten zuzugreifen, können wir mithilfe einer nutzerdefinierten Logik Markteinblicke in Echtzeit verarbeiten und visualisieren. Erforschen Sie mit uns, wie Sie Indikatordaten in MQL5 manipulieren können, um ein intelligentes und platzsparendes Scrolling Insights System zu erstellen, und das alles auf einer einzigen horizontalen Spur in Ihrem Trading Chart.