Overview
The Volume Divergence Markers indicator is a technical analysis tool designed to identify specific structural shifts in market behavior by monitoring the relationship between price movement (bar size) and trading activity (volume). Instead of simply looking at raw numbers, this script analyzes "dynamics"—specifically, how consistently one metric grows or shrinks relative to another over a set period.
How the Logic Works
The indicator functions by scanning historical price data to detect two specific types of momentum:
1. Bar Size Dynamics (Price Movement)
The tool first looks at the physical size of the price candles (the distance between the opening and closing prices). It calculates whether these candles have been consistently increasing or decreasing in size over a consecutive sequence of bars. This helps the indicator understand the "expansion" or "contraction" of price volatility.
2. Volume Dynamics (Trading Activity)
Simultaneously, the script monitors trading volume to see if it is consistently rising or falling. It looks for a continuous trend where each new bar shows a lower (or higher) volume than the one preceding it.
The Core Signal: Divergence and Convergence
The indicator generates a visual marker on your chart when two specific conditions are met at the same time:
- Price Strength vs. Volume Weakness: It identifies moments where price candles have been consistently expanding in size, while the trading volume has been consistently shrinking (or vice-versa).
- Threshold Matching: You can customize how "persistent" these trends must be. For example, if you set your global parameters to 2, the indicator will only trigger a marker if the price movement and the volume trend have both persisted for at least two consecutive bars.
Visual Output
When the mathematical requirements for these dynamics are satisfied, the indicator places a specific marker (an arrow) on the chart. This allows traders to visually pinpoint areas where there may be a disconnect between price action and the underlying volume—potentially signaling a shift in market sentiment or an exhaustion of momentum.
Key Settings Explained
- Mode: Determines which direction of divergence the user is looking for (e.g., whether you want to see price rising on falling volume, or vice-versa).
- Global Volumes/Sizes: These act as the "sensitivity" filters. Higher numbers require longer consecutive trends to occur before a marker appears, making the signals less frequent but potentially more significant.
- ThisBar: A toggle that allows you to decide whether the indicator should include the candle currently forming in real-time or only look at completed historical bars.
//+------------------------------------------------------------------+
//| Volume Divergence Markers.mq4 |
//| Vitaliy Samara |
//| https://www.mql5.com/en/users/samaridze |
//+------------------------------------------------------------------+
#property copyright "Vitaliy Samara"
#property link "https://www.mql5.com/en/users/samaridze"
#property version "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 1
//------
enum mode
{
Convergence = 0,
Divergence = 1,
};
//------
input bool ThisBar = 0; //Is current bar counted?
input mode Mode = 0; //Marker display mode
extern int VolGlobal = 2; //Continuous change in volume (in bars)
extern int SizeGlobal = 2; //Continuous change in barsize (in bars)
input color main=clrRoyalBlue; //Marker colour
//------
double BufferMarker[];
//------
int OnInit()
{
SetIndexBuffer(0,BufferMarker);
//------
SetIndexStyle(0,DRAW_ARROW,EMPTY,4,main);
//------
SetIndexArrow(0,119);
//------
SetIndexEmptyValue(0,0);
//------
IndicatorShortName("VDM "+VolGlobal+","+SizeGlobal);
//------ Values above 4 are pointless because it never happens on a market
if(VolGlobal>4) VolGlobal=4;
if(SizeGlobal>4) SizeGlobal=4;
return(INIT_SUCCEEDED);
}
//------
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[])
{
//------ Reversing timeseries to make Series[0] the earliest
ArraySetAsSeries(BufferMarker,false);
ArraySetAsSeries(High,false);
ArraySetAsSeries(Low,false);
ArraySetAsSeries(Open,false);
ArraySetAsSeries(Close,false);
ArraySetAsSeries(Volume,false);
//------ Initializing variables
int bar;
int vol,barsize;
bool dir;
vol=barsize=dir=0;
//------ Checking for sufficient bars in history
if(rates_total<MathMax(VolGlobal*2,SizeGlobal*2))
{
return (0);
}
//------ Setting the starting bar to calculate from
if(prev_calculated==0)
bar=MathMax(VolGlobal*2,SizeGlobal*2);
else
bar=prev_calculated-1;
//------ Main program cycle for each bar
while(bar<rates_total-!ThisBar)
{
BarSizeDynamics(bar,dir,barsize);
if(barsize>=SizeGlobal && VolumeDynamics(bar,dir)==true)
BufferMarker[bar]=Close[bar];
bar++;
}
//------
return(rates_total);
}
//------ Function that counts continuous increase or decrease of barsize (in bars)
//------ Need to return two values, so variables are passed by reference
void BarSizeDynamics(int bar,bool &dir,int &barsize)
{
int i=bar;
int up=0,down=0;
//------ Counting continuous increase in barsize
while(MathAbs(Open[i]-Close[i])>MathAbs(Open[i-1]-Close[i-1]))
{
up++;
i--;
}
//------ Counting continuous decrease in barsize
i=bar;
while(MathAbs(Open[i]-Close[i])<MathAbs(Open[i-1]-Close[i-1]))
{
down++;
i--;
}
//------ Selecting the correct movement and setting the value of direction variable (0 = increase, 1 = decrease)
barsize=MathMax(up,down);
if(up==0)
dir=Mode;
else
dir=!Mode;
}
//------ Function that counts continuous increase or decrease of volume (in bars)
bool VolumeDynamics(int bar,bool dir)
{
int i=bar;
int counter=0;
//------
if(dir==0)
while(Volume[i]<Volume[i-1])
{
counter++;
i--;
}
else
while(Volume[i]>Volume[i-1])
{
counter++;
i--;
}
//------ Both variables (barsize, vol) derived from these functions have to match user input globals
//------ Returning values as a boolean (to draw a marker or not) because no further calculations are required
if(counter==VolGlobal)
return true;
else
return false;
}
//+------------------------------------------------------------------+
Comments