从 MQL5 向 Discord 发送消息,创建 Discord-MetaTrader 5 机器人·综合运用
◍ 把持仓变动推到Discord的拼接逻辑
在MT5里做持仓监控,最实用的落点不是画图,而是把开仓、改仓、平仓事件实时丢到外部聊天室。下面这段拼接消息体的写法,直接决定了你手机上收到的警报能不能一眼看懂。
开仓和改仓两块都用m_position对象取数:StopLoss()和TakeProfit()返回的是double,必须经过DoubleToString(val, m_symbol.Digits())按品种精度转字符串,否则黄金(XAUUSD)这类3位小数和欧美5位小数会混成一团。TimeGMT()配合TimeToString()给出UTC时间,避免本地时区把复盘搞乱。
改仓分支里有两个细节容易踩坑:先SelectByTicket(position_ticket)失败就直接return,不往下拼消息;m_symbol.Name(m_deal.Symbol())若选不到品种也会打印__LINE__行号报错。这两处保护能让机器人少发一堆垃圾警报。
平仓分支开头只做了品种选择校验,并调用m_symbol.RefreshRates()刷新ask/bid,说明平仓警报后续还要用实时报价算盈亏,而不是拿历史开仓价硬凑。外汇和贵金属杠杆高,这类推送只是辅助记录,实际风险仍以账户净值波动为准。
class="type">class="kw">string message = "- **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:** `"+(class="type">class="kw">string)position_ticket+"`\n" "> "DISCORD_EMOJI_WARNING" *Risk what you can afford to lose.*"; discord.SendMessage(message); class=class="str">"cmt">//... Other lines of code if (IS_TRANSACTION_POSITION_MODIFIED) { if (!m_position.SelectByTicket(position_ticket)) { printf("Failed to modify a position. Erorr = %s",ErrorDescription(GetLastError())); class="kw">return; } if (!m_symbol.Name(m_deal.Symbol())) { 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_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:** `"+(class="type">class="kw">string)position_ticket+"`\n" "> "DISCORD_EMOJI_WARNING" *Risk what you can afford to lose.*"; discord.SendMessage(message); } 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())); class="kw">return; } m_symbol.RefreshRates(); class=class="str">"cmt">//Get recent ask and bid prices }
把平仓明细拼成一条可推送的消息
在 MT5 的 C++ 风格交易事件里,平仓后第一时间要拿到的不是盈亏数字本身,而是这笔成交的触发来源。用 m_deal.InfoInteger(DEAL_REASON, reason_integer) 把枚举写进 long 变量,再交给 GetDealReasonText() 转成可读文本,才能区分是手动平、EA 平,还是 SL/TP 被打。 下面这段代码直接构造了发往 Discord 的平仓播报串。入场价用 m_deal.Price() 取,出场价则按持仓方向切 Bid 或 Ask——买仓平在 Bid,卖仓平在 Ask,Digits() 控制小数位,贵金属 XAUUSD 通常是 2 位、部分券商 3 位,需以 m_symbol.Digits() 为准。 盈亏行用 m_deal.Profit() >= 0 判断 thumbs 表情,佣金和库存费分别读 m_position.Commission() 与 Swap(),都固定留 2 位小数。时间走 TimeToString(TimeGMT()) 输出 UTC,避免本地时区造成跨设备对账错位。外汇与贵金属杠杆高,播报里那句“Risk what you can afford to lose”不是装饰,是实盘强制留痕的合规底线。 GetDealReasonText() 的 switch 覆盖了 9 种来源:DEAL_REASON_CLIENT 到 ROLLOVER。其中 DEAL_REASON_SO 代表保证金止损出局,和 SL 被打是两回事——前者是账户层面爆仓线触发,后者是订单层面止损。复制时若你的 EA 只关心自动策略成交,可只保留 EXPERT / SL / TP 三个分支做过滤。
class="type">long reason_integer; m_deal.InfoInteger(DEAL_REASON, reason_integer); class="type">class="kw">string reason_text = GetDealReasonText(reason_integer); class="type">class="kw">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() >= class="num">0 ? DISCORD_EMOJI_THUMBS_UP : DISCORD_EMOJI_THUMBS_DOWN) + " `" + DoubleToString(m_deal.Profit(), class="num">2) + "`\n" "- **Close Reason:** `" + reason_text+ "`\n" "- **Commission:** `" + DoubleToString(m_position.Commission(), class="num">2) + "`\n" "- **Swap:** `" + DoubleToString(m_position.Swap(), class="num">2)+ "`\n" "- **Time(UTC):** `" + TimeToString(TimeGMT()) + "`\n" "- **Deal Ticket:** `" + class="type">class="kw">string(deal_ticket) + "`\n" "> "DISCORD_EMOJI_WARNING" *Risk what you can afford to lose.*"; discord.SendMessage(message); } class="type">class="kw">string GetDealReasonText(class="type">long reason) { class="kw">switch((class="type">int)reason) { case DEAL_REASON_CLIENT: class="kw">return "Client(Desktop Terminal)"; case DEAL_REASON_MOBILE: class="kw">return "Mobile App"; case DEAL_REASON_WEB: class="kw">return "Web Platform"; case DEAL_REASON_EXPERT: class="kw">return "Expert Advisor"; case DEAL_REASON_SL: class="kw">return "Stop Loss Hit"; case DEAL_REASON_TP: class="kw">return "Take Profit Hit"; case DEAL_REASON_SO: class="kw">return "Stop Out(Margin)"; case DEAL_REASON_ROLLOVER: class="kw">return "Rollover Execution";
「成交原因枚举的尾段兜底」
上面这段是 DEAL_REASON 枚举解析函数的收尾部分,覆盖了三种特殊成交来源以及默认分支。 DEAL_REASON_VMARGIN 对应变动保证金扣收,多见于跨期或保证金重估场景;DEAL_REASON_SPLIT 是股票拆分调整,DEAL_REASON_CORPORATE_ACTION 为公司行为事件,这两类在外汇与贵金属现货里基本不会触发,但写全能避免日志里出现空字符串。 default 分支直接返回 "Unknown Reason",这是排查异常成交记录时的最后一道标识——若你在 MT5 成交历史里看到这个返回值,说明终端版本或桥接层可能下发了未文档化的 reason 代码,需要抓包核对。 外汇与贵金属交易高风险,这类原因映射只用于诊断,不构成任何方向判断。
case DEAL_REASON_VMARGIN: class="kw">return "Variation Margin Charged"; case DEAL_REASON_SPLIT: class="kw">return "Stock Split Adjustment"; case DEAL_REASON_CORPORATE_ACTION: class="kw">return "Corporate Action"; class="kw">default: class="kw">return "Unknown Reason"; }
◍ 一点提醒
把 MT5 的报警推到 Discord,Webhook 是最轻量的入口,但速率红线不能碰。单条 Webhook 每 2 秒最多发 5 条,单频道每分钟上限 30 条,超限直接吃 HTTP 429。EA 里加个发送队列或延时逻辑,比事后查为什么静默更省事。 Webhook 和机器人不是一回事:前者只是个能 POST 消息的 URL,看不到频道历史,每条消息能带 10 个 embed;后者跑在 Discord 侧、要常驻托管、靠 token 鉴权,权限泄露的破坏面大得多。对只做交易播报的 MQL5 玩家,Webhook 够用,别为了「完整」去养一个 Python 机器人。 文末那套 include 文件里,discord.mqh 的 CDiscord 类和 Discord EA.mq5 可直接丢进 MT5 测连通性;外汇与贵金属行情剧烈,任何自动推送都只是辅助信号,不构成操作依据,实盘前先在策略测试器跑通再上 URL。