//------------------------------------------------------------------
#property copyright "mladen"
#property link      "www.forex-station.cm"
//------------------------------------------------------------------
#property     indicator_separate_window
#property     indicator_buffers                                    8
#property     indicator_minimum                                    0
#property     indicator_maximum                                    5
#property     strict

#define       up                                                   0
#define       dn                                                   1

enum          selection                                         {
	           curr			                                     =  0,	            // Current Bar
	           prev	                                           =  1	               // Closed Bar
                                                                };
enum          enTimeFrames                                      {
              tf_cu                                             =  PERIOD_CURRENT,  // Current time frame
              tf_m1                                             =  PERIOD_M1,       // 1 minute
              tf_m5                                             =  PERIOD_M5,       // 5 minutes
              tf_m15                                            =  PERIOD_M15,      // 15 minutes
              tf_m30                                            =  PERIOD_M30,      // 30 minutes
              tf_h1                                             =  PERIOD_H1,       // 1 hour
              tf_h4                                             =  PERIOD_H4,       // 4 hours
              tf_d1                                             =  PERIOD_D1,       // Daily
              tf_w1                                             =  PERIOD_W1,       // Weekly
              tf_mn1                                            =  PERIOD_MN1,      // Monthly
              tf_n1                                             = -1,               // First higher time frame
              tf_n2                                             = -2,               // Second higher time frame
              tf_n3                                             = -3                // Third higher time frame
                                                                };
extern string ————————————————————————1———————————————————————— = "————————|  Settings for Time Frames";
input         enTimeFrames        TimeFrame1                    =  tf_cu;           // First time frame:
input         enTimeFrames        TimeFrame2                    =  tf_n1;           // Second time frame:
input         enTimeFrames        TimeFrame3                    =  tf_n2;           // Third time frame:
input         enTimeFrames        TimeFrame4                    =  tf_n3;           // Fourth time frame:

extern string ————————————————————————2———————————————————————— = "————————|  Settings for Parameters";
input         int                 SlowLength                    =  7;               // Slow length:
input         double              SlowPipDisplace               =  0;               // Slow pip displace:
input         int                 FastLength                    =  3;               // Fast length:
input         double              FastPipDisplace               =  0;               // Fast pip displace:

extern string ————————————————————————3———————————————————————— = "————————|  Settings for the number of history bars :";
extern        int                 CountBars                     =  1000;            // Number of history bars:

extern string ————————————————————————4———————————————————————— = "————————|  Settings for Alerts";
input         selection           NumSigBar                     =  curr;            // Selecting the signal bar:
input         int                 AlertsLevel                   =  3;               // Alerts level:
input         bool	             ShowAlert                     =  true;            // Use signals "Alert" ?
input         bool                SendPush                      =  false;           // PUSH send messages to your phone ?
input         bool                SendMailInfo                  =  false;           // Send notifications by e-mail ?
input         bool                ShowSound                     =  false;           // Use beeps "Sound" ?
input         string              SoundNameBuy                  =  "buy.wav";       // Title signal buy:
input         string              SoundNameSell                 =  "sell.wav";      // Title signal sell:

extern string ————————————————————————5———————————————————————— = "————————|  Settings for Labels";
input         color               LabelsColor                   =  clrDarkGray;     // Labels color:
input         int                 LabelsHorizontalShift         =  5;               // Labels horizontal shift:
input         double              LabelsVerticalShift           =  1.5;             // Labels vertical shift:
input         string ————————————————————————————————           =  "";
input         color               ColorUp                       =  clrDodgerBlue;   // Color for up:
input         color               ColorDown                     =  clrSandyBrown;   // Color for down:
string        IndID                                             =  "© MultiTF";

double        bb1u[],
              bb1d[],
              bb2u[],
              bb2d[],
              bb3u[],
              bb3d[],
              bb4u[],
              bb4d[],
              count[];

