//+------------------------------------------------------------------+
//|                                                RSI_breakband.mq4 |
//|                                                          k-matsu |
//|                               http://www.age.jp/~k-matsu/FX/MT4/ |
//+------------------------------------------------------------------+

// version 1.00  2010/04/10

#property copyright "k-matsu"
#property link      "http://www.age.jp/~k-matsu/FX/MT4/"

#property indicator_chart_window
#property indicator_buffers 4
#property indicator_color1 SlateBlue
#property indicator_color2 Coral
#property indicator_color3 Aqua
#property indicator_color4 Yellow
#property indicator_width1 1
#property indicator_width2 1

//---- input parameters
extern int       RSIPeriod=14;
extern int       SwingMargin=4;
extern int       RSIMargin=2;
extern bool      TargetRSIFix=false;
extern bool      TargetNewerPeakTrough=false;
extern bool      ArrowPeakTrough=true;
extern double    TargetFixHigh=70.0;
extern double    TargetFixLow=30.0;
extern double    TargetHighLimit=99.0;
extern double    TargetLowLimit=1.0;
//---- buffers
double HiBandBuffer[];
double LwBandBuffer[];
double PeakBuffer[];
double TroughBuffer[];
//---- internal buffers
double UpMABuffer[];
double DnMABuffer[];
double RSIBuffer[];
//---- internal variables
double TargetRSIValue;
double bandtmpvalhigh, bandtmpvallow;
//----

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
   // sweep lines
   deinit();

//---- indicators
   IndicatorBuffers(7);
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,HiBandBuffer);
   SetIndexLabel(0, "RSIHigherBand("+RSIPeriod+")");
//   SetIndexShift(0,1);
   SetIndexStyle(1,DRAW_LINE);
   SetIndexBuffer(1,LwBandBuffer);
   SetIndexLabel(1, "RSILowerBand("+RSIPeriod+")");
