Overview of the Volume Spread Analyzer
The Volume Spread Analyzer is a sophisticated technical tool designed to help traders identify market psychology by examining the relationship between trading volume (the amount of activity) and price spread (the range within which a price moves). Instead of simply looking at where the price is going, this script analyzes how much "effort" is being expended to move the price in a specific direction, helping users spot potential exhaustion, strength, or hidden weakness.
Core Analytical Logic
The script operates by comparing current market activity against historical averages. It evaluates each candle based on several key pillars:
1. Volume Categorization
The tool first establishes a "baseline" by calculating an exponential average of volume over a specific period. It then classifies every new candle into one of five categories based on how much its volume deviates from that average:
- Ultra-High Volume: Significant spikes indicating massive participation.
- Very High Volume: Stronger than usual activity.
- High Volume: Activity slightly above the norm.
- Low Volume: Notable lack of participation.
- Normal Volume: Standard market behavior.
2. Spread and Price Action Analysis
Simultaneously, the script analyzes the "Spread"—the distance between the high and low of a candle. By combining this with the volume category, the tool can differentiate between a price move caused by genuine conviction and a move caused by lack of liquidity.
Key Market Signals Identified
The logic is programmed to detect specific market scenarios and label them on your chart:
-
Exhaustion Points (Climax & Thrusts):
- Buying/Selling Climax: When price moves significantly but is accompanied by "Ultra-High" volume, it often suggests that the trend has reached a peak of exhaustion where all participants have entered the market, potentially leading to a reversal.
- Thrusts: Identifies instances where the price pushes into new territory with very high volume but fails to sustain the movement, often signaling a "down-thrust" or "up-thrust."
-
Trend Strength and Weakness:
- Possible End of Trend: Detects moments when the spread is shrinking (narrowing) and volume is falling, suggesting that neither buyers nor sellers are interested in continuing the current move.
- Lack of Demand/Selling: Identifies "weak" candles where the price moves against the trend but without any significant supporting volume, indicating a lack of conviction from the opposing side.
-
Pressure Indicators:
- Demand/Selling Pressure: Highlights active pressure where high volume confirms that aggressive participants are actively pushing the price in one direction.
- Hidden Buying/Selling: Identifies complex scenarios where "Ultra-High" volume occurs at extreme price levels, suggesting large institutional activity that may not be immediately obvious through price action alone.
Summary for the User
This tool is designed to be a decision-support system. It does not provide direct "Buy" or "Sell" commands but rather paints a picture of market health. By identifying where volume is high relative to the price spread, it helps you distinguish between a healthy, well-supported trend and a "hollow" move that is likely to fail, allowing for more informed entries and exits.
//+------------------------------------------------------------------+
//| Volume Spread Analyzer.mq4 |
//| Copyright © 2009, Obaidah & GodfreyH |
//| clefts@hotmail.com |
//| godfreyh@gmail.com |
//| |
//| The Indicator's main function aims at analyizing |
//|Volume and Spread in order to identify weakness and strength spots|
//| |
//| WARNING! |
//| Signs provided by this technical indicator cannot be considered |
//| As entry signals unless there's at least 1 more indicator shows |
//| The same signal, This indicator is well-designed to help you |
//| Deciding your entery points and your exit points. |
//| Please notice that this indicator uses the average for the last |
//| 13 bars, so, ANY signal in the first 13 bars |
//| Of any season open is NOT liable. |
//| |
//| This indicator is to remain open source for further developments |
//| The programmers are NOT responsible about any losing trades |
//| Taken based on this indicator, Do learn about volume and spread |
//| Analysis before you use this indicator seriuosly |
//| |
//| In order to know how does this indicator provides signal and how |
//| Strong such a signal is please read Master The Markets |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2009, Obaidah & GodfreyH"
#property link "clefts@hotmail.com"
#property link "godfreyh@gmail.com"
extern string aa="Calculate the average for the last";
extern int Lb=9;
extern double coefficient=0.25;
extern int Sleeptime = 30000;
double Vol[], Vol1[], LowVolume[], HighVolume[], VeryHighVolume[],UltraHighVolume[],VeryLowVolume[];
double Change[], ChangeAve[];
double BuySignal[],SellSignal[];
double VolAve[], LowVolume1[], HighVolume1[];
extern bool displayAlert = true;
static datetime lastAlertTime;
double PrevVol, PrevVol1, PrevVol2;
#property indicator_separate_window
#property indicator_minimum 0
#property indicator_buffers 8
#property indicator_color1 White
#property indicator_color2 -1
#property indicator_color3 -1
#property indicator_color4 ForestGreen
#property indicator_color5 Orange
#property indicator_color6 Orange
#property indicator_color7 Red
#property indicator_color8 BlueViolet
#property indicator_width4 2
#property indicator_width5 2
#property indicator_width6 2
#property indicator_width7 2
#property indicator_width8 2
// Custom indicator initialization function
int init()
{
IndicatorBuffers(8);
SetIndexStyle(0,DRAW_LINE);
SetIndexStyle(1,DRAW_LINE);
SetIndexStyle(2,DRAW_LINE);
SetIndexStyle(3,DRAW_HISTOGRAM);
SetIndexStyle(4,DRAW_HISTOGRAM);
SetIndexStyle(5,DRAW_HISTOGRAM);
SetIndexStyle(6,DRAW_HISTOGRAM);
SetIndexStyle(7,DRAW_HISTOGRAM);
SetIndexDrawBegin(0,Lb);
SetIndexDrawBegin(1,Lb);
SetIndexDrawBegin(2,Lb);
SetIndexDrawBegin(3,Lb);
SetIndexDrawBegin(4,Lb);
SetIndexDrawBegin(5,Lb);
SetIndexDrawBegin(6,Lb);
SetIndexDrawBegin(7,Lb);
SetIndexBuffer(0,VolAve);
SetIndexBuffer(1,LowVolume1);
SetIndexBuffer(2,HighVolume1);
SetIndexBuffer(3,Vol);
SetIndexBuffer(4,LowVolume);
SetIndexBuffer(5,HighVolume);
SetIndexBuffer(6,VeryHighVolume);
SetIndexBuffer(7,UltraHighVolume);
IndicatorDigits(0);
IndicatorShortName("Volume Spread Analyzer");
return(0);
}
// Expert deinitialization function
int deinit()
{
for(int i = ObjectsTotal() - 1; i >= 0; i--) {
string label = ObjectName(i);
if(StringSubstr(label, 0, 6) != "Signal")
continue;
ObjectDelete(label);
}
return(0);
}
// Expert start function
int start()
{
int countedBars = IndicatorCounted();
int limit;
if(countedBars > 0)
countedBars--;
limit=Bars-countedBars;
CalculateVolume(limit);
return(0);
}
void CalculateVolume(int limit)
{
int i,q;
for(i=0; i<limit; i++)
Vol[i]=Volume[i];
CalculateLimits(limit);
}
void CalculateLimits(int limit)
{
int i,j;
for(i=1; i<limit; i++) {
VolAve[i]=0;
Vol1[i]=0;
/*for(j=i+Lb+1; j>i+1; j--) // Calculate the volume average
VolAve[i]+=Volume[j];*/
//VolAve[i]=VolAve[i]/Lb;
VolAve[i]=iCustom(Symbol(),0,"Exponential Volume Average",Lb,0,i+1);
LowVolume1[i]=VolAve[i]*(1-coefficient);
HighVolume1[i]=VolAve[i]*(1+coefficient);
if(Vol[i] >= VolAve[i]*(1+coefficient*3)) { // Analyze Volume
UltraHighVolume[i]=Volume[i];
Vol[i]=0;
VeryHighVolume[i]=0;
HighVolume[i]=0;
LowVolume[i]=0;
}
else {
if(Vol[i] >= VolAve[i]*(1+coefficient*2)) {
VeryHighVolume[i]=Volume[i];
UltraHighVolume[i]=0;
HighVolume[i]=0;
LowVolume[i]=0;
Vol[i]=0;
}
else {
if(Vol[i] >= VolAve[i]*(1+coefficient)) {
VeryHighVolume[i]=0;
UltraHighVolume[i]=0;
HighVolume[i]=Volume[i];
LowVolume[i]=0;
Vol[i]=0;
}
else {
if(Vol[i] <= VolAve[i]*(1-coefficient)) {
VeryHighVolume[i]=0;
UltraHighVolume[i]=0;
HighVolume[i]=0;
LowVolume[i]=Volume[i];
Vol[i]=0;
}
else {
VeryHighVolume[i]=0;
UltraHighVolume[i]=0;
HighVolume[i]=0;
LowVolume[i]=0;
}
}
}
}
CalculateSignals(i);
}
}
void CalculateSignals(int i)
{
double SpreadSize=iCustom(Symbol(),0,"Spread Analyzer",Lb,1,i);
double SpreadSize1=iCustom(Symbol(),0,"Spread Analyzer",Lb,1,i+1);
// double ChangeSize=iCustom(Symbol(),0,"Spread Analyzer",Lb,4,i);
double BarType=iCustom(Symbol(),0,"Spread Analyzer",Lb,5,i);
double ClosePart=iCustom(Symbol(),0,"Spread Analyzer",Lb,6,i);
double BarType1=iCustom(Symbol(),0,"Spread Analyzer",Lb,5,i+1);
double BarType2=iCustom(Symbol(),0,"Spread Analyzer",Lb,5,i+2);
PrevVol=Volume[i+1];
PrevVol1=Volume[i+2];
PrevVol2=Volume[i+3];
double Highest1=High[i+1], Lowest1=Low[i+1], Highest11=Low[i], Lowest11=High[i];
for(int w=i+24;w>i;w--) {
if(High[w] >= Highest1)
Highest1=High[w];
if(Low[w] <= Lowest1)
Lowest1=Low[w];
}
for(w=i+8;w>i;w--) {
if(High[w] >= Highest11)
Highest11=High[w];
if(Low[w] <= Lowest11)
Lowest11=Low[w];
}
if(High[i]== High[i]) {
if((VeryHighVolume[i] != 0 || UltraHighVolume[i] != 0) && SpreadSize >= 0 && SpreadSize < 3) { // Thrusts
if(ClosePart < 1) {
if(BarType == 1 && BarType1 == 1) {
if(High[i] >= Highest11) {
Draw(Time[i], High[i]+20*Point, Red, STYLE_SOLID, 1, "Down-Thrust",i);
if(displayAlert == true)
DisplayAlert("Down Thrust Appears On: ", i);
Sleep(Sleeptime);
}
}
else {
if(BarType== 2 && BarType1 == 2) {
if(Low[i] <= Lowest11) {
Draw(Time[i], Low[i]-5*Point, LimeGreen, STYLE_SOLID, 2, "Up-Thrust",i);
if(displayAlert == true)
DisplayAlert("Up Thrust Appears On: ", i);
Sleep(Sleeptime);
}
}
}
}
}
if(VeryHighVolume[i] != 0 || UltraHighVolume[i] != 0) { // Buying/Selling Climax
if(SpreadSize >= 2) {
if(ClosePart <= 1) {
if(BarType == 1) {
if(High[i] >= Highest1) {
Draw(Time[i], High[i]+20*Point, Red, STYLE_SOLID, 1, "Buying Climax",i);
if(displayAlert == true)
DisplayAlert("Buying Climax Appears On: ", i);
Sleep(Sleeptime);
}
}
else {
if(Low[i] <= Lowest1) {
Draw(Time[i], Low[i]-5*Point, LimeGreen, STYLE_SOLID, 2, "Selling Climax",i);
if(displayAlert == true)
DisplayAlert("Selling Climax Appears On: ", i);
Sleep(Sleeptime);
}
}
}
}
}
else {
if(LowVolume[i] == 0 && Vol[i]==0) { // Possible End Of The Trend
if(SpreadSize < 0) {
if(BarType == 1) {
if(High[i] >= Highest1) {
Draw(Time[i], High[i]+20*Point, Red, STYLE_SOLID, 1, "Possible End Of Trend",i);
if(displayAlert == true)
DisplayAlert("Possible End of Trend Appears On: ", i);
Sleep(Sleeptime);
}
}
else {
if(Low[i] <= Lowest1) {
Draw(Time[i], Low[i]-5*Point, LimeGreen, STYLE_SOLID, 2, "Possible End Of Trend",i);
if(displayAlert == true)
DisplayAlert("Possible end of trend Appears On: ", i);
Sleep(Sleeptime);
}
}
}
}
}
/*if(Volume[i] < PrevVol && Volume[i] < PrevVol1) { // No Demand/Selling
if(BarType==1 && BarType1==1) {
if(Close[i] > Close[i+1]) {
if(SpreadSize < 0) {
Draw(Time[i], High[i]+20*Point, Red, STYLE_SOLID, 3, "No Demand",i);
if(displayAlert == true)
DisplayAlert("No demand Appears On: ", i);
Sleep(Sleeptime);
}
}
}
else {
if(BarType==2 && BarType1==2) {
if(Close[i] < Close[i+1]) {
if(SpreadSize <0) {
Draw(Time[i], Low[i]-5*Point, LimeGreen, STYLE_SOLID, 3, "No Selling Pressure",i);
if(displayAlert == true)
DisplayAlert("No Selling Pressure Appears On: ", i);
Sleep(Sleeptime);
}
}
}
}
}*/
if(LowVolume[i] != 0) {
if(BarType==1 && BarType1==1) {
if(Close[i] > Close[i+1]) {
if(SpreadSize < 0 && SpreadSize1 <= 0) {
//if(ClosePart <= 1) {
Draw(Time[i], High[i]+20*Point, Red, STYLE_SOLID, 3, "Lack Of Demand",i);
if(displayAlert == true)
DisplayAlert("Lack of demand Appears On: ", i);
Sleep(Sleeptime);
//}
}
}
}
else {
if(BarType==2 && BarType1==2) {
//if(ClosePart <= 1)
if(Close[i] < Close[i+1]) {
if(SpreadSize < 0 && SpreadSize1 <= 0) {
Draw(Time[i], Low[i]-5*Point, LimeGreen, STYLE_SOLID, 3, "Lack Of Selling",i);
if(displayAlert == true)
DisplayAlert("Lack of Selling Appears On: ", i);
Sleep(Sleeptime);
}
}
}
}
}
/* if(VeryHighVolume[i] != 0 || UltraHighVolume[i] != 0) {
if(SpreadSize >= 2 && SpreadSize < 4) {
if(ClosePart <= 1) {
if(BarType == 1) {
if(High[i] >= Highest11) {
Draw(Time[i], High[i]+20*Point, Red, STYLE_SOLID, 3, "Weakness Comming In",i);
if(displayAlert == true)
DisplayAlert("Weakness Comming In: ", i);
Sleep(Sleeptime);
}}
else {
if(Low[i] <= Lowest11) {
Draw(Time[i], Low[i]-5*Point, LimeGreen, STYLE_SOLID, 3, "Strength Comming In",i);
if(displayAlert == true)
DisplayAlert("Strength Comming In: ", i);
Sleep(Sleeptime);
}}
}
}
}*/
if(UltraHighVolume[i] != 0) {
if(SpreadSize >= 2) {
if(ClosePart == 1) {
if(BarType==1) {
Draw(Time[i], High[i]+20*Point, Red, STYLE_SOLID, 3, "Selling Pressure",i);
if(displayAlert == true)
DisplayAlert("Selling Pressure: ", i);
Sleep(Sleeptime);
}
else {
Draw(Time[i], Low[i]-5*Point, LimeGreen, STYLE_SOLID, 3, "Demand Pressure",i);
if(displayAlert == true)
DisplayAlert("Demand Pressure: ", i);
Sleep(Sleeptime);
}
}
}
}
if(High[i] >= Highest1 && UltraHighVolume[i] != 0) {
if(SpreadSize > 3) {
if(BarType==1) {
if(ClosePart <= 1) {
Draw(Time[i], High[i]+20*Point, Red, STYLE_SOLID, 3, "Possible Hidden Selling",i);
if(displayAlert == true)
DisplayAlert("Possible Hidden Selling: ", i);
Sleep(Sleeptime);
}
}
}
}
else {
if(Low[i] <= Lowest1 && UltraHighVolume[i] != 0) {
if(SpreadSize > 3) {
if(BarType==2) {
if(ClosePart <= 1) {
Draw(Time[i], Low[i]-5*Point, LimeGreen, STYLE_SOLID, 3, "Possible Hidden Buying",i);
if(displayAlert == true)
DisplayAlert("Possible Hidden SellBuying: ", i);
Sleep(Sleeptime);
}
}
}
}
}
}
if(LowVolume[i] != 0) {
if(SpreadSize >= 1) {
if(BarType==1) {
//if(ClosePart==2)
Draw(Time[i], High[i]+20*Point, Red, STYLE_SOLID, 3, "Rising Pressure",i);
if(displayAlert == true)
DisplayAlert("No Raising Pressure: ", i);
Sleep(Sleeptime);
}
else {
//if(ClosePart==2)
if(BarType == 1) {
Draw(Time[i], Low[i]-5*Point, LimeGreen, STYLE_SOLID, 3, "Falling Pressure",i);
if(displayAlert == true)
DisplayAlert("No Falling Pressure: ", i);
Sleep(Sleeptime);
}
}
}
}
}
void Draw(datetime x1, double y1, color lineColor, double style, int xx, string comment, int i)
{
string label;
if(lineColor==Red) {
label = "Signal (Sell)" + DoubleToStr(x1, 0);
}
else {
label = "Signal (Buy)" + DoubleToStr(x1, 0);
}
ObjectDelete(label);
ObjectCreate(label, OBJ_ARROW, 0, x1, y1);
ObjectSet(label, OBJPROP_RAY, 0);
ObjectSet(label, OBJPROP_COLOR, lineColor);
ObjectSet(label, OBJPROP_STYLE, style);
if(xx==2)
ObjectSet(label, OBJPROP_ARROWCODE,241);
if(xx==1)
ObjectSet(label, OBJPROP_ARROWCODE,242);
if(xx==3)
ObjectSet(label, OBJPROP_ARROWCODE,251);
ObjectSetText(label,comment);
}
void DisplayAlert(string message, int i)
{
if(i <= 2 && Time[i] != lastAlertTime)
{
lastAlertTime = Time[i];
Alert(message, Symbol(), " , ", Period(), " minutes chart");
}
}
Comments