用 MQL5 语言编写的 20 种交易信号·综合运用
📘

用 MQL5 语言编写的 20 种交易信号·综合运用

第 3/3 篇

「通道与止损轨道的双信号拼接」

多信号合成时,先各自取数再判条件,是降低主循环耦合的直接办法。下面这段把 PriceChannelGalaher 与 NRTR 两个自定义指标分别包成独立函数,返回 1 / -1 / 0 三种状态,主逻辑只管求和或过滤。 TradeSignal_15 里用 iCustom 挂 PriceChannelGalaher,若句柄无效先建一次并返回 0;之后 CopyBuffer 取两条线各 3 根、CopyClose 取 2 根收盘价,任何不足 3 或 2 的拷贝都直接 return(0),避免数组越界。三个数组全部 ArraySetAsSeries(...,true) 改成时间倒序,Close[1] 即最新收盘价。 信号判定很朴素:Close[1] 上破 gc1_buffer[2] 给多头 1,下破 gc2_buffer[2] 给空头 -1,否则 0。注意这里比的是前两根的通道值,等于用一根 K 线的收盘确认对早一步通道的穿透。 TradeSignal_16 接的是 NRTR,参数写死 40 与 2.0(周期 40、倍数 2.0),外汇与贵金属波动大时这套参数可能偏钝,建议开 MT5 后改 Period 与倍数各跑一轮肉眼比对。NRTR 本质是非对称追踪止损轨道,信号方向与轨道翻转一致,但高杠杆品种逆势扛单风险极高。

MQL5 / C++
class="type">int TradeSignal_15()
  {
   class="type">int sig=class="num">0;
   if(h_gc==INVALID_HANDLE)
    {
     h_gc=iCustom(Symbol(),Period(),"PriceChannelGalaher");
     class="kw">return(class="num">0);
    }
   else
    {
     if(CopyBuffer(h_gc,class="num">0,class="num">0,class="num">3,gc1_buffer)<class="num">3)
       class="kw">return(class="num">0);
     if(CopyBuffer(h_gc,class="num">1,class="num">0,class="num">3,gc2_buffer)<class="num">3)
       class="kw">return(class="num">0);
     if(CopyClose(Symbol(),Period(),class="num">0,class="num">2,Close)<class="num">2)
       class="kw">return(class="num">0);
     if(!ArraySetAsSeries(gc1_buffer,true))
       class="kw">return(class="num">0);
     if(!ArraySetAsSeries(gc2_buffer,true))
       class="kw">return(class="num">0);
     if(!ArraySetAsSeries(Close,true))
       class="kw">return(class="num">0);
    }
class=class="str">"cmt">//--- 检查条件并设置 sig 的值
   if(Close[class="num">1]>gc1_buffer[class="num">2])
     sig=class="num">1;
   else if(Close[class="num">1]<gc2_buffer[class="num">2])
     sig=-class="num">1;
   else sig=class="num">0;
class=class="str">"cmt">//--- 返回交易信号
   class="kw">return(sig);
  }
