从 MQL5 向 Discord 发送消息,创建 Discord-MetaTrader 5 机器人·进阶篇
📘

从 MQL5 向 Discord 发送消息,创建 Discord-MetaTrader 5 机器人·进阶篇

第 2/3 篇

「把交易信号推到 Discord 的代码片段」

在 MT5 里把策略告警外发到 Discord,最省事的做法是用 Webhook 对象直接 SendMessage。下面这段演示了如何塞代码块和表情符号,开 MT5 新建 EA 把 discord 指针接好就能跑。 发消息时若要在 Discord 里高亮代码,要在三个反引号后写语言名。例如 MQL5 块里顺手放了指针释放逻辑:CheckPointer(discord)!=POINTER_INVALID 时 delete discord,避免内存泄漏;Python 块则只是 while(True): break 的占位。 discord.SendMessage(":rotating_light: Trade Alert!"); 这行会推一条带警灯 emoji 的纯文本,适合做裸告警。 为了不在代码里到处写裸字符串,用 #define 把常用 emoji 映射成常量更干净。原文列了 15 个宏,从 :rocket: 到 :blush: 都有,覆盖趋势、风险、情绪三类提示。复制下面宏定义进头文件,之后发消息拼 DISCORD_EMOJI_CHART_UP 比手写字符串不易拼错。 外汇与贵金属波动剧烈,这类推送只解决通知延迟,不替你过滤假突破,实盘前先在模拟盘验证 Webhook 可达性。

MQL5 / C++
discord.SendMessage("```MQL5\n" class=class="str">"cmt">//we put the type of language for markdown highlighters after three consecutive backticks
                     "if (CheckPointer(discord)!=POINTER_INVALID)\n"
                     "  class="kw">delete discord;\n"
                     "```");

   discord.SendMessage("```Python\n"
                     "while(True):\n"
                     "  class="kw">break\n"
                     "```");
discord.SendMessage(":rotating_light: Trade Alert!");
class="macro">#define DISCORD_EMOJI_ROCKET                ":rocket:"
class="macro">#define DISCORD_EMOJI_CHART_UP              ":chart_with_upwards_trend:"
class="macro">#define DISCORD_EMOJI_CHART_DOWN            ":chart_with_downwards_trend:"
class="macro">#define DISCORD_EMOJI_BAR_CHART             ":bar_chart:"
class="macro">#define DISCORD_EMOJI_BOMB                  ":bomb:"
class="macro">#define DISCORD_EMOJI_THUMBS_UP             ":thumbsup:"
class="macro">#define DISCORD_EMOJI_THUMBS_DOWN           ":thumbsdown:"
class="macro">#define DISCORD_EMOJI_WARNING               ":warning:"
class="macro">#define DISCORD_EMOJI_JOY                   ":joy:"
class="macro">#define DISCORD_EMOJI_SOB                   ":sob:"
class="macro">#define DISCORD_EMOJI_SMILE                 ":smile:"
class="macro">#define DISCORD_EMOJI_FIRE                  ":fire:"
class="macro">#define DISCORD_EMOJI_STAR                  ":star:"
class="macro">#define DISCORD_EMOJI_BLUSH                 ":blush:"

把状态写成 Emoji 宏

在 MT5 里给 Discord 推信号,最烦的是每条消息手敲表情符号。用预处理器一次性把 Discord 的 emoji 短码定义成宏,后面发消息直接拼常量,可读性立刻上来。 上面这组宏覆盖了从 :thinking: 到 :keycap_ten: 共 18 个基础符号,外加红绿圆点,足够标记「思考中 / 报警 / 通过 / 序号」等 EA 运行状态。 实际调用时直接字符串拼接:discord.SendMessage(DISCORD_EMOJI_ROTATING_LIGHT " Trade Alert! ") 就会在频道里先弹旋转警灯再出文字。外汇与贵金属波动剧烈,这类推送只作状态同步,任何信号都不构成入场保证,爆仓风险自担。 顺手补一句,@everyone 和 @here 的 mention 语法在 Discord webhook 里照样生效,但群里人多时慎用,容易刷屏被踢。

MQL5 / C++
class="macro">#define DISCORD_EMOJI_THINKING                ":thinking:"
class="macro">#define DISCORD_EMOJI_ROTATING_LIGHT            ":rotating_light:"
class="macro">#define DISCORD_EMOJI_X                         ":x:"
class="macro">#define DISCORD_EMOJI_WHITE_CHECK_MARK          ":white_check_mark:"
class="macro">#define DISCORD_EMOJI_BALLOT_BOX_WITH_CHECK     ":ballot_box_with_check:"
class="macro">#define DISCORD_EMOJI_HASH                      ":hash:"
class="macro">#define DISCORD_EMOJI_ASTERISK                  ":asterisk:"
class="macro">#define DISCORD_EMOJI_ZERO                      ":zero:"
class="macro">#define DISCORD_EMOJI_ONE                       ":one:"
class="macro">#define DISCORD_EMOJI_TWO                       ":two:"
class="macro">#define DISCORD_EMOJI_THREE                     ":three:"
class="macro">#define DISCORD_EMOJI_FOUR                      ":four:"
class="macro">#define DISCORD_EMOJI_FIVE                      ":five:"
class="macro">#define DISCORD_EMOJI_SIX                       ":six:"
class="macro">#define DISCORD_EMOJI_SEVEN                     ":seven:"
class="macro">#define DISCORD_EMOJI_EIGHT                     ":eight:"
class="macro">#define DISCORD_EMOJI_NINE                      ":nine:"
class="macro">#define DISCORD_EMOJI_TEN                       ":keycap_ten:"

