//------------------------------------------------------------------
//------------------------------------------------------------------
#property copyright "mladen"
#property link      "www.forex-tsd.com"

#property indicator_separate_window
#property indicator_buffers 7
#property indicator_color1  clrLimeGreen
#property indicator_color2  clrPaleVioletRed
#property indicator_color3  clrSilver
#property indicator_color4  clrLimeGreen
#property indicator_color5  clrLimeGreen
#property indicator_color6  clrPaleVioletRed
#property indicator_color7  clrPaleVioletRed
#property indicator_width3  2
#property indicator_width4  3
#property indicator_width5  3
#property indicator_width6  3
#property indicator_width7  3
#property indicator_style1  STYLE_DOT
#property indicator_style2  STYLE_DOT
#property strict

//
//
//
//
//

//
//
//
//
//

#import "dynamicZone.dll"
   double dzBuyP(double& sourceArray[],double probabiltyValue, int lookBack, int bars, int i,double precision);
   double dzSellP(double& sourceArray[],double probabiltyValue, int lookBack, int bars, int i,double precision);
#import
enum enRsiTypes
{
   rsi_rsi,  // Regular RSI
   rsi_slow, // Slow RSI
   rsi_rap,  // Rapid RSI
   rsi_har,  // Harris RSI
   rsi_rsx,  // RSX
   rsi_cut   // Cuttlers RSI
};

extern int                RsiPeriod       = 14;           // Rsi period to use
extern ENUM_APPLIED_PRICE RsiPrice        = PRICE_CLOSE;  // Price to use
extern enRsiTypes         RsiType         = rsi_rsi;      // Rsi type to use
extern int                AccPeriod       = 5;            // Accumulate last n rsi values
extern double             LevelUp         = 90;           // Level up (%)
extern double             LevelDown       = 10;           // Level down (%)
extern int                LookBackBars    = 25;           // Look back bars no
extern bool               alertsOn        = false;        // Turn alerts on
extern bool               alertsOnCurrent = false;        // Alerts on current (still opened) bar?
extern bool               alertsMessage   = true;         // Alerts should display a pop-up message
extern bool               alertsSound     = true;         // Alerts should play an alert sound?
extern bool               alertsEmail     = false;        // Alerts should send an email?

//
//
//
//
//

double rsi[],rsic[],rsicDa[],rsicDb[],rsicUa[],rsicUb[];
double up[],down[],bellow[];

//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

int init()
{
   IndicatorBuffers(9);   
      SetIndexBuffer(0,up);
      SetIndexBuffer(1,down);
      SetIndexBuffer(2,rsic);
      SetIndexBuffer(3,rsicDa);
      SetIndexBuffer(4,rsicDb);
      SetIndexBuffer(5,rsicUa);
      SetIndexBuffer(6,rsicUb);
      SetIndexBuffer(7,rsi);
      SetIndexBuffer(8,bellow);
   IndicatorShortName("Accumulated "+getRsiName(RsiType)+" ("+(string)RsiPeriod+","+(string)AccPeriod+")");
   return(0);
}



//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int start()
{
   int counted_bars=IndicatorCounted();
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
           int limit=MathMin(Bars-counted_bars,Bars-1);

   //
   //
   //
   //
   //
   
   if (bellow[limit] ==-1) CleanPoint(limit,rsicDa,rsicDb);
   if (bellow[limit] == 1) CleanPoint(limit,rsicUa,rsicUb);
   for(int i=limit; i>=0; i--)
   {         
      rsi[i]    = iRsi(RsiType,iMA(NULL,0,1,0,MODE_SMA,RsiPrice,i),RsiPeriod,i);
      rsic[i]   = 0;
      rsicDa[i] = EMPTY_VALUE;
      rsicDb[i] = EMPTY_VALUE;
      rsicUa[i] = EMPTY_VALUE;
      rsicUb[i] = EMPTY_VALUE;
      bellow[i] = 0;
         for (int k=0; k<AccPeriod && (i+k)<Bars; k++) rsic[i] += rsi[i+k];
            up[i]   = dzBuyP (rsic,   LevelUp  /100,LookBackBars, Bars, i,0.0001);
            down[i] = dzSellP(rsic, 1-LevelDown/100,LookBackBars, Bars, i,0.0001);
         if (rsic[i]<down[i])                   bellow[i] =  1;
         if (rsic[i]>up[i]  )                   bellow[i] = -1;
         if (bellow[i] == 1) PlotPoint(i,rsicUa,rsicUb,rsic);
         if (bellow[i] ==-1) PlotPoint(i,rsicDa,rsicDb,rsic);
   }
   manageAlerts();
   return(0);
}


