//------------------------------------------------------------------
#property copyright "www.forex-station.com "
#property link      "www.forex-station.com/"
//------------------------------------------------------------------

#property indicator_chart_window
#property indicator_buffers 4
#property indicator_color1  clrNONE
#property indicator_color2  clrNONE
#property indicator_color3  clrNONE
#property indicator_color4  clrNONE

extern ENUM_TIMEFRAMES    TimeFrame           = PERIOD_H1;
extern int                KPeriod             = 14;
extern int                DPeriod             = 3;
extern int                Slowing             = 3;
extern ENUM_STO_PRICE     Price               = STO_LOWHIGH;
extern ENUM_MA_METHOD     MaMethod            = MODE_EMA; 
extern int                ZeroLagPeriod       = 21;
input bool                arrowsVisible       = true;                 // Arrows visible true/false?
input bool                arrowsOnNewest      = true;                 // Arrows drawn on newest bar of higher time frame bar true/false?
input string              UniqueArrowsID      = "ZLarrowsH1";         // Unique ID for arrows
input double              arrowsUpperGap      = 0.5;                  // Upper arrow gap
input double              arrowsLowerGap      = 0.5;                  // Lower arrow gap
input color               arrowsUpColor       = clrDarkGreen;         // Up arrow color
input color               arrowsDnColor       = clrCrimson;           // Down arrow color
input int                 arrowsUpCode        = 221;                  // Up arrow code
input int                 arrowsDnCode        = 222;                  // Down arrow code
input int                 arrowsUpSize        = 5;                    // Up arrow size
input int                 arrowsDnSize        = 5;                    // Down arrow size
extern bool               ColorOnSlope        = true;
extern bool               Interpolate         = true;
extern int                MaxCandlesToDisplay = 100;

double sto[],stoDa[],stoDb[],sig[], state[];
string indicatorFileName;
bool   returnBars;
//------------------------------------------------------------------
int OnInit()
{
   IndicatorBuffers(5);
   SetIndexBuffer(0, sto);   SetIndexStyle(0,DRAW_NONE);
   SetIndexBuffer(1, stoDa); SetIndexStyle(1,DRAW_NONE);
   SetIndexBuffer(2, stoDb); SetIndexStyle(2,DRAW_NONE);
   SetIndexBuffer(3, sig);   SetIndexStyle(3,DRAW_NONE);
   SetIndexBuffer(4, state);
       
   indicatorFileName = WindowExpertName();
   returnBars        = TimeFrame==-99;
   TimeFrame         = MathMax(TimeFrame,_Period);

   IndicatorShortName(timeFrameToString(TimeFrame)+ " ZeroLag Stochastic ("+KPeriod+","+DPeriod+","+Slowing+", ZL "+DoubleToStr(ZeroLagPeriod,1)+ ")");
return(0);
}
//------------------------------------------------------------------
void OnDeinit(const int reason)
{ 
    string lookFor       = UniqueArrowsID+":";
    int    lookForLength = StringLen(lookFor);
    for (int i=ObjectsTotal()-1; i>=0; i--)
    {
       string objectName = ObjectName(i);
       if (StringSubstr(objectName,0,lookForLength) == lookFor) ObjectDelete(objectName);
    }
}
//------------------------------------------------------------------
int start()
{
   int i,counted_bars=IndicatorCounted();
      if (counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
//         int limit = MathMin(Bars-counted_bars,Bars-1);
         int limit=MaxCandlesToDisplay-counted_bars;
         if (returnBars) { sto[0] = limit+1; return(0); }

         //
         //
         //
         //
         //
         if (TimeFrame == Period())
         {
           if (state[limit]==-1) CleanPoint(limit,stoDa,stoDb);
           for (i=limit; i>=0 ; i--)
           {
              sto[i] = iZeroLag(iStochastic(NULL,0,KPeriod,DPeriod,Slowing,MaMethod,Price,MODE_MAIN,i),  ZeroLagPeriod,i,0);
              sig[i] = iZeroLag(iStochastic(NULL,0,KPeriod,DPeriod,Slowing,MaMethod,Price,MODE_SIGNAL,i),ZeroLagPeriod,i,1);
              stoDa[i] = stoDb[i] = EMPTY_VALUE;
              state[i] = state[i+1];
               if (ColorOnSlope)
               {
                  if (sto[i]>sto[i+1]) state[i] =  1;
                  if (sto[i]<sto[i+1]) state[i] = -1;
               }
               else
               {
                  if (sto[i]>sig[i]) state[i] =  1;
                  if (sto[i]<sig[i]) state[i] = -1;
               }
               if (state[i]==-1) PlotPoint(i,stoDa,stoDb,sto);
               
               //
               //
               //
               
               if (arrowsVisible)
               {
                 string lookFor = UniqueArrowsID+":"+(string)Time[i]; ObjectDelete(lookFor);            
                 if (state[i] != state[i+1])
                 {
                   if (state[i] == 1) drawArrow(i,arrowsUpColor,arrowsUpCode,arrowsUpSize,false);
                   if (state[i] ==-1) drawArrow(i,arrowsDnColor,arrowsDnCode,arrowsDnSize, true);
                 }
               }          
         }   
   return(0);
   }
   
   //
   //
   //
   //
   //       
   
   limit = MathMax(limit,MathMin(Bars-1,iCustom(NULL,TimeFrame,indicatorFileName,-99,0,0)*TimeFrame/Period()));
   if (state[limit]==-1) CleanPoint(limit,stoDa,stoDb);
   for (i=limit;i>=0; i--)
   {
         int y = iBarShift(NULL,TimeFrame,Time[i]);
         sto[i]   = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,KPeriod,DPeriod,Slowing,Price,MaMethod,ZeroLagPeriod,arrowsVisible,arrowsOnNewest,UniqueArrowsID,arrowsUpperGap,arrowsLowerGap,arrowsUpColor,arrowsDnColor,arrowsUpCode,arrowsDnCode,arrowsUpSize,arrowsDnSize,ColorOnSlope,0,y);
         stoDa[i] = stoDb[i] = EMPTY_VALUE; 
         sig[i]   = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,KPeriod,DPeriod,Slowing,Price,MaMethod,ZeroLagPeriod,arrowsVisible,arrowsOnNewest,UniqueArrowsID,arrowsUpperGap,arrowsLowerGap,arrowsUpColor,arrowsDnColor,arrowsUpCode,arrowsDnCode,arrowsUpSize,arrowsDnSize,ColorOnSlope,3,y);
         state[i] = iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,KPeriod,DPeriod,Slowing,Price,MaMethod,ZeroLagPeriod,arrowsVisible,arrowsOnNewest,UniqueArrowsID,arrowsUpperGap,arrowsLowerGap,arrowsUpColor,arrowsDnColor,arrowsUpCode,arrowsDnCode,arrowsUpSize,arrowsDnSize,ColorOnSlope,4,y);
         if (ColorOnSlope)
         {
            if (sto[i]>sto[i+1]) state[i] =  1;
            if (sto[i]<sto[i+1]) state[i] = -1;
         }
         else
         {
            if (sto[i]>sig[i]) state[i] =  1;
            if (sto[i]<sig[i]) state[i] = -1;
         }
     
         if (!Interpolate || y==iBarShift(NULL,TimeFrame,Time[i-1])) continue;

         //
         //
         //
         //
         //

         datetime time = iTime(NULL,TimeFrame,y);
            for(int n = 1; i+n < Bars && Time[i+n] >= time; n++) continue;
            for(int x = 1; x < n; x++)
            { 
               sto[i+x] = sto[i] + (sto[i+n] - sto[i]) * x/n;
               sig[i+x] = sig[i] + (sig[i+n] - sig[i]) * x/n;
            }
    }
    for (i=limit;i>=0;i--) if (state[i]==-1) PlotPoint(i,stoDa,stoDb,sto);
