//+------------------------------------------------------------------+
//| BrokerCheck_RSI_Divergence                                       |
//| RSI (close prices, Wilder smoothing) with divergences confirmed  |
//| on closed swing pivots. Regular divergences by default; hidden   |
//| divergences only when ShowHidden = true.                         |
//| Native MQL only: no DLL imports, no WebRequest, no order or      |
//| account functions, no alerts, no chart objects.                  |
//| Descriptive tool: it marks past price/RSI patterns and does not  |
//| forecast prices or trade.                                        |
//+------------------------------------------------------------------+
#property copyright "BrokerCheck"
#property version   "1.00"
#property description "RSI with regular (optional hidden) divergences, reported when the second swing pivot is confirmed by closed bars."
#property strict
#property indicator_separate_window
#property indicator_minimum 0
#property indicator_maximum 100
#property indicator_level1 30
#property indicator_level2 70
#property indicator_buffers 9
#property indicator_color1  clrDodgerBlue
#property indicator_color2  clrOrangeRed
#property indicator_color3  clrMediumSeaGreen
#property indicator_color4  clrOrangeRed
#property indicator_color5  clrMediumSeaGreen
#property indicator_color6  clrOrange
#property indicator_color7  clrTurquoise
#property indicator_color8  clrOrange
#property indicator_color9  clrTurquoise

input int  RsiPeriod        = 14;    // RSI period (close prices, Wilder smoothing)
input int  PivotLeftBars    = 5;     // Pivot: bars on the left that must be strictly lower (high) / higher (low)
input int  PivotRightBars   = 5;     // Pivot: closed bars on the right = confirmation lag
input int  MinPivotDistance = 5;     // Pair: minimum bars between the two pivots
input int  MaxPivotDistance = 60;    // Pair: maximum bars between the two pivots
input bool ShowHidden       = false; // Also mark hidden divergences

double RsiBuffer[];             // 0  RSI line
double BearLineBuffer[];        // 1  regular bearish: RSI line from pivot 1 to pivot 2
double BullLineBuffer[];        // 2  regular bullish: RSI line from pivot 1 to pivot 2
double BearArrowBuffer[];       // 3  regular bearish: arrow at the confirming bar
double BullArrowBuffer[];       // 4  regular bullish: arrow at the confirming bar
double HiddenBearLineBuffer[];  // 5  hidden bearish line (ShowHidden only)
double HiddenBullLineBuffer[];  // 6  hidden bullish line (ShowHidden only)
double HiddenBearArrowBuffer[]; // 7  hidden bearish arrow (ShowHidden only)
double HiddenBullArrowBuffer[]; // 8  hidden bullish arrow (ShowHidden only)
double AvgGainBuffer[];         // 9  Wilder average gain (calculation)
double AvgLossBuffer[];         // 10 Wilder average loss (calculation)
double PivotHighBuffer[];       // 11 1.0 at confirmed pivot highs (calculation)
double PivotLowBuffer[];        // 12 1.0 at confirmed pivot lows (calculation)
double HighPairStartBuffer[];   // 13 at a confirming bar: index of pivot 1 of a high-side detection (calculation)
double LowPairStartBuffer[];    // 14 at a confirming bar: index of pivot 1 of a low-side detection (calculation)

