Automatic Support/Resistance indicator using ZigZag

To write an indicator drawing support and resistance lines you need a set of rules that a computer can follow to create those lines. This technique uses the ZigZag indicator to find potential S/R levels and then refines further by finding multiple points.

The code here is a combination for MT4 and MT5. Most code is the same and I show where there are differences.

Start with the input statements, these are common for MT4 and MT5. The first block of inputs are just for the values that will be used by the ZigZag indicator.

//	Zigzag inputs
input int		InpDepth			=	12;			// Depth
input int		InpDeviation	=	5;				// Deviation
input int		InpBackstep		=	3;				// Backstep

Next capture inputs to set the gap between points where multiple points would be considered part of a level, the minimum number of points needed to set a level, and how many ZIgZag points to capture to draw S/R lines

// Peak analysis inputs
input int		InpGapPoints	=	100;			// Minimum gap between peaks in points
input int		InpSensitivity =	2;				//	Peak sensitivity
input int		InpLookback		=	50;			// Lookback

The last set of inputs are just used to set the common prefix name for the support and resistance line objects, colour and size of the lines

// Drawing inputs
input string	InpPrefix		=	"SRLevel_";	//	Object name prefix
input color		InpLineColour	=	clrYellow;	// Line colour
input	int		InpLineWeight	=	2;				//	Line weight

Then I define an array to hold the S/R line prices

// For the levels
double	SRLevels[];

and for MT5 create a handle for the ZigZag indicator and a buffer to hold the values form the ZigZag indicator.

// For the MT5 ZigZag indicator
double	Buffer[];
int		Handle;

In the OnInit section for MT5 initialise the indicator handle and set up the buffer to hold ZZ values.

int OnInit() {

 

//	Init the zz indicator
Handle	=	iCustom(Symbol(), Period(), "Examples\\ZigZag", InpDepth, InpDeviation, InpBackstep);
if (Handle==INVALID_HANDLE) {
Print("Could not create a handle to zigzag indicator");
return(INIT_FAILED);
}
ArraySetAsSeries(Buffer, true);

Also in the OnInit section, for both MT4 and MT5 add some code to clean up in case an earlier indicator has left SR lines showing on the chart and set up the SRLevels array to hold the maximum number of levels.

//	Clean up any sr levels left from earlier indicators
ObjectsDeleteAll(0, InpPrefix, 0, OBJ_HLINE);
ChartRedraw(0);
ArrayResize(SRLevels, InpLookback);
return(INIT_SUCCEEDED);
} // end OnInit

In OnDeInit for MT5 release the handle to the ZigZag indicator.

void OnDeinit(const int reason) {

 

IndicatorRelease(Handle);

and for both MT4 and MT5 again clean up the S/R level lines on exit

ObjectsDeleteAll(0, InpPrefix, 0, OBJ_HLINE);
ChartRedraw(0);
} // end OnDeInit

