价格行为分析工具包开发(第十三部分):RSI 哨兵工具·进阶篇
📡

价格行为分析工具包开发(第十三部分):RSI 哨兵工具·进阶篇

(2/3)·手动扫背离又慢又漏?这篇进阶 EA 把 RSI 常规与隐藏背离画成箭头

实战向进阶 第 2/3 篇
很多交易者盯着副图找 RSI 背离,肉眼扫几根 K 线就疲劳,漏掉隐藏背离是常事。等发现反转苗头,价格已经跑出一段。把这套识别交给代码,你只做确认。

「摆动低点上的两类牛背离判定」

在 RSI 背离扫描里,牛侧逻辑只处理 swingLows 数组长度 ≥ 2 的情况。数组经 ArraySort 升序后,索引 0 为最近摆动低点、索引 1 为前一处,两点的 lowBuffer 与 rsiBuffer 差值即构成判定基础。 常规牛背离要求价格走出更低低点(lowBuffer[recent] < lowBuffer[previous]),而 RSI 反而更高(rsiBuffer[recent] > rsiBuffer[previous]),且 RSI 差值不小于 InpMinRSIDiff。若开启 InpUseRSIThreshold,还会额外限定前一处 RSI 必须 ≤ InpRSIOversold,才允许 Print 与 DisplaySignal 输出 RegBullish Divergence。 隐藏牛背离走 else 分支:价格更高低点(lowBuffer[recent] > lowBuffer[previous])配合 RSI 更低低点,RSI 差值同样需跨过 InpMinRSIDiff 门槛,信号标记 HiddenBullish Divergence。外汇与贵金属杠杆高,背离仅示概率倾向,实盘须自验。 IsSignificantSwingLow 用 left/right 窗口核验局部最低:左右任一端出现比 currentLow 低且偏离超 InpMinSwingDiffPct% 的 bar 即判非显著低点。以 0.1% 的最小摆动阈值跑 EURUSD M5,显著摆动低点数量可能比 0.5% 设定多出 3~4 倍,直接影响背离信号密度。

MQL5 / C++
   if(ArraySize(swingLows) >= class="num">2)
   {
      ArraySort(swingLows); class=class="str">"cmt">// ascending order: index class="num">0 is most recent
      class="type">int recent   = swingLows[class="num">0];
      class="type">int previous = swingLows[class="num">1];
      class=class="str">"cmt">// Regular Bullish Divergence: Price makes a lower low class="kw">while RSI makes a higher low
      if(lowBuffer[recent] < lowBuffer[previous] &&
          rsiBuffer[recent] > rsiBuffer[previous] &&
          (rsiBuffer[recent] - rsiBuffer[previous]) >= InpMinRSIDiff)
         {
          class=class="str">"cmt">// Optionally require the earlier swing&class="macro">#x27;s RSI be oversold
          if(!InpUseRSIThreshold || rsiBuffer[previous] <= InpRSIOversold)
            {
               Print("Regular Bullish Divergence detected at bar ", recent);
               DisplaySignal("RegBullish Divergence", recent);
            }
         }
      class=class="str">"cmt">// Hidden Bullish Divergence: Price makes a higher low class="kw">while RSI makes a lower low
      else
       if(lowBuffer[recent] > lowBuffer[previous] &&
           rsiBuffer[recent] < rsiBuffer[previous] &&
           (rsiBuffer[previous] - rsiBuffer[recent]) >= InpMinRSIDiff)
         {
               Print("Hidden Bullish Divergence detected at bar ", recent);
               DisplaySignal("HiddenBullish Divergence", recent);
         }
   }

class="type">bool IsSignificantSwingLow(class="type">int index, class="type">int left, class="type">int right)
  {
   class="type">class="kw">double currentLow = lowBuffer[index];
class=class="str">"cmt">// Check left side for a local minimum condition
   for(class="type">int i = index - left; i < index; i++)
     {
         if(i < class="num">0)
            class="kw">continue;
         class="type">class="kw">double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * class="num">100.0;
         if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
            class="kw">return class="kw">false;
     }
class=class="str">"cmt">// Check right side for a local minimum condition
   for(class="type">int i = index + class="num">1; i <= index + right; i++)
     {
         if(i >= g_totalBars)
            class="kw">break;
         class="type">class="kw">double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * class="num">100.0;
         if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
            class="kw">return class="kw">false;
     }
   class="kw">return true;
  }

显著摆动高点判定与信号去重绘制

