Complete .mq5 source code

RSI & MACD Divergence MQL5 Indicator: Automated Regular & Hidden Divergence Detection

Inspect an educational MT5 indicator that compares paired price/oscillator pivots for regular and hidden divergence. RSI and MACD main-minus-signal results use separate price-chart arrows. Confirmation waits for closed bars to the right of a pivot; no verified profitability, win rate or false-signal reduction is supplied.

How the indicator signal works

Calculation and signal conditions

A bullish regular pattern compares a lower price low with a higher oscillator low; a hidden pattern compares a higher price low with a lower oscillator low. This implementation requires price and oscillator pivots on the same bars and compares the nearest eligible earlier pair within InpLookback. The optional EMA filter checks the pivot close against the EMA. RSI and MACD-difference scans are independent, not a combined confirmation rule.

Interpretation and limitations

Bearish comparisons mirror the high pivots. The indicator only draws four arrow series and optionally emits a local Alert; it does not open or close positions, trail stops, send push notifications or scan another timeframe. With pivot width 2, two subsequent bars must close before the arrow appears at the older pivot. Backtests must use that confirmation time rather than trading at the plotted pivot bar.

MQL5 Custom Indicator Code

// Educational price-chart divergence markers; no orders or verified performance.
#property strict
#property indicator_chart_window
#property indicator_buffers 4
#property indicator_plots 4
#property indicator_label1 "RSI bullish"
#property indicator_type1 DRAW_ARROW
#property indicator_color1 clrLime
#property indicator_label2 "RSI bearish"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrRed
#property indicator_label3 "MACD difference bullish"
#property indicator_type3 DRAW_ARROW
#property indicator_color3 clrDodgerBlue
#property indicator_label4 "MACD difference bearish"
#property indicator_type4 DRAW_ARROW
#property indicator_color4 clrOrange
input int InpRSIPeriod=14;
input int InpMACDFast=12;
input int InpMACDSlow=26;
input int InpMACDSignal=9;
input int InpMinBarsGap=5;
input int InpLookback=60;
input int InpPivotWidth=2; // Closed bars required on each side
input bool InpUseRSI=true;
input bool InpUseMACD=true;
input bool InpFilterEMA=true;
input int InpEMA=200;
input bool InpScanRegular=true;
input bool InpScanHidden=true;
input bool InpAlerts=false;
double RBull[],RBear[],MBull[],MBear[];
double Rsi[],Main[],Signal[],Osc[],Ema[];
int hRSI=INVALID_HANDLE,hMACD=INVALID_HANDLE,hEMA=INVALID_HANDLE;
datetime lastAlert=0;
int OnInit()
  {
   if(InpRSIPeriod<2 || InpMACDFast<1 || InpMACDSlow<=InpMACDFast ||
      InpMACDSignal<1 || InpPivotWidth<1 || InpMinBarsGap<1 ||
      InpLookback<InpMinBarsGap || InpEMA<1 || (!InpUseRSI && !InpUseMACD))
      return INIT_PARAMETERS_INCORRECT;
   SetIndexBuffer(0,RBull,INDICATOR_DATA);
   SetIndexBuffer(1,RBear,INDICATOR_DATA);
   SetIndexBuffer(2,MBull,INDICATOR_DATA);
   SetIndexBuffer(3,MBear,INDICATOR_DATA);
   for(int p=0;p<4;p++)
     {
      PlotIndexSetInteger(p,PLOT_ARROW,p%2==0 ? 233 : 234);
      PlotIndexSetDouble(p,PLOT_EMPTY_VALUE,EMPTY_VALUE);
     }
   if(InpUseRSI) hRSI=iRSI(_Symbol,_Period,InpRSIPeriod,PRICE_CLOSE);
   if(InpUseMACD) hMACD=iMACD(_Symbol,_Period,InpMACDFast,InpMACDSlow,InpMACDSignal,PRICE_CLOSE);
   if(InpFilterEMA) hEMA=iMA(_Symbol,_Period,InpEMA,0,MODE_EMA,PRICE_CLOSE);
   if((InpUseRSI && hRSI==INVALID_HANDLE) || (InpUseMACD && hMACD==INVALID_HANDLE) ||
      (InpFilterEMA && hEMA==INVALID_HANDLE)) return INIT_FAILED;
   IndicatorSetString(INDICATOR_SHORTNAME,"Confirmed pivot divergence (educational)");
   return INIT_SUCCEEDED;
  }
void OnDeinit(const int reason)
  {
   if(hRSI!=INVALID_HANDLE) IndicatorRelease(hRSI);
   if(hMACD!=INVALID_HANDLE) IndicatorRelease(hMACD);
   if(hEMA!=INVALID_HANDLE) IndicatorRelease(hEMA);
  }
