| //+------------------------------------------------------------------+
|
| //| FVG Grid.mq5 |
|
| //| Copyright 2026, Shahrukh & AI |
|
| //| https://mql5.io |
|
| //+------------------------------------------------------------------+
|
| #property copyright "Copyright 2026, Shahrukh & AI"
|
| #property link "https://mql5.io"
|
| #property version "5.20"
|
| #property description "FVG Grid: Уникальные короткие комментарии + Изолированный SL и Трейлинг"
|
|
|
| #include <Trade\Trade.mqh>
|
| CTrade trade;
|
| input group "--- Риск и ММ ---"
|
| input double InpLotSize = 0.01; // Размер лота для каждого ордера
|
| input int InpStopLoss = 150; // Стоп-Лосс в пунктах от границы гэпа (0 - отключен)
|
| input int InpTakeProfit = 300; // Тейк-Профит (в пунктах, 0 - отключен)
|
| input ulong InpMagicNumber = 123456; // Мэджик номер советника
|
| input int InpMaxSpreadPoints = 35; // Максимальный спред в пунктах для входа (0 - отключен)
|
|
|
| input group "--- Детектор FVG ---"
|
| input int InpMaximumBars = 400; // Сколько бар сканировать для отрисовки
|
| input double InpMinimumGapPoints = 20.0; // Минимальный размер гэпа в пунктах
|
| input bool InpHideMitigated = true; // Скрывать протестированные гэпы
|
| input group "--- Настройки Сетки Ордеров ---"
|
| input double InpOrderOffsetPoints = 10.0; // Смещение уровней ордеров в пунктах
|
| input group "--- Визуализация (Пунктир) ---"
|
| input int InpExtensionBars = 80; // Длина прямоугольника в барах
|
| input color InpBullishGapColor = clrGreen; // Яркий цвет контура для Buy
|
| input color InpBearishGapColor = clrRed; // Яркий цвет контура для Sell
|
| input color InpMitigatedColor = clrSilver; // Цвет для смягченных зон
|
| input bool InpShowLabels = true; // Показывать текст
|
| input group "--- Индивидуальный Трейлинг-Стоп ---"
|
| input bool InpUseTrailing = true; // Включить трейлинг?
|
| input int InpTrailingStart = 150; // Активация трейлинга (в пунктах от безубытка гэпа)
|
| input int InpTrailingDist = 50; // Расстояние от текущей цены (в пунктах)
|
| input int InpTrailingStep = 10; // Шаг изменения стоп-лосса (в пунктах)
|
| // Глобальные переменные
|
| string g_prefix = "FVGSCAN_";
|
| datetime g_last_bar = 0;
|
| bool g_first_run = true;
|
| string g_file_name;
|
|
|
| // База данных отторгованных FVG в ОЗУ
|
| string g_traded_gaps[];
|
|
|
| // Предварительное объявление функций
|
| void BuildGapsAndTrade(const int rates_copied, const datetime &time[], const double &high[], const double &low[]);
|
| void CheckAndPlaceFvgGrid(string gap_id, double top, double bottom, bool is_bullish);
|
| bool IsGridAlreadyPlaced(string gap_id);
|
| bool WasGapTraded(string gap_id);
|
| void MarkGapAsTraded(string gap_id);
|
| void ApplyIsolatedTrailing();
|
| void ModifyIsolatedGridSL(string target_comment, long position_type, double new_sl);
|
| void CancelLimitOrdersOnPositionClose();
|
| bool IsBullishMitigated(const int formation_index, const double gap_bottom, const double gap_top, const double &low[]);
|
| bool IsBearishMitigated(const int formation_index, const double gap_bottom, const double gap_top, const double &high[]);
|
| void DrawGap(const string id, const datetime start_time, const datetime end_time, const double top, const double bottom, const bool bullish, const bool mitigated);
|
| void LoadGapsFromFile();
|
| void SaveGapToFile(string gap_id);
|
| string GetShortUniqueId(datetime bar_time);
|
| //+------------------------------------------------------------------+
|
| //| Expert initialization function |
|
| //+------------------------------------------------------------------+
|
| int OnInit()
|
| {
|
| if(InpMaximumBars < 20 || InpMinimumGapPoints < 0.0 || InpExtensionBars < 1)
|
| {
|
| Print("Invalid scanner settings.");
|
| return(INIT_PARAMETERS_INCORRECT);
|
| }
|
|
|
| trade.SetExpertMagicNumber(InpMagicNumber);
|
| trade.SetMarginMode();
|
| g_last_bar = 0;
|
| g_first_run = true;
|
| ArrayResize(g_traded_gaps, 0);
|
|
|
| g_file_name = "FVG_Grid_Storage_" + _Symbol + "_" + EnumToString(_Period) + ".txt";
|
| LoadGapsFromFile();
|
|
|
| return(INIT_SUCCEEDED);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Expert deinitialization function |
|
|
|
|
|
| //+------------------------------------------------------------------+
|
| void OnDeinit(const int reason)
|
| {
|
| ObjectsDeleteAll(0, g_prefix);
|
| ChartRedraw(0);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Expert tick function |
|
| //+------------------------------------------------------------------+
|
| void OnTick()
|
| {
|
| datetime current_bar_time = iTime(_Symbol, _Period, 0);
|
| if(current_bar_time == 0) return;
|
|
|
| double high[], low[];
|
| datetime time[];
|
| ArraySetAsSeries(high, true);
|
| ArraySetAsSeries(low, true);
|
| ArraySetAsSeries(time, true);
|
|
|
| int copied_time = CopyTime(_Symbol, _Period, 0, InpMaximumBars, time);
|
| int copied_high = CopyHigh(_Symbol, _Period, 0, InpMaximumBars, high);
|
| int copied_low = CopyLow(_Symbol, _Period, 0, InpMaximumBars, low);
|
|
|
| if(copied_time < 10 || copied_high < 10 || copied_low < 10) return;
|
| int rates_total = MathMin(copied_time, MathMin(copied_high, copied_low));
|
|
|
| // 1. Сканирование и выставление новых сеток на открытии бара
|
| if(current_bar_time != g_last_bar || g_first_run)
|
| {
|
| g_last_bar = current_bar_time;
|
| BuildGapsAndTrade(rates_total, time, high, low);
|
|
|
|
|
| }
|
|
|
| // 2. Контроль закрытия рыночных ордеров и очистка оставшихся лимиток текущего гэпа
|
| CancelLimitOrdersOnPositionClose();
|
|
|
| // 3. Индивидуальный трейлинг-стоп отдельно для каждого гэпа
|
| if(InpUseTrailing)
|
| {
|
| ApplyIsolatedTrailing();
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| ОБРАБОТКА ИЗМЕНЕНИЙ ГРАФИКА |
|
| //+------------------------------------------------------------------+
|
| void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
|
| {
|
| if(id == CHARTEVENT_CLICK || id == CHARTEVENT_CHART_CHANGE)
|
| {
|
| double high[], low[];
|
| datetime time[];
|
| ArraySetAsSeries(high, true);
|
| ArraySetAsSeries(low, true);
|
| ArraySetAsSeries(time, true);
|
|
|
| int copied_time = CopyTime(_Symbol, _Period, 0, InpMaximumBars, time);
|
| int copied_high = CopyHigh(_Symbol, _Period, 0, InpMaximumBars, high);
|
| int copied_low = CopyLow(_Symbol, _Period, 0, InpMaximumBars, low);
|
|
|
| if(copied_time >= 10 && copied_high >= 10 && copied_low >= 10)
|
| {
|
| int rates_total = MathMin(copied_time, MathMin(copied_high, copied_low));
|
| BuildGapsAndTrade(rates_total, time, high, low);
|
|
|
|
|
| }
|
| }
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Генерация короткого ID для соблюдения лимита в 31 символ коммента|
|
| //+------------------------------------------------------------------+
|
| string GetShortUniqueId(datetime bar_time)
|
| {
|
| // Используем последние 6 цифр таймштампа (этого достаточно для уникальности в рамках файла)
|
| ulong long_id = (ulong)bar_time;
|
| return IntegerToString((int)(long_id % 1000000));
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Сканирование истории, построение объектов и выставление ордеров |
|
| //+------------------------------------------------------------------+
|
| void BuildGapsAndTrade(const int rates_copied, const datetime &time[], const double &high[], const double &low[])
|
| {
|
| ObjectsDeleteAll(0, g_prefix);
|
|
|
| const int seconds = PeriodSeconds(_Period);
|
| const int bar_seconds = (seconds > 0 ? seconds : 60);
|
|
|
| const int oldest = MathMin(InpMaximumBars, rates_copied - 3);
|
| if(oldest < 1) return;
|
|
|
| for(int index = oldest; index >= 1; index--)
|
| {
|
| const double bullish_size = low[index] - high[index + 2];
|
| const double bearish_size = low[index + 2] - high[index];
|
| const datetime end_time = time[index] + (datetime)(bar_seconds * InpExtensionBars);
|
|
|
| // Генерируем компактный ID, который гарантированно не обрежется в комментарии ордера
|
| const string short_id = GetShortUniqueId(time[index]);
|
|
|
| // 1. Проверяем Бычьи гэпы (Buy зоны)
|
| if(bullish_size >= InpMinimumGapPoints * _Point)
|
| {
|
| const double top = low[index];
|
| const double bottom = high[index + 2];
|
| const bool mitigated = IsBullishMitigated(index, bottom, top, low);
|
|
|
| if(!mitigated || !InpHideMitigated)
|
| DrawGap(short_id + "_B", time[index + 2], end_time, top, bottom, true, mitigated);
|
|
|
| if(!mitigated)
|
| CheckAndPlaceFvgGrid(short_id + "_B", top, bottom, true);
|
| }
|
|
|
| // 2. Проверяем Медвежьи гэпы (Sell зоны)
|
| if(bearish_size >= InpMinimumGapPoints * _Point)
|
| {
|
| const double top = low[index + 2];
|
| const double bottom = high[index];
|
| const bool mitigated = IsBearishMitigated(index, bottom, top, high);
|
|
|
| if(!mitigated || !InpHideMitigated)
|
| DrawGap(short_id + "_S", time[index + 2], end_time, top, bottom, false, mitigated);
|
|
|
| if(!mitigated)
|
| CheckAndPlaceFvgGrid(short_id + "_S", top, bottom, false);
|
| }
|
|
|
|
|
| }
|
|
|
| if(g_first_run) g_first_run = false;
|
| ChartRedraw(0);
|
| }
|
|
|
| //+------------------------------------------------------------------+
|
| //| Функция выставления 3-х ордеров со СВОИМ индивидуальным Стоп-Лоссом|
|
| //+------------------------------------------------------------------+
|
| void CheckAndPlaceFvgGrid(string gap_id, double top, double bottom, bool is_bullish)
|
| {
|
| if(InpMaxSpreadPoints > 0)
|
| {
|
| int current_spread = (int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
|
| if(current_spread > InpMaxSpreadPoints) return;
|
| }
|
| if(IsGridAlreadyPlaced(gap_id) || WasGapTraded(gap_id)) return;
|
| double center = NormalizeDouble((top + bottom) * 0.5, _Digits);
|
| double offset = InpOrderOffsetPoints * _Point;
|
| double grid_levels[3];
|
| if(is_bullish)
|
| {
|
| grid_levels[0] = NormalizeDouble(top + offset, _Digits);
|
| grid_levels[1] = NormalizeDouble(center + offset, _Digits);
|
| grid_levels[2] = NormalizeDouble(bottom + offset, _Digits);
|
| }
|
| else
|
| {
|
| grid_levels[0] = NormalizeDouble(top - offset, _Digits);
|
| grid_levels[1] = NormalizeDouble(center - offset, _Digits);
|
| grid_levels[2] = NormalizeDouble(bottom - offset, _Digits);
|
| }
|
| double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
| double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
| // Комментарий теперь короткий и уникальный (Пример: FVG_123456_B)
|
| string comment = StringFormat("FVG_%s", gap_id);
|
| bool grid_placed = false;
|
| // Вычисляем Стоп-Лосс для каждого ордера от дальней границы гэпа
|
| double common_sl = 0;
|
| if(InpStopLoss > 0)
|
| {
|
| if(is_bullish)
|
| common_sl = NormalizeDouble(bottom - InpStopLoss * _Point, _Digits);
|
| else
|
| common_sl = NormalizeDouble(top + InpStopLoss * _Point, _Digits);
|
| }
|
| for(int i = 0; i < 3; i++)
|
| {
|
| if(is_bullish)
|
| {
|
| double tp = (InpTakeProfit > 0) ? NormalizeDouble(grid_levels[i] + InpTakeProfit * _Point, _Digits) : 0;
|
| if(ask <= grid_levels[i])
|
| {
|
| if(trade.Buy(InpLotSize, _Symbol, ask, common_sl, tp, comment)) grid_placed = true;
|
| }
|
| else
|
| {
|
| if(trade.BuyLimit(InpLotSize, grid_levels[i], _Symbol, common_sl, tp, ORDER_TIME_GTC, 0, comment)) grid_placed = true;
|
| }
|
| }
|
| else // BEARISH FVG (SELL)
|
| {
|
| double tp = (InpTakeProfit > 0) ? NormalizeDouble(grid_levels[i] - InpTakeProfit * _Point, _Digits) : 0;
|
| if(bid >= grid_levels[i])
|
| {
|
| if(trade.Sell(InpLotSize, _Symbol, bid, common_sl, tp, comment)) grid_placed = true;
|
| }
|
| else
|
| {
|
| if(trade.SellLimit(InpLotSize, grid_levels[i], _Symbol, common_sl, tp, ORDER_TIME_GTC, 0, comment)) grid_placed = true;
|
| }
|
| }
|
| }
|
| if(grid_placed)
|
| {
|
| MarkGapAsTraded(gap_id);
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Функция отмены лимиток ТОЛЬКО при закрытии позиций этого гэпа |
|
| //+------------------------------------------------------------------+
|
| void CancelLimitOrdersOnPositionClose()
|
| {
|
| int total_gaps = ArraySize(g_traded_gaps);
|
| if(total_gaps == 0) return;
|
| if(!HistorySelect(0, TimeCurrent())) return;
|
| int total_deals = HistoryDealsTotal();
|
| for(int g = 0; g < total_gaps; g++)
|
| {
|
| string gap_id = g_traded_gaps[g];
|
| string target_comment = "FVG_" + gap_id;
|
| bool has_open_positions = false;
|
| bool has_limit_orders = false;
|
| for(int i = PositionsTotal() - 1; i >= 0; i--)
|
| {
|
| // Безопасное чтение свойств позиции в MQL5 по индексу
|
| if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
|
| {
|
| if(PositionGetString(POSITION_COMMENT) == target_comment)
|
| {
|
| has_open_positions = true;
|
| break;
|
| }
|
| }
|
| }
|
| for(int i = OrdersTotal() - 1; i >= 0; i--)
|
| {
|
| ulong ticket = OrderGetTicket(i);
|
| if(ticket > 0 && OrderGetInteger(ORDER_MAGIC) == InpMagicNumber)
|
| {
|
| if(OrderGetString(ORDER_COMMENT) == target_comment)
|
| {
|
| long type = OrderGetInteger(ORDER_TYPE);
|
| if(type == ORDER_TYPE_BUY_LIMIT || type == ORDER_TYPE_SELL_LIMIT)
|
| {
|
| has_limit_orders = true;
|
| break;
|
| }
|
| }
|
| }
|
| }
|
| if(has_limit_orders && !has_open_positions)
|
| {
|
| bool was_executed_and_closed = false;
|
| for(int d = total_deals - 1; d >= 0; d--)
|
| {
|
| ulong deal_ticket = HistoryDealGetTicket(d);
|
| if(deal_ticket > 0 && HistoryDealGetInteger(deal_ticket, DEAL_MAGIC) == InpMagicNumber)
|
| {
|
| if(HistoryDealGetString(deal_ticket, DEAL_COMMENT) == target_comment)
|
| {
|
| was_executed_and_closed = true;
|
| break;
|
| }
|
| }
|
| }
|
| if(was_executed_and_closed)
|
| {
|
| for(int i = OrdersTotal() - 1; i >= 0; i--)
|
| {
|
| ulong ticket = OrderGetTicket(i);
|
| if(ticket > 0 && OrderGetInteger(ORDER_MAGIC) == InpMagicNumber)
|
| {
|
| if(OrderGetString(ORDER_COMMENT) == target_comment)
|
| {
|
| long type = OrderGetInteger(ORDER_TYPE);
|
| if(type == ORDER_TYPE_BUY_LIMIT || type == ORDER_TYPE_SELL_LIMIT)
|
| {
|
| trade.OrderDelete(ticket);
|
| Print("Лимитный ордер удален. Причина: Исполненные позиции данного FVG закрылись.");
|
| }
|
| }
|
| }
|
| }
|
| }
|
| }
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Индивидуальный трейлинг-стоп отдельно для каждого гэпа |
|
| //+------------------------------------------------------------------+
|
| void ApplyIsolatedTrailing()
|
| {
|
| int total_gaps = ArraySize(g_traded_gaps);
|
| if(total_gaps == 0) return;
|
| double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
| double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
| for(int g = 0; g < total_gaps; g++)
|
| {
|
| string gap_id = g_traded_gaps[g];
|
| string target_comment = "FVG_" + gap_id;
|
| double total_vol_buy = 0, total_vol_sell = 0;
|
| double weighted_price_buy = 0, weighted_price_sell = 0;
|
| for(int i = PositionsTotal() - 1; i >= 0; i--)
|
| {
|
| if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
|
| {
|
| if(PositionGetString(POSITION_COMMENT) == target_comment)
|
| {
|
| double vol = PositionGetDouble(POSITION_VOLUME);
|
| double open = PositionGetDouble(POSITION_PRICE_OPEN);
|
| long type = PositionGetInteger(POSITION_TYPE);
|
| if(type == POSITION_TYPE_BUY)
|
| {
|
| weighted_price_buy += open * vol;
|
| total_vol_buy += vol;
|
| }
|
| if(type == POSITION_TYPE_SELL)
|
| {
|
| weighted_price_sell += open * vol;
|
| total_vol_sell += vol;
|
| }
|
| }
|
| }
|
| }
|
| if(total_vol_buy > 0)
|
| {
|
| double breakeven_buy = weighted_price_buy / total_vol_buy;
|
| if(bid - breakeven_buy >= InpTrailingStart * _Point)
|
| {
|
| double new_sl = NormalizeDouble(bid - InpTrailingDist * _Point, _Digits);
|
| if(new_sl > breakeven_buy)
|
| ModifyIsolatedGridSL(target_comment, POSITION_TYPE_BUY, new_sl);
|
| }
|
| }
|
| if(total_vol_sell > 0)
|
| {
|
| double breakeven_sell = weighted_price_sell / total_vol_sell;
|
| if(breakeven_sell - ask >= InpTrailingStart * _Point)
|
| {
|
| double new_sl = NormalizeDouble(ask + InpTrailingDist * _Point, _Digits);
|
| if(new_sl < breakeven_sell)
|
| ModifyIsolatedGridSL(target_comment, POSITION_TYPE_SELL, new_sl);
|
| }
|
| }
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Модификация Стоп-Лосса внутри изолированной сетки конкретного FVG|
|
| //+------------------------------------------------------------------+
|
| void ModifyIsolatedGridSL(string target_comment, long position_type, double new_sl)
|
| {
|
| for(int i = PositionsTotal() - 1; i >= 0; i--)
|
| {
|
| ulong ticket = PositionGetTicket(i);
|
| if(ticket > 0 && PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
|
| {
|
| if(PositionGetString(POSITION_COMMENT) == target_comment && PositionGetInteger(POSITION_TYPE) == position_type)
|
| {
|
| double current_sl = PositionGetDouble(POSITION_SL);
|
| if(position_type == POSITION_TYPE_BUY)
|
| {
|
| if(current_sl == 0 || (new_sl - current_sl >= InpTrailingStep * _Point))
|
| trade.PositionModify(ticket, new_sl, PositionGetDouble(POSITION_TP));
|
| }
|
| else if(position_type == POSITION_TYPE_SELL)
|
| {
|
| if(current_sl == 0 || (current_sl - new_sl >= InpTrailingStep * _Point))
|
| trade.PositionModify(ticket, new_sl, PositionGetDouble(POSITION_TP));
|
| }
|
| }
|
| }
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Исторический учет отторгованных зон |
|
| //+------------------------------------------------------------------+
|
| bool WasGapTraded(string gap_id)
|
| {
|
| int size = ArraySize(g_traded_gaps);
|
| for(int i = 0; i < size; i++)
|
| if(g_traded_gaps[i] == gap_id) return(true);
|
| return(false);
|
| }
|
| void MarkGapAsTraded(string gap_id)
|
| {
|
| int size = ArraySize(g_traded_gaps);
|
| for(int i = 0; i < size; i++) if(g_traded_gaps[i] == gap_id) return;
|
| ArrayResize(g_traded_gaps, size + 1);
|
| g_traded_gaps[size] = gap_id;
|
| SaveGapToFile(gap_id);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| РАБОТА С ФАЙЛАМИ (Запись/Чтение с диска) |
|
| //+------------------------------------------------------------------+
|
| void SaveGapToFile(string gap_id)
|
| {
|
| int file_handle = FileOpen(g_file_name, FILE_WRITE|FILE_READ|FILE_TXT|FILE_ANSI);
|
| if(file_handle != INVALID_HANDLE)
|
| {
|
| FileSeek(file_handle, 0, SEEK_END);
|
| FileWriteString(file_handle, gap_id + "\r\n");
|
| FileClose(file_handle);
|
| }
|
| }
|
| void LoadGapsFromFile()
|
| {
|
| if(!FileIsExist(g_file_name)) return;
|
| int file_handle = FileOpen(g_file_name, FILE_READ|FILE_TXT|FILE_ANSI);
|
| if(file_handle != INVALID_HANDLE)
|
| {
|
| while(!FileIsEnding(file_handle))
|
| {
|
| // Исправлено: Сдвиг указателя (параметр 0) предотвращает бесконечный цикл
|
| string line = FileReadString(file_handle, 0);
|
| StringTrimLeft(line);
|
| StringTrimRight(line);
|
| if(line != "")
|
| {
|
| int size = ArraySize(g_traded_gaps);
|
| ArrayResize(g_traded_gaps, size + 1);
|
| g_traded_gaps[size] = line;
|
| }
|
| }
|
| FileClose(file_handle);
|
| Print("Успешно загружено ", ArraySize(g_traded_gaps), " ID зон FVG из дискового файла хранилища.");
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Проверка: были ли уже выставлены ордера по ID данного гэпа |
|
| //+------------------------------------------------------------------+
|
| bool IsGridAlreadyPlaced(string gap_id)
|
| {
|
| string target_comment = "FVG_" + gap_id;
|
| for(int i = OrdersTotal() - 1; i >= 0; i--)
|
| {
|
| ulong ticket = OrderGetTicket(i);
|
| if(ticket > 0 && OrderGetInteger(ORDER_MAGIC) == InpMagicNumber && OrderGetString(ORDER_COMMENT) == target_comment)
|
| return(true);
|
| }
|
| for(int i = PositionsTotal() - 1; i >= 0; i--)
|
| {
|
| ulong ticket = PositionGetTicket(i);
|
| if(ticket > 0 && PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber && PositionGetString(POSITION_COMMENT) == target_comment)
|
| return(true);
|
| }
|
| return(false);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Проверка на смягчение бычьего гэпа |
|
|
|
| //+------------------------------------------------------------------+
|
| bool IsBullishMitigated(const int formation_index, const double gap_bottom, const double gap_top, const double &low[])
|
| {
|
| for(int index = formation_index - 1; index >= 0; index--)
|
| if(low[index] <= gap_bottom)
|
| return(true);
|
| return(false);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Проверка на смягчение медвежьего гэпа |
|
|
|
| //+------------------------------------------------------------------+
|
| bool IsBearishMitigated(const int formation_index, const double gap_bottom, const double gap_top, const double &high[])
|
| {
|
| for(int index = formation_index - 1; index >= 0; index--)
|
| if(high[index] >= gap_top)
|
| return(true);
|
| return(false);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Отрисовка пунктирных прямоугольников без заливки |
|
|
|
| //+------------------------------------------------------------------+
|
| void DrawGap(const string id, const datetime start_time, const datetime end_time,
|
| const double top, const double bottom, const bool bullish, const bool mitigated)
|
| {
|
| const string rectangle_name = g_prefix + "BOX_" + id;
|
| color base_color = (mitigated ? InpMitigatedColor : (bullish ? InpBullishGapColor : InpBearishGapColor));
|
| if(!ObjectCreate(0, rectangle_name, OBJ_RECTANGLE, 0, start_time, top, end_time, bottom))
|
| return;
|
| ObjectSetInteger(0, rectangle_name, OBJPROP_COLOR, base_color);
|
| ObjectSetInteger(0, rectangle_name, OBJPROP_STYLE, STYLE_DOT);
|
| ObjectSetInteger(0, rectangle_name, OBJPROP_WIDTH, 1);
|
| ObjectSetInteger(0, rectangle_name, OBJPROP_FILL, false);
|
| ObjectSetInteger(0, rectangle_name, OBJPROP_BACK, true);
|
| ObjectSetInteger(0, rectangle_name, OBJPROP_SELECTABLE, false);
|
| ObjectSetInteger(0, rectangle_name, OBJPROP_HIDDEN, true);
|
| if(!InpShowLabels) return;
|
| const string label_name = g_prefix + "TEXT_" + id;
|
| const double label_price = (top + bottom) * 0.5;
|
| if(ObjectCreate(0, label_name, OBJ_TEXT, 0, start_time, label_price))
|
| {
|
| string label = (bullish ? "Bullish FVG" : "Bearish FVG");
|
| if(mitigated) label += " filled";
|
| ObjectSetString(0, label_name, OBJPROP_TEXT, label);
|
| ObjectSetString(0, label_name, OBJPROP_FONT, "Arial");
|
| ObjectSetInteger(0, label_name, OBJPROP_FONTSIZE, 8);
|
| ObjectSetInteger(0, label_name, OBJPROP_COLOR, mitigated ? clrGray : (bullish ? clrForestGreen : clrFireBrick));
|
| ObjectSetInteger(0, label_name, OBJPROP_ANCHOR, ANCHOR_LEFT);
|
| ObjectSetInteger(0, label_name, OBJPROP_SELECTABLE, false);
|
| ObjectSetInteger(0, label_name, OBJPROP_HIDDEN, true);
|
| }
|
|
|
| } |