int           trend[][2];
int           timeFrames[4];
bool          returnBars;
bool          calculateValue;
string        indicatorFileName;

datetime      curbar;
datetime      lastbar;

bool          upAlerts=false;
bool          dnAlerts=false;
bool          upSignal=false;
bool          dnSignal=false;

//——————————————————————————|

int OnInit()
{
   SetIndexBuffer(0,bb1u);
   SetIndexBuffer(1,bb1d);
   SetIndexBuffer(2,bb2u);
   SetIndexBuffer(3,bb2d);
   SetIndexBuffer(4,bb3u);
   SetIndexBuffer(5,bb3d);
   SetIndexBuffer(6,bb4u);
   SetIndexBuffer(7,bb4d);

   indicatorFileName = WindowExpertName();
   returnBars        =(TimeFrame1==-99); if(returnBars)     return(0);
   calculateValue    =(TimeFrame1==-98); if(calculateValue) return(0);
      
   for(int i=0; i<8; i++) 
   {
      SetIndexArrow(i,167);
      SetIndexLabel(i,NULL);
   }

   timeFrames[0]= timeFrameValue(TimeFrame1);
   timeFrames[1]= timeFrameValue(TimeFrame2);
   timeFrames[2]= timeFrameValue(TimeFrame3);
   timeFrames[3]= timeFrameValue(TimeFrame4);

   IndicatorShortName("");

   return(INIT_SUCCEEDED);
}

//————————————————————————————|

void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0,"label");
}