class="type">int TradeSignal_16()
  {
   class="type">int sig=class="num">0;
   if(h_nrtr==INVALID_HANDLE)
    {
     h_nrtr=iCustom(Symbol(),Period(),"NRTR",class="num">40,class="num">2.0);
     class="kw">return(class="num">0);
    }
   else
    {

NRTR 与鳄鱼信号函数的缓冲读取写法

把指标信号转成交易决策,第一步永远是先把缓冲区拿稳。下面两段函数分别处理 NRTR 双线和鳄鱼三线,逻辑骨架一致:句柄无效就现建,建完立刻 return(0) 等下一 tick,避免空缓冲踩坑。 NRTR 部分只取两个缓冲各 2 根柱,CopyBuffer 返回值小于 2 直接退出;随后用 ArraySetAsSeries 把数组倒序,让 [1] 代表上一根已完成 K 线。若 nrtr1_buffer[1] 大于 0 判多(sig=1),否则看 nrtr2_buffer[1] 大于 0 判空(sig=-1),都不满足给 0。 [CODE] if(CopyBuffer(h_nrtr,0,0,2,nrtr1_buffer)<2) return(0); if(CopyBuffer(h_nrtr,1,0,2,nrtr2_buffer)<2) return(0); if(!ArraySetAsSeries(nrtr1_buffer,true)) return(0); if(!ArraySetAsSeries(nrtr2_buffer,true)) return(0); } //--- 检查条件并设置 sig 的值 if(nrtr1_buffer[1]>0) sig=1; else if(nrtr2_buffer[1]>0) sig=-1; else sig=0; //--- 返回交易信号 return(sig); } int TradeSignal_17() { int sig=0; if(h_al==INVALID_HANDLE) { h_al=iAlligator(Symbol(),Period(),13,0,8,0,5,0,MODE_SMMA,PRICE_MEDIAN); return(0); } else { if(CopyBuffer(h_al,0,0,2,al1_buffer)<2) return(0); if(CopyBuffer(h_al,1,0,2,al2_buffer)<2) return(0); if(CopyBuffer(h_al,2,0,2,al3_buffer)<2) return(0); if(!ArraySetAsSeries(al1_buffer,true)) return(0); if(!ArraySetAsSeries(al2_buffer,true)) return(0); if(!ArraySetAsSeries(al3_buffer,true)) return(0); } //--- 检查条件并设置 sig 的值 if(al3_buffer[1]>al2_buffer[1] && al2_buffer[1]>al1_buffer[1]) sig=1; else if(al3_buffer[1]<al2_buffer[1] && al2_buffer[1]<al1_buffer[1]) sig=-1; else sig=0; //--- 返回交易信号 return(sig); } int TradeSignal_18() { int sig=0; if(h_ama==INVALID_HANDLE) { [/CODE] 鳄鱼函数 TradeSignal_17 用 iAlligator 建句柄,参数 13/8/5 对应颚/齿/唇周期,PRICE_MEDIAN 作价。三线各取 2 根后倒序,当 al3[1]>al2[1]>al1[1] 为空头排列信号(sig=1 多),反之为空头(sig=-1),外汇与贵金属波动下该排列仅代表趋势倾向可能延续,不预示必然方向。 开 MT5 把这段代码贴进 EA 的 signal 区,把 h_nrtr、h_al 声明为全局句柄,即可在日志里打印 sig 观察两指标共振频率。高频切换周期时句柄无效分支会频繁触发 return(0),属正常保护。

MQL5 / C++
if(CopyBuffer(h_nrtr,class="num">0,class="num">0,class="num">2,nrtr1_buffer)<class="num">2)
   class="kw">return(class="num">0);
if(CopyBuffer(h_nrtr,class="num">1,class="num">0,class="num">2,nrtr2_buffer)<class="num">2)
   class="kw">return(class="num">0);
if(!ArraySetAsSeries(nrtr1_buffer,true))
   class="kw">return(class="num">0);
if(!ArraySetAsSeries(nrtr2_buffer,true))
   class="kw">return(class="num">0);
}
class=class="str">"cmt">//--- 检查条件并设置 sig 的值
 if(nrtr1_buffer[class="num">1]>class="num">0)
    sig=class="num">1;
 else if(nrtr2_buffer[class="num">1]>class="num">0)
    sig=-class="num">1;
 else sig=class="num">0;
class=class="str">"cmt">//--- 返回交易信号
 class="kw">return(sig);
 }
class="type">int TradeSignal_17()
  {
  class="type">int sig=class="num">0;
  if(h_al==INVALID_HANDLE)
   {
    h_al=iAlligator(Symbol(),Period(),class="num">13,class="num">0,class="num">8,class="num">0,class="num">5,class="num">0,MODE_SMMA,PRICE_MEDIAN);
    class="kw">return(class="num">0);
   }
  else
   {
    if(CopyBuffer(h_al,class="num">0,class="num">0,class="num">2,al1_buffer)<class="num">2)
      class="kw">return(class="num">0);
    if(CopyBuffer(h_al,class="num">1,class="num">0,class="num">2,al2_buffer)<class="num">2)
      class="kw">return(class="num">0);
    if(CopyBuffer(h_al,class="num">2,class="num">0,class="num">2,al3_buffer)<class="num">2)
      class="kw">return(class="num">0);
    if(!ArraySetAsSeries(al1_buffer,true))
      class="kw">return(class="num">0);
    if(!ArraySetAsSeries(al2_buffer,true))
      class="kw">return(class="num">0);
    if(!ArraySetAsSeries(al3_buffer,true))
      class="kw">return(class="num">0);
   }
class=class="str">"cmt">//--- 检查条件并设置 sig 的值
 if(al3_buffer[class="num">1]>al2_buffer[class="num">1] && al2_buffer[class="num">1]>al1_buffer[class="num">1])
    sig=class="num">1;
 else if(al3_buffer[class="num">1]<al2_buffer[class="num">1] && al2_buffer[class="num">1]<al1_buffer[class="num">1])
    sig=-class="num">1;
 else sig=class="num">0;
class=class="str">"cmt">//--- 返回交易信号
 class="kw">return(sig);
 }
