Article-22742-EquiVolume-In.../Article-22742-EquiVolume-Indicator-In-MQL5.mq5

223 lines
7.6 KiB
MQL5

//+------------------------------------------------------------------+
//| Article-22742-EquiVolume-Indicator-In-MQL5.mq5 |
//| Abioye Israel Pelumi |
//| https://Algoyin.com |
//+------------------------------------------------------------------+
#property indicator_separate_window
#property indicator_plots 0
//--- Input parameters
input int InpLookback = 100; // number of bars for EquiVolume
input color InpBullColor = clrDodgerBlue; // bullish bar color (close >= open)
input color InpBearColor = clrTomato; // bearish bar color (close < open)
input color InpOutlineColor = clrGray; // rectangle outline color
input double InpMaxWidthPct = 10; // maximum box width scaling factor
//--- Global variables
int indicator_window; // indicator subwindow index
datetime lastTradeBarTime = 0; // ensures logic runs once per new candle
//+------------------------------------------------------------------+
//| Returns unique empty box object name |
//+------------------------------------------------------------------+
string BoxEmptyName(datetime t)
{
return StringFormat("Box%d", (int)t);
}
//+------------------------------------------------------------------+
//| Returns unique filled box object name |
//+------------------------------------------------------------------+
string BoxFillName(datetime t)
{
return StringFormat("Box Fill%d", (int)t);
}
//+------------------------------------------------------------------+
//| Create or update volume box |
//+------------------------------------------------------------------+
bool DrawBox(const string name,
datetime x1, double yTop,
datetime x2, double yBot, bool is_fill,
color Col, int in_window)
{
bool created = false;
//--- create rectangle object only if it does not exist
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_RECTANGLE, in_window, x1, yTop, x2, yBot);
//--- configure rectangle properties
ObjectSetInteger(0, name, OBJPROP_FILL, is_fill);
ObjectSetInteger(0, name, OBJPROP_COLOR, Col);
created = true;
}
//--- continuously update rectangle coordinates
ObjectMove(0, name, 0, x1, yTop);
ObjectMove(0, name, 1, x2, yBot);
return created;
}
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- set indicator short name
IndicatorSetString(INDICATOR_SHORTNAME, "EquiVolume");
//--- locate indicator subwindow
indicator_window = ChartWindowFind(ChartID(), "EquiVolume");
//--- remove old box objects during initialization
ObjectsDeleteAll(0, "Box", indicator_window);
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Deinitialization |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- delete all indicator objects during cleanup
ObjectsDeleteAll(0, "Box", indicator_window);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int32_t rates_total,
const int32_t 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 int32_t &spread[])
{
//--- limit processing to selected lookback range
int bars = MathMin(rates_total, InpLookback);
//--- ensure enough bars are available
if(bars < 2)
return rates_total;
bool need_redraw = false;
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
//--- execute logic only once per new candle
if(currentBarTime != lastTradeBarTime)
{
//--- check whether broker provides real volume
bool useRealVol = (volume[rates_total - 1] > 0);
int startIdx = rates_total - bars;
long maxVol = 0; // highest volume in range
double maxHigh = 0; // highest price in range
double minLow = low[startIdx]; // lowest price in range
//--- scan candles to determine volume and price boundaries
for(int i = startIdx; i < rates_total; i++)
{
long vol = useRealVol ? volume[i] : tick_volume[i];
//--- track maximum volume
if(vol > maxVol)
maxVol = vol;
//--- track highest price
double h = high[i];
if(h > maxHigh)
maxHigh = high[i];
//--- track lowest price
double l = low[i];
if(l < minLow)
minLow = low[i];
}
//--- adjust indicator scale dynamically
IndicatorSetDouble(INDICATOR_MAXIMUM, maxHigh);
IndicatorSetDouble(INDICATOR_MINIMUM, minLow);
//--- prevent division by zero
if(maxVol == 0)
return rates_total;
//--- retrieve timeframe duration in seconds
datetime periodSec = (datetime)PeriodSeconds();
datetime start_time = time[rates_total - 1]; // starting point of current box
datetime end_time = 0;
//--- process candles backward
for(int i = rates_total - 2; i >= startIdx; i--)
{
long vol = useRealVol ? volume[i] : tick_volume[i];
//--- skip candles with zero volume
if(vol == 0)
continue;
//--- normalize width relative to highest volume
int val = (int)((double)vol / maxVol * InpMaxWidthPct);
//--- calculate horizontal width endpoint
end_time = start_time - (periodSec * val);
//--- draw candle outline rectangle
if(DrawBox(BoxEmptyName(time[i]), start_time, high[i], end_time, low[i], false, InpOutlineColor, indicator_window))
{
need_redraw = true;
}
//--- determine candle direction
bool bullish = (close[i] >= open[i]);
//--- select fill color based on candle direction
color fillCol = bullish ? InpBullColor : InpBearColor;
//--- draw candle body rectangle
if(DrawBox(BoxFillName(time[i]), start_time, open[i], end_time, close[i], true, fillCol, indicator_window))
{
need_redraw = true;
}
//--- shift next rectangle starting point
start_time = end_time;
//--- remove outdated objects outside visible range
for(int i = ObjectsTotal(0, indicator_window)-1; i >= 0; i--)
{
string obj_name = ObjectName(0, i);
if((datetime)ObjectGetInteger(0, obj_name, OBJPROP_TIME, 0) < end_time)
{
ObjectDelete(0, obj_name);
need_redraw = true;
}
}
}
//--- update last processed candle time
lastTradeBarTime = currentBarTime;
}
//--- redraw chart only when changes occur
if(need_redraw)
ChartRedraw(0);
//--- return value of prev_calculated for next call
return(rates_total);
}
//+------------------------------------------------------------------+