Market Profile Ultimate PRO
Сообщите нам, если считаете, что материал размещён без разрешения.
Сообщить о материале
Обращение по товару «marketprofile_ultimate_premium_pro.mq5» отправится на рассмотрение. Укажите контакт для ответа и опишите причину.
О программе
Market Profile Ultimate PRO — индикатор для MetaTrader 5, рассчитывающий и отображающий аналитические рыночные профили на графике цены. Поддерживает ежедневные, недельные, месячные и интрадей-сессии с тремя режимами построения: TPO, объёмный и гибридный. Основные уровни — POC (точка контроля), VAH и VAL (границы зоны значимости) — отрисовываются линиями и светящимися полосами.
Индикатор включает три ключевых механизма: обнаружение бимодальных распределений с вторичным POC и мёртвой зоной, дельта-профиль на основе потока ордеров с раскраской баров по покупательской/продавательской активности, а также составной память нескольких сессий с экспоненциальным затуханием. Дополнительно определяются узлы высокого и низкого объёма (HVN/LVN), нетестированные прошлые POC и события поглощения.
Для визуализации доступны шесть тем оформления, настраиваемые цвета, информационная панель с данными о текущей сессии, лучи и метки. Система уведомлений поддерживает popup, push и email-алерты при пересечении ключевых уровней и обнаружении поглощения. Развивающиеся уровни (Dev POC, Dev VAH, Dev VAL) выводятся через шесть индикаторных буферов.
Настройки программы
| Настройка | Описание |
|---|---|
| InpTheme | Тема оформления индикатора: Neon Dark, Sunset, Ocean, Matrix, Royal или Custom |
| InpGlowEffect | Включает свечение для линий POC и VA на текущей сессии |
| InpGlowIntensity | Количество слоёв свечения для линий профиля (влияет на ширину свечения) |
| InpShowInfoPanel | Включает отображение информационной панели с данными текущей сессии |
| InpPanelCorner | Угол графика, к которому привязывается информационная панель |
| InpPanelX | Горизонтальное смещение информационной панели от угла графика |
| InpPanelY | Вертикальное смещение информационной панели от угла графика |
| InpSession | Тип сессии для построения профиля: ежедневная, недельная, месячная или интрадей |
| InpProfileMode | Режим построения профиля: TPO (по количеству баров), объёмный (по объёму) или гибридный |
| InpIntradayHours | Продолжительность интрадей-сессии в часах для определения границ |
| InpSessionsToShow | Количество прошлых сессий, отображаемых на графике |
| InpValueAreaPercent | Процент активности, определяющий границы зоны значимости (VA) |
| InpProfileWidth | Ширина гистограммы профиля в процентах от длительности сессии |
| InpEnableBimodal | Включает обнаружение бимодальных распределений с вторичным POC и мёртвой зоной |
| InpBimodalDipRatio | Максимальное отношение глубины впадины к высоте пика для подтверждения бимодального распределения |
| InpMinPeakDistance | Минимальное расстояние в уровнях между двумя пиками при обнаружении бимодальности |
| InpSecondaryPOCMinPct | Минимальный процент активности вторичного пика от основного POC для подтверждения второго POC |
| InpEnableDelta | Включает раскраску баров профиля по потоку ордеров (дельта) |
| InpUseRealTicks | Использует данные реальных тиков (CopyTicks) для расчёта дельты вместо направления закрытия баров |
| InpAbsorptionThresh | Чувствительность обнаружения поглощения — отношение дельты к активности на уровне POC, при превышении которого фиксируется поглощение |
| InpEnableComposite | Включает построение составного профиля из нескольких прошлых сессий с затуханием |
| InpCompositeSessions | Количество сессий, учитываемых в составном профиле с памятью |
| InpDecayFactor | Коэффициент экспоненциального затухания для составного профиля (новые сессии имеют больший вес) |
| InpShowHVN_LVN | Отображает линии узлов высокого (HVN) и низкого (LVN) объёма в составе профиля |
| InpShowNakedPOCs | Отображает нетестированные прошлые POC — уровни, к которым цена не возвращалась после окончания сессии |
| InpMaxNakedPOCs | Максимальное количество отображаемых нетестированных прошлых POC |
| InpPointMultiplier | Множитель для расчёта шага цены профиля; при значении 0 используется автоматический расчёт на основе ATR |
| InpMaxPriceLevels | Максимальное количество ценовых уровней при построении профиля |
| InpCustomProfile | Основной цвет профиля при использовании пользовательской темы |
| InpCustomVA | Цвет зоны значимости при использовании пользовательской темы |
| InpCustomPOC | Цвет уровня POC при использовании пользовательской темы |
| InpCustomSP | Цвет точки спика при использовании пользовательской темы |
| InpCustomBG | Цвет фона информационной панели при использовании пользовательской темы |
| InpShowSinglePrints | Отображает уровни одиночных печатей — уровней с активностью значительно ниже соседних |
| InpShowPOCLine | Отображает горизонтальную линию уровня POC |
| InpShowVALines | Отображает горизонтальные линии уровней VAH и VAL |
| InpShowProfileLabels | Отображает текстовые метки с ценами POC, VAH и VAL рядом с профилем |
| InpShowSessionDivider | Отображает вертикальную линию-разделитель между сессиями |
| InpEnableDevPOC | Отображает развивающийся уровень POC через индикаторные буферы |
| InpEnableDevVA | Отображает развивающиеся уровни VAH и VAL через индикаторные буферы |
| InpShowPOCRays | Отображает луч от уровня POC вправо от текущей сессии |
| InpShowVARays | Отображает лучи от уровней VAH и VAL вправо от текущей сессии |
| InpStopRaysOnCross | Останавливает лучи POC и VA при пересечении их ценой |
| InpAlertOnPOCCross | Срабатывает при пересечении цены уровня POC |
| InpAlertOnVACross | Срабатывает при пересечении цены уровней VAH или VAL |
| InpAlertOnNakedTest | Срабатывает при тестировании нетестированного прошлого POC |
| InpAlertOnAbsorption | Срабатывает при обнаружении поглощения на уровне POC |
| InpAlertPopup | Включает всплывающие уведомления об алертах |
| InpAlertPush | Включает отправку push-уведомлений об алертах |
| InpAlertEmail | Включает отправку уведомлений об алертах по электронной почте |
| InpThrottleMs | Задержка в миллисекундах между перерисовками индикатора для снижения нагрузки |
| InpDisableOnWrongTF | Отключает индикатор при несоответствии текущего таймфрейма требованиям выбранной сессии |
Поиск по коду файла
#property version "4.00"
#property description "Market Profile Ultimate PRO — neon design + 3 breakout mechanics:"
#property description "1) Bimodal VA + Multiple POCs 2) Delta Profile (order flow) 3) Composite Memory + Naked POCs"
#property strict
#property indicator_chart_window
#property indicator_plots 6
#property indicator_buffers 6
#property indicator_label1 "Dev POC"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrGold
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
#property indicator_label2 "Dev POC (2)"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrGold
#property indicator_style2 STYLE_SOLID
#property indicator_width2 2
#property indicator_label3 "Dev VAH"
#property indicator_type3 DRAW_LINE
#property indicator_color3 C'0,229,255'
#property indicator_style3 STYLE_DOT
#property indicator_width3 1
#property indicator_label4 "Dev VAH (2)"
#property indicator_type4 DRAW_LINE
#property indicator_color4 C'0,229,255'
#property indicator_style4 STYLE_DOT
#property indicator_width4 1
#property indicator_label5 "Dev VAL"
#property indicator_type5 DRAW_LINE
#property indicator_color5 C'255,64,129'
#property indicator_style5 STYLE_DOT
#property indicator_width5 1
#property indicator_label6 "Dev VAL (2)"
#property indicator_type6 DRAW_LINE
#property indicator_color6 C'255,64,129'
#property indicator_style6 STYLE_DOT
#property indicator_width6 1
enum ENUM_SESSION_TYPE
{
SESSION_DAILY,
SESSION_WEEKLY,
SESSION_MONTHLY,
SESSION_INTRADAY
};
enum ENUM_PROFILE_MODE
{
PROFILE_TPO,
PROFILE_VOLUME,
PROFILE_HYBRID
};
enum ENUM_THEME
{
THEME_NEON_DARK,
THEME_SUNSET,
THEME_OCEAN,
THEME_MATRIX,
THEME_ROYAL,
THEME_CUSTOM
};
input group "=== 🎨 Theme ==="
input ENUM_THEME InpTheme = THEME_NEON_DARK;
input bool InpGlowEffect = true;
input int InpGlowIntensity = 3;
input bool InpShowInfoPanel = false;
input int InpPanelCorner = CORNER_RIGHT_UPPER;
input int InpPanelX = 20;
input int InpPanelY = 40;
input group "=== Profile Settings ==="
input ENUM_SESSION_TYPE InpSession = SESSION_DAILY;
input ENUM_PROFILE_MODE InpProfileMode = PROFILE_HYBRID;
input int InpIntradayHours = 4;
input int InpSessionsToShow = 5;
input double InpValueAreaPercent = 70.0;
input int InpProfileWidth = 35;
input group "=== 🔥 Bimodal VA Detection (Idea #1) ==="
input bool InpEnableBimodal = true; // Detect split/bimodal distributions
input double InpBimodalDipRatio = 0.55; // Valley must be <= N * peak height (0.4-0.7)
input int InpMinPeakDistance = 8; // Min levels between peaks
input double InpSecondaryPOCMinPct= 35.0; // Secondary POC min % of primary
input group "=== 🔥 Delta Profile (Idea #2) ==="
input bool InpEnableDelta = true; // Tint bars by order flow delta
input bool InpUseRealTicks = true; // Use CopyTicks (exchange data)
input double InpAbsorptionThresh = 0.4; // Absorption detection sensitivity
input group "=== 🔥 Composite Memory (Idea #3) ==="
input bool InpEnableComposite = true; // Multi-session composite profile
input int InpCompositeSessions = 20; // Sessions in composite memory
input double InpDecayFactor = 0.92; // Exponential decay (0.8-0.98)
input bool InpShowHVN_LVN = true; // Mark High/Low Volume Nodes
input bool InpShowNakedPOCs = true; // Untested past POCs
input int InpMaxNakedPOCs = 10; // Max naked POCs shown
input group "=== Price Resolution ==="
input int InpPointMultiplier = 0;
input int InpMaxPriceLevels = 300;
input group "=== 🎨 Custom Colors (only for THEME_CUSTOM) ==="
input color InpCustomProfile = clrSteelBlue;
input color InpCustomVA = clrDarkOrange;
input color InpCustomPOC = clrGold;
input color InpCustomSP = clrMagenta;
input color InpCustomBG = C'15,20,35';
input group "=== Visualization ==="
input bool InpShowSinglePrints = true;
input bool InpShowPOCLine = true;
input bool InpShowVALines = true;
input bool InpShowProfileLabels = true;
input bool InpShowSessionDivider= true;
input group "=== Developing Levels ==="
input bool InpEnableDevPOC = true;
input bool InpEnableDevVA = true;
input group "=== Rays ==="
input bool InpShowPOCRays = true;
input bool InpShowVARays = false;
input bool InpStopRaysOnCross = true;
input group "=== Alerts ==="
input bool InpAlertOnPOCCross = false;
input bool InpAlertOnVACross = false;
input bool InpAlertOnNakedTest = false; // Alert on naked POC test
input bool InpAlertOnAbsorption = false; // Alert on absorption detected
input bool InpAlertPopup = true;
input bool InpAlertPush = false;
input bool InpAlertEmail = false;
input group "=== Performance ==="
input int InpThrottleMs = 500;
input bool InpDisableOnWrongTF = false;
double DevPOC_1[], DevPOC_2[];
double DevVAH_1[], DevVAH_2[];
double DevVAL_1[], DevVAL_2[];
struct ThemePalette
{
color profile_base;
color profile_accent;
color va_color;
color va_accent;
color poc_color;
color poc_glow;
color single_print;
color panel_bg;
color panel_border;
color panel_text;
color panel_accent;
color divider;
color label_shadow;
color secondary_poc;
color dead_zone;
color delta_buy;
color delta_sell;
color absorption;
color hvn_color;
color lvn_color;
color naked_poc;
color composite_bg;
};
ThemePalette g_palette;
struct NakedPOC
{
double price;
datetime created;
int age_sessions;
bool tested;
};
struct SessionProfile
{
datetime start_time;
datetime end_time;
int start_bar;
int end_bar;
double high;
double low;
double poc_price;
double secondary_poc; // Bimodal: secondary POC (0 if unimodal)
double dead_zone_top; // Bimodal: dead zone bounds
double dead_zone_bot;
bool is_bimodal;
double vah_price;
double val_price;
double vah2_price; // Bimodal: second VA
double val2_price;
double total_activity;
double delta_total; // Delta: net buy-sell
double delta_at_poc; // Delta: delta at POC price
bool absorption_poc; // Delta: absorption detected at POC
};
string g_prefix = "MPU_";
string g_suffix = "";
double g_tick_size = 0;
int g_point_mult = 1;
bool g_init_failed = false;
datetime g_last_redraw = 0;
int g_sessions_calc = 0;
double g_last_poc = 0;
double g_last_vah = 0;
double g_last_val = 0;
datetime g_last_alert_bar = 0;
double g_composite_prices[];
double g_composite_activity[];
int g_composite_levels = 0;
double g_composite_low = 0;
double g_composite_high = 0;
int g_composite_sessions = 1;
bool g_show_naked_pocs = false;
NakedPOC g_naked_pocs[];
int g_naked_count = 0;
int OnInit()
{
// код доступен только после оплаты
}
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, g_prefix);
ChartRedraw(0);
}
void ApplyTheme()
{
switch(InpTheme)
{
case THEME_NEON_DARK:
g_palette.profile_base = C'72,96,140';
g_palette.profile_accent = C'120,160,220';
g_palette.va_color = C'0,188,212';
g_palette.va_accent = C'0,229,255';
g_palette.poc_color = C'255,193,7';
g_palette.poc_glow = C'255,152,0';
g_palette.single_print = C'255,64,129';
g_palette.panel_bg = C'18,24,38';
g_palette.panel_border = C'0,229,255';
g_palette.panel_text = C'220,230,240';
g_palette.panel_accent = C'255,193,7';
g_palette.divider = C'40,60,90';
g_palette.label_shadow = C'5,10,20';
g_palette.secondary_poc = C'255,140,0';
g_palette.dead_zone = C'60,30,80';
g_palette.delta_buy = C'0,230,118';
g_palette.delta_sell = C'255,82,82';
g_palette.absorption = C'255,64,129';
g_palette.hvn_color = C'124,77,255';
g_palette.lvn_color = C'255,241,118';
g_palette.naked_poc = C'255,109,176';
g_palette.composite_bg = C'30,40,65';
break;
case THEME_SUNSET:
g_palette.profile_base = C'180,80,120';
g_palette.profile_accent = C'240,120,80';
g_palette.va_color = C'255,140,60';
g_palette.va_accent = C'255,200,80';
g_palette.poc_color = C'255,235,130';
g_palette.poc_glow = C'255,180,60';
g_palette.single_print = C'220,80,180';
g_palette.panel_bg = C'40,15,30';
g_palette.panel_border = C'255,140,60';
g_palette.panel_text = C'255,220,200';
g_palette.panel_accent = C'255,235,130';
g_palette.divider = C'120,40,60';
g_palette.label_shadow = C'20,5,15';
g_palette.secondary_poc = C'240,120,80';
g_palette.dead_zone = C'80,30,40';
g_palette.delta_buy = C'180,220,100';
g_palette.delta_sell = C'220,60,80';
g_palette.absorption = C'255,80,180';
g_palette.hvn_color = C'200,100,160';
g_palette.lvn_color = C'255,220,140';
g_palette.naked_poc = C'255,180,60';
g_palette.composite_bg = C'60,25,40';
break;
case THEME_OCEAN:
g_palette.profile_base = C'40,80,120';
g_palette.profile_accent = C'70,140,200';
g_palette.va_color = C'0,150,180';
g_palette.va_accent = C'80,220,220';
g_palette.poc_color = C'255,255,255';
g_palette.poc_glow = C'180,240,255';
g_palette.single_print = C'255,100,100';
g_palette.panel_bg = C'10,25,45';
g_palette.panel_border = C'80,220,220';
g_palette.panel_text = C'200,230,240';
g_palette.panel_accent = C'255,255,255';
g_palette.divider = C'30,60,90';
g_palette.label_shadow = C'5,15,25';
g_palette.secondary_poc = C'180,240,255';
g_palette.dead_zone = C'15,45,70';
g_palette.delta_buy = C'0,220,180';
g_palette.delta_sell = C'255,120,100';
g_palette.absorption = C'255,180,100';
g_palette.hvn_color = C'140,180,255';
g_palette.lvn_color = C'200,220,240';
g_palette.naked_poc = C'150,240,240';
g_palette.composite_bg = C'15,40,65';
break;
case THEME_MATRIX:
g_palette.profile_base = C'20,80,40';
g_palette.profile_accent = C'60,180,80';
g_palette.va_color = C'0,200,100';
g_palette.va_accent = C'80,255,140';
g_palette.poc_color = C'180,255,180';
g_palette.poc_glow = C'100,255,100';
g_palette.single_print = C'255,200,60';
g_palette.panel_bg = C'5,20,10';
g_palette.panel_border = C'80,255,140';
g_palette.panel_text = C'180,255,200';
g_palette.panel_accent = C'255,255,255';
g_palette.divider = C'20,60,30';
g_palette.label_shadow = C'0,10,5';
g_palette.secondary_poc = C'120,255,100';
g_palette.dead_zone = C'10,30,15';
g_palette.delta_buy = C'100,255,140';
g_palette.delta_sell = C'255,80,80';
g_palette.absorption = C'255,200,60';
g_palette.hvn_color = C'60,220,120';
g_palette.lvn_color = C'200,255,200';
g_palette.naked_poc = C'180,255,100';
g_palette.composite_bg = C'10,35,20';
break;
case THEME_ROYAL:
g_palette.profile_base = C'80,50,140';
g_palette.profile_accent = C'140,90,220';
g_palette.va_color = C'180,140,255';
g_palette.va_accent = C'220,180,255';
g_palette.poc_color = C'255,215,0';
g_palette.poc_glow = C'255,180,60';
g_palette.single_print = C'255,100,200';
g_palette.panel_bg = C'25,15,40';
g_palette.panel_border = C'255,215,0';
g_palette.panel_text = C'230,220,255';
g_palette.panel_accent = C'255,215,0';
g_palette.divider = C'60,40,100';
g_palette.label_shadow = C'10,5,20';
g_palette.secondary_poc = C'220,150,60';
g_palette.dead_zone = C'40,25,65';
g_palette.delta_buy = C'160,220,80';
g_palette.delta_sell = C'220,80,140';
g_palette.absorption = C'255,100,200';
g_palette.hvn_color = C'200,150,255';
g_palette.lvn_color = C'255,220,180';
g_palette.naked_poc = C'255,180,100';
g_palette.composite_bg = C'35,20,60';
break;
case THEME_CUSTOM:
default:
g_palette.profile_base = InpCustomProfile;
g_palette.profile_accent = LightenColor(InpCustomProfile, 1.4);
g_palette.va_color = InpCustomVA;
g_palette.va_accent = LightenColor(InpCustomVA, 1.3);
g_palette.poc_color = InpCustomPOC;
g_palette.poc_glow = LightenColor(InpCustomPOC, 0.7);
g_palette.single_print = InpCustomSP;
g_palette.panel_bg = InpCustomBG;
g_palette.panel_border = InpCustomPOC;
g_palette.panel_text = C'220,230,240';
g_palette.panel_accent = InpCustomPOC;
g_palette.divider = C'60,60,80';
g_palette.label_shadow = C'0,0,0';
g_palette.secondary_poc = LightenColor(InpCustomPOC, 0.8);
g_palette.dead_zone = C'40,40,55';
g_palette.delta_buy = C'0,220,120';
g_palette.delta_sell = C'240,80,80';
g_palette.absorption = InpCustomSP;
g_palette.hvn_color = C'140,100,220';
g_palette.lvn_color = C'240,220,140';
g_palette.naked_poc = C'240,120,180';
g_palette.composite_bg = C'30,35,55';
break;
}
}
bool ValidateTimeframe()
{
int tf = PeriodSeconds();
bool ok = false;
string msg = "";
switch(InpSession)
{
case SESSION_DAILY:
ok = (tf >= PeriodSeconds(PERIOD_M1) && tf <= PeriodSeconds(PERIOD_M30));
msg = "Daily requires M1-M30";
break;
case SESSION_WEEKLY:
ok = (tf >= PeriodSeconds(PERIOD_M15) && tf <= PeriodSeconds(PERIOD_H4));
msg = "Weekly requires M15-H4";
break;
case SESSION_MONTHLY:
ok = (tf >= PeriodSeconds(PERIOD_H1) && tf <= PeriodSeconds(PERIOD_D1));
msg = "Monthly requires H1-D1";
break;
case SESSION_INTRADAY:
ok = (tf >= PeriodSeconds(PERIOD_M1) && tf <= PeriodSeconds(PERIOD_M15));
msg = "Intraday requires M1-M15";
break;
}
if(!ok)
Print("⚠ ", msg, ". Current: ", EnumToString(_Period));
return ok;
}
void CalculatePointMultiplier()
{
if(InpPointMultiplier > 0)
{
g_point_mult = InpPointMultiplier;
return;
}
double atr = 0;
int h = iATR(_Symbol, _Period, 14);
if(h != INVALID_HANDLE)
{
double buf[1];
if(CopyBuffer(h, 0, 0, 1, buf) == 1)
atr = buf[0];
IndicatorRelease(h);
}
if(atr <= 0)
atr = SymbolInfoDouble(_Symbol, SYMBOL_BID) * 0.005;
double raw = atr / _Point;
g_point_mult = (int)MathMax(1, MathRound(raw / 150.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[])
{
// код доступен только после оплаты
}
void DeleteProfileObjects()
{
int total = ObjectsTotal(0, 0, -1);
for(int i = total - 1; i >= 0; i--)
{
string name = ObjectName(0, i, 0, -1);
if(StringFind(name, g_prefix) == 0 && StringFind(name, g_prefix + "PANEL") != 0)
ObjectDelete(0, name);
}
}
bool FindSessionBounds(const datetime &time[], const int rates_total,
const int search_from_bar, int &session_start_bar)
{
if(search_from_bar >= rates_total)
return false;
datetime t_ref = time[search_from_bar];
MqlDateTime dt_ref;
TimeToStruct(t_ref, dt_ref);
for(int bar = search_from_bar; bar < rates_total - 1; bar++)
{
datetime t_cur = time[bar];
datetime t_prev = time[bar + 1];
MqlDateTime dt_cur, dt_prev;
TimeToStruct(t_cur, dt_cur);
TimeToStruct(t_prev, dt_prev);
bool boundary = false;
switch(InpSession)
{
case SESSION_DAILY:
boundary = (dt_cur.day != dt_prev.day ||
dt_cur.mon != dt_prev.mon ||
dt_cur.year != dt_prev.year);
if(bar > search_from_bar && boundary)
{
session_start_bar = bar;
return true;
}
break;
case SESSION_WEEKLY:
if(bar > search_from_bar && dt_cur.day_of_week < dt_prev.day_of_week)
{
session_start_bar = bar;
return true;
}
break;
case SESSION_MONTHLY:
if(bar > search_from_bar &&
(dt_cur.mon != dt_prev.mon || dt_cur.year != dt_prev.year))
{
session_start_bar = bar;
return true;
}
break;
case SESSION_INTRADAY:
{
int hours_diff = (int)((t_ref - t_cur) / 3600);
if(hours_diff >= InpIntradayHours)
{
session_start_bar = bar;
return true;
}
break;
}
}
}
session_start_bar = rates_total - 1;
return true;
}
void BuildSessionProfile(SessionProfile &prof,
const double &high[], const double &low[], const double &close[],
const long &tick_volume[], const datetime &time[],
const int session_index, const bool draw_visuals)
{
double s_high = -DBL_MAX, s_low = DBL_MAX;
for(int b = prof.end_bar; b <= prof.start_bar; b++)
{
if(high[b] > s_high)
s_high = high[b];
if(low[b] < s_low)
s_low = low[b];
}
prof.high = s_high;
prof.low = s_low;
if(s_high <= s_low)
return;
s_high = MathCeil(s_high / g_tick_size) * g_tick_size;
s_low = MathFloor(s_low / g_tick_size) * g_tick_size;
int levels = (int)MathRound((s_high - s_low) / g_tick_size) + 1;
if(levels > InpMaxPriceLevels)
levels = InpMaxPriceLevels;
if(levels < 2)
return;
double activity[], prices[];
ArrayResize(activity, levels);
ArrayResize(prices, levels);
ArrayInitialize(activity, 0.0);
for(int i = 0; i < levels; i++)
prices[i] = s_low + i * g_tick_size;
double total = 0;
for(int b = prof.end_bar; b <= prof.start_bar; b++)
{
double weight = 1.0;
if(InpProfileMode == PROFILE_VOLUME)
weight = (double)tick_volume[b];
else
if(InpProfileMode == PROFILE_HYBRID)
weight = 1.0 + MathLog(1.0 + (double)tick_volume[b]);
int lo_idx = (int)MathMax(0, MathRound((low[b] - s_low) / g_tick_size));
int hi_idx = (int)MathMin(levels - 1, MathRound((high[b] - s_low) / g_tick_size));
for(int i = lo_idx; i <= hi_idx; i++)
{
activity[i] += weight;
total += weight;
}
}
prof.total_activity = total;
if(total <= 0)
return;
int poc_idx = 0;
double poc_val = activity[0];
for(int i = 1; i < levels; i++)
if(activity[i] > poc_val)
{
poc_val = activity[i];
poc_idx = i;
}
prof.poc_price = prices[poc_idx];
double target = total * (InpValueAreaPercent / 100.0);
double va_sum = activity[poc_idx];
int va_hi = poc_idx, va_lo = poc_idx;
while(va_sum < target && (va_hi < levels - 1 || va_lo > 0))
{
double up_pair = 0, dn_pair = 0;
if(va_hi + 1 < levels)
up_pair += activity[va_hi + 1];
if(va_hi + 2 < levels)
up_pair += activity[va_hi + 2];
if(va_lo - 1 >= 0)
dn_pair += activity[va_lo - 1];
if(va_lo - 2 >= 0)
dn_pair += activity[va_lo - 2];
if(up_pair >= dn_pair && va_hi < levels - 1)
{
va_hi++;
va_sum += activity[va_hi];
if(va_hi < levels - 1 && va_sum < target)
{ va_hi++; va_sum += activity[va_hi]; }
}
else
if(va_lo > 0)
{
va_lo--;
va_sum += activity[va_lo];
if(va_lo > 0 && va_sum < target)
{ va_lo--; va_sum += activity[va_lo]; }
}
else
break;
}
prof.vah_price = prices[va_hi];
prof.val_price = prices[va_lo];
prof.is_bimodal = false;
prof.secondary_poc = 0;
prof.dead_zone_top = 0;
prof.dead_zone_bot = 0;
prof.vah2_price = 0;
prof.val2_price = 0;
if(InpEnableBimodal)
DetectBimodal(prof, prices, activity, levels, poc_idx, poc_val, total);
prof.delta_total = 0;
prof.delta_at_poc = 0;
prof.absorption_poc = false;
double delta_arr[];
bool have_delta = false;
if(InpEnableDelta && draw_visuals)
{
ComputeDeltaProfile(prof, prices, levels, delta_arr, tick_volume, time);
if(ArraySize(delta_arr) == levels)
{
have_delta = true;
for(int i = 0; i < levels; i++)
prof.delta_total += delta_arr[i];
prof.delta_at_poc = delta_arr[poc_idx];
double delta_magnitude = MathAbs(prof.delta_at_poc);
double activity_here = activity[poc_idx];
if(activity_here > 0 && delta_magnitude / activity_here > InpAbsorptionThresh)
prof.absorption_poc = true;
}
}
if(draw_visuals)
{
DrawPremiumHistogram(prof, prices, activity, levels, poc_val, va_hi, va_lo,
time, session_index);
if(have_delta)
ApplyDeltaTint(prof, prices, delta_arr, levels, session_index);
if(prof.is_bimodal)
DrawBimodalFeatures(prof, session_index);
if(InpShowPOCLine)
DrawGlowLine(prof, prof.poc_price, "POC", g_palette.poc_color,
g_palette.poc_glow, STYLE_SOLID, 2, session_index, true);
if(InpShowVALines)
{
DrawGlowLine(prof, prof.vah_price, "VAH", g_palette.va_accent,
g_palette.va_color, STYLE_DASH, 1, session_index, false);
DrawGlowLine(prof, prof.val_price, "VAL", g_palette.va_accent,
g_palette.va_color, STYLE_DASH, 1, session_index, false);
}
if(InpShowSinglePrints)
DrawSinglePrints(prof, prices, activity, levels, time, session_index);
if(session_index == 0)
{
if(InpShowPOCRays)
DrawRay(prof, prof.poc_price, "POC_RAY", g_palette.poc_color, time);
if(InpShowVARays)
{
DrawRay(prof, prof.vah_price, "VAH_RAY", g_palette.va_accent, time);
DrawRay(prof, prof.val_price, "VAL_RAY", g_palette.va_color, time);
}
}
if(InpShowProfileLabels)
DrawPremiumLabels(prof, session_index);
}
}
void DetectBimodal(SessionProfile &prof, const double &prices[],
const double &activity[], const int levels,
const int primary_poc_idx, const double primary_poc_val,
const double total)
{
int peaks[];
double peak_vals[];
ArrayResize(peaks, 0);
ArrayResize(peak_vals, 0);
double min_peak_height = primary_poc_val * (InpSecondaryPOCMinPct / 100.0);
for(int i = 1; i < levels - 1; i++)
{
bool is_peak = true;
int check_range = MathMax(2, InpMinPeakDistance / 2);
for(int d = 1; d <= check_range; d++)
{
int left = i - d;
int right = i + d;
if(left >= 0 && activity[left] >= activity[i])
{
is_peak = false;
break;
}
if(right < levels && activity[right] > activity[i])
{
is_peak = false;
break;
}
}
if(is_peak && activity[i] >= min_peak_height)
{
int sz = ArraySize(peaks);
ArrayResize(peaks, sz + 1);
ArrayResize(peak_vals, sz + 1);
peaks[sz] = i;
peak_vals[sz] = activity[i];
}
}
int n_peaks = ArraySize(peaks);
if(n_peaks < 2)
return;
int secondary_idx = -1;
double secondary_val = 0;
for(int p = 0; p < n_peaks; p++)
{
if(MathAbs(peaks[p] - primary_poc_idx) < InpMinPeakDistance)
continue;
if(peak_vals[p] > secondary_val)
{
secondary_val = peak_vals[p];
secondary_idx = peaks[p];
}
}
if(secondary_idx < 0)
return;
int lo = MathMin(primary_poc_idx, secondary_idx);
int hi = MathMax(primary_poc_idx, secondary_idx);
double valley_min = DBL_MAX;
int valley_idx = lo;
for(int i = lo + 1; i < hi; i++)
{
if(activity[i] < valley_min)
{
valley_min = activity[i];
valley_idx = i;
}
}
double min_peak = MathMin(activity[primary_poc_idx], secondary_val);
if(valley_min > min_peak * InpBimodalDipRatio)
return; // Not deep enough
prof.is_bimodal = true;
prof.secondary_poc = prices[secondary_idx];
double dz_threshold = valley_min + (min_peak - valley_min) * 0.25;
int dz_lo = valley_idx, dz_hi = valley_idx;
while(dz_lo > lo && activity[dz_lo] < dz_threshold)
dz_lo--;
while(dz_hi < hi && activity[dz_hi] < dz_threshold)
dz_hi++;
prof.dead_zone_bot = prices[dz_lo];
prof.dead_zone_top = prices[dz_hi];
double sec_target = secondary_val * 1.5; // heuristic: capture secondary cluster
double sec_sum = activity[secondary_idx];
int sec_hi = secondary_idx, sec_lo = secondary_idx;
int sec_bound_lo = (secondary_idx > primary_poc_idx) ? dz_hi : 0;
int sec_bound_hi = (secondary_idx > primary_poc_idx) ? levels - 1 : dz_lo;
while(sec_sum < sec_target && (sec_hi < sec_bound_hi || sec_lo > sec_bound_lo))
{
double up = (sec_hi + 1 <= sec_bound_hi) ? activity[sec_hi + 1] : 0;
double dn = (sec_lo - 1 >= sec_bound_lo) ? activity[sec_lo - 1] : 0;
if(up >= dn && sec_hi < sec_bound_hi)
{
sec_hi++;
sec_sum += activity[sec_hi];
}
else
if(sec_lo > sec_bound_lo)
{
sec_lo--;
sec_sum += activity[sec_lo];
}
else
break;
}
prof.vah2_price = prices[sec_hi];
prof.val2_price = prices[sec_lo];
}
void DrawBimodalFeatures(const SessionProfile &prof, const int session_index)
{
if(!prof.is_bimodal)
return;
string dz_name = StringFormat("%sDEADZONE_%s_%d", g_prefix, g_suffix, session_index);
if(ObjectCreate(0, dz_name, OBJ_RECTANGLE, 0,
prof.start_time, prof.dead_zone_top,
prof.end_time, prof.dead_zone_bot))
{
ObjectSetInteger(0, dz_name, OBJPROP_COLOR, g_palette.dead_zone);
ObjectSetInteger(0, dz_name, OBJPROP_FILL, true);
ObjectSetInteger(0, dz_name, OBJPROP_BACK, true);
ObjectSetInteger(0, dz_name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, dz_name, OBJPROP_HIDDEN, true);
ObjectSetInteger(0, dz_name, OBJPROP_WIDTH, 1);
}
string sp_name = StringFormat("%sPOC2_%s_%d", g_prefix, g_suffix, session_index);
if(ObjectCreate(0, sp_name, OBJ_TREND, 0,
prof.start_time, prof.secondary_poc,
prof.end_time, prof.secondary_poc))
{
ObjectSetInteger(0, sp_name, OBJPROP_COLOR, g_palette.secondary_poc);
ObjectSetInteger(0, sp_name, OBJPROP_STYLE, STYLE_DASH);
ObjectSetInteger(0, sp_name, OBJPROP_WIDTH, 2);
ObjectSetInteger(0, sp_name, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(0, sp_name, OBJPROP_BACK, false);
ObjectSetInteger(0, sp_name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, sp_name, OBJPROP_HIDDEN, true);
}
if(InpShowProfileLabels && session_index == 0)
{
string lbl_name = StringFormat("%sLBL_POC2_%s_%d", g_prefix, g_suffix, session_index);
if(ObjectCreate(0, lbl_name, OBJ_TEXT, 0, prof.end_time, prof.secondary_poc))
{
ObjectSetString(0, lbl_name, OBJPROP_TEXT,
" ◇ POC2 " + DoubleToString(prof.secondary_poc, _Digits));
ObjectSetInteger(0, lbl_name, OBJPROP_COLOR, g_palette.secondary_poc);
ObjectSetInteger(0, lbl_name, OBJPROP_FONTSIZE, 9);
ObjectSetString(0, lbl_name, OBJPROP_FONT, "Consolas");
ObjectSetInteger(0, lbl_name, OBJPROP_ANCHOR, ANCHOR_LEFT);
ObjectSetInteger(0, lbl_name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, lbl_name, OBJPROP_HIDDEN, true);
}
}
}
void ComputeDeltaProfile(const SessionProfile &prof, const double &prices[],
const int levels, double &delta_out[],
const long &tick_volume[], const datetime &time[])
{
ArrayResize(delta_out, levels);
ArrayInitialize(delta_out, 0.0);
double s_low = prices[0];
if(InpUseRealTicks)
{
MqlTick ticks[];
int copied = CopyTicksRange(_Symbol, ticks, COPY_TICKS_TRADE,
(ulong)prof.start_time * 1000,
(ulong)prof.end_time * 1000);
if(copied > 0)
{
for(int i = 0; i < copied; i++)
{
double p = ticks[i].last;
if(p <= 0)
p = (ticks[i].bid + ticks[i].ask) / 2.0;
if(p <= 0)
continue;
int idx = (int)MathRound((p - s_low) / g_tick_size);
if(idx < 0 || idx >= levels)
continue;
double vol = (ticks[i].volume_real > 0) ? ticks[i].volume_real : (double)ticks[i].volume;
if(vol <= 0)
vol = 1.0;
if((ticks[i].flags & TICK_FLAG_BUY) != 0)
delta_out[idx] += vol;
else
if((ticks[i].flags & TICK_FLAG_SELL) != 0)
delta_out[idx] -= vol;
}
return;
}
}
for(int b = prof.end_bar; b <= prof.start_bar; b++)
{
double o = iOpen(_Symbol, _Period, b);
double c = iClose(_Symbol, _Period, b);
double h = iHigh(_Symbol, _Period, b);
double l = iLow(_Symbol, _Period, b);
double vol = (double)tick_volume[b];
if(vol <= 0)
continue;
double bias = (c > o) ? 0.65 : (c < o) ? -0.65 : 0.0;
int lo_idx = (int)MathMax(0, MathRound((l - s_low) / g_tick_size));
int hi_idx = (int)MathMin(levels - 1, MathRound((h - s_low) / g_tick_size));
int span = hi_idx - lo_idx + 1;
Показано первые 50% кода (символов: 53695)
Открыть по тарифу
Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.
Разовая покупка файла
На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.
Зачем нужен email? После подтверждения оплаты файл сохранится в кабинете на этот email. Если аккаунта ещё нет, мы создадим его и пришлём ссылку для задания пароля. Если аккаунт уже есть, покупка добавится в него. Укажите адрес, к которому у вас есть доступ.
Чтобы оформить заказ, введите email и перейдите к оплате.