/*
https://www.mql5.com/en/forum/179807/page344
*/
#property indicator_chart_window
#property strict

extern string TimeFrame       = "Current time frame";
extern bool    Display         = false;
extern int     boxbarperiods   = 16;
extern int     boxbarmode      = MODE_EMA;
extern int     boxbaroffset    = 0;
extern bool    DisplayText     = true;
extern string  symbol          = "";
extern int     MoveHorizontal  = 0;
extern int     MoveVertical    = 10;
extern int     Corner          = 0;
extern string  texttype        = "Arial Black";
extern int     textsize        = 11; 
extern color   strongbreakoutcolor  = Blue;
extern color   breakoutcolor   = Lime;
extern color   trendcolor      = Yellow;
extern color   rangecolor      = White;


extern string  linetypes       = "None";   //"ATR,ATR";
extern string  linepositions   = "1.5,-1.5";
extern string  linestyles      = "0,0";
extern string  linewidths      = "2,2";
extern string  linecolors      = "Aqua,Crimson";

extern int     boxstyle        = 0;
extern int     boxwidth        = 1;
extern color   boxcolor        = Yellow;
bool           boxcolorfill    = false;
extern bool    ShowGraphics    = false;      
string         buffername      = "BreakOut Box: ";
      
double   Trend[], aHigh[], aLow[], aClose[];
int      Lines=-1,Style[], Width[];
double   Position[];
string   Type[], Color[],TrendTxt;
double   AvgHighs, AvgLows, Top, Bottom, TrendHigh, TrendLow;
color    AvgColor = Aquamarine, TrendColor[7];  
datetime LastTime;
string indicatorFileName;
int    timeFrame;
int init()
{
         indicatorFileName = WindowExpertName();
         timeFrame         = stringToTimeFrame(TimeFrame);
   TrendColor[0]=strongbreakoutcolor; 
   TrendColor[6]=strongbreakoutcolor; 
   TrendColor[1]=breakoutcolor; 
   TrendColor[5]=breakoutcolor;
   TrendColor[2]=trendcolor; 
   TrendColor[4]=trendcolor; 
   TrendColor[3]=rangecolor;
  // for(int i=0;i<7;i++) Print(i," ",rangecolor," ",TrendColor[i]);
     
   if (symbol=="") symbol=Symbol(); 
   if(Symbol() != symbol)  Display=false;
   
   IndicatorShortName("BreakOut Box"); 
   SetIndexStyle(0, DRAW_NONE); SetIndexBuffer(0, Trend); SetIndexLabel(0, NULL);
      
   if(ShowGraphics)      {
      int xpos=5;    int ypos=5;
      Create_Labels("AAA-Panel01", xpos+1,ypos- 4, "gggggg", 40, "Webdings", Black);
      Create_Labels("AAA-Panel02", xpos+1,ypos+34, "gggggg", 40, "Webdings", Black);
      Create_Labels("AAA-Panel03", xpos+6,ypos+ 1, "gggggg", 38, "Webdings", Gray);
      Create_Labels("AAA-Panel04", xpos+6,ypos+34, "gggggg", 38, "Webdings", Gray);
      }
      
   if(linetypes == "" || linetypes == "None") {Lines=-1;return(0);}
     
   Lines=sParse_Tokens(linetypes, Type);
   dParse_Tokens(linepositions, Position);
   
   iParse_Tokens(linestyles, Style);
   iParse_Tokens(linewidths, Width); 
   sParse_Tokens(linecolors, Color);
   
   Delete_Objects(buffername);  
   return(0);
}

