//------------------------------------------------------------------
// original idea for this indicator from Godfreyh
// this version by mladen and mrtools
//------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers    4
#property indicator_levelcolor DarkOrchid
#property strict

//
//
//
//
//

enum enRsiTypes
{
   rsi_rsi,  // Regular RSI
   rsi_wil,  // Slow RSI
   rsi_rap,  // Rapid RSI
   rsi_har,  // Harris RSI
   rsi_rsx,  // RSX
   rsi_cut   // Cuttlers RSI
};

extern ENUM_TIMEFRAMES TimeFrame         = PERIOD_CURRENT;     // Time frame
extern string          ForSymbol         = "";                 // Symbol to use (leave empty for current symbol)
extern int             RsiPeriod         = 10;                 // Rsi period
extern enRsiTypes      RsiMethod         = rsi_rsx;            // Rsi type
extern double          levelOb           = 80;                 // Overbought level
extern double          levelOs           = 20;                 // Oversold level
extern bool            alertsOn          = true;               // Turn alerts on?
extern bool            alertsOnCurrent   = false;              // Alerts on still open bar?
extern bool            alertsMessage     = true;               // Alerts should show popup message?
extern bool            alertsSound       = false;              // Alerts should play a sound?
extern bool            alertsEmail       = false;              // Alerts should send email?
extern bool            alertsPushNotif   = false;              // Alerts should send notification?
extern color           colorBarDn        = clrRed;             // Color for down bar
extern color           colorBarUp        = clrLimeGreen;       // Color for up bar
extern color           colorWick         = clrSnow;            // Color for wick
extern int             widthWick         = 1;                  // Wick width
extern int             widthBody         = 3;                  // Body width
extern bool            DrawAsBack        = false;              // Draw as back
extern string          UniqueID          = "Rsi bars 1";       // Unique id
extern bool            BarsOnFirst       = false;              // Show on first bar when using mtf
  
