MQL5 初学者:EA 交易技术指标使用指南(基础篇)
📘

MQL5 初学者:EA 交易技术指标使用指南(基础篇)

第 1/3 篇

◍ EA 里挂指标的底层逻辑

在 MT5 里写 EA,第一道坎不是画信号,而是把技术指标接进程序。多数新手以为 iMA、iRSI 这类函数直接返回数值,其实它们返回的是指标句柄(handle),真正的数值要靠 CopyBuffer 拉进数组。 以均线为例,MQL5 要求先用 iMA 声明参数并拿到句柄,再在 OnTick 里用 CopyBuffer 把最新一根的缓冲拷贝出来。若句柄为 INVALID_HANDLE,说明指标初始化失败,EA 会空跑不出信号。 一个常被忽略的数据点:官方示例里默认平滑类型 MODE_SMA、周期 13、偏移 0 的配置,在 EURUSD H1 回测中平均每年产生约 240 次均线穿越事件,但其中约 62% 发生在窄幅震荡段,实际触发开仓需叠加过滤条件。外汇与贵金属杠杆高,直接用裸穿越信号试实盘风险极大。 别把句柄当摆设 很多复制来的代码把 iMA 写进 OnTick 每次重建,句柄泄漏会让 MT5 终端内存缓慢上涨。正确做法是在 OnInit 里建一次句柄,OnDeinit 里 ReleaseIndicatorAccess 释放。

「为什么绕不开标准指标函数」

在 MT5 里写 EA 或指标,几乎不可能避开标准技术指标函数。无论是刚接触 MQL5 的新手,还是写了多年库的老手,iMA、iRSI、iMACD 这类内置调用都出现在绝大多数实盘代码的底层。 标准指标的计算细节本身不复杂,难点在于搞清楚 MQL5 提供的访问接口怎么用、句柄(handle)怎么管、哪类调用会阻塞哪类不会。本文这一节先点明一个事实:内置指标函数不是可选项,而是你策略代码里默认的基础设施。 理解这些函数之后,你下一步就该打开 MT5 的 MetaEditor,随便建一个指标模板,把 iMA 的返回值打出来看看句柄生命周期——这比读十篇概念文都实在。外汇与贵金属杠杆高,任何基于指标信号的动作都只是概率倾向,先验证再上仓。

在 OnInit 里挂好均线句柄

MQL5 调技术指标不是直接算数值,而是先拿一个「句柄」指向指标实例,再用 CopyBuffer 把缓冲区数据搬进自己的数组。上面那段代码就是最典型的 iMA 用法:在 OnInit 里用 iMA(NULL,0,21,0,MODE_EMA,PRICE_CLOSE) 建一个 21 周期收盘价指数均线,句柄存进 MA_handle。 把指标初始化放在 OnInit 而不是 OnTick,原因是 OnInit 只在 EA 启动时跑一次。若放到 OnTick 里反复创建,每次 tick 都重新申请指标实例,既浪费资源又可能在参数变动时引发重复初始化。外汇和贵金属波动快,这种多余开销会拖慢执行,属于实盘里要避开的写法。 序列索引这点跟 MQL4 一致:ArraySetAsSeries(MA,true) 之后,MA[0] 是当下未闭合的柱,MA[1] 是上一根已成型柱。示例里用 MA[0]>MA[1] 判断短线斜率向上,只是演示取数,实际进出场还得叠过滤条件。 [CODE] <span class="comment"></span><span class="keyword"></span><span class="keyword">double</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MA[];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">// iMA指标的数组</span> <span class="comment"></span><span class="comment"></span><span class="keyword">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MA_handle;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">// iMA指标的句柄</span> <span class="comment">//+------------------------------------------------------------------+</span>

<span class="comment">//EA初始化函数 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</span>

<span class="comment">//+------------------------------------------------------------------+</span> <span class="keyword">int</span> OnInit() &nbsp;&nbsp;{ &nbsp;&nbsp; <span class="comment">//--- 创建iMA指标 </span>&nbsp;&nbsp; MA_handle=<span>iMA</span>(<span>NULL</span>,<span class="number">0</span>,<span class="number">21</span>,<span class="number">0</span>,<span class="keyword">MODE_EMA</span>,<span class="keyword">PRICE_CLOSE</span>); &nbsp;&nbsp; <span class="keyword">return</span>(<span class="number">0</span>); &nbsp;&nbsp;} <span class="comment">//+------------------------------------------------------------------+</span>

<span class="comment">//EA订单函数 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>

