MQL5 简介(第 11 部分):MQL5 中使用内置指标的初学者指南(二)(基础篇)
「用 iMA 把均线请进 EA」
MQL5 里调用内置指标不走硬编码,而是用以 i 开头的函数族,比如均线对应 iMA。它返回的是指标句柄,真正取值要靠 CopyBuffer,这套机制让指标和图表解耦,EA 重载时不会卡在重算上。 以 24 十一月 2025 发布的初学者指南数据看,该文在站内获得 1 010 次浏览、4 条互动,说明用内置函数接指标仍是新手最高频的痛点。外汇与贵金属杠杆高,指标信号只代表概率倾向,实盘前务必在 MT5 策略测试器跑历史。 下面这段是 20 周期收盘价均线的标准接法,句柄建一次即可反复用。
class="type">int ma_handle = iMA(_Symbol, _Period, class="num">20, class="num">0, MODE_SMA, PRICE_CLOSE); class="type">class="kw">double ma_buffer[class="num">1]; CopyBuffer(ma_handle, class="num">0, class="num">0, class="num">1, ma_buffer);
从单指标到三指标:隐藏背离项目要做什么
上一章只用了 RSI 做内置指标演示,这一章直接把 RSI、随机振荡器(Stochastic)和移动平均线(MA)三个指标打包进同一个 EA 项目。核心不再是单纯看指标数值,而是让程序自动抓「隐藏背离」——也就是价格没破前高/前低、但指标却破了,这种结构常被视为趋势延续的概率信号。 本项目纯教学向,目标就两个:一是练你在 MQL5 里写多指标协同的胆量,二是搞懂怎么用代码识别隐藏看涨/看跌背离。外汇和贵金属杠杆高、滑点跳空频繁,真跑实盘前务必先上 MT5 策略测试器用历史数据验逻辑。 具体落地四件事:比对 RSI 拐点与对应价格走势判定隐藏背离;用 RSI+MA+Stochastic 拼一个 EA 骨架;在图表上把隐藏背离画出来;按美元风险算手数、用不一致烛台尺寸定止损,再依预设风险回报比(RRR)自动下单。打开 MT5 新建 EA,先把这三个指标的 handle 建出来,就能照这个清单一步步填代码。
◍ EA 怎么靠背离抓隐藏动能
这套 EA 的核心是用均线定趋势、RSI 找隐藏背离、随机指标做二次确认,而不是靠随机指标本身去发现背离。RSI 负责比对自身摆动与价格的错位,200 EMA 只用来过滤方向,信号有效性先看结构再看随机突破。 隐藏看涨背离出现在 RSI 走更低的低点、但价格却踩出更高的低点之时,说明表面弱势下买盘承接更强,后续上涨动量可能发酵。隐藏看跌背离则反过来:RSI 更高的高点配价格更低的高点,下行惯性倾向延续。 卖单逻辑里,价格必须低于 200 EMA 确认偏空。系统先定位一根看涨烛形后的最新 RSI 高点,紧跟连续 3 根看跌烛形,在 RSI 与折线图价格对应位各画一条水平线;当 RSI 再破该线且对应价格高点更低,隐藏看跌背离成立。最后等随机指标突破超买(如 80)后 11 根柱内再度破线,超时不开仓,SL 取末次波动高点,TP 由 RRR 参数算。 买入对称处理:价格高于 200 EMA,找看跌烛形后的 RSI 低点加 3 根看涨烛形,画线后等 RSI 新低点低于水平线且价格低点更高,随机跌破超卖(如 20)后 11 根柱内再破方触发,SL 为末次波动低点。外汇与贵金属杠杆高,这类结构信号失效时回撤可能很快,参数上线前建议 MT5 策略测试器先跑历史段。
「给三件工具在 MT5 里发“工牌”」
做 EA 第一步不是写逻辑,而是先让终端认识你要用的指标。MA、RSI、Stochastic 各自需要一个 handle(句柄),本质就是给工具在内存里留个柜子,后面取数全靠它。 具体参数直接决定指标性格:iMA 用 200 周期、MODE_EMA、PRICE_CLOSE,相当于用指数均线看大尺度趋势,对价格反应比 SMA 快;iRSI 周期设 14,是经典的放大倍数,专门抓背离;iStochastic 的 5/3/3 分别代表 %K 周期、%D 平滑、减速平滑,MODE_SMA + STO_LOWHIGH 表示用高低价范围算随机值,灵敏度偏短线。 下面这段代码在 OnInit 里一次性把三个句柄建出来。外汇和贵金属波动大,句柄若返回 INVALID_HANDLE,后续取数会直接崩 EA,实盘前务必在策略测试器里跑一遍确认初始化成功。
class=class="str">"cmt">//DECLEAR INDICATOR HANDLES class="type">int ma_handle; class="type">int rsi_handle; class="type">int stoch_handle; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//INDICATOR PROPERTIES ma_handle = iMA(_Symbol,PERIOD_CURRENT,class="num">200,class="num">0,MODE_EMA,PRICE_CLOSE); rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,class="num">14,PRICE_CLOSE); stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,class="num">5,class="num">3,class="num">3,MODE_SMA,STO_LOWHIGH); class="kw">return(INIT_SUCCEEDED); } class="type">int ma_handle; class="type">int rsi_handle; class="type">int stoch_handle; ma_handle = iMA(_Symbol,PERIOD_CURRENT,class="num">200,class="num">0,MODE_EMA,PRICE_CLOSE); rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,class="num">14,PRICE_CLOSE); stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,class="num">5,class="num">3,class="num">3,MODE_SMA,STO_LOWHIGH);
把指标和烛形数据拉进EA内存
EA要下单,先得知道盘面给了什么信号。从已建好的指标句柄里取数,是后续所有条件判断的前提:随机振荡、RSI、MA三条线的值若不进数组,程序根本无从比较。 声明双精度数组是第一步。ma_buffer[] 装移动平均,rsi_buffer[] 装RSI,stoch_buffer_k[] 与 stoch_buffer_d[] 分别装随机指标的%K和%D;烛形侧则开 open[]、close[]、high[]、low[]、time[] 存OHLC及时间。数组初始为空,等着被填充。 默认数组从最旧数据填起,但交易只看最新。ArraySetAsSeries(..., true) 把最新柱推到索引0,指标与烛形数组都这么处理,两边结构才对齐。 真正取数靠 CopyBuffer() 与 CopyOpen/CopyClose 等系列函数。示例里从倒数第2根柱抓最近30根:指标传参30和1(代码中实际用CopyBuffer时第3参为1、第4参为30),烛形同理,确保价格背景和指标值一一对应。外汇与贵金属波动剧烈,这套取数逻辑若错位,信号会整体偏移,实盘风险偏高。 下面这段初始化代码可直接贴进 MT5 的 EA 模板验证句柄与数组声明:
class=class="str">"cmt">//DECLEAR INDICATOR HANDLES class="type">int ma_handle; class="type">int rsi_handle; class="type">int stoch_handle; class=class="str">"cmt">//DECLEAR DOUBLE VARIABLES THAT STORES INDICATORS DATA class="type">class="kw">double ma_buffer[]; class="type">class="kw">double rsi_buffer[]; class="type">class="kw">double stoch_buffer_k[], stoch_buffer_d[]; class=class="str">"cmt">//DECLEAR VARIABLES TO STORE CANDLE DATA class="type">class="kw">double open[]; class="type">class="kw">double close[]; class="type">class="kw">double high[]; class="type">class="kw">double low[]; class="type">class="kw">datetime time[]; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//INDICATOR PROPERTIES ma_handle = iMA(_Symbol,PERIOD_CURRENT,class="num">200,class="num">0,MODE_EMA,PRICE_CLOSE); rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,class="num">14,PRICE_CLOSE); stoch_handle = iStochastic(_Symbol,PERIOD_CURRENT,class="num">5,class="num">3,class="num">3,MODE_SMA,STO_LOWHIGH); class=class="str">"cmt">//START FROM THE LATEST CANDLE ON THE CHART ArraySetAsSeries(ma_buffer,true); ArraySetAsSeries(rsi_buffer,true); ArraySetAsSeries(stoch_buffer_k,true); ArraySetAsSeries(stoch_buffer_d,true); class=class="str">"cmt">//START FROM THE LATEST CANDLE ON THE CHART ArraySetAsSeries(open,true); ArraySetAsSeries(close,true); ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(time,true); 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) { class=class="str">"cmt">//--- } class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 把指标和K线缓存一次性拉进数组
在 OnTick 里把多品种数据同步到本地数组,是后续做信号判定的前提。下面这段逻辑直接从图表第 2 根 K 线(索引 1)向后取 30 根,把均线、随机指标 K/D、RSI 以及 OHLC 和时间全部拷进缓冲区。 注意 CopyBuffer 的第三、四参数:起点 1、长度 30,意味着你拿到的是 bar[1] 到 bar[30] 的窗口,最新一根 bar[0] 故意不取,避免 Tick 中途价格跳动造成信号抖动。外汇与贵金属杠杆高,这种错位取数能降低假触发概率。 数组声明后必须调 ArraySetAsSeries(..., true),否则默认正序会导致你用 buffer[1] 时指向最老一根而非次新一根。代码里 OnTick 外又重复拷了一次 OHLC,实盘里若已在 tick 内拷贝,这层冗余可以删掉以省开销。 开 MT5 新建 EA 把下面代码贴进去,编译后挂 EURUSD 十五分钟图,用 Print(open[0]) 验证取到的确是次新蜡烛开盘价,就能确认窗口错位符合预期。
class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//COPY INDICATOR&class="macro">#x27;S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART CopyBuffer(ma_handle,class="num">0,class="num">1,class="num">30,ma_buffer); CopyBuffer(stoch_handle,class="num">0,class="num">1,class="num">30,stoch_buffer_k); CopyBuffer(stoch_handle,class="num">1,class="num">1,class="num">30,stoch_buffer_d); CopyBuffer(rsi_handle,class="num">0,class="num">1,class="num">30,rsi_buffer); class=class="str">"cmt">//COPY CANDLE&class="macro">#x27;S DATA FROM THE SECOND BAR ON THE CHART TO THE LAST 31st BAR ON THE CHART CopyOpen(_Symbol,PERIOD_CURRENT,class="num">1,class="num">30,open); CopyClose(_Symbol,PERIOD_CURRENT,class="num">1,class="num">30,close); CopyHigh(_Symbol,PERIOD_CURRENT,class="num">1,class="num">30,high); CopyLow(_Symbol,PERIOD_CURRENT,class="num">1,class="num">30,low); CopyTime(_Symbol,PERIOD_CURRENT,class="num">1,class="num">30,time); } class="type">class="kw">double open[]; class="type">class="kw">double close[]; class="type">class="kw">double high[]; class="type">class="kw">double low[]; class="type">class="kw">datetime time[]; ArraySetAsSeries(open,true); ArraySetAsSeries(close,true); ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(time,true); CopyOpen(_Symbol,PERIOD_CURRENT,class="num">1,class="num">30,open); CopyClose(_Symbol,PERIOD_CURRENT,class="num">1,class="num">30,close); CopyHigh(_Symbol,PERIOD_CURRENT,class="num">1,class="num">30,high); CopyLow(_Symbol,PERIOD_CURRENT,class="num">1,class="num">30,low); CopyTime(_Symbol,PERIOD_CURRENT,class="num">1,class="num">30,time);