//+------------------------------------------------------------------+
//|                                                  LaguerreRSI.mq4 |
//|                                                                  |
//| a variation on the LAguerre RSI theme                            |
//| solves some problems with low lag smoothing                      |
//+------------------------------------------------------------------+

#property copyright "mladen"
#property link      "mladenfx@gmail.com"

#property indicator_separate_window
#property indicator_buffers 5
#property strict

//
//
//
//
//

input double                LaguerreGamma        = 0.6;          // Gamma
input ENUM_APPLIED_PRICE    LaguerrePrice        = PRICE_CLOSE;  // Price 
input int                   RSIDataLevel         = 0;            // Data level
input int                   RSIPeriod            = 8;            // Rsi period
input bool                  Smooth               = true;         // Smooth
input double                inpFilter            = 0;            // Filter to use (<= 0, no filter)
input int                   inpFilPer            = 0;            // Filter period (0 to use rmi period)
enum  enFilterWhat
      {
         flt_prc,                                                  // Filter the price 
         flt_val,                                                  // Filter the value
         flt_all                                                   // Filter all
       };
input enFilterWhat          inpFilOn               = flt_val;      // Apply filter to :
input string                __dis__01              = "";           //.Display settings
input int                   inpLinesWidth          = 3;            // Laguerre lines width
input color                 inpColorUp             = clrLimeGreen; // Up color for lines
input color                 inpColorDn             = clrOrange;    // Down color for lines
input color                 inpColorNu             = clrSilver;    // Neutral color for lines

double val[],valUa[],valUb[],valDa[],valDb[],valc[];
struct sGloStruct
{
   double prc;
   int    fper;
   double pfil;
   double vfil;
   int    data;
};
sGloStruct glo;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+

int OnInit()
{
   IndicatorBuffers(6);
   SetIndexBuffer(0, val,  INDICATOR_DATA); SetIndexStyle(0,DRAW_LINE,EMPTY,inpLinesWidth,inpColorNu);
   SetIndexBuffer(1, valUa,INDICATOR_DATA); SetIndexStyle(1,DRAW_LINE,EMPTY,inpLinesWidth,inpColorUp);
   SetIndexBuffer(2, valUb,INDICATOR_DATA); SetIndexStyle(2,DRAW_LINE,EMPTY,inpLinesWidth,inpColorUp);
   SetIndexBuffer(3, valDa,INDICATOR_DATA); SetIndexStyle(3,DRAW_LINE,EMPTY,inpLinesWidth,inpColorDn);
   SetIndexBuffer(4, valDb,INDICATOR_DATA); SetIndexStyle(4,DRAW_LINE,EMPTY,inpLinesWidth,inpColorDn);
   SetIndexBuffer(5, valc,INDICATOR_CALCULATIONS);
   
   glo.fper = (glo.fper<=0) ?  RSIPeriod : inpFilPer;
   glo.pfil = (inpFilOn==flt_val) ? 0 : inpFilter;
   glo.vfil = (inpFilOn==flt_prc) ? 0 : inpFilter;
   glo.data = MathMax(MathMin(RSIDataLevel,2),0);
   
   IndicatorSetString(INDICATOR_SHORTNAME,"Laguerre RSI variation ("+DoubleToStr(LaguerreGamma, 2)+","+(string)RSIDataLevel+","+(string)RSIPeriod+")");
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason){ }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+

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[])
{
   int i,r,limit=fmin(rates_total-prev_calculated+1,rates_total-1);
         
   //
   //
   //
   
   struct sWorkStruct
   {
     double L0;
     double L1;
     double L2;
     double L3;
     double LR;
   };
   static sWorkStruct wrk[];
   static int        wrkSize = -1;
                 if (wrkSize<rates_total) wrkSize = ArrayResize(wrk,rates_total+500);
   
   //
   //
   //
   
   if (valc[limit]== 1) iCleanPoint(limit,rates_total,valUa,valUb);
   if (valc[limit]==-1) iCleanPoint(limit,rates_total,valDa,valDb);
   for(i=limit, r=rates_total-limit-1; i>=0; i--,r++)
   {
		glo.prc   = iFilter(iMA(NULL,0,1,0,MODE_SMA,LaguerrePrice,i),glo.pfil,glo.fper,i,rates_total,0);
		wrk[r].L0 = (r>0) ? (1.0-LaguerreGamma) * glo.prc + LaguerreGamma*wrk[r-1].L0          : glo.prc;
		wrk[r].L1 = (r>0) ? -LaguerreGamma*wrk[r].L0 + wrk[r-1].L0 + LaguerreGamma*wrk[r-1].L1 : glo.prc;
		wrk[r].L2 = (r>0) ? -LaguerreGamma*wrk[r].L1 + wrk[r-1].L1 + LaguerreGamma*wrk[r-1].L2 : glo.prc;
		wrk[r].L3 = (r>0) ? -LaguerreGamma*wrk[r].L2 + wrk[r-1].L2 + LaguerreGamma*wrk[r-1].L3 : glo.prc;

      //
      //
      //
      		
      double cu = 0;
      double cd = 0;
      for (int k=0; k<RSIPeriod && (r-k)>0; k++)
      {
          double diff=0;
          switch (RSIDataLevel) 
          {
             case 0: diff = wrk[r-k].L0-wrk[r-k].L1; break;
             case 1: diff = wrk[r-k].L1-wrk[r-k].L2; break;
             case 2: diff = wrk[r-k].L2-wrk[r-k].L3; break;
          }                  
          if (diff > 0) cu += diff;
          if (diff < 0) cd -= diff;
       }
       wrk[r].LR = ((cu+cd)!=0) ? 0.5*((cu-cd)/(cu+cd)+1) : 0;
       val[i]  = (r>1 && Smooth) ? iFilter((wrk[r].LR+2.0*wrk[r-1].LR+wrk[r-2].LR)/4.0,glo.vfil,glo.fper,i,rates_total,1) : wrk[r].LR;  
       valc[i] = (r>0) ? (val[i]>val[i+1]) ? 1 : (val[i]<val[i+1]) ? -1 : valc[i+1] : 0; 
       if (valc[i]==-1) iPlotPoint(i,rates_total,valDa,valDb,val); else valDa[i] = valDb[i] = EMPTY_VALUE;
       if (valc[i]== 1) iPlotPoint(i,rates_total,valUa,valUb,val); else valUa[i] = valUb[i] = EMPTY_VALUE;           
	}
return(rates_total);
}