bool Pivot(const double &a[],int i,int w,bool peak)
  {
   if(i<w || i+w>=ArraySize(a)) return false;
   for(int k=i-w;k<=i+w;k++)
      if(k!=i && (peak ? a[k]>=a[i] : a[k]<=a[i])) return false;
   return true;
  }
void Scan(const double &hi[],const double &lo[],const double &cl[],
          const double &osc[],double &bull[],double &bear[],int n,int warm,double offset)
  {
   int w=InpPivotWidth;
   // n-1 is still forming; require w CLOSED bars to the right of each pivot.
   for(int i=warm+w+InpMinBarsGap;i<=n-2-w;i++)
     {
      double pad=MathMax((hi[i]-lo[i])*0.2,_Point*10)*offset;
      if(Pivot(lo,i,w,false) && Pivot(osc,i,w,false))
        {
         for(int j=i-InpMinBarsGap;j>=MathMax(warm+w,i-InpLookback);j--)
           {
            if(!Pivot(lo,j,w,false) || !Pivot(osc,j,w,false)) continue;
            bool regular=lo[i]<lo[j] && osc[i]>osc[j];
            bool hidden=lo[i]>lo[j] && osc[i]<osc[j];
            if(((InpScanRegular && regular) || (InpScanHidden && hidden)) &&
               (!InpFilterEMA || cl[i]>Ema[i])) bull[i]=lo[i]-pad;
            break; // Compare the nearest earlier eligible paired pivot.
           }
        }
      if(Pivot(hi,i,w,true) && Pivot(osc,i,w,true))
        {
         for(int j=i-InpMinBarsGap;j>=MathMax(warm+w,i-InpLookback);j--)
           {
            if(!Pivot(hi,j,w,true) || !Pivot(osc,j,w,true)) continue;
            bool regular=hi[i]>hi[j] && osc[i]<osc[j];
            bool hidden=hi[i]<hi[j] && osc[i]>osc[j];
            if(((InpScanRegular && regular) || (InpScanHidden && hidden)) &&
               (!InpFilterEMA || cl[i]<Ema[i])) bear[i]=hi[i]+pad;
            break;
           }
        }
     }
  }
int OnCalculate(const int rates_total,const int prev_calculated,const datetime &time[],
                const double &open[],const double &high[],const double &low[],const double &close[],
                const long &tick_volume[],const long &volume[],const int &spread[])
  {
   int n=rates_total;
   int warm=MathMax(InpRSIPeriod,InpMACDSlow+InpMACDSignal);
   if(InpFilterEMA) warm=MathMax(warm,InpEMA);
   if(n<warm+2*InpPivotWidth+InpMinBarsGap+2) return 0;
   ArraySetAsSeries(high,false);ArraySetAsSeries(low,false);
   ArraySetAsSeries(close,false);ArraySetAsSeries(time,false);
   if(InpUseRSI && CopyBuffer(hRSI,0,0,n,Rsi)!=n) return prev_calculated;
   if(InpUseMACD)
     {
      if(CopyBuffer(hMACD,0,0,n,Main)!=n || CopyBuffer(hMACD,1,0,n,Signal)!=n) return prev_calculated;
      ArrayResize(Osc,n);
      for(int i=0;i<n;i++) Osc[i]=Main[i]-Signal[i];
     }
   if(InpFilterEMA && CopyBuffer(hEMA,0,0,n,Ema)!=n) return prev_calculated;
   ArrayInitialize(RBull,EMPTY_VALUE);ArrayInitialize(RBear,EMPTY_VALUE);
   ArrayInitialize(MBull,EMPTY_VALUE);ArrayInitialize(MBear,EMPTY_VALUE);
   if(InpUseRSI) Scan(high,low,close,Rsi,RBull,RBear,n,warm,1);
   if(InpUseMACD) Scan(high,low,close,Osc,MBull,MBear,n,warm,2);
   int p=n-2-InpPivotWidth;
   if(InpAlerts && time[p]!=lastAlert &&
      (RBull[p]!=EMPTY_VALUE || RBear[p]!=EMPTY_VALUE || MBull[p]!=EMPTY_VALUE || MBear[p]!=EMPTY_VALUE))
     {
      Alert(_Symbol," ",EnumToString(_Period)," divergence pivot confirmed at ",TimeToString(time[p]));
      lastAlert=time[p];
     }
   return rates_total;
  }

Copy this code into MetaEditor, save it in the MQL5/Indicators folder, and compile with F7.

Change the settings or add your own signal filter

Describe the inputs, buffers, alerts, visual style, or confirmation filter you want. Pineify generates editable MQL5 source code that you can inspect and compile in MetaEditor.

Frequently Asked Questions

Related MQL5 pages

Technical references

Risk and testing note

These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.