显著摆动高点的识别不是简单看左右两根 K 线更低,而是用百分比阈值过滤噪音。函数 IsSignificantSwingHigh 以当前柱 highBuffer[index] 为基准,向左回溯 left 根、向右前瞻 right 根,若任一侧出现高于当前高点且偏离幅度超过 InpMinSwingDiffPct(百分比)的柱,就直接返回 false,判定失败。 左端循环里先用 i<0 做边界保护,避免历史不足时数组越界;右端则用 i>=g_totalBars 跳出,两者都依赖外部传入的图表总柱数。pctDiff 的计算方式是 MathAbs((currentHigh - highBuffer[i]) / currentHigh) * 100.0,这意味着阈值参数直接决定摆动的「显著」程度——设 0.1 可能在大周期黄金上频繁触发,设 0.5 则只抓大级别拐点。 DisplaySignal 负责把信号画到图上,但先做了同柱去重:遍历已有 signals 数组,若类型包含相同 signalText 且柱距小于 InpMinBarsBetweenSignals 就直接 return。这一步能压住震荡市里的重复箭头噪音。 对于带 "Reg" 字样的常规信号,代码在左上角(距边 10、20 像素)创建名为 LatestSignal 的 OBJ_LABEL 文本对象,颜色白。若 ObjectCreate 失败会 Print 报错并返回,实盘加载后可在 MT5 终端日志里抓这类创建异常。外汇与贵金属波动剧烈、杠杆风险高,任何信号都只是概率参考,需结合仓位管理。

MQL5 / C++
class="type">bool IsSignificantSwingHigh(class="type">int index, class="type">int left, class="type">int right)
  {
   class="type">class="kw">double currentHigh = highBuffer[index];
   for(class="type">int i = index - left; i < index; i++)
     {
       if(i < class="num">0)
         class="kw">continue;
       class="type">class="kw">double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * class="num">100.0;
       if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         class="kw">return class="kw">false;
     }
   for(class="type">int i = index + class="num">1; i <= index + right; i++)
     {
       if(i >= g_totalBars)
         class="kw">break;
       class="type">class="kw">double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * class="num">100.0;
       if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         class="kw">return class="kw">false;
     }
   class="kw">return true;
  }
