← К каталогу
Исходный код · ID 4568

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 и перейдите к оплате.

Стоимость полного кода
5 369,50₽ ≈ $66.90