//+------------------------------------------------------------------+
//|                                       17_8_2020_RS_Indicator.mq4 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+

#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property description "Indicator to show Grouped Fractal Support and Resisitance Level"
#property strict
#property indicator_chart_window
#property indicator_buffers 2
#define Confirm_Level 3
#define Up_Fractal  1
#define Down_Fractal  2
#define Combined_Up   4
#define Combined_Down  5
 
enum timeframe_display_option
   {
    Current = 0,
    PeriodM5 =5,
    PeriodM15 =15,
    PeriodM30 =30,
    PeriodH1 =60,
    PeriodH4 =240,
    PeriodD1 =1440,
    PeriodW1 =10080,
   };
double RSBuffer1[];//hold new form fractal
double RSMode[];//hold new form fractal status
double Display_level[];// hold fractal display level 
double Display_Mode[];// hold fractal display level status

extern int                       Show_Level_Limit =15;         //Max Level to be display on chart
extern timeframe_display_option  Level_of_TimeFrame=Current; //Option to display which timeframe level on chart
extern double                    sensitvity = 15;     // Min distance between each RS level,input in pips values
int DisplayIndex = 0;


//++++ These are adjusted for 5 digit brokers.
   int     pips2points;    // slippage  3 pips    3=points    30=points
   double  pips2dbl;       // Stoploss 15 pips    0.0015      0.00150
   int     Digitspips;    // DoubleToStr(dbl/pips2dbl, Digits.pips)
   string indId ="SR_";    // string to name object on chart for delete and create object
   double fractal;         // to store fractal point value
   bool modify = false;    // to indicate a existing level on chart being modify or not
   double status;// status of display level, use to reduce and combine level within min distance
   color mark = clrRed;                 

   //--------------------------------------------------
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
                                            
     if (Digits % 2 == 1)
     {      // DE30=1/JPY=3/EURUSD=5 forum.mql4.com/43064#515262
                pips2dbl    = Point*10; 
                pips2points = 10;   
                Digitspips = 1;
     } 
     else 
     {   pips2dbl    = Point;    
         pips2points =  1;   
         Digitspips = 0; 
     }
   SetIndexBuffer(0, RSBuffer1);
   SetIndexStyle(0,DRAW_ARROW);
   SetIndexArrow(0,251);  //buffer 0 draw line not symbol
   SetIndexEmptyValue(0,0.0);
   SetIndexBuffer(1, RSMode);
   SetIndexStyle(1,DRAW_NONE);
   SetIndexArrow(1,251);  //buffer 0 draw line not symbol
   SetIndexEmptyValue(1,0.0);
   sensitvity = sensitvity * pips2dbl;// min distance of each display level/fractal point
    return(0);

  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
  {
   DeleteAllObjects();
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   int counted_bars=IndicatorCounted();
   if(counted_bars > 0)       //---- the last calculated bar will be recalculated   
      counted_bars--;
   int limit = Bars - counted_bars-1;

//-------------------------------------------------------------------------------------------------  
//---- store fractal point to array   
//-------------------------------------------------------------------------------------------------  
   while(limit>0)
   { 
   fractal = iFractals(NULL, Level_of_TimeFrame, MODE_UPPER, limit);
      if(fractal != 0)
     {   
      RSBuffer1[limit]= fractal;
      RSMode[limit]= Up_Fractal;
     }
   fractal = iFractals(NULL, Level_of_TimeFrame, MODE_LOWER, limit);
       if(fractal != 0)
      {
      RSBuffer1[limit]= fractal;
      RSMode[limit]= Down_Fractal;
      }
//-----------------------------------------------------------------------------------------------------------------------------------------------      
//below status for fractal point reduction  
//Confirm_Level 3
//Up_Fractal  1
//Down_Fractal  2
//Combined_Up   4
//Combined_Down  5
//upper fractal point with another upper fractal point == combined up
//lower fractal point with another lower fractal point == combine down
//opposite fractal merge == confirm level (up & down || up & combine down || up && confirm || down & up || down & combine up ||down && confirm)
//--------------------------------------------------------------------------------------------------------------------------------------------------

   ArrayResize(Display_level,Show_Level_Limit+1);
   ArrayResize(Display_Mode,Show_Level_Limit+1);
     if( RSBuffer1[limit]!=0)
     { 
      for (int i=Show_Level_Limit-1;  i>=0; i--)
      {
         double diff = MathAbs(RSBuffer1[limit]-Display_level[i]);// calculate min distance between level
         modify = false;

                  if(diff<=sensitvity )//new fractal, check against Displaylevel
                     {  
                        if(Display_Mode[i]==1&& RSMode[limit] == 1)// 
                           {   
                                 Display_Mode[i]= 4;// mode up & up = combine up
                                 if(RSBuffer1[limit]<Display_level[i])
                                    Display_level[i]=Display_level[i];
                                    
                              Comment(  " Up & Up== " + Display_level[i]+ "Display_Mode = " +Display_Mode[i]);
                           }
                        if(Display_Mode[i]==4 && RSMode[limit] == 1)
                           {
                                    Display_Mode[i]= 4;// mode combine up & up = combine up
                                    if(RSBuffer1[limit]<Display_level[i])
                                       Display_level[i]=Display_level[i];
                              Comment(  " Up & combine Up== "+ Display_level[i]+ "Display_Mode = " +Display_Mode[i]);
                           }      
                        if(Display_Mode[i]==2 && RSMode[limit] == 2)
                           {  
                                  Display_Mode[i]= 5;// mode down & down = combine down
                                  if(RSBuffer1[limit]>Display_level[i])
                                    Display_level[i]=Display_level[i];
                              Comment(  "down & down== "+ Display_level[i]+ "Display_Mode = " +Display_Mode[i]); 
                           }
                        if(Display_Mode[i]==5 && RSMode[limit] == 2)
                           {
                                    Display_Mode[i]= 5;// mode down & combine down  = combine down
                                    if(RSBuffer1[limit]>Display_level[i])
                                       Display_level[i]=Display_level[i];
                              Comment(  " Down & combine down== "+ Display_level[i]+ "Display_Mode = " +Display_Mode[i]);
                           }   
                         
                     }    

                     if(diff<=sensitvity) 
                     {    
                         
                        if((Display_Mode[i]==2 && RSMode[limit] == 1 )||(Display_Mode[i]==1 && RSMode[limit] == 2  ))
                           {
                               Display_Mode[i]= 3;// mode  up & down & combine down  = confirm 
                                    Display_level[i]=Display_level[i];
                              Comment(  "Up & down or combine down == "+ Display_level[i]+ "Display_Mode = " +Display_Mode[i]);
                              modify = true;
                              break;
                           }                          
                         if( (Display_Mode[i]==3 || Display_Mode[i]==5))
                           {
                               Display_Mode[i]= 3;// mode  up & confirm level = confirm 
                               Display_level[i]=Display_level[i];
                              Comment(  "Up & confirm level== "+ Display_level[i]+ "Display_Mode = " +Display_Mode[i]);
                              modify = true;
                              break;
                           }
                           
                         if( Display_Mode[i]==3 || Display_Mode[i]==4)
                           {
                               Display_Mode[i]= 3;// mode  down & confirm level = confirm 
                               Display_level[i]=Display_level[i];
                              Comment(  "Down & confirm level== "+ Display_level[i]+ "Display_Mode = " +Display_Mode[i]);
                              modify = true;
                              break;
                           }
                           
                     }
                  
       }//end of modify existing level for loop 
                  
                  if(modify == false)// store disply level untill reacah show level limit
                  { 
                     for (int a=0;  a<Show_Level_Limit-1; a++)
                     { 
                        if (Display_Mode[a]==0)
                        {
                           Display_Mode[a]=RSMode[limit];
                           Display_level[a] = RSBuffer1[limit];
                           
                           modify =true;
                           break;
                        } 
                        a++;  
                     }
                 
                  if(modify == false)// store new level when display level limit have no more room
                     {      
                                 if(RSMode[limit]==1)
                                    { 
                                       int index = ArrayMinimum(Display_level,WHOLE_ARRAY,0);
                                       Display_Mode[index]=RSMode[limit] ;
                                       Display_level[index]=RSBuffer1[limit];
                                    }  
                                 if(RSMode[limit]==2)
                                    { 
                                       int index = ArrayMaximum(Display_level,WHOLE_ARRAY,0);
                                       Display_Mode[index]=RSMode[limit] ;
                                       Display_level[index]=RSBuffer1[limit];
                                    }
                                    
                                  modify =true; 
                                      
                     } 
                 }          
      }

      limit--;
      
   }    
                  DeleteAllObjects();
                 
                  int uplevel=1;
                  int downlevel=1;
                  ArraySort(Display_level,WHOLE_ARRAY,0,MODE_ASCEND);
                  DisplayIndex = 0;
                  for(int a=0;a<ArraySize(Display_level)-1; a++)
                  {
                        if (Display_level[a]<Bid && Display_level[a+1]>Bid)
                           DisplayIndex =a+1;
                  }         

                     for (int c =DisplayIndex ; c<=Show_Level_Limit; c++)
                  {         
                     HLine("SR_RES"+uplevel+ " # " + c, Display_level[c],mark);
                     uplevel++;
                  }   
                     for (int c =DisplayIndex-1 ; c>0; c--)
                  {         
                     HLine("SR_SUP"+downlevel+ " # " + c, Display_level[c],mark);
                     downlevel++;
                  }   

                     

       



return(0);
   } 