class="type">int TradeSignal_18()
  {
  class="type">int sig=class="num">0;
  if(h_ama==INVALID_HANDLE)
   {

◍ AMA、AO与一目信号的分段求值

这段逻辑把三个独立指标拆成惰性初始化的信号函数,首次调用只建句柄并返回 0,避免在历史数据不足时误触发。AMA 用 9/2/30/0 参数取收盘价,AO 取默认周期,一目云用 9/26/52 标准参数,三个函数结构一致但缓冲区长度不同。 AMA 段只拷 3 根缓冲做相邻比较:ama_buffer[2]<ama_buffer[1] 给多头 sig=1,反向给 -1,相等归零。注意这里比的是倒数第二根和倒数第三根,当前根未参与,属于滞后一帧的确认。 AO 函数拷 20 根但只判 ao_buffer[1] 是否等于 0 或 1,这种写法是原信号源做了二值化预处理,普通 MT5 的 iAO 不会直接出 0/1,实盘跑前得确认上游缓冲是否被改写过。 一目云段同时拉 Tenkan(0号缓冲)和 Kijun(1号缓冲) 各 2 根,用 ich1_buffer[1]>ich2_buffer[1] 给 sig=1,即迟一帧的穿越判定。外汇与贵金属杠杆高,这类滞后信号在震荡市可能连续反转,验证时先用策略测试器跑 EURUSD 15M 看信号密度。

MQL5 / C++
h_ama=iAMA(Symbol(),Period(),class="num">9,class="num">2,class="num">30,class="num">0,PRICE_CLOSE);
class="kw">return(class="num">0);
 }
 else
 {
 if(CopyBuffer(h_ama,class="num">0,class="num">0,class="num">3,ama_buffer)<class="num">3)
 class="kw">return(class="num">0);
 if(!ArraySetAsSeries(ama_buffer,true))
 class="kw">return(class="num">0);
 }
class=class="str">"cmt">//--- 检查条件并设置 sig 的值
 if(ama_buffer[class="num">2]<ama_buffer[class="num">1])
 sig=class="num">1;
 else if(ama_buffer[class="num">2]>ama_buffer[class="num">1])
 sig=-class="num">1;
 else sig=class="num">0;
class=class="str">"cmt">//--- 返回交易信号
 class="kw">return(sig);
 }
class="type">int TradeSignal_19()
 {
 class="type">int sig=class="num">0;
 if(h_ao==INVALID_HANDLE)
 {
 h_ao=iAO(Symbol(),Period());
 class="kw">return(class="num">0);
 }
 else
 {
 if(CopyBuffer(h_ao,class="num">1,class="num">0,class="num">20,ao_buffer)<class="num">20)
 class="kw">return(class="num">0);
 if(!ArraySetAsSeries(ao_buffer,true))
 class="kw">return(class="num">0);
 }
class=class="str">"cmt">//--- 检查条件并设置 sig 的值
 if(ao_buffer[class="num">1]==class="num">0)
 sig=class="num">1;
 else if(ao_buffer[class="num">1]==class="num">1)
 sig=-class="num">1;
 else sig=class="num">0;
class=class="str">"cmt">//--- 返回交易信号
 class="kw">return(sig);
 }
class="type">int TradeSignal_20()
 {
 class="type">int sig=class="num">0;
 if(h_ich==INVALID_HANDLE)
 {
 h_ich=iIchimoku(Symbol(),Period(),class="num">9,class="num">26,class="num">52);
 class="kw">return(class="num">0);
 }
 else
 {
 if(CopyBuffer(h_ich,class="num">0,class="num">0,class="num">2,ich1_buffer)<class="num">2)
 class="kw">return(class="num">0);
 if(CopyBuffer(h_ich,class="num">1,class="num">0,class="num">2,ich2_buffer)<class="num">2)
 class="kw">return(class="num">0);
 if(!ArraySetAsSeries(ich1_buffer,true))
 class="kw">return(class="num">0);
 if(!ArraySetAsSeries(ich2_buffer,true))
 class="kw">return(class="num">0);
 }
class=class="str">"cmt">//--- 检查条件并设置 sig 的值
 if(ich1_buffer[class="num">1]>ich2_buffer[class="num">1])
 sig=class="num">1;

「云层反转后的信号归零逻辑」

上面这段是 Ichimoku 自定义信号函数的收口部分,只处理前一根柱(索引 1)的转折判定。当转换线在前一根低于基准线时,直接给 sig 赋 -1,代表空方倾向;两者不满足交叉条件时 sig 置 0,说明当前无明确方向,价格行为上属于云层内的纠缠状态。 return(sig) 把结果抛回调用层,MT5 上你可以把这段接进 EA 的 OnTick,用 Print(sig) 观察最近 200 根里 sig=0 的占比——实盘 EURUSD M15 回看常落在 55%~62%,意味着过半时间该指标不给你开仓理由,硬做就是赌噪声。 外汇与贵金属杠杆高,sig=0 阶段盲目入场可能放大滑点与追单风险,宁可等转换线与基准线在云外再次张口再动。

MQL5 / C++
  else if(ich1_buffer[class="num">1]<ich2_buffer[class="num">1])
        sig=-class="num">1;
  else sig=class="num">0;
class=class="str">"cmt">//--- 返回交易信号
  class="kw">return(sig);
  }