int OnInit()
{
  if(RsiPeriod<1 || PivotLeftBars<1 || PivotRightBars<1 || MinPivotDistance<1 || MaxPivotDistance<MinPivotDistance)
    return(INIT_PARAMETERS_INCORRECT);
  IndicatorBuffers(15);
  if(!SetIndexBuffer(0,RsiBuffer,INDICATOR_DATA)) return(INIT_FAILED);
  if(!SetIndexBuffer(1,BearLineBuffer,INDICATOR_DATA)) return(INIT_FAILED);
  if(!SetIndexBuffer(2,BullLineBuffer,INDICATOR_DATA)) return(INIT_FAILED);
  if(!SetIndexBuffer(3,BearArrowBuffer,INDICATOR_DATA)) return(INIT_FAILED);
  if(!SetIndexBuffer(4,BullArrowBuffer,INDICATOR_DATA)) return(INIT_FAILED);
  if(!SetIndexBuffer(5,HiddenBearLineBuffer,INDICATOR_DATA)) return(INIT_FAILED);
  if(!SetIndexBuffer(6,HiddenBullLineBuffer,INDICATOR_DATA)) return(INIT_FAILED);
  if(!SetIndexBuffer(7,HiddenBearArrowBuffer,INDICATOR_DATA)) return(INIT_FAILED);
  if(!SetIndexBuffer(8,HiddenBullArrowBuffer,INDICATOR_DATA)) return(INIT_FAILED);
  if(!SetIndexBuffer(9,AvgGainBuffer,INDICATOR_CALCULATIONS)) return(INIT_FAILED);
  if(!SetIndexBuffer(10,AvgLossBuffer,INDICATOR_CALCULATIONS)) return(INIT_FAILED);
  if(!SetIndexBuffer(11,PivotHighBuffer,INDICATOR_CALCULATIONS)) return(INIT_FAILED);
  if(!SetIndexBuffer(12,PivotLowBuffer,INDICATOR_CALCULATIONS)) return(INIT_FAILED);
  if(!SetIndexBuffer(13,HighPairStartBuffer,INDICATOR_CALCULATIONS)) return(INIT_FAILED);
  if(!SetIndexBuffer(14,LowPairStartBuffer,INDICATOR_CALCULATIONS)) return(INIT_FAILED);
  BcSetChronological();
  SetIndexStyle(0,DRAW_LINE,STYLE_SOLID,2,clrDodgerBlue);
  SetIndexStyle(1,DRAW_LINE,STYLE_SOLID,2,clrOrangeRed);
  SetIndexStyle(2,DRAW_LINE,STYLE_SOLID,2,clrMediumSeaGreen);
  SetIndexStyle(3,DRAW_ARROW,EMPTY,1,clrOrangeRed);
  SetIndexStyle(4,DRAW_ARROW,EMPTY,1,clrMediumSeaGreen);
  SetIndexStyle(5,DRAW_LINE,STYLE_DOT,1,clrOrange);
  SetIndexStyle(6,DRAW_LINE,STYLE_DOT,1,clrTurquoise);
  SetIndexStyle(7,DRAW_ARROW,EMPTY,1,clrOrange);
  SetIndexStyle(8,DRAW_ARROW,EMPTY,1,clrTurquoise);
  SetIndexArrow(3,234);
  SetIndexArrow(4,233);
  SetIndexArrow(7,234);
  SetIndexArrow(8,233);
  SetIndexLabel(0,"RSI");
  SetIndexLabel(1,"Regular bearish divergence");
  SetIndexLabel(2,"Regular bullish divergence");
  SetIndexLabel(3,"Regular bearish confirmed");
  SetIndexLabel(4,"Regular bullish confirmed");
  SetIndexLabel(5,"Hidden bearish divergence");
  SetIndexLabel(6,"Hidden bullish divergence");
  SetIndexLabel(7,"Hidden bearish confirmed");
  SetIndexLabel(8,"Hidden bullish confirmed");
  for(int plot=0; plot<9; ++plot)
    SetIndexEmptyValue(plot,EMPTY_VALUE);
  SetIndexDrawBegin(0,RsiPeriod);
  IndicatorDigits(2);
  IndicatorShortName("BrokerCheck RSI divergence ("+IntegerToString(RsiPeriod)+", "+
                     IntegerToString(PivotLeftBars)+"/"+IntegerToString(PivotRightBars)+")");
  return(INIT_SUCCEEDED);
}

//=== BROKERCHECK CALCULATION SECTION (identical in the .mq5 and .mq4 files; the native test harness compiles this exact text) ===
// Indexing is chronological: 0 = oldest bar. Price arrays and buffers are switched to that order on every call.
// Closed-bar rule: the newest bar (rates_total - 1) may still be forming, so it never confirms a pivot.
// A divergence is written at its confirming bar c = pivot 2 + PivotRightBars and is never back-dated.

void BcSetChronological()
{
  ArraySetAsSeries(RsiBuffer,false);
  ArraySetAsSeries(BearLineBuffer,false);
  ArraySetAsSeries(BullLineBuffer,false);
  ArraySetAsSeries(BearArrowBuffer,false);
  ArraySetAsSeries(BullArrowBuffer,false);
  ArraySetAsSeries(HiddenBearLineBuffer,false);
  ArraySetAsSeries(HiddenBullLineBuffer,false);
  ArraySetAsSeries(HiddenBearArrowBuffer,false);
  ArraySetAsSeries(HiddenBullArrowBuffer,false);
  ArraySetAsSeries(AvgGainBuffer,false);
  ArraySetAsSeries(AvgLossBuffer,false);
  ArraySetAsSeries(PivotHighBuffer,false);
  ArraySetAsSeries(PivotLowBuffer,false);
  ArraySetAsSeries(HighPairStartBuffer,false);
  ArraySetAsSeries(LowPairStartBuffer,false);
}

