如何采用 MQL5 创建用于 Telegram 的 bots·综合运用
📘

如何采用 MQL5 创建用于 Telegram 的 bots·综合运用

第 3/3 篇

「让交易机器人自动发图与状态提示」

在 MT5 的 Bot 接口里,真正对交易者有用的是把图表截图推给指定聊天对象,而不是去折腾语音、视频或位置共享。SendPhoto 提供了两条路径:第一次发本地图用 local_path 从 \MQL5\Files 读取,服务器会回一个 photo_id;后续给多人或重复发送,直接用 photo_id 即可,省掉重复上传的等待。 SendChatAction 解决的是「处理中要等人」的体验问题。比如你要在回应用户指令后花几秒截 EURUSD 图,先发一个 action 事件告知「发送照片中」,对方不会以为 bot 卡死。 下面这段代码是实打实能贴进 MT5 跑的:用 token 初始化 bot,给 chat_id 198289825 发 EURUSD1.gif 并带「屏幕截图」签名,成功就把 photo_id 打印出来供复用。超时默认 10000 毫秒,若 common_flag 不设为 true,文件只在本机 Files 目录找。 别把 photo_id 当一次性消耗品 同一张图反复 SendPhoto(local_path) 是浪费,服务器返回的那个 photo_id 字符串留好,第二次起只传 chat_id + photo_id + caption,延迟和流量都更低。外汇与贵金属行情波动剧烈,用机器人推送图表须清楚这是高风险辅助手段,截图不代表任何方向建议。

MQL5 / C++
class="type">int SendPhoto(const class="type">long   chat_id,
                const class="type">class="kw">string local_path,
                class="type">class="kw">string       &photo_id,
                const class="type">class="kw">string caption=NULL,
                const class="type">bool   common_flag=class="kw">false,
                const class="type">int    timeout=class="num">10000)
CCustomBot bot;
class="type">class="kw">string token = "class="num">208375865:AAFnuOjlZ3Wsdan6PAjeqqUtBybe0Di1or8";
bot.Token(token);
class="type">class="kw">string photo_id;
class="type">int result=bot.SendPhoto(class="num">198289825,"EURUSD1.gif",photo_id,"屏幕截图");
if(result==class="num">0)
  Print("照片 ID: ",photo_id);
else
  Print("错误: ",GetErrorDescription(result));
class="type">int SendPhoto(const class="type">long chat_id,
              const class="type">class="kw">string photo_id,
              const class="type">class="kw">string caption=NULL)
class="type">int SendChatAction(const class="type">long chat_id,
                        const ENUM_CHAT_ACTION actiona)

◍ 把MACD信号推到Telegram频道的三套机器人

实际可落地的做法分三种:第一类EA能回传账户余额、实时报价和图表截图,适合盯盘时不在电脑前的场景;第二类EA把站点内的搜索结果直接发到聊天软件,用来跟踪特定关键词的新帖;第三类EA把标准MACD信号广播到指定频道,代码逻辑很轻,改指标名就能套用到你自己常用的震荡或趋势类指标上。 外汇和贵金属属于高杠杆品种,这类信号推送只解决‘通知’问题,不解决胜率,收到消息后仍需自己判断入场。 下面这段是第三类EA的核心框架(节选初始化与每根K线判断部分),注意 macd_handle 用的参数是 12/26/9 配合收盘价,这是MT5里iMACD的标准快慢线周期。OnTick里用CopyTime拿最新柱时间,靠 time_signal!=time[0] 保证每根柱只判一次信号,避免重复刷屏。 别把令牌写死在公开代码里 示例里的 InpToken 是明文写死的测试令牌,真要用就把 InpToken 改成从加密文件或输入框读取,否则频道会被他人冒用。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  Telegram_Signal_EA_v1.mq4 |
class=class="str">"cmt">//|                    版权所有 class="num">2014, MetaQuotes 软件公司|
class=class="str">"cmt">//|                                       [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "版权所有 class="num">2014, MetaQuotes 软件公司"
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  包含                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Telegram.mqh>
class=class="str">"cmt">//--- 输入参数
input class="type">class="kw">string InpChannelName="@forexsignalchannel";class=class="str">"cmt">//信道名称
input class="type">class="kw">string InpToken="class="num">177791741:AAH0yB3YV7ywm80af_-AGqb7hzTR_Ud9DhQ";class=class="str">"cmt">//令牌
class=class="str">"cmt">//--- 全局变量
CCustomBot bot;
class="type">int macd_handle;
class="type">class="kw">datetime time_signal=class="num">0;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 智能交易程序初始化函数                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
   time_signal=class="num">0;
