//+------------------------------------------------------------------+ //| CFVGModule.mqh | //| Модуль обнаружения FVG и iFVG (Fair Value Gap) | //| для ICT Entry индикатора | //+------------------------------------------------------------------+ #ifndef CFVG_MODULE_MQH #define CFVG_MODULE_MQH #define FVG_MAX_STORAGE 50 //+------------------------------------------------------------------+ //| Структура зоны FVG/iFVG | //| end_bar == -1 → зона активна | //| end_bar >= 0 → замёрзла (митигирована) | //+------------------------------------------------------------------+ struct SFVGZone { double top; // верхняя граница double bot; // нижняя граница int start_bar; // левый (старый) край (AS_SERIES: бар создания = bar+2) int end_bar; // правый край: -1=активна, иначе=бар митигации string obj_name; // имя OBJ_RECTANGLE на графике ("" = нет объекта) datetime start_time; // datetime левого края зоны }; //+------------------------------------------------------------------+ //| Класс CFVGModule | //+------------------------------------------------------------------+ class CFVGModule { public: CFVGModule(); ~CFVGModule(); //--- Инициализация bool Init(int qty_history, color fvg_col, color ifvg_col, bool show_fvg, bool show_ifvg, bool trend_only, bool eq_filter, bool eq_strict, bool eq_use_lvl2); //--- Сброс + удаление всех объектов с графика void Reset(); //--- Инициализация буферов при полном пересчёте void InitBuffers(double &buf_fvg_top[], double &buf_fvg_bot[], double &buf_ifvg_top[], double &buf_ifvg_bot[], int rates_total); //--- Расчёт на подтверждённом баре (bar >= 1) void Calculate(int bar, const double &high[], const double &low[], const double &close[], const datetime &time[], double &buf_fvg_top[], double &buf_fvg_bot[], double &buf_ifvg_top[], double &buf_ifvg_bot[], int rates_total, int in_dir_big, const double &buf_eq[], const double &buf_premium2[], const double &buf_discount2[]); //--- Продление активных зон на live bar=0 (в направлении тренда) void UpdateBar0(int in_dir_big, double &buf_fvg_top[], double &buf_fvg_bot[], double &buf_ifvg_top[], double &buf_ifvg_bot[], double eq_bar1, double premium2_bar1, double discount2_bar1); //--- Удалить все FVG/iFVG объекты с графика (вызывать в OnDeinit) void Cleanup(); //--- Обновить видимость объектов согласно фильтрам void ApplyVisibility(int in_dir_big, double current_price, double eq_value, double premium2_value, double discount2_value); //--- Доступ к данным для Entry-сигналов bool HasDemandFVG() const; bool HasSupplyFVG() const; bool HasDemandIFVG() const; bool HasSupplyIFVG() const; double GetDemandFVGTop() const; double GetDemandFVGBot() const; double GetSupplyFVGTop() const; double GetSupplyFVGBot() const; double GetDemandIFVGTop() const; double GetDemandIFVGBot() const; double GetSupplyIFVGTop() const; double GetSupplyIFVGBot() const; private: int m_hist_bars; color m_fvg_col; // цвет FVG объектов color m_ifvg_col; // цвет iFVG объектов bool m_show_fvg; // показывать FVG объекты bool m_show_ifvg; // показывать iFVG объекты bool m_trend_only; // только активные зоны по тренду bool m_eq_filter; // фильтр по Equilibrium bool m_eq_strict; // строгий: вся зона за линией bool m_eq_use_lvl2; // использовать 2-й уровень //--- Массивы зон (index 0 = новейшая). cnt = ВСЕГО зон (активные + замороженные) SFVGZone m_dfvg_zones[]; int m_dfvg_cnt; SFVGZone m_sfvg_zones[]; int m_sfvg_cnt; //--- Pre-Inversion FVG (кандидаты на инверсию): параллельные массивы double m_dfvg_R_top[], m_dfvg_R_bot[]; int m_dfvg_R_bar[]; int m_dfvg_R_cnt; double m_sfvg_R_top[], m_sfvg_R_bot[]; int m_sfvg_R_bar[]; int m_sfvg_R_cnt; //--- iFVG зоны SFVGZone m_ifvg_d_zones[]; int m_ifvg_d_cnt; SFVGZone m_ifvg_s_zones[]; int m_ifvg_s_cnt; //--- Вспомогательные методы void UnshiftZone(SFVGZone &zones[], int &cnt, int max_cnt, double top, double bot, int start_bar, datetime start_time); void UnshiftPreInv(double &top_arr[], double &bot_arr[], int &bar_arr[], int &cnt, double top, double bot, int bar_idx); void RemovePreInv(double &top_arr[], double &bot_arr[], int &bar_arr[], int &cnt, int idx); void FillZoneRange(double &buf_top[], double &buf_bot[], double top, double bot, int start_bar, int cur_bar, int rates_total); void PruneOldZones(SFVGZone &zones[], int &cnt, int max_cnt, double &buf_top[], double &buf_bot[], int rates_total); int FindActiveZone(const SFVGZone &zones[], int cnt) const; //--- Создать OBJ_RECTANGLE на графике, вернуть имя объекта string CreateRect(string prefix, datetime t1, double price_top, datetime t2, double price_bot, color clr); //--- Обновить правый край объекта (ObjectMove point 1) void MoveRectRight(string name, datetime t, double price_bot); double ResolveEqLevel(int in_dir_big, double eq_val, double premium2_val, double discount2_val) const; bool PassesEqFilter(double zone_top, double zone_bot, int in_dir_big, bool zone_is_demand, double eq_value) const; }; //+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CFVGModule::CFVGModule() { m_hist_bars = 0; m_fvg_col = clrDodgerBlue; m_ifvg_col = clrDeepSkyBlue; m_show_fvg = true; m_show_ifvg = true; m_trend_only = true; m_eq_filter = false; m_eq_strict = false; m_eq_use_lvl2 = false; m_dfvg_cnt = 0; m_sfvg_cnt = 0; m_dfvg_R_cnt = 0; m_sfvg_R_cnt = 0; m_ifvg_d_cnt = 0; m_ifvg_s_cnt = 0; } //+------------------------------------------------------------------+ //| Деструктор | //+------------------------------------------------------------------+ CFVGModule::~CFVGModule() { } //+------------------------------------------------------------------+ //| Инициализация | //+------------------------------------------------------------------+ bool CFVGModule::Init(int qty_history, color fvg_col, color ifvg_col, bool show_fvg, bool show_ifvg, bool trend_only, bool eq_filter, bool eq_strict, bool eq_use_lvl2) { m_hist_bars = (qty_history >= 0) ? qty_history : 0; m_fvg_col = fvg_col; m_ifvg_col = ifvg_col; m_show_fvg = show_fvg; m_show_ifvg = show_ifvg; m_trend_only = trend_only; m_eq_filter = eq_filter; m_eq_strict = eq_strict; m_eq_use_lvl2 = eq_use_lvl2; int max_zones = FVG_MAX_STORAGE + 1; int max_preinv = 20; if(ArrayResize(m_dfvg_zones, max_zones) < 0) return false; if(ArrayResize(m_sfvg_zones, max_zones) < 0) return false; if(ArrayResize(m_ifvg_d_zones, max_zones) < 0) return false; if(ArrayResize(m_ifvg_s_zones, max_zones) < 0) return false; if(ArrayResize(m_dfvg_R_top, max_preinv) < 0) return false; if(ArrayResize(m_dfvg_R_bot, max_preinv) < 0) return false; if(ArrayResize(m_dfvg_R_bar, max_preinv) < 0) return false; if(ArrayResize(m_sfvg_R_top, max_preinv) < 0) return false; if(ArrayResize(m_sfvg_R_bot, max_preinv) < 0) return false; if(ArrayResize(m_sfvg_R_bar, max_preinv) < 0) return false; Reset(); return true; } //+------------------------------------------------------------------+ //| Сброс состояния + удаление объектов с графика | //+------------------------------------------------------------------+ void CFVGModule::Reset() { Cleanup(); m_dfvg_cnt = 0; m_sfvg_cnt = 0; m_dfvg_R_cnt = 0; m_sfvg_R_cnt = 0; m_ifvg_d_cnt = 0; m_ifvg_s_cnt = 0; } //+------------------------------------------------------------------+ //| Удалить все FVG/iFVG объекты с графика | //+------------------------------------------------------------------+ void CFVGModule::Cleanup() { ObjectsDeleteAll(0, "ICTE_FVG_D_"); ObjectsDeleteAll(0, "ICTE_FVG_S_"); ObjectsDeleteAll(0, "ICTE_iFVG_D_"); ObjectsDeleteAll(0, "ICTE_iFVG_S_"); } //+------------------------------------------------------------------+ //| Инициализация буферов нулями (DRAW_FILLING скрыт, но буферы | //| используются для Data Window) | //+------------------------------------------------------------------+ void CFVGModule::InitBuffers(double &buf_fvg_top[], double &buf_fvg_bot[], double &buf_ifvg_top[], double &buf_ifvg_bot[], int rates_total) { ArrayInitialize(buf_fvg_top, 0.0); ArrayInitialize(buf_fvg_bot, 0.0); ArrayInitialize(buf_ifvg_top, 0.0); ArrayInitialize(buf_ifvg_bot, 0.0); } //+------------------------------------------------------------------+ //| Создать OBJ_RECTANGLE, вернуть имя объекта (или "" при ошибке) | //+------------------------------------------------------------------+ string CFVGModule::CreateRect(string prefix, datetime t1, double price_top, datetime t2, double price_bot, color clr) { string name = prefix + IntegerToString((long)t1); if(!ObjectCreate(0, name, OBJ_RECTANGLE, 0, t1, price_top, t2, price_bot)) return ""; ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_FILL, true); ObjectSetInteger(0, name, OBJPROP_BACK, true); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, name, OBJPROP_HIDDEN, true); return name; } //+------------------------------------------------------------------+ //| Сдвинуть правый край объекта (point 1) к новому времени | //+------------------------------------------------------------------+ void CFVGModule::MoveRectRight(string name, datetime t, double price_bot) { if(name != "") ObjectMove(0, name, 1, t, price_bot); } //+------------------------------------------------------------------+ //| Вставить зону в начало массива (index 0 = новейшая) | //+------------------------------------------------------------------+ void CFVGModule::UnshiftZone(SFVGZone &zones[], int &cnt, int max_cnt, double top, double bot, int start_bar, datetime start_time) { int shift_to = MathMin(cnt, max_cnt - 1); for(int i = shift_to; i > 0; i--) zones[i] = zones[i-1]; zones[0].top = top; zones[0].bot = bot; zones[0].start_bar = start_bar; zones[0].end_bar = -1; zones[0].obj_name = ""; zones[0].start_time = start_time; if(cnt < max_cnt) cnt++; } //+------------------------------------------------------------------+ //| Вставить в начало Pre-Inversion массивов | //+------------------------------------------------------------------+ void CFVGModule::UnshiftPreInv(double &top_arr[], double &bot_arr[], int &bar_arr[], int &cnt, double top, double bot, int bar_idx) { int max = ArraySize(top_arr); int shift_to = MathMin(cnt, max - 1); for(int i = shift_to; i > 0; i--) { top_arr[i] = top_arr[i-1]; bot_arr[i] = bot_arr[i-1]; bar_arr[i] = bar_arr[i-1]; } top_arr[0] = top; bot_arr[0] = bot; bar_arr[0] = bar_idx; if(cnt < max) cnt++; } //+------------------------------------------------------------------+ //| Удалить элемент из Pre-Inversion массивов по индексу | //+------------------------------------------------------------------+ void CFVGModule::RemovePreInv(double &top_arr[], double &bot_arr[], int &bar_arr[], int &cnt, int idx) { if(idx < 0 || idx >= cnt) return; for(int i = idx; i < cnt - 1; i++) { top_arr[i] = top_arr[i+1]; bot_arr[i] = bot_arr[i+1]; bar_arr[i] = bar_arr[i+1]; } cnt--; } //+------------------------------------------------------------------+ //| Заполнить диапазон буферов значениями зоны [start_bar..cur_bar] | //+------------------------------------------------------------------+ void CFVGModule::FillZoneRange(double &buf_top[], double &buf_bot[], double top, double bot, int start_bar, int cur_bar, int rates_total) { int from = MathMin(start_bar, rates_total - 1); int to = MathMax(cur_bar, 0); for(int i = from; i >= to; i--) { buf_top[i] = top; buf_bot[i] = bot; } } //+------------------------------------------------------------------+ //| Удалить старейшие зоны если cnt >= max_cnt | //| Вызывать ПЕРЕД UnshiftZone | //+------------------------------------------------------------------+ void CFVGModule::PruneOldZones(SFVGZone &zones[], int &cnt, int max_cnt, double &buf_top[], double &buf_bot[], int rates_total) { while(cnt >= max_cnt) { SFVGZone oldest = zones[cnt - 1]; //--- Удалить объект с графика if(oldest.obj_name != "") ObjectDelete(0, oldest.obj_name); //--- Очистить бары в буфере int right = (oldest.end_bar >= 0) ? oldest.end_bar : 0; int from = MathMin(oldest.start_bar, rates_total - 1); int to = MathMax(right, 0); for(int i = from; i >= to; i--) { buf_top[i] = 0.0; buf_bot[i] = 0.0; } cnt--; } } //+------------------------------------------------------------------+ //| Найти индекс первой (новейшей) активной зоны или -1 | //+------------------------------------------------------------------+ int CFVGModule::FindActiveZone(const SFVGZone &zones[], int cnt) const { for(int i = 0; i < cnt; i++) if(zones[i].end_bar == -1) return i; return -1; } //+------------------------------------------------------------------+ //| Доступ к данным для Entry-сигналов | //+------------------------------------------------------------------+ bool CFVGModule::HasDemandFVG() const { return FindActiveZone(m_dfvg_zones, m_dfvg_cnt) >= 0; } bool CFVGModule::HasSupplyFVG() const { return FindActiveZone(m_sfvg_zones, m_sfvg_cnt) >= 0; } bool CFVGModule::HasDemandIFVG() const { return FindActiveZone(m_ifvg_d_zones, m_ifvg_d_cnt) >= 0; } bool CFVGModule::HasSupplyIFVG() const { return FindActiveZone(m_ifvg_s_zones, m_ifvg_s_cnt) >= 0; } double CFVGModule::GetDemandFVGTop() const { int i = FindActiveZone(m_dfvg_zones, m_dfvg_cnt); return (i >= 0) ? m_dfvg_zones[i].top : 0.0; } double CFVGModule::GetDemandFVGBot() const { int i = FindActiveZone(m_dfvg_zones, m_dfvg_cnt); return (i >= 0) ? m_dfvg_zones[i].bot : 0.0; } double CFVGModule::GetSupplyFVGTop() const { int i = FindActiveZone(m_sfvg_zones, m_sfvg_cnt); return (i >= 0) ? m_sfvg_zones[i].top : 0.0; } double CFVGModule::GetSupplyFVGBot() const { int i = FindActiveZone(m_sfvg_zones, m_sfvg_cnt); return (i >= 0) ? m_sfvg_zones[i].bot : 0.0; } double CFVGModule::GetDemandIFVGTop() const { int i = FindActiveZone(m_ifvg_d_zones, m_ifvg_d_cnt); return (i >= 0) ? m_ifvg_d_zones[i].top : 0.0; } double CFVGModule::GetDemandIFVGBot() const { int i = FindActiveZone(m_ifvg_d_zones, m_ifvg_d_cnt); return (i >= 0) ? m_ifvg_d_zones[i].bot : 0.0; } double CFVGModule::GetSupplyIFVGTop() const { int i = FindActiveZone(m_ifvg_s_zones, m_ifvg_s_cnt); return (i >= 0) ? m_ifvg_s_zones[i].top : 0.0; } double CFVGModule::GetSupplyIFVGBot() const { int i = FindActiveZone(m_ifvg_s_zones, m_ifvg_s_cnt); return (i >= 0) ? m_ifvg_s_zones[i].bot : 0.0; } //+------------------------------------------------------------------+ //| Главный метод расчёта на подтверждённом баре (bar >= 1) | //+------------------------------------------------------------------+ void CFVGModule::Calculate(int bar, const double &high[], const double &low[], const double &close[], const datetime &time[], double &buf_fvg_top[], double &buf_fvg_bot[], double &buf_ifvg_top[], double &buf_ifvg_bot[], int rates_total, int in_dir_big, const double &buf_eq[], const double &buf_premium2[], const double &buf_discount2[]) { if(bar < 1 || bar >= rates_total) return; int max_zones = FVG_MAX_STORAGE + 1; double eq_value = ResolveEqLevel(in_dir_big, (bar < rates_total) ? buf_eq[bar] : EMPTY_VALUE, (bar < rates_total) ? buf_premium2[bar] : EMPTY_VALUE, (bar < rates_total) ? buf_discount2[bar] : EMPTY_VALUE); //========================================================= // ШАГ 1: Митигация FVG //========================================================= //--- Demand FVG: low <= bot → полная митигация { int ai = FindActiveZone(m_dfvg_zones, m_dfvg_cnt); if(ai >= 0 && low[bar] <= m_dfvg_zones[ai].bot) { m_dfvg_zones[ai].end_bar = bar; //--- Зафиксировать правый край объекта на баре митигации MoveRectRight(m_dfvg_zones[ai].obj_name, time[bar], m_dfvg_zones[ai].bot); // Pre-Inversion (dfvg_R) НЕ удаляем: он нужен для инверсии в Supply iFVG. } } //--- Supply FVG: high >= top → полная митигация { int ai = FindActiveZone(m_sfvg_zones, m_sfvg_cnt); if(ai >= 0 && high[bar] >= m_sfvg_zones[ai].top) { m_sfvg_zones[ai].end_bar = bar; MoveRectRight(m_sfvg_zones[ai].obj_name, time[bar], m_sfvg_zones[ai].bot); // Pre-Inversion (sfvg_R) НЕ удаляем: он нужен для инверсии в Demand iFVG. } } //========================================================= // ШАГ 2: Митигация iFVG //========================================================= //--- Demand iFVG: low <= bot { int ai = FindActiveZone(m_ifvg_d_zones, m_ifvg_d_cnt); if(ai >= 0 && low[bar] <= m_ifvg_d_zones[ai].bot) { m_ifvg_d_zones[ai].end_bar = bar; MoveRectRight(m_ifvg_d_zones[ai].obj_name, time[bar], m_ifvg_d_zones[ai].bot); } } //--- Supply iFVG: high >= top { int ai = FindActiveZone(m_ifvg_s_zones, m_ifvg_s_cnt); if(ai >= 0 && high[bar] >= m_ifvg_s_zones[ai].top) { m_ifvg_s_zones[ai].end_bar = bar; MoveRectRight(m_ifvg_s_zones[ai].obj_name, time[bar], m_ifvg_s_zones[ai].bot); } } //========================================================= // ШАГ 3: Инверсия FVG → iFVG (только close, не wick) //========================================================= //--- Supply Pre-Inv → Demand iFVG (close > sfvg_R_top[0]) if(m_sfvg_R_cnt > 0 && close[bar] > m_sfvg_R_top[0]) { double i_top = m_sfvg_R_top[0]; double i_bot = m_sfvg_R_bot[0]; int i_bar = m_sfvg_R_bar[0]; datetime i_time = (i_bar >= 0 && i_bar < rates_total) ? time[i_bar] : time[bar]; PruneOldZones(m_ifvg_d_zones, m_ifvg_d_cnt, max_zones, buf_ifvg_top, buf_ifvg_bot, rates_total); UnshiftZone(m_ifvg_d_zones, m_ifvg_d_cnt, max_zones, i_top, i_bot, i_bar, i_time); //--- Создать объект на графике m_ifvg_d_zones[0].obj_name = CreateRect("ICTE_iFVG_D_", i_time, i_top, time[bar], i_bot, m_ifvg_col); //--- Заполнить буфер (если проходит Equilibrium-фильтр) if(PassesEqFilter(i_top, i_bot, in_dir_big, true, eq_value)) FillZoneRange(buf_ifvg_top, buf_ifvg_bot, i_top, i_bot, i_bar, bar, rates_total); RemovePreInv(m_sfvg_R_top, m_sfvg_R_bot, m_sfvg_R_bar, m_sfvg_R_cnt, 0); } //--- Demand Pre-Inv → Supply iFVG (close < dfvg_R_bot[0]) if(m_dfvg_R_cnt > 0 && close[bar] < m_dfvg_R_bot[0]) { double i_top = m_dfvg_R_top[0]; double i_bot = m_dfvg_R_bot[0]; int i_bar = m_dfvg_R_bar[0]; datetime i_time = (i_bar >= 0 && i_bar < rates_total) ? time[i_bar] : time[bar]; PruneOldZones(m_ifvg_s_zones, m_ifvg_s_cnt, max_zones, buf_ifvg_top, buf_ifvg_bot, rates_total); UnshiftZone(m_ifvg_s_zones, m_ifvg_s_cnt, max_zones, i_top, i_bot, i_bar, i_time); m_ifvg_s_zones[0].obj_name = CreateRect("ICTE_iFVG_S_", i_time, i_top, time[bar], i_bot, m_ifvg_col); if(PassesEqFilter(i_top, i_bot, in_dir_big, false, eq_value)) FillZoneRange(buf_ifvg_top, buf_ifvg_bot, i_top, i_bot, i_bar, bar, rates_total); RemovePreInv(m_dfvg_R_top, m_dfvg_R_bot, m_dfvg_R_bar, m_dfvg_R_cnt, 0); } //========================================================= // ШАГ 4: Создание новых FVG (bar+2 < rates_total) //========================================================= if(bar + 2 < rates_total) { //--- Demand FVG: зазор вверх — low[bar] > high[bar+2] if(low[bar] > high[bar + 2]) { double new_top = low[bar]; double new_bot = high[bar + 2]; int new_start = bar + 2; datetime new_time = time[new_start]; PruneOldZones(m_dfvg_zones, m_dfvg_cnt, max_zones, buf_fvg_top, buf_fvg_bot, rates_total); UnshiftZone(m_dfvg_zones, m_dfvg_cnt, max_zones, new_top, new_bot, new_start, new_time); //--- Создать объект (левый край = new_time, правый = time[bar]) m_dfvg_zones[0].obj_name = CreateRect("ICTE_FVG_D_", new_time, new_top, time[bar], new_bot, m_fvg_col); UnshiftPreInv(m_dfvg_R_top, m_dfvg_R_bot, m_dfvg_R_bar, m_dfvg_R_cnt, new_top, new_bot, new_start); //--- Заполнить буфер (если проходит Equilibrium-фильтр) if(PassesEqFilter(new_top, new_bot, in_dir_big, true, eq_value)) FillZoneRange(buf_fvg_top, buf_fvg_bot, new_top, new_bot, new_start, bar, rates_total); } //--- Supply FVG: зазор вниз — high[bar] < low[bar+2] if(high[bar] < low[bar + 2]) { double new_top = low[bar + 2]; double new_bot = high[bar]; int new_start = bar + 2; datetime new_time = time[new_start]; PruneOldZones(m_sfvg_zones, m_sfvg_cnt, max_zones, buf_fvg_top, buf_fvg_bot, rates_total); UnshiftZone(m_sfvg_zones, m_sfvg_cnt, max_zones, new_top, new_bot, new_start, new_time); m_sfvg_zones[0].obj_name = CreateRect("ICTE_FVG_S_", new_time, new_top, time[bar], new_bot, m_fvg_col); UnshiftPreInv(m_sfvg_R_top, m_sfvg_R_bot, m_sfvg_R_bar, m_sfvg_R_cnt, new_top, new_bot, new_start); if(PassesEqFilter(new_top, new_bot, in_dir_big, false, eq_value)) FillZoneRange(buf_fvg_top, buf_fvg_bot, new_top, new_bot, new_start, bar, rates_total); } } //========================================================= // ШАГ 5: Продление активных зон на текущий бар + ObjectMove //========================================================= //--- FVG: продлеваем активную зону (demand приоритет) { int d_ai = FindActiveZone(m_dfvg_zones, m_dfvg_cnt); int s_ai = FindActiveZone(m_sfvg_zones, m_sfvg_cnt); if(d_ai >= 0 && bar <= m_dfvg_zones[d_ai].start_bar && PassesEqFilter(m_dfvg_zones[d_ai].top, m_dfvg_zones[d_ai].bot, in_dir_big, true, eq_value)) { buf_fvg_top[bar] = m_dfvg_zones[d_ai].top; buf_fvg_bot[bar] = m_dfvg_zones[d_ai].bot; MoveRectRight(m_dfvg_zones[d_ai].obj_name, time[bar], m_dfvg_zones[d_ai].bot); } else if(s_ai >= 0 && bar <= m_sfvg_zones[s_ai].start_bar && PassesEqFilter(m_sfvg_zones[s_ai].top, m_sfvg_zones[s_ai].bot, in_dir_big, false, eq_value)) { buf_fvg_top[bar] = m_sfvg_zones[s_ai].top; buf_fvg_bot[bar] = m_sfvg_zones[s_ai].bot; MoveRectRight(m_sfvg_zones[s_ai].obj_name, time[bar], m_sfvg_zones[s_ai].bot); } } //--- iFVG: продлеваем активную зону { int d_ai = FindActiveZone(m_ifvg_d_zones, m_ifvg_d_cnt); int s_ai = FindActiveZone(m_ifvg_s_zones, m_ifvg_s_cnt); if(d_ai >= 0 && bar <= m_ifvg_d_zones[d_ai].start_bar && PassesEqFilter(m_ifvg_d_zones[d_ai].top, m_ifvg_d_zones[d_ai].bot, in_dir_big, true, eq_value)) { buf_ifvg_top[bar] = m_ifvg_d_zones[d_ai].top; buf_ifvg_bot[bar] = m_ifvg_d_zones[d_ai].bot; MoveRectRight(m_ifvg_d_zones[d_ai].obj_name, time[bar], m_ifvg_d_zones[d_ai].bot); } else if(s_ai >= 0 && bar <= m_ifvg_s_zones[s_ai].start_bar && PassesEqFilter(m_ifvg_s_zones[s_ai].top, m_ifvg_s_zones[s_ai].bot, in_dir_big, false, eq_value)) { buf_ifvg_top[bar] = m_ifvg_s_zones[s_ai].top; buf_ifvg_bot[bar] = m_ifvg_s_zones[s_ai].bot; MoveRectRight(m_ifvg_s_zones[s_ai].obj_name, time[bar], m_ifvg_s_zones[s_ai].bot); } } //========================================================= // ШАГ 6: Расширить OBJ_RECTANGLE ВСЕХ активных зон //========================================================= for(int i = 0; i < m_dfvg_cnt; i++) if(m_dfvg_zones[i].end_bar == -1 && m_dfvg_zones[i].obj_name != "" && bar <= m_dfvg_zones[i].start_bar) MoveRectRight(m_dfvg_zones[i].obj_name, time[bar], m_dfvg_zones[i].bot); for(int i = 0; i < m_sfvg_cnt; i++) if(m_sfvg_zones[i].end_bar == -1 && m_sfvg_zones[i].obj_name != "" && bar <= m_sfvg_zones[i].start_bar) MoveRectRight(m_sfvg_zones[i].obj_name, time[bar], m_sfvg_zones[i].bot); for(int i = 0; i < m_ifvg_d_cnt; i++) if(m_ifvg_d_zones[i].end_bar == -1 && m_ifvg_d_zones[i].obj_name != "" && bar <= m_ifvg_d_zones[i].start_bar) MoveRectRight(m_ifvg_d_zones[i].obj_name, time[bar], m_ifvg_d_zones[i].bot); for(int i = 0; i < m_ifvg_s_cnt; i++) if(m_ifvg_s_zones[i].end_bar == -1 && m_ifvg_s_zones[i].obj_name != "" && bar <= m_ifvg_s_zones[i].start_bar) MoveRectRight(m_ifvg_s_zones[i].obj_name, time[bar], m_ifvg_s_zones[i].bot); } //+------------------------------------------------------------------+ //| Обновление bar=0: продление активных зон на live-бар | //+------------------------------------------------------------------+ void CFVGModule::UpdateBar0(int in_dir_big, double &buf_fvg_top[], double &buf_fvg_bot[], double &buf_ifvg_top[], double &buf_ifvg_bot[], double eq_bar1, double premium2_bar1, double discount2_bar1) { //--- Сбросить буферы bar 0 buf_fvg_top[0] = 0.0; buf_fvg_bot[0] = 0.0; buf_ifvg_top[0] = 0.0; buf_ifvg_bot[0] = 0.0; double eq_value = ResolveEqLevel(in_dir_big, eq_bar1, premium2_bar1, discount2_bar1); datetime now = TimeCurrent(); if(in_dir_big == 1) { //--- Бычий тренд: расширяем Demand FVG + Demand iFVG int ai = FindActiveZone(m_dfvg_zones, m_dfvg_cnt); if(ai >= 0 && PassesEqFilter(m_dfvg_zones[ai].top, m_dfvg_zones[ai].bot, in_dir_big, true, eq_value)) { buf_fvg_top[0] = m_dfvg_zones[ai].top; buf_fvg_bot[0] = m_dfvg_zones[ai].bot; MoveRectRight(m_dfvg_zones[ai].obj_name, now, m_dfvg_zones[ai].bot); } ai = FindActiveZone(m_ifvg_d_zones, m_ifvg_d_cnt); if(ai >= 0 && PassesEqFilter(m_ifvg_d_zones[ai].top, m_ifvg_d_zones[ai].bot, in_dir_big, true, eq_value)) { buf_ifvg_top[0] = m_ifvg_d_zones[ai].top; buf_ifvg_bot[0] = m_ifvg_d_zones[ai].bot; MoveRectRight(m_ifvg_d_zones[ai].obj_name, now, m_ifvg_d_zones[ai].bot); } } else if(in_dir_big == -1) { //--- Медвежий тренд: расширяем Supply FVG + Supply iFVG int ai = FindActiveZone(m_sfvg_zones, m_sfvg_cnt); if(ai >= 0 && PassesEqFilter(m_sfvg_zones[ai].top, m_sfvg_zones[ai].bot, in_dir_big, false, eq_value)) { buf_fvg_top[0] = m_sfvg_zones[ai].top; buf_fvg_bot[0] = m_sfvg_zones[ai].bot; MoveRectRight(m_sfvg_zones[ai].obj_name, now, m_sfvg_zones[ai].bot); } ai = FindActiveZone(m_ifvg_s_zones, m_ifvg_s_cnt); if(ai >= 0 && PassesEqFilter(m_ifvg_s_zones[ai].top, m_ifvg_s_zones[ai].bot, in_dir_big, false, eq_value)) { buf_ifvg_top[0] = m_ifvg_s_zones[ai].top; buf_ifvg_bot[0] = m_ifvg_s_zones[ai].bot; MoveRectRight(m_ifvg_s_zones[ai].obj_name, now, m_ifvg_s_zones[ai].bot); } } //--- Расширить OBJ_RECTANGLE ВСЕХ активных зон до now (независимо от тренда) for(int i = 0; i < m_dfvg_cnt; i++) if(m_dfvg_zones[i].end_bar == -1 && m_dfvg_zones[i].obj_name != "") MoveRectRight(m_dfvg_zones[i].obj_name, now, m_dfvg_zones[i].bot); for(int i = 0; i < m_sfvg_cnt; i++) if(m_sfvg_zones[i].end_bar == -1 && m_sfvg_zones[i].obj_name != "") MoveRectRight(m_sfvg_zones[i].obj_name, now, m_sfvg_zones[i].bot); for(int i = 0; i < m_ifvg_d_cnt; i++) if(m_ifvg_d_zones[i].end_bar == -1 && m_ifvg_d_zones[i].obj_name != "") MoveRectRight(m_ifvg_d_zones[i].obj_name, now, m_ifvg_d_zones[i].bot); for(int i = 0; i < m_ifvg_s_cnt; i++) if(m_ifvg_s_zones[i].end_bar == -1 && m_ifvg_s_zones[i].obj_name != "") MoveRectRight(m_ifvg_s_zones[i].obj_name, now, m_ifvg_s_zones[i].bot); } //+------------------------------------------------------------------+ //| Обновить видимость объектов согласно фильтрам | //| in_dir_big: 1=бычий, -1=медвежий | //| current_price: close[1] для фильтрации зон выше/ниже цены | //+------------------------------------------------------------------+ void CFVGModule::ApplyVisibility(int in_dir_big, double current_price, double eq_value, double premium2_value, double discount2_value) { double eq_lvl = ResolveEqLevel(in_dir_big, eq_value, premium2_value, discount2_value); //--- Demand FVG: видим при бычьем тренде, зона ниже цены for(int i = 0; i < m_dfvg_cnt; i++) { if(m_dfvg_zones[i].obj_name == "") continue; bool show = m_show_fvg; if(show) { int right_edge = (m_dfvg_zones[i].end_bar == -1) ? 0 : m_dfvg_zones[i].end_bar; show = (m_hist_bars == 0) ? (m_dfvg_zones[i].end_bar == -1) : (right_edge <= m_hist_bars); } if(show && m_trend_only) show = (in_dir_big == 1) && (m_dfvg_zones[i].end_bar == -1) && (m_dfvg_zones[i].top < current_price); if(show && m_eq_filter) show = PassesEqFilter(m_dfvg_zones[i].top, m_dfvg_zones[i].bot, in_dir_big, true, eq_lvl); ObjectSetInteger(0, m_dfvg_zones[i].obj_name, OBJPROP_TIMEFRAMES, show ? OBJ_ALL_PERIODS : OBJ_NO_PERIODS); } //--- Supply FVG: видим при медвежьем тренде, зона выше цены for(int i = 0; i < m_sfvg_cnt; i++) { if(m_sfvg_zones[i].obj_name == "") continue; bool show = m_show_fvg; if(show) { int right_edge = (m_sfvg_zones[i].end_bar == -1) ? 0 : m_sfvg_zones[i].end_bar; show = (m_hist_bars == 0) ? (m_sfvg_zones[i].end_bar == -1) : (right_edge <= m_hist_bars); } if(show && m_trend_only) show = (in_dir_big == -1) && (m_sfvg_zones[i].end_bar == -1) && (m_sfvg_zones[i].bot > current_price); if(show && m_eq_filter) show = PassesEqFilter(m_sfvg_zones[i].top, m_sfvg_zones[i].bot, in_dir_big, false, eq_lvl); ObjectSetInteger(0, m_sfvg_zones[i].obj_name, OBJPROP_TIMEFRAMES, show ? OBJ_ALL_PERIODS : OBJ_NO_PERIODS); } //--- Demand iFVG: видим при бычьем тренде, зона ниже цены for(int i = 0; i < m_ifvg_d_cnt; i++) { if(m_ifvg_d_zones[i].obj_name == "") continue; bool show = m_show_ifvg; if(show) { int right_edge = (m_ifvg_d_zones[i].end_bar == -1) ? 0 : m_ifvg_d_zones[i].end_bar; show = (m_hist_bars == 0) ? (m_ifvg_d_zones[i].end_bar == -1) : (right_edge <= m_hist_bars); } if(show && m_trend_only) show = (in_dir_big == 1) && (m_ifvg_d_zones[i].end_bar == -1) && (m_ifvg_d_zones[i].top < current_price); if(show && m_eq_filter) show = PassesEqFilter(m_ifvg_d_zones[i].top, m_ifvg_d_zones[i].bot, in_dir_big, true, eq_lvl); ObjectSetInteger(0, m_ifvg_d_zones[i].obj_name, OBJPROP_TIMEFRAMES, show ? OBJ_ALL_PERIODS : OBJ_NO_PERIODS); } //--- Supply iFVG: видим при медвежьем тренде, зона выше цены for(int i = 0; i < m_ifvg_s_cnt; i++) { if(m_ifvg_s_zones[i].obj_name == "") continue; bool show = m_show_ifvg; if(show) { int right_edge = (m_ifvg_s_zones[i].end_bar == -1) ? 0 : m_ifvg_s_zones[i].end_bar; show = (m_hist_bars == 0) ? (m_ifvg_s_zones[i].end_bar == -1) : (right_edge <= m_hist_bars); } if(show && m_trend_only) show = (in_dir_big == -1) && (m_ifvg_s_zones[i].end_bar == -1) && (m_ifvg_s_zones[i].bot > current_price); if(show && m_eq_filter) show = PassesEqFilter(m_ifvg_s_zones[i].top, m_ifvg_s_zones[i].bot, in_dir_big, false, eq_lvl); ObjectSetInteger(0, m_ifvg_s_zones[i].obj_name, OBJPROP_TIMEFRAMES, show ? OBJ_ALL_PERIODS : OBJ_NO_PERIODS); } } //+------------------------------------------------------------------+ //| Определить значение Equilibrium для фильтрации | //+------------------------------------------------------------------+ double CFVGModule::ResolveEqLevel(int in_dir_big, double eq_val, double premium2_val, double discount2_val) const { if(!m_eq_filter) return EMPTY_VALUE; if(m_eq_use_lvl2) return (in_dir_big == 1) ? discount2_val : (in_dir_big == -1) ? premium2_val : EMPTY_VALUE; return eq_val; } //+------------------------------------------------------------------+ //| Проверка: зона проходит Equilibrium-фильтр? | //| zone_is_demand: true=Demand, false=Supply | //| Возвращает true = показывать зону | //+------------------------------------------------------------------+ bool CFVGModule::PassesEqFilter(double zone_top, double zone_bot, int in_dir_big, bool zone_is_demand, double eq_value) const { if(!m_eq_filter) return true; if(eq_value == EMPTY_VALUE) return true; if(in_dir_big == 0) return true; //--- Фильтруем только если зона соответствует тренду if(in_dir_big == 1 && !zone_is_demand) return true; // Supply в бычьем — не фильтруем if(in_dir_big == -1 && zone_is_demand) return true; // Demand в медвежьем — не фильтруем if(zone_is_demand) { //--- Demand: зона должна быть ниже eq_value (Discount) return m_eq_strict ? (zone_top <= eq_value) : (zone_bot <= eq_value); } else { //--- Supply: зона должна быть выше eq_value (Premium) return m_eq_strict ? (zone_bot >= eq_value) : (zone_top >= eq_value); } } #endif // CFVG_MODULE_MQH