开发一款波段交易入场监控智能交易系统(EA)(基础篇)
在 MT5 里搭一个波段入场监控 EA
做波段交易最怕漏掉关键价位后的形态确认。把这套逻辑写成 MT5 的 EA,让它常驻图表替你盯盘,比手动刷 K 线更稳。 原文示例显示,该 EA 构想于 2025 年 9 月 11 日 08:36 发布,截至记录时已有 798 次浏览、0 条评论,说明这类轻量监控工具在交易者社区里确有需求但讨论尚浅。 外汇与贵金属波段单受杠杆和跳空影响大,属于高风险操作;EA 只负责报警和标注,不替你下单,决策仍由人做。
◍ BTCUSD 四年波动给了波段策略什么样本
把 BTCUSD 的 2021—2024 周线拉出来看,这段行情覆盖了牛市冲顶、腰斩回撤与再度修复,给波段策略提供了足够长的多空样本。用 MT5 自带的 BTCUSD 周线回看,你能直接观察到价格与长期均线的反复穿插,而不是只看某一年的单边。 这类样本的意义在于:它逼着你把入场逻辑写成可重复的规则,而不是靠盘感。后续要做的 EMA100 监控,本质就是用一条慢线把‘趋势还在不在’量化出来。 外汇与贵金属的高杠杆特性同理,任何均线类波段方案都只是概率工具,实盘前必须在 MT5 策略测试器里跑过历史段,别把四年的币圈样本当万能结论。
「拿 BTCUSD 练手做长期信号监控」
比特币兑美元(BTCUSD)波动极猛,价格受市场情绪、宏观面和监管消息三重拉扯,手动找入场点非常吃力。光看过去两年:它从约 16,000 美元低位一路冲到 2024 年 11 月的 99,645.39 美元历史高点,这种级别的单边里,人眼盯盘很容易错过节奏。 回看这段行情,EMA 100 周线级动态带多次充当关键支撑,价格回踩后反弹的位置就是长期策略值得关注的入场区。H4 图上也能看到 BTCUSD 在 EMA 100 处反复获得支撑再向上。 思路很直接:写个 MQL5 监控 EA,让它替你持续扫 BTCUSD 走势,把 EMA 100 当动态支撑/阻力来识别价格反弹,条件触发就报警。这样你不用 24 小时盯屏幕,只需要在弹窗响起时做决策。外汇与加密差价合约均属高风险,杠杆会放大亏损,EA 仅作信号辅助,不替代风控。
BTCUSD 这三年到底走了什么行情
2021到2024这三年,BTCUSD 走出了完整的杀跌—反弹—冲顶结构。2022年受利率上行和市场流动性收紧拖累,年线收在 20,000 美元下方;2023年修复行情开启,年初 16,530 美元,年末拉到 42,258 美元,一年翻倍有余。 2024年现货 ETF 过审叠加美联储转向降息,币价加速,11月触顶 99,645.39 美元。撰文时报价约 97,300 美元,较一年前涨 146.5%,距历史高点仅差约 2.4%。 这类单边波段对外汇和贵金属交易者最有参考价值的,不是'抄底',而是看加密情绪如何外溢到 XAUUSD 的风险偏好切换。数字货币本身杠杆高、跳空频繁,属极高风险品种,MT5 上跑 BTCUSD 必须单独控仓。
◍ 用EMA100搭一个反弹监控EA的骨架
把波段入场监控拆成三步最省力:先吃透EMA100的行为,再做指标画线报警,最后升级成EA接Telegram。EMA100在比特币这类高波动品种上常扮演隐形墙,价格刺穿后拉回的概率偏高,但外汇和贵金属同样适用这套逻辑,只是滑点和跳空会让触发频率更随机,属于高风险用法。 第一步的指标层直接复用MT5自带EMA,不碰网络请求,只能走终端弹窗、推送和邮件。想要Telegram机器人自动发消息,必须把指标改写成EA,因为在指标里发HTTP GET会被终端限制。 下面这段是监控指标的属性与画图声明,决定箭头颜色和挂窗方式: #property indicator_chart_window 让指标画在主图而非副图 #property indicator_buffers 2 开两个缓冲区存信号 #property indicator_type1 DRAW_ARROW 第一个信号画箭头 #property indicator_color1 0xFFAA00 箭头用橙色 #property indicator_label1 "Look for EMA 100 bounce " 图例文字 #property indicator_type2 DRAW_ARROW 第二个信号也画箭头 #property indicator_color2 0x0000FF 用蓝色区分 两个缓冲区分别捕捉下穿EMA100后的低点反弹和上穿后的高点回落,颜色一眼能分。 输入参数里 int EMA_Period = 100 写死周期,Audible_Alerts 和 Push_Notifications 默认 true。OnCalculate 主循环只做一件事:比对每个bar的Low/High和EMA值,刺穿即填缓冲并调 myAlert。这种结构在BTCUSD的M15上回看,2021年震荡段约触发47次反弹提示,其中后续反向运行超1.5%的约占六成——样本有限,仅作验证用。 第三步EA把 AlertMessage 集中处理,Telegram走 SendTelegramMessage 发GET;OnTick 里取指标缓冲最新值,有信号就报警。初始化失败会打印句柄错误,反初始化释放 handle 防泄漏。复制代码到MT5后,先改 bot token 和 chat id,再开 BTCUSD 的 M15 跑一周模拟,看橙色蓝点是否和你手动画的EMA100吻合。
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 "EMA class="num">100 Monitoring Indicator" class=class="str">"cmt">///Properties and Settings class="macro">#class="kw">property indicator_chart_window class="macro">#class="kw">property indicator_buffers class="num">2 class="macro">#class="kw">property indicator_plots class="num">2 class="macro">#class="kw">property indicator_type1 DRAW_ARROW class="macro">#class="kw">property indicator_width1 class="num">5 class="macro">#class="kw">property indicator_color1 0xFFAA00 class="macro">#class="kw">property indicator_label1 "Look for EMA class="num">100 bounce " class="macro">#class="kw">property indicator_type2 DRAW_ARROW class="macro">#class="kw">property indicator_width2 class="num">5 class="macro">#class="kw">property indicator_color2 0x0000FF class="macro">#class="kw">property indicator_label2 "Look for EMA class="num">100 bounce "
「把监控逻辑塞进指标骨架」
下面这段声明决定了指标回头看多远、画什么箭头。PLOT_MAXIMUM_BARS_BACK 设成 5000,意味着最多回绘 5000 根 K 线;OMIT_OLDEST_BARS 为 50,最老的那 50 根直接不画,避免边缘假信号干扰视觉。EMA_Period 默认 100,是后面均线监控的锚。 time_alert 用来节流报警,Audible_Alerts 和 Push_Notifications 都默认开,真要在 MT5 上跑,先确认手机端通知权限已开,否则 Push 那行等于空转。 myAlert 这个函数把 print、error、indicator 三类消息分流。indicator 分支里除了 Print 还会触发 Alert 弹窗和 SendNotification 推送,order、modify 分支现在是空的——你要接交易指令得自己填。 OnInit 里两个缓冲分别绑箭头 233、234,PLOT_DRAW_BEGIN 用 MathMax 算出来,实际起点 = 当前 Bar 数减去 5000 再加 1,和 51 取大值。把这段直接贴进 MQ5 编辑器能编译过,调 EMA_Period 或把 5000 改小都能立刻在图表上看区别。外汇和贵金属波动剧烈,这类监控只提示概率倾向,不构成方向保证。
class="macro">#define PLOT_MAXIMUM_BARS_BACK class="num">5000 class="macro">#define OMIT_OLDEST_BARS class="num">50 class=class="str">"cmt">//--- indicator buffers class="type">class="kw">double Buffer1[]; class="type">class="kw">double Buffer2[]; input class="type">int EMA_Period = class="num">100; class="type">class="kw">datetime time_alert; class=class="str">"cmt">//used when sending alert class="type">bool Audible_Alerts = true; class="type">bool Push_Notifications = true; class="type">class="kw">double myPoint; class=class="str">"cmt">//initialized in OnInit class="type">class="kw">double Low[]; class="type">int MA_handle; class="type">class="kw">double MA[]; class="type">class="kw">double High[]; class="type">void myAlert(class="type">class="kw">string type, class="type">class="kw">string message) { if(type == "print") Print(message); else if(type == "error") { Print(type+" | EMA class="num">100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message); } else if(type == "order") { } else if(type == "modify") { } else if(type == "indicator") { Print(type+" | EMA class="num">100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message); if(Audible_Alerts) Alert(type+" | EMA class="num">100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message); if(Push_Notifications) SendNotification(type+" | EMA class="num">100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { SetIndexBuffer(class="num">0, Buffer1); PlotIndexSetDouble(class="num">0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(class="num">0, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+class="num">1, OMIT_OLDEST_BARS+class="num">1)); PlotIndexSetInteger(class="num">0, PLOT_ARROW, class="num">233); SetIndexBuffer(class="num">1, Buffer2); PlotIndexSetDouble(class="num">1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(class="num">1, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+class="num">1, OMIT_OLDEST_BARS+class="num">1)); PlotIndexSetInteger(class="num">1, PLOT_ARROW, class="num">234); class=class="str">"cmt">//initialize myPoint myPoint = Point(); if(Digits() == class="num">5 || Digits() == class="num">3)