Most traders treat the Accumulation/Distribution (A/D) line like that old toolbox in the garage—they know it's there, they've seen it on their charts, but they never actually open it. That's a shame, because the A/D indicator is one of the few volume-based tools that actually tells you something about intent, not just activity. And when you code it into an MQL5 Expert Advisor, you stop guessing and start measuring whether big money is quietly building a position or quietly exiting one.
In this post, I'm going to walk you through exactly how to implement the A/D indicator in MQL5, how to build an EA around accumulation and distribution phases, and—most importantly—how to code divergence signals that catch reversals before they appear on price. This isn't theory. I've traded these setups and I've made the mistakes so you don't have to.
Before we dive into the code, let me be clear about one thing: this EA won't turn you into a millionaire overnight. Anyone who promises that is selling something. What it will do is give you a systematic way to trade a concept that has real market logic behind it. The edge comes from discipline and proper risk management, not from magic indicators.
What the A/D Indicator Actually Measures
The Accumulation/Distribution line, developed by Marc Chaikin, doesn't just count volume. It weighs volume by where the close sits within the day's range. The core formula for each bar is:
Money Flow Multiplier = ((Close - Low) - (High - Close)) / (High - Low)
Money Flow Volume = Money Flow Multiplier * Volume
A/D = Previous A/D + Money Flow VolumeThink about what that means. When price closes near the high of the bar, the multiplier approaches +1, so that bar's volume gets added almost fully to the A/D line. When price closes near the low, the multiplier approaches -1, and volume subtracts from the line. A close dead in the middle adds almost nothing. This is fundamentally different from OnBalanceVolume (OBV), which simply adds or subtracts the full volume based on whether the close is higher or lower than the previous close.
Here's the practical difference: OBV treats a bar that closes 1 pip higher the same as a bar that closes 50 pips higher, as long as the volume is identical. A/D is more nuanced—it respects the position of the close. That's why I prefer A/D for detecting accumulation phases. A series of bars with strong closes on rising volume will push the A/D line up even if price is barely moving. That's accumulation. The reverse—price holding steady while A/D drops—is distribution.
One thing to keep in mind: on forex pairs, the "volume" in MetaTrader is tick volume, not actual exchange volume. There's no central exchange for forex, so we can't get true volume. But tick volume is a decent proxy—more ticks usually mean more trading activity. If you're trading futures or CFDs on instruments that report real volume, even better. The A/D formula works the same either way; just understand what your volume data actually represents.
A Note on Timeframes and Volume Interpretation
The A/D line behaves differently depending on your timeframe. On M1 and M5, tick volume can be erratic and the A/D line looks like a jagged mess. On H1 and above, it smooths out considerably. I've found the H4 chart gives the cleanest divergence signals for swing trading, while H1 works well for intraday momentum strategies. Daily charts produce rare but powerful signals—you might wait weeks for a good setup, but when it comes, it's usually worth the wait.
There's also a subtle point about how the A/D line interacts with gaps. On markets that gap (like indices or individual stocks), the open price matters. The A/D formula uses the close, low, and high of each bar, but it doesn't account for the gap between the previous close and the current open. Some traders adjust the formula to use the open instead of the previous close when calculating the multiplier. I've tested both versions and found the difference is negligible on forex but significant on gapping instruments. If you're coding this for stocks or indices, consider adding an input that switches between the standard and gap-adjusted formulas.
Why Divergence Matters for Your EA
Divergence between price and the A/D line is where the real edge lives. A bullish divergence forms when price makes a lower low but the A/D line makes a higher low. That tells you sellers are exhausting themselves—each new price low is accompanied by less selling pressure. Smart money is often absorbing those dips. A bearish divergence is the mirror image: price makes a higher high, but A/D makes a lower high, signaling buyers are losing conviction.
Most traders eyeball this on a chart. That works for discretionary trading, but an EA needs defined rules. You need to decide: how do you identify a swing high or low in the A/D line? How many bars must pass before you confirm a divergence? What's the minimum price difference to count as a "lower low"? These decisions make or break the EA.
Let me give you a concrete example of why this matters. Say you're looking at EURUSD on H1. Price makes a low at 1.0850, bounces, then drops again to 1.0845. The A/D line, however, made its low at the first price low and is now making a higher low. That's a classic bullish divergence. But how do you know the second price low is actually a swing low? You need a rule—maybe "price must close above the previous bar's high" or "the swing must be confirmed by N bars on each side." Without these rules, your EA will fire signals randomly and you'll have no idea why it's losing money.
Hidden Divergence vs. Regular Divergence
Most traders only look at regular divergence—where price makes a lower low but the indicator makes a higher low. But there's another type worth coding: hidden divergence. In a hidden bullish divergence, price makes a higher low while the A/D line makes a lower low. This typically appears during pullbacks within an uptrend, suggesting the pullback is running out of steam and the trend is about to resume.
Hidden divergence is trickier to code because it requires you to establish a trend context first. You can't just look at two swing points in isolation—you need to know whether price is in an uptrend or downtrend. A simple moving average filter (say, price above the 200 EMA for bullish setups) works well. I've seen traders use hidden divergence as a continuation signal with good results, but it's not a standalone strategy. It needs to be combined with trend confirmation.
For the EA in this post, I'm focusing on regular divergence because it's the most straightforward to code and test. Once you have the basic framework working, you can extend it to hidden divergence by adding a trend filter. I'll mention where to add that in the code.
Building the A/D Indicator EA in MQL5
Let's get into the code. I'll show you a complete implementation that you can compile and test in the Strategy Tester. The EA will do three things: calculate the A/D line, identify swing points using a simple fractal method, and fire signals on divergence.
Before you start coding, open MetaEditor (F4 in MetaTrader 5) and create a new Expert Advisor. Name it something descriptive like "AD_Divergence_EA". The wizard will generate a basic template with the OnTick() function and the standard includes. You'll want to delete the generated OnTick() body and replace it with what I show below.
Step 1: Calculate A/D Without the Built-in Indicator
You could use iAD() in MQL5, but I prefer to calculate it manually. Why? Because the built-in indicator uses tick volume, and I sometimes want to swap in real volume or test with custom volume data. Also, calculating it yourself gives you full control over the buffer handling, which matters when you're detecting swings on the indicator line itself.
Here's the calculation function. I'm passing in the price arrays and volume array directly, which keeps the function independent and testable:
//+------------------------------------------------------------------+
//| Calculate A/D values into a dynamic array |
//+------------------------------------------------------------------+
bool CalculateAD(const int rates_total, const int prev_calculated,
const double &high[], const double &low[],
const double &close[], const long &volume[],
double &ad_buffer[])
{
int start = (prev_calculated > 0) ? prev_calculated - 1 : 1;
for(int i = start; i < rates_total; i++)
{
double range = high[i] - low[i];
if(range == 0)
{
ad_buffer[i] = ad_buffer[i-1];
continue;
}
double mfm = ((close[i] - low[i]) - (high[i] - close[i])) / range;
double mfv = mfm * (double)volume[i];
ad_buffer[i] = ad_buffer[i-1] + mfv;
}
return true;
}Notice the range == 0 check. In low-liquidity instruments or on illiquid timeframes, you'll get bars where high equals low. If you don't guard against division by zero, your EA throws a divide-by-zero error and the tester stops. That's a classic bug that eats hours of your time.
Also notice the start calculation. The prev_calculated parameter tells you how many bars were already processed in the previous tick. If it's greater than zero, you only need to recalculate from prev_calculated - 1 to pick up the new bar. This is a standard MQL5 optimization pattern—you don't want to recalculate the entire history on every tick, especially if you're running on a 10-year daily chart with thousands of bars.
Step 2: Detect Swing Highs and Lows on the A/D Line
You can't just compare adjacent bars to find divergences—you need to identify meaningful swings. The simplest reliable method is a fractal pattern: a swing high is a bar whose value is higher than the N bars on both sides. Same logic for swing lows. Here's the function:
//+------------------------------------------------------------------+
//| Check if index i is a swing high in ad_buffer |
//+------------------------------------------------------------------+
bool IsSwingHigh(const double &ad_buffer[], int i, int strength)
{
for(int j = 1; j <= strength; j++)
{
if(ad_buffer[i] <= ad_buffer[i-j]) return false;
if(ad_buffer[i] <= ad_buffer[i+j]) return false;
}
return true;
}
bool IsSwingLow(const double &ad_buffer[], int i, int strength)
{
for(int j = 1; j <= strength; j++)
{
if(ad_buffer[i] >= ad_buffer[i-j]) return false;
if(ad_buffer[i] >= ad_buffer[i+j]) return false;
}
return true;
}A strength of 2 means the bar must be higher than two bars on each side. That's usually enough to filter noise on the daily timeframe. On lower timeframes, you'll want strength of 3 or 4, otherwise you'll get divergence signals on every minor wiggle and the EA will overtrade.
One thing to watch out for: these functions assume you're passing valid indices. If i - strength or i + strength goes out of bounds, you'll get an array-out-of-range error. In the calling code, make sure you loop from strength to rates_total - strength - 1. I'll show that in the divergence detection function below.
There's also the question of what to do with equal values. My implementation uses <= and >=, which means if two adjacent bars have exactly the same value, neither counts as a swing point. That's the safe choice—it avoids ambiguity. Some traders prefer to use strict inequality (< and >) which would allow one of the equal bars to count. I've found that equal values are rare in A/D because the line is cumulative, but they can happen on low-volume instruments. The strict version is fine too; just be consistent.
Step 3: Detect Divergence
Now the core logic. A bullish divergence requires a lower low in price but a higher low in A/D. The tricky part is matching the correct swing points. You need the most recent confirmed swing low in price and the most recent confirmed swing low in A/D, then compare them. Here's the function:
//+------------------------------------------------------------------+
//| Detect bullish divergence on the last confirmed swings |
//+------------------------------------------------------------------+
bool CheckBullishDivergence(const double &price_lows[], const double &ad_lows[],
int last_bar, int strength)
{
// Find last two swing lows in price
int p1 = -1, p2 = -1;
for(int i = last_bar - strength; i > strength; i--)
{
if(IsSwingLow(price_lows, i, strength))
{
if(p1 == -1) p1 = i;
else { p2 = i; break; }
}
}
if(p1 == -1 || p2 == -1) return false;
// Find last two swing lows in A/D
int a1 = -1, a2 = -1;
for(int i = last_bar - strength; i > strength; i--)
{
if(IsSwingLow(ad_lows, i, strength))
{
if(a1 == -1) a1 = i;
else { a2 = i; break; }
}
}
if(a1 == -1 || a2 == -1) return false;
// Price made lower low, A/D made higher low
if(price_lows[p1] < price_lows[p2] && ad_lows[a1] > ad_lows[a2])
return true;
return false;
}This is a simplified version, but it works. The real challenge in production is handling the case where the price swing and A/D swing don't align on the same bars. In practice, you'll want to search for the nearest A/D swing within a window around each price swing. I've left that out here for clarity, but it's worth adding if you're serious about reducing false signals.
Let me explain the misalignment issue with an example. Suppose price makes a swing low at bar 100, then the A/D line makes its swing low at bar 102. If you're comparing the price swing at bar 100 to the A/D swing at bar 102, you're comparing two different points in time. That might still be a valid divergence—sometimes the indicator lags price by a bar or two—but you need to decide how much lag is acceptable. I typically allow a window of 3-5 bars. If the A/D swing is more than 5 bars away from the price swing, I ignore it.
To implement this window, you'd modify the function to search for the A/D swing within a range around each price swing index, rather than just taking the most recent one. It's a bit more code, but it significantly reduces false signals. I'll show a version of this in the "Advanced Enhancements" section below.
Step 4: EA Input Parameters
Here's the input set I use. These are the parameters you'll tweak endlessly in the Strategy Tester, so make them all inputs rather than hardcoding them.
| Parameter | Type | Default | Description |
|---|---|---|---|
| SwingStrength</td |