The OnCalculate function then is where most of the code happens. To begin for both MT4 and MT5 convert the gap size in points entered in the inputs into a price based gap, check that this is a new bar by comparing rates_total and prev_calculated (this indicator only processes on a new bar) and set up an array to hold peak values from the ZigZag indicator.

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[]) {
//	One time convert points to a price gap
static double	levelGap = InpGapPoints*SymbolInfoDouble(Symbol(), SYMBOL_POINT);
//	Only do this on a new bar
if (rates_total==prev_calculated)	return(rates_total);
//	Get the most recent <lookback> peaks
double	zz			=	0;
double	zzPeaks[];
int		zzCount	=	0;
ArrayResize(zzPeaks, InpLookback);
ArrayInitialize(zzPeaks, 0.0);

The following section gathers values from the ZigZag indicator and it is different between MT4 and MT5 due to the different way the products handle indicators. First for MT4 simply look through and use iCustom to get values from the indicator, bars that are not peaks will return zero or empty values

for (int i=1; i<rates_total && zzCount<InpLookback; i++) {
zz	=	iCustom(Symbol(), Period(), "ZigZag", InpDepth, InpDeviation, InpBackstep, 0, i);
if (zz!=0 && zz!=EMPTY_VALUE) {
zzPeaks[zzCount] = zz;
zzCount++;
}
}
ArraySort(zzPeaks);

For MT5 iCustom was set up in the initialisation and we only need to copy information from the indicator handle. The MT5 version also checks that the handle is ready and values are available and exits the loop early if not

int		count		=	CopyBuffer(Handle, 0, 0, rates_total, Buffer);
if (count<0) {
int err=GetLastError();
return(0);
}
for (int i=1; i<rates_total && zzCount<InpLookback; i++) {
zz	=	Buffer[i];
if (zz!=0 && zz!=EMPTY_VALUE) {
zzPeaks[zzCount] = zz;
zzCount++;
}
}
ArraySort(zzPeaks);

The final line in both of the above just places the peaks in ascending ordder to make finding peaks at the same level easier.

The remaining code is common to MT4 and MT5.

This code will loop through the ZigZag peaks array, matching prices within the range specified in the inputs and counting the number of peaks in each range. If enough peaks are found the average price is calculated and that price is set as a S/R level.

//	Search for groupings and set levels
int		srCounter	=	0;
double	price			=	0;
int		priceCount	=	0;
ArrayInitialize(SRLevels, 0.0);
for (int i=InpLookback-1; i>=0; i--) {
if (zzPeaks[i]>0) {
price	+=	zzPeaks[i];
priceCount++;
}
if (i==0 || (zzPeaks[i]-zzPeaks[i-1])>levelGap) {
if (priceCount>=InpSensitivity) {
price = price/priceCount;
SRLevels[srCounter] = price;
srCounter++;
}
price 		=	0;
priceCount	=	0;
}
}
DrawLevels();
//--- return value of prev_calculated for next call
return(rates_total);
} // end OnCalculate

The DrawLevels function called above will scan through the SRLevels array, move any existing lines to any changed prices, draw new lines where needed and clean up any extra lines.

void	DrawLevels() {
for (int i=0; i<InpLookback; i++) {
string name = InpPrefix + IntegerToString(i);
if (SRLevels[i]==0) {
ObjectDelete(0, name);
continue;
}
if (ObjectFind(0, name)<0) {
ObjectCreate(0, name, OBJ_HLINE, 0, 0, SRLevels[i]);
ObjectSetInteger(0, name, OBJPROP_COLOR, InpLineColour);
ObjectSetInteger(0, name, OBJPROP_WIDTH, InpLineWeight);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, true);
} else {
ObjectSetDouble(0, name, OBJPROP_PRICE, SRLevels[i]);
}
}
ChartRedraw(0);
} // end DrawLevels

