Создано: CLiquidityLevels.mqh — новый модуль с кольцевым буферным хранилищем для уровней спроса/предложения, обнаружение развертки (каскадный время-loop), условия con_buy/con_sell с фильтрацией трендов Изменено: CSwingDetector.mqh — форвардное объявление, расширенный Calculate() с 8 новыми параметрами (время[], 6 буферов ликвидности, указатель CLiquidityLevels), обратный вызов в основном цикле после равновесия Ind_Aleks_ICT_Entry_V2_TS_Indie.mq5 — 24 буфера / 16 участков, 4 новых буфера DRAW_LINE (Вверх2/Вверх3/Dn2/Dn3), INDICATOR_CALCULATIONS смещены на индексы 16-23, g_liquidity init с параметром inp_show_lid Сборник: 0 ошибок, 0 предупреждений. Индикатор готов к визуальному тестированию на графике — вы должны увидеть до 6 зеленых горизонтальных линий (3 ближайших уровня предложения выше цены, 3 ближайших уровня спроса ниже). Когда уровень подметается, линии перескакивают на следующие доступные уровни.
413 lines
18 KiB
MQL5
413 lines
18 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| CLiquidityLevels.mqh |
|
|
//| Модуль уровней ликвидности (Liquidity Levels) |
|
|
//| Sweep detection + con_buy/con_sell для ICT Entry |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef CLIQUIDITY_LEVELS_MQH
|
|
#define CLIQUIDITY_LEVELS_MQH
|
|
|
|
//--- Требует SLiquiditySwingState и SStructureSwingState из CSwingDetector.mqh
|
|
//--- Подключать ПОСЛЕ CSwingDetector.mqh
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Структура состояния уровней ликвидности |
|
|
//+------------------------------------------------------------------+
|
|
struct SLiquidityLevelsState
|
|
{
|
|
bool break_dem; // Sweep Demand на этом баре
|
|
bool break_sup; // Sweep Supply на этом баре
|
|
bool con_buy; // Условие Buy активно (persistent)
|
|
bool con_sell; // Условие Sell активно (persistent)
|
|
double value_dem_lid; // Значение последнего swept Demand уровня
|
|
double value_sup_lid; // Значение последнего swept Supply уровня
|
|
datetime pre_bar_Dlid_left; // Время создания swept Demand
|
|
datetime pre_bar_Dlid_right; // Время sweep Demand
|
|
datetime pre_bar_Slid_left; // Время создания swept Supply
|
|
datetime pre_bar_Slid_right; // Время sweep Supply
|
|
//--- Копии для Entry Signal (сохраняются при активации con_buy/con_sell)
|
|
double pre_dem_lid;
|
|
double pre_sup_lid;
|
|
datetime pre2_bar_Dlid_left;
|
|
datetime pre2_bar_Dlid_right;
|
|
datetime pre2_bar_Slid_left;
|
|
datetime pre2_bar_Slid_right;
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Класс CLiquidityLevels — уровни ликвидности и sweep |
|
|
//+------------------------------------------------------------------+
|
|
class CLiquidityLevels
|
|
{
|
|
public:
|
|
CLiquidityLevels();
|
|
~CLiquidityLevels();
|
|
|
|
//--- Инициализация — вызывать из OnInit()
|
|
bool Init(int len, int max_levels, bool show_lid);
|
|
|
|
//--- Per-bar расчёт — вызывается из цикла CSwingDetector.Calculate
|
|
void Calculate(int bar,
|
|
const SLiquiditySwingState &liq,
|
|
const SStructureSwingState &str,
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &open[],
|
|
const double &close[],
|
|
const datetime &time[],
|
|
double &buf_liq_up1[],
|
|
double &buf_liq_up2[],
|
|
double &buf_liq_up3[],
|
|
double &buf_liq_dn1[],
|
|
double &buf_liq_dn2[],
|
|
double &buf_liq_dn3[],
|
|
int rates_total);
|
|
|
|
//--- Полный сброс — вызывать при prev_calculated == 0
|
|
void FullReset();
|
|
|
|
//--- Геттеры
|
|
SLiquidityLevelsState GetState() const { return m_state; }
|
|
bool GetConBuy() const { return m_state.con_buy; }
|
|
bool GetConSell() const { return m_state.con_sell; }
|
|
bool GetBreakDem() const { return m_state.break_dem; }
|
|
bool GetBreakSup() const { return m_state.break_sup; }
|
|
|
|
private:
|
|
//--- Параметры
|
|
int m_len; // Сила пивота Liquidity Swings (Len=5)
|
|
int m_max_levels; // Максимум уровней в массиве
|
|
bool m_show_lid; // Заполнять буферы отрисовки
|
|
|
|
//--- Массивы уровней (ring buffer: [0] = самый новый)
|
|
double m_lid_sup[]; // Supply уровни (Swing Highs)
|
|
double m_lid_dem[]; // Demand уровни (Swing Lows)
|
|
datetime m_lid_bar_sup[]; // Время создания Supply уровней
|
|
datetime m_lid_bar_dem[]; // Время создания Demand уровней
|
|
int m_count_sup; // Количество активных Supply
|
|
int m_count_dem; // Количество активных Demand
|
|
|
|
//--- Состояние
|
|
SLiquidityLevelsState m_state;
|
|
|
|
//--- Внутренние методы
|
|
void AddSupplyLevel(double value, datetime time_created);
|
|
void AddDemandLevel(double value, datetime time_created);
|
|
void RemoveSupplyLevel(int index);
|
|
void RemoveDemandLevel(int index);
|
|
void CheckSweepDemand(double bar_low, datetime bar_time);
|
|
void CheckSweepSupply(double bar_high, datetime bar_time);
|
|
void UpdateConditions(double bar_close, double bar_open,
|
|
bool up_trend, bool down_trend);
|
|
void FillBuffers(int bar,
|
|
double &buf_liq_up1[],
|
|
double &buf_liq_up2[],
|
|
double &buf_liq_up3[],
|
|
double &buf_liq_dn1[],
|
|
double &buf_liq_dn2[],
|
|
double &buf_liq_dn3[]);
|
|
void ResetState();
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Конструктор |
|
|
//+------------------------------------------------------------------+
|
|
CLiquidityLevels::CLiquidityLevels()
|
|
{
|
|
m_len = 5;
|
|
m_max_levels = 200;
|
|
m_show_lid = true;
|
|
m_count_sup = 0;
|
|
m_count_dem = 0;
|
|
ZeroMemory(m_state);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Деструктор |
|
|
//+------------------------------------------------------------------+
|
|
CLiquidityLevels::~CLiquidityLevels()
|
|
{
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Инициализация параметров |
|
|
//+------------------------------------------------------------------+
|
|
bool CLiquidityLevels::Init(int len, int max_levels, bool show_lid)
|
|
{
|
|
m_len = len;
|
|
m_max_levels = max_levels;
|
|
m_show_lid = show_lid;
|
|
|
|
//--- Выделяем массивы фиксированного размера
|
|
ArrayResize(m_lid_sup, m_max_levels);
|
|
ArrayResize(m_lid_dem, m_max_levels);
|
|
ArrayResize(m_lid_bar_sup, m_max_levels);
|
|
ArrayResize(m_lid_bar_dem, m_max_levels);
|
|
|
|
ResetState();
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Полный сброс — при prev_calculated == 0 |
|
|
//+------------------------------------------------------------------+
|
|
void CLiquidityLevels::FullReset()
|
|
{
|
|
ResetState();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Сброс всего состояния |
|
|
//+------------------------------------------------------------------+
|
|
void CLiquidityLevels::ResetState()
|
|
{
|
|
m_count_sup = 0;
|
|
m_count_dem = 0;
|
|
ArrayInitialize(m_lid_sup, 0);
|
|
ArrayInitialize(m_lid_dem, 0);
|
|
ArrayInitialize(m_lid_bar_sup, 0);
|
|
ArrayInitialize(m_lid_bar_dem, 0);
|
|
ZeroMemory(m_state);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Добавить Supply уровень (Swing High → ликвидность сверху) |
|
|
//| Ring buffer: вставка в начало, последний элемент теряется |
|
|
//+------------------------------------------------------------------+
|
|
void CLiquidityLevels::AddSupplyLevel(double value, datetime time_created)
|
|
{
|
|
//--- Сдвиг вправо на 1 позицию (от конца к началу)
|
|
int last = MathMin(m_count_sup, m_max_levels - 1);
|
|
for(int i = last; i > 0; i--)
|
|
{
|
|
m_lid_sup[i] = m_lid_sup[i - 1];
|
|
m_lid_bar_sup[i] = m_lid_bar_sup[i - 1];
|
|
}
|
|
//--- Новый уровень в [0]
|
|
m_lid_sup[0] = value;
|
|
m_lid_bar_sup[0] = time_created;
|
|
m_count_sup = MathMin(m_count_sup + 1, m_max_levels);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Добавить Demand уровень (Swing Low → ликвидность снизу) |
|
|
//+------------------------------------------------------------------+
|
|
void CLiquidityLevels::AddDemandLevel(double value, datetime time_created)
|
|
{
|
|
int last = MathMin(m_count_dem, m_max_levels - 1);
|
|
for(int i = last; i > 0; i--)
|
|
{
|
|
m_lid_dem[i] = m_lid_dem[i - 1];
|
|
m_lid_bar_dem[i] = m_lid_bar_dem[i - 1];
|
|
}
|
|
m_lid_dem[0] = value;
|
|
m_lid_bar_dem[0] = time_created;
|
|
m_count_dem = MathMin(m_count_dem + 1, m_max_levels);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Удалить Supply уровень по индексу (сдвиг влево) |
|
|
//+------------------------------------------------------------------+
|
|
void CLiquidityLevels::RemoveSupplyLevel(int index)
|
|
{
|
|
for(int j = index; j < m_count_sup - 1; j++)
|
|
{
|
|
m_lid_sup[j] = m_lid_sup[j + 1];
|
|
m_lid_bar_sup[j] = m_lid_bar_sup[j + 1];
|
|
}
|
|
m_count_sup--;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Удалить Demand уровень по индексу (сдвиг влево) |
|
|
//+------------------------------------------------------------------+
|
|
void CLiquidityLevels::RemoveDemandLevel(int index)
|
|
{
|
|
for(int j = index; j < m_count_dem - 1; j++)
|
|
{
|
|
m_lid_dem[j] = m_lid_dem[j + 1];
|
|
m_lid_bar_dem[j] = m_lid_bar_dem[j + 1];
|
|
}
|
|
m_count_dem--;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Проверка sweep Demand уровней (tz.md строки 608-620) |
|
|
//| Sweep: цена Low пробивает уровень поддержки |
|
|
//| Множественный sweep: while-loop пока low <= lid_dem[0] |
|
|
//+------------------------------------------------------------------+
|
|
void CLiquidityLevels::CheckSweepDemand(double bar_low, datetime bar_time)
|
|
{
|
|
if(m_count_dem == 0)
|
|
return;
|
|
if(bar_low > m_lid_dem[0])
|
|
return;
|
|
|
|
//--- Минимум один уровень пробит
|
|
while(m_count_dem > 0 && bar_low <= m_lid_dem[0])
|
|
{
|
|
//--- Сохраняем данные swept уровня
|
|
m_state.value_dem_lid = m_lid_dem[0];
|
|
m_state.pre_bar_Dlid_left = m_lid_bar_dem[0];
|
|
m_state.pre_bar_Dlid_right = bar_time;
|
|
|
|
//--- Удаляем уровень из массива
|
|
RemoveDemandLevel(0);
|
|
}
|
|
|
|
m_state.break_dem = true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Проверка sweep Supply уровней (tz.md строки 622-634) |
|
|
//| Sweep: цена High пробивает уровень сопротивления |
|
|
//+------------------------------------------------------------------+
|
|
void CLiquidityLevels::CheckSweepSupply(double bar_high, datetime bar_time)
|
|
{
|
|
if(m_count_sup == 0)
|
|
return;
|
|
if(bar_high < m_lid_sup[0])
|
|
return;
|
|
|
|
//--- Минимум один уровень пробит
|
|
while(m_count_sup > 0 && bar_high >= m_lid_sup[0])
|
|
{
|
|
//--- Сохраняем данные swept уровня
|
|
m_state.value_sup_lid = m_lid_sup[0];
|
|
m_state.pre_bar_Slid_left = m_lid_bar_sup[0];
|
|
m_state.pre_bar_Slid_right = bar_time;
|
|
|
|
//--- Удаляем уровень из массива
|
|
RemoveSupplyLevel(0);
|
|
}
|
|
|
|
m_state.break_sup = true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Обновление торговых условий con_buy/con_sell |
|
|
//| (tz.md строки 921-933, 965-977) |
|
|
//+------------------------------------------------------------------+
|
|
void CLiquidityLevels::UpdateConditions(double bar_close, double bar_open,
|
|
bool up_trend, bool down_trend)
|
|
{
|
|
//=== Активация con_buy: Demand swept + цена выше swept уровня ===
|
|
if(m_state.break_dem
|
|
&& bar_close > m_state.value_dem_lid
|
|
&& bar_open > m_state.value_dem_lid
|
|
&& up_trend)
|
|
{
|
|
m_state.con_buy = true;
|
|
m_state.pre_dem_lid = m_state.value_dem_lid;
|
|
m_state.pre2_bar_Dlid_left = m_state.pre_bar_Dlid_left;
|
|
m_state.pre2_bar_Dlid_right = m_state.pre_bar_Dlid_right;
|
|
m_state.con_sell = false; // Деактивация противоположного
|
|
}
|
|
|
|
//=== Активация con_sell: Supply swept + цена ниже swept уровня ===
|
|
if(m_state.break_sup
|
|
&& bar_close < m_state.value_sup_lid
|
|
&& bar_open < m_state.value_sup_lid
|
|
&& down_trend)
|
|
{
|
|
m_state.con_sell = true;
|
|
m_state.pre_sup_lid = m_state.value_sup_lid;
|
|
m_state.pre2_bar_Slid_left = m_state.pre_bar_Slid_left;
|
|
m_state.pre2_bar_Slid_right = m_state.pre_bar_Slid_right;
|
|
m_state.con_buy = false; // Деактивация противоположного
|
|
}
|
|
|
|
//=== Деактивация con_buy: цена вернулась ниже swept уровня ===
|
|
if(m_state.con_buy && bar_close < m_state.value_dem_lid)
|
|
m_state.con_buy = false;
|
|
|
|
//=== Деактивация con_sell: цена вернулась выше swept уровня ===
|
|
if(m_state.con_sell && bar_close > m_state.value_sup_lid)
|
|
m_state.con_sell = false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Заполнение буферов: 3 ближайших уровня на сторону |
|
|
//+------------------------------------------------------------------+
|
|
void CLiquidityLevels::FillBuffers(int bar,
|
|
double &buf_liq_up1[],
|
|
double &buf_liq_up2[],
|
|
double &buf_liq_up3[],
|
|
double &buf_liq_dn1[],
|
|
double &buf_liq_dn2[],
|
|
double &buf_liq_dn3[])
|
|
{
|
|
if(m_show_lid)
|
|
{
|
|
buf_liq_up1[bar] = (m_count_sup > 0) ? m_lid_sup[0] : EMPTY_VALUE;
|
|
buf_liq_up2[bar] = (m_count_sup > 1) ? m_lid_sup[1] : EMPTY_VALUE;
|
|
buf_liq_up3[bar] = (m_count_sup > 2) ? m_lid_sup[2] : EMPTY_VALUE;
|
|
buf_liq_dn1[bar] = (m_count_dem > 0) ? m_lid_dem[0] : EMPTY_VALUE;
|
|
buf_liq_dn2[bar] = (m_count_dem > 1) ? m_lid_dem[1] : EMPTY_VALUE;
|
|
buf_liq_dn3[bar] = (m_count_dem > 2) ? m_lid_dem[2] : EMPTY_VALUE;
|
|
}
|
|
else
|
|
{
|
|
buf_liq_up1[bar] = EMPTY_VALUE;
|
|
buf_liq_up2[bar] = EMPTY_VALUE;
|
|
buf_liq_up3[bar] = EMPTY_VALUE;
|
|
buf_liq_dn1[bar] = EMPTY_VALUE;
|
|
buf_liq_dn2[bar] = EMPTY_VALUE;
|
|
buf_liq_dn3[bar] = EMPTY_VALUE;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Главный per-bar расчёт |
|
|
//| Порядок: добавление → sweep → conditions → буферы |
|
|
//+------------------------------------------------------------------+
|
|
void CLiquidityLevels::Calculate(int bar,
|
|
const SLiquiditySwingState &liq,
|
|
const SStructureSwingState &str,
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &open[],
|
|
const double &close[],
|
|
const datetime &time[],
|
|
double &buf_liq_up1[],
|
|
double &buf_liq_up2[],
|
|
double &buf_liq_up3[],
|
|
double &buf_liq_dn1[],
|
|
double &buf_liq_dn2[],
|
|
double &buf_liq_dn3[],
|
|
int rates_total)
|
|
{
|
|
//--- 1. Сброс per-bar флагов
|
|
m_state.break_dem = false;
|
|
m_state.break_sup = false;
|
|
|
|
//--- 2. Добавление уровней при подтверждении свинга
|
|
//--- Supply: Ch_swh AND sw_high >= ph (ss_high = ph для текущего бара)
|
|
if(liq.ch_swh && liq.sw_high >= liq.ss_high)
|
|
{
|
|
//--- Время создания уровня = время бара свинга
|
|
int bar_idx = liq.bar_sw_high;
|
|
if(bar_idx >= 0 && bar_idx < rates_total)
|
|
AddSupplyLevel(liq.sw_high, time[bar_idx]);
|
|
}
|
|
|
|
//--- Demand: Ch_swl AND sw_low <= pl (ss_low = pl для текущего бара)
|
|
if(liq.ch_swl && liq.sw_low <= liq.ss_low)
|
|
{
|
|
int bar_idx = liq.bar_sw_low;
|
|
if(bar_idx >= 0 && bar_idx < rates_total)
|
|
AddDemandLevel(liq.sw_low, time[bar_idx]);
|
|
}
|
|
|
|
//--- 3. Sweep detection
|
|
CheckSweepDemand(low[bar], time[bar]);
|
|
CheckSweepSupply(high[bar], time[bar]);
|
|
|
|
//--- 4. Обновление торговых условий
|
|
UpdateConditions(close[bar], open[bar], str.up_trend, str.down_trend);
|
|
|
|
//--- 5. Заполнение буферов
|
|
FillBuffers(bar, buf_liq_up1, buf_liq_up2, buf_liq_up3,
|
|
buf_liq_dn1, buf_liq_dn2, buf_liq_dn3);
|
|
}
|
|
|
|
#endif // CLIQUIDITY_LEVELS_MQH
|