//+------------------------------------------------------------------+
//|                         Clemmo 3 bar Fractal Alert Indicator.mq4 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Clemmo - EvidenceBasedFX"
#property link      "https://www.forexrobotsclub.com"
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 4
#property indicator_color1  clrLimeGreen
#property indicator_color2  clrRed
#property indicator_width1 1
#property indicator_width2 1

input int ValidBars=5;
input bool ShowBox=true;
input bool ShowLines=true;
input bool WaitForCandleClosed=true;
input color UpperFractalBoxColor=clrLightPink;
input color LowerFractalBoxColor=clrLightBlue;
input double IgnoreCandlesTallerThan=100;
input double IgnoreCandlesShorterThan=20;
input string Info8 = "=== ALERT SETTINGS ===";
input bool EnableTextAlert=true;
input bool EnableSoundAlert=true;
input string SoundFilename="Alert2.wav";
input string Info9="You Need to Enable and Set your email settings in menu Tools-Options-Email";
input bool EnableEmail=true;
input string Info10="You Need to Enable and Set Notifications in menu Tools-Options-Notification";
input bool EnableNotification=true;
input string Info11="Screenshoot file can be found at \\Data Folder\\MQL4\\Files";
input bool EnableScreenShot=true;

string UpperLineObjName="ClemFractalUpperLines"+_Symbol;
string LowerLineObjName="ClemFractalLowerLines"+_Symbol;
string BoxObjName="ClemBox"+_Symbol;

double UpperFractalBreakoutLinesArr[],LowerFractalBreakoutLinesArr[],UpArrow[],DnArrow[];
datetime LastAlertTime=0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   LastAlertTime=0;
   Print("Deinit");
   IndicatorBuffers(4);
   SetIndexBuffer(0, UpArrow); SetIndexStyle(0,DRAW_ARROW); SetIndexArrow(0,233);
   SetIndexBuffer(1, DnArrow); SetIndexStyle(1,DRAW_ARROW); SetIndexArrow(1,234);

   SetIndexBuffer(2, UpperFractalBreakoutLinesArr); SetIndexStyle(2,DRAW_NONE);
   SetIndexBuffer(3, LowerFractalBreakoutLinesArr); SetIndexStyle(3,DRAW_NONE);
//---
   return(INIT_SUCCEEDED);
  }
  
