Author: George Egroeg (egro1egro at yahoo dot com)
S_R_lines

This script is designed to automatically identify and display Support and Resistance lines on a trading chart. Instead of drawing every single price peak or valley, it uses a specific set of rules to filter out "noise" and only highlight significant price levels that are likely to influence market movement.

Core Logic and Functionality

1. Fractal Detection (Identifying Turning Points) The script scans the historical price data to find "fractals." In simple terms, these are local highs and lows where a candle's peak or valley is higher (or lower) than the candles immediately preceding and following it. These serve as the primary candidates for support and resistance levels.

2. Smart Filtering and Cleaning To prevent the chart from becoming cluttered with too many lines, the script applies several layers of logic:

  • Overlapping Protection: If a new price level is found that is very close to an existing line (based on the current market spread), the script treats them as one. It prevents "double-counting" levels that are virtually identical.
  • Price Breach Cancellation: One of the most important features is its ability to "delete" lines based on market action. If the price moves significantly through a resistance level, the script considers that level "broken" and removes it from the display.
  • Proximity Limits: The script ignores levels that are too far away from the current market price (configurable by a percentage limit), ensuring you only see the levels that are relevant to current trading.
  • Expiration: Lines have a lifespan. If a line is not refreshed or remains stagnant for a certain number of candles, it is discarded to keep the chart clean.

3. Multi-Instance Coordination The script is designed to be used in a "set" of 20 instances. Each instance is assigned an index. This allows the user to populate a large number of potential levels across the chart simultaneously, while the internal logic ensures that each individual instance manages its specific group of four lines independently.

4. Performance Optimization The script includes settings to limit how far back into the past it looks. By defining how many "screens" of historical data to analyze, the user can balance the depth of the analysis with the speed of the software, ensuring the platform remains responsive even when processing large amounts of data.

Summary of Visual Behavior

  • Resistance Lines (Green): These are drawn at local price ceilings. If the price breaks significantly above them, they disappear, signaling that the "ceiling" has been breached.
  • Support Lines (Red): These are drawn at local price floors. If the price falls significantly below them, they disappear, signaling that the "floor" has given way.
Indicators Used
Fractals
Miscellaneous
Implements a curve of type %1
13 Views
0 Downloads
0 Favorites
S_R_lines
///////////////////////////////////////////////////////
// George Egroeg (C) 2006 e-m: egro1egro at yahoo dot com
// aka "egro1egro"
//
// "Support/resistance lines" Version 3.0
//
///////////////////////////////////////////////////////
// The package contains two files:
// 1. S_R_lines.mq4 (script) should be placed into experts/indicators folder
// 2. S_R_lines.tpl (chart template) should be placed into templates folder
///////////////////////////////////////////////////////
// This indicator has only one parameter: lineIndex = 1,...,20
// In order to make it work you will have to place 20 indicators 
// all with different parameter values. A chart template
// with 20 indicators of this type is included for
// your convenience.
//
// There are two global variable that you might want to set before running this script:
//
// "sr_last_screens" - number of last screens to start drawing s/r lines from
// One screen is assumed to have 150 bars/candles
// If you specify global value 2 it will mean that 300 candles back s/r lines 
// can be started only. Using this parameter should improve performance significantly.
//
// "sr_spread_multiple" - number of (Bid-Ask) spreads to use as a delta when merging two s/r 
// lines together. If you specify value 2 and current spread is 0.0003 then two lines within 
// 6 pips will be merged into one. You might want to increase this parameter if you are 
// planning to use bigger time-frames (for instance, for hourly charts you might need 5-10 
// spreads to use as a delta in order to unclutter the screen). By default, it is set to 1.5 spreads.
//
// !!! After applying global variables or after recompiling this script it is necessary to
// de-select the chart template and then select it again to apply a new.
//
// Also, some parameters are defined as constants in the code
// and can be changed sensibly. These parameters cannot be made external
// because they need to be exactly the same on all instances of the indicator.
//
// MAX_NUM_LINES is a number of indicators on a chart
// If you think that 20 is overkill try to reduce this constant and
// accordingly delete some of the indicators from the chart template
// It might be enough to have 10, but if you notice that some lines are 
// not drawn where they should be - increase the number (make it even bigger than 20)
// The point is - always maintain the same number of indicators 
// on the chart al with different indices
//
// MAX_PERCENT_OUT is how far from the last price potential s/r lines 
// should be maintained (I think 3% is quite enough for intraday, but...)
//
// DELTA_PERIODS a number of candles/bars after which s/r lines "expire"
// indicator script discards old line and stops drawing them to the right.
//
// DEFAULT_NUM_SCREENS how many screens back shell we start the first s/r line
// if the global variable "sr_last_screens" is not defined or invalid
///////////////////////////////////////////////////////
#property copyright "George Egroeg (egro1egro at yahoo dot com)"