int deinit()  {Delete_Objects(buffername); return(0);}
double sumcalc(int aboxbarsize,double& a[])
{
   double sumlength=0.; 
   int i;
   for(i=boxbaroffset;i<aboxbarsize+boxbaroffset;i++) sumlength+=a[i];
   return(sumlength/aboxbarsize);
}
int start()  { 
   if (timeFrame != Period())
   {
         double dummy = iCustom(NULL,timeFrame,indicatorFileName,"",Display,boxbarperiods,boxbarmode,boxbaroffset,DisplayText,symbol,MoveHorizontal,MoveVertical,Corner,texttype,textsize,strongbreakoutcolor,breakoutcolor,
                                                                    trendcolor,rangecolor,linetypes,linepositions,linestyles,linewidths,linecolors,boxstyle,boxwidth,boxcolor,ShowGraphics,0,0);
         return(0);
   }      
      if(iTime(symbol,0,0) != LastTime)         {
         ArrayCopySeries(aHigh,MODE_HIGH,symbol);
         ArrayCopySeries(aLow ,MODE_LOW ,symbol);
         ArrayCopySeries(aClose ,MODE_CLOSE ,symbol);
         AvgHighs=sumcalc(boxbarperiods,aHigh); 
         AvgLows=sumcalc(boxbarperiods,aLow);
         
         if(boxbaroffset != 0)   boxbaroffset+=1;
         Top=0.;
         Bottom=99999.;
 
         for(int i=1+boxbaroffset;i<=boxbarperiods+boxbaroffset;i++)            {
            if(aHigh[i]>Top)      Top=aHigh[i];
            if(aLow[i] <Bottom )  Bottom=aLow[i];
            }
         LastTime=iTime(symbol,0,0);
         } 
         
         TrendHigh=Top-(Top-AvgHighs)/2.;
         TrendLow=Bottom -(Bottom-AvgLows)/2.;
         double Height=Top-Bottom;
         double AvgSize=AvgHighs-AvgLows;
         Trend[0]=Determine_Trend(TrendTxt,aClose);
         
         if(DisplayText)   {
            int trendindex=(int)(Trend[0]+3);
            //if(symbol ==  "XAGUSD")    
            string state = " "+DoubleToStr(100.0*(aClose[boxbaroffset]-(Top+Bottom)/2.0)/((Top-Bottom)/2.0),2)+ "%";
            Create_Labels(buffername+symbol+"Trend", 10,10, timeFrameToString(Period())+" "+symbol+" "+TrendTxt+state,textsize,texttype,TrendColor[trendindex]);
            //Print(symbol,"<",state,">",trendindex," ",Trend[0]," ",MoveVertical," ",aClose[0]," ",iClose(symbol,0,0)," ",Top," ",Bottom,  ":: ",rangecolor," ",TrendColor[3]," ",TrendColor[trendindex]);
         }
         if(!Display)   return(0);
                
         Create_BlockRange("Block-Range");
         for(int i=0;i<Lines;i++)         Draw_Line(i);
         double median=(AvgHighs+AvgLows)/2.;
         double med=(AvgHighs-AvgLows)/2.;
         Create_AverageRange("Avg-Range");
         Createline("Avg-High", AvgHighs,AvgColor,boxwidth+1);
         Createline("Avg-Low",  AvgLows,AvgColor,boxwidth+1);
         Createline("Avg-Mid",  median,AvgColor,boxwidth+1);
         //Createline("LowMedian",  (AvgLows-med),AvgColor,boxwidth+1);
         //Createline("HighMedian", (AvgHighs+med),AvgColor,boxwidth+1);
         //Createline("Trend-High", TrendHigh,Blue);
         //Createline("Trend-Low", TrendLow,Red);
 
   ObjectDelete (buffername+"Trend");
      
   if(ShowGraphics)      {
      Create_Labels("AAB-Panel01",  18,70, "BreakOut Box", 12, "Arial Black", White);
      Create_Labels("AAB-Panel0100",168,70, "Trend "+DoubleToStr(Trend[0],0), 12, "Arial Black", White);
      
      Create_Labels("AAB-Panel010", 15,55, "T   "+DoubleToStr(Top,Digits),    10, "Arial Black", White);
      Create_Labels("AAB-Panel011", 120,55,"B   "+DoubleToStr(Bottom,Digits), 10, "Arial Black", White);
      Create_Labels("AAB-Panel012", 215,55,"H   "+DoubleToStr(Height,Digits), 10, "Arial Black", White);
      
      Create_Labels("AAB-Panel013", 15,40, "AH "+DoubleToStr(AvgHighs,Digits), 10, "Arial Black", White);
      Create_Labels("AAB-Panel014", 105,40, "AL "+DoubleToStr(AvgLows,Digits), 10, "Arial Black", White);
      Create_Labels("AAB-Panel015", 200,40, "AS "+DoubleToStr(AvgSize,Digits), 10, "Arial Black", White);
      
      Create_Labels("AAB-Panel016", 15,25, "AM "+DoubleToStr(Height/AvgSize,Digits), 10, "Arial Black", White);
      }
   return(0);
  }