class="macro">#define DISCORD_EMOJI_RED_CIRCLE                ":red_circle:"
class="macro">#define DISCORD_EMOJI_GREEN_CIRCLE              ":green_circle:"
discord.SendMessage(DISCORD_EMOJI_ROTATING_LIGHT " Trade Alert! " DISCORD_EMOJI_JOY);
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 最实用的一个点:它不锁死默认昵称和头像,每次发请求都能临时改。多个策略机器人共用同一个 webhook 往一个频道推信号时,靠不同身份区分来源,比事后翻日志省事得多。 我们把身份字段 m_name 和 m_avatar_url 声明为 public,原因很直接——用户可能在类初始化之后还想改昵称或换头像,公开才能随时访问。构造函数里把传入的 name 和 avatar_url 直接赋给成员,并顺手用 logging.Config(m_name) 把日志标签也绑上,后续排查某个用户名发了什么就清晰了。 SendMessage() 里拼 JSON 时,把 "username" 和 "avatar_url" 写死进模板,值取自 m_name / m_avatar_url。这样不传原始 JSON 的普通调用也会自动带身份。 注意 SendJSON() 应把这些字段设为可选:当用户想完全自己控制 payload 时,就别强行塞用户名。外汇与贵金属信号推送涉及高风险,身份伪装仅用于区分策略,不代表任何胜率保证,开 MT5 挂 EA 前先确认 webhook 地址没写错。

MQL5 / C++
class CDiscord
  {
class="kw">protected:
class=class="str">"cmt">//...
class=class="str">"cmt">//...

class="kw">public:
                      class="type">class="kw">string m_name;
                      class="type">class="kw">string m_avatar_url;

                      CDiscord(const class="type">class="kw">string webhook_url,
                               const class="type">class="kw">string name,
                               const class="type">class="kw">string avatar_url,
                               const class="type">class="kw">string headers="Content-Type: application/json; charset=utf-class="num">8",
                               const class="type">uint timeout=class="num">10000);
}
CDiscord::CDiscord(const class="type">class="kw">string webhook_url,
                    const class="type">class="kw">string name,
                    const class="type">class="kw">string avatar_url,
                    const class="type">class="kw">string headers="Content-Type: application/json; charset=utf-class="num">8",
                    const class="type">uint timeout=class="num">10000):

m_webhook_url(webhook_url),
m_headers(headers),
m_timeout(timeout),
m_name(name),
m_avatar_url(avatar_url)
{
class=class="str">"cmt">//--- Initialize the logger
   logging.Config(m_name);
   if (!logging.init())
      class="kw">return;

   logging.info("Initialized",MQLInfoString(MQL_PROGRAM_NAME),__LINE__);
}
class="type">bool CDiscord::SendMessage(const class="type">class="kw">string message)
{
   class="type">class="kw">string raw_json = StringFormat("{"
                              "\"username\": \"%s\","
                              "\"avatar_url\": \"%s\","
                              "\"content\": \"%s\""
                              "}",
                              m_name, m_avatar_url, message);

「给 Discord 推送换上自定义身份」

把交易信号丢到 Discord 并不只是发一段文本,EA 初始化时给 Webhook 绑定用户名和头像,能让多条策略在群里一眼区分开。下面这段 MQL5 在 OnInit 里 new 了一个 CDiscord 对象,构造参数依次塞入 webhook 地址、机器人名和头像链接,随后调 SendMessage 自报身份。 CDiscord 的 SendJSON 方法里有个 use_id 开关:为真时把类构造时存的 m_name 和 m_avatar_url 写进 JSON 的 username 与 avatar_url 字段,再序列化 POST 出去;关掉它就只发你传进去的裸 raw_json。实测用文内写死的 webhook(ID 1384809399767269527 那段)能直接推到对应频道,换身份不用重建 hook。 在外接消息通道这块,外汇与贵金属行情跳动快、误报成本高,自动化推送只是通知手段,信号本身仍需人工核对,杠杆品种高风险不可忽视。

MQL5 / C++
class="type">class="kw">string json = GetFormattedJson(raw_json); class=class="str">"cmt">//Deserialize & Serialize the message in JSON format

class="kw">return PostRequest(json);
}
class="type">bool CDiscord::SendJSON(const class="type">class="kw">string raw_json, class="type">bool use_id=true)
{
  CJAVal js;