//
//
//

#define filterInstances 2
double workFil[][filterInstances*3];

#define _fchange 0
#define _fachang 1
#define _fprice  2

double iFilter(double tprice, double filter, int period, int i, int bars, int instanceNo=0)
{
   if (filter<=0 || period<=0) return(tprice);
   if (ArrayRange(workFil,0)!= bars) ArrayResize(workFil,bars); i = bars-i-1; instanceNo*=3;
   
   //
   //
   //
   //
   //
   
   workFil[i][instanceNo+_fprice]  = tprice; if (i<1) return(tprice);
   workFil[i][instanceNo+_fchange] = MathAbs(workFil[i][instanceNo+_fprice]-workFil[i-1][instanceNo+_fprice]);
   workFil[i][instanceNo+_fachang] = workFil[i][instanceNo+_fchange];

   for (int k=1; k<period && (i-k)>=0; k++) workFil[i][instanceNo+_fachang] += workFil[i-k][instanceNo+_fchange];
                                            workFil[i][instanceNo+_fachang] /= period;
    
   double stddev = 0;
   for (int k=0;  k<period && (i-k)>=0; k++) stddev += MathPow(workFil[i-k][instanceNo+_fchange]-workFil[i-k][instanceNo+_fachang],2);
                                             stddev  = MathSqrt(stddev/(double)period); 
   double filtev = filter * stddev;
   if( MathAbs(workFil[i][instanceNo+_fprice]-workFil[i-1][instanceNo+_fprice]) < filtev ) workFil[i][instanceNo+_fprice]=workFil[i-1][instanceNo+_fprice];
   return(workFil[i][instanceNo+_fprice]);
}

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------

void iCleanPoint(int i, int bars,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 iPlotPoint(int i, int bars,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; }
}