//+------------------------------------------------------------------+ //| Zigzag Color Oscillator.mq5 | //| Copyright 2000-2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2000-2025, MetaQuotes Ltd. // advancements by phade 2025" #property link "https://www.mql5.com" #property version "1.02" //--- indicator settings #property indicator_separate_window #property indicator_buffers 10 #property indicator_plots 2 #property indicator_type1 DRAW_COLOR_ZIGZAG #property indicator_color1 clrDodgerBlue, clrRed, clrGray #property indicator_width1 2 #property indicator_type2 DRAW_COLOR_CANDLES #property indicator_color2 clrDodgerBlue, clrRed, clrGray #property indicator_width2 1 enum EnZigZagStyle { Oscillator, HighLow }; enum EnPriceType { PR_OPEN, // Open PR_CLOSE, // Close PR_HIGH, // High PR_LOW, // Low PR_WEIGHTED, // Weighted PR_TYPICAL // Typical }; //--- input parameters input int InpDepth =12; // Depth input int InpDeviation=5; // Deviation input int InpBackstep =3; // Back Step input EnZigZagStyle zzStyle = HighLow; // Type of ZigZag input bool useFibLevels = true; // Fibonacci Retracement Levels input bool trackCurrentBar = true; // Track current price input EnPriceType priceType = PR_OPEN; // Current price tracking input long vol = 60; // Volume threshold (for unconfirmed leg color) input double scaling_offset = 0.2; // Window scaling offset //--- indicator buffers double ZigzagPeakBuffer[]; double ZigzagBottomBuffer[]; double HighMapBuffer[]; double LowMapBuffer[]; double ColorBuffer[]; double opens[], closes[], highs[], lows[], candleColor[]; int ExtRecalc=3; // recounting's depth enum EnSearchMode { Extremum=0, // searching for the first extremum Peak=1, // searching for the next ZigZag peak Bottom=-1 // searching for the next ZigZag bottom }; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ void OnInit() { //--- indicator buffers mapping SetIndexBuffer(0,ZigzagPeakBuffer,INDICATOR_DATA); SetIndexBuffer(1,ZigzagBottomBuffer,INDICATOR_DATA); SetIndexBuffer(2,ColorBuffer,INDICATOR_COLOR_INDEX); SetIndexBuffer(3,opens,INDICATOR_DATA); SetIndexBuffer(4,closes,INDICATOR_DATA); SetIndexBuffer(5,highs,INDICATOR_DATA); SetIndexBuffer(6,lows,INDICATOR_DATA); SetIndexBuffer(7,candleColor,INDICATOR_COLOR_INDEX); SetIndexBuffer(8,HighMapBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(9,LowMapBuffer,INDICATOR_CALCULATIONS); //--- set accuracy IndicatorSetInteger(INDICATOR_DIGITS,_Digits); //--- name for DataWindow and indicator subwindow label string short_name=StringFormat("ZigZag Oscillator (%d,%d,%d)",InpDepth,InpDeviation,InpBackstep); IndicatorSetString(INDICATOR_SHORTNAME,short_name); PlotIndexSetString(0,PLOT_LABEL,short_name); //--- set an empty value PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0); if(useFibLevels && zzStyle == HighLow){ IndicatorSetInteger(INDICATOR_LEVELS, 7); IndicatorSetInteger(INDICATOR_LEVELCOLOR, 0, clrGray); IndicatorSetInteger(INDICATOR_LEVELCOLOR, 1, clrGray); IndicatorSetInteger(INDICATOR_LEVELCOLOR, 2, clrGray); IndicatorSetInteger(INDICATOR_LEVELCOLOR, 3, clrGray); IndicatorSetInteger(INDICATOR_LEVELCOLOR, 4, clrGray); IndicatorSetInteger(INDICATOR_LEVELCOLOR, 5, clrGray); IndicatorSetInteger(INDICATOR_LEVELCOLOR, 6, clrGray); } else if(zzStyle == Oscillator){ IndicatorSetInteger(INDICATOR_LEVELS, 0); } if(!useFibLevels){ IndicatorSetInteger(INDICATOR_LEVELS, 0); } ChartNavigate(0, CHART_END); } //+------------------------------------------------------------------+ //| ZigZag 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[]) { int start = (prev_calculated > 0) ? prev_calculated - 1 : 1; if(zzStyle == HighLow){ for(int i = start; i close[i]) candleColor[i] = 1; else candleColor[i] = 2; } for(int i = rates_total-50; i>=0; i--){ opens[i] = EMPTY_VALUE; closes[i] = EMPTY_VALUE; highs[i] = EMPTY_VALUE; lows[i] = EMPTY_VALUE; } } else{ for(int i = 0; i 0) { i = rates_total - 1; while (extreme_counter < ExtRecalc && i > rates_total - 100) { res = (ZigzagPeakBuffer[i] + ZigzagBottomBuffer[i]); if (res != 0) extreme_counter++; i--; } i++; start = i; if (LowMapBuffer[i] != 0) { cur_low = LowMapBuffer[i]; extreme_search = Peak; } else { cur_high = HighMapBuffer[i]; extreme_search = Bottom; } // Clear indicator values for (i = start + 1; i < rates_total && !IsStopped(); i++) { ZigzagPeakBuffer[i] = 0.0; ZigzagBottomBuffer[i] = 0.0; LowMapBuffer[i] = 0.0; HighMapBuffer[i] = 0.0; } } // --- Searching for high and low extremes for (shift = start; shift < rates_total && !IsStopped(); shift++) { // --- Low val = 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 = InpBackstep; back >= 1; back--) { res = LowMapBuffer[shift - back]; if ((res != 0) && (res > val)) LowMapBuffer[shift - back] = 0.0; } } } if (low[shift] == val) LowMapBuffer[shift] = val; else LowMapBuffer[shift] = 0.0; // --- High val = 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 = InpBackstep; back >= 1; back--) { res = HighMapBuffer[shift - back]; if ((res != 0) && (res < val)) HighMapBuffer[shift - back] = 0.0; } } } if (high[shift] == val) HighMapBuffer[shift] = val; else HighMapBuffer[shift] = 0.0; } // --- Set last values if (extreme_search == 0) { last_low = 0; last_high = 0; } else { last_low = cur_low; last_high = cur_high; } static double bufMax = -DBL_MAX; static double bufMin = DBL_MAX; static double legStart = 0.0; static double legEnd = 0.0; double range = 0.0; double adaptiveScalingOffset = (ChartGetDouble(0, CHART_PRICE_MAX, 1) - ChartGetDouble(0, CHART_PRICE_MIN, 1)) * scaling_offset; double price_range = 0; double normalized_high = 0; double normalized_low = 0; // --- Move Zigzag line progressively for (shift = start; shift < rates_total && !IsStopped(); shift++) { res = 0.0; if(bufMax < ZigzagPeakBuffer[shift]) bufMax = ZigzagPeakBuffer[shift]; if(bufMin > ZigzagBottomBuffer[shift]) bufMin = ZigzagBottomBuffer[shift]; if(zzStyle == HighLow){ if (extreme_search == Peak){ if (LowMapBuffer[shift] != 0.0 && LowMapBuffer[shift] < last_low && HighMapBuffer[shift] == 0.0) { // New low found, update legStart and legEnd legStart = last_high; // Previous high legEnd = LowMapBuffer[shift]; // Current low if(useFibLevels) CalculateBullFibLevels(legStart, legEnd); double maxLevel = legStart + adaptiveScalingOffset; double minLevel = legEnd - adaptiveScalingOffset; IndicatorSetDouble(INDICATOR_MAXIMUM, MathMax(maxLevel, legStart)); // Prevent overshooting IndicatorSetDouble(INDICATOR_MINIMUM, MathMin(minLevel, legEnd)); // Prevent undershooting } if (HighMapBuffer[shift] != 0.0 && LowMapBuffer[shift] == 0.0) { // New high found, update legStart and legEnd legStart = last_low; // Previous low legEnd = HighMapBuffer[shift]; // Current high if(useFibLevels) CalculateBearFibLevels(legStart, legEnd); double maxLevel = legEnd + adaptiveScalingOffset; double minLevel = legStart - adaptiveScalingOffset; IndicatorSetDouble(INDICATOR_MAXIMUM, MathMax(maxLevel, legEnd)); // Prevent overshooting IndicatorSetDouble(INDICATOR_MINIMUM, MathMin(minLevel, legStart)); // Prevent undershooting } } else if (extreme_search == Bottom){ if (HighMapBuffer[shift] != 0.0 && HighMapBuffer[shift] > last_high && LowMapBuffer[shift] == 0.0) { // New high found, update legStart and legEnd legStart = last_low; // Previous low legEnd = HighMapBuffer[shift]; // Current high if(useFibLevels) CalculateBearFibLevels(legStart, legEnd); double maxLevel = legEnd + adaptiveScalingOffset; double minLevel = legStart - adaptiveScalingOffset; IndicatorSetDouble(INDICATOR_MAXIMUM, MathMax(maxLevel, legEnd)); // Prevent overshooting IndicatorSetDouble(INDICATOR_MINIMUM, MathMin(minLevel, legStart)); // Prevent undershooting } if (LowMapBuffer[shift] != 0.0 && HighMapBuffer[shift] == 0.0) { // New low found, update legStart and legEnd legStart = last_high; // Previous high legEnd = LowMapBuffer[shift]; // Current low if(useFibLevels) CalculateBullFibLevels(legStart, legEnd); double maxLevel = legStart + adaptiveScalingOffset; double minLevel = legEnd - adaptiveScalingOffset; IndicatorSetDouble(INDICATOR_MAXIMUM, MathMax(maxLevel, legStart)); // Prevent overshooting IndicatorSetDouble(INDICATOR_MINIMUM, MathMin(minLevel, legEnd)); // Prevent undershooting } } } switch (extreme_search) { case Extremum: if (last_low == 0 && last_high == 0) { if (HighMapBuffer[shift] != 0) { last_high = high[shift]; last_high_pos = shift; extreme_search = -1; ColorBuffer[shift] = 0; res = 1; } if (LowMapBuffer[shift] != 0) { last_low = low[shift]; last_low_pos = shift; extreme_search = 1; ColorBuffer[shift] = 1; res = 1; } } break; case Peak: if (LowMapBuffer[shift] != 0.0 && LowMapBuffer[shift] < last_low && HighMapBuffer[shift] == 0.0){ ZigzagBottomBuffer[last_low_pos] = 0.0; last_low_pos = shift; last_low = LowMapBuffer[shift]; if(zzStyle == Oscillator) ZigzagBottomBuffer[shift] = Point(); // defining the new low if(zzStyle == HighLow) ZigzagBottomBuffer[shift] = last_low; // defining the new low ColorBuffer[shift] = 1; res = 1; } if (HighMapBuffer[shift] != 0.0 && LowMapBuffer[shift] == 0.0){ last_high = HighMapBuffer[shift]; last_high_pos = shift; price_range = HighMapBuffer[shift] - LowMapBuffer[shift-1]; normalized_high = (HighMapBuffer[shift] - LowMapBuffer[shift-1]) / price_range; if(zzStyle == Oscillator) ZigzagPeakBuffer[shift] = normalized_high; // defining the new high if(zzStyle == HighLow) ZigzagPeakBuffer[shift] = last_high; // defining the new high ColorBuffer[shift] = 0; extreme_search = Bottom; res = 1; } break; case Bottom: if (HighMapBuffer[shift] != 0.0 && HighMapBuffer[shift] > last_high && LowMapBuffer[shift] == 0.0){ ZigzagPeakBuffer[last_high_pos] = 0.0; last_high_pos = shift; last_high = HighMapBuffer[shift]; price_range = HighMapBuffer[shift] - LowMapBuffer[shift-1]; normalized_high = (HighMapBuffer[shift] - LowMapBuffer[shift-1]) / price_range; if(zzStyle == Oscillator) ZigzagPeakBuffer[shift] = normalized_high; // defining the new high if(zzStyle == HighLow) ZigzagPeakBuffer[shift] = last_high; // defining the new high ColorBuffer[shift] = 0; } if (LowMapBuffer[shift] != 0.0 && HighMapBuffer[shift] == 0.0){ last_low = LowMapBuffer[shift]; // last low last_low_pos = shift; if(zzStyle == Oscillator) ZigzagBottomBuffer[shift] = Point(); // defining the new low if(zzStyle == HighLow) ZigzagBottomBuffer[shift] = last_low; // defining the new low ColorBuffer[shift] = 1; extreme_search = Peak; } break; default: return(rates_total); } } int current_bar_index = rates_total-1; if(trackCurrentBar && current_bar_index != 0){ if(zzStyle == HighLow){ // Current leg price tracking ZigzagPeakBuffer[current_bar_index] = GetPrice(priceType, current_bar_index, open, high, low, close); ZigzagBottomBuffer[current_bar_index] = GetPrice(priceType, current_bar_index, open, high, low, close); } if(zzStyle == Oscillator){ double pr_range = MathAbs(high[current_bar_index] - low[current_bar_index]); double normalized_range = (high[current_bar_index] - MathMin(low[current_bar_index], high[current_bar_index])) / pr_range; ZigzagPeakBuffer[current_bar_index] = normalized_range; ZigzagBottomBuffer[current_bar_index] = normalized_range; } long currentVolume = iVolume(_Symbol, 0, 0); long volumeThreshold = vol; for (shift = start; shift < rates_total && !IsStopped(); shift++) { if(shift == rates_total - 1){ if(extreme_search == Peak && close[shift] < open[shift]) ColorBuffer[shift] = 2; else if(extreme_search == Bottom && close[shift] > open[shift]) ColorBuffer[shift] = 2; // override color during high volume market conditions if(close[shift] > open[shift] && currentVolume >= volumeThreshold) ColorBuffer[shift] = 0; // force bullish color else if(close[shift] < open[shift] && currentVolume >= volumeThreshold) ColorBuffer[shift] = 1; // force bearish color } } } if(zzStyle == Oscillator){ IndicatorSetDouble(INDICATOR_MINIMUM, Point()-adaptiveScalingOffset); IndicatorSetDouble(INDICATOR_MAXIMUM, 1.0+adaptiveScalingOffset); } return rates_total; } //+------------------------------------------------------------------+ //| Return the selected price type for a given bar | //+------------------------------------------------------------------+ double GetPrice(EnPriceType type, int barIndex, const double &open[], const double &high[], const double &low[], const double &close[]) { switch(type) { case PR_OPEN: return open[barIndex]; case PR_CLOSE: return close[barIndex]; case PR_HIGH: return high[barIndex]; case PR_LOW: return low[barIndex]; case PR_WEIGHTED: return (high[barIndex] + low[barIndex] + close[barIndex] * 2.0) / 4.0; case PR_TYPICAL: return (high[barIndex] + low[barIndex] + close[barIndex]) / 3.0; default: return 0.0; } } //+------------------------------------------------------------------+ //| Get highest value for range | //+------------------------------------------------------------------+ double Highest(const double&array[],int count,int start) { double res=array[start]; //--- for(int i=start-1; i>start-count && i>=0; i--) if(resstart-count && i>=0; i--) if(res>array[i]) res=array[i]; //--- return(res); } //+------------------------------------------------------------------+ //| Calculate fibonacci levels for range | //+------------------------------------------------------------------+ void CalculateBearFibLevels(double start, double end) { double range = MathAbs(end - start); IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, start); IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, start + 0.236 * range); IndicatorSetDouble(INDICATOR_LEVELVALUE, 2, start + 0.382 * range); IndicatorSetDouble(INDICATOR_LEVELVALUE, 3, start + 0.5 * range); IndicatorSetDouble(INDICATOR_LEVELVALUE, 4, start + 0.618 * range); IndicatorSetDouble(INDICATOR_LEVELVALUE, 5, start + 0.764 * range); IndicatorSetDouble(INDICATOR_LEVELVALUE, 6, end); } //+------------------------------------------------------------------+ //| Calculate fibonacci levels for range | //+------------------------------------------------------------------+ void CalculateBullFibLevels(double start, double end) { double range = MathAbs(start - end); IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, end); IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, end + 0.236 * range); IndicatorSetDouble(INDICATOR_LEVELVALUE, 2, end + 0.382 * range); IndicatorSetDouble(INDICATOR_LEVELVALUE, 3, end + 0.5 * range); IndicatorSetDouble(INDICATOR_LEVELVALUE, 4, end + 0.618 * range); IndicatorSetDouble(INDICATOR_LEVELVALUE, 5, end + 0.764 * range); IndicatorSetDouble(INDICATOR_LEVELVALUE, 6, start); }