OBJ_RECTANGLE_LABEL, привязанных к динамическим экранным координатам через функцию ChartTimePriceToXY. Это позволяет строить точную гистограмму распределения времени на ценовых уровнях прямо на основном графике без искажений при масштабировании.Исходный код
При разработке сложных торговых систем, таких как MT5: Кастомный индикатор Equity Curve для мультивалютного портфеля, визуализация рыночных объемов и профилей играет ключевую роль. Ниже представлен полный код индикатора, который рассчитывает профиль TPO (Time Price Opportunity) за заданное количество баров и отрисовывает его в виде гистограммы с помощью графических элементов OBJ_RECTANGLE_LABEL.
//+------------------------------------------------------------------+
//| MarketProfile_TPO.mq5 |
//| Copyright 2024, Quant Developer |
//+------------------------------------------------------------------+
#property copyright "Quant Developer"
#property link "https://finfluct.com"
#property version "1.00"
#property indicator_chart_window
input int InpTpoSizePoints = 100; // Размер TPO шага в пунктах
input color InpProfileColor = clrDodgerBlue; // Цвет гистограммы
input int InpMaxBarCount = 300; // Глубина анализа (баров)
input int InpWidthScale = 5; // Масштаб ширины (пикселей на TPO)
struct TpoBin
{
double price;
int count;
};
TpoBin Profile[];
string Prefix = "MP_TPO_";
int OnInit()
{
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, Prefix);
ChartRedraw(0);
}
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 < InpMaxBarCount) return(0);
ObjectsDeleteAll(0, Prefix);
ArrayFree(Profile);
int limit = rates_total - InpMaxBarCount;
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double step = InpTpoSizePoints * point;
if(step <= 0) return(0);
for(int i = limit; i < rates_total; i++)
{
double lowPrice = low[i];
double highPrice = high[i];
double startPrice = MathFloor(lowPrice / step) * step;
for(double p = startPrice; p <= highPrice; p += step)
{
p = NormalizeDouble(p, _Digits);
int foundIdx = -1;
for(int j = 0; j < ArraySize(Profile); j++)
{
if(MathAbs(Profile[j].price - p) < (point / 2.0))
{
foundIdx = j;
break;
}
}
if(foundIdx != -1)
{
Profile[foundIdx].count++;
}
else
{
int size = ArraySize(Profile);
ArrayResize(Profile, size + 1);
Profile[size].price = p;
Profile[size].count = 1;
}
}
}
int maxCount = 0;
for(int i = 0; i < ArraySize(Profile); i++)
{
if(Profile[i].count > maxCount) maxCount = Profile[i].count;
}
if(maxCount == 0) return(rates_total);
int subWindow = 0;
datetime lastBarTime = time[rates_total - 1];
for(int i = 0; i < ArraySize(Profile); i++)
{
string objName = Prefix + IntegerToString(i);
int x = 0, y = 0;
if(ChartTimePriceToXY(0, subWindow, lastBarTime, Profile[i].price, x, y))
{
int width = Profile[i].count * InpWidthScale;
int height = 8;
if(ObjectCreate(0, objName, OBJ_RECTANGLE_LABEL, subWindow, 0, 0))
{
ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, x + 50);
ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, y - (height / 2));
ObjectSetInteger(0, objName, OBJPROP_XSIZE, width);
ObjectSetInteger(0, objName, OBJPROP_YSIZE, height);
ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, InpProfileColor);
ObjectSetInteger(0, objName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, objName, OBJPROP_HIDDEN, true);
}
}
}
ChartRedraw(0);
return(rates_total);
} Разбор параметров
InpTpoSizePoints: Шаг сетки TPO в пунктах. Определяет чувствительность и детализацию профиля рынка.InpProfileColor: Цвет заливки прямоугольников гистограммыOBJ_RECTANGLE_LABEL.InpMaxBarCount: Количество баров истории, используемых для расчета распределения TPO.InpWidthScale: Коэффициент масштабирования ширины гистограммы в пикселях на одно попадание цены в зону TPO.
Как запустить
Для запуска индикатора откройте MetaEditor в MetaTrader 5, создайте новый пользовательский индикатор и вставьте предложенный код. Скомпилируйте проект и перенесите индикатор на график нужного финансового инструмента.
Для более глубокого анализа сантимента рынка, помимо профиля объема, часто используют Парсинг отчетов COT с CFTC и индикатор нетто-позиций для MT4. Если вам требуется сохранять тиковую историю для последующего анализа распределения TPO на внешних серверах, рекомендуем изучить Экспорт тиков в SQL из MQL5: Скрипт реального времени.




