Discord 接 MetaTrader 5:把交易机器人的实时告警推到手机
多数 EA 只在本机弹窗,你出门就失联;用 Webhook 把执行与行情告警转推 Discord,任意地点都能盯盘。
用 Discord 给 MT5 机器人加实时播报
把 Discord 接进 MetaTrader 5,核心是解决「信号发生」与「人不在电脑前」之间的断层。通过 Webhook,MT5 里的 EA 能在订单成交、止损触发、指标背离的瞬间,把消息推送到你私有的 Discord 频道,延迟通常低于 1 秒(取决于 broker 与网络)。 外汇与贵金属本身属于高杠杆、高波动品种,这类实时通知只解决「知道」,不解决「该不该跟」,误报照样会亏。 实现上不依赖第三方付费桥,MT5 自带的 WebRequest 就能 POST 到 Discord 的 incoming webhook URL。下面这段是最小可用骨架,跑通后你再往里塞自己的交易逻辑。
◍ 用 Discord 给 MT5 加一层远程盯盘
把 Discord 接进 MetaTrader 5,本质是用平台的 WebRequest 能力往外推消息,让你在手机端也能实时看到下单、平仓和异动警报。这套思路不挑品种,外汇和贵金属都跑得通,但两者杠杆高、跳空频繁,远程通知只能帮你盯,不能替你控风险。 我们后面会拿一个随机交易机器人做样例,把「平台侧开权限 → Discord 服务端收消息 → MQL5 里发请求」这条链路走通。你照着配完,至少能在不在电脑前时,靠 Discord 推送知道 EA 有没有动手。 前提是你已经写过 MQL5、知道 EA 和脚本怎么挂。如果连 WebRequest 选项卡都没碰过,先去把 MT5 选项里的「允许 WebRequest」名单搞明白,不然代码写了也发不出去。
「给 MT5 放行 Discord 的 Webhook 地址」
在 MT5 里调用 Discord 提醒,先要在「选项 → 智能交易系统」里把两个 https 地址加进允许 Web 请求的列表,否则 EA 发不出消息。实际配置时,除创建并复制 webhook URL,还得准备一台服务器,或者直接用 Discord 里现成的服务器承接推送。 漏掉 URL 白名单这一步,MT5 终端会在日志里报 403 类网络错误,EA 不会崩但提醒全丢,这种隐性失效比报错更难排查。
把 MT5 的报警推到 Discord 频道
MT5 的 EA 要往 Discord 发消息,核心就是 Webhook URL。注意老教程里写的 discordapp.com 域名已经失效,现在必须换成 discord.com 才能被服务器接受,漏掉这一步会一直收不到推送。 EA 里先放几个 input 参数:机器人名、消息颜色、是否发价格更新。这些不写死在代码里,方便你换账号或换品种时直接改面板,不用动源码。 发送函数最怕两件事:MT5 没开 WebRequest 权限,以及 JSON 里混进未转义的特殊字符。权限在 OnInit 里用 TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) 查,没开就直接 INIT_FAILED,别让 EA 挂着却哑火。 实际发请求时,WebRequest 超时设 5000 毫秒,返回码 200 或 204 才算成功。失败就返回 false,调用处可以写重试或兜底通知。字符数组必须用 CP_UTF8 转换,否则中文和 emoji 会乱码。 Webhook URL 等于半把钥匙,谁拿到都能往你频道灌消息。实盘里别明文写死在 EA 顶部,用加密或外部配置存。Discord 自身有速率限制,慢网络下建议加指数退避的重发队列,别硬撞。
class="macro">#include <Trade/Trade.mqh> CTrade trade; class=class="str">"cmt">// Discord webhook URL - Replace with your webhook URL class="type">class="kw">string discord_webhook = "https:class=class="str">"cmt">//discord.com/api/webhooks/XXXXXXXXXXXXXXXXXXXXXXXXX"; class=class="str">"cmt">// Strategy Parameters class="type">class="kw">string discord_webhook = "https:class=class="str">"cmt">//discord.com/api/webhooks/your-webhook-url"; input group "Discord Settings" input class="type">class="kw">string DiscordBotName = "MT5 Trading Bot"; input class="type">color MessageColor = clrBlue; input class="type">bool SendPriceUpdates = true; class="type">int OnInit() { Print("Initialization step class="num">1: Checking WebRequest permissions..."); if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { Print("Error: WebRequest is not allowed. Please allow in Tool -> Options -> Expert Advisors"); class="kw">return INIT_FAILED; } class="type">class="kw">string test_message = "{\"content\":\"Test message from MT5\"}"; class="type">class="kw">string headers = "Content-Type: application/json\r\n"; class="type">char data[], result[]; ArrayResize(data, StringToCharArray(test_message, data, class="num">0, WHOLE_ARRAY, CP_UTF8) - class="num">1);
◍ 把交易信号推到 Discord 的底层写法
EA 想在 MT5 里把开仓信号实时丢进 Discord,核心就是 WebRequest 打 POST 到 webhook。下面这段初始化里的请求若返回 -1,基本是 MT5 的「允许 URL」名单没加,得手动补上 https://discord.com/* 与 https://discordapp.com/*,否则 EA 会直接 INIT_FAILED 起不来。 int res = WebRequest( "POST", discord_webhook, headers, 5000, data, result, headers ); if(res == -1) { Print("Make sure these URLs are allowed: https://discord.com/*, https://discordapp.com/*"); return INIT_FAILED; } return(INIT_SUCCEEDED); 上面代码逐行拆:第1行声明 res 接收 WebRequest 返回值;POST 方法发往 discord_webhook 地址;headers 带请求头;5000 是超时毫秒数;data 为发出去的包体;result 存响应;若 res 为 -1 说明请求层失败,打印提示并返回初始化失败;否则返回成功。 FormatTradeMessage 负责把交易结构体拼成可读文本,价格与止损用 _Digits 精度、手数固定 2 位。EscapeJSON 必须把反斜杠、双引号、换行等转义,不然 JSON 包会断。SendDiscordMessage 里 res==200 或 204 才认发送成功,并记下 lastMessageTime——这套返回码判断比只看大于0更稳,能避掉 400 类假阳性。
class="type">int res = WebRequest( "POST", discord_webhook, headers, class="num">5000, data, result, headers ); if(res == -class="num">1) { Print("Make sure these URLs are allowed: https:class=class="str">"cmt">//discord.com/*, https://discordapp.com/*"); class="kw">return INIT_FAILED; } class="kw">return(INIT_SUCCEEDED); } class="type">class="kw">string FormatTradeMessage(TradeInfo& tradeInfo) { class="type">class="kw">string message = "**New " + tradeInfo.type + " Signal Alert!**\n"; message += "Symbol: " + tradeInfo.symbol + "\n"; message += "Type: " + tradeInfo.type + "\n"; message += "Price: " + DoubleToString(tradeInfo.price, _Digits) + "\n"; message += "Lots: " + DoubleToString(tradeInfo.lots, class="num">2) + "\n"; message += "Stop Loss: " + DoubleToString(tradeInfo.sl, _Digits) + "\n"; message += "Take Profit: " + DoubleToString(tradeInfo.tp, _Digits) + "\n"; message += "Time: " + TimeToString(TimeCurrent()); class="kw">return message; } class="type">class="kw">string EscapeJSON(class="type">class="kw">string text) { class="type">class="kw">string escaped = text; StringReplace(escaped, "\\", "\\\\"); StringReplace(escaped, "\"", "\\\""); StringReplace(escaped, "\n", "\\n"); StringReplace(escaped, "\r", "\\r"); StringReplace(escaped, "\t", "\\t"); class="kw">return escaped; } class="type">bool SendDiscordMessage(class="type">class="kw">string message, class="type">bool isError = class="kw">false) { if(!isWebRequestEnabled) class="kw">return class="kw">false; message = (isError ? "❌ " : "✅ ") + message; class="type">class="kw">string payload = "{\"content\":\"" + EscapeJSON(message) + "\"}"; class="type">class="kw">string headers = "Content-Type: application/json\r\n"; class="type">char post[], result[]; ArrayResize(post, StringToCharArray(payload, post, class="num">0, WHOLE_ARRAY, CP_UTF8) - class="num">1); class="type">int res = WebRequest( "POST", discord_webhook, headers, class="num">5000, post, result, headers ); if(res == class="num">200 || res == class="num">204) { lastMessageTime = TimeCurrent(); class="kw">return true; }
「随机下单与报价推送的实现骨架」
这段逻辑把「随机策略」落到了可执行的 MQL5 函数里:用 MathRand() 对 2 取模决定多空,再用 SymbolInfoDouble 抓当前 ASK/BID 作为开仓价。SL 和 TP 点数各自在 50~149 点之间随机生成(50 + 0~99),通过 _Point 换算成绝对价格,避免硬编码点位。 PlaceRandomTrade 里先拼好 TradeInfo 结构,调用 FormatTradeMessage 发 Discord 预告;只有 SendDiscordMessage 返回 true 才真正 trade.PositionOpen。成功后会回传订单号,失败则静默——这种「先通知后下单」的顺序,能在 MT5 实盘里帮你确认网络通道是否活着。 SendPriceUpdate 受 SendPriceUpdates 开关控制,且用 TimeCurrent() - lastMessageTime < 300 做了 5 分钟节流,只推送 Bid/Ask/Spread。外汇与贵金属杠杆高、滑点随机,这类自动推送仅供监控,不代表任何方向建议。 OnDeinit 在 EA 停止时把 reason 码发出来,方便你区分是手动卸载还是断线重连。把下面代码直接塞进 EA 的同类文件,改 LotSize 和 magicNumber 就能在策略测试器里跑一遍验证。
class="kw">return class="kw">false; } class="type">void PlaceRandomTrade() { class="type">bool isBuy = (MathRand() % class="num">2) == class="num">1; class="type">class="kw">double price = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID); class="type">int slPoints = class="num">50 + (MathRand() % class="num">100); class="type">int tpPoints = class="num">50 + (MathRand() % class="num">100); class="type">class="kw">double sl = isBuy ? price - slPoints * _Point : price + slPoints * _Point; class="type">class="kw">double tp = isBuy ? price + tpPoints * _Point : price - tpPoints * _Point; TradeInfo tradeInfo; tradeInfo.symbol = _Symbol; tradeInfo.type = isBuy ? "BUY" : "SELL"; tradeInfo.price = price; tradeInfo.lots = LotSize; tradeInfo.sl = sl; tradeInfo.tp = tp; class="type">class="kw">string message = FormatTradeMessage(tradeInfo); if(SendDiscordMessage(message)) { trade.SetExpertMagicNumber(magicNumber); class="type">bool success = isBuy ? trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, LotSize, price, sl, tp, "Random Strategy Trade") : trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, LotSize, price, sl, tp, "Random Strategy Trade"); if(success) { SendDiscordMessage("✅ Trade executed successfully! Ticket: " + IntegerToString(trade.ResultOrder())); } } } class="type">void SendPriceUpdate() { if(!SendPriceUpdates) class="kw">return; if(TimeCurrent() - lastMessageTime < class="num">300) class="kw">return; class="type">class="kw">string message = "```\n"; message += "Price Update for " + _Symbol + "\n"; message += "Bid: " + DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits) + "\n"; message += "Ask: " + DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits) + "\n"; message += "Spread: " + DoubleToString(SymbolInfoInteger(_Symbol, SYMBOL_SPREAD), class="num">0) + " points\n"; message += "```"; SendDiscordMessage(message); } class="type">void OnDeinit(const class="type">int reason) { SendDiscordMessage("```\nEA stopped. Reason code: " + IntegerToString(reason) + "```"); }
把通知链路接进 MT5 才算闭环
Discord 与 MT5 打通后,实时推送、群内沟通和远程盯盘确实能补上手动交易的盲区。但落地不是点几下就完事:Webhook 稳定性、EA 占用资源、密钥泄露风险都得自己压住,外汇和贵金属本身高波动,链路断了可能直接错过信号。 示例文件 Discord_examples.mq5 要丢进 MQL5/Experts/ 才能被 MT5 加载,跑之前先在本机测通网络延迟。后续若加语音播报或 LSTM 过滤警报,也只是把这条通道变得更聪明,根基还是这套自动化对接。 真要验证值不值,开 MT5 挂上 EA,拿模拟账户跑三天,看漏报和误报哪个先让你烦,再决定要不要上实盘。