//—————————————————————————————————————————————|

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[])
{
//———————————————————————————————————————————|

   for(int i=0; i<8; i++) 
   {
      SetIndexStyle(i,DRAW_ARROW,EMPTY,width(),
                    i%2==0?ColorUp:ColorDown);
   }

//————————————————————————————————————————————————————|

   int                           limit = 0;
   if(CountBars > rates_total-1) limit = rates_total-1;
   else                          limit = CountBars;

//————————————————————————————————————————————————————|

   if(returnBars){bb1u[0]= limit+1; return(0);}
   if(calculateValue){calculateByBB(limit); return(0);}
   if(ArrayRange(trend,0)!= Bars) ArrayResize(trend,Bars);

//———————————————————————————————————————————————————————|
         
   bool initialized = false;
   if(! initialized)
   {
      initialized = true;
      int window = ChartWindowFind();

      for(int t=0; t<4; t++)
      {
         string label = timeFrameToString(timeFrames[t]);
         ObjectCreate("label"+(string)t,OBJ_TEXT,window,0,0);
         ObjectSet("label"+(string)t,OBJPROP_COLOR,LabelsColor);
         ObjectSet("label"+(string)t,OBJPROP_PRICE1,t+LabelsVerticalShift);
         ObjectSetText("label"+(string)t,label,8,"Arial");
      }               
   }
   for(int t=0; t<4; t++) ObjectSet("label"+(string)t,OBJPROP_TIME1,Time[0]+Period()*LabelsHorizontalShift*60);

//————————————————————————————————————————————————————————————————————————————————————————————————————————————|

   for(int i = limit; i >= 0; i --)
   {
      if(i >= rates_total-2) continue;

      trend[i][up]= 0;
      trend[i][dn]= 0;

      for(int k=0; k<4; k++)
      {
         int y = iBarShift(NULL,timeFrames[k],Time[i]);

         double bb1 = iCustom(NULL,timeFrames[k],indicatorFileName,"",-98,0,0,0,"",SlowLength,SlowPipDisplace,FastLength,FastPipDisplace,2,y);
         double bb2 = iCustom(NULL,timeFrames[k],indicatorFileName,"",-98,0,0,0,"",SlowLength,SlowPipDisplace,FastLength,FastPipDisplace,3,y);

         bool   isUp =(bb1<bb2);
          
         switch(k)
         {
            case 0 :if(isUp){bb1u[i]= k+1; bb1d[i]= EMPTY_VALUE;} else{bb1d[i]= k+1; bb1u[i]= EMPTY_VALUE;} break;
            case 1 :if(isUp){bb2u[i]= k+1; bb2d[i]= EMPTY_VALUE;} else{bb2d[i]= k+1; bb2u[i]= EMPTY_VALUE;} break;
            case 2 :if(isUp){bb3u[i]= k+1; bb3d[i]= EMPTY_VALUE;} else{bb3d[i]= k+1; bb3u[i]= EMPTY_VALUE;} break;
            case 3 :if(isUp){bb4u[i]= k+1; bb4d[i]= EMPTY_VALUE;} else{bb4d[i]= k+1; bb4u[i]= EMPTY_VALUE;} break;
         }

         if(isUp) trend[i][up]+= 1;
         else     trend[i][dn]+= 1;
      }
   }

//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————|

   if(trend[0][up]>= AlertsLevel && trend[NumSigBar][up]>= AlertsLevel && trend[NumSigBar+1][up]< AlertsLevel && upSignal==false) upSignal=true;
   if(new_bar(NULL,PERIOD_CURRENT)== true && upSignal==true)                                                                      upSignal=false;

   if(trend[0][dn]>= AlertsLevel && trend[NumSigBar][dn]>= AlertsLevel && trend[NumSigBar+1][dn]< AlertsLevel && dnSignal==false) dnSignal=true;
   if(new_bar(NULL,PERIOD_CURRENT)== true && dnSignal==true)                                                                      dnSignal=false;

//————————————————————————————————————————————————|

   if(SendMailInfo||ShowAlert||ShowSound||SendPush)
   {
      if(upSignal && upAlerts)
      {
         upAlerts=false;

         if(SendPush)     SendNotification(StringConcatenate(IndID, " / ", Symbol(), " « ", timeFrameToString(_Period), " » ( BUY = ", close[0], "  •  ", trend[NumSigBar][up], " TF of PTL aligned UP )"));
         if(SendMailInfo) SendMail(StringConcatenate(IndID, " / ", AccountCompany(), " ", AccountNumber(), " ", Symbol()),
                          StringConcatenate(" « ", timeFrameToString(_Period), " » ( BUY = ", close[0], "  •  ", trend[NumSigBar][up], " TF of PTL aligned UP ) ", TimeToStr(TimeCurrent(),TIME_DATE|TIME_MINUTES)));
         if(ShowAlert)    Alert(IndID, " / ", Symbol(), " « ", timeFrameToString(_Period), " » ( BUY = ", close[0], "  •  ", trend[NumSigBar][up], " TF of PTL aligned UP )");
         if(ShowSound)    PlaySound(SoundNameBuy);
      }
      if(!upAlerts &&! upSignal) upAlerts=true;

//————————————————————————————————————————————|

      if(dnSignal && dnAlerts)
      {
         dnAlerts=false;

         if(SendPush)     SendNotification(StringConcatenate(IndID, " / ", Symbol(), " « ", timeFrameToString(_Period), " » ( SELL = ", close[0], "  •  ", trend[NumSigBar][dn], " TF of PTL aligned Down )"));
         if(SendMailInfo) SendMail(StringConcatenate(IndID, " / ", AccountCompany(), " ", AccountNumber(), " ", Symbol()),
                          StringConcatenate(" « ", timeFrameToString(_Period), " » ( SELL = ", close[0], "  •  ", trend[NumSigBar][dn], " TF of PTL aligned Down ) ", TimeToStr(TimeCurrent(),TIME_DATE|TIME_MINUTES)));
         if(ShowAlert)    Alert(IndID, " / ", Symbol(), " « ", timeFrameToString(_Period), " » ( SELL = ", close[0], "  •  ", trend[NumSigBar][dn], " TF of PTL aligned Down )");
         if(ShowSound)    PlaySound(SoundNameSell);
      }
      if(!dnAlerts &&! dnSignal) dnAlerts=true;
   }

   return(rates_total);
}

