The setup
We will need :
- 1 zigzag plot
- 2 data buffers for the highs and the lows
- input parameters
- an ongoing set of system variables which reset whenever the indicator recalculates
#property indicator_buffers 2
#property indicator_plots 1
input double retracement=23.6;//retracement amount
input double minSizeInAtrUnits=0.0;//min size of waves in atr units
input int rollingAtrPeriod=14;//rolling atr period
input color Color=clrDodgerBlue;//wave color
input int Width=3;//wave width
input ENUM_LINE_STYLE Style=STYLE_SOLID;//wave style
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
//--- up waves and the downwaves
double upWaves[],dwWaves[];
The upWaves array will store the highs and the dwWaves array will store the lows
System variables :
we need to know the last wave type , where it started , where it ended , the distance in bars from the start and the end.
Then we need a local high and local low variable as well as distances in bars from each point.
//--- keeping track of the zigzag
//--- the type of wave we have [0] none [1] up [2] down
int wave_type=0;
//--- the price from of the wave (starting price)
double wave_start_price=0.0;
//--- the price to of the wave (ending price)
double wave_end_price=0.0;
//--- the distance in bars from the start price
int wave_start_distance=0;
//--- the distance in bars from the end price
int wave_end_distance=0;
//--- high price tracking
double high_mem=0.0;
int distance_from_high=0;
//--- low price tracking
double low_mem=0.0;
int distance_from_low=0;
//--- rolling atr
double rollingAtr=0.0;
int rollingAtrs=0;
Finally the rolling atr unit and how many have been calculated
We then create a system reset function :
void resetSystem(){
ArrayFill(upWaves,0,ArraySize(upWaves),0.0);
ArrayFill(dwWaves,0,ArraySize(dwWaves),0.0);
wave_type=0;
wave_start_price=0.0;
wave_end_price=0.0;
wave_start_distance=0;
wave_end_distance=0;
high_mem=0.0;
low_mem=0.0;
distance_from_high=0;
distance_from_low=0;
rollingAtr=0.0;
rollingAtrs=0;
}
Standard stuff , fill the arrays with zeroes and reset the system variables.
Oninit we setup the buffers , the plot , and we call reset for the first time :
SetIndexBuffer(0,upWaves,INDICATOR_DATA);
SetIndexBuffer(1,dwWaves,INDICATOR_DATA);
PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_ZIGZAG);
PlotIndexSetInteger(0,PLOT_LINE_COLOR,0,Color);
PlotIndexSetInteger(0,PLOT_LINE_WIDTH,Width);
PlotIndexSetInteger(0,PLOT_LINE_STYLE,Style);
resetSystem();
So let's jump right in the calculation.
The first thing we need to take care of is the rolling atr.
Until we have collected more bars that the atr period we will not do anything else.
The portion that manages the rolling atr is as follows :
- if we have not collected more than the period keep adding the range of the bars found to a summation
- once we hit the period we perform the first division (average)
- after that we clip one portion of the rolling atr off , which is atr/period , and then add a new portion which is range of bar / period
we place the last part first because it will happen more often and we wont need to be accessing 2 if statements
//--- manage the atr
rollingAtrs++;
if(rollingAtrs>rollingAtrPeriod){
double new_portion=((high[i]-low[i])/_Point)/((double)rollingAtrPeriod);
//--- we remove an old portion and add a new portion
rollingAtr=(rollingAtr)-(rollingAtr/((double)rollingAtrPeriod))+new_portion;
}
else if(rollingAtrs<=rollingAtrPeriod){
rollingAtr+=(high[i]-low[i])/_Point;
if(rollingAtrs==rollingAtrPeriod){
rollingAtr/=((double)rollingAtrs);
//--- start the memory for highs and lows and the system
high_mem=high[i];
low_mem=low[i];
distance_from_high=0;
distance_from_low=0;
}
}
Awesome , now , there is another issue.
The foundation of this zig zag is a retracement .
But for a retracement to occur there must be at least one wave.
But what will the first wave retrace ? xD
For that reason we will do the following :
- as soon as the atr fills up (atr collected = period) , we will grab the high and the low in our system variables
- whichever side manages to form a wave that has a valid size in atr units , and forms a new high (upwave) or a new low (down wave) wins
this way we dont have a retracement as an initial wave , but , we gotta start the sequence somehow.
Note we could have also opted in to have a classic fractal approach here for the first wave only and then continue onwards with retracements.
This is what we do as long as we don't have a wave :
//--- if we don't have a wave type yet
else{
//--- if we broke the high and not the low
if(high[i]>high_mem&&low[i]>=low_mem){
double new_wave_size_in_atr_units=((high[i]-low_mem)/_Point)/rollingAtr;
//--- if the new wave size is valid
if(new_wave_size_in_atr_units>=minSizeInAtrUnits){
//--- start a new up wave
wave_type=1;
//--- start price is the low mem
wave_start_price=low_mem;
wave_start_distance=distance_from_low;
//--- end price is the new high
wave_end_price=high[i];
wave_end_distance=0;
//--- draw the wave
dwWaves[i-wave_start_distance]=low_mem;
upWaves[i]=high[i];
//--- change the high
high_mem=high[i];
distance_from_high=0;
//--- change the low
low_mem=low[i];
distance_from_low=0;
}
}
//--- if we broke the low and not the high
else if(low[i]<low_mem&&high[i]<=high_mem){
double new_wave_size_in_atr_units=((high_mem-low[i])/_Point)/rollingAtr;
//--- if the new wave size is valid
if(new_wave_size_in_atr_units>=minSizeInAtrUnits){
//--- start a new down wave
wave_type=-1;
//--- start price is the high mem
wave_start_price=high_mem;
wave_start_distance=distance_from_high;
//--- end price is the new low
wave_end_price=low[i];
wave_end_distance=0;
//--- draw the wave
upWaves[i-wave_start_distance]=high_mem;
dwWaves[i]=low[i];
//--- change the high
high_mem=high[i];
distance_from_high=0;
//--- change the low
low_mem=low[i];
distance_from_low=0;
}
}
//--- if we broke both
else if(low[i]<low_mem&&high[i]>high_mem){
//--- change them
high_mem=high[i];
low_mem=low[i];
distance_from_high=0;
distance_from_low=0;
}
}
Great . Now the final piece.
- If we have an upwave :
- if a new high is made , move the zigzag from the previous high position to the new high position which we can do since we retain bar distances.Also update the low and the distance from the low.We do that so we can catch the lowest low since the peak and check if it retraces enough in order to start a new low
- if a new low is made , or , a new low is set , we calculate the distance from the peak to the low and divide it by the wave size . And also multiply it with 100 to match the input parameter scale.So if the wave size is 100 points and the retracement is 24 points we get 24/100 0.24 , then x 100 24% . If the size of the new "would be" wave that retraces the previous one is also valid against the atr units we start a new down wave, set the new local highs and lows , set the bar distances.
here is the relevant code for the above :
//--- if we have an up wave
if(wave_type==1){
//--- if the wave expands up
if(high[i]>wave_end_price){
//--- remove the previous end price from its array position (0.0=empty)
upWaves[i-wave_end_distance]=0.0;
//--- place it on the new position
upWaves[i]=high[i];
wave_end_price=high[i];
wave_end_distance=0;
//--- change the high
high_mem=high[i];
distance_from_high=0;
//--- change the low
low_mem=low[i];
distance_from_low=0;
}
//--- check for retracement
if(low[i]<low_mem||distance_from_low==0){
low_mem=low[i];
distance_from_low=0;
double size_of_wave=(wave_end_price-wave_start_price)/_Point;
double size_of_retracement=(wave_end_price-low_mem)/_Point;
if(size_of_wave>0.0){
double retraced=(size_of_retracement/size_of_wave)*100.0;
double new_wave_size_in_atr_units=((wave_end_price-low_mem)/_Point)/rollingAtr;
//--- if the new wave size is valid
if(new_wave_size_in_atr_units>=minSizeInAtrUnits){
//--- if the retracement is significant , start a down wave
if(retraced>=retracement){
//--- start a new down wave
wave_type=-1;
//--- start price is the high mem
wave_start_price=high[i-distance_from_high];
wave_start_distance=distance_from_high;
//--- end price is the new low
wave_end_price=low[i];
wave_end_distance=0;
//--- draw the wave
upWaves[i-wave_start_distance]=high_mem;
dwWaves[i]=low[i];
//--- change the high
high_mem=high[i];
distance_from_high=0;
//--- change the low
low_mem=low[i];
distance_from_low=0;
}
}
}
}
}
We do the opposite when we have a down wave.
And we are done , our retracement zig zag is ready.
Here is the zigzag with 23.6% retracement and 0.0 min size of waves in atr units
and here is the same zig zag with 3 min size of waves in atr units