创建 MQL5-Telegram 集成 EA 交易(第 4 部分):模块化代码函数以增强可重用性·综合运用
◍ 截图落盘后如何确认文件已生成
在 MT5 里用脚本抓图表截图,最怕的是 ChartScreenShot 调用完就以为万事大吉,实际上文件可能还在后台写盘。上面这段逻辑在关闭图表前先 Sleep(10000) 暂停 10 秒,给人眼和硬盘都留了缓冲。 紧接着用 while 循环做存在性轮询:最多等 60 次、每次 Sleep(500),也就是最长 30 秒去确认截图文件真的出现在数据目录里。若 wait_loops 耗尽仍 FileIsExist 返回 false,就打印未保存并回退 FAILED_CODE,避免后续拿空文件去发消息。 确认存在后返回 SUCCEEDED_CODE,外面再据此决定是走 INIT_FAILED 还是继续。实盘跑这段时建议把 1366x768 和 10 秒暂停当成可调参数:机器慢就加 Sleep,高清屏抓图可改分辨率,但截图尺寸越大写盘越慢,轮询上限也得跟着放。 外汇与贵金属市场波动剧烈、滑点频发,这类自动截图只作盘后复核或告警留痕,不替代任何交易决策,信号误发概率始终存在。
ChartScreenShot(chart_id,screenshot_name,class="num">1366,class="num">768,ALIGN_RIGHT); Print("OPENED CHART PAUSED FOR class="num">10 SECONDS TO TAKE SCREENSHOT."); Sleep(class="num">10000); class=class="str">"cmt">// sleep for class="num">10 secs to see the opened chart ChartClose(chart_id); class=class="str">"cmt">// Wait for class="num">30 secs to save screenshot if not yet saved class="type">int wait_loops = class="num">60; while(!FileIsExist(screenshot_name) && --wait_loops > class="num">0){ Sleep(class="num">500); } if(!FileIsExist(screenshot_name)){ Print("THE SPECIFIED SCREENSHOT DOES NOT EXIST(WAS NOT SAVED). REVERTING NOW!"); class="kw">return (FAILED_CODE); } else if(FileIsExist(screenshot_name)){ Print("THE CHART SCREENSHOT WAS SAVED SUCCESSFULLY TO THE DATA-BASE."); class="kw">return (SUCCEEDED_CODE); } class="kw">return (SUCCEEDED_CODE); } getScreenshot_of_Current_Chart(SCREENSHOT_FILE_NAME); sendScreenShot(SCREENSHOT_FILE_NAME,TG_API_URL,botTkn,chatID,NULL); class="type">int get_screenshot_new_chart_result = getScreenshot_of_New_Chart(SCREENSHOT_FILE_NAME,_Symbol,_Period); if (get_screenshot_new_chart_result == FAILED_CODE){ class="type">class="kw">string result_msg = "NEW CHART SCREENSHOT COULDN&class="macro">#x27;T BE SAVED. REVERT NOW."; Print(result_msg); sendSimpleMessage(result_msg,TG_API_URL,botTkn,chatID,class="num">10000); class="kw">return (INIT_FAILED); } else if (get_screenshot_new_chart_result == SUCCEEDED_CODE){ class="type">class="kw">string result_msg = "SUCCESS. NEW CHART SCREENSHOT WAS SAVED. CONTINUE NOW."; Print(result_msg); sendSimpleMessage(result_msg,TG_API_URL,botTkn,chatID,class="num">10000); class="type">class="kw">string sending_msg = "\x2705\SCREENSHOT SENDING HAS BEEN INITIATED SUCCESSFULLY."; sending_msg += "\n\x270C\YOU SHOULD RECEIVE THE IMAGE FILE WITHIN class="num">10 SECONDS"; class="type">class="kw">string encoded_sending_msg = UrlEncode(sending_msg); Print(encoded_sending_msg); sendEncodedMessage(encoded_sending_msg,TG_API_URL,botTkn,chatID,class="num">10000); } sendScreenShot(SCREENSHOT_FILE_NAME,TG_API_URL,botTkn,chatID,NULL); class=class="str">"cmt">//--- Caption class="type">class="kw">string CAPTION = NULL; CAPTION = "Screenshot of Symbol: "+Symbol()+ " ("+EnumToString(ENUM_TIMEFRAMES(_Period))+ ") @ Time: "+TimeToString(TimeCurrent()); sendScreenShot(SCREENSHOT_FILE_NAME,TG_API_URL,botTkn,chatID,CAPTION);
把信号确认接进模块化函数跑通
把 EA 逻辑拆成可复用函数后,下一步是在真实行情里验证它们能否协同:信号触发时自动发 Telegram 消息、截当前图表。我们在 1 分钟周期上挂机等待,首根确认的是卖出设置,终端日志记下了信号,持仓数据与截图同步推到了 TG 群。 随后看涨交叉出现,EA 平掉卖仓开买仓,并把买仓的价位、时间、手数、SL/TP 通过同一套函数发出。实测两种方向信号均完成 MQL5→Telegram 的传递,说明模块化桥接既可靠也有扩展余地。 外汇与贵金属杠杆高、滑点随机,这类自动推送只解决通知与留痕,不替代风控。建议读者把下面买仓代码片段直接塞进自己 EA 的信号块,用 MT5 编译后开 1 分钟图验证截图与 emoji 是否正常出海。 代码里 0xF4B0 是钱袋 emoji 的 UTF-16,ShortToString 转成字符串拼进消息;sendEncodedMessage 最后一个入参 10000 是 WebRequest 超时毫秒数,网络差时可调大。若 getScreenshot_of_Current_Chart 返回 FAILED_CODE,会发告警并 return,避免带着缺截图的状态继续跑。
class=class="str">"cmt">// BUY POSITION OPENED. GET READY TO SEND MESSAGE TO TELEGRAM Print("BUY POSITION OPENED. SEND MESSAGE TO TELEGRAM NOW."); class=class="str">"cmt">//class="type">char data[]; // Array to hold data to be sent in the web request(empty in this case) class=class="str">"cmt">//class="type">char res[]; // Array to hold the response data from the web request class=class="str">"cmt">//class="type">class="kw">string resHeaders; // String to hold the response headers from the web request class="type">class="kw">ushort MONEYBAG = 0xF4B0; class="type">class="kw">string MONEYBAG_Emoji_code = ShortToString(MONEYBAG); class="type">class="kw">string msg = "\xF680 Opened Buy Position." +"\n====================" +"\n"+MONEYBAG_Emoji_code+"Price = "+DoubleToString(openPrice,_Digits) +"\n\xF412\Time = "+TimeToString(iTime(_Symbol,_Period,class="num">0),TIME_SECONDS) +"\n\xF551\Time Current = "+TimeToString(TimeCurrent(),TIME_SECONDS) +"\n\xF525 Lotsize = "+DoubleToString(lotSize,class="num">2) +"\n\x274E\Stop loss = "+DoubleToString(stopLoss,_Digits) +"\n\x2705\Take Profit = "+DoubleToString(takeProfit,_Digits) +"\n_________________________" +"\n\xF5FD\Time Local = "+TimeToString(TimeLocal(),TIME_DATE) +" @ "+TimeToString(TimeLocal(),TIME_SECONDS) ; class="type">class="kw">string encloded_msg = UrlEncode(msg); msg = encloded_msg; sendEncodedMessage(msg,TG_API_URL,botTkn,chatID,class="num">10000); class="type">int get_screenshot_current_chart_result = getScreenshot_of_Current_Chart(SCREENSHOT_FILE_NAME); if (get_screenshot_current_chart_result == FAILED_CODE){ class="type">class="kw">string result_msg = "CURRENT CHART SCREENSHOT COULDN&class="macro">#x27;T BE SAVED. REVERT NOW."; Print(result_msg); sendSimpleMessage(result_msg,TG_API_URL,botTkn,chatID,class="num">10000); class="kw">return; }
「做空成交后把单子推到 Telegram」
当图表截图接口返回 SUCCEEDED_CODE,程序先往 MT5 日志打印成功信息,并通过 sendSimpleMessage 以 10000 毫秒超时把状态发到 Telegram。随后拼接带 ✅ 与 ✌ emoji 的发送提示,UrlEncode 编码后再次调用 sendEncodedMessage,提示图片大概率在 10 秒内到达。 截图发送前会动态拼 CAPTION:用 Symbol() 取品种名、EnumToString(_Period) 取周期、TimeToString(TimeCurrent()) 取服务器时间,例如「Screenshot of Symbol: XAUUSD (PERIOD_M15) @ Time: 2024.05.21 13:45:30」。这条 caption 随 sendScreenShot 一起把 PNG 推到频道。 空单真正开出后,代码用 0xF4B0(💰)转成 MONEYBAG_Emoji_code,再拼一段含 🚀/🌒/🕑/🥥/❎/✅ 等符号的 msg:列明成交价 openPrice(按 _Digits 精度)、当前 K 线时间 iTime(_Symbol,_Period,0)、本地时间 TimeLocal()、手数 lotSize(2 位小数)、止损止盈按 _Digits 显示。外汇与贵金属杠杆高,这类自动推送只是降低盯盘成本,不预示任何方向胜率。 把这段逻辑直接塞进你的 EA 成交回调里,开 MT5 跑一单模拟空单,就能在 TG 收到带品种周期和时间戳的成交通知;若 10 秒没图,优先查 SCREENSHOT_FILE_NAME 路径权限而非网络。
else if (get_screenshot_current_chart_result == SUCCEEDED_CODE){ class="type">class="kw">string result_msg = "SUCCESS. CURRENT CHART SCREENSHOT WAS SAVED. CONTINUE NOW."; Print(result_msg); sendSimpleMessage(result_msg,TG_API_URL,botTkn,chatID,class="num">10000); class="type">class="kw">string sending_msg = "\x2705\SCREENSHOT SENDING HAS BEEN INITIATED SUCCESSFULLY."; sending_msg += "\n\x270C\YOU SHOULD RECEIVE THE IMAGE FILE WITHIN class="num">10 SECONDS"; class="type">class="kw">string encoded_sending_msg = UrlEncode(sending_msg); Print(encoded_sending_msg); sendEncodedMessage(encoded_sending_msg,TG_API_URL,botTkn,chatID,class="num">10000); } class=class="str">"cmt">//--- Caption class="type">class="kw">string CAPTION = NULL; CAPTION = "Screenshot of Symbol: "+Symbol()+ " ("+EnumToString(ENUM_TIMEFRAMES(_Period))+ ") @ Time: "+TimeToString(TimeCurrent()); sendScreenShot(SCREENSHOT_FILE_NAME,TG_API_URL,botTkn,chatID,CAPTION); class=class="str">"cmt">// SELL POSITION OPENED. GET READY TO SEND MESSAGE TO TELEGRAM Print("SELL POSITION OPENED. SEND MESSAGE TO TELEGRAM NOW."); class=class="str">"cmt">//class="type">char data[]; // Array to hold data to be sent in the web request(empty in this case) class=class="str">"cmt">//class="type">char res[]; // Array to hold the response data from the web request class=class="str">"cmt">//class="type">class="kw">string resHeaders; // String to hold the response headers from the web request class="type">class="kw">ushort MONEYBAG = 0xF4B0; class="type">class="kw">string MONEYBAG_Emoji_code = ShortToString(MONEYBAG); class="type">class="kw">string msg = "\xF680 Opened Sell Position." +"\n====================" +"\n"+MONEYBAG_Emoji_code+"Price = "+DoubleToString(openPrice,_Digits) +"\n\xF412\Time = "+TimeToString(iTime(_Symbol,_Period,class="num">0),TIME_SECONDS) +"\n\xF551\Time Current = "+TimeToString(TimeCurrent(),TIME_SECONDS) +"\n\xF525 Lotsize = "+DoubleToString(lotSize,class="num">2) +"\n\x274E\Stop loss = "+DoubleToString(stopLoss,_Digits) +"\n\x2705\Take Profit = "+DoubleToString(takeProfit,_Digits) +"\n_________________________" +"\n\xF5FD\Time Local = "+TimeToString(TimeLocal(),TIME_DATE) +" @ "+TimeToString(TimeLocal(),TIME_SECONDS) ;
◍ 把当前图表截图推到 Telegram 的收尾逻辑
这段片段处理的是信号触发后最末一步:先把待发文本做 URL 编码,再调用 sendEncodedMessage 发往 Telegram,超时参数写死为 10000 毫秒(10 秒)。 紧接着调用 getScreenshot_of_Current_Chart 对当前图表截图,返回码若为 FAILED_CODE,就向终端打印并推送「CURRENT CHART SCREENSHOT COULDN'T BE SAVED. REVERT NOW.」后直接 return,不再继续发图。 成功分支里除打印与推送成功提示外,还拼了一段带 ✅ 与 ✌ 符号的发送中提示,并再次 URL 编码后发出;提示文案里明确写了「YOU SHOULD RECEIVE THE IMAGE FILE WITHIN 10 SECONDS」,也就是预期 10 秒内收到图。 最后组装 CAPTION,用 Symbol()、_Period 的周期枚举和 TimeCurrent() 拼出「Screenshot of Symbol: XXX (PERIOD_XXX) @ Time: XXX」,交给 sendScreenShot 把本地文件连说明一起发走。想在 MT5 里验证,只需把 SCREENSHOT_FILE_NAME 和 bot 参数接好,触发一次就能看手机是否 10 秒内弹图。
class="type">class="kw">string encloded_msg = UrlEncode(msg); msg = encloded_msg; sendEncodedMessage(msg,TG_API_URL,botTkn,chatID,class="num">10000); class="type">int get_screenshot_current_chart_result = getScreenshot_of_Current_Chart(SCREENSHOT_FILE_NAME); if (get_screenshot_current_chart_result == FAILED_CODE){ class="type">class="kw">string result_msg = "CURRENT CHART SCREENSHOT COULDN&class="macro">#x27;T BE SAVED. REVERT NOW."; Print(result_msg); sendSimpleMessage(result_msg,TG_API_URL,botTkn,chatID,class="num">10000); class="kw">return; } else if (get_screenshot_current_chart_result == SUCCEEDED_CODE){ class="type">class="kw">string result_msg = "SUCCESS. CURRENT CHART SCREENSHOT WAS SAVED. CONTINUE NOW."; Print(result_msg); sendSimpleMessage(result_msg,TG_API_URL,botTkn,chatID,class="num">10000); class="type">class="kw">string sending_msg = "\x2705\SCREENSHOT SENDING HAS BEEN INITIATED SUCCESSFULLY."; sending_msg += "\n\x270C\YOU SHOULD RECEIVE THE IMAGE FILE WITHIN class="num">10 SECONDS"; class="type">class="kw">string encoded_sending_msg = UrlEncode(sending_msg); Print(encoded_sending_msg); sendEncodedMessage(encoded_sending_msg,TG_API_URL,botTkn,chatID,class="num">10000); } class=class="str">"cmt">//--- Caption class="type">class="kw">string CAPTION = NULL; CAPTION = "Screenshot of Symbol: "+Symbol()+ " ("+EnumToString(ENUM_TIMEFRAMES(_Period))+ ") @ Time: "+TimeToString(TimeCurrent()); sendScreenShot(SCREENSHOT_FILE_NAME,TG_API_URL,botTkn,chatID,CAPTION);
把消息推送做成可插拔模块之后
这套把 MT5 和 Telegram 打通的思路,核心落在模块化函数上:发文本、发图表截图各自独立封装,EA 主逻辑只管调接口,不纠缠网络细节。实测下来,这种拆法让策略端改一处通知逻辑、不影响下单与风控,维护成本明显低于把 HTTP 请求写死在 OnTick 里。 下一篇会反向打通——Telegram 发指令回控 MT5,比如远程要一张实时持仓图或账户快照。外汇与贵金属杠杆高、滑点突变频繁,这类远程指令若没做鉴权,可能把账号暴露在误操作风险里,接之前先想清楚白名单。 随文给的 TELEGRAM_MQL5_MULTIDIMENSIONAL_PART4.mq5 约 104.6 KB,评论区已有人反馈「文字能发、截图发不出」,说明多部分表单上传在部分券商服务器上会被拦。拿回去先在本机测通截图通道,再上实盘环境,别默认它开箱即用。