void OnDeinit(const int reason) {
   for(int i=0;i<ObjectsTotal();i++){
      string name=ObjectName(i);
      if(StringFind(name,UpperLineObjName,0)>=0 || StringFind(name,LowerLineObjName,0)>=0 || StringFind(name,BoxObjName,0)>=0){
         ObjectDelete(name);
         i--;
      }
   }
}
  
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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 limit;
//--- check for rates total
//--- last counted bar will be recounted
   limit=rates_total-prev_calculated-1; 
   //Print(rates_total," ",prev_calculated," ",limit);
   if(limit<1) limit=2;
   for(int i=2; i<limit; i++){
      double UpperFractal=iFractals(_Symbol,0,MODE_UPPER,i+1);
      double LowerFractal=iFractals(_Symbol,0,MODE_LOWER,i+1);
      double range=(High[i]-Low[i])/Point;
      if(UpperFractal>0&& UpperFractal!=EMPTY_VALUE){
         if((IgnoreCandlesTallerThan>0 && range>=IgnoreCandlesTallerThan) || (IgnoreCandlesShorterThan>0 && range<=IgnoreCandlesShorterThan)) {}
         else{
            string name=UpperLineObjName+(string)(int)Time[i+1];
            if(ShowBox){
               string BoxName=BoxObjName+(string)(int)Time[i+1];
               if(ObjectFind(0,BoxName)<0){// no box object found
                  datetime TTime=Time[i+1]+(_Period*2*60);
                  ObjectCreate(0,BoxName,OBJ_RECTANGLE,0,Time[i+3],Low[i+2],TTime,High[i+1]);
                  ObjectSetInteger(0,BoxName,OBJPROP_COLOR,UpperFractalBoxColor);
               }
               else{// box object found
                  datetime TTime=Time[i+1]+(_Period*3*60);
                  ObjectSetInteger(0,BoxName,OBJPROP_TIME1,Time[i+3]);
                  ObjectSetInteger(0,BoxName,OBJPROP_TIME2,TTime);
                  ObjectSetDouble(0,BoxName,OBJPROP_PRICE1,Low[i+2]);
                  ObjectSetDouble(0,BoxName,OBJPROP_PRICE2,High[i+1]);
                  ObjectSetInteger(0,BoxName,OBJPROP_COLOR,UpperFractalBoxColor);
               }
            }
            if(ObjectFind(0,name)<0){// no Upper fractal Line found
               if(ShowLines){
                  datetime TTime=Time[i-1]+(_Period*ValidBars*60);
                  ObjectCreate(0,name,OBJ_TREND,0,Time[i-1],Low[i-1],TTime,Low[i-1]);
                  ObjectSetInteger(0,name,OBJPROP_COLOR,clrRed);
                  ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
                  ObjectSetInteger(0,name,OBJPROP_RAY,false);
               }
               UpperFractalBreakoutLinesArr[i-1]=Low[i-1];
            }
         }
      }
      if(LowerFractal>0&& LowerFractal!=EMPTY_VALUE){
         if((IgnoreCandlesTallerThan>0 && range>=IgnoreCandlesTallerThan) || (IgnoreCandlesShorterThan>0 && range<=IgnoreCandlesShorterThan)) {}
            else{
            string name=LowerLineObjName+(string)(int)Time[i+1];
            if(ShowBox){
               string BoxName=BoxObjName+(string)(int)Time[i+1];
               if(ObjectFind(0,BoxName)<0){// no box object found
                  datetime TTime=Time[i+1]+(_Period*2*60);
                  ObjectCreate(0,BoxName,OBJ_RECTANGLE,0,Time[i+3],High[i+2],TTime,Low[i+1]);
                  ObjectSetInteger(0,BoxName,OBJPROP_COLOR,LowerFractalBoxColor);
               }
               else{// box object found
                  datetime TTime=Time[i+1]+(_Period*3*60);
                  ObjectSetInteger(0,BoxName,OBJPROP_TIME1,Time[i+3]);
                  ObjectSetInteger(0,BoxName,OBJPROP_TIME2,TTime);
                  ObjectSetDouble(0,BoxName,OBJPROP_PRICE1,High[i+2]);
                  ObjectSetDouble(0,BoxName,OBJPROP_PRICE2,Low[i+1]);
                  ObjectSetInteger(0,BoxName,OBJPROP_COLOR,LowerFractalBoxColor);
               }         
            }
            if(ObjectFind(0,name)<0){// no Lower fractal Line found
               if(ShowLines){
                  datetime TTime=Time[i-1]+(_Period*ValidBars*60);
                  ObjectCreate(0,name,OBJ_TREND,0,Time[i-1],High[i-1],TTime,High[i-1]);
                  ObjectSetInteger(0,name,OBJPROP_COLOR,clrLimeGreen);
                  ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
                  ObjectSetInteger(0,name,OBJPROP_RAY,false);
               }
               LowerFractalBreakoutLinesArr[i-1]=High[i-1];
            }
         }
      }
   }
   WindowRedraw();
   
   int shift=0;
   if(WaitForCandleClosed) shift=1;
   for(int i=limit;i>shift;i--){
      double LowerFractalBreakoutLines=0;
      double UpperFractalBreakoutLines=0;
      if(UpperFractalBreakoutLinesArr[i]>0 && UpperFractalBreakoutLinesArr[i]!=EMPTY_VALUE){
         UpperFractalBreakoutLines=UpperFractalBreakoutLinesArr[i];
         bool Drawn=false;
         //if(i==2) Print(UpperFractalBreakoutLines);
         for(int j=1;j<=ValidBars;j++) {
            if(i-j>=shift){
               if(!Drawn && Close[i-j]<Open[i-j] && Close[i-j]<=UpperFractalBreakoutLines) {
                  DnArrow[i-j]=High[i-j]  + iATR(NULL,0,20,i-j)/2.0;
                  Drawn=true;
               }
            }
         }
      }
      if(LowerFractalBreakoutLinesArr[i]>0 && LowerFractalBreakoutLinesArr[i]!=EMPTY_VALUE){
         LowerFractalBreakoutLines=LowerFractalBreakoutLinesArr[i];
         bool Drawn=false;
         for(int j=1;j<=ValidBars;j++) {
            if(i-j>=shift) {
               if(!Drawn && Close[i-j]>Open[i-j] && Close[i-j]>=LowerFractalBreakoutLines) {
                  UpArrow[i-j]=Low[i-j]  - iATR(NULL,0,20,i-j)/2.0;
                  Drawn=true;
               }
            }
         }
      }
   }
   
   if(UpArrow[shift]>0 && UpArrow[shift]!=EMPTY_VALUE)SendAlert("UP");
   if(DnArrow[shift]>0 && DnArrow[shift]!=EMPTY_VALUE)SendAlert("DN");
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

void SendAlert(string sig){
   if(LastAlertTime<Time[0]){
      if(sig=="UP") {
         string msg=StringConcatenate(TimeToStr(TimeCurrent(),TIME_DATE|TIME_MINUTES)," Breakout UP ",_Symbol,TFToStr(_Period));
         if(EnableTextAlert) Alert(msg);
         if(EnableSoundAlert) PlaySound(SoundFilename);
         if(EnableEmail) SendMail(StringConcatenate("Acc# ",AccountNumber(),"Alert"),msg);
         if(EnableNotification) SendNotification(msg);
         if(EnableScreenShot) ChartScreenShot(0,StringConcatenate("BUY ",_Symbol," ",TimeToStr(Time[1],TIME_DATE),"_",TimeHour(Time[1]),"_",TimeMinute(Time[1]),".gif"),800,600);
      }
      if(sig=="DN") {
         string msg=StringConcatenate(TimeToStr(TimeCurrent(),TIME_DATE|TIME_MINUTES)," Breakout DOWN ",_Symbol,TFToStr(_Period));
         if(EnableTextAlert)Alert(msg);
         if(EnableSoundAlert) PlaySound(SoundFilename);
         if(EnableEmail) SendMail(StringConcatenate("Acc# ",AccountNumber(),"Alert"),msg);
         if(EnableNotification) SendNotification(msg);
         if(EnableScreenShot) ChartScreenShot(0,StringConcatenate("SELL ",_Symbol," ",TimeToStr(Time[1],TIME_DATE),"_",TimeHour(Time[1]),"_",TimeMinute(Time[1]),".gif"),800,600);
      }
      LastAlertTime=Time[0];
   }
}

string TFToStr(int tf){
   if(tf==PERIOD_M1) return("M1");
   if(tf==PERIOD_M5) return("M5");
   if(tf==PERIOD_M15) return("M15");
   if(tf==PERIOD_M30) return("M30");
   if(tf==PERIOD_H1) return("H1");
   if(tf==PERIOD_H4) return("H4");
   if(tf==PERIOD_D1) return("D1");
   return(DoubleToStr(_Period,0));
}