//—————————————————————————————————————————————|

int width()
{
   switch((int)ChartGetInteger(0,CHART_SCALE,0))
   {
      case 0 :return(0);
      case 1 :return(0);
      case 2 :return(0);
      case 3 :return(2);
      case 4 :return(5);
      case 5 :return(15);
   default   :return(0);
   }
}

//———————————————————————————————————————————————————————————————————————————————————————|

bool new_bar(string symbol,ENUM_TIMEFRAMES periods)
{
   curbar=(datetime)SeriesInfoInteger(symbol,periods,SERIES_LASTBAR_DATE);

   if(lastbar==0) lastbar=(datetime)SeriesInfoInteger(symbol,periods,SERIES_LASTBAR_DATE);
   if(lastbar!=curbar)
   {
      lastbar=curbar;
      return(true);
   }
   return(false);
}

//————————————————————————————————————————————————————————————————————————————————————————————————|

void calculateByBB(int limit)
{
   for(int i=limit; i>=0; i--) 
   {
      double pipMultiplier =_Point*MathPow(10,Digits%2);
      double thigh1 = High[iHighest(NULL,0,MODE_HIGH,SlowLength,i)]+ SlowPipDisplace*pipMultiplier;
      double tlow1  = Low [iLowest (NULL,0,MODE_LOW, SlowLength,i)]- SlowPipDisplace*pipMultiplier;
      double thigh2 = High[iHighest(NULL,0,MODE_HIGH,FastLength,i)]+ FastPipDisplace*pipMultiplier;
      double tlow2  = Low [iLowest (NULL,0,MODE_LOW, FastLength,i)]- FastPipDisplace*pipMultiplier;

      if(i<Bars-1 && Close[i]>bb1u[i+1])
            bb1u[i]= tlow1;
      else  bb1u[i]= thigh1;             

      if(i<Bars-1 && Close[i]>bb1d[i+1])
            bb1d[i]= tlow2;
      else  bb1d[i]= thigh2;             
            
//——————————————————————————————————————————————————————|
  
      bb2d[i]= EMPTY_VALUE;
      bb2u[i]= EMPTY_VALUE;
      bb3u[i]= i<Bars-1 ? bb3u[i+1]: 0;
      bb4u[i]= 0;

      if(Close[i]<bb1u[i]&& Close[i]<bb1d[i]) bb4u[i]= 1;
      if(Close[i]>bb1u[i]&& Close[i]>bb1d[i]) bb4u[i]=-1;
      if(bb1u[i]> bb1d[i]|| bb4u[i]== 1)      bb3u[i]= 1;
      if(bb1u[i]< bb1d[i]|| bb4u[i]==-1)      bb3u[i]=-1;
      if(bb3u[i]== 1)                         bb2d[i]= 1;
      if(bb3u[i]==-1)                         bb2u[i]= 1;
   }
}

//————————————————————————————————————————————————————————————————|

string sTfTable[]={"M1","M5","M15","M30","H1","H4","D1","W1","MN"};
int    iTfTable[]={1,5,15,30,60,240,1440,10080,43200};

//———————————————————————————————————————————————————|

int timeFrameValue(int tf)
{
   int  add  =( tf>=0)? 0 : MathAbs(tf);
   if(  add  != 0)tf =_Period;
   int  size =  ArraySize(iTfTable); 
   int  i=0;
   for(;i<size; i++) if(iTfTable[i]==tf) break;
                     if(i==size) return(_Period);
   return(iTfTable[(int)MathMin(i+add,size-1)]);
}

//——————————————————————————————————————————————|

string timeFrameToString(int tf)
{
   for(int i=ArraySize(iTfTable)-1; i>=0; i--)
   if(tf==iTfTable[i]) return(sTfTable[i]);
   return("");
}

//—————————| end of the algorithm |——————————|