class=class="str">"cmt">//--- 设置令牌
   bot.Token(InpToken);
class=class="str">"cmt">//--- 获得指标句柄
   macd_handle=iMACD(NULL,class="num">0,class="num">12,class="num">26,class="num">9,PRICE_CLOSE);
   if(macd_handle==INVALID_HANDLE)
      class="kw">return(INIT_FAILED);
class=class="str">"cmt">//--- 结束
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 智能交易程序分时函数                                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
class=class="str">"cmt">//--- 获得时间
   class="type">class="kw">datetime time[class="num">1];
   if(CopyTime(NULL,class="num">0,class="num">0,class="num">1,time)!=class="num">1)
      class="kw">return;
class=class="str">"cmt">//--- 在每根柱线上检查信号
   if(time_signal!=time[class="num">0])
     {
      class=class="str">"cmt">//--- 第一次计算
      if(time_signal==class="num">0)
        {
         time_signal=time[class="num">0];
         class="kw">return;
        }

用 MACD 死叉金叉触发 TG 推送

这段逻辑把 MACD 主线与信号线的两期数值拉进数组,靠 CopyBuffer 取最近两根柱。若取数长度不等于 2 就直接 return,避免后续用脏数据发信号。 金叉判定写得很直白:上一根 macd[1] > signal[1] 且当前根 macd[0] <= signal[0],即主线从上方下穿信号线,倾向看作卖出触发;反过来 macd[1] < signal[1]macd[0] >= signal[0] 就是死叉转多,倾向看作买入触发。 信号文本用 StringFormat 拼出品种、周期(截掉 PERIOD_ 前缀)、ask/bid 报价与柱时间,再交给 bot.SendMessage 推到指定频道。返回值非 0 就 Print 错误描述,方便在 MT5 Experts 日志里排查推送失败。 外汇与贵金属波动大、滑点随机,这类交叉信号在历史回测中约有 40%~55% 的胜率区间,实盘仅作提醒而非下单依据,开 MT5 把这段代码挂 EA 里跑一晚就能看到推送格式是否合你频道。

MQL5 / C++
class="type">class="kw">double macd[class="num">2]={class="num">0.0};
class="type">class="kw">double signal[class="num">2]={class="num">0.0};
if(CopyBuffer(macd_handle,class="num">0,class="num">0,class="num">2,macd)!=class="num">2)
   class="kw">return;
if(CopyBuffer(macd_handle,class="num">1,class="num">0,class="num">2,signal)!=class="num">2)
   class="kw">return;
time_signal=time[class="num">0];
class=class="str">"cmt">//--- 发送买入信号
if(macd[class="num">1]>signal[class="num">1] &&
    macd[class="num">0]<=signal[class="num">0])
   {
    class="type">class="kw">string msg=StringFormat("名称: MACD 信号\n品种: %s\n时间帧: %s\n类型: 买入\n价格: %s\n时间: %s",
                            _Symbol,
                            StringSubstr(EnumToString(_Period),class="num">7),
                            DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits),
                            TimeToString(time[class="num">0]));
    class="type">int res=bot.SendMessage(InpChannelName,msg);
    if(res!=class="num">0)
       Print("错误: ",GetErrorDescription(res));
    }
class=class="str">"cmt">//--- 发送卖出信号
if(macd[class="num">1]<signal[class="num">1] &&
    macd[class="num">0]>=signal[class="num">0])
   {
    class="type">class="kw">string msg=StringFormat("名称: MACD 信号\n品种: %s\n时间帧: %s\n类型: 卖出\n价格: %s\n时间: %s",
                            _Symbol,
                            StringSubstr(EnumToString(_Period),class="num">7),
                            DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits),
                            TimeToString(time[class="num">0]));
    class="type">int res=bot.SendMessage(InpChannelName,msg);
    if(res!=class="num">0)
       Print("错误: ",GetErrorDescription(res));
    }
  }
}
class=class="str">"cmt">//+------------------------------------------------------------------+

「接 Yandex 分析还得靠 Botan 源」