double trend[],open[],close[],high[],low[],count[];
int    window;
string shortName,indicatorFileName;
#define _mtfCall(_buff,_y) iCustom(ForSymbol,TimeFrame,indicatorFileName,PERIOD_CURRENT,"",RsiPeriod,RsiMethod,levelOb,levelOs,alertsOn,alertsOnCurrent,alertsMessage,alertsSound,alertsEmail,alertsPushNotif,colorBarDn,colorBarUp,colorWick,0,0,DrawAsBack,UniqueID,BarsOnFirst,_buff,_y)

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int init()
{
   IndicatorBuffers(6);
   SetIndexBuffer(0,open);  SetIndexStyle(0,DRAW_LINE,EMPTY,EMPTY,clrNONE);
   SetIndexBuffer(1,close); SetIndexStyle(1,DRAW_LINE,EMPTY,EMPTY,clrNONE);
   SetIndexBuffer(2,high);  SetIndexStyle(2,DRAW_LINE,EMPTY,EMPTY,clrNONE);
   SetIndexBuffer(3,low);   SetIndexStyle(3,DRAW_LINE,EMPTY,EMPTY,clrNONE);
   SetIndexBuffer(4,trend);
   SetIndexBuffer(5,count);
   SetLevelValue(0,levelOb);
   SetLevelValue(1,levelOs);
   
         indicatorFileName = WindowExpertName();
         TimeFrame         = MathMax(TimeFrame,_Period);
         ForSymbol = (ForSymbol=="") ? _Symbol : ForSymbol; 
         shortName = UniqueID+": "+timeFrameToString(TimeFrame)+" "+ ForSymbol+" "+getRsiName((int)RsiMethod)+" rsi bar chart";
     
   IndicatorShortName(shortName);
return(0);
}
int deinit()
{
   string lookFor       = UniqueID+":";
   int    lookForLength = StringLen(lookFor);
   for (int i=ObjectsTotal()-1; i>=0; i--)
   {
      string objectName = ObjectName(i); if (StringSubstr(objectName,0,lookForLength) == lookFor) ObjectDelete(objectName);
   }
   return(0);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//

int start()
{
   int counted_bars=IndicatorCounted();
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
           int limit=fmin(Bars-counted_bars,Bars-1); count[0] = limit;
            window = WindowFind(shortName);
            if (TimeFrame != _Period || ForSymbol!=_Symbol)
            {
               limit = (int)MathMax(limit,MathMin(Bars-1,_mtfCall(5,0)*TimeFrame/_Period));
               for(int i=limit; i>=0; i--)
               {
                  int y = iBarShift(ForSymbol,TimeFrame,Time[i]);
                  int x = y;
                  if (BarsOnFirst)
                        {  if (i<Bars-1) x = iBarShift(ForSymbol,TimeFrame,Time[i+1]);               }
                  else  {  if (i>0)      x = iBarShift(ForSymbol,TimeFrame,Time[i-1]); else x = -1;  }
                  if (x!=y)
                  {
                     open[i]  = _mtfCall(0,y);
                     close[i] = _mtfCall(1,y);
                     high[i]  = _mtfCall(2,y);
                     low[i]   = _mtfCall(3,y);
                     drawCandle(i);
                  }
               }
            return(0);
            }

        //
        //
        //
        //
        //
   
        for (int i=limit; i>=0; i--)
        {
           open[i]  = iRsi(RsiMethod,Open[i], RsiPeriod,i,Bars,0);
           close[i] = iRsi(RsiMethod,Close[i],RsiPeriod,i,Bars,1);
           high[i]  = iRsi(RsiMethod,High[i], RsiPeriod,i,Bars,2);
           low[i]   = iRsi(RsiMethod,Low[i],  RsiPeriod,i,Bars,3);;
           trend[i] = (i<Bars-1) ? (close[i]<levelOs) ? 1 : (close[i] > levelOb) ? -1 : trend[i+1] : 0;   
           drawCandle(i);
      }
      if (alertsOn)
      {
         int whichBar = 1; if (alertsOnCurrent) whichBar = 0; 
         if (trend[whichBar] != trend[whichBar+1])
         {
            if (trend[whichBar] == 1) doAlert(" crossing oversold down ");
            if (trend[whichBar] ==-1) doAlert(" crossing overbought up ");       
         }         
      }      
return(0);
}
   
//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//
//

string rsiMethodNames[] = {"RSI","Slow RSI","Rapid RSI","Harris RSI","RSX","Cuttler RSI"};
string getRsiName(int method)
{
   int max = ArraySize(rsiMethodNames)-1;
      method=fmax(fmin(method,max),0); return(rsiMethodNames[method]);
}

//
//
//
//
//

#define rsiInstances 4
double workRsi[][rsiInstances*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 bars, 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/fmax(period,1); 
         if (r<period)
            {
               int k; double sum = 0; for (k=0; k<period && (r-k-1)>=0; k++) sum += fabs(workRsi[r-k][z+_price]-workRsi[r-k-1][z+_price]);
                  workRsi[r][z+_change] = (workRsi[r][z+_price]-workRsi[0][z+_price])/fmax(k,1);
                  workRsi[r][z+_changa] =                                         sum/fmax(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*(fabs(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_wil :
         {         
            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 (r<1)
                  workRsi[r][z+_rsival] = 50;
            else               
               if(up + dn == 0)
                     workRsi[r][z+_rsival] = workRsi[r-1][z+_rsival]+(1/fmax(period,1))*(50            -workRsi[r-1][z+_rsival]);
               else  workRsi[r][z+_rsival] = workRsi[r-1][z+_rsival]+(1/fmax(period,1))*(100*up/(up+dn)-workRsi[r-1][z+_rsival]);
            return(workRsi[r][z+_rsival]);      
         }
      
      //
      //
      //
      //
      //

      case rsi_rap :
         {
            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)
                  return(50);
            else  return(100 * up / (up + dn));      
         }            

      //
      //
      //
      //
      //

      
      case rsi_har :
         {
            double avgUp=0,avgDn=0; double up=0; double 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 = fabs(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(fmax(fmin((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);
}

//
//
//
//
//

string sTfTable[] = {"M1","M5","M15","M30","H1","H4","D1","W1","MN"};
int    iTfTable[] = {1,5,15,30,60,240,1440,10080,43200};

string timeFrameToString(int tf)
{
   for (int i=ArraySize(iTfTable)-1; i>=0; i--) 
         if (tf==iTfTable[i]) return(sTfTable[i]);
                              return("");
}

//
//
//
//
//

void drawCandle(int i)
{
   datetime time = Time[i];
   string   name = UniqueID+":"+(string)time+":";
   
      ObjectCreate(name,OBJ_TREND,window,0,0,0,0);
         ObjectSet(name,OBJPROP_COLOR,colorWick);
         ObjectSet(name, OBJPROP_WIDTH, widthWick);
         ObjectSet(name,OBJPROP_TIME1,time);
         ObjectSet(name,OBJPROP_TIME2,time);
         ObjectSet(name,OBJPROP_PRICE1,MathMax(high[i],MathMin(close[i],open[i])));
         ObjectSet(name,OBJPROP_PRICE2,MathMin(low[i] ,MathMax(close[i],open[i])));
         ObjectSet(name,OBJPROP_RAY ,false);
         ObjectSet(name,OBJPROP_BACK,DrawAsBack);
      
   //
   //
   //
   //
   //
         
   name = name+"body";
      ObjectCreate(name,OBJ_TREND,window,0,0,0,0);
         ObjectSet(name,OBJPROP_TIME1,time);
         ObjectSet(name,OBJPROP_TIME2,time);
         ObjectSet(name,OBJPROP_PRICE1,open[i]);
         ObjectSet(name,OBJPROP_PRICE2,close[i]);
         ObjectSet(name,OBJPROP_WIDTH,widthBody);
         ObjectSet(name,OBJPROP_RAY  ,false);
         ObjectSet(name,OBJPROP_BACK,DrawAsBack);
         if (open[i]<close[i])
               ObjectSet(name,OBJPROP_COLOR,colorBarUp);
         else  ObjectSet(name,OBJPROP_COLOR,colorBarDn);
}

//------------------------------------------------------------------
//                                                                  
//------------------------------------------------------------------
//
//
//
//
//

void doAlert(string doWhat)
{
   static string   previousAlert="nothing";
   static datetime previousTime;
   string message;
   
      if (previousAlert != doWhat || previousTime != Time[0]) {
          previousAlert  = doWhat;
          previousTime   = Time[0];

          //
          //
          //
          //
          //

          message = timeFrameToString(_Period)+" "+ForSymbol+" at "+TimeToStr(TimeLocal(),TIME_SECONDS)+" Rsi bar chart "+doWhat;
             if (alertsMessage)   Alert(message);
             if (alertsPushNotif) SendNotification(message);
             if (alertsEmail)     SendMail(ForSymbol+" Rsi bar chart ",message);
             if (alertsSound)     PlaySound("alert2.wav");
      }
}