#property indicator_chart_window
#property indicator_buffers 8

#property indicator_color1 LimeGreen
#property indicator_color2 LimeGreen
#property indicator_color3 LimeGreen
#property indicator_color4 LimeGreen

#property indicator_color5 Red
#property indicator_color6 Red
#property indicator_color7 Red
#property indicator_color8 Red

// maximum number of support/resistance line pairs
#define MAX_NUM_LINES      5
#define MAX_NUM_LINES_REAL 20 // has to be exactly 4 times the previous number

// max deviation from current price
#define MAX_PERCENT_OUT    3.0

// 2 lines within (Bid-Ask)* DELTA_SPREAD_MULTIPLE are merged into one
#define DELTA_SPREAD_MULTIPLE 1.5

#define DELTA_PERIOD       600

// 10 screens back from now with 150 bars/canldes in each - the very first s/r/ line can possibly start
#define DEFAULT_NUM_SCREENS 10

//////////////////////////////////////////////////////////////////////////////
// the following constants are for internal use and should not be changed
//////////////////////////////////////////////////////////////////////////////
// number of bars left behind in a fractal
#define NUM_BARS_FRACTAL   2
// constants that denote empty values (i.e. don't draw if empty)
#define LINE_EMPTY_VALUE   0.0
#define FRACTAL_EMPTY_VALUE 0.0
#define DATE_EMPTY_VALUE 0 // 0 am 1 Jan 1970
#define NUM_BARS_SCREEN 150 // number of bars in a screen normally


// which one of s/r pair of lines to show (can be from 1 to 20)
extern int lineIndex = 1;

double lineDelta = 0.0005;

//---- buffers
double resLine1[];
double resLine2[];
double resLine3[];
double resLine4[];

double suppLine1[];
double suppLine2[];
double suppLine3[];
double suppLine4[];


// list of resistance lines
// we maintain the whole list of them but use only the requested one
double upper_f[MAX_NUM_LINES_REAL]; // f is for tracking previous fractal values (hi/lo of candles)
double upper_l[MAX_NUM_LINES_REAL]; // l is for tracking previous r/s lines levels (open/close of candles)
datetime upper_s[MAX_NUM_LINES_REAL]; // s is for tracking start time of s/r lines

// queue of support lines
double lower_f[MAX_NUM_LINES_REAL];
double lower_l[MAX_NUM_LINES_REAL];
datetime lower_s[MAX_NUM_LINES_REAL];

double valHigh = LINE_EMPTY_VALUE;
double valLow = LINE_EMPTY_VALUE;

datetime startTime = DATE_EMPTY_VALUE;
double deltaSpreadMultiple = DELTA_SPREAD_MULTIPLE;

// loop variable
int i;
int j;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+  

void initRunningLists()
{
   for(j = 0; j < MAX_NUM_LINES_REAL; j++)
   {
      upper_f[j] = LINE_EMPTY_VALUE;
      upper_l[j] = LINE_EMPTY_VALUE;
      upper_s[j] = DATE_EMPTY_VALUE;
      lower_f[j] = LINE_EMPTY_VALUE;
      lower_l[j] = LINE_EMPTY_VALUE;
      lower_s[j] = DATE_EMPTY_VALUE;
   }
}

int init()
  {
  // adjust input values
  if ( lineIndex < 1 )
   lineIndex = 1;
  if ( lineIndex > MAX_NUM_LINES )
   lineIndex = MAX_NUM_LINES;
   
  j = (lineIndex-1)*4;
//----  
   SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 1, LimeGreen);
   SetIndexEmptyValue(0, LINE_EMPTY_VALUE);
   SetIndexDrawBegin(0, 1);
   SetIndexBuffer(0, resLine1);
   SetIndexLabel(0,"Resistance " + (j + 1));
   
   SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 1, LimeGreen);
   SetIndexEmptyValue(1, LINE_EMPTY_VALUE);
   SetIndexDrawBegin(1, 1);
   SetIndexBuffer(1, resLine2);
   SetIndexLabel(1,"Resistance " + (j + 2));
   
   SetIndexStyle(2, DRAW_LINE, STYLE_SOLID, 1, LimeGreen);
   SetIndexEmptyValue(2, LINE_EMPTY_VALUE);
   SetIndexDrawBegin(2, 1);
   SetIndexBuffer(2, resLine3);
   SetIndexLabel(2,"Resistance " + (j + 3));
   
   SetIndexStyle(3, DRAW_LINE, STYLE_SOLID, 1, LimeGreen);
   SetIndexEmptyValue(3, LINE_EMPTY_VALUE);
   SetIndexDrawBegin(3, 1);
   SetIndexBuffer(3, resLine4);
   SetIndexLabel(3,"Resistance " + (j + 4));
