//+------------------------------------------------------------------+
//|                                          optimal tracking filter |
//|                                                           mladen |
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link      "mladenfx@gmail.com"

#property indicator_chart_window
#property indicator_buffers 7
#property indicator_label1  "up level"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrLimeGreen
#property indicator_style1  STYLE_DOT
#property indicator_label2  "down level"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrOrange
#property indicator_style2  STYLE_DOT
#property indicator_label3  "OTF neutral"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrSilver
#property indicator_width3  1
#property indicator_label4  "OTF up"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrLimeGreen
#property indicator_width4  3
#property indicator_label5  "OTF up"
#property indicator_type5   DRAW_LINE
#property indicator_color5  clrLimeGreen
#property indicator_width5  3
#property indicator_label6  "OTF down"
#property indicator_type6   DRAW_LINE
#property indicator_color6  clrOrange
#property indicator_width6  3
#property indicator_label7  "OTF down"
#property indicator_type7   DRAW_LINE
#property indicator_color7  clrOrange
#property indicator_width7  3
#property strict

//
//
//

input int                 Smooth      = 5;                 // Price smoothing period
input ENUM_MA_METHOD      SmoothMode  = MODE_EMA;          // Price smoothing mode 
input ENUM_APPLIED_PRICE  Price       = PRICE_MEDIAN;      // Price
input int                 inpSigPer   = 9;                 // Signal period

double levu[],levd[],otf[],valua[],valub[],valda[],valdb[],valc[];
struct sGlobalStruct
{
   double alpha;
};
sGlobalStruct global;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+

int OnInit()
{
   IndicatorBuffers(8);
   SetIndexBuffer(0,levu ,INDICATOR_DATA);
   SetIndexBuffer(1,levd ,INDICATOR_DATA);
   SetIndexBuffer(2,otf  ,INDICATOR_DATA);
   SetIndexBuffer(3,valua,INDICATOR_DATA); 
   SetIndexBuffer(4,valub,INDICATOR_DATA); 
   SetIndexBuffer(5,valda,INDICATOR_DATA); 
   SetIndexBuffer(6,valdb,INDICATOR_DATA); 
   SetIndexBuffer(7,valc, INDICATOR_DATA);
   
   global.alpha=2.0/(1.0+inpSigPer);
 
   IndicatorSetString(INDICATOR_SHORTNAME," Optimal tracking filter levels");
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 sOtfStruct
   {
      double prc;
      double tmp1;
      double tmp2;
   };
   static sOtfStruct wrk[];
   static int        wrkSize = -1;
                 if (wrkSize<rates_total) wrkSize = ArrayResize(wrk,rates_total+500);
   
   //
   //
   //
   
   if (valc[limit]==-1) iCleanPoint(limit,rates_total,valda,valdb); 
   if (valc[limit]== 1) iCleanPoint(limit,rates_total,valua,valub); 
   for(i=limit, r=rates_total-i-1; i>=0; i--,r++)
   {
      wrk[r].prc = iMA(NULL,0,Smooth,0,SmoothMode,Price,i);
      if (r>0)
      {
         wrk[r].tmp1 = 0.2*(wrk[r].prc-wrk[r-1].prc)+0.8*wrk[r-1].tmp1;
         wrk[r].tmp2 = 0.1*(high[i]-low[i])         +0.8*wrk[r-1].tmp2;
         double lambda = (wrk[r].tmp2!=0) ? fabs(wrk[r].tmp1/wrk[r].tmp2) : 0;
         double alphas = (-lambda*lambda + sqrt(lambda*lambda*lambda*lambda + 16.0*lambda*lambda))/8.0;
            otf[i] = otf[i+1]+alphas*(wrk[r].prc-otf[i+1]);
       }
       levu[i] = (r>0) ? (otf[i]>levd[i+1]) ? levu[i+1]+global.alpha*(otf[i]-levu[i+1]) : levu[i+1] : otf[i];
       levd[i] = (r>0) ? (otf[i]<levu[i+1]) ? levd[i+1]+global.alpha*(otf[i]-levd[i+1]) : levd[i+1] : otf[i];
       valc[i] = (otf[i]>levu[i]) ? 1 : (otf[i]<levd[i]) ? -1 : (r>0) ? (otf[i]==otf[i+1]) ? valc[i+1]: 0 : 0;
       valda[i] = valdb[i] = EMPTY_VALUE; if (valc[i] ==-1) iPlotPoint(i,rates_total,valda,valdb,otf);
       valua[i] = valub[i] = EMPTY_VALUE; if (valc[i] == 1) iPlotPoint(i,rates_total,valua,valub,otf);
   }
return(rates_total);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------

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; }
}