int Determine_Trend(string& text, double& aclose[])   {
   if (aclose[boxbaroffset] >= Top)                                           { text="Strong Breakout UP";   return( 3);}
   if (aclose[boxbaroffset] >= TrendHigh && aclose[boxbaroffset] < Top)       { text="Breakout UP";          return( 2);}
   if (aclose[boxbaroffset] >= AvgHighs &&  aclose[boxbaroffset]  < TrendHigh){ text="Trending UP";          return( 1);}
   if (aclose[boxbaroffset] <  AvgHighs &&  aclose[boxbaroffset]  > AvgLows)  { text="Range Bound";          return( 0);}
   if (aclose[boxbaroffset] <= AvgLows  &&  aclose[boxbaroffset]  > TrendLow) { text="Trending DOWN";        return(-1);}
   if (aclose[boxbaroffset] <= TrendLow &&  aclose[boxbaroffset] > Bottom)    { text="Breakout DOWN";        return(-2);}
   if (aclose[boxbaroffset] <= Bottom)                                        { text="Strong Breakout DOWN"; return(-3);}
   return(0);
}      
void  Create_BlockRange(string buffer)
{  
   buffer=buffername+buffer;
   ObjectDelete(buffer);
   ObjectCreate(buffer, OBJ_RECTANGLE,  0, 0, 0, 0, 0);
   ObjectSet   (buffer, OBJPROP_STYLE,  boxstyle);
   ObjectSet   (buffer, OBJPROP_WIDTH,  boxwidth);
   ObjectSet   (buffer, OBJPROP_COLOR,  boxcolor);
   ObjectSet   (buffer, OBJPROP_BACK,   boxcolorfill);
   ObjectSet   (buffer, OBJPROP_TIME1 , Time[boxbarperiods+boxbaroffset+1]);
   ObjectSet   (buffer, OBJPROP_PRICE1, Top);
   ObjectSet   (buffer, OBJPROP_TIME2 , Time[boxbaroffset]+Time[boxbaroffset]-Time[boxbaroffset+1]);
   ObjectSet   (buffer, OBJPROP_PRICE2, Bottom);
}
void  Create_AverageRange(string buffer)
{
   buffer=buffername+buffer;
   ObjectDelete(buffer);
   ObjectCreate(buffer, OBJ_RECTANGLE,  0, 0, 0, 0, 0);
   ObjectSet   (buffer, OBJPROP_STYLE,  boxstyle);
   ObjectSet   (buffer, OBJPROP_WIDTH,  boxwidth+1);
   ObjectSet   (buffer, OBJPROP_COLOR,  AvgColor);
   ObjectSet   (buffer, OBJPROP_BACK,   boxcolorfill);
   ObjectSet   (buffer, OBJPROP_TIME1 , Time[boxbaroffset]+Time[boxbaroffset]-Time[boxbaroffset+1]);
   ObjectSet   (buffer, OBJPROP_PRICE1, AvgHighs);
   ObjectSet   (buffer, OBJPROP_TIME2 , Time[boxbaroffset]+Time[boxbaroffset]-Time[boxbaroffset+2]);
   ObjectSet   (buffer, OBJPROP_PRICE2, AvgLows);
}              
color Colors(string aColor)
{
   if(aColor == "Red")     return(Red);
   if(aColor == "Green")   return(Green);
   if(aColor == "Aqua")    return(Aqua);
   if(aColor == "Lime")    return(Lime);
   if(aColor == "White")   return(White);
   if(aColor == "Yellow")  return(Yellow);
   if(aColor == "Blue")    return(Blue);
   return(White);
}
int Calculate_Position(int line)
{
   if(Type[line] == "ATR")
      {
      double atr=iATR(NULL,0,boxbarperiods,0);///(Point*100.);
      
      if(Position[line]>=0.)     return((int)(100+atr*Position[line]));
      else                       return((int)(atr*Position[line]));
      }
   if(Type[line] == "DEV")
      {
      double AvgClose=iMA(NULL,0,boxbarperiods,0,boxbarmode,PRICE_CLOSE,0)/(Point*100.);
      //double deviation = iDeviation(Close,boxbarperiods,AvgClose,1);
      //if(Position[line]>=0.)     return(100+deviation*Position[line]);
      //else                       return(deviation*Position[line]);
      } 
   return((int)Position[line]);     
}
void  Draw_Line(int line)
{
   string buffer=buffername+"Line"+(string)line;
   ObjectDelete(buffer);
   ObjectCreate(buffer, OBJ_TREND,   0, 0, 0);
   ObjectSet   (buffer, OBJPROP_STYLE,  Style[line]);
   ObjectSet   (buffer, OBJPROP_WIDTH,  Width[line]);
   ObjectSet   (buffer, OBJPROP_COLOR,  Colors(Color[line]));
   ObjectSet   (buffer, OBJPROP_RAY,    0);
   ObjectSet   (buffer, OBJPROP_BACK,   boxcolorfill);
   ObjectSet   (buffer, OBJPROP_TIME1 , Time[boxbarperiods+boxbaroffset+1]);
   
   int position=Calculate_Position(line);
   ObjectSet   (buffer, OBJPROP_PRICE1, Bottom+((Top-Bottom)/100)*position);
   ObjectSet   (buffer, OBJPROP_TIME2 , Time[boxbaroffset]+Time[boxbaroffset]-Time[boxbaroffset+1]);
   ObjectSet   (buffer, OBJPROP_PRICE2, Bottom+((Top-Bottom)/100)*position);
} 
int Count_Tokens(string Input)
{
   int cnt,pos=0;
   while(pos>-1)
      {
      pos=StringFind(Input,",",pos+1);
      if(pos>-1)      cnt+=1;  
      }
      cnt+=1;
   return(cnt);
}
int iParse_Tokens(string Input, int& Output[])
{
   int Tokens=Count_Tokens(Input); 
   if(Input=="") return(-1);
   ArrayResize(Output,Tokens);
   
   int i,start=0,stop=-1;
   for(i=0;i<Tokens-1;i++)
      {
      stop=StringFind(Input,",",stop+1);
      Output[i]=StrToInteger(StringSubstr(Input,start,stop-start));
      start=stop+1;
      }
   Output[i]=StrToInteger(StringSubstr(Input,start));
   return(Tokens);  
}   
int sParse_Tokens(string Input, string& Output[])
{
   int Tokens=Count_Tokens(Input); 
   if(Input=="") return(-1);
   ArrayResize(Output,Tokens);
   
   int i,start=0,stop=-1;
   for(i=0;i<Tokens-1;i++)
      {
      stop=StringFind(Input,",",stop+1);
      Output[i]=StringSubstr(Input,start,stop-start);
      start=stop+1;
      }
   Output[i]=StringSubstr(Input,start);
   return(Tokens);  
} 
int dParse_Tokens(string Input, double& Output[])
{
   int Tokens=Count_Tokens(Input); 
   if(Input=="") return(-1);
   ArrayResize(Output,Tokens);
   
   int i,start=0,stop=-1;
   for(i=0;i<Tokens-1;i++)
      {
      stop=StringFind(Input,",",stop+1);
      Output[i]=StrToDouble(StringSubstr(Input,start,stop-start));
      start=stop+1;
      }
   Output[i]=StrToDouble(StringSubstr(Input,start));
   return(Tokens);  
} 
double iDeviation(double& array[], double period, double ma, int i)
{
   double sum = 0.00;
      for(int k=0; k<period; k++) sum += (array[i+k]-ma)*(array[i+k]-ma);
   return(MathSqrt(sum/period));
}


 void Createline(string buffer, double start,  color aColor, int width)
   {
   buffer=buffername+buffer;
   ObjectDelete(buffer);
   ObjectCreate(buffer, OBJ_TREND,   0, 0, 0);
   ObjectSet   (buffer, OBJPROP_STYLE,  boxstyle);
   ObjectSet   (buffer, OBJPROP_WIDTH,  width);
   ObjectSet   (buffer, OBJPROP_COLOR,  aColor); 
   ObjectSet   (buffer, OBJPROP_RAY,    0);
   ObjectSet   (buffer, OBJPROP_BACK,   boxcolorfill);
   ObjectSet   (buffer, OBJPROP_TIME1 , Time[boxbarperiods+boxbaroffset+1]);
   ObjectSet   (buffer, OBJPROP_PRICE1, start);
   ObjectSet   (buffer, OBJPROP_TIME2 , Time[boxbaroffset]+Time[boxbaroffset]-Time[boxbaroffset+1]);
   ObjectSet   (buffer, OBJPROP_PRICE2, start);
   } 
   