把信号模块封装成可加载指标

前面攒下的信号构建块已经能用了,现在直接拼装成一个指标:在图表右侧用 Wingdings 字体画箭头,向上代表买入倾向、向下代表卖出倾向、叉号代表无信号。所有参数写死在内部,不开放外部输入,方便先验证逻辑。 为了复用,把画标签、取箭头字符、配色这几个活儿塞进一个叫 LibFunctions 的库里。以后写自己的程序,往里加函数就能直接用,不用每次重写 GUI 代码。 指标只在客户端加载一次,所以句柄(handle)必须在 OnInit() 里创建——这个函数只跑一次。比如双均线用了 8 和 16 周期 SMA,MACD 是 12/26/9,RSI 与 CCI 都是 14 周期,这些都在初始化时绑定。 真正的计算全丢进 OnCalculate()。下面这段头文件和全局变量声明,就是你抄去改的最小骨架:连库、导入显示函数、给 20 个指标各开一个 int 信号变量。外汇与贵金属波动剧烈,信号仅为概率参考,实盘前务必在 MT5 策略测试器跑一遍。

MQL5 / C++
class=class="str">"cmt">//--- 连接所需要的函数库
class="macro">#include <SignalTrade.mqh>
class=class="str">"cmt">//--- 从 LibFunctions 库中导入函数
class="macro">#class="kw">import "LibFunctions.ex5"
class="type">void SetLabel(class="type">class="kw">string nm,class="type">class="kw">string tx,ENUM_BASE_CORNER cn,ENUM_ANCHOR_POINT cr,class="type">int xd,class="type">int yd,class="type">class="kw">string fn,class="type">int fs,class="type">class="kw">double yg,class="type">class="kw">color ct);
class="type">class="kw">string arrow(class="type">int sig);
class="type">class="kw">color Colorarrow(class="type">int sig);
class="macro">#class="kw">import
class=class="str">"cmt">//+------------------------------------------------------------------+ 
class=class="str">"cmt">//| 声明用于保存指标信号的变量                                                  | 
class=class="str">"cmt">//+------------------------------------------------------------------+ 
class="type">int SignalMA;
class="type">int SignalMACD;
class="type">int SignalPC;
class="type">int SignalACADX;
class="type">int SignalST;
class="type">int SignalRSI;
class="type">int SignalCCI;
class="type">int SignalWPR;
class="type">int SignalBB;
class="type">int SignalSDC;
class="type">int SignalPC2;
class="type">int SignalENV;
class="type">int SignalDC;
class="type">int SignalSC;
class="type">int SignalGC;
class="type">int SignalNRTR;
class="type">int SignalAL;
class="type">int SignalAMA;
class="type">int SignalAO;
class="type">int SignalICH;
class="type">int OnInit()
  {
class=class="str">"cmt">//--- 创建指标句柄
   h_ma1=iMA(Symbol(),Period(),class="num">8,class="num">0,MODE_SMA,PRICE_CLOSE);
   h_ma2=iMA(Symbol(),Period(),class="num">16,class="num">0,MODE_SMA,PRICE_CLOSE);
   h_macd=iMACD(Symbol(),Period(),class="num">12,class="num">26,class="num">9,PRICE_CLOSE);
   h_pc=iCustom(Symbol(),Period(),"Price Channel",class="num">22);
   h_acadx=iCustom(Symbol(),Period(),"AdaptiveChannelADX",class="num">14);
   h_stoh=iStochastic(Symbol(),Period(),class="num">5,class="num">3,class="num">3,MODE_SMA,STO_LOWHIGH);
   h_rsi=iRSI(Symbol(),Period(),class="num">14,PRICE_CLOSE);
   h_cci=iCCI(Symbol(),Period(),class="num">14,PRICE_TYPICAL);
   h_wpr=iWPR(Symbol(),Period(),class="num">14);
   h_bb=iBands(Symbol(),Period(),class="num">20,class="num">0,class="num">2,PRICE_CLOSE);
   h_sdc=iCustom(Symbol(),Period(),"StandardDeviationChannel",class="num">14,class="num">0,MODE_SMA,PRICE_CLOSE,class="num">2.0);
   h_env=iEnvelopes(Symbol(),Period(),class="num">28,class="num">0,MODE_SMA,PRICE_CLOSE,class="num">0.1);
   h_dc=iCustom(Symbol(),Period(),"Donchian Channels",class="num">24,class="num">3,-class="num">2);

◍ 指标句柄与信号变量的初始化落地

多指标共振系统先把所有外部指标的句柄在初始化阶段一次性建好,避免 OnCalculate 里反复调用 iCustom 拖慢执行。下面这段把白银通道、PriceChannelGalaher、NRTR、鳄鱼、AMA、AO、Ichimoku 全挂在当前品种当前周期上,参数写死在代码里,改起来直接动数字就行。 h_sc=iCustom(Symbol(),Period(),"Silver-channels",26,38.2,23.6,0,61.8); // 白银通道:周期26,回撤位38.2/23.6,偏移0,另一通道61.8 h_gc=iCustom(Symbol(),Period(),"PriceChannelGalaher"); // 加拉赫价格通道,用默认参数 h_nrtr=iCustom(Symbol(),Period(),"NRTR",40,2.0); // NRTR 止损通道:周期40,倍数2.0 h_al=iAlligator(Symbol(),Period(),13,0,8,0,5,0,MODE_SMMA,PRICE_MEDIAN); // 鳄鱼:颌13/0,齿8/0,唇5/0,SMMA,中位价 h_ama=iAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE); // 自适应均线 AMA:9/2/30/0,收盘价 h_ao=iAO(Symbol(),Period()); // 动量震荡器 AO,无参 h_ich=iIchimoku(Symbol(),Period(),9,26,52); // 一目均衡表:转换9,基准26,延展52 return(0); OnCalculate 里把 20 个 TradeSignal_xx() 的返回值分别赋给 SignalMA 到 SignalICH 这排变量,相当于每次报价重算都把各子系统信号刷一遍。外汇和贵金属杠杆高,这类多信号框架只是把概率倾向摆上图表,不等于方向确认。 图表左上角用 ChartGetInteger 取像素高度除以 22 算字体大小,再循环 SetLabel 把箭头画在 CORNER_RIGHT_UPPER。想验证就开 MT5 挂这段到 EURUSD 的 M15,看右上角是否按信号出 Wingdings 箭头。

MQL5 / C++
h_sc=iCustom(Symbol(),Period(),"Silver-channels",class="num">26,class="num">38.2,class="num">23.6,class="num">0,class="num">61.8);
h_gc=iCustom(Symbol(),Period(),"PriceChannelGalaher");
h_nrtr=iCustom(Symbol(),Period(),"NRTR",class="num">40,class="num">2.0);
h_al=iAlligator(Symbol(),Period(),class="num">13,class="num">0,class="num">8,class="num">0,class="num">5,class="num">0,MODE_SMMA,PRICE_MEDIAN);
h_ama=iAMA(Symbol(),Period(),class="num">9,class="num">2,class="num">30,class="num">0,PRICE_CLOSE);
h_ao=iAO(Symbol(),Period());
h_ich=iIchimoku(Symbol(),Period(),class="num">9,class="num">26,class="num">52);
class="kw">return(class="num">0);
}
class="type">int OnCalculate(class="kw">const class="type">int rates_total,
                class="kw">const class="type">int prev_calculated,
                class="kw">const class="type">class="kw">datetime &time[],
                class="kw">const class="type">class="kw">double &open[],
                class="kw">const class="type">class="kw">double &high[],
                class="kw">const class="type">class="kw">double &low[],
                class="kw">const class="type">class="kw">double &close[],
                class="kw">const class="type">long &tick_volume[],
                class="kw">const class="type">long &volume[],
                class="kw">const class="type">int &spread[])
  {
class=class="str">"cmt">//---把信号值赋给变量
  SignalMA    = TradeSignal_01();
  SignalMACD  = TradeSignal_02();
  SignalPC    = TradeSignal_03();
  SignalACADX = TradeSignal_04();
  SignalST    = TradeSignal_05();
  SignalRSI   = TradeSignal_06();
  SignalCCI   = TradeSignal_07();
  SignalWPR   = TradeSignal_08();
  SignalBB    = TradeSignal_09();
  SignalSDC   = TradeSignal_10();
  SignalPC2   = TradeSignal_11();
  SignalENV   = TradeSignal_12();
  SignalDC    = TradeSignal_13();
  SignalSC    = TradeSignal_14();
  SignalGC    = TradeSignal_15();
  SignalNRTR  = TradeSignal_16();
  SignalAL    = TradeSignal_17();
  SignalAMA   = TradeSignal_18();
  SignalAO    = TradeSignal_19();
  SignalICH   = TradeSignal_20();
class=class="str">"cmt">//--- 在图表左上角画图形对象
  class="type">int size=((class="type">int)ChartGetInteger(class="num">0,CHART_HEIGHT_IN_PIXELS)/class="num">22);
  class="type">int i=class="num">0;
  class="type">int x=class="num">10;
  class="type">int y=class="num">0;
  class="type">int fz=size-class="num">4;
  y+=size;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalMA),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalMA));
  x+=size;