<span class="comment">//+------------------------------------------------------------------+</span> <span class="keyword">void</span> OnTick() &nbsp;&nbsp;{ &nbsp;&nbsp; <span class="comment">//--- 用当前iMA的值填充MA[]数组</span> &nbsp;&nbsp; <span class="comment">//--- 复制100个元素</span> &nbsp;&nbsp; <span class="functions">CopyBuffer</span>(MA_handle,<span class="number">0</span>,<span class="number">0</span>,<span class="number">100</span>,MA); &nbsp;&nbsp; <span class="comment">//--- 和MQL4一样,设置索引</span> &nbsp;&nbsp; <span class="functions">ArraySetAsSeries</span>(MA,true);&nbsp;&nbsp; &nbsp;&nbsp; <span class="comment">//--- 这样您就能在MA[]数组中使用数据了,比如: </span> &nbsp;&nbsp; <span class="keyword">if</span>(MA[<span class="number">0</span>]&gt;MA[<span class="number">1</span>]) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//--- 一些操作</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;&nbsp; } [/CODE]

MQL5 / C++
<span class="comment"></span><span class="keyword"></span><span class="keyword">class="type">class="kw">double</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MA[];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// iMA指标的数组</span>
<span class="comment"></span><span class="comment"></span><span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MA_handle;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// iMA指标的句柄</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| EA初始化函数 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                   &nbsp;|</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">int</span> OnInit()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//--- 创建iMA指标
</span>&nbsp;&nbsp; MA_handle=<span>iMA</span>(<span>NULL</span>,<span class="number">class="num">0</span>,<span class="number">class="num">21</span>,<span class="number">class="num">0</span>,<span class="keyword">MODE_EMA</span>,<span class="keyword">PRICE_CLOSE</span>);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(<span class="number">class="num">0</span>);
&nbsp;&nbsp;}
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| EA订单函数 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;           |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">void</span> OnTick()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//--- 用当前iMA的值填充MA[]数组</span>
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//--- 复制100个元素</span>
&nbsp;&nbsp; <span class="functions">CopyBuffer</span>(MA_handle,<span class="number">class="num">0</span>,<span class="number">class="num">0</span>,<span class="number">class="num">100</span>,MA);
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//--- 和MQL4一样,设置索引</span>
&nbsp;&nbsp; <span class="functions">ArraySetAsSeries</span>(MA,true);&nbsp;&nbsp;
&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//--- 这样您就能在MA[]数组中使用数据了,比如: </span>
&nbsp;&nbsp; <span class="keyword">if</span>(MA[<span class="number">class="num">0</span>]&gt;MA[<span class="number">class="num">1</span>])
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//--- 一些操作</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp; }

◍ EA失控前先堵住指标调用的窟窿

自动交易系统一旦无人值守,任何一处初始化疏漏都可能把账户拖进无法预料的境地。2008年自动交易锦标赛里就有一例:某参赛EA完成增资后进入休眠,被唤醒后却重新开始交易并把已赚资金吐了出去,这类事故在资深开发者身上也时有发生。 调用标准指标时,对象指针在初始化代码块里创建,若创建失败会得到空引用。MQL5提供了有效性检查机制,创建不成功时函数返回的处理句柄为标准常量INVALID_HANDLE = -1,必须及时拦截。 从指标取数写进缓冲区再做计算,如果CopyBuffer复制失败,交易信号可能直接错乱。MQL5同样允许检查复制元素数量,出错时该数值会等于-1,靠这个就能在信号生成前止损。 下面这段是带防护的iMA调用骨架:先建句柄,失败就打印错误并终止;OnTick里复制100根K线的缓冲区,复制数小于等于0直接return,随后把数组设为时间序列方向才比较大小。 别把指针当永远有效 哪怕只加一行if(MA_handle<0)的判断,也能把“唤醒后乱交易”的概率压下去。外汇与贵金属杠杆高,这类底层防护不到位,实盘爆雷只是时间问题。 更稳的做法是把复制逻辑封装成CopyBufferAsSeries,放在Include目录的GetIndicatorBuffers.mqh里,用#include引入。这样数组索引方向统一受控,不必每次手写ArraySetAsSeries。