//----    
   SetIndexStyle(4,DRAW_LINE, STYLE_SOLID,1, Red);
   SetIndexEmptyValue(4, LINE_EMPTY_VALUE);
   SetIndexDrawBegin(4,1);
   SetIndexBuffer(4, suppLine1);
   SetIndexLabel(4,"Support " + (j + 1));
   
   SetIndexStyle(5,DRAW_LINE, STYLE_SOLID,1, Red);
   SetIndexEmptyValue(5, LINE_EMPTY_VALUE);
   SetIndexDrawBegin(5,1);
   SetIndexBuffer(5, suppLine2);
   SetIndexLabel(5,"Support " + (j + 2));
   
   SetIndexStyle(6,DRAW_LINE, STYLE_SOLID,1, Red);
   SetIndexEmptyValue(6, LINE_EMPTY_VALUE);
   SetIndexDrawBegin(6,1);
   SetIndexBuffer(6, suppLine3);
   SetIndexLabel(6,"Support " + (j + 3));
   
   SetIndexStyle(7,DRAW_LINE, STYLE_SOLID,1, Red);
   SetIndexEmptyValue(7, LINE_EMPTY_VALUE);
   SetIndexDrawBegin(7,1);
   SetIndexBuffer(7, suppLine4);
   SetIndexLabel(7,"Support " + (j + 4));
   
   IndicatorShortName("S/R lines " + lineIndex);
      
   if ( GlobalVariableCheck("sr_last_screens") )
   {
      double sn = GlobalVariableGet("sr_last_screens");
      if ( sn > 0 )
      {
         startTime = TimeCurrent() - (Period() * 60 * sn * NUM_BARS_SCREEN);
      }
      else
      {
         startTime = TimeCurrent() - (Period() * 60 * DEFAULT_NUM_SCREENS * NUM_BARS_SCREEN);
      }
   }
   else
      startTime = TimeCurrent() - (Period() * 60 * DEFAULT_NUM_SCREENS * NUM_BARS_SCREEN);
   
   if ( GlobalVariableCheck("sr_spread_multiple") )
   {
      double sm = GlobalVariableGet("sr_spread_multiple");
      if ( sm > 0 )
          deltaSpreadMultiple = sm;
      else
          deltaSpreadMultiple = DELTA_SPREAD_MULTIPLE;
   }
   
   initRunningLists();
   
//---- 
   return(0);
  }
  
 double myFractal(int mode, int shift)
 {
   if ( (shift < 2) || (shift > (Bars - 2)) )
      return (0.0);
      
   if ( NUM_BARS_FRACTAL > 2 )
      return (iFractals(NULL, 0, mode, shift));
   
   // need to compare High of (shift-1, shift, shift+1)
   if ( mode == MODE_UPPER )
   {
      if ( (High[shift] >= High[shift-1]) && (High[shift] >= High[shift+1]) )
        return(High[shift]);
        
      /*
      // check right side
      int k = shift - 1;
      while( (k >= 0) && (High[k] == High[shift]) )
      {
         k--;
      }
      if ( k >= 0 && (High[k] > High[shift]) )
         return (0.0);
       
      // now check left side  
      k = shift + 1;
      while( (k < Bars) && (High[k] == High[shift]) )
      {
         k++;
      }
      if ( (k < Bars) && (High[k] > High[shift]) )
         return (0.0);
         
      return (High[shift]);
      */
   }
   else
   {
      if ( (Low[shift] <= Low[shift-1]) && (Low[shift] <= Low[shift+1]) )
         return(Low[shift]);
       
      /*
      // check right side
      k = shift - 1;
      while( (k >= 0) && (Low[k] == Low[shift]) )
      {
         k--;
      }
      if ( k >= 0 && (Low[k] < Low[shift]) )
         return (0.0);
       
      // now check left side  
      k = shift + 1;
      while( (k < Bars) && (Low[k] == Low[shift]) )
      {
         k++;
      }
      if ( (k < Bars) && (Low[k] > Low[shift]) )
         return (0.0);
         
      return (Low[shift]);
      */
   }
   
   return (0.0);
 }