return(0);
}

//-------------------------------------------------------------------
double workZl[][4];
#define _price 0
#define _zlema 1

double iZeroLag(double price, double length, int r, int instanceNo=0)
{
   if (ArrayRange(workZl,0)!=Bars) ArrayResize(workZl,Bars); r = Bars-r-1; instanceNo *= 2; workZl[r][_price+instanceNo] = price;

   double median = 0;
   double alpha  = 2.0/(1.0+length); 
   int    per    = (length-1.0)/2.0;
   if (r<per)
          workZl[r][_zlema+instanceNo] = price;
   else   
      {
         if ((int)length%2==0)
               median = (workZl[r-per][_price+instanceNo]+workZl[r-per-1][_price+instanceNo])/2.0;
         else  median =  workZl[r-per][_price+instanceNo];
         workZl[r][_zlema+instanceNo] = workZl[r-1][_zlema+instanceNo]+alpha*(2.0*price-median-workZl[r-1][_zlema+instanceNo]);
      }            
   return(workZl[r][_zlema+instanceNo]);
}
//------------------------------------------------------------------
void CleanPoint(int i,double& first[],double& second[])
{
   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 (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};

string timeFrameToString(int tf)
{
   for (int i=ArraySize(iTfTable)-1; i>=0; i--) 
         if (tf==iTfTable[i]) return(sTfTable[i]);
                              return("");
}
//-------------------------------------------------------------------
void drawArrow(int i,color theColor,int theCode, int theSize, bool up)
{
   string name = UniqueArrowsID+":"+(string)Time[i];
   double gap  = iATR(NULL,0,20,i);   
   
      //
      //
      //

      datetime atime = Time[i]; if (arrowsOnNewest) atime += _Period*60-1;      
      ObjectCreate(name,OBJ_ARROW,0,atime,0);
         ObjectSet(name,OBJPROP_ARROWCODE,theCode);
         ObjectSet(name,OBJPROP_COLOR,theColor);
         ObjectSet(name,OBJPROP_WIDTH,theSize);
         if (up)
               ObjectSet(name,OBJPROP_PRICE1,High[i] + arrowsUpperGap * gap);
         else  ObjectSet(name,OBJPROP_PRICE1,Low[i]  - arrowsLowerGap * gap);
}
//-------------------------------------------------------------------
