mql5/Indicators/ultra liquidity.mq5

374 lines
13 KiB
MQL5
Raw Permalink Normal View History

2026-05-07 12:17:30 +00:00
//+------------------------------------------------------------------+
//| LiquidityHeatmap.mq5 |
//| Copyright 2024, Your Name |
//| https://www.yourwebsite.com|
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Your Name"
#property link "https://www.yourwebsite.com"
#property version "1.00"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots 0
//+------------------------------------------------------------------+
//| Input parameters |
//+------------------------------------------------------------------+
input int iBarsBack = 500; // Number of bars to analyze
input int iRows = 50; // Number of price rows (resolution)
input double dLiquidityFactor = 2.0; // Sensitivity (higher = smoother)
input color clrLowLiquidity = clrDarkBlue; // Low liquidity color
input color clrHighLiquidity = clrRed; // High liquidity color
input bool bShowLegend = true; // Show legend on chart
input ENUM_TIMEFRAMES tfAnalysis = PERIOD_CURRENT; // Timeframe for analysis
//+------------------------------------------------------------------+
//| Global variables |
//+------------------------------------------------------------------+
double g_dPriceMin, g_dPriceMax;
int g_iRows;
double g_dHeatmap[]; // Heatmap values per price level
double g_dVolumeProfile[];
double g_dPriceLevels[];
int g_iHandleVolumes;
string g_sIndicatorName = "LiquidityHeatmap";
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Set rows based on input
g_iRows = iRows;
// Calculate initial price range
CalculatePriceRange();
// Initialize arrays
ArrayResize(g_dVolumeProfile, g_iRows);
ArrayResize(g_dPriceLevels, g_iRows);
ArrayResize(g_dHeatmap, g_iRows);
// Fill price levels
double step = (g_dPriceMax - g_dPriceMin) / g_iRows;
for(int i = 0; i < g_iRows; i++)
{
g_dPriceLevels[i] = g_dPriceMin + (i * step);
}
// Calculate volume profile
CalculateVolumeProfile();
// Normalize and create heatmap colors
NormalizeHeatmap();
// Redraw chart
ChartRedraw();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Calculate price range across specified bars |
//+------------------------------------------------------------------+
void CalculatePriceRange()
{
g_dPriceMin = DBL_MAX;
g_dPriceMax = -DBL_MAX;
int totalBars = iBarsBack;
int startBar = 1; // skip current incomplete bar
int endBar = startBar + totalBars;
for(int i = startBar; i < endBar; i++)
{
double high = iHigh(_Symbol, tfAnalysis, i);
double low = iLow(_Symbol, tfAnalysis, i);
if(high > g_dPriceMax) g_dPriceMax = high;
if(low < g_dPriceMin) g_dPriceMin = low;
}
// Add some padding (2% above and below)
double padding = (g_dPriceMax - g_dPriceMin) * 0.02;
g_dPriceMin -= padding;
g_dPriceMax += padding;
}
//+------------------------------------------------------------------+
//| Calculate volume profile using tick volume or real volume |
//+------------------------------------------------------------------+
void CalculateVolumeProfile()
{
// Initialize volume profile array
ArrayInitialize(g_dVolumeProfile, 0.0);
int totalBars = iBarsBack;
int startBar = 1;
int endBar = startBar + totalBars;
double step = (g_dPriceMax - g_dPriceMin) / g_iRows;
for(int i = startBar; i < endBar; i++)
{
double high = iHigh(_Symbol, tfAnalysis, i);
double low = iLow(_Symbol, tfAnalysis, i);
long volume = iVolume(_Symbol, tfAnalysis, i);
// Determine which price levels this bar affects
int startRow = (int)((low - g_dPriceMin) / step);
int endRow = (int)((high - g_dPriceMin) / step);
// Cap to array bounds
startRow = MathMax(0, startRow);
endRow = MathMin(g_iRows - 1, endRow);
// Distribute volume across price levels using linear weighting
double volumePerRow = (double)volume / (endRow - startRow + 1);
for(int row = startRow; row <= endRow; row++)
{
g_dVolumeProfile[row] += volumePerRow;
}
}
// Apply smoothing
SmoothVolumeProfile(dLiquidityFactor);
}
//+------------------------------------------------------------------+
//| Smooth the volume profile using moving average |
//+------------------------------------------------------------------+
void SmoothVolumeProfile(double factor)
{
if(factor <= 0) return;
int window = (int)factor;
if(window < 1) window = 1;
double smoothCopy[];
ArrayResize(smoothCopy, g_iRows);
ArrayCopy(smoothCopy, g_dVolumeProfile);
for(int i = 0; i < g_iRows; i++)
{
double sum = 0;
int count = 0;
for(int j = -window; j <= window; j++)
{
int idx = i + j;
if(idx >= 0 && idx < g_iRows)
{
sum += smoothCopy[idx];
count++;
}
}
if(count > 0)
g_dVolumeProfile[i] = sum / count;
}
}
//+------------------------------------------------------------------+
//| Normalize heatmap values to range 0-1 for coloring |
//+------------------------------------------------------------------+
void NormalizeHeatmap()
{
double maxVol = 0;
for(int i = 0; i < g_iRows; i++)
{
if(g_dVolumeProfile[i] > maxVol)
maxVol = g_dVolumeProfile[i];
}
if(maxVol > 0)
{
for(int i = 0; i < g_iRows; i++)
{
g_dHeatmap[i] = g_dVolumeProfile[i] / maxVol; // 0 to 1
}
}
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Remove all objects created by this indicator
ObjectsDeleteAll(0, g_sIndicatorName);
ChartRedraw();
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
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[])
{
// Recalculate on new bar
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, tfAnalysis, 0);
if(currentBarTime != lastBarTime || prev_calculated == 0)
{
lastBarTime = currentBarTime;
CalculatePriceRange();
// Reinitialize arrays for new range
ArrayResize(g_dVolumeProfile, g_iRows);
ArrayResize(g_dPriceLevels, g_iRows);
ArrayResize(g_dHeatmap, g_iRows);
double step = (g_dPriceMax - g_dPriceMin) / g_iRows;
for(int i = 0; i < g_iRows; i++)
{
g_dPriceLevels[i] = g_dPriceMin + (i * step);
}
CalculateVolumeProfile();
NormalizeHeatmap();
DrawHeatmap();
}
return(rates_total);
}
//+------------------------------------------------------------------+
//| Draw heatmap rectangles on chart |
//+------------------------------------------------------------------+
void DrawHeatmap()
{
// Delete old objects
ObjectsDeleteAll(0, g_sIndicatorName);
datetime startTime = iTime(_Symbol, tfAnalysis, iBarsBack);
datetime endTime = iTime(_Symbol, tfAnalysis, 0);
// Extend to current time + some future space
endTime += (endTime - startTime) / 10;
double step = (g_dPriceMax - g_dPriceMin) / g_iRows;
for(int i = 0; i < g_iRows; i++)
{
double priceLow = g_dPriceLevels[i];
double priceHigh = priceLow + step;
double intensity = g_dHeatmap[i];
// Get color based on intensity
color rectColor = GetHeatmapColor(intensity);
// Create rectangle name
string rectName = StringFormat("%s_row_%d", g_sIndicatorName, i);
// Create rectangle object
if(ObjectCreate(0, rectName, OBJ_RECTANGLE, 0, startTime, priceLow, endTime, priceHigh))
{
ObjectSetInteger(0, rectName, OBJPROP_FILL, true);
ObjectSetInteger(0, rectName, OBJPROP_COLOR, rectColor);
ObjectSetInteger(0, rectName, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, rectName, OBJPROP_BACK, true);
ObjectSetInteger(0, rectName, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, rectName, OBJPROP_HIDDEN, true);
}
}
// Draw legend if enabled
if(bShowLegend)
DrawLegend();
}
//+------------------------------------------------------------------+
//| Map intensity (0-1) to RGB color |
//+------------------------------------------------------------------+
color GetHeatmapColor(double intensity)
{
// intensity: 0 = low liquidity (blue), 1 = high liquidity (red)
int r = (int)(intensity * 255);
int g = 0;
int b = (int)((1.0 - intensity) * 255);
// Alternative gradient: Blue -> Cyan -> Yellow -> Red
// But simple RGB works well
return (color)((r << 16) | (g << 8) | b);
}
//+------------------------------------------------------------------+
//| Draw color legend |
//+------------------------------------------------------------------+
void DrawLegend()
{
string legendName = g_sIndicatorName + "_legend";
ObjectDelete(0, legendName);
// Legend position (top right corner)
int x = 50;
int y = 50;
int width = 150;
int height = 20;
// Background
string bgName = legendName + "_bg";
if(ObjectCreate(0, bgName, OBJ_RECTANGLE_LABEL, 0, 0, 0))
{
ObjectSetInteger(0, bgName, OBJPROP_XDISTANCE, x - 5);
ObjectSetInteger(0, bgName, OBJPROP_YDISTANCE, y - 20);
ObjectSetInteger(0, bgName, OBJPROP_XSIZE, width + 10);
ObjectSetInteger(0, bgName, OBJPROP_YSIZE, height + 30);
ObjectSetInteger(0, bgName, OBJPROP_BGCOLOR, clrBlack);
ObjectSetInteger(0, bgName, OBJPROP_BORDER_COLOR, clrWhite);
ObjectSetInteger(0, bgName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetInteger(0, bgName, OBJPROP_BACK, false);
ObjectSetInteger(0, bgName, OBJPROP_FILL, true);
}
// Color bar
for(int i = 0; i <= 100; i++)
{
double intensity = i / 100.0;
color barColor = GetHeatmapColor(intensity);
string barName = legendName + "_bar_" + IntegerToString(i);
if(ObjectCreate(0, barName, OBJ_RECTANGLE_LABEL, 0, 0, 0))
{
int barX = x + (int)(i * width / 100.0);
int barY = y;
ObjectSetInteger(0, barName, OBJPROP_XDISTANCE, barX);
ObjectSetInteger(0, barName, OBJPROP_YDISTANCE, barY);
ObjectSetInteger(0, barName, OBJPROP_XSIZE, width / 100);
ObjectSetInteger(0, barName, OBJPROP_YSIZE, height);
ObjectSetInteger(0, barName, OBJPROP_BGCOLOR, barColor);
ObjectSetInteger(0, barName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetInteger(0, barName, OBJPROP_BACK, false);
}
}
// Labels
if(ObjectCreate(0, legendName + "_low", OBJ_LABEL, 0, 0, 0))
{
ObjectSetInteger(0, legendName + "_low", OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, legendName + "_low", OBJPROP_YDISTANCE, y + height + 2);
ObjectSetString(0, legendName + "_low", OBJPROP_TEXT, "Low Liquidity");
ObjectSetInteger(0, legendName + "_low", OBJPROP_COLOR, clrWhite);
ObjectSetInteger(0, legendName + "_low", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetInteger(0, legendName + "_low", OBJPROP_FONTSIZE, 10);
}
if(ObjectCreate(0, legendName + "_high", OBJ_LABEL, 0, 0, 0))
{
ObjectSetInteger(0, legendName + "_high", OBJPROP_XDISTANCE, x + width - 60);
ObjectSetInteger(0, legendName + "_high", OBJPROP_YDISTANCE, y + height + 2);
ObjectSetString(0, legendName + "_high", OBJPROP_TEXT, "High Liquidity");
ObjectSetInteger(0, legendName + "_high", OBJPROP_COLOR, clrWhite);
ObjectSetInteger(0, legendName + "_high", OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetInteger(0, legendName + "_high", OBJPROP_FONTSIZE, 10);
}
}
//+------------------------------------------------------------------+