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

Leave a Reply