mql5/Indicators/ProfileHeatmap.mq5
abel324d df3e6b92f5
2026-01-29 11:34:39 +03:00

372 líneas
12 KiB
MQL5

//+------------------------------------------------------------------+
//| ProfileHeatmap.mq5 |
//| Copyright 2026, Google Gemini |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Google Gemini"
#property link "https://www.google.com"
#property version "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_NONE
#include <Canvas\Canvas.mqh>
#include <Math\Stat\Normal.mqh>
//--- Input Parameters
input int InpRangeBars = 300; // Bars to Analyze
input int InpPriceStep = 10; // Price Step (Ticks per bin)
input color InpBaseColor = clrOrange; // Heatmap Color
input int InpMaxOpacity = 180; // Max Opacity (0-255)
input int InpWidthRatio = 200; // Width of Heatmap (Pixels)
//--- GPR Parameters
input int InpGPR_Bars = 100; // GPR: Training Bars
input int InpGPR_Future = 50; // GPR: Future Bars to Predict
input double InpGPR_LS = 10.0; // GPR: Length Scale (Bars)
input double InpGPR_SigmaF = 1.0; // GPR: Signal Variance
input double InpGPR_SigmaN = 0.05; // GPR: Noise Variance
input color InpGPR_Color = clrCyan; // GPR: Heatmap Color
//--- Global Variables
CCanvas canvas;
string canvasName = "ProfileHeatmapCanvas";
int chartWidth, chartHeight;
double minPrice, maxPrice;
double bins[];
int numBins;
int lastBarCount = 0;
datetime lastUpdate = 0;
double DummyBuffer[];
// GPR Results
vector gprMean;
vector gprVar;
double gprMinPrice, gprMaxPrice;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
chartWidth = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
chartHeight = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
if(!canvas.CreateBitmapLabel(canvasName, 0, 0, chartWidth, chartHeight, COLOR_FORMAT_ARGB_NORMALIZE))
{
Print("Canvas creation failed");
return(INIT_FAILED);
}
SetIndexBuffer(0, DummyBuffer, INDICATOR_DATA);
PlotIndexSetString(0, PLOT_LABEL, "Dummy");
canvas.Erase(0);
canvas.Update();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
canvas.Destroy();
}
//+------------------------------------------------------------------+
//| 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[])
{
if(rates_total < InpRangeBars) return(0);
// Update chart dimensions
int w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
int h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
if(w != chartWidth || h != chartHeight)
{
chartWidth = w;
chartHeight = h;
canvas.Resize(chartWidth, chartHeight);
ChartRedraw();
}
// Throttle updates: every 2 seconds or on new bar
datetime now = TimeCurrent();
if(rates_total == lastBarCount && (now - lastUpdate) < 2) return(rates_total);
lastBarCount = rates_total;
lastUpdate = now;
CalculateHeatmap(high, low, tick_volume, rates_total);
CalculateGPR(close, rates_total);
DrawAll();
return(rates_total);
}
//+------------------------------------------------------------------+
//| Chart Event Handler |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
if(id == CHARTEVENT_CHART_CHANGE)
{
int w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
int h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
if(w != chartWidth || h != chartHeight)
{
chartWidth = w;
chartHeight = h;
canvas.Resize(chartWidth, chartHeight);
lastBarCount = 0; // Force redraw
}
}
}
//+------------------------------------------------------------------+
//| Calculate the Profile with Gaussian Distribution |
//+------------------------------------------------------------------+
void CalculateHeatmap(const double &high[], const double &low[], const long &tick_volume[], int total)
{
double point = _Point;
if(point == 0) point = 0.00001;
double tickSize = _Point;
if(tickSize == 0) tickSize = point;
double stepPrice = InpPriceStep * tickSize;
// 1. Find Price Range
minPrice = 999999;
maxPrice = -1;
for(int i = 0; i < InpRangeBars; i++)
{
int idx = total - 1 - i;
if(idx < 0) break;
if(high[idx] > maxPrice) maxPrice = high[idx];
if(low[idx] < minPrice) minPrice = low[idx];
}
if(maxPrice <= minPrice) return;
numBins = (int)((maxPrice - minPrice) / stepPrice) + 1;
ArrayResize(bins, numBins);
ArrayInitialize(bins, 0);
// 2. Accumulate Volume in Bins (Raw Volume Profile)
for(int i = 0; i < InpRangeBars; i++)
{
int idx = total - 1 - i;
if(idx < 0) break;
int startBin = (int)((low[idx] - minPrice) / stepPrice);
int endBin = (int)((high[idx] - minPrice) / stepPrice);
long vol = tick_volume[idx];
int span = (endBin - startBin + 1);
double volPerBin = (double)vol / span;
for(int b = startBin; b <= endBin; b++)
{
if(b >= 0 && b < numBins) bins[b] += volPerBin;
}
}
}
//+------------------------------------------------------------------+
//| Calculate GPR Prediction |
//+------------------------------------------------------------------+
void CalculateGPR(const double &close[], int total)
{
int n = InpGPR_Bars;
int m = InpGPR_Future;
if(total < n) return;
vector y(n);
vector x(n);
double firstPrice = close[total - n];
double lastPrice = close[total - 1];
// Normalize y (price) around the current price for stability
for(int i = 0; i < n; i++)
{
y[i] = close[total - n + i] - lastPrice;
x[i] = i;
}
matrix K(n, n);
double lsSq = 2.0 * InpGPR_LS * InpGPR_LS;
double sfSq = InpGPR_SigmaF * InpGPR_SigmaF;
double snSq = InpGPR_SigmaN * InpGPR_SigmaN;
for(int i = 0; i < n; i++)
{
for(int j = 0; j <= i; j++)
{
double dist = x[i] - x[j];
double val = sfSq * exp(-(dist * dist) / lsSq);
if(i == j) val += snSq;
K[i][j] = val;
if(i != j) K[j][i] = val;
}
}
matrix Kinv = K.Inv();
if(Kinv.Rows() == 0) return; // Inverse failed
vector alpha = Kinv.MatMul(y);
gprMean.Resize(m);
gprVar.Resize(m);
vector xStar(m);
for(int i = 0; i < m; i++) xStar[i] = n + i;
matrix KStar(m, n);
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
double dist = xStar[i] - x[j];
KStar[i][j] = sfSq * exp(-(dist * dist) / lsSq);
}
}
gprMean = KStar.MatMul(alpha);
// Posterior Variance: diag(K** - K* K^-1 K*T)
for(int i = 0; i < m; i++)
{
vector kStar_i = KStar.Row(i);
double kss = sfSq + snSq;
double q = kStar_i.MatMul(Kinv.MatMul(kStar_i));
gprVar[i] = MathMax(0, kss - q);
}
// Restore normalization
for(int i = 0; i < m; i++) gprMean[i] += lastPrice;
}
//+------------------------------------------------------------------+
//| Draw Everything |
//+------------------------------------------------------------------+
void DrawAll()
{
canvas.Erase(0);
DrawVolumeProfile();
DrawGPRHeatmap();
canvas.Update();
}
//+------------------------------------------------------------------+
//| Draw the Volume Profile Heatmap |
//+------------------------------------------------------------------+
void DrawVolumeProfile()
{
double maxVal = -1;
for(int i = 0; i < numBins; i++) if(bins[i] > maxVal) maxVal = bins[i];
if(maxVal <= 0) return;
double chartMinP = ChartGetDouble(0, CHART_PRICE_MIN);
double chartMaxP = ChartGetDouble(0, CHART_PRICE_MAX);
double priceRange = chartMaxP - chartMinP;
if(priceRange <= 0) return;
double stepPrice = (maxPrice - minPrice) / numBins;
for(int i = 0; i < numBins; i++)
{
double price = minPrice + i * stepPrice;
double intensity = bins[i] / maxVal;
int y = (int)((chartMaxP - price) / priceRange * chartHeight);
int yNext = (int)((chartMaxP - (price + stepPrice)) / priceRange * chartHeight);
if(y < 0 && yNext < 0) continue;
if(y >= chartHeight && yNext >= chartHeight) continue;
uchar alpha = (uchar)(intensity * InpMaxOpacity);
uint c = ColorToARGB(InpBaseColor, alpha);
int drawWidth = (int)(intensity * InpWidthRatio);
canvas.FillRectangle(chartWidth - drawWidth, yNext, chartWidth, y, c);
}
}
//+------------------------------------------------------------------+
//| Draw the GPR Heatmap |
//+------------------------------------------------------------------+
void DrawGPRHeatmap()
{
if(gprMean.Size() == 0) return;
double chartMinP = ChartGetDouble(0, CHART_PRICE_MIN);
double chartMaxP = ChartGetDouble(0, CHART_PRICE_MAX);
double priceRange = chartMaxP - chartMinP;
if(priceRange <= 0) return;
int barsVisible = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
int lastBarX = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
// Calculate pixel width per bar
double barWidth = (double)chartWidth / barsVisible;
// Find price steps for the heatmap bins at each future step
int futureSteps = (int)gprMean.Size();
for(int i = 0; i < futureSteps; i++)
{
double mean = gprMean[i];
double std = sqrt(gprVar[i]);
if(std <= 0) std = _Point;
int xStart = (int)(chartWidth - (barsVisible - i) * barWidth); // This is complex, let's use a simpler mapping
// Better way: use ChartTimePriceToXY
datetime futureDate = TimeCurrent() + PeriodSeconds() * (i + 1);
int x, y_mean;
if(!ChartTimePriceToXY(0, 0, futureDate, mean, x, y_mean)) continue;
// Draw a vertical density for this time step
// Heatmap bins for price probability
int p_bins = 40;
double range_std = 3.0;
for(int j = 0; j < p_bins; j++)
{
double p_offset = (double)(j - p_bins / 2) / (p_bins / 2) * (range_std * std);
double price = mean + p_offset;
double z = p_offset / std;
double prob = exp(-0.5 * z * z); // Gaussian density (approx)
int py1, py2;
double priceNext = mean + (double)(j + 1 - p_bins / 2) / (p_bins / 2) * (range_std * std);
ChartTimePriceToXY(0, 0, futureDate, price, x, py1);
ChartTimePriceToXY(0, 0, futureDate, priceNext, x, py2);
uchar alpha = (uchar)(prob * InpMaxOpacity);
uint c = ColorToARGB(InpGPR_Color, alpha);
int x2 = x + (int)barWidth;
canvas.FillRectangle(x, py2, x2, py1, c);
}
}
}
//+------------------------------------------------------------------+