//+------------------------------------------------------------------+
//|                                     Fractals - adjustable period |
//+------------------------------------------------------------------+
#property link      "www.forex-tsd.com"
#property copyright "www.forex-tsd.com"

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1  clrRed
#property indicator_color2  clrRed
#property indicator_width1  5
#property indicator_width2  5

//
//
//
//
//

extern ENUM_TIMEFRAMES    TimeFrame  = PERIOD_CURRENT;    // Time frame to use
extern int    FractalPeriod          = 13;
extern double UpperArrowDisplacement = 0.2;
extern double LowerArrowDisplacement = 0.2;
extern bool   alertsOn               = true;
extern bool   alertsMessage          = true;
extern bool   alertsSound            = true;
extern bool   alertsEmail            = false;
extern bool   ShowPriceLabels        = false;
extern string UniqueID               = "fractalPrice1";
extern color  UpperPriceColor        = clrNONE;
extern color  LowerPriceColor        = clrNONE;
extern int    PriceSize              = 1;

double UpperBuffer[],LowerBuffer[],count[];
string indicatorFileName;
#define _mtfCall(_buff,_ind) iCustom(NULL,TimeFrame,indicatorFileName,PERIOD_CURRENT,FractalPeriod,UpperArrowDisplacement,LowerArrowDisplacement,alertsOn,alertsMessage,alertsSound,alertsEmail,ShowPriceLabels,UniqueID,UpperPriceColor,LowerPriceColor,PriceSize,_buff,_ind)

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int init()
{
   if (MathMod(FractalPeriod,2)==0) FractalPeriod = FractalPeriod+1;
   IndicatorBuffers(3);
   SetIndexBuffer(0,UpperBuffer); SetIndexStyle(0,DRAW_ARROW); SetIndexArrow(0,234);
   SetIndexBuffer(1,LowerBuffer); SetIndexStyle(1,DRAW_ARROW); SetIndexArrow(1,233);
   SetIndexBuffer(2,count);
   
   indicatorFileName = WindowExpertName();
   TimeFrame         = fmax(TimeFrame,_Period); 
return(0);
}
int deinit()
{
   string lookFor       = UniqueID+":";
   int    lookForLength = StringLen(lookFor);
   for (int i = ObjectsTotal(); i>= 0; i--)
   {
      string name = ObjectName(i); if (StringSubstr(name,0,lookForLength)==lookFor) ObjectDelete(name);
   }
   string lookFor2       = UniqueID+":";
   int    lookForLength2 = StringLen(lookFor2);
   for (i = ObjectsTotal(); i>= 0; i--)
   {
      string name2 = ObjectName(i); if (StringSubstr(name2,0,lookForLength2)==lookFor2) ObjectDelete(name2);
   }
   return (0);
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int start()
{
   int half = FractalPeriod/2;
   int i,limit,counted_bars=IndicatorCounted();
      if(counted_bars<0) return(-1);
      if(counted_bars>0) counted_bars--;
         limit=MathMin(MathMax(Bars-counted_bars,FractalPeriod),Bars-1); count[0]=limit;
            if (TimeFrame!=_Period)
            {
               limit = (int)fmax(limit,fmin(Bars-1,_mtfCall(2,0)*TimeFrame/_Period));
               for (i=limit;i>=0 && !_StopFlag; i--)
               {
                  int y = iBarShift(NULL,TimeFrame,Time[i]);
                  int x = iBarShift(NULL,TimeFrame,Time[i+1]);
                  UpperBuffer[i] = EMPTY_VALUE;
                  LowerBuffer[i] = EMPTY_VALUE;
                  if (x!=y)
                  {
                     UpperBuffer[i] = _mtfCall(0,y);
                     LowerBuffer[i] = _mtfCall(1,y);                                                
                  }
             }   
   return(0);
   }
   
   //
   //
   //
   //
   //

   for(i=limit; i>=0; i--)
   {
         bool   found     = true;
         double compareTo = High[i];
         for (int k=1;k<=half;k++)
            {
               if ((i+k)<Bars && High[i+k]> compareTo) { found=false; break; }
               if ((i-k)>=0   && High[i-k]>=compareTo) { found=false; break; }
            }
         if (found) 
               UpperBuffer[i]=High[i]+iATR(NULL,0,20,i)*UpperArrowDisplacement;
         else  UpperBuffer[i]=EMPTY_VALUE;

      //
      //
      //
      //
      //
      
         found     = true;
         compareTo = Low[i];
         for (k=1;k<=half;k++)
            {
               if ((i+k)<Bars && Low[i+k]< compareTo) { found=false; break; }
               if ((i-k)>=0   && Low[i-k]<=compareTo) { found=false; break; }
            }
         if (found)
              LowerBuffer[i]=Low[i]-iATR(NULL,0,20,i)*LowerArrowDisplacement;
         else LowerBuffer[i]=EMPTY_VALUE;

   }
 
 
   //
   //
   //
   //
   //
   
   static datetime previousLevel = -1; static int previousSignal = 0;
   
   if (alertsOn)
   {
      int currentBar=-1, cb, cb1;
               for (i=0; i<Bars-1; i++) if (LowerBuffer[i]!=EMPTY_VALUE || UpperBuffer[i]!=EMPTY_VALUE) { currentBar = i; break; }
      if (currentBar>-1) checkAlert(currentBar,previousLevel,previousSignal,UpperBuffer,LowerBuffer ,"");
   }
   if (ShowPriceLabels)
   {
         
             //---
   for(cb=limit;cb>=0;cb--)
     {
      if(GreaterDoubles(UpperBuffer[cb],0,Digits))
        {
         string name= UniqueID+":"+"up"+string(Time[cb]);
        
         //--- first find object by name
         if(ObjectFind(name)<0)
           {
            //--- if not found, create it
            if(ObjectCreate(name,OBJ_ARROW,0,Time[cb],UpperBuffer[cb]))
              {
               //--- set object properties
               //--- arrow code
               ObjectSet(name,OBJPROP_ARROWCODE,SYMBOL_LEFTPRICE);
               //--- color
               ObjectSet(name,OBJPROP_COLOR,DodgerBlue);
               //--- price
               ObjectSet(name,OBJPROP_PRICE1,UpperBuffer[cb]);
               //--- time
               ObjectSet(name,OBJPROP_TIME1,Time[cb]);
              }
           }
         else
           {
            //--- if the object exists, just modify its price coordinate
            ObjectSet(name,OBJPROP_PRICE1,UpperBuffer[cb]);
           }
        }
     }
//----
//---
   for(cb1=limit;cb1>=0;cb1--)
     {
      if(GreaterDoubles(LowerBuffer[cb1],0,Digits))
        {
         string name2= UniqueID+":"+"dn"+string(Time[cb1]);
         //--- first find object by name
         if(ObjectFind(name2)<0)
           {
            //--- if not found, create it
            if(ObjectCreate(name2,OBJ_ARROW,0,Time[cb1],LowerBuffer[cb1]))
              {
               //--- set object properties
               //--- arrow code
               ObjectSet(name2,OBJPROP_ARROWCODE,SYMBOL_LEFTPRICE);
               //--- color
               ObjectSet(name2,OBJPROP_COLOR,Magenta);
               //--- price
               ObjectSet(name2,OBJPROP_PRICE1,LowerBuffer[cb1]);
               //--- time
               ObjectSet(name2,OBJPROP_TIME1,Time[cb1]);
              }
           }
         else
           {
            //--- if the object exists, just modify its price coordinate
            ObjectSet(name2,OBJPROP_PRICE1,LowerBuffer[cb1]);
           }
        }
     }
//----             
   }
return(0);
}

//+------------------------------------------------------------------+
//|                                                             
//+------------------------------------------------------------------+
//
//
//
//
//

void checkAlert(int currentBar, datetime& previousLevel, int& previousSignal, double& upBuffer[], double& dnBuffer[], string text)
{
   int previousBar = iBarShift(NULL,0,previousLevel);
   int currentSignal;
         if (upBuffer[currentBar]!=EMPTY_VALUE)                                      currentSignal = -1;
         if (dnBuffer[currentBar]!=EMPTY_VALUE)                                      currentSignal =  1;
         if (dnBuffer[currentBar]!=EMPTY_VALUE && upBuffer[currentBar]!=EMPTY_VALUE) currentSignal =  0;

   //
   //
   //
   //
   //
   
   if (currentBar != previousBar || currentSignal != previousSignal)
   {
      if (currentSignal != previousSignal && currentBar > previousBar && previousLevel != -1)
            string alertText = "reverted to ";
      else         alertText = "current signal ";            
      switch(currentSignal)
      {
         case  0 : doAlert(text+alertText+"up/down"); break;
         case  1 : doAlert(text+alertText+"up");      break;
         case -1 : doAlert(text+alertText+"down");
      }               
 
      //
      //
      //
      //
      //
              
      previousLevel  = Time[currentBar];
      previousSignal = currentSignal;
   }
}

//
//
//
//
//

void doAlert(string doWhat)
{
   string message = timeFrameToString(_Period)+" "+_Symbol+" at "+TimeToStr(TimeLocal(),TIME_SECONDS)+" Fractals "+doWhat;
      if (alertsMessage) Alert(message);
      if (alertsEmail)   SendMail(_Symbol+" Fractals ",message);
      if (alertsSound)   PlaySound("alert2.wav");
}
//+------------------------------------------------------------------+
//| GreaterDoubles                                                   |
//+------------------------------------------------------------------+
bool GreaterDoubles(double number1,double number2,int dig)
  {
   if(NormalizeDouble(number1-number2,dig)>0) return(true);
   else return(false);
  }
//+------------------------------------------------------------------+

//-------------------------------------------------------------------
//                                                                  
//-------------------------------------------------------------------
//
//
//
//
//

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("");
}