class="type">void DisplaySignal(class="type">class="kw">string signalText, class="type">int barIndex)
  {
   for(class="type">int i = class="num">0; i < ArraySize(signals); i++)
     {
       if(StringFind(signals[i].type, signalText) != -class="num">1)
         if(MathAbs(signals[i].barIndex - barIndex) < InpMinBarsBetweenSignals)
           class="kw">return;
     }
   if(StringFind(signalText, "Reg") != -class="num">1)
     {
       class="type">class="kw">string labelName = "LatestSignal";
       if(ObjectFind(class="num">0, labelName) == -class="num">1)
         {
           if(!ObjectCreate(class="num">0, labelName, OBJ_LABEL, class="num">0, class="num">0, class="num">0))
             {
               Print("Failed to create LatestSignal label");
               class="kw">return;
             }
           ObjectSetInteger(class="num">0, labelName, OBJPROP_CORNER, class="num">0);
           ObjectSetInteger(class="num">0, labelName, OBJPROP_XDISTANCE, class="num">10);
           ObjectSetInteger(class="num">0, labelName, OBJPROP_YDISTANCE, class="num">20);
           ObjectSetInteger(class="num">0, labelName, OBJPROP_COLOR, clrWhite);
         }

◍ 给信号挂箭头并记账

这一段负责把判定的形态在图上画出来,同时把信号塞进数组留待后续统计。箭头用 OBJ_ARROW 对象,Wingdings 码 233 画向上箭头、234 画向下箭头,偏移量由外部参数 InpArrowOffset 乘上 SYMBOL_POINT 决定,避免箭头压在 K 线上。 多头信号箭头落在 lowBuffer[barIndex] 下方,空头落在 highBuffer[barIndex] 上方,颜色分别用 clrLime 和 clrRed。创建前先用 ObjectFind 查重,同名对象已存在就跳过,防止重复 K 线刷新时叠一堆箭头。 信号记录用结构体 SignalInfo 存类型、bar 索引、时间和触发价,ArrayResize 后追加到 signals[]。Regular 类含 "Reg" 子串、Hidden 类含 "Hidden" 子串,UpdateSignalCountLabel 遍历数组分别计数——这两类在背离策略里出现频率差异,可能反映当前趋势动能的收敛或扩张。 外汇与贵金属杠杆高,这类图形标记只作辅助,信号失效概率不低,实盘前务必在 MT5 策略测试器跑一遍看箭头位置和计数是否符合预期。

MQL5 / C++
  ObjectSetString(class="num">0, labelName, OBJPROP_TEXT, signalText);
  }
class=class="str">"cmt">// Create an arrow object for the signal.
  class="type">class="kw">string arrowName = "Arrow_" + signalText + "_" + IntegerToString(barIndex);
  if(ObjectFind(class="num">0, arrowName) < class="num">0)
  {
    class="type">int arrowCode = class="num">0;
    class="type">class="kw">double arrowPrice = class="num">0.0;
    class="type">class="kw">color arrowColor = clrWhite;
    class="type">class="kw">double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    if(StringFind(signalText, "Bullish") != -class="num">1)
      {
       arrowCode  = class="num">233; class=class="str">"cmt">// Wingdings up arrow
       arrowColor = clrLime;
       arrowPrice = lowBuffer[barIndex] - (InpArrowOffset * point);
      }
    else
      if(StringFind(signalText, "Bearish") != -class="num">1)
        {
         arrowCode  = class="num">234; class=class="str">"cmt">// Wingdings down arrow
         arrowColor = clrRed;
         arrowPrice = highBuffer[barIndex] + (InpArrowOffset * point);
        }
    if(!ObjectCreate(class="num">0, arrowName, OBJ_ARROW, class="num">0, timeBuffer[barIndex], arrowPrice))
      {
       Print("Failed to create arrow object ", arrowName);
       class="kw">return;
      }
    ObjectSetInteger(class="num">0, arrowName, OBJPROP_COLOR, arrowColor);
    ObjectSetInteger(class="num">0, arrowName, OBJPROP_ARROWCODE, arrowCode);
  }
class=class="str">"cmt">// Record the signal for evaluation.
  SignalInfo sig;
  sig.type = signalText;
  sig.barIndex = barIndex;
  sig.signalTime = timeBuffer[barIndex];
  if(StringFind(signalText, "Bullish") != -class="num">1)
    sig.signalPrice = lowBuffer[barIndex];
  else
    sig.signalPrice = highBuffer[barIndex];
  ArrayResize(signals, ArraySize(signals) + class="num">1);
  signals[ArraySize(signals) - class="num">1] = sig;
  UpdateSignalCountLabel();
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| UpdateSignalCountLabel: Updates a label showing signal counts     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void UpdateSignalCountLabel()
  {
   class="type">int regCount = class="num">0, hidCount = class="num">0;
   for(class="type">int i = class="num">0; i < ArraySize(signals); i++)
     {
      if(StringFind(signals[i].type, "Reg") != -class="num">1)
        regCount++;
      else
        if(StringFind(signals[i].type, "Hidden") != -class="num">1)
          hidCount++;
     }

「回测后给信号贴准确率标签」

背离信号抓出来只是一半活,另一半是看它事后到底灵不灵。下面这段把常规(Regular)和隐藏(Hidden)信号数量直接画在图表左上角,距离左边 10 像素、上边 40 像素,用黄字标出,开盘就能看见本轮统计基数。 评估函数 EvaluateSignalsAndPrint 先把整段收盘价用 CopyClose 拉进数组,再按 InpEvalBars 往前推这根信号对应的验证 bar。比如设 InpEvalBars=20,那信号 bar 往前数 20 根收盘价比信号价高, Bullish 类就算蒙对一次。 regCorrect 和 hidCorrect 分别累加两类信号里「收盘越过了 signalPrice」的次数,regTotal / hidTotal 是总数。外汇与贵金属杠杆高,历史命中率只代表样本内概率,换周期或品种可能明显衰减,实盘前务必自己跑一遍。 别把计数面板当圣杯 面板里的 Regular/Hidden 计数只是回测样本内的正确次数,没扣除点差和滑点。MT5 里把模型切到「每个即时价」重跑,数字往往会掉几个百分点。

MQL5 / C++
class="type">class="kw">string countText = "Regular Signals: " + IntegerToString(regCount) +
                    "\nHidden Signals: " + IntegerToString(hidCount);
class="type">class="kw">string countLabel = "SignalCount";
if(ObjectFind(class="num">0, countLabel) == -class="num">1)
   {
    if(!ObjectCreate(class="num">0, countLabel, OBJ_LABEL, class="num">0, class="num">0, class="num">0))
       {
       Print("Failed to create SignalCount label");
       class="kw">return;
       }
    ObjectSetInteger(class="num">0, countLabel, OBJPROP_CORNER, class="num">0);
    ObjectSetInteger(class="num">0, countLabel, OBJPROP_XDISTANCE, class="num">10);
    ObjectSetInteger(class="num">0, countLabel, OBJPROP_YDISTANCE, class="num">40);
    ObjectSetInteger(class="num">0, countLabel, OBJPROP_COLOR, clrYellow);
   }
ObjectSetString(class="num">0, countLabel, OBJPROP_TEXT, countText);
}
class=class="str">"cmt">//+--------------------------------------------------------------------+
class=class="str">"cmt">//| EvaluateSignalsAndPrint: After backtesting, prints signal accuracy |
class=class="str">"cmt">//+--------------------------------------------------------------------+
class="type">void EvaluateSignalsAndPrint()
  {
   class="type">class="kw">double closeAll[];
   class="type">int totalBars = CopyClose(_Symbol, _Period, class="num">0, WHOLE_ARRAY, closeAll);
   if(totalBars <= class="num">0)
      {
       Print("Error copying complete close data for evaluation");
       class="kw">return;
      }
   ArraySetAsSeries(closeAll, true);
   class="type">int totalEvaluated = class="num">0, regTotal = class="num">0, hidTotal = class="num">0;
   class="type">int regEval = class="num">0, hidEval = class="num">0;
   class="type">int regCorrect = class="num">0, hidCorrect = class="num">0;
   for(class="type">int i = class="num">0; i < ArraySize(signals); i++)
      {
       class="type">int evalIndex = signals[i].barIndex - InpEvalBars;
       if(evalIndex < class="num">0)
          class="kw">continue;
       class="type">class="kw">double evalClose = closeAll[evalIndex];
       if(StringFind(signals[i].type, "Bullish") != -class="num">1)
         {
          if(StringFind(signals[i].type, "Reg") != -class="num">1)
             {
              regTotal++;
              regEval++;
              if(evalClose > signals[i].signalPrice)
                 regCorrect++;
             }
          else
          if(StringFind(signals[i].type, "Hidden") != -class="num">1)
             {

分类型统计背离信号命中率

这段逻辑接在信号枚举之后,把常规(Regular)与隐藏(Hidden)两类背离分开计数,再按多空方向比对收盘价与信号触发价,得出各自的命中次数。 对于看涨信号,若评估收盘价高于 signalPrice 则记一次正确;看跌信号则要求收盘价低于 signalPrice。regCorrect、hidCorrect 分别累加,regEval、hidEval 记录进入评估的样本量,totalEvaluated 统计总评估数。 最后用三行除法算出整体、常规、隐藏三类准确率,并以 Print 输出到专家日志:整体准确率 = (regCorrect+hidCorrect)/totalEvaluated*100,常规与隐藏各自除以对应 eval 基数。若某类样本为 0,准确率直接给 0.0 避免除零。 在 MT5 策略测试器里跑完,打开 Experts 标签就能看到 '----- Backtest Signal Evaluation -----' 开头的几行文本,里面 Total Signals Generated 是 ArraySize(signals) 的原始信号数,Evaluated 是实际可比对的条数,两者差异往往来自信号之后 K 线不足导致无法取 evalClose。外汇与贵金属波动剧烈,回测命中率仅代表历史样本概率,实盘可能明显偏离。

MQL5 / C++
         hidTotal++;
         hidEval++;
         if(evalClose > signals[i].signalPrice)
           hidCorrect++;
         }
      totalEvaluated++;
      }
   else
      if(StringFind(signals[i].type, "Bearish") != -class="num">1)
      {
         if(StringFind(signals[i].type, "Reg") != -class="num">1)
         {
          regTotal++;
          regEval++;
          if(evalClose < signals[i].signalPrice)
            regCorrect++;
         }
      else
         if(StringFind(signals[i].type, "Hidden") != -class="num">1)
         {
           hidTotal++;
           hidEval++;
           if(evalClose < signals[i].signalPrice)
             hidCorrect++;
         }
         totalEvaluated++;
      }
   }
   class="type">class="kw">double overallAccuracy = (totalEvaluated > class="num">0) ? (class="type">class="kw">double)(regCorrect + hidCorrect) / totalEvaluated * class="num">100.0 : class="num">0.0;
   class="type">class="kw">double regAccuracy = (regEval > class="num">0) ? (class="type">class="kw">double)regCorrect / regEval * class="num">100.0 : class="num">0.0;
   class="type">class="kw">double hidAccuracy = (hidEval > class="num">0) ? (class="type">class="kw">double)hidCorrect / hidEval * class="num">100.0 : class="num">0.0;
   Print("----- Backtest Signal Evaluation -----");
   Print("Total Signals Generated: ", ArraySize(signals));
   Print("Signals Evaluated: ", totalEvaluated);
   Print("Overall Accuracy: ", DoubleToString(overallAccuracy, class="num">2), "%");
   Print("Regular Signals: ", regTotal, " | Evaluated: ", regEval, " | Accuracy: ", DoubleToString(regAccuracy, class="num">2), "%");
   Print("Hidden Signals:  ", hidTotal, " | Evaluated: ", hidEval, " | Accuracy: ", DoubleToString(hidAccuracy, class="num">2), "%");
   }
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ RSI背离脚本的骨架与可调旋钮

这套 RSI 背离检测脚本把逻辑拆成了六块:头部声明与输入参数、指标初始化、逐根 K 线取数、摆动点识别、背离判定、信号绘制与回测评估。读源码时先盯输入参数段,因为后面所有过滤阈值都从那里来。 头部用 #property strict 强制严格编译,能挡掉一批隐式类型转换的坑。输入参数里 InpRSIPeriod 默认 14,InpLookback 默认扫 100 根 K 线,InpEvalBars 设 5 根——意思是信号触发后等 5 根收盘再回头算准确率。 摆动点识别靠 IsSignificantSwingLow / IsSignificantSwingHigh,用左右各 1 根(InpSwingLeft、InpSwingRight 默认值)加百分比差过滤毛刺。背离判定分常规与隐藏:看跌背离查价格 HH 而 RSI LH;看涨则反过来,且可开 InpUseRSIThreshold 锁死早段 RSI 在超卖区。 信号落地由 DisplaySignal() 画箭头并写全局数组,反初始化时 EvaluateSignalsAndPrint() 拉全量收盘价,按 InpEvalBars 间距复算常规/隐藏信号准确率。外汇与贵金属波动剧烈、杠杆风险高,回测准确率仅反映历史样本,实盘可能显著衰减。 想验证就把自己 MT5 里的 RSI Divergence 脚本输入段改成下面这份,把 InpSwingLeft/Right 从 1 调到 2~3,看图表摆动点噪音是否明显减少。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|&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; RSI Divergence.mql5&nbsp;&nbsp;&nbsp;&nbsp;|
class=class="str">"cmt">//|&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; Copyright class="num">2025, Christian Benjamin |
class=class="str">"cmt">//|&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; [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "class="num">2025, MetaQuotes Software Corp."
class="macro">#class="kw">property link&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"[MQL5官方文档]
class="macro">#class="kw">property version&nbsp;&nbsp; "class="num">1.0"
class="macro">#class="kw">property strict
class=class="str">"cmt">//---- Input parameters
input class="type">int&nbsp;&nbsp;&nbsp;&nbsp;InpRSIPeriod&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = class="num">14;&nbsp;&nbsp;&nbsp;&nbsp;class=class="str">"cmt">// RSI period
input class="type">int&nbsp;&nbsp;&nbsp;&nbsp;InpSwingLeft&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = class="num">1;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Bars to the left for swing detection(relaxed)
input class="type">int&nbsp;&nbsp;&nbsp;&nbsp;InpSwingRight&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= class="num">1;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Bars to the right for swing detection(relaxed)
input class="type">int&nbsp;&nbsp;&nbsp;&nbsp;InpLookback&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= class="num">100;&nbsp;&nbsp; class=class="str">"cmt">// Number of bars to scan for divergence
input class="type">int&nbsp;&nbsp;&nbsp;&nbsp;InpEvalBars&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= class="num">5;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Bars after which to evaluate a signal
input class="type">int&nbsp;&nbsp;&nbsp;&nbsp;InpMinBarsBetweenSignals = class="num">1;&nbsp;&nbsp;&nbsp;&nbsp; class=class="str">"cmt">// Minimum bars between same-type signals(allows frequent re-entry)
input class="type">class="kw">double InpArrowOffset&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = class="num">3.0;&nbsp;&nbsp; class=class="str">"cmt">// Arrow offset(in points) for display
让小布替你跑这套
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到 RSI 背离标注,把重复劳动交给小布,你专注决策。外汇贵金属波动剧烈,高风险始终在场。

常见问题

常规背离多视为趋势反转倾向信号,隐藏背离则更可能是趋势延续概率的确认,两者在 EA 逻辑中需分开标记。
默认 14 是常见起点,但不同品种波动特性不同,回测中可微调周期以平衡信号灵敏度与噪音。
可以,小布盯盘对应品种页已内置 AIGC 诊断,会自动标注常规与隐藏背离,省去手动编译 EA 的步骤。
未过滤窄幅波动与指标钝化区时,RSI 小幅穿插易被误判,可加入价格波段阈值与成交量过滤。
使用独立箭头缓冲并设定垂直偏移,或绘制在副图窗口,具体见本篇代码分解小节。