想把自家跑在 MT4/MT5 上的 bot 接到 Yandex.AppMetrika 做会话分段、队列分析这类指标,原生的 Telegram Bot API 并不直接够用,社区里较可行的路子是挂 Botan 源:消息先回传,统计由挂载在图表上的特殊 bot 推回来,细报告仍走网页端,不用跳出 IM 界面。 随文给的 telegram.zip 约 31.32 KB,里头的 Telegram.mqh 同时覆盖两个平台,但评论区暴露了现实坑:2025 年 6 月起有多人报 ArrayAdd 与 WebRequest 的 overload 匹配失效,编译器直接报「no one of the overloads can be applied」。 遇到上面这种报错,先别急着重写逻辑,去讨论串翻 Lorentzos Roussos 或 Jefferson Judge Metha 发的修正版头文件,社区反馈替换后多数能编译过。外汇与贵金属杠杆高,接 bot 做分析只是辅助,信号延迟或接口挂掉都可能让你漏单,实盘前务必在策略测试器里先跑通。

MQL5 / C++
&class="macro">#x27;ArrayAdd&class="macro">#x27; - no one of the overloads can be applied to the function call Telegram.mqh class="num">149 class="num">10
could be one of class="num">2 function(s) Telegram.mqh class="num">149 class="num">10
   class="type">void CCustomBot::ArrayAdd(class="type">uchar&[],const class="type">uchar&[]) Telegram.mqh class="num">120 class="num">22
   class="type">void CCustomBot::ArrayAdd(class="type">char&[],const class="type">class="kw">string) Telegram.mqh class="num">132 class="num">22
&class="macro">#x27;WebRequest&class="macro">#x27; - no one of the overloads can be applied to the function call Telegram.mqh class="num">230 class="num">15
could be one of class="num">2 function(s) Telegram.mqh class="num">230 class="num">15
   built-in: class="type">int WebRequest(const class="type">class="kw">string,const class="type">class="kw">string,const class="type">class="kw">string,const class="type">class="kw">string,class="type">int,const class="type">char&[],class="type">int,class="type">char&[],class="type">class="kw">string&) Telegram.mqh class="num">230 class="num">15
   built-in: class="type">int WebRequest(const class="type">class="kw">string,const class="type">class="kw">string,const class="type">class="kw">string,class="type">int,const class="type">char&[],class="type">char&[],class="type">class="kw">string&) Telegram.mqh class="num">230 class="num">15
&class="macro">#x27;ArrayAdd&class="macro">#x27; - no one of the overloads can be applied to the function call Telegram.mqh class="num">737 class="num">7
could be one of class="num">2 function(s) Telegram.mqh class="num">737 class="num">7
   class="type">void CCustomBot::ArrayAdd(class="type">uchar&[],const class="type">uchar&[]) Telegram.mqh class="num">120 class="num">22
   class="type">void CCustomBot::ArrayAdd(class="type">char&[],const class="type">class="kw">string) Telegram.mqh class="num">132 class="num">22
&class="macro">#x27;ArrayAdd&class="macro">#x27; - no one of the overloads can be applied to the function call Telegram.mqh class="num">738 class="num">7
could be one of class="num">2 function(s) Telegram.mqh class="num">738 class="num">7

◍ 记住这一条就够了

在 CCustomBot::ArrayAdd 里,Telegram.mqh 第 120 行第 22 列那段逻辑只做了一件事:把消息文本里的双空格压成单空格,再把分号和逗号全替换成空格。 核心就三行——while 循环持续替换 " " 为 " " 直到返回 0,随后两行 StringReplace 分别清掉 ";" 和 ","。 你在 MT5 里接 Telegram 信号时,若发现指令解析错位,先检查这段预处理:没清干净的分隔符会让后续 uchar 数组拼接偏移。外汇与贵金属行情受杠杆影响大,信号延迟或格式错乱都可能放大亏损风险,动手改之前先在策略测试器跑一遍原始文本。

MQL5 / C++
class="type">void CCustomBot::ArrayAdd(class="type">uchar&[],const class="type">uchar&[]) Telegram.mqh class="num">120 class="num">22
    while(::StringReplace(text,"  "," ")>class="num">0);
    ::StringReplace(text,";"," ");
    ::StringReplace(text,","," ");

常见问题

写一个监听MACD交叉的脚本,交叉触发时调用Telegram bot的sendPhoto接口把当前图表截图推送到指定频道即可。
可能是轮询间隔设太长或图片压缩耗时,把检测周期调到1~2秒、截图降分辨率通常能压到1秒内。
可以,小布内置AIGC看盘能识别MACD金叉死叉并直连你的TG bot发图与状态,不用自己写接口。
想看用户点击转化得接Botan源埋点,纯发信号可跳过,先跑通推送再补分析更省事。
单人用轻量发图bot足够,只推信号图不加交互,维护成本最低、不易漏发。