//   SetIndexShift(1,1);
   SetIndexStyle(2,DRAW_ARROW);
   SetIndexArrow(2,218);
   SetIndexBuffer(2,PeakBuffer);
   SetIndexEmptyValue(2,0.0);
   SetIndexStyle(3,DRAW_ARROW);
   SetIndexArrow(3,217);
   SetIndexBuffer(3,TroughBuffer);
   SetIndexEmptyValue(3,0.0);
   SetIndexBuffer(4,UpMABuffer);
   SetIndexBuffer(5,DnMABuffer);
   SetIndexBuffer(6,RSIBuffer);

   if (!ArrowPeakTrough) {
     SetIndexStyle(2,DRAW_NONE);
     SetIndexStyle(3,DRAW_NONE);
   }

   // parameter check
   if (SwingMargin < 1) SwingMargin = 1;
   if (RSIMargin < 1) RSIMargin = 1;
   if (TargetHighLimit < 0.1) TargetHighLimit = 0.1;
   if (TargetHighLimit > 99.9) TargetHighLimit = 99.9;
   if (TargetLowLimit < 0.1) TargetLowLimit = 0.1;
   if (TargetLowLimit > 99.9) TargetLowLimit = 99.9;
   if (TargetFixHigh < 0.1) TargetFixHigh = 0.1;
   if (TargetFixHigh > 99.9) TargetFixHigh = 99.9;
   if (TargetFixLow < 0.1) TargetFixLow = 0.1;
   if (TargetFixLow > 99.9) TargetFixLow = 99.9;
   
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
  {
//----
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
    int i, j, limit, limitRSI;
    int counted_bars = IndicatorCounted();
    int prevpeakidx, prevtroughidx;
    if (counted_bars > 0) counted_bars--;
    int RSIidx, prevRSIidx;
    bool found=false;

    limit = Bars - counted_bars;
    for (i = 0; i< limit; i++) {
      PeakBuffer[i] = EMPTY_VALUE;
      TroughBuffer[i] = EMPTY_VALUE;
    }

    // calc RSI and its numerator/denominator
    limit = Bars - MathMax(counted_bars, 2);
    double dif, Uptmp = 0.0, Dntmp = 0.0, numerator, denominator;

    int initidx = Bars - RSIPeriod - 1;
    limit = Bars - 1 - MathMax(counted_bars, RSIPeriod);
    if (limit == initidx) {
      for (j= Bars-2; j >= initidx; j--) {
        dif =Close[j]-Close[j+1];
        if (dif >= 0) {
          Uptmp += dif;
        } else {
          Dntmp -= dif;
        }
      }
      UpMABuffer[initidx] = Uptmp / RSIPeriod;
      DnMABuffer[initidx] = Dntmp / RSIPeriod;

      numerator = UpMABuffer[initidx];
      denominator = numerator + DnMABuffer[initidx];
      if (denominator > 0.0) {
        RSIBuffer[initidx] = 100.0*numerator/denominator;
      } else {
        RSIBuffer[initidx] = 0.0;
      }
      limit--;
    }
    for (i = limit; i >= 0; i--) {
      dif =Close[i]-Close[i+1];
      if (dif >= 0) {
        Uptmp = dif;
        Dntmp = 0.0;
      } else {
        Uptmp = 0.0;
        Dntmp = -dif;
      }
      UpMABuffer[i] = (Uptmp + (RSIPeriod-1)*UpMABuffer[i+1]) / RSIPeriod;
      DnMABuffer[i] = (Dntmp + (RSIPeriod-1)*DnMABuffer[i+1]) / RSIPeriod;
      numerator = UpMABuffer[i];
      denominator = numerator + DnMABuffer[i];
      if (denominator > 0.0) {
        RSIBuffer[i] = 100.0*numerator/denominator;
      } else {
        RSIBuffer[i] = 0.0;
      }
    }

  // search peak
    prevpeakidx = 1; prevRSIidx = 0; limitRSI = Bars-MathMax(RSIPeriod, SwingMargin);
    if (TargetRSIFix) { updateTargetRSI(TargetFixHigh, TargetHighLimit, -1); }
    for (limit = 3; limit < limitRSI && PeakBuffer[limit] == EMPTY_VALUE; limit++) {}
    if (limit > SwingMargin) {
      for (i = 2; i< limit && High[i] == High[1]; i++) {}
      if (High[i] < High[1]) {
        i = searchNextTroughIdx(MODE_HIGH, i, limit, 1) + 1;
      }
      while (i < limit) {
        found=false;
        i = searchNextPeakIdx(MODE_HIGH, i, limit, SwingMargin);
        if (i - prevpeakidx >= SwingMargin) {
          if (PeakBuffer[i] == EMPTY_VALUE) {
            if (iHighest(NULL, 0, MODE_HIGH, SwingMargin*2+1, i-SwingMargin) == i) {
              found = true;
              PeakBuffer[i] = High[i];
            }
          } else {
            found = true;
          }
          if (found) {
            RSIidx = ArrayMaximum(RSIBuffer, RSIMargin*2+1, i-RSIMargin);
            calcBandBuffer(HiBandBuffer, RSIidx, prevRSIidx, 1);
            prevpeakidx = i;
            prevRSIidx = RSIidx;
          }
        }
        i = searchNextTroughIdx(MODE_HIGH, i+1, limit, 1);
        i++;
      }
      if (prevRSIidx == 0) {
        for (i = 1; PeakBuffer[i] == 0; i++) {}
        RSIidx = ArrayMaximum(RSIBuffer, RSIMargin*2+1, i-RSIMargin);
        calcBandBuffer(HiBandBuffer, RSIidx, prevRSIidx, 1);
      }
    }


  // search trough
    prevtroughidx = 1; prevRSIidx = 0; limitRSI = Bars-MathMax(RSIPeriod, SwingMargin);
    if (TargetRSIFix) { updateTargetRSI(TargetFixLow, TargetLowLimit, 1); }
    for (limit = 3; limit < limitRSI && TroughBuffer[limit] == EMPTY_VALUE; limit++) {}
    if (limit > SwingMargin) {
      for (i = 2; i< limit && Low[i] == Low[1]; i++) {}
      if (Low[i] > Low[1]) {
        i = searchNextPeakIdx(MODE_LOW, i, limit, 1) + 1;
      }
      while (i < limit) {
        found = false;
        i = searchNextTroughIdx(MODE_LOW, i, limit, SwingMargin);
        if (i - prevtroughidx >= SwingMargin) {
          if (TroughBuffer[i] == EMPTY_VALUE) {
            if (iLowest(NULL, 0, MODE_LOW, SwingMargin*2+1, i-SwingMargin) == i) {
              found = true;
              TroughBuffer[i] = Low[i];
            }
          } else {
            found = true;
          }
          if (found) {
            RSIidx = ArrayMinimum(RSIBuffer, RSIMargin*2+1, i-RSIMargin);
            calcBandBuffer(LwBandBuffer, RSIidx, prevRSIidx, -1);
            prevtroughidx = i;
            prevRSIidx = RSIidx;
          }
        }
        i = searchNextPeakIdx(MODE_LOW, i+1, limit, 1);
        i++;
      }
      if (prevRSIidx == 0) {
        for (i = 1; TroughBuffer[i] == 0; i++) {}
        RSIidx = ArrayMinimum(RSIBuffer, RSIMargin*2+1, i-RSIMargin);
        calcBandBuffer(LwBandBuffer, RSIidx, prevRSIidx, -1);
      }
    }
   
//----
   return(0);
  }