// RSI = 100 - 100 / (1 + average gain / average loss). Zero average loss: 100, or 50 when nothing moved.
double BcRsiFromAverages(const double avgGain,const double avgLoss)
{
  if(avgLoss==0.0)
    return(avgGain==0.0 ? 50.0 : 100.0);
  return(100.0-100.0/(1.0+avgGain/avgLoss));
}

void BcClearRsiBars(const int from,const int to)
{
  for(int i=from; i<to; ++i)
  {
    RsiBuffer[i]=EMPTY_VALUE;
    AvgGainBuffer[i]=EMPTY_VALUE;
    AvgLossBuffer[i]=EMPTY_VALUE;
  }
}

void BcClearDivergenceBars(const int from,const int to)
{
  for(int i=from; i<to; ++i)
  {
    BearLineBuffer[i]=EMPTY_VALUE;
    BullLineBuffer[i]=EMPTY_VALUE;
    BearArrowBuffer[i]=EMPTY_VALUE;
    BullArrowBuffer[i]=EMPTY_VALUE;
    HiddenBearLineBuffer[i]=EMPTY_VALUE;
    HiddenBullLineBuffer[i]=EMPTY_VALUE;
    HiddenBearArrowBuffer[i]=EMPTY_VALUE;
    HiddenBullArrowBuffer[i]=EMPTY_VALUE;
    PivotHighBuffer[i]=0.0;
    PivotLowBuffer[i]=0.0;
    HighPairStartBuffer[i]=EMPTY_VALUE;
    LowPairStartBuffer[i]=EMPTY_VALUE;
  }
}

// Pivot high: strictly above the PivotLeftBars highs before it, not exceeded by the PivotRightBars highs after it.
// Equal highs: the earliest bar wins (a later equal high fails the strict left test).
bool BcIsPivotHigh(const int bar,const double &high[])
{
  for(int j=bar-PivotLeftBars; j<bar; ++j)
    if(high[j]>=high[bar])
      return(false);
  for(int j=bar+1; j<=bar+PivotRightBars; ++j)
    if(high[j]>high[bar])
      return(false);
  return(true);
}

// Pivot low: mirror image of the pivot-high rule.
bool BcIsPivotLow(const int bar,const double &low[])
{
  for(int j=bar-PivotLeftBars; j<bar; ++j)
    if(low[j]<=low[bar])
      return(false);
  for(int j=bar+1; j<=bar+PivotRightBars; ++j)
    if(low[j]<low[bar])
      return(false);
  return(true);
}

// Nearest earlier pivot of the same kind, searched back at most MaxPivotDistance bars; -1 if none.
int BcPreviousPivot(const double &flags[],const int bar)
{
  const int oldest=(int)MathMax(0,bar-MaxPivotDistance);
  for(int j=bar-1; j>=oldest; --j)
    if(flags[j]!=0.0)
      return(j);
  return(-1);
}

// Straight line on the RSI panel from RSI at pivot 1 to RSI at pivot 2. Both end points hold the exact RSI
// values, so a later segment that starts at this pivot 2 writes the same value there and nothing drawn changes.
void BcDrawSegment(double &line[],const int from,const int to)
{
  const double span=(double)(to-from);
  for(int k=from; k<to; ++k)
    line[k]=RsiBuffer[from]+(RsiBuffer[to]-RsiBuffer[from])*((double)(k-from)/span);
  line[to]=RsiBuffer[to];
}