double updateViewResistance(int li, double vhPrevious, double NewLow)
{
     if ( upper_s[li] == DATE_EMPTY_VALUE )
     {
         // no line no picture
         return (LINE_EMPTY_VALUE);
     }
     else
     {
        // new line can be cancelled if a candle has printed over it
        if ( NewLow > (upper_l[li] + lineDelta) )
        {
            upper_f[li] = LINE_EMPTY_VALUE;
            upper_l[li] = LINE_EMPTY_VALUE;
            upper_s[li] = DATE_EMPTY_VALUE;
            return (LINE_EMPTY_VALUE);
        }
        else
        {
            // we have a line but if we have an old line we will have to cancel it first
            if ( vhPrevious != LINE_EMPTY_VALUE )
            {
                // we have a line already
                if ( vhPrevious != upper_l[li] ) // got a new line instead now
                    return (LINE_EMPTY_VALUE); // cancel the old one
                else
                    return (vhPrevious); // continue the same old one
            }
            else
            {
                return (upper_l[li]); // start a new one here
            }
         
         }
     }
}
  
double updateViewSupport(int li, double vlPrevious, double NewHigh)
{
     if ( lower_s[li] == DATE_EMPTY_VALUE )
     {
         // no line no picture
         return (LINE_EMPTY_VALUE);
     }
     else
     {
        // new line can be cancelled if a candle has printed over it
        if ( NewHigh < (lower_l[li] - lineDelta) )
        {
            lower_f[li] = LINE_EMPTY_VALUE;
            lower_l[li] = LINE_EMPTY_VALUE;
            lower_s[li] = DATE_EMPTY_VALUE;
            return (LINE_EMPTY_VALUE);
        }
        else
        {
            // we have a line but if we have an old line we will have to cancel it first
            if ( vlPrevious != LINE_EMPTY_VALUE )
            {
                // we have a line already
                if ( vlPrevious != lower_l[li] ) // got a new line instead now
                    return (LINE_EMPTY_VALUE); // cancel the old one
                else
                    return (vlPrevious); // continue the same old one
            }
            else
            {
                return(lower_l[li]); // start a new one here
            }
         
         }
     }
}
 
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int start()
{
   initRunningLists();
   
   lineDelta = MathAbs(Ask - Bid) * deltaSpreadMultiple;
   
   int td1 = MathAbs(Time[Bars-1] - Time[Bars-2]); // number of seconds in one period
   int td = td1 * DELTA_PERIOD;
   
   i = Bars-1;
   
   // skip old dates
   while( (i >= 0) && (Time[i] < startTime) )
   {
      i--;
   }
   
   while( i >= 0 )
   { 
       double rangeMax = Close[i] * ( 1.0 + (MAX_PERCENT_OUT/100.0) );
       double rangeMin = Close[i] * ( 1.0 - (MAX_PERCENT_OUT/100.0) );
       datetime t = Time[i];

        // when fractal function returns non-zero value we get a new potential resistance line
       valHigh = myFractal(MODE_UPPER, i);
       
       double body_max = MathMax(Close[i], Open[i]);
        
       if ( valHigh > FRACTAL_EMPTY_VALUE )
       {
         // fractal is getting reported only on the very last bar of it
         // so we need to select the highest body top out of 5 bars
         
         for(j = MathMin((i + NUM_BARS_FRACTAL - 1), Bars); j >= MathMax((i - NUM_BARS_FRACTAL + 1),0); j--)
         {
             if ( body_max < MathMax(Close[j], Open[j]) )
               body_max = MathMax(Close[j], Open[j]);
         }
         j = MathMax((i - NUM_BARS_FRACTAL),0);
         while( (j >= 0) && (MathMax(Close[j], Open[j]) < valHigh) && (MathMax(Close[j], Open[j]) > body_max) )
         {
             body_max = MathMax(Close[j], Open[j]);
             j--;
         }
       }
       
       valLow = myFractal(MODE_LOWER, i);
       
       double body_min = MathMin(Close[i], Open[i]);
       if ( valLow > FRACTAL_EMPTY_VALUE )
       {
         // fractal is getting reported only on the very last bar of it
         // so we need to select the highest body top out of 5 bars
         for(j = MathMin((i + NUM_BARS_FRACTAL - 1), Bars); j >= MathMax((i - NUM_BARS_FRACTAL + 1),0); j--)
         {
           if ( body_min > MathMin(Close[j], Open[j]) )
             body_min = MathMin(Close[j], Open[j]);
         } 
         j = MathMax((i - NUM_BARS_FRACTAL),0);
         while( (j >= 0) && (MathMin(Close[j], Open[j]) > valLow) && (MathMin(Close[j], Open[j]) < body_min) )
         {
             body_min = MathMin(Close[j], Open[j]);
             j--;
         }
       }
       
     // First, cancel old resistance lines because of overlapping
     for(j = 0; j < MAX_NUM_LINES_REAL; j++ )
     {
       if ( ((upper_l[j] /*+ lineDelta*/) < body_max) 
           || (upper_l[j] > rangeMax) 
           || ((upper_s[j] + td) < t)
           )
       {
         upper_f[j] = LINE_EMPTY_VALUE; // cancel the item
         upper_l[j] = LINE_EMPTY_VALUE;
         upper_s[j] = DATE_EMPTY_VALUE;
       }
     }
     
     if ( valHigh > FRACTAL_EMPTY_VALUE ) 
     {

       // Next, cancel old resistance lines because of new fractals
       for(j = 0; j < MAX_NUM_LINES_REAL; j++ )
       {
         if (upper_f[j] < valHigh)
         {
           upper_f[j] = LINE_EMPTY_VALUE; // cancel the item
           upper_l[j] = LINE_EMPTY_VALUE;
           upper_s[j] = DATE_EMPTY_VALUE;
         }
       }
       
       bool bExistsAlready = false;
       for(j = 0; j < MAX_NUM_LINES_REAL; j++)
       {
          if ( upper_f[j] != LINE_EMPTY_VALUE )
          {
            if ( ((upper_l[j] - lineDelta) < body_max) 
                && ((upper_l[j] + lineDelta) > body_max) )
            {
              bExistsAlready = true;
              break;
            }
          }
       }
       
       // if we have a new fractal up - insert a new s/r line
       if ( !bExistsAlready )
       {
         for(j = 0; j < MAX_NUM_LINES_REAL; j++)
         {
           if ( upper_f[j] == LINE_EMPTY_VALUE )
           {
             upper_f[j] = valHigh;
             upper_l[j] = body_max;
             upper_s[j] = t;
             break;
           }
         }
       }
     }     

     // First, cancel old s/r lines because of candle-overlapping
     for(j = 0; j < MAX_NUM_LINES_REAL; j++ )
     {
       if ( ((lower_l[j] /*- lineDelta*/) > body_min) 
           || (lower_l[j] < rangeMin) 
           || ((lower_s[j] + td) < t)
           )
       {
         lower_f[j] = LINE_EMPTY_VALUE; // cancel the item
         lower_l[j] = LINE_EMPTY_VALUE;
         lower_s[j] = DATE_EMPTY_VALUE;
       }
     }
     
     if ( valLow > FRACTAL_EMPTY_VALUE ) 
     {

       // First, cancel old resistance lines
       for(j = 0; j < MAX_NUM_LINES_REAL; j++)
       {
         if (lower_f[j] > valLow)
         {
           lower_f[j] = LINE_EMPTY_VALUE; // cancel the item
           lower_l[j] = LINE_EMPTY_VALUE;
           lower_s[j] = DATE_EMPTY_VALUE;
         }
       }
       
       bExistsAlready = false;
       for(j = 0; j < MAX_NUM_LINES_REAL; j++)
       {
          if ( lower_f[j] != LINE_EMPTY_VALUE )
          {
            if ( ((lower_l[j] - lineDelta) < body_min) 
               && ((lower_l[j] + lineDelta) > body_min) )
            {
              bExistsAlready = true;
              break;
            }
          }
       }
       
       // if we have a new fractal down - insert a new s/r line
       if ( !bExistsAlready )
       {
         for(j = 0; j < MAX_NUM_LINES_REAL; j++)
         {
           if ( lower_f[j] == LINE_EMPTY_VALUE )
           {
             lower_f[j] = valLow;
             lower_l[j] = body_min;
             lower_s[j] = t;
             break;
           }
         }
       }
     }  

     j = (lineIndex-1)*4;
     resLine1[i]  = updateViewResistance(j, resLine1[i+1], Low[i]);
     resLine2[i]  = updateViewResistance(j+1, resLine2[i+1], Low[i]);
     resLine3[i]  = updateViewResistance(j+2, resLine3[i+1], Low[i]);
     resLine4[i]  = updateViewResistance(j+3, resLine4[i+1], Low[i]);
     
     suppLine1[i] = updateViewSupport(j, suppLine1[i+1], High[i]);
     suppLine2[i] = updateViewSupport(j+1, suppLine2[i+1], High[i]);
     suppLine3[i] = updateViewSupport(j+2, suppLine3[i+1], High[i]);
     suppLine4[i] = updateViewSupport(j+3, suppLine4[i+1], High[i]);

     i--;
   }   
   return(0);
}
//+------------------------------------------------------------------+

Comments