开发一款波段交易入场监控智能交易系统(EA)·综合运用
(3/3)·把前两步的指标与逻辑收拢成一套能自动报警、可回测优化的MQL5监控EA,告别盯盘疲劳
把 EMA100 弹点信号接进多通道报警
这段逻辑干的事很直接:当自定义指标的 buffer1 或 buffer2 在最新柱不为 EMPTY_VALUE,且当前时间不等于上一次报警时间,就触发一条带品种名的提示,并把 lastAlertTime 刷新为 TimeCurrent(),避免同一根 K 线里反复弹窗。 代码里 buffer1 对应「Low 端 EMA100 弹点」、buffer2 对应「High 端 EMA100 弹点」,两条分支结构对称,只是 message 文本不同。实盘里 BTCUSD 这种高波动品种,EMA100 在 1H 图上被刺穿后回收的概率偏高,但外汇和贵金属同样套路风险更高,点差扩大时假信号会明显增多。 调试段用 CopyBuffer(emaHandle,0,0,1,emaValueArray) 只取 1 根值,ArraySetAsSeries 置为序列后 emaValueArray[0] 就是当前 EMA100 数值,Print 出来能在 MT5 终端日志里直接核对指标句柄是否生效;若返回小于等于 0 则打印 GetLastError() 方便排查。 EA 头部把报警拆成终端、推送、Telegram 三个开关,Telegram 默认关,填了 token 和 chat_id 才启用,URL 拼接里 timeout 写死 5000 毫秒,复制去跑前记得把 YOUR_BOT_TOKEN 换成真实值。
class="type">class="kw">string message = "Signal detected: Look for EMA class="num">100 bounce(Low). Symbol: " + _Symbol; AlertMessage(message); lastAlertTime = TimeCurrent(); } class=class="str">"cmt">// Check for signals in Buffer2 if (buffer2[class="num">0] != EMPTY_VALUE && TimeCurrent() != lastAlertTime) { class="type">class="kw">string message = "Signal detected: Look for EMA class="num">100 bounce(High). Symbol: " + _Symbol; AlertMessage(message); lastAlertTime = TimeCurrent(); } class=class="str">"cmt">// Debugging EMA class="num">100 value class="type">class="kw">double emaValueArray[]; ArraySetAsSeries(emaValueArray, true); class=class="str">"cmt">// Ensure it&class="macro">#x27;s set as series if (CopyBuffer(emaHandle, class="num">0, class="num">0, class="num">1, emaValueArray) > class="num">0) { Print("EMA class="num">100 Current Value: ", emaValueArray[class="num">0]); } else { Print("Failed to read EMA class="num">100 buffer. Error: ", GetLastError()); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Bitcoin Monitoring Expert Advisor | class=class="str">"cmt">//| Copyright class="num">2024, Clemence Benjamin | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Clemence Benjamin" class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.0" class="macro">#class="kw">property description "BTCUSD Monitoring Expert Advisor" class=class="str">"cmt">//--- Input parameters input class="type">class="kw">string IndicatorName = "ema100_monitoring_indicator"; class=class="str">"cmt">// Name of the custom indicator input class="type">bool EnableTerminalAlerts = true; input class="type">bool EnablePushNotifications = true; input class="type">bool EnableTelegramAlerts = false; input class="type">class="kw">string TelegramBotToken = "YOUR_BOT_TOKEN"; class=class="str">"cmt">// Replace with your bot token input class="type">class="kw">string TelegramChatID = "YOUR_CHAT_ID"; class=class="str">"cmt">// Replace with your chat ID class=class="str">"cmt">//--- Indicator handles class="type">int indicatorHandle = INVALID_HANDLE; class="type">int emaHandle = INVALID_HANDLE; class=class="str">"cmt">//--- Alert function class="type">void AlertMessage(class="type">class="kw">string message) { if (EnableTerminalAlerts) Alert(message); if (EnablePushNotifications) SendNotification(message); if (EnableTelegramAlerts) SendTelegramMessage(message); } class=class="str">"cmt">//--- Telegram Alerting class="type">void SendTelegramMessage(class="type">class="kw">string message) { if (EnableTelegramAlerts) { class="type">class="kw">string url = "https:class=class="str">"cmt">//api.telegram.org/bot" + TelegramBotToken + "/sendMessage?chat_id=" + TelegramChatID + "&text=" + message; class="type">int timeout = class="num">5000;
「EA 初始化与生命周期里的句柄管理」
这段 MT5 代码把监控 EA 的骨架搭清楚了:OnInit 里同时挂自定义指标和内置 EMA100,两个句柄任一失效就直接 INIT_FAILED,避免后续 OnTick 空跑。 ResetLastError(); char postData[]; uchar result[]; string response; int res = WebRequest("GET", url, NULL, timeout, postData, result, response); if (res != 200) { Print("Telegram WebRequest failed. Error: ", GetLastError(), ", HTTP Code: ", res); } else { Print("Telegram message sent successfully: ", response); } } 上面这段是 WebRequest 调 Telegram 的收尾:先清错误码再发 GET,HTTP 返回码不是 200 就打印错误号和码值,是 200 才认发送成功。实盘里若频繁看到非 200,优先查 MT5 的 URL 白名单和 timeout 参数(默认 5000ms 常不够)。 OnDeinit 用 IndicatorRelease 释放两个句柄,防止图表残留指标引用;OnTick 开头用 static datetime lastAlertTime 拦重复信号,这是低价高频品种(如 BTC 波动段)防刷屏的关键。外汇与贵金属品种同样适用该结构,但需注意杠杆放大下信号触发频率更高,风险也更大。
ResetLastError(); class="type">char postData[]; class="type">uchar result[]; class="type">class="kw">string response; class="type">int res = WebRequest("GET", url, NULL, timeout, postData, result, response); if (res != class="num">200) { Print("Telegram WebRequest failed. Error: ", GetLastError(), ", HTTP Code: ", res); } else { Print("Telegram message sent successfully: ", response); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { Print("Bitcoin Monitoring EA started."); class=class="str">"cmt">// Attach the custom indicator to the chart indicatorHandle = iCustom(_Symbol, _Period, IndicatorName); if (indicatorHandle == INVALID_HANDLE) { Print("Failed to attach indicator: ", IndicatorName, ". Error: ", GetLastError()); class="kw">return(INIT_FAILED); } class=class="str">"cmt">// Attach built-in EMA class="num">100 to the chart emaHandle = iMA(_Symbol, _Period, class="num">100, class="num">0, MODE_EMA, PRICE_CLOSE); if (emaHandle == INVALID_HANDLE) { Print("Failed to create EMA class="num">100. Error: ", GetLastError()); class="kw">return(INIT_FAILED); } class=class="str">"cmt">// Add EMA class="num">100 to the terminal chart if (!ChartIndicatorAdd(class="num">0, class="num">0, emaHandle)) { Print("Failed to add EMA class="num">100 to the chart. Error: ", GetLastError()); } class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { Print("Bitcoin Monitoring EA stopped."); if (indicatorHandle != INVALID_HANDLE) { IndicatorRelease(indicatorHandle); } if (emaHandle != INVALID_HANDLE) { IndicatorRelease(emaHandle); } } class="type">void OnTick() { class="kw">static class="type">class="kw">datetime lastAlertTime = class="num">0; class=class="str">"cmt">// Prevent repeated alerts for the same signal if (indicatorHandle == INVALID_HANDLE || emaHandle == INVALID_HANDLE) class="kw">return; class="type">class="kw">double buffer1[], buffer2[]; ArraySetAsSeries(buffer1, true); ArraySetAsSeries(buffer2, true); class=class="str">"cmt">// Read data from the custom indicator
◍ 缓冲读取与 EMA100 弹跳信号触发
指标句柄拿到之后,第一步是把两条信号缓冲各取最新一根柱的值。CopyBuffer 的第三、四参数填 0 和 1,意思是从当前柱开始复制 1 个元素的数组;返回值为负就直接 Print 错误码并 return,避免后续空数组越界。 信号判定只看 buffer1[0] 和 buffer2[0] 是否不等于 EMPTY_VALUE,同时用 TimeCurrent() != lastAlertTime 做同根柱去重。命中后拼出带 _Symbol 的提示语走 AlertMessage,并把 lastAlertTime 刷成当前时间,这样同一根 K 线里不会重复弹窗。 顺手把 EMA100 也拉进来做调试输出。ArraySetAsSeries 设成 true 后,emaValueArray[0] 才是当下价格对应的均线值;CopyBuffer 成功就 Print 出具体数值,失败则报 GetLastError,方便你在 MT5 终端里核对弹跳参考位。 外汇与贵金属波动剧烈,EMA100 弹跳仅代表价格触及参考均线,后续反转属概率事件,实盘请自担高风险。
if (CopyBuffer(indicatorHandle, class="num">0, class="num">0, class="num">1, buffer1) < class="num">0) { Print("Failed to copy Buffer1. Error: ", GetLastError()); class="kw">return; } if (CopyBuffer(indicatorHandle, class="num">1, class="num">0, class="num">1, buffer2) < class="num">0) { Print("Failed to copy Buffer2. Error: ", GetLastError()); class="kw">return; } class=class="str">"cmt">// Check for signals in Buffer1 if (buffer1[class="num">0] != EMPTY_VALUE && TimeCurrent() != lastAlertTime) { class="type">class="kw">string message = "Signal detected: Look for EMA class="num">100 bounce(Low). Symbol: " + _Symbol; AlertMessage(message); lastAlertTime = TimeCurrent(); } class=class="str">"cmt">// Check for signals in Buffer2 if (buffer2[class="num">0] != EMPTY_VALUE && TimeCurrent() != lastAlertTime) { class="type">class="kw">string message = "Signal detected: Look for EMA class="num">100 bounce(High). Symbol: " + _Symbol; AlertMessage(message); lastAlertTime = TimeCurrent(); } class=class="str">"cmt">// Debugging EMA class="num">100 value class="type">class="kw">double emaValueArray[]; ArraySetAsSeries(emaValueArray, true); class=class="str">"cmt">// Ensure it&class="macro">#x27;s set as series if (CopyBuffer(emaHandle, class="num">0, class="num">0, class="num">1, emaValueArray) > class="num">0) { Print("EMA class="num">100 Current Value: ", emaValueArray[class="num">0]); } else { Print("Failed to read EMA class="num">100 buffer. Error: ", GetLastError()); }
用策略测试器验证 EA 的盯盘逻辑
代码编译通过后,下一步不是直接上实盘,而是进 MT5 的策略测试器(Strategy Tester)跑一遍。这里能脱离真实行情环境,把 EA 的每笔 tick 处理逻辑摊开看,尤其适合验证价格监测类脚本有没有漏 tick 或重复触发。 我们拿比特币监控 EA 做了一次历史回放测试,测试器在 2022 年数据上逐 tick 推进,EA 成功输出了每个 tick 的比特币价格变动记录,说明其实时监测链路在离线环境也能跑通。这一结果只证明逻辑可达,不预示任何收益。 外汇与贵金属品种在测试器里同样可跑,但这类高杠杆品种点差跳空频繁,历史 tick 质量直接影响回测可信度,务必手动核对品种规格里的点值与时区。
「H4与D1上EMA100的交互验证」
把该指标加载到 H4 和 D1 周期,叠加 EMA 100,可以直观看到价格与这条均线的贴合程度。实测中,价格在大多数波段内沿所选 EMA 运行,说明较高时间框架下均线对价格的吸附具有可观察的重复性。 系统支持三种报警方式,其中含 Telegram 推送,意味着你不必一直盯盘也能收到触发信号。下图对应的场景是在 MT5 同时挂上 EA 与指标,二者协同工作,EA 负责执行与通知,指标负责可视化状态。 在图表上添加 EA 和指标后,建议先不开实盘,用历史片段回看报警是否与实际穿越 EMA 100 的时点一致。外汇与贵金属波动剧烈,任何信号都只是概率倾向,验证通过再考虑小仓位接入。
◍ 别急着下结论
这套监控型 EA 目前只做一件事:盯盘和发 Telegram 警报,不替你下单。附带的 ema100_monitoring_indicator.mq5 约 5.62 KB,bitcoin_monitoring_expert.mq5 约 4.95 KB,两者都可在 MT5 里直接编译跑起来。 它原本按 BTCUSD 的 EMA 100 反弹逻辑写,但监控框架是通用的,换品种或加别的指标都只是改参数和 WebRequest 的事。外汇与贵金属波动剧烈、杠杆风险高,拿去实盘前务必先回测。 下载 ZIP 后用自己的参数回测,再按策略调阈值;把它当警报器而不是自动印钞机,才可能少踩坑。