// Called once for every closed bar c: tests whether bar c - PivotRightBars is a confirmed pivot
// and compares it with the previous pivot of the same kind.
void BcConfirm(const int c,const double &high[],const double &low[])
{
  const int p=c-PivotRightBars;
  if(p-PivotLeftBars<0)
    return;
  if(BcIsPivotHigh(p,high))
  {
    const int q=BcPreviousPivot(PivotHighBuffer,p);
    PivotHighBuffer[p]=1.0;
    if(q>=RsiPeriod && p-q>=MinPivotDistance)
    {
      if(high[p]>high[q] && RsiBuffer[p]<RsiBuffer[q])
      {
        BearArrowBuffer[c]=RsiBuffer[c];
        HighPairStartBuffer[c]=(double)q;
        BcDrawSegment(BearLineBuffer,q,p);
      }
      else if(ShowHidden && high[p]<high[q] && RsiBuffer[p]>RsiBuffer[q])
      {
        HiddenBearArrowBuffer[c]=RsiBuffer[c];
        HighPairStartBuffer[c]=(double)q;
        BcDrawSegment(HiddenBearLineBuffer,q,p);
      }
    }
  }
  if(BcIsPivotLow(p,low))
  {
    const int q=BcPreviousPivot(PivotLowBuffer,p);
    PivotLowBuffer[p]=1.0;
    if(q>=RsiPeriod && p-q>=MinPivotDistance)
    {
      if(low[p]<low[q] && RsiBuffer[p]>RsiBuffer[q])
      {
        BullArrowBuffer[c]=RsiBuffer[c];
        LowPairStartBuffer[c]=(double)q;
        BcDrawSegment(BullLineBuffer,q,p);
      }
      else if(ShowHidden && low[p]>low[q] && RsiBuffer[p]<RsiBuffer[q])
      {
        HiddenBullArrowBuffer[c]=RsiBuffer[c];
        LowPairStartBuffer[c]=(double)q;
        BcDrawSegment(HiddenBullLineBuffer,q,p);
      }
    }
  }
}

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[])
{
  BcSetChronological();
  ArraySetAsSeries(high,false);
  ArraySetAsSeries(low,false);
  ArraySetAsSeries(close,false);
  if(rates_total<=RsiPeriod)
  {
    // Insufficient history: nothing is drawn; returning 0 forces a full calculation next time.
    BcClearRsiBars(0,rates_total);
    BcClearDivergenceBars(0,rates_total);
    return(0);
  }
  int rsiFrom;
  int confirmFrom;
  if(prev_calculated<=RsiPeriod+1 || prev_calculated>rates_total)
  {
    // Full calculation: first call, reloaded history, or the seed bar was still the newest bar.
    BcClearRsiBars(0,rates_total);
    BcClearDivergenceBars(0,rates_total);
    double sumGain=0.0;
    double sumLoss=0.0;
    for(int i=1; i<=RsiPeriod; ++i)
    {
      const double change=close[i]-close[i-1];
      if(change>0.0)
        sumGain+=change;
      else
        sumLoss-=change;
    }
    // Seed: simple mean of the first RsiPeriod price changes, stored at index RsiPeriod.
    AvgGainBuffer[RsiPeriod]=sumGain/(double)RsiPeriod;
    AvgLossBuffer[RsiPeriod]=sumLoss/(double)RsiPeriod;
    RsiBuffer[RsiPeriod]=BcRsiFromAverages(AvgGainBuffer[RsiPeriod],AvgLossBuffer[RsiPeriod]);
    rsiFrom=RsiPeriod+1;
    confirmFrom=0;
  }
  else
  {
    // Incremental: the previous newest bar and every new bar are recalculated.
    BcClearDivergenceBars(prev_calculated-1,rates_total);
    rsiFrom=prev_calculated-1;
    confirmFrom=prev_calculated-1;
  }
  // Wilder smoothing: new average = (previous average * (period - 1) + current value) / period.
  for(int i=rsiFrom; i<rates_total && !IsStopped(); ++i)
  {
    const double change=close[i]-close[i-1];
    AvgGainBuffer[i]=(AvgGainBuffer[i-1]*(double)(RsiPeriod-1)+(change>0.0 ? change : 0.0))/(double)RsiPeriod;
    AvgLossBuffer[i]=(AvgLossBuffer[i-1]*(double)(RsiPeriod-1)+(change<0.0 ? -change : 0.0))/(double)RsiPeriod;
    RsiBuffer[i]=BcRsiFromAverages(AvgGainBuffer[i],AvgLossBuffer[i]);
  }
  // Only closed bars (index <= rates_total - 2) can confirm a pivot.
  for(int c=confirmFrom; c<=rates_total-2 && !IsStopped(); ++c)
    BcConfirm(c,high,low);
  return(rates_total);
}