//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//
//

string rsiMethodNames[] = {"RSI","Slow RSI","Rapid RSI","Harris RSI","RSX","Cuttler RSI"};
string getRsiName(int method)
{
   int max = ArraySize(rsiMethodNames)-1;
      method=MathMax(MathMin(method,max),0); return(rsiMethodNames[method]);
}

//
//
//
//
//

double workRsi[][13];
#define _price  0
#define _change 1
#define _changa 2
#define _rsival 1
#define _rsval  1

double iRsi(int rsiMode, double price, double period, int i, int instanceNo=0)
{
   if (ArrayRange(workRsi,0)!=Bars) ArrayResize(workRsi,Bars);
      int z = instanceNo*13; 
      int r = Bars-i-1;
   
   //
   //
   //
   //
   //
   
   workRsi[r][z+_price] = price;
   switch (rsiMode)
   {
      case rsi_rsi:
         {
            double alpha = 1.0/period; 
            if (r<period)
               {
                  int k; double sum = 0; for (k=0; k<period && (r-k-1)>=0; k++) sum += MathAbs(workRsi[r-k][z+_price]-workRsi[r-k-1][z+_price]);
                     workRsi[r][z+_change] = (workRsi[r][z+_price]-workRsi[0][z+_price])/MathMax(k,1);
                     workRsi[r][z+_changa] =                                         sum/MathMax(k,1);
               }
            else
               {
                  double change = workRsi[r][z+_price]-workRsi[r-1][z+_price];
                                  workRsi[r][z+_change] = workRsi[r-1][z+_change] + alpha*(        change  - workRsi[r-1][z+_change]);
                                  workRsi[r][z+_changa] = workRsi[r-1][z+_changa] + alpha*(MathAbs(change) - workRsi[r-1][z+_changa]);
               }
            if (workRsi[r][z+_changa] != 0)
                  return(50.0*(workRsi[r][z+_change]/workRsi[r][z+_changa]+1));
            else  return(50.0);
         }            
         
      //
      //
      //
      //
      //
      
      case rsi_slow :
         {
            if (r<period) { workRsi[r][z+_rsival]=0; return(0); } 
            double up = 0;
            double dn = 0;
            for (int k=0; k<(int)period && (r-k-1)>=0; k++)
            {
               double diff = workRsi[r-k][z+_price]- workRsi[r-k-1][z+_price];
               if(diff>0)
                     up += diff;
               else  dn -= diff;
            }
            if(up + dn == 0)
                  workRsi[r][z+_rsival] = workRsi[r-1][z+_rsival]+(1/period)*(50            -workRsi[r-1][z+_rsival]);
            else  workRsi[r][z+_rsival] = workRsi[r-1][z+_rsival]+(1/period)*(100*up/(up+dn)-workRsi[r-1][z+_rsival]);
            return(workRsi[r][z+_rsival]);      
         }            

      //
      //
      //
      //
      //

      case rsi_rap :
         {
            double up = 0, dn = 0;
            for(int k=0; k<(int)period && (r-k-1)>=0; k++)
            {
               double diff = workRsi[r-k][z+_price]- workRsi[r-k-1][z+_price];
               if(diff>0)
                     up += diff;
               else  dn -= diff;
            }
            if(up + dn == 0)
                  return(50);
            else  return(100 * up / (up + dn));      
         }            

      //
      //
      //
      //
      //

      
      case rsi_har :
         {
            double avgUp=0,avgDn=0,up=0,dn=0;
            for(int k=0; k<(int)period && (r-k-1)>=0; k++)
            {
               double diff = workRsi[r-k][instanceNo+_price]- workRsi[r-k-1][instanceNo+_price];
               if(diff>0)
                     { avgUp += diff; up++; }
               else  { avgDn -= diff; dn++; }
            }
            if (up!=0) avgUp /= up;
            if (dn!=0) avgDn /= dn;
            double rs = 1;
               if (avgDn!=0) rs = avgUp/avgDn;
               return(100-100/(1.0+rs));
         }               

      //
      //
      //
      //
      //
      
      case rsi_rsx :  
         {   
            double Kg = (3.0)/(2.0+period), Hg = 1.0-Kg;
            if (r<period) { for (int k=1; k<13; k++) workRsi[r][k+z] = 0; return(50); }  

            //
            //
            //
            //
            //
      
            double mom = workRsi[r][_price+z]-workRsi[r-1][_price+z];
            double moa = MathAbs(mom);
            for (int k=0; k<3; k++)
            {
               int kk = k*2;
               workRsi[r][z+kk+1] = Kg*mom                + Hg*workRsi[r-1][z+kk+1];
               workRsi[r][z+kk+2] = Kg*workRsi[r][z+kk+1] + Hg*workRsi[r-1][z+kk+2]; mom = 1.5*workRsi[r][z+kk+1] - 0.5 * workRsi[r][z+kk+2];
               workRsi[r][z+kk+7] = Kg*moa                + Hg*workRsi[r-1][z+kk+7];
               workRsi[r][z+kk+8] = Kg*workRsi[r][z+kk+7] + Hg*workRsi[r-1][z+kk+8]; moa = 1.5*workRsi[r][z+kk+7] - 0.5 * workRsi[r][z+kk+8];
            }
            if (moa != 0)
                 return(MathMax(MathMin((mom/moa+1.0)*50.0,100.00),0.00)); 
            else return(50);
         }            
            
      //
      //
      //
      //
      //
      
      case rsi_cut :
         {
            double sump = 0;
            double sumn = 0;
            for (int k=0; k<(int)period && (r-k-1)>=0; k++)
            {
               double diff = workRsi[r-k][z+_price]-workRsi[r-k-1][z+_price];
                  if (diff > 0) sump += diff;
                  if (diff < 0) sumn -= diff;
            }
            if (sumn > 0)
                  return(100.0-100.0/(1.0+sump/sumn));
            else  return(50);
         }            
   } 
   return(0);
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

void CleanPoint(int i,double& first[],double& second[])
{
   if (i>=Bars-3) return;
   if ((second[i]  != EMPTY_VALUE) && (second[i+1] != EMPTY_VALUE))
        second[i+1] = EMPTY_VALUE;
   else
      if ((first[i] != EMPTY_VALUE) && (first[i+1] != EMPTY_VALUE) && (first[i+2] == EMPTY_VALUE))
          first[i+1] = EMPTY_VALUE;
}

void PlotPoint(int i,double& first[],double& second[],double& from[])
{
   if (i>=Bars-2) return;
   if (first[i+1] == EMPTY_VALUE)
      if (first[i+2] == EMPTY_VALUE) 
            { first[i]  = from[i];  first[i+1]  = from[i+1]; second[i] = EMPTY_VALUE; }
      else  { second[i] =  from[i]; second[i+1] = from[i+1]; first[i]  = EMPTY_VALUE; }
   else     { first[i]  = from[i];                           second[i] = EMPTY_VALUE; }
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

void manageAlerts()
{
   if (alertsOn)
   {
      int whichBar = 1; if (alertsOnCurrent) whichBar = 0;
      if (bellow[whichBar] != bellow[whichBar+1])
      {
         if (bellow[whichBar] ==  1)                             doAlert(whichBar," lower level broken down");
         if (bellow[whichBar] == -1)                             doAlert(whichBar," upper level broken up");
         if (bellow[whichBar] ==  0 && bellow[whichBar+1] ==  1) doAlert(whichBar," lower level broken up");
         if (bellow[whichBar] ==  0 && bellow[whichBar+1] == -1) doAlert(whichBar," upper level broken down");
      }
   }
}

//
//
//
//
//

void doAlert(int forBar, string doWhat)
{
   static string   previousAlert="nothing";
   static datetime previousTime;
   string message;
   
   if (previousAlert != doWhat || previousTime != Time[forBar]) {
       previousAlert  = doWhat;
       previousTime   = Time[forBar];

       //
       //
       //
       //
       //

       message =  Symbol()+" at "+TimeToStr(TimeLocal(),TIME_SECONDS)+" rsi accumulated "+doWhat;
          if (alertsMessage) Alert(message);
          if (alertsEmail)   SendMail(Symbol()+" rsi accumulated ",message);
          if (alertsSound)   PlaySound("alert2.wav");
   }
}