void Create_Labels( string buffer, int x, int y,  string Text, int FontSize, string Font, color aColor ) {
   buffer=buffername+buffer;
   ObjectDelete(buffer);
   ObjectCreate( buffer, OBJ_LABEL, 0, 0, 0 );
   ObjectSet( buffer, OBJPROP_CORNER, Corner);
   ObjectSet( buffer, OBJPROP_XDISTANCE,x+MoveHorizontal);
   ObjectSet( buffer, OBJPROP_YDISTANCE,y+MoveVertical);
   ObjectSetText(buffer, Text, FontSize, Font, aColor);
 } 
    void Delete_Objects(string buffer)
{
   int total,i,buffersize;
   buffersize=StringLen(buffer);
   string name,prefix;
   total=ObjectsTotal();
   for(i=total;i>=0;i--)
      {
      name=ObjectName(i);
      prefix=StringSubstr(name,0,buffersize);
      if(prefix==buffer)         ObjectDelete(name);
      }
   GetLastError();

}       

//-------------------------------------------------------------------
//
//-------------------------------------------------------------------
//
//
//
//
//

string sTfTable[] = {"M1","M5","M15","M30","H1","H4","D1","W1","MN"};
int    iTfTable[] = {1,5,15,30,60,240,1440,10080,43200};

//
//
//
//
//

int stringToTimeFrame(string tfs)
{
   tfs = stringUpperCase(tfs);
   for (int i=ArraySize(iTfTable)-1; i>=0; i--)
         if (tfs==sTfTable[i] || tfs==""+(string)iTfTable[i]) return(MathMax(iTfTable[i],Period()));
                                                      return(Period());
}
string timeFrameToString(int tf)
{
   for (int i=ArraySize(iTfTable)-1; i>=0; i--) 
         if (tf==iTfTable[i]) return(sTfTable[i]);
                              return("");
}

//
//
//
//
//

string stringUpperCase(string str)
{
   string   s = str;

   for (int length=StringLen(str)-1; length>=0; length--)
   {
      int tchar = StringGetChar(s, length);
         if((tchar > 96 && tchar < 123) || (tchar > 223 && tchar < 256))
                     s = StringSetChar(s, length, tchar - 32);
         else if(tchar > -33 && tchar < 0)
                     s = StringSetChar(s, length, tchar + 224);
   }
   return(s);
}