MQL5からDiscordへのメッセージの送信、Discord-MetaTrader 5ボットの作成
内容
- はじめに
- Discord Webhookを作成する
- MetaTrader 5からDiscordに最初のメッセージを送信する
- MQL5でDiscordクラスを作成する
- DiscordボットのID
- 埋め込みファイルと画像ファイルの操作
- Discordに取引通知を送信する
- 結論
はじめに
私たちはもはやインターネットの黎明期や原始的なデジタル時代にいるわけではありません。現在ではほとんどあらゆるものがインターネットを介して相互接続可能であり、実際に接続するかどうかはそれをおこなう意志と労力次第です。
API (Application Programming Interface)は、異なるソフトウェアアプリケーション同士が通信するための規則やプロトコルの集合体であり、複数のソフトウェアやアプリケーションが容易に接続できるようになりました。その結果として、今日私たちが目にしている高度に接続されたインターネットが実現しています。
提供されるAPIを利用するためには、その規則やプロトコルを遵守する必要があり、さらに(存在する場合には)API提供者が定めるセキュアなアクセス手段を取得しなければなりません。
MetaTrader5と外部アプリケーション間で通信をおこなうことは新しい取り組みではありません。既に、MQL5開発者がWebRequestを利用して情報を送受信するための信頼性あるAPIを提供しているアプリケーションはいくつも存在します。MQL5トレーダーがこうした通信に最もよく活用しているのはTelegramです。。
本記事では、MQL5プログラミング言語を用いてMetaTrader5からDiscordへメッセージや取引情報(シグナル)を送信する方法について掘り下げていきます。
Discord Webhookを作成する
Discordにまだ馴染みのない方のために説明します。
Discordは無料のコミュニケーションプラットフォームで、ユーザーはテキスト、ボイス、ビデオでチャットをしたり、画面を共有したりできます。Discordは友人やコミュニティとの交流に広く利用されており、特にオンラインゲームの分野で人気ですが、読書会やハンドクラフトのサークルなどさまざまなグループでも利用されています。
Telegramと同様に、Discordもユーザー同士がコミュニケーションを取るためのプラットフォームです。両者ともにプラットフォーム外との通信を可能にするAPIを提供していますが、統合性や自動化の観点ではDiscordはTelegramよりはるかに先を進んでいます。
このプラットフォームでは、柔軟なコミュニティを構築したり、さまざまな種類のアプリ(「ボット」)を管理することが可能です。
Discordにボットを作成したり、情報共有やコミュニケーションを自動化する方法は複数ありますが、最も簡単なのはコミュニティ内のチャンネルにWebhookを作成する方法です。以下に作成手順を示します。
![]()
図01
コミュニティから[Server Settings]に移動します。
![]()
図02
Server Settingsから、[Integrations]>[Webhooks]に移動します。
![]()
図03
[New Webhook]ボタンをクリックしてWebhookを作成します。デフォルト名「Captain Hook」のWebhookが作成されますが、名前は自由に変更できます。
![]()
図04
名前を変更したら、このWebhookを適用したいチャンネルを選択できます。このサーバーでは「trading signals」というチャンネルを用意しており、ここにWebhookを適用します(図01参照)。
最後に、[Copy Webhook URL]を選択して、Webhook URLをコピーします。これはMQL5でWebRequestを使う際に利用できるAPIゲートウェイとなります。
![]()
図05
MetaTrader 5からDiscordに最初のメッセージを送信する
最初におこなわなければならないのは、MetaTrader5で許可されたURLリストにhttps://discord.com を追加することです。これを設定しなければ、この後に説明する内容は動作しません。
MetaTrader 5で、[ツール] > [オプション] > [エキスパートアドバイザ]を開き、[WebRequestを許可するURLリスト:]にチェックが入っていることを確認してください。その後、下のフォームにURLを追加します。
Webhook URLを使えば、JSON形式のデータを指定してこのAPIエンドポイントにPOSTリクエストを送信することができます。
ファイル名: Discord EA.mq5
#include <jason.mqh> //https://www.mql5.com/ja/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); }
Discordに送信したいすべての情報やメッセージは、常にJSON形式である必要があります。
JSONオブジェクト内のcontentキーの下にある内容は、Discordに送信されるメッセージの本文を表します。
上記のコードを実行すると、Discordに簡単なメッセージが送信されます。
うまくいきました。しかし、単純なメッセージを送信するだけのためにJSONデータやその他の処理を毎回手作業で扱うのは面倒です。そこで、すべてをクラス化して、毎回技術的な詳細を気にせずに簡単にメッセージを送信できるようにしましょう。
MQL5でDiscordクラスを作成する
標準的な機能を持つクラスにするために、APIエンドポイントや送信リクエスト全体の処理で発生したエラーを追跡できるよう、ログ機能を追加しましょう。
ファイル名: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__); }
POSTリクエストの送信やテキストをJSON形式に変換する処理など、繰り返し発生する処理をクラス内にまとめます。
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; }
以下は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); }
使い方は次の通りです。
ファイル名: 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!"); }
このシンプルで強力な関数は、さまざまな種類のメッセージを送信するために利用できます。titlehttps://support.discord.com/hc/en-us/articles/210298617-Markdown-Text-101-Chat-Formatting-Bold-Italic-UnderlinetitleDiscordはMarkdown形式のテキストをサポートしている ためです。
Markdown形式メッセージの例
discord.SendMessage( "# h1 header\n" "## h2 header\n" "### h3 header\n\n" "**bold text** normal text\n" "[MQL5](https://www.mql5.com)\n" "" );
以下が出力です。
Discordが提供するMarkdownテキストエディタでできることは、これだけではありません。ここでは、Discordでコードを共有する方法を見てみましょう。
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" "```");
以下が出力です。
これは、Markdownテキストエディタの強力さを示す例でした。
メッセージに絵文字を追加する
絵文字はテキストメッセージにおいて非常に便利です。メッセージに華やかさを加え、可読性を向上させるだけでなく、ちょっとしたユーモアも演出できます。
他のコミュニケーションプラットフォームでは、絵文字は特定の数値コードから生成されますが、Discordでは独自の構文を用いて、テキストメッセージ内で直接絵文字を識別、表示できます。
絵文字の名前を2つのコロン(:)で囲む方法は簡単です。絵文字名の前後にコロンを1つずつ付けるだけです。
このようにコードをテキストメッセージに挿入すると、Discord上では絵文字として表示されます。
discord.SendMessage(":rotating_light: Trade Alert!"); 以下がメッセージです。
Discordには何千もの絵文字があり、すべてを把握するのは困難です。こちらのチートシートを活用すると便利です。 私はいくつかの絵文字コードと構文をまとめた簡単なライブラリファイルを作成しました。必要に応じて、自由に絵文字コードを追加してください。
ファイル名:discord 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:"
メッセージを送信します。
discord.SendMessage(DISCORD_EMOJI_ROTATING_LIGHT " Trade Alert! " DISCORD_EMOJI_JOY); 以下がメッセージです。
ユーザーやロールへのメンション
このシンプルなメッセージ機能では、メンションやロールを扱うことができ、効果的なメッセージ配信には欠かせません。
- @everyone:チャンネル内のすべてのユーザーに通知します。オフラインのユーザーも含まれます。
- @here:チャット内のすべての非アイドル状態のユーザーに通知します。オンラインユーザー全員です。
- <@user_id>:特定のユーザーに通知します。
- <@&role_id>:特定のロールに割り当てられたすべてのユーザーに通知します。たとえば、裁量トレーダーのロールを持つすべてのユーザーに取引シグナルを送信する場合などです。
以下が使用例です。
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");
以下がメッセージです。
Discordボットのアイデンティティ
Webhookの利点の一つは、デフォルトの表示名やアバターに縛られず、POSTリクエストを送信するたびにこれらの値を変更できる点です。
この機能は非常に便利です。複数のトレーディングロボットが同じWebhookを使用してコミュニティに情報を送信する場合でも、異なるアイデンティティを使うことで送信者や受信メッセージを区別することができます。
さらに、Discordクラスが呼び出されるたび(初期化されるたび)にこのアイデンティティを必須とするように設定すれば、アイデンティティをグローバルに管理することも可能です。
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); }
これらの変数はpublicとして宣言します。ユーザーがクラスの初期化後にこれらを変更したりアクセスしたりする可能性があるためです。
こうすることで、このDiscordメッセンジャーに割り当てられたユーザー名ごとのログも簡単に追跡できます。
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__); }
次に、このアイデンティティ(名前とアバター)の情報を、JSON文字列が使用されているすべての箇所に追加する必要があります。
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); }
SendJSON()関数内では、このアイデンティティをオプション扱いにする必要があります。ユーザーは最初から別の方法を考えている場合もあり、送信したい情報を自由に指定できる生のJSON文字列を使う関数を選択することもあるためです。
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); }
ボットに別のIDを設定しましょう。
#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); }
以下が結果です。
埋め込みファイルと画像ファイルの操作
前のセクションでDiscordクラスを作成する際にメッセージを添付する方法を説明しましたが、アイテムを埋め込む適切な方法があります。
これは独立したJSONリクエストで実現できます。
discord.SendJSON( "{" "\"content\": \"Here's the latest chart update:\"," "\"embeds\": [" "{" "\"title\": \"Chart Update\"," "\"image\": {" "\"url\": \"https://imgur.com/SBsomI7.png\"" "}" "}" "]" "}" );
以下は結果です。
残念ながら、Discordはファイルの受信に対応していますが、MQL5からWebRequestを使って直接画像を送信することはできません。 現状、最も現実的な方法は、オンライン上にホストされているファイルのリンクを共有することです。
前の例ではimgur.comを使用しました。
MetaTrader 5からDiscordに取引通知を送信する
トレーダーは、MetaTrader5から外部プラットフォームへ情報を送信する機能を利用して、自分の取引活動に関するアップデートを送信することがよくあります。そのようなタスクのためにこのボット(エキスパートアドバイザー)を活用します。
取引開始の通知から始めます。
01:取引開始通知の送信
このタスクにはOnTradeTransaction関数が便利です。
以下は、最近のポジションと取引の状態を確認するための条件チェッカーです。
#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 //... }
注意:
エントリープライス、ストップロス、テイクプロフィットなどの市場価格をフォーマットする際には、それぞれの数値を単一のバッククォート(`)で囲んでいます。これにより、Discord上でインラインコードブロックとして表示され、数値が視覚的に区切られ、ユーザーがコピーしやすくなります。
Discordでは以下の形式を使用します。
- 単一のバッククォート(`)はインラインコードのフォーマットに使用します。短い数値や価格に適しています。
- 三重バッククォート(```)は複数行のコードブロックに使用します。大きなコードや長いメッセージに適しています。
MetaTrader 5のワンクリック取引機能を使用して、手動で2つの逆方向の取引を開きました。以下がその結果です。
02:取引変更通知の送信
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); }
このメッセージの内容は、取引通知を送信する際のものとすべて同じです。唯一の違いは、ストップロスとテイクプロフィットの項目名が変更されている点です。
03:取引終了通知の送信
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); }
決済取引のメッセージでは、利益で決済された場合はサムズアップの絵文字を、損失で決済された場合はサムズダウンの絵文字を付けます。これは、ポジションの新規建てや変更時にポジションチケット番号を使っていたのとは異なります。決済済みのポジションはもはやポジションではなく「取引」となるため、決済では取引チケット([Deal Ticket])番号を使用します。
今回の取引は手動で決済されたため、その理由はClient (Desktop Terminal)となります。これは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"; } }
結論
Discord Webhookは、MetaTrader 5とDiscordを統合するための良い出発点であり、これら2つの強力なプラットフォーム間でのコミュニケーションを可能にします。しかし、前述した通り、すべてのAPIにはルールと制限があります。Discord Webhook APIも例外ではなく、Webhookリクエストにはレート制限があります。
- 1つのWebhookでは、2秒ごとに最大5件のメッセージを送信可能
- 1つのWebhookでは、チャンネルごとに最大30件のメッセージ/分まで送信可能
この制限を超えると、Discordは「HTTP 429 (Too Many Requests)」を返します。この問題に対処するためには、MQL5コード内で送信に遅延やキューを追加する方法があります。
また、この記事全体で「Discordボット」についても触れていますが、WebhookはDiscordボットとは大きく異なります。私がMQL5とDiscord間のワークフロー全体を指して「ボット」と呼んでいるのはこのためです。
ボットとは、Discord内で動作するプログラムで、基本的なコマンドから複雑な自動化まで幅広い機能を提供します。一方、Webhookは単純なURLで、外部アプリケーションから特定のDiscordチャンネルに自動でメッセージを送信するために使われます。
以下に、Discord WebhookとDiscordボットの違いを表形式でまとめました。
| Webhook | ボット | |
|---|---|---|
| 機能 |
|
|
| カスタマイズ |
|
|
| 負荷とセキュリティ |
|
|
Discordボットは複雑で、MQL5プログラマーにとっては実用的ではありません。通常は、取引の進捗状況を他のトレーダーに通知する機能があれば十分だからです。ボットを作成するにはPythonのモジュールもいくつか必要になります。
とはいえ、MetaTrader 5と連携可能な本格的なDiscordボットを開発することも可能です。
MetaTrader 5用Pythonパッケージを使用すれば、Python環境から両プラットフォーム間の完全統合を実現できます。
ご一読、誠にありがとうございました。
添付ファイルの表
| ファイル名 | 説明と使用法 |
|---|---|
| Include\discord emojis.mqh | Discordと互換性のある絵文字コードと構文 |
| Include\discord.mqh | DiscordプラットフォームにJSON形式でメッセージを送信するためのCDiscordクラス |
| Include\errordescription.mqh | MetaTrader 5が生成するすべてのエラーコードの説明を含むファイル(MQL5言語用) |
| Include\jason.mqh | JSONプロトコルのシリアル化とデシリアル化のためのライブラリ |
| Include\logging.mqh | CDiscordクラス(Webhook送信元)によって生成されたすべての情報とエラーをログに記録するためのライブラリ |
| Experts\Discord EA.mq5 | Discord Webhookをテストし、割り当てられたDiscord Webhook URLに取引アラートを送信するためのEA |
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/18550
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
MQL5で自己最適化エキスパートアドバイザーを構築する(第8回):複数戦略分析(2)
データサイエンスとML(第44回):ベクトル自己回帰(VAR)を用いた外国為替OHLC時系列予測
MQL5での取引戦略の自動化(第20回):CCIとAOを使用した多銘柄戦略
プライスアクション分析ツールキットの開発(第28回):Opening Range Breakout Tool
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索