6 Comments

  1. Error:
    ‘(‘ – unbalanced left parenthesis S&R with Zigzag.mq4 Line: 98 Column: 4
    empty controlled statement found S&R with Zigzag.mq4 Line: 98 Column: 27
    ‘)’ – unexpected token S&R with Zigzag.mq4 Line: 98 Column: 29
    expression has no effect S&R with Zigzag.mq4 Line: 98 Column: 28
    ‘else’ – illegal ‘else’ without matching ‘if’ S&R with Zigzag.mq4 Line: 104 Column:1

    // Zigzag inputs
    input int InpDepth = 12; // Depth
    input int InpDeviation = 5; // Deviation
    input int InpBackstep = 3; // Backstep
    // Peak analysis inputs
    input int InpGapPoints = 100; // Minimum gap between peaks in points
    input int InpSensitivity = 2; // Peak sensitivity
    input int InpLookback = 50; // Lookback
    // Drawing inputs
    input string InpPrefix = “SRLevel_”; // Object name prefix
    input color InpLineColour = clrYellow; // Line colour
    input int InpLineWeight = 2; // Line weight
    // For the levels
    double SRLevels[];
    int OnInit() {
    // Clean up any sr levels left from earlier indicators
    ObjectsDeleteAll(0, InpPrefix, 0, OBJ_HLINE);
    ChartRedraw(0);
    ArrayResize(SRLevels, InpLookback);

    return(INIT_SUCCEEDED);

    } // end OnInit
    void OnDeinit(const int reason) {
    ObjectsDeleteAll(0, InpPrefix, 0, OBJ_HLINE);
    ChartRedraw(0);

    } // end OnDeInit
    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[]) {

    // One time convert points to a price gap
    static double levelGap = InpGapPoints*SymbolInfoDouble(Symbol(), SYMBOL_POINT);

    // Only do this on a new bar
    if (rates_total==prev_calculated) return(rates_total);

    // Get the most recent <lookback> peaks
    double zz = 0;
    double zzPeaks[];
    int zzCount = 0;
    ArrayResize(zzPeaks, InpLookback);
    ArrayInitialize(zzPeaks, 0.0);
    for (int i=1; i<rates_total && zzCount<InpLookback; i++) {

    zz = iCustom(Symbol(), Period(), “ZigZag”, InpDepth, InpDeviation, InpBackstep, 0, i);
    if (zz!=0 && zz!=EMPTY_VALUE) {
    zzPeaks[zzCount] = zz;
    zzCount++;
    }

    }
    ArraySort(zzPeaks);
    // Search for groupings and set levels
    int srCounter = 0;
    double price = 0;
    int priceCount = 0;
    ArrayInitialize(SRLevels, 0.0);
    for (int i=InpLookback-1; i>=0; i–) {
    if (zzPeaks[i]>0) {
    price += zzPeaks[i];
    priceCount++;
    }
    if (i==0 || (zzPeaks[i]-zzPeaks[i-1])>levelGap) {
    if (priceCount>=InpSensitivity) {
    price = price/priceCount;
    SRLevels[srCounter] = price;
    srCounter++;
    }
    price = 0;
    priceCount = 0;
    }
    }
    DrawLevels();

    //— return value of prev_calculated for next call
    return(rates_total);

    } // end OnCalculate
    void DrawLevels() {

    for (int i=0; i<InpLookback; i++) {
    string name = InpPrefix + IntegerToString(i);

    if (SRLevels[i]==0) {
    ObjectDelete(0, name);
    continue;
    }

    if (ObjectFind(0, name)<0) {
    ObjectCreate(0, name, OBJ_HLINE, 0, 0, SRLevels[i]);
    ObjectSetInteger(0, name, OBJPROP_COLOR, InpLineColour);
    ObjectSetInteger(0, name, OBJPROP_WIDTH, InpLineWeight);
    ObjectSetInteger(0, name, OBJPROP_SELECTABLE, true);
    }
    else {
    ObjectSetDouble(0, name, OBJPROP_PRICE, SRLevels[i]);
    }

    }

    ChartRedraw(0);

    } // end DrawLevels

    1. That’s a problem we get at times. The code formatter decides to change the & < and > characters to url encoded and if you just copy and paste you get & < and > which won’t work at all in your code.
      I fixed it on this page for now, but the problem seems to come back without warning. You can either copy again or edit your code and fix the characters.
      BTW you do need to add statements like #property indicator_chart_window and #property strict

  2. // Zigzag inputs
    input int InpDepth = 12; // Depth
    input int InpDeviation = 5; // Deviation
    input int InpBackstep = 3; // Backstep

    // Peak analysis inputs
    input int InpGapPoints = 100; // Minimum gap between peaks in points
    input int InpSensitivity = 2; // Peak sensitivity
    input int InpLookback = 50; // Lookback

    // Drawing inputs
    input string InpPrefix = “SRLevel_”; // Object name prefix
    input color InpLineColour = clrYellow; // Line colour
    input int InpLineWeight = 2; // Line weight

    // For the levels
    double SRLevels[];

    int OnInit() {
    // Clean up any sr levels left from earlier indicators
    ObjectsDeleteAll(0, InpPrefix, 0, OBJ_HLINE);
    ChartRedraw(0);
    ArrayResize(SRLevels, InpLookback);
    return(INIT_SUCCEEDED);
    } // end OnInit

    void OnDeinit(const int reason) {
    ObjectsDeleteAll(0, InpPrefix, 0, OBJ_HLINE);
    ChartRedraw(0);
    } // end OnDeInit

    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[]) {
    // One time convert points to a price gap
    static double levelGap = InpGapPoints*SymbolInfoDouble(Symbol(), SYMBOL_POINT);
    // Only do this on a new bar
    if (rates_total==prev_calculated) return(rates_total);
    // Get the most recent <lookback> peaks
    double zz = 0;
    double zzPeaks[];
    int zzCount = 0;
    ArrayResize(zzPeaks, InpLookback);
    ArrayInitialize(zzPeaks, 0.0);

    for (int i=1; i<rates_total && zzCount=0; i–) {
    if (zzPeaks[i]>0)
    price += zzPeaks[i];
    priceCount++;
    }
    if (i==0 || (zzPeaks[i]-zzPeaks[i-1])>levelGap) {
    if (priceCount>=InpSensitivity) {
    price = price/priceCount;
    SRLevels[srCounter] = price;
    srCounter++;
    }
    price = 0;
    priceCount = 0;
    }

    DrawLevels();
    return(rates_total);
    } // end OnCalculate

    void DrawLevels() {
    for (int i=0; i<InpLookback; i++) {
    string name = InpPrefix + IntegerToString(i);
    if (SRLevels[i]==0) {
    ObjectDelete(0, name);
    continue;
    }
    if (ObjectFind(0, name)<0) {
    ObjectCreate(0, name, OBJ_HLINE, 0, 0, SRLevels[i]);
    ObjectSetInteger(0, name, OBJPROP_COLOR, InpLineColour);
    ObjectSetInteger(0, name, OBJPROP_WIDTH, InpLineWeight);
    ObjectSetInteger(0, name, OBJPROP_SELECTABLE, true);
    } else {
    ObjectSetDouble(0, name, OBJPROP_PRICE, SRLevels[i]);
    }
    }
    ChartRedraw(0);
    } // end DrawLevels

  3. Hi your Mt4 code not work
    // Zigzag inputs
    input int InpDepth = 12; // Depth
    input int InpDeviation = 5; // Deviation
    input int InpBackstep = 3; // Backstep

    // Peak analysis inputs
    input int InpGapPoints = 100; // Minimum gap between peaks in points
    input int InpSensitivity = 2; // Peak sensitivity
    input int InpLookback = 50; // Lookback

    // Drawing inputs
    input string InpPrefix = “SRLevel_”; // Object name prefix
    input color InpLineColour = clrYellow; // Line colour
    input int InpLineWeight = 2; // Line weight

    // For the levels
    double SRLevels[];

    int OnInit() {
    // Clean up any sr levels left from earlier indicators
    ObjectsDeleteAll(0, InpPrefix, 0, OBJ_HLINE);
    ChartRedraw(0);
    ArrayResize(SRLevels, InpLookback);
    return(INIT_SUCCEEDED);
    } // end OnInit

    void OnDeinit(const int reason) {
    ObjectsDeleteAll(0, InpPrefix, 0, OBJ_HLINE);
    ChartRedraw(0);
    } // end OnDeInit

    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[]) {
    // One time convert points to a price gap
    static double levelGap = InpGapPoints*SymbolInfoDouble(Symbol(), SYMBOL_POINT);
    // Only do this on a new bar
    if (rates_total==prev_calculated) return(rates_total);
    // Get the most recent <lookback> peaks
    double zz = 0;
    double zzPeaks[];
    int zzCount = 0;
    ArrayResize(zzPeaks, InpLookback);
    ArrayInitialize(zzPeaks, 0.0);

    for (int i=1; i<rates_total && zzCount=0; i–) {
    if (zzPeaks[i]>0)
    price += zzPeaks[i];
    priceCount++;
    }
    if (i==0 || (zzPeaks[i]-zzPeaks[i-1])>levelGap) {
    if (priceCount>=InpSensitivity) {
    price = price/priceCount;
    SRLevels[srCounter] = price;
    srCounter++;
    }
    price = 0;
    priceCount = 0;
    }

    DrawLevels();
    return(rates_total);
    } // end OnCalculate

    void DrawLevels() {
    for (int i=0; i<InpLookback; i++) {
    string name = InpPrefix + IntegerToString(i);
    if (SRLevels[i]==0) {
    ObjectDelete(0, name);
    continue;
    }
    if (ObjectFind(0, name)<0) {
    ObjectCreate(0, name, OBJ_HLINE, 0, 0, SRLevels[i]);
    ObjectSetInteger(0, name, OBJPROP_COLOR, InpLineColour);
    ObjectSetInteger(0, name, OBJPROP_WIDTH, InpLineWeight);
    ObjectSetInteger(0, name, OBJPROP_SELECTABLE, true);
    } else {
    ObjectSetDouble(0, name, OBJPROP_PRICE, SRLevels[i]);
    }
    }
    ChartRedraw(0);
    } // end DrawLevels

    1. My code works. You are missing some code like

      // Search for groupings and set levels
      int srCounter = 0;
      double price = 0;
      int priceCount = 0;
      ArrayInitialize(SRLevels, 0.0);

Leave a Reply