MQL5 中如何调用指标·进阶篇
用 IndicatorCreate 拿指标句柄的坑与写法
在 MT5 里用 IndicatorCreate() 拿指标句柄,成功就返回一个正数句柄,失败直接给 -1。凡是带参数的指标,调用前都得先填一个 MqlParam 结构数组,每个元素有 4 个字段,第一个 type 字段决定后面三个里哪个字段真正被当作参数用。 把创建逻辑塞进 OnInit() 而不是每笔订单都调一次,是基本常识——指标句柄只需在程序启动那一下建好。动态数组 params[] 声明时不写大小,所以必须用 ArrayResize() 先定长,移动平均线要传 4 个参数就 resize 成 4。 以快速 MA 为例:周期 5、偏移 0、MODE_SMA 简单平均、PRICE_CLOSE 收盘价,四个全是 int 类型,所以每一项的 type 都写 TYPE_INT,值进 integer_value。IndicatorCreate() 五个实参依次是 Symbol()、0(当前周期)、IND_MA、4、params,返回的句柄存进 MA1。 慢线只需把周期从 5 改成 21 重新建一次;ParabolicSAR 则不同,它的参数是 double,数组 type 要写 TYPE_DOUBLE、值进 double_value,且只要 2 个参数所以 ArrayResize(params,2)。外汇和贵金属波动大、杠杆高,句柄建错会导致指标读取失效,实盘前务必在策略测试器里先跑一遍确认返回值不为 -1。
class="type">int MA1 = class="num">0, class=class="str">"cmt">// 声明变量存储快速MA指标的句柄 MA2 = class="num">0, class=class="str">"cmt">// 声明变量存储慢速MA指标的句柄 SAR = class="num">0; class=class="str">"cmt">// 声明变量存储SAR指标的句柄 class="type">MqlParam params[]; class=class="str">"cmt">// 存储指标参数的数组 class="type">int OnInit() { class=class="str">"cmt">//--- class=class="str">"cmt">// 设置params的大小和被调用指标参数数量一致 ArrayResize(params,class="num">4); class=class="str">"cmt">// 设置快速MA的周期 params[class="num">0].type =TYPE_INT; params[class="num">0].integer_value=class="num">5; class=class="str">"cmt">// 偏移 params[class="num">1].type =TYPE_INT; params[class="num">1].integer_value=class="num">0; class=class="str">"cmt">// 计算方法:简单平均 params[class="num">2].type =TYPE_INT; params[class="num">2].integer_value=MODE_SMA; class=class="str">"cmt">// 用于计算的价格类型:收盘价 params[class="num">3].type =TYPE_INT; params[class="num">3].integer_value=PRICE_CLOSE; MA1 = IndicatorCreate(Symbol(), class=class="str">"cmt">// 用于计算指标的交易品种 class="num">0, class=class="str">"cmt">// 时间框架。class="num">0 代表当前时间框架 IND_MA, class=class="str">"cmt">// ENUM_INDICATOR枚举值定义的指标类型 class="num">4, class=class="str">"cmt">// 数组传递的参数数量 params class=class="str">"cmt">// 参数数组 ); class=class="str">"cmt">//--- class="kw">return(class="num">0); }
◍ 用 IndicatorCreate 拼出双均线加 SAR
在 MT5 里用 IndicatorCreate 动态挂指标,比直接调 iMA、iSAR 更底层,也更适合做多品种批量扫描。下面这段把快线、慢线和 SAR 一次性建出来,参数全走 MqlParam 结构体数组。 快线用了 8 周期简单平均,慢线用 21 周期,都是基于收盘价(PRICE_CLOSE)计算;注意 params 数组在创建慢线前不用重新 resize,因为还是 4 个整型参数。 SAR 部分先把 params 用 ArrayResize 砍到 2 个元素,步长 0.02、最大值 0.2,这是 MT5 里抛物线转向指标的默认区间。外汇和贵金属波动大,SAR 在单边市里可能反应滞后,实盘前务必在策略测试器里跑一遍。 代码里 Symbol() 和 0 分别代表当前品种和当期图表周期,换品种扫描时把 Symbol() 替成具体符号即可。
params[class="num">3].type =TYPE_INT; params[class="num">3].integer_value=PRICE_CLOSE; MA1 = IndicatorCreate(Symbol(), class="num">0, IND_MA, class="num">4, params); class=class="str">"cmt">// 设置慢速MA的周期 params[class="num">0].type =TYPE_INT; params[class="num">0].integer_value=class="num">21; class=class="str">"cmt">// 偏移 params[class="num">1].type =TYPE_INT; params[class="num">1].integer_value=class="num">0; class=class="str">"cmt">// 计算方法:简单平均 params[class="num">2].type =TYPE_INT; params[class="num">2].integer_value=MODE_SMA; class=class="str">"cmt">// 用于计算的价格类型:收盘价 params[class="num">3].type =TYPE_INT; params[class="num">3].integer_value=PRICE_CLOSE; MA2 = IndicatorCreate(Symbol(), class="num">0, IND_MA, class="num">4, params); class=class="str">"cmt">// 改变数组的大小来存储SAR指标的参数 ArrayResize(params,class="num">2); class=class="str">"cmt">// 步长 params[class="num">0].type =TYPE_DOUBLE; params[class="num">0].double_value = class="num">0.02; class=class="str">"cmt">// 最大值 params[class="num">1].type =TYPE_DOUBLE; params[class="num">1].double_value = class="num">0.2; SAR = IndicatorCreate(Symbol(), class="num">0, IND_SAR, class="num">2, params); class=class="str">"cmt">//--- class="kw">return(class="num">0); }
「用 iCustom 在启动阶段拿指标句柄」
获取指标句柄的第二类路子,是直接调技术指标函数。这类函数每个都对应 MT5 自带的一个标准指标,参数组合随目标指标而定。比如 iCCI() 拿 CCI 句柄时,要传品种、周期、平滑周期和价格类型——把最后一项换成另一个指标句柄,就能让该指标基于前级输出算,而不是裸价格。 iCustom() 比专函更灵活,能挂任何自定义指标。它前三个参数固定:品种填 NULL 即用当前品种(Symbol() 也行,差异极小)、周期填 0 表示当前图表周期、指标名必须已在 MQL5/Indicators/ 下编译好。后面跟着的才是被调指标的输入,类型顺序要对上,缺省就走默认。 句柄最好在 EA 初始化时一次性拿完,别放进 OnTick 反复申请。下面这段代码在 OnInit 里同时挂了两条均线(5 与 21 周期 SMA)和 ParabolicSAR,返回 0 即初始化成功。外汇与贵金属杠杆高,句柄失效或参数错配可能导致信号中断,实盘前务必在策略测试器跑一遍。 别把 NULL 和 0 当万能填法 若切换品种交易,NULL 不会自动跟票,跨品种 EA 里显式写 Symbol() 更稳;周期写 0 在加载多周期逻辑时容易踩坑,明确时段常量更不易误判。
CCI = iCCI( Symbol(), class=class="str">"cmt">// 交易品种 PERIOD_CURRENT, class=class="str">"cmt">// 周期 class="num">20, class=class="str">"cmt">// 平滑周期 PRICE_CLOSE class=class="str">"cmt">// 价格类型或者句柄 ); class="type">int OnInit() { class=class="str">"cmt">//--- MA1 = iCustom(NULL,class="num">0,"Custom Moving Average", class="num">5, class=class="str">"cmt">// 周期 class="num">0, class=class="str">"cmt">// 偏移 MODE_SMA, class=class="str">"cmt">// 计算方式 PRICE_CLOSE class=class="str">"cmt">// 在收盘价上进行计算 ); MA2 = iCustom(NULL,class="num">0,"Custom Moving Average", class="num">21, class=class="str">"cmt">// 周期 class="num">0, class=class="str">"cmt">// 偏移 MODE_SMA, class=class="str">"cmt">// 计算方式 PRICE_CLOSE class=class="str">"cmt">// 在收盘价上进行计算 ); SAR = iCustom(NULL,class="num">0,"ParabolicSAR", class="num">0.02, class=class="str">"cmt">// 步长 class="num">0.2 class=class="str">"cmt">// 最大值 ); class=class="str">"cmt">//--- class="kw">return(class="num">0); }
收束
把指标句柄交给 CopyBuffer() 之后,真正干活的地方就在 OnTick() 里。上面那段代码每跳一次价就重拉最近 20 根柱的 MA 与 SAR 缓冲,复制失败立刻把错误码打进日志,思路直接能抄进自己的 EA。 外汇和贵金属杠杆高、滑点随机,这类轮询式取数在极端行情可能漏掉瞬时穿透,实盘前务必在 MT5 策略测试器用历史数据跑一遍。 MQL5 的指标调用远不止本文说的几种基础手法,用类封装句柄和缓冲会省掉大量重复代码,留着这个方向自己往后挖。
class="type">void OnTick() { class=class="str">"cmt">//--- class=class="str">"cmt">// 存储指标变量值的动态数组 class="type">class="kw">double _ma1[], _ma2[], _sar[]; class=class="str">"cmt">// 将数组设置成和时间序列一样,例如数组索引为0的元素 class=class="str">"cmt">// 存储最近一个柱形的值,索引为1的元素存储第二近柱形的值,等等。 ArraySetAsSeries(_ma1, true); ArraySetAsSeries(_ma2, true); ArraySetAsSeries(_sar, true); class=class="str">"cmt">// 使用指标句柄将指标的缓存值,复制到数组中, class=class="str">"cmt">//这些数组正是用于这个目的 if (CopyBuffer(MA1,class="num">0,class="num">0,class="num">20,_ma1) < class="num">0){Print("CopyBufferMA1 error =",GetLastError());} if (CopyBuffer(MA2,class="num">0,class="num">0,class="num">20,_ma2) < class="num">0){Print("CopyBufferMA2 error =",GetLastError());} if (CopyBuffer(SAR,class="num">0,class="num">0,class="num">20,_sar) < class="num">0){Print("CopyBufferSAR error =",GetLastError());} class=class="str">"cmt">//--- }