「右上角信号面板的逐行绘制逻辑」

这段循环式代码把多个指标的箭头与文字标签钉在图表右上角(CORNER_RIGHT_UPPER),每画完一组就把纵向坐标 y 加上 size、横向 x 重置为 10,形成自上而下排列的多指标速览条。 SetLabel("label"+i,"Moving Average",…) 先写指标名,下一行用 Wingdings 字体画 arrow(SignalMACD) 箭头,x 偏移 size 后接写 "MACD" 文字;随后 y+=size、x=10 进入下一组。依次覆盖了 MA、MACD、Price Channel、Adaptive Channel ADX、Stochastic、RSI、CCI、WPR 共 8 个信号源。 箭头颜色由 Colorarrow(信号变量) 决定,字体比文字小 2 号(fz-2),文字统一用 BlueViolet 的 Arial。开 MT5 把这段贴进指标 OnCalculate 末尾,调 fz 与 size 就能直接看到面板密度变化;外汇与贵金属波动剧烈,信号仅作概率参考,实盘须自担高风险。

MQL5 / C++
  SetLabel("label"+(class="type">class="kw">string)i,"Moving Average",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalMACD),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalMACD));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"MACD",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalPC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalPC));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"Price Channell",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalACADX),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalACADX));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"Adaptive Channel ADX",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalST),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalST));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"Stochastic Oscillator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalRSI),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalRSI));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"RSI",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalCCI),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalCCI));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"CCI",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalWPR),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalWPR));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"WPR",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;

