//------------------------------------------------------------------
#property copyright "mladen"
#property link      "www.forex-station.com"
//------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_color1  DeepSkyBlue
#property indicator_color2  Red
#property indicator_color3  Red
#property indicator_width1  2
#property indicator_width2  2
#property indicator_width3  2
#property strict

//
//
//
//
//

extern string   TimeFrame = "Current time frame"; // Time frame to use
extern int      RsiPeriod = 14;          // RSi period
extern int      period    = 14;          // Calculating period
extern int      Price     = PRICE_CLOSE; // Price
extern double   Divisor   = 3;           // Divisor to use for "ema" calculation
extern double   Filter    = 1;           // Filter 
extern int      Precision = 5;           // Precision to use for comparison
extern bool     Interpolate = false;     // Interpolate multi time frame data?

double rsi[];
double rsida[];
double rsidb[];
double slope[];
double buffer[];

string indicatorFileName;
bool   returnBars;
int    timeFrame;

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int init()
{
   IndicatorDigits(Precision);
   IndicatorBuffers(5);
   SetIndexBuffer(0,rsi);
   SetIndexBuffer(1,rsida);
   SetIndexBuffer(2,rsidb);
   SetIndexBuffer(3,slope);
   SetIndexBuffer(4,buffer);
   
         indicatorFileName = WindowExpertName();
         returnBars        = TimeFrame=="returnBars";     if (returnBars)     { return(0); }
         timeFrame         = stringToTimeFrame(TimeFrame);
  
  IndicatorShortName(timeFrameToString(timeFrame)+" Rsi of ema variation ("+(string)RsiPeriod+","+(string)period+","+(string)Divisor+")");
  return(0);
}

//
//
//
//
//

double work[][2];
#define _change 0
#define _achang 1
int start()
{
   int i,r,counted_bars=IndicatorCounted();
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
           int limit=MathMin(Bars-counted_bars,Bars-2);
           if (returnBars) { rsi[0] = MathMin(limit+1,Bars-1); return(0); }
           if (timeFrame!=Period())
           {
               limit = (int)MathMax(limit,MathMin(Bars-1,iCustom(NULL,timeFrame,indicatorFileName,"returnBars",0,0)*timeFrame/Period()));
               if (slope[limit]==-1) CleanPoint(limit,rsida,rsidb);
               for (i=limit; i>=0; i--)
               {
                  int y = iBarShift(NULL,timeFrame,Time[i]);
                     rsi[i]   = iCustom(NULL,timeFrame,indicatorFileName,"",RsiPeriod,period,Price,Divisor,Filter,Precision,0,y);
                     slope[i] = iCustom(NULL,timeFrame,indicatorFileName,"",RsiPeriod,period,Price,Divisor,Filter,Precision,3,y);
                     rsida[i] = EMPTY_VALUE;
                     rsidb[i] = EMPTY_VALUE;
                     if (!Interpolate || ((i>0) && y==iBarShift(NULL,timeFrame,Time[(int)MathMax(i-1,0)]))) continue;
                         datetime time = iTime(NULL,timeFrame,y);
                         int n,x;
                           for(n = 1; (i+n)<(Bars-1) && Time[i+n] >= time; n++) continue;	
                           for(x = 1; x < n && (i+x)<Bars; x++)
                              rsi[i+x] = rsi[i] + (rsi[i+n] - rsi[i]) * x/n;
               }                   
               for (i=limit; i>=0; i--) if (slope[i]==-1) PlotPoint(i,rsida,rsidb,rsi);
               return(0);
           }
           if (ArrayRange(work,0)!=Bars) ArrayResize(work,Bars);

   //
   //
   //
   //
   //

   if (slope[limit]==-1) CleanPoint(limit,rsida,rsidb);
   for(i=limit, r=Bars-i-1; i>=0; i--,r++)
   {
      buffer[i]   = iEma(iEma(iMA(NULL,0,1,0,MODE_SMA,Price,i),period/Divisor,Bars,i,0),period/Divisor,Bars,i,1);
      
         //
         //
         //
         //
         //
         
         if (Filter>0)
         {
            work[r][_change] = MathAbs(buffer[i]-buffer[i+1]);
            work[r][_achang] = work[r][_change];
            for (int k=1; k<period && (r-k)>=0; k++) work[r][_achang] += work[r-k][_change];
                                                     work[r][_achang] /= 1.0*period;
    
            double stddev = 0; for (int k=0;  k<period && (r-k)>=0; k++) stddev += MathPow(work[r-k][_change]-work[r-k][_achang],2);
                   stddev = MathSqrt(stddev/period); 
            double filter = Filter * stddev;
            if( MathAbs(buffer[i]-buffer[i+1]) < filter ) buffer[i]=buffer[i+1];
         }
   }         
   for(    i=limit; i>=0; i--)
   {
      rsi[i]   = iRSIOnArray(buffer,0,RsiPeriod,i);
      rsida[i] = EMPTY_VALUE;
      rsidb[i] = EMPTY_VALUE;
      slope[i] = slope[i+1];
         if (NormalizeDouble(rsi[i],Precision)>NormalizeDouble(rsi[i+1],Precision)) slope[i] =  1;
         if (NormalizeDouble(rsi[i],Precision)<NormalizeDouble(rsi[i+1],Precision)) slope[i] = -1;
         if (slope[i]==-1) PlotPoint(i,rsida,rsidb,rsi);
   }      
   return(0);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

double workEma[][2];
double iEma(double price, double eperiod, int bars, int r, int instanceNo=0)
{
   if (ArrayRange(workEma,0)!= bars) ArrayResize(workEma,bars); r = bars-r-1;

   //
   //
   //
   //
   //
      
   double alpha = 2.0 / (1.0+eperiod);
          workEma[r][instanceNo] = workEma[r-1][instanceNo]+alpha*(price-workEma[r-1][instanceNo]);
   return(workEma[r][instanceNo]);
}

//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//

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-3)) 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; }
}

//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//

string sTfTable[] = {"M1","M5","M15","M30","H1","H4","D1","W1","MN"};
int    iTfTable[] = {1,5,15,30,60,240,1440,10080,43200};

//
//
//
//
//

int stringToTimeFrame(string tfs)
{
   tfs = stringUpperCase(tfs);
   for (int i=ArraySize(iTfTable)-1; i>=0; i--)
         if (tfs==sTfTable[i] || tfs==""+(string)iTfTable[i]) return(MathMax(iTfTable[i],Period()));
                                                              return(Period());
}
string timeFrameToString(int tf)
{
   for (int i=ArraySize(iTfTable)-1; i>=0; i--) 
         if (tf==iTfTable[i]) return(sTfTable[i]);
                              return("");
}

//
//
//
//
//

string stringUpperCase(string str) { string s = str; StringToUpper(s); return(s); }