//+------------------------------------------------------------------+

// subfunction

// need call after searchNextTroughIdx
int searchNextPeakIdx(int mode, int startidx, int limit, int margin)
{
  int retidx, nextstartidx = startidx, prevpeakidx = nextstartidx, count;

  count = margin*2+1;  
  if (nextstartidx+count > limit) count = limit-nextstartidx+1;

  retidx = iHighest(NULL, 0, mode, count, nextstartidx);
  while (retidx > prevpeakidx && retidx < limit) {
    prevpeakidx = retidx;

    nextstartidx =prevpeakidx-margin;
    if (nextstartidx <= startidx) nextstartidx = startidx+1;

    count = margin*2+1;
    if (nextstartidx+count >= limit) count = limit-nextstartidx+1;

    retidx = iHighest(NULL, 0, mode, count, nextstartidx);
  }

  return(retidx);
}

// need call after searchNextPeakIdx
int searchNextTroughIdx(int mode, int startidx, int limit, int margin)
{
  int retidx, nextstartidx = startidx, prevtroughidx = nextstartidx, count;

  count = margin*2+1;
  if (nextstartidx+count > limit) count = limit-nextstartidx+1;

  retidx = iLowest(NULL, 0, mode, count, nextstartidx);
  while (retidx > prevtroughidx && retidx < limit) {
    prevtroughidx = retidx;

    nextstartidx =prevtroughidx-margin;
    if (nextstartidx <= startidx) nextstartidx = startidx+1;

    count = margin*2+1;
    if (nextstartidx+count >= limit) count = limit-nextstartidx+1;

    retidx = iLowest(NULL, 0, mode, count, nextstartidx);  
  }

  return(retidx);
}


int calcBandBuffer(double& BandBuffer[], int RSIidx, int prevRSIidx, int updatestyle)
{
  int i;
  if (!TargetRSIFix) {
    if (updatestyle == 1) {
      updateTargetRSI(RSIBuffer[RSIidx], 0.0, 1);
    } else {
      updateTargetRSI(RSIBuffer[RSIidx], 100.0, -1);
    }
  }
  for (i = RSIidx; i >= prevRSIidx; i--) {
    if ((!TargetRSIFix) && TargetNewerPeakTrough) {
      updateTargetRSI(RSIBuffer[i+1], TargetRSIValue, updatestyle);
    }
    if (RSIBuffer[i+1] > TargetRSIValue) {
      BandBuffer[i] = Close[i+1] - (RSIPeriod-1)*(bandtmpvallow*UpMABuffer[i+1] - DnMABuffer[i+1]);
    } else {
      BandBuffer[i] = Close[i+1] + (RSIPeriod-1)*(bandtmpvalhigh*DnMABuffer[i+1] - UpMABuffer[i+1]);
    }
  }
  return(0);
}

int updateTargetRSI(double newRSI, double oldRSITarget, int updatestyle)
{
  if (((updatestyle == 1) && (newRSI > oldRSITarget))
   || ((updatestyle == -1) && (newRSI < oldRSITarget)) ) {
    TargetRSIValue = MathMin(newRSI, TargetHighLimit);
    TargetRSIValue = MathMax(TargetRSIValue, TargetLowLimit);
    bandtmpvalhigh = TargetRSIValue/(100-TargetRSIValue);
    bandtmpvallow  = (100-TargetRSIValue)/TargetRSIValue;
  }

  return(0);
}