void HLine(string name, double P0, color clr)
      {
          #define WINDOW_MAIN 0
    /**/  if (ObjectMove( name, 0, Time[0], P0 )){}
            else if(!ObjectCreate( name, OBJ_HLINE, WINDOW_MAIN, Time[0], P0 ))
                    Alert("ObjectCreate(",name,",HLINE) failed: ", GetLastError() );
          if (!ObjectSet(name, OBJPROP_COLOR, clr )) // Allow color change
                  Alert("ObjectSet(", name, ",Color) [1] failed: ", GetLastError() );
          if (!ObjectSetText(name, PriceToStr(P0), 10))
                     Alert("ObjectSetText(",name,") [1] failed: ", GetLastError());
}
void DeleteAllObjects()
{
   // Delete all objects created by the indicator
   for (int i = ObjectsTotal() - 1;  i >= 0;  i--)
   {
      string name = ObjectName(i);
      
      if (StringSubstr(name, 0, StringLen(indId)) == indId)
         ObjectDelete(name);
   }
}
 
string  PriceToStr(double p)
   {
    string pFrc = DoubleToStr(p, Digits);       
    if(Digitspips==0) 
      return(pFrc);
    string pPip = DoubleToStr(p, Digits-1);
    if (pPip+"0" == pFrc)       
      return(pPip);           
      return(pFrc);          
   }
//---------------------------------------------------------------------------------------------  
double Price(double Old, double New, int result)//1 return larger value, 2 return smaller value
   { 
   double value=0;
   if(result==1)
   value=MathMax(Old,New);
   if(result==2)
   value=MathMin(Old,New);
   return(value);
   }
//---------------------------------------------------------------------------------------------   