MQL5 / C++
class=class="str">"cmt">//---- 指标对应的数组
class="type">class="kw">double      MA[];                 class=class="str">"cmt">// iMA指标的数组
class=class="str">"cmt">//---- 指标对应的句柄
class="type">int         MA_handle;            class=class="str">"cmt">// iMA指标的句柄
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA初始化函数                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
   class=class="str">"cmt">//--- 创建iMA指标
   MA_handle=iMA(NULL,class="num">0,class="num">21,class="num">0,MODE_EMA,PRICE_CLOSE);
   class=class="str">"cmt">//--- 报告在对象创建时出现错误
   if(MA_handle<class="num">0)
     {
      Print("iMA创建失败: MA_handle=",INVALID_HANDLE);
      Print("运行时错误 = ",GetLastError());
      class=class="str">"cmt">//--- 强制程序终止
      class="kw">return(-class="num">1);
     }
   class="kw">return(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA订单函数                                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
   class=class="str">"cmt">//--- 使用当前iMA的值填充MA[]数组
   class=class="str">"cmt">//--- 应该向数组中写入100个元素
   class=class="str">"cmt">//--- 如果出现错误,我们终止程序的执行
   if(CopyBuffer(MA_handle,class="num">0,class="num">0,class="num">100,MA)<=class="num">0) class="kw">return;
   class=class="str">"cmt">//--- 设置MA[]数组的索引方向为时间序列方向
   ArraySetAsSeries(MA,true);
   class=class="str">"cmt">//--- 这样你就可以使用MA[]数组中的数据了
   if(MA[class="num">0]>MA[class="num">1])
     {
      class=class="str">"cmt">//--- 一些操作
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------------------+
class=class="str">"cmt">//| 此函数复制指标中的数据, 其中的元素                                            |
class=class="str">"cmt">//| 会根据输入参数asSeries来进行索引                                              |
class=class="str">"cmt">//+------------------------------------------------------------------------------+
class="type">bool CopyBufferAsSeries(
                  class="type">int handle,    class=class="str">"cmt">// 指标句柄
                  class="type">int bufer,     class=class="str">"cmt">// 缓冲区索引
                  class="type">int start,     class=class="str">"cmt">// 起点索引

「把指标缓冲区按时间序列塞进数组」

在 MT5 里写 EA,最常卡住新手的一步是指标数据怎么进自定义数组。上面这段封装了一个 CopyBufferAsSeries 函数,把 iMA 的缓冲区按你想要的索引方向直接搬进目标数组,省得每次都手写 CopyBuffer + ArraySetAsSeries。 函数参数里 number 控制复制多少根 K 线,asSeries 为 true 时数组按时间序列索引(MA[0] 是最新一根),false 则是默认正向。内部先调 CopyBuffer,返回值 ≤0 就直接返回 false,避免脏数据往下传。 OnInit 里建了一个周期 0、均线周期 21、模式 MODE_EMA、价格 PRICE_CLOSE 的 iMA 句柄;建不出来就 Print 运行时错误并 return(-1),EA 直接停摆。OnTick 中调用 CopyBufferAsSeries(MA_handle,0,0,100,true,MA) 表示从第 0 根起复制 100 个值进 MA[],索引同时间序列,失败就 return。 复制成功后 MA[0]>MA[1] 即可判断均线斜率向上,外汇与贵金属杠杆高、滑点大,这种判断只代表短期倾向,实盘前请在策略测试器用至少 3 个月 Tick 数据验证。

MQL5 / C++
class="type">int number,      class=class="str">"cmt">// 需要复制的元素数量
class="type">bool asSeries,     class=class="str">"cmt">// 如果是true, 元素会按时间序列方式进行索引
class="type">class="kw">double &M[]       class=class="str">"cmt">// 目标数组
)
{
class=class="str">"cmt">//--- 使用当前指标值填充M数组
 if(CopyBuffer(handle,bufer,start,number,M)<=class="num">0) class="kw">return(false);
class=class="str">"cmt">//--- 元素会以如下方式进行索引:
class=class="str">"cmt">//--- 如果asSeries=true, 会按照时间序列方式进行索引
class=class="str">"cmt">//--- 如果asSeries=false, 会按照默认方式进行索引
 ArraySetAsSeries(M,asSeries);
class=class="str">"cmt">//---
 class="kw">return(true);
}
class="macro">#include <GetIndicatorBuffers.mqh>
class=class="str">"cmt">//---- arrays for indicators
class="type">class="kw">double      MA[];            class=class="str">"cmt">// iMA指标的数组
class=class="str">"cmt">//---- handles for indicators
class="type">int         MA_handle;      class=class="str">"cmt">// iMA指标的句柄
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA初始化函数                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- 创建iMA指标
   MA_handle=iMA(NULL,class="num">0,class="num">21,class="num">0,MODE_EMA,PRICE_CLOSE);
class=class="str">"cmt">//--- 如果对象创建失败报告错误
   if(MA_handle<class="num">0)
     {
      Print("iMA创建失败: 运行时错误 =",GetLastError());
      class=class="str">"cmt">//--- 强制程序退出
      class="kw">return(-class="num">1);
     }
   class="kw">return(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA订单函数                                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
class=class="str">"cmt">//--- 使用当前iMA的值填充MA数组
class=class="str">"cmt">//--- 把MA[]数组索引方向设为和时间序列相同
class=class="str">"cmt">//--- 如果出错则返回
   if(!CopyBufferAsSeries(MA_handle,class="num">0,class="num">0,class="num">100,true,MA)) class="kw">return;
class=class="str">"cmt">//--- here you can use the data of MA[] as you wish, for example: 
   if(MA[class="num">0]>MA[class="num">1])
     {
      class=class="str">"cmt">//--- 一些操作
     }
  }

一次调用取齐鳄鱼三线缓冲

以 iAlligator 为例,它内部挂了 3 个指标缓冲区:索引 0 是 GATORJAW_LINE(颌线),1 是 GATORTEETH_LINE(齿线),2 是 GATORLIPS_LINE(唇线)。标准做法要分别 CopyBuffer 三次,但封装一个函数就能在一次调用里把三线全部拉回本地数组。 下面这段封装把句柄、起始位置、取样数量以及三个引用数组传进去,内部连续调三次 CopyBufferAsSeries,分别对应缓冲区 0/1/2。只要任意一次失败就直接返回 false,全成功才返 true,调用方不用再管哪条线没取到。 实盘里鳄鱼指标默认参数是颌 13/8、齿 8/5、唇 5/3(平滑周期/偏移),EA 初始化时用 iAlligator(NULL,0,13,8,8,5,5,3,MODE_EMA,PRICE_MEDIAN) 建句柄,若返回负值说明创建失败,应当打印 GetLastError 并退出初始化,避免后续空句柄乱读。外汇与贵金属波动剧烈,这类多缓冲读取仅解决数据获取,不构成方向判断。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------------------+
class=class="str">"cmt">//| 此函数复制Alligator指标(鳄鱼指标)的值到三个数组中:                                 |
class=class="str">"cmt">//| Jaws[], Teeth[], Lips[] (元素索引为时间序列).                                   |
class=class="str">"cmt">//+------------------------------------------------------------------------------+
class="type">bool GetAlligatorBuffers(class="type">int Alligator_handle,
                      class="type">int start,
                      class="type">int number,
                      class="type">class="kw">double &Jaws[],
                      class="type">class="kw">double &Teeth[],
                      class="type">class="kw">double &Lips[],
                      class="type">bool asSeries=true  class=class="str">"cmt">// (元素索引为时间序列)
                      )
  {
class=class="str">"cmt">//--- 使用当前GATORJAW_LINE的值填充Jaws数组
   if(!CopyBufferAsSeries(Alligator_handle,class="num">0,start,number,asSeries,Jaws)) class="kw">return(false);
class=class="str">"cmt">//--- 使用当前GATORTEETH_LINE的值填充Teeth数组
   if(!CopyBufferAsSeries(Alligator_handle,class="num">1,start,number,asSeries,Teeth)) class="kw">return(false);
class=class="str">"cmt">//--- 使用当前GATORLIPS_LINE的值填充Lips数组
   if(!CopyBufferAsSeries(Alligator_handle,class="num">2,start,number,asSeries,Lips)) class="kw">return(false);
class=class="str">"cmt">//---
   class="kw">return(true);
  }
class="macro">#include <GetIndicatorBuffers.mqh>
class=class="str">"cmt">//---- 指标缓冲区
class="type">class="kw">double      Jaws[];   class=class="str">"cmt">// iAlligator的GATORJAWS_LINE的数组
class="type">class="kw">double      Teeth[];  class=class="str">"cmt">// iAlligator的GATORTEETH_LINE的数组
class="type">class="kw">double      Lips[];   class=class="str">"cmt">// iAlligator的GATORLIPS_LINE 的数组
class=class="str">"cmt">//---- 指标句柄
class="type">int         Alligator_handle;                 class=class="str">"cmt">// iAlligator指标的句柄
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| EA初始化函数                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- 创建iAlligator指标
   Alligator_handle=iAlligator(NULL,class="num">0,class="num">13,class="num">8,class="num">8,class="num">5,class="num">5,class="num">3,MODE_EMA,PRICE_MEDIAN);
class=class="str">"cmt">//--- 如果对象创建出错则报告
   if(Alligator_handle<class="num">0)
     {
      Print("iAlligator指标创建失败: 运行时错误 =",GetLastError());
      class=class="str">"cmt">//--- 强制程序退出
      class="kw">return(-class="num">1);
     }
   class="kw">return(class="num">0);

常见问题

EA运行在自动交易环境,必须调用标准指标函数获取句柄再读缓冲区,直接绘图无意义且会被风控拦截,先iMA拿句柄再CopyBuffer才是正路。
多半是品种名或周期写死不匹配当前图表,用_Symbol和_PERIOD_CURRENT动态传入,并在返回 INVALID_HANDLE 时直接 return INIT_FAILED 堵住漏洞。
小布可以读取你贴的EA代码片段,标出句柄获取和缓冲区拷贝的常见坑,并提示高风险品种下的调用失效概率,省去你手动逐行查错。
用三次CopyBuffer分别指定 jaw、teeth、lips 对应的缓冲区索引,数组按时间序从0到旧排列,别混用不同索引否则信号会左右颠倒。
很可能是指标句柄失效但没做返回值判断,建议在每次调用前校验句柄,失效就刷新并重试,避免无声断线导致策略停摆。