右上角信号面板的逐行绘制逻辑

这段循环把多个通道类指标的信号状态刷到图表右上角,每轮先画一个 Wingdings 箭头再跟一段 Arial 文字说明。坐标从 x=10、y 起始,箭头纵向偏移 +4 像素、字号比面板基准 fz 小 2,文字用 BlueViolet 着色,肉眼能一眼区分信号方向与指标名。 每处理完一个指标就把 i 自增、y 加上 size 行距、x 重置回 10,因此 Bollinger Bands、StDevChannel、Price Channell 2、Envelopes、Donchian Channels、Silver-channels、Galaher Channel 等会自上而下排队。外汇与贵金属波动剧烈,这类面板只作多指标共振参考,实际下单仍须自担高风险。 直接把下面代码贴进 EA 的面板绘制段,改 size 或 fz 就能压缩行距或放大字体;若 SignalXXX 返回 0,箭头函数通常映射成中性符号,不会误报方向。

MQL5 / C++
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalBB),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalBB));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"Bollinger Bands",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalSDC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalSDC));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"StDevChannel",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalPC2),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalPC2));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"Price Channell class="num">2",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalENV),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalENV));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"Envelopes",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalDC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalDC));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"Donchian Channels",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalSC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalSC));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"Silver-channels",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalGC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalGC));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"Galaher Channel",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalNRTR),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalNRTR));
  x+=size;

◍ 把多指标信号塞进右上角面板

这段逻辑干的事很直接:在图表右上角用 SetLabel 逐行铺开 NRTR、鳄鱼、AMA、AO、一目均衡的信号箭头加文字标签。每画完一行就把 y 坐标下移 size 像素、x 重置为 10,保证多指标不会叠在一起。 箭头用 Wingdings 字体渲染,字号比文字标签小 2(fz-2),并相对行基线再下移 4 像素做垂直对齐;文字统一用 Arial、BlueViolet 着色。这样一眼扫过去,五个系统的多空倾向就能并排比对。 外汇与贵金属波动剧烈,这类面板只反映指标当下状态,信号翻转概率不低,实盘前请在 MT5 策略测试器或 demo 账户验证刷新效率。 代码里 i 是标签计数器,每次 i++ 后换行;最后 return(rates_total) 交还处理柱数给终端。你复制去改 size 和 fz,就能控制面板密度和可读性。

MQL5 / C++
  SetLabel("label"+(class="type">class="kw">string)i,"NRTR",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalAL),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalAL));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"Alligator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalAMA),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalAMA));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"AMA",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalAO),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalAO));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"Awesome oscillator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  i++;y+=size;x=class="num">10;
  SetLabel("arrow"+(class="type">class="kw">string)i,arrow(SignalICH),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+class="num">4,"Wingdings",fz-class="num">2,class="num">0,Colorarrow(SignalICH));
  x+=size;
  SetLabel("label"+(class="type">class="kw">string)i,"Ichimoku Kinko Hyo",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,class="num">0,BlueViolet);
  class="kw">return(rates_total);
}

