//+------------------------------------------------------------------+ //| ADZigZag.mq5 | //| AD Institutional Indicators | //+------------------------------------------------------------------+ // Renamed/rebranded copy of MetaQuotes' stock ZigZag.mq5 (Copyright 2000-2026, MetaQuotes Ltd., // https://www.mql5.com) - file, property, and identifier names only; the pivot-detection algorithm // itself (Depth/Deviation/Backstep scan, extreme tracking, final-selection state machine) is // untouched. Used by CExpertSignalAIBase as the ground-truth source for "is this bar a confirmed // ZigZag reversal" training labels (see AdvanceZigZagLabelState() in ExpertSignalAIBase.mqh) - // replacing the old hand-rolled fractal/deviation-%/ATR-trend-context approximation of a ZigZag // with the real, well-known indicator, at its own stock default settings (Depth=12, Deviation=5, // Backstep=3). #property copyright "AD Institutional Indicators" #property link "" #property version "1.00" #property indicator_chart_window #property indicator_buffers 3 #property indicator_plots 1 //--- plot ADZigZag #property indicator_label1 "ADZigZag" #property indicator_type1 DRAW_SECTION #property indicator_color1 clrRed #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- input parameters input int InpDepth = 12; // Depth input int InpDeviation = 5; // Deviation input int InpBackstep = 3; // Back Step //--- indicator buffers double ADZZBuffer[]; // main buffer double ADZZHighMapBuffer[]; // ZigZag high extremes (peaks) double ADZZLowMapBuffer[]; // ZigZag low extremes (bottoms) int ADZZExtRecalc = 3; // number of last extremes for recalculation enum EnADZZSearchMode { ADZZ_Extremum = 0, // searching for the first extremum ADZZ_Peak = 1, // searching for the next ZigZag peak ADZZ_Bottom = -1 // searching for the next ZigZag bottom }; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ void OnInit() { //--- indicator buffers mapping SetIndexBuffer(0, ADZZBuffer, INDICATOR_DATA); SetIndexBuffer(1, ADZZHighMapBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(2, ADZZLowMapBuffer, INDICATOR_CALCULATIONS); //--- set short name and digits string short_name = StringFormat("ADZigZag(%d,%d,%d)", InpDepth, InpDeviation, InpBackstep); IndicatorSetString(INDICATOR_SHORTNAME, short_name); PlotIndexSetString(0, PLOT_LABEL, short_name); IndicatorSetInteger(INDICATOR_DIGITS, _Digits); //--- set an empty value PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0); } //+------------------------------------------------------------------+ //| ADZigZag calculation | //+------------------------------------------------------------------+ 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[]) { if(rates_total < 100) return(0); //--- int i = 0; int start = 0, extreme_counter = 0, extreme_search = ADZZ_Extremum; int shift = 0, back = 0, last_high_pos = 0, last_low_pos = 0; double val = 0, res = 0; double curlow = 0, curhigh = 0, last_high = 0, last_low = 0; //--- initializing if(prev_calculated == 0) { ArrayInitialize(ADZZBuffer, 0.0); ArrayInitialize(ADZZHighMapBuffer, 0.0); ArrayInitialize(ADZZLowMapBuffer, 0.0); start = InpDepth; } //--- ADZigZag was already calculated before if(prev_calculated > 0) { i = rates_total - 1; //--- searching for the third extremum from the last uncompleted bar while(extreme_counter < ADZZExtRecalc && i > rates_total - 100) { res = ADZZBuffer[i]; if(res != 0.0) extreme_counter++; i--; } i++; start = i; //--- what type of extremum we search for if(ADZZLowMapBuffer[i] != 0.0) { curlow = ADZZLowMapBuffer[i]; extreme_search = ADZZ_Peak; } else { curhigh = ADZZHighMapBuffer[i]; extreme_search = ADZZ_Bottom; } //--- clear indicator values for(i = start + 1; i < rates_total && !IsStopped(); i++) { ADZZBuffer[i] = 0.0; ADZZLowMapBuffer[i] = 0.0; ADZZHighMapBuffer[i] = 0.0; } } //--- searching for high and low extremes for(shift = start; shift < rates_total && !IsStopped(); shift++) { //--- low val = low[Lowest(low, InpDepth, shift)]; if(val == last_low) val = 0.0; else { last_low = val; if((low[shift] - val) > InpDeviation * _Point) val = 0.0; else { for(back = 1; back <= InpBackstep; back++) { res = ADZZLowMapBuffer[shift - back]; if((res != 0) && (res > val)) ADZZLowMapBuffer[shift - back] = 0.0; } } } if(low[shift] == val) ADZZLowMapBuffer[shift] = val; else ADZZLowMapBuffer[shift] = 0.0; //--- high val = high[Highest(high, InpDepth, shift)]; if(val == last_high) val = 0.0; else { last_high = val; if((val - high[shift]) > InpDeviation * _Point) val = 0.0; else { for(back = 1; back <= InpBackstep; back++) { res = ADZZHighMapBuffer[shift - back]; if((res != 0) && (res < val)) ADZZHighMapBuffer[shift - back] = 0.0; } } } if(high[shift] == val) ADZZHighMapBuffer[shift] = val; else ADZZHighMapBuffer[shift] = 0.0; } //--- set last values if(extreme_search == 0) // undefined values { last_low = 0.0; last_high = 0.0; } else { last_low = curlow; last_high = curhigh; } //--- final selection of extreme points for ADZigZag for(shift = start; shift < rates_total && !IsStopped(); shift++) { res = 0.0; switch(extreme_search) { case ADZZ_Extremum: if(last_low == 0.0 && last_high == 0.0) { if(ADZZHighMapBuffer[shift] != 0) { last_high = high[shift]; last_high_pos = shift; extreme_search = ADZZ_Bottom; ADZZBuffer[shift] = last_high; res = 1; } if(ADZZLowMapBuffer[shift] != 0.0) { last_low = low[shift]; last_low_pos = shift; extreme_search = ADZZ_Peak; ADZZBuffer[shift] = last_low; res = 1; } } break; case ADZZ_Peak: if(ADZZLowMapBuffer[shift] != 0.0 && ADZZLowMapBuffer[shift] < last_low && ADZZHighMapBuffer[shift] == 0.0) { ADZZBuffer[last_low_pos] = 0.0; last_low_pos = shift; last_low = ADZZLowMapBuffer[shift]; ADZZBuffer[shift] = last_low; res = 1; } if(ADZZHighMapBuffer[shift] != 0.0 && ADZZLowMapBuffer[shift] == 0.0) { last_high = ADZZHighMapBuffer[shift]; last_high_pos = shift; ADZZBuffer[shift] = last_high; extreme_search = ADZZ_Bottom; res = 1; } break; case ADZZ_Bottom: if(ADZZHighMapBuffer[shift] != 0.0 && ADZZHighMapBuffer[shift] > last_high && ADZZLowMapBuffer[shift] == 0.0) { ADZZBuffer[last_high_pos] = 0.0; last_high_pos = shift; last_high = ADZZHighMapBuffer[shift]; ADZZBuffer[shift] = last_high; } if(ADZZLowMapBuffer[shift] != 0.0 && ADZZHighMapBuffer[shift] == 0.0) { last_low = ADZZLowMapBuffer[shift]; last_low_pos = shift; ADZZBuffer[shift] = last_low; extreme_search = ADZZ_Peak; } break; default: return(rates_total); } } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| Search for the index of the highest bar | //+------------------------------------------------------------------+ int Highest(const double &array[], const int depth, const int start) { if(start < 0) return(0); double max = array[start]; int index = start; //--- start searching for(int i = start - 1; i > start - depth && i >= 0; i--) { if(array[i] > max) { index = i; max = array[i]; } } //--- return index of the highest bar return(index); } //+------------------------------------------------------------------+ //| Search for the index of the lowest bar | //+------------------------------------------------------------------+ int Lowest(const double &array[], const int depth, const int start) { if(start < 0) return(0); double min = array[start]; int index = start; //--- start searching for(int i = start - 1; i > start - depth && i >= 0; i--) { if(array[i] < min) { index = i; min = array[i]; } } //--- return index of the lowest bar return(index); } //+------------------------------------------------------------------+