  js.Deserialize(raw_json);

  if (use_id) class=class="str">"cmt">//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;
    }

  class="type">class="kw">string json;
  js.Serialize(json);

  class="kw">return PostRequest(json);
}
class="macro">#include <Discord.mqh>
CDiscord *discord;
input class="type">class="kw">string discord_webhook_url = "https:class=class="str">"cmt">//discord.com/api/webhooks/class="num">1384809399767269527/105Kp27yKnQDpKD01VdEb01GS5P-KH5o5rYKuJb_xD_D8O23GPkGLXGn9pHBB1aOt4wR";
input class="type">class="kw">string avatar_url_ = "https:class=class="str">"cmt">//imgur.com/m7sVf51.jpeg";
input class="type">class="kw">string bots_name = "Signals Bot";
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//---

    discord = new CDiscord(discord_webhook_url,
                            bots_name,
                            avatar_url_);

    discord.SendMessage("Same webhook but, different Identity :smile: cheers!");

class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }

处理嵌入式文件和图像文件

尽管在创建 Discord 类时已将图像附加到上一节中讨论的消息中,但仍有一种正确的方法可以将文件和嵌入项附加到消息中。 我们可以通过独立的 JSON 请求来实现这一点。 结果。 遗憾的是, 尽管 Discord 能够接收文件, 但您无法使用 Web Request 直接从 MQL5 发送图像。  目前,您所能做的最好的事情就是从在线托管文件的某个链接中共享图像。 在之前的例子中,我必须使用 imgur.com 。

MQL5 / C++
discord.SendJSON(
&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"{"</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"\"content\": \"Here&class="macro">#x27;s the latest chart update:\","</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"\"embeds\": ["</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"{"</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"\"title\": \"Chart Update\","</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"\"image\": {"</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"\"url\": \"https:class=class="str">"cmt">//imgur.com/SBsomI7.png\""</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"}"</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"}"</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"]"</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"}"</span>
);

◍ 用 EA 把 MT5 成交推到 Discord 频道

在 MT5 里做外部消息推送,核心钩子是 OnTradeTransaction。它能在每笔交易事务发生时被触发,比轮询持仓省资源,也更适合做实时通知。 开仓、修改、平仓三类动作靠宏区分:DEAL_ENTRY_IN 判为开仓,DEAL_ENTRY_OUT 且非 SL/TP 原因判为手动平仓,TRADE_TRANSACTION_POSITION 则对应止损止盈改动。实测用一键交易手动开两个反向单,Discord 能秒级收到带内联价格的卡片。 Discord 排版有个细节:价格类短值用单个反引号包裹会生成行内代码块,视觉上和数字分隔明显,方便复制;大段内容才用三个反引号。平仓盈利放 👍、亏损放 👎,且已平头寸走交易单号而非持仓编号——手动平掉的那笔,DEAL_REASON 会落为客户端(桌面终端)。 外汇与贵金属杠杆高、滑点跳空频繁,这类通知只解决「知道发生了什么」,不替代风控。把下面宏和函数骨架直接塞进 EA,改下 Webhook 发送段就能在 MT5 跑通验证。

MQL5 / C++
class="macro">#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)
class="macro">#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))
class="macro">#define IS_TRANSACTION_POSITION_MODIFIED(trans.type == TRADE_TRANSACTION_POSITION)
class="type">void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const class="type">MqlTradeRequest& request,
                        const class="type">MqlTradeResult& result)
  {
   class="type">ulong deal_ticket = trans.deal;
   class="type">ulong position_ticket = trans.position;
class=class="str">"cmt">//---
   
   m_deal.Ticket(deal_ticket); class=class="str">"cmt">//select a deal by it&class="macro">#x27;s ticket
   
   if (IS_TRANSACTION_POSITION_OPENED)
     {
      if (deal_ticket==class="num">0)
        class="kw">return;
      
      if (!m_position.SelectByTicket(position_ticket)) class=class="str">"cmt">//select a position by ticket
        class="kw">return;
        
      if (!m_symbol.Name(m_deal.Symbol())) class=class="str">"cmt">//select a symbol from a position
        {
         printf("line=%d Failed to select symbol %s. Error = %s",__LINE__,m_deal.Symbol(),ErrorDescription(GetLastError()));
         class="kw">return;
        }
      
      class="type">class="kw">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"

常见问题

用 Webhook 发 POST 请求即可,核心是把消息内容按 Discord 的 JSON 格式拼好,再调用系统网络函数推送到频道链接。
定义一组 Emoji 宏映射状态值,发送前把状态字段替换成对应符号,再写进消息文本里推送到 Discord。
可以,小布能接管信号格式化与推送流程,你只需在面板里绑定频道,它就会按你的规则把成交和状态发过去。
在 Webhook 的 JSON 里填 username 和 avatar_url 字段,每次发送前按场景改这两个值就能换皮。
先把图表存成图像文件,在嵌入消息的 embeds 里填 image 字段指向文件地址,随成交事件一起 POST 出去。