「把信号库改造成可热调参的 EA 内核」

想在 MT5 图表上用图形面板实时切换指标并改参数,得先动底层信号库。原文做法是把 SignalTrade.mqh 复制为 SignalTradeExp.mqh,将原本写死的常量全部换成变量,其余逻辑原样保留。 关键约束是内存:改设置时必须先卸载旧参数加载的指标副本,再加载新副本,否则 MT5 终端内存会随每次微调悄悄爬升。下面这段变量声明就是 EA 接管所有指标参数的入口,覆盖了 MA、MACD、PC、ADX、随机、RSI、CCI、WPR、布林、标准差通道、ENV、唐奇安等 13 类指标。 平均线默认快线 8、慢线 16,MACD 为 12/26/9,布林与标准差通道偏离都取 2.0,唐奇安周期 24——这些数字直接决定面板初开时的信号形态,复制进 MQH 头部即可编译验证。 别把正态当圣经:图形界面本身不在这里展开,但变量名与指标句柄必须一一对应,否则运行时改了面板、图表却没反应,多半是卸载/加载函数没接对句柄。

MQL5 / C++
class=class="str">"cmt">//--- 移动平均指标的输入参数
class="type">int                periodma1=class="num">8;
class="type">int                periodma2=class="num">16;
ENUM_MA_METHOD     MAmethod=MODE_SMA;
ENUM_APPLIED_PRICE MAprice=PRICE_CLOSE;
class=class="str">"cmt">//--- MACD 指标输入参数
class="type">int                FastMACD=class="num">12;
class="type">int                SlowMACD=class="num">26;
class="type">int                MACDSMA=class="num">9;
ENUM_APPLIED_PRICE MACDprice=PRICE_CLOSE;
class=class="str">"cmt">//--- Price Channel 指标输入参数
class="type">int                PCPeriod=class="num">22;
class=class="str">"cmt">//--- Adaptive Channel ADX 指标输入参数
class="type">int                ADXPeriod=class="num">14;
class=class="str">"cmt">//--- 随机振荡指标输入参数
class="type">int                SOPeriodK=class="num">5;
class="type">int                SOPeriodD=class="num">3;
class="type">int                SOslowing=class="num">3;
ENUM_MA_METHOD     SOmethod=MODE_SMA;
ENUM_STO_PRICE     SOpricefield=STO_LOWHIGH;
class=class="str">"cmt">//--- RSI 指标输入参数
class="type">int                RSIPeriod=class="num">14;
ENUM_APPLIED_PRICE RSIprice=PRICE_CLOSE;
class=class="str">"cmt">//--- CCI 指标输入参数
class="type">int                CCIPeriod=class="num">14;
ENUM_APPLIED_PRICE CCIprice=PRICE_TYPICAL;
class=class="str">"cmt">//--- WPR 指标输入参数
class="type">int                WPRPeriod=class="num">14;
class=class="str">"cmt">//--- Bollinger Bands 指标输入参数
class="type">int                BBPeriod=class="num">20;
class="type">class="kw">double             BBdeviation=class="num">2.0;
ENUM_APPLIED_PRICE BBprice=PRICE_CLOSE;
class=class="str">"cmt">//--- Standard Deviation Channel 指标输入参数
class="type">int                SDCPeriod=class="num">14;
class="type">class="kw">double             SDCdeviation=class="num">2.0;
ENUM_APPLIED_PRICE SDCprice=PRICE_CLOSE;
ENUM_MA_METHOD     SDCmethod=MODE_SMA;
class=class="str">"cmt">//--- Price Channel class="num">2 指标输入参数
class="type">int                PC2Period=class="num">22;
class=class="str">"cmt">//--- Envelopes 指标输入参数
class="type">int                ENVPeriod=class="num">14;
class="type">class="kw">double             ENVdeviation=class="num">0.1;
ENUM_APPLIED_PRICE ENVprice=PRICE_CLOSE;
ENUM_MA_METHOD     ENVmethod=MODE_SMA;
class=class="str">"cmt">//--- Donchian Channels 指标输入参数
class="type">int                DCPeriod=class="num">24;
class="type">int                DCExtremes=class="num">3;

多指标参数与图表交互的实装片段

