ZigZag Divergence: A Complete EA Strategy Guide

Learn how to combine the classic ZigZag indicator with divergence detection to build a reliable EA. This guide covers MQL4/MQL5 implementation, trade filter.

zigzag-divergence-a-complete-ea-strategy-guide

Introduction: Why Most Divergence EAs Fail

The first time I coded a divergence Expert Advisor, I thought I had struck gold. The backtest equity curve was a smooth 45-degree angle. Then came the forward test: a series of losing trades that wiped out three months of gains. The problem wasn't the concept of divergence—it was how I defined it.

Most retail traders and even many EA developers treat divergence as a binary signal: "the price made a lower low, the RSI made a higher low, buy!" In reality, divergence is a probabilistic edge, not a crystal ball. The ZigZag indicator offers a cleaner way to identify swing points than raw peak/trough detection, but it introduces its own latency and repainting issues. This article is about building a divergence EA that respects those limitations.

I have been writing MQL4 and MQL5 code for over seven years, and I have published several ZigZag-based tools on tradingbotmaker.com. The strategy I will walk through here is one I have refined through dozens of iterations. It is not a holy grail, but it is a framework you can trust.

Understanding ZigZag Divergence

What Is ZigZag Divergence?

Divergence occurs when the price makes a new swing high or low that is not confirmed by an oscillator (like RSI, MACD, or CCI). ZigZag divergence specifically uses the ZigZag indicator to define the swing points, then compares those price swings to corresponding oscillator swings. This removes the subjectivity of manual trendline drawing.

There are two main types:

  • Regular Bullish Divergence: Price makes a lower low (LL), but the oscillator makes a higher low (HL). Suggests upward reversal.
  • Regular Bearish Divergence: Price makes a higher high (HH), but the oscillator makes a lower high (LH). Suggests downward reversal.
  • Hidden Bullish Divergence: Price makes a higher low (HL), but the oscillator makes a lower low (LL). Suggests trend continuation upward.
  • Hidden Bearish Divergence: Price makes a lower high (LH), but the oscillator makes a higher high (HH). Suggests trend continuation downward.

For EAs, I recommend focusing on regular divergence because it is more reliable for reversal entries and easier to code without false signals.

The ZigZag Indicator: Friend and Foe

The built-in ZigZag in MetaTrader draws lines connecting swing highs and swing lows. Its three parameters—Depth, Deviation, and Backstep—control sensitivity. A Depth of 12 with Deviation 5 and Backstep 3 works well on H1, but you must tune these to your timeframe and instrument.

The biggest criticism of ZigZag is that it repaints: as new bars form, the last swing point can shift. This is a deal-breaker for many EAs because a signal that existed at bar close can disappear. However, if you code your EA to only use confirmed, closed swings (i.e., the last two fully-formed swings), repainting becomes manageable. Never trade the current forming swing.

Practical Implementation in MetaTrader (MQL4/MQL5)

Step 1: Detecting ZigZag Swing Points

You cannot rely on the built-in iZigZag() function for swing detection because it only returns the current value. Instead, you need to find the actual swing points by scanning price bars. Here is a robust MQL4 function that finds the last two swing highs and lows:

//+------------------------------------------------------------------+
//| Find ZigZag swing points (non-repainting, confirmed bars)       |
//+------------------------------------------------------------------+
void FindZigZagSwings(int depth, int deviation, int backstep,
                      double &swingHigh[], double &swingLow[])
   {
   // Use the custom ZigZag buffer from an indicator handle
   // For simplicity, we'll use price-based zigzag detection
   int bars = MathMin(500, Bars - depth);
   double high[], low[];
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   CopyHigh(_Symbol, _Period, 1, bars, high);
   CopyLow(_Symbol, _Period, 1, bars, low);

   // Simple swing detection: find local highs and lows
   int swingIdx = 0;
   for(int i = depth; i < bars - depth; i++)
     {
      bool isHigh = true, isLow = true;
      for(int j = 1; j <= depth; j++)
        {
         if(high[i] <= high[i - j] || high[i] <= high[i + j]) isHigh = false;
         if(low[i] >= low[i - j] || low[i] >= low[i + j]) isLow = false;
        }
      if(isHigh)
        {
         swingHigh[swingIdx] = high[i];
         swingIdx++;
        }
      if(isLow)
        {
         swingLow[swingIdx] = low[i];
         swingIdx++;
        }
     }
   }

This approach avoids repainting because it only uses fully formed bars. However, it is slower than using indicator buffers. For a production EA, you should attach a custom ZigZag indicator and read its buffer on each tick, then confirm the swing after a few bars.

Step 2: Oscillator Divergence Logic

I prefer RSI for divergence because its bounded range (0-100) makes comparisons straightforward. Here is the core divergence check:

//+------------------------------------------------------------------+
//| Check for regular bullish divergence                             |
//+------------------------------------------------------------------+
bool IsBullishDivergence(double &swingLowPrice[], double &swingLowRSI[])
   {
   // Need at least two swing lows
   if(ArraySize(swingLowPrice) < 2) return false;

   // Price made a lower low
   if(swingLowPrice[0] < swingLowPrice[1])
     {
     // RSI made a higher low
     if(swingLowRSI[0] > swingLowRSI[1])
        return true;
     }
   return false;
   }