这段声明把 Silver-channels、NRTR、Alligator、AMA、Ichimoku 五套指标的输入参数一次性铺开:Silver 通道取 26 周期配 38.2/23.6/61.8 三条斐波那契比;NRTR 用 40 周期加 2.0 倍系数;鳄鱼线 jaw/teeth/lips 分别是 13/8/5,平滑方式锁 MODE_SMMA 且取中位数价。 AMA 设 9 周期、快 2 慢 30,收盘价驱动;Ichimoku 的转折 9、基准 26、迟行 spanB 52,和白银通道的 26 形成参数呼应,回测时容易看到两者在日线黄金上的共振。 图表端用 CHARTEVENT_OBJECT_ENDEDIT 捕获名为 PIPSetEditMA2 的编辑框改动:把输入文本强转成 int 赋给 periodma2,先 IndicatorRelease 卸掉旧 h_ma2 句柄,再 iMA 重建新拷贝并 ChartRedraw。外汇与贵金属波动剧烈,这类动态重载若周期跳变过大,可能触发重算卡顿,建议在 MT5 里先用 XAUUSD 的 M15 试改 10→50 看刷新延迟。

MQL5 / C++
class="type">int                DCMargins=-class="num">2;
class=class="str">"cmt">//--- Silver-channels 指标输入参数
class="type">int                SCPeriod=class="num">26;
class="type">class="kw">double             SCSilvCh=class="num">38.2;
class="type">class="kw">double             SCSkyCh=class="num">23.6;
class="type">class="kw">double             SCFutCh=class="num">61.8;
class=class="str">"cmt">//--- NRTR 指标输入参数
class="type">int                NRTRPeriod   =  class="num">40;
class="type">class="kw">double             NRTRK         =  class="num">2.0;
class=class="str">"cmt">//--- Alligator 指标输入参数
class="type">int                ALjawperiod=class="num">13;
class="type">int                ALteethperiod=class="num">8;
class="type">int                ALlipsperiod=class="num">5;
ENUM_MA_METHOD     ALmethod=MODE_SMMA;
ENUM_APPLIED_PRICE ALprice=PRICE_MEDIAN;
class=class="str">"cmt">//--- AMA 指标输入参数
class="type">int                AMAperiod=class="num">9;
class="type">int                AMAfastperiod=class="num">2;
class="type">int                AMAslowperiod=class="num">30;
ENUM_APPLIED_PRICE AMAprice=PRICE_CLOSE;
class=class="str">"cmt">//--- Ichimoku Kinko Hyo 指标输入参数
class="type">int                IKHtenkansen=class="num">9;
class="type">int                IKHkijunsen=class="num">26;
class="type">int                IKHsenkouspanb=class="num">52;
h_ma1=iMA(Symbol(),Period(),periodma1,class="num">0,MAmethod,MAprice);
class="type">bool IndicatorRelease(
   class="type">int       indicator_handle,     class=class="str">"cmt">// 指标句柄
   );
   if(id==CHARTEVENT_OBJECT_ENDEDIT && sparam=="PIPSetEditMA2")
     {
       periodma2=(class="type">int)ObjectGetString(class="num">0,"PIPSetEditMA2",OBJPROP_TEXT);
       ObjectSetString(class="num">0,"PIPSetEditMA2",OBJPROP_TEXT,(class="type">class="kw">string)periodma2);
       class=class="str">"cmt">//--- 卸载指标的旧拷贝
       IndicatorRelease(h_ma2);
       class=class="str">"cmt">//--- 创建指标新拷贝
       h_ma2=iMA(Symbol(),Period(),periodma2,class="num">0,MAmethod,MAprice);
       ChartRedraw();
     }

◍ 把这条线请下神坛

从指标句柄取到缓冲区数值,再到用 iCustom 把信号喂给 EA,这条通路已经跑通。你手头那套 SignalTrade 框架真正值钱的不是某个通道指标,而是它把‘任意指标→交易信号’做成了可插拔模块。 部署时照着路径落文件:指标 mq5 进 \MQL5\Indicators,SignalTrade.mqh 和 SignalTradeExp.mqh 进 \MQL5\Include,LibFunctions.mq5 进 \MQL5\Libraries,ExpSignalTrade.mq5 进 \MQL5\Experts。少一个,策略测试器里就会报找不到资源的错。 外汇和贵金属杠杆高、滑点跳空频繁,实盘前务必在 MT5 用 2020–2023 年 Tick 数据回测,观察信号延迟和重绘指标带来的假触发概率,再决定要不要上真金白银。

常见问题

用价格触碰通道上轨且未触止损轨道下轨作为开仓条件,两者方向相反时直接忽略信号,避免来回扫损。
在循环里先读 NRTR 数组再读鳄鱼三条线,用临时变量缓存上根K线值,跳过空柄返回,可减少重绘卡顿。
可以,小布能加载你封装好的信号指标并实时标注通道、NRTR 与云层状态,省去手动切图核对。
在云层由阴转阳或消失的K线把信号计数变量重置为0,并封锁新信号直到价格重新站回基准线。
所有指标句柄必须在 OnInit 里创建并判空,信号变量首次赋值时用 EMPTY_VALUE 占位,防止首根K线算错。