For bearish divergence, the logic is inverted: higher high in price but lower high in RSI.

Step 3: EA Entry and Exit Rules

Here is the complete trade logic I use in my ZigZag divergence EA:

Rule Condition Action
Entry Bullish divergence detected + RSI < 40 (oversold) Buy at market
Entry Bearish divergence detected + RSI > 60 (overbought) Sell at market
Stop Loss Below/above the most recent swing point 1.5x ATR(14) from entry
Take Profit 2x risk-to-reward or opposite divergence Limit order
Filter ADX(14) < 25 (ranging market) Skip trade

The ADX filter prevents trading divergence in strong trends where reversals are less likely. You can adjust the threshold based on backtesting.

Step 4: Handling Repainting in MQL5

In MQL5, you can use indicator handles and CopyBuffer() to read the ZigZag values. To avoid repainting, only consider swing points that are at least 3 bars old. Here is a snippet:

//+------------------------------------------------------------------+
//| Get confirmed ZigZag swing (MQL5)                              |
//+------------------------------------------------------------------+
double GetConfirmedSwing(int handle, int shift)
   {
   double buffer[];
   ArraySetAsSeries(buffer, true);
   CopyBuffer(handle, 0, shift, 3, buffer);
   // Return the value only if it is non-zero and confirmed
   if(buffer[0] != 0 && buffer[1] == 0)
      return buffer[0];
   return 0;
   }

This reads three bars: the current, previous, and one before. If the current bar has a ZigZag point and the next bar has none, the point is confirmed. This is a simple but effective method.

Pros, Cons, and Risks

Advantages

  • Objective signal: No subjective trendline drawing. The EA defines swings algorithmically.
  • Works on any timeframe: From M15 to H4, divergence patterns appear consistently. Higher timeframes yield fewer but more reliable signals.
  • Combines well with other filters: Adding volume or volatility filters improves win rate significantly.

Disadvantages

  • Lag: You must wait for swing confirmation. On H1, this can mean 6-12 bars before entry. You will miss the first move.
  • Repainting risk: Even with careful coding, ZigZag can repaint if you do not enforce a confirmation delay. Always backtest with "every tick" mode to catch this.
  • Low frequency: On a single pair, you might get 1-2 signals per week. This is not a scalping EA.

Key Risks

The biggest risk is false divergence. In a strong trend, price can make multiple lower lows while RSI also makes lower lows—this is not divergence, but the EA might misidentify it if the swing detection is too tight. Always use a minimum price swing size (e.g., 0.5% of price) to filter noise. Also, never trade divergence against the daily trend without a strong confirmation.

Example Walkthrough: EURUSD H1 Trade

Let me walk you through a real trade from my forward test journal.

Setup: EURUSD, H1 timeframe, RSI(14), ZigZag(12,5,3).

  1. Bar 1 (14:00): Price makes a swing low at 1.0850. RSI reads 32.
  2. Bar 2 (18:00): Price makes a lower swing low at 1.0830. RSI reads 38. Bullish divergence detected.
  3. Bar 3 (19:00): The EA confirms the second swing is at least 3 bars old. RSI is still below 40. A buy market order is placed at 1.0835.
  4. Stop Loss: 1.0805 (30 pips, 1.5x ATR which was 20 pips).
  5. Take Profit: 1.0895 (60 pips, 2:1 risk-reward).
  6. Outcome: Price reaches 1.0900 within 12 hours. Profit: +60 pips.

This trade worked because the market was ranging (ADX was 22 at entry) and the RSI divergence was clear. In a trending market, the same setup would have likely failed.

Summary and Key Takeaways

  • ZigZag divergence is a reliable reversal signal when coded correctly, but it requires careful swing detection to avoid repainting.
  • Always use confirmed swing points (at least 2-3 bars old) and filter with an oscillator like RSI.
  • Add a trend filter (ADX or moving average) to avoid trading divergence against strong momentum.
  • Backtest on "every tick" mode and forward test on a demo account for at least 50 trades before going live.
  • Never risk more than 1% per trade. Divergence EAs have drawdowns of 15-20% even in good months.

Building this EA taught me that the best algorithmic strategies are not the most complex—they are the ones that respect market structure and avoid over-optimization. Start with the code above, test it on a few pairs, and tweak the parameters slowly. Your first version will lose money. Your tenth version might not.

Frequently Asked Questions

Does the ZigZag indicator repaint in MetaTrader?

Yes, the built-in ZigZag repaints because it recalculates swing points as new price data arrives. To use it in an EA, you must code your own swing detection or read indicator buffers with a confirmation delay of at least 2-3 bars. Never trade the current forming swing.

What is the best oscillator to use with ZigZag divergence?

RSI is the most popular because its bounded range (0

Want to build your own version?

Recreate similar entry logic, risk rules, and filters in TradingBotMaker—no MQL coding. Start free.

Community

Clap for the article and open comments only when you want to read them.

0 claps0 comments

Related articles