От новичка до эксперта: Создание индикатора-оверлея CRT для нескольких таймфреймов в MQL5
Теория свечного диапазона (CRT) позволяет определять надежные структурные уровни на старших таймфреймах, однако трейдерам сложно отображать эти диапазоны на графиках младших таймфреймов. В этой статье представлен индикатор на языке MQL5, который решает эту проблему, отображая элементы свечи старшего таймфрейма — полный диапазон, тело и тени — непосредственно на текущем графике младшего таймфрейма. Этот инструмент позволяет трейдерам выявлять ключевые ценовые уровни, зоны ликвидности и точки входа с учетом контекста без переключения между таймфреймами. Он превращает CRT из абстрактного понятия в постоянно видимый ориентир и помогает принимать более обоснованные решения при совершении сделок.
Содержание
Введение
Трейдеры, использующие теорию свечного диапазона (CRT), сталкиваются с практической проблемой: структурные уровни, видимые на старших таймфреймах (H1, H4, Daily), исчезают при переходе на таймфреймы для совершения сделок (M1-M30). Потеря этого контекста приводит к ошибкам интерпретации: то, что на пятиминутном графике выглядит как пробой, на дневном часто оказывается снятием ликвидности институциональными участниками. Поэтому практическое решение должно быть точным и проверяемым: каждый бар младшего таймфрейма должен однозначно сопоставляться с соответствующей свечой старшего таймфрейма (точная привязка по времени), графическое наложение не должно перерисовываться (сигналы фиксируются только после закрытия свечи старшего таймфрейма), а полный диапазон, тело и тени этой свечи должны отображаться непосредственно на торговом графике без ручной разметки и переключения окон.
В статье представлена реализация на языке MQL5, отвечающая этим требованиям: данные OHLC старшего таймфрейма накладываются на текущий график, каждый бар младшего таймфрейма связывается с соответствующей свечой старшего, а паттерны CRT подтверждаются только после ее закрытия. Вместо необходимости держать уровни CRT в памяти трейдер получает воспроизводимое графическое наложение, готовое к использованию при совершении сделок.
Концепция — истоки и логика CRT
Теория свечного диапазона (CRT) опирается на две разные традиции: давно известные рыночные принципы и современную авторскую терминологию. Эти принципы восходят к моделям Вайкоффа "Springs" (снятиям ликвидности ниже поддержки с последующим разворотом) и "Upthrusts" (снятиям ликвидности выше сопротивления с последующим разворотом). Позднее эти механизмы популяризировала Линда Брэдфорд Рашке в стратегии "Turtle Soup". Однако CRT как система с собственным названием — это современная разработка, авторство которой в сообществе, близком к ICT, обычно приписывают трейдеру Romeotpt (Raid). Raid систематизировал цикл "накопление — манипуляция — распределение" (A-M-D) в терминах CRT, используемых сегодня.
Основной принцип
CRT основана на простой аксиоме: каждая свеча представляет собой диапазон. У каждой свечи есть полный торговый диапазон (от максимума до минимума свечи — область размещения ликвидности) и диапазон от открытия до закрытия — тело свечи, отражающее уровень цены, принятый рынком. Тени показывают, что цена была отвергнута на этих уровнях. Ключевая опорная точка — цена открытия: сразу после открытия часто сначала движется против предполагаемого направления свечи, снимая ликвидность, а затем разворачивается.
Старшие таймфреймы (дневной, H4) придают этим уровням структурную значимость. На младших таймфреймах (M5, M15) они полностью скрыты — так было до сих пор.
Постоянное переключение между таймфреймами — это не просто неудобство. Это напрямую влияет на качество исполнения сделок. Когда трейдер наблюдает за пятиминутным графиком без диапазонов дневных свечей или свечей H4, он фактически не видит уровней сосредоточения ордеров институциональных участников. Например, резкое падение на M5 ниже недавнего минимума может выглядеть как медвежий пробой. Если этот минимум совпадает с ценой открытия дневной свечи (ключевым уровнем ликвидности CRT), то, вероятнее всего, происходит манипулятивное снятие ликвидности и формируется сетап для входа в длинную позицию. Без наложения данных старшего таймфрейма многие трейдеры неверно интерпретируют движение и входят в короткую позицию у самого минимума движения, уже после снятия стопов. Этот индикатор для работы на нескольких таймфреймах избавляет от догадок: структурный контекст отображается непосредственно на торговом графике, и сбивающий с толку ценовой выброс становится узнаваемым сетапом CRT.
Почему крупные игроки снова и снова разворачивают цену у границ диапазона? Ответ кроется в микроструктуре рынка: стоп-ордера и отложенные ордера скапливаются чуть выше очевидных максимумов и чуть ниже очевидных минимумов. Снимая ликвидность на этих уровнях (фаза "манипуляции"), институциональные участники вызывают срабатывание стоп-ордеров розничных трейдеров и набирают собственные крупные позиции, прежде чем направить цену к противоположной границе диапазона (фаза "распределения"). Свечи старшего таймфрейма отражают эту активность — именно в диапазонах дневных свечей или H4 сосредоточена значительная ликвидность. Паттерн CRT на дневном графике сигнализирует о том, что крупный участник рынка, вероятно, завершил накопление или распределение и цена вернется к противоположной границе диапазона. Индикатор не просто рисует линии. Он показывает следы активности институциональных участников на каждом баре младшего таймфрейма, помогая розничным трейдерам согласовывать входы с логикой профессионалов.

Рис. 1. Диапазон медвежьей дневной свечи (оранжевый блок), наложенный на график H1.
На рис. 1 цена снимает ликвидность ниже дневного минимума (манипуляция), разворачивается и дает сигнал на покупку с целью у дневного максимума (TP). Индикатор автоматически отображает диапазон, уровень входа, стоп-лосс и тейк-профит.
Практическая польза индикатора
Этот индикатор для работы на нескольких таймфреймах восполняет недостающий контекст. Он накладывает данные OHLC свечей старшего таймфрейма непосредственно на график младшего таймфрейма. Например, вы видите, достигает ли снятие ликвидности на M5 цены открытия дня или середины диапазона предыдущего дня — зон с высокой вероятностью разворота. Важно, что индикатор можно настроить: использовать закрытые свечи для построения запаздывающих уровней, удобных для тестирования на исторических данных, или текущую цену открытия (с проекциями на основе предыдущих диапазонов) для опережающего инструмента CRT, работающего в реальном времени. Кроме того, он отсеивает воскресные и небольшие свечи (например, на участках разрывов 22:00-00:00), которые иначе искажали бы расчет диапазона.
В результате получается постоянно отображаемое наложение CRT без перерисовки, которое сохраняет контекст старшего таймфрейма именно там, где он нужен, — на торговом графике.
Требования к корректному решению
Описанные выше проблемы задают обязательные критерии, которым должно соответствовать любое работоспособное решение. В таблице ниже критерии успешности сформулированы так, чтобы их можно было проверить.
| Требование | Определение, поддающееся проверке |
|---|---|
| Воспроизводимость | Для паттерна CRT на исторических данных при каждом запуске строятся одинаковые линии входа, TP и SL |
| Обнаружение в реальном времени | Сигнал фиксируется сразу после закрытия сигнальной свечи на старшем таймфрейме |
| Отсутствие ручной разметки | Все прямоугольники, линии входа, уровни TP и SL отображаются на текущем графике автоматически |
| Алерты | Звуковые оповещения и push-уведомления при формировании нового сетапа |
| Производительность | Работа с тысячами баров без зависания графика |
| Гибкость | Можно настраивать смещение, фильтр размера тела свечи, инверсия TP/SL и длина продления линий |
Реализация
Индикатор CRT_MTF полностью написан на языке MQL5 и работает непосредственно на любом графике младшего таймфрейма. Он загружает данные старшего таймфрейма, сопоставляет каждый бар младшего таймфрейма с соответствующей свечой старшего, обнаруживает корректные паттерны CRT и автоматически отображает все графические элементы. Ниже мы пошагово рассмотрим реализацию, объясним назначение каждого компонента, обоснуем ключевые решения и покажем, как индикатор работает в реальных рыночных условиях. Полный исходный код приводится отдельно — здесь мы сосредоточимся на основных механизмах.
Шаг 1 — свойства индикатора, входные параметры и настройка инструмента
Каждый индикатор на языке MQL5 начинается с директив #property, которые задают MetaTrader 5 правила работы с пользовательским инструментом. Для CRT MTF нам нужны два буфера стрелок (один для сигналов на продажу, другой — для сигналов на покупку). Также мы указываем, что индикатор отображает данные непосредственно в окне графика (а не в отдельном подокне). Далее идут входные параметры, сгруппированные по назначению: выбор старшего таймфрейма, настройка условий CRT (смещение, множитель тела свечи), переключатели алертов, цвета всех графических элементов, настройки стрелок и параметры линий TP/SL. Эти параметры позволяют трейдерам полностью контролировать внешний вид индикатора и чувствительность сигналов без изменения кода.
//+------------------------------------------------------------------+ //| CRT_MTF.mq5 | //| Copyright 2026, Clemence Benjamin| //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Clemence Benjamin" #property link "https://www.mql5.com/ru/users/billionaire2024/seller" #property version "2.90" #property description "CRT MTF—Intelligent entry and exit levels, plus signal alerts." #property indicator_chart_window #property indicator_buffers 2 #property indicator_plots 2 #property indicator_type1 DRAW_ARROW #property indicator_width1 4 #property indicator_color1 0x8000FF #property indicator_label1 "Sell Signal" #property indicator_type2 DRAW_ARROW #property indicator_width2 4 #property indicator_color2 0xFF9500 #property indicator_label2 "Buy Signal" //--- input parameters input ENUM_TIMEFRAMES InpHigherTF = PERIOD_H1; // Higher Timeframe input double InpOffset = 2.0; // Range Offset Factor input double InpCandleBodyMultiplier = 3.0; // Body Size Filter input bool InpEnableSoundAlerts = true; input bool InpEnablePushAlerts = true; //--- Colors input color InpSellRangeColor = clrLightPink; input color InpSellSignalColor = clrCrimson; input color InpBuyRangeColor = clrPaleGreen; input color InpBuySignalColor = clrLimeGreen; input color InpRangeRectColor = clrOrange; //--- Arrow settings input bool InpDrawArrowOnSignal = true; //--- TP/SL/Entry lines (fixed length) input bool InpDrawEntryLine = true; input bool InpDrawTPLine = true; input bool InpDrawSLLine = true; input bool InpFlipTPSL = false; input color InpEntryLineColor = clrWhite; input color InpTPLineColor = clrDodgerBlue; input color InpSLLineColor = clrRed; input int InpLineExtendBars = 50; input bool InpDebugPrint = false;
Шаг 2 — глобальные структуры и переменные, упорядочение данных
Для эффективной работы с данными старшего таймфрейма мы определяем пользовательскую структуру HTFCandle. В ней хранятся время, цена открытия, максимум, минимум и цена закрытия одной свечи. Массив структур htfCandles[] будет хранить все загруженные свечи старшего таймфрейма. Мы также объявляем два буфера (BufferSell и BufferBuy) для отображения стрелок и массив сопоставления htfIndexMap, связывающий каждый бар младшего таймфрейма с соответствующей свечой старшего. Кроме того, вводим point (для ценового смещения), lastAlertTime (для предотвращения повторных алертов) и переменные, хранящие длительность периодов обоих таймфреймов в секундах. Эти глобальные переменные доступны из любой функции, что делает код более упорядоченным и быстрым.
//--- indicator buffers double BufferSell[]; double BufferBuy[]; //--- globals datetime lastAlertTime; string indicatorShortName; double point; int htfPeriodSeconds; int currPeriodSeconds; //+------------------------------------------------------------------+ //| Structure for higher timeframe candle | //+------------------------------------------------------------------+ struct HTFCandle { datetime time; double open, high, low, close; }; HTFCandle htfCandles[]; int htfTotal; int htfIndexMap[];
Шаг 3 — инициализация в OnInit (однократная настройка)
Функция OnInit автоматически вызывается при добавлении индикатора на график. Ее задача — подготовить индикатор к работе. Сначала мы получаем значение пункта символа с помощью SymbolInfoDouble — это позволяет учитывать брокеров с пятизначными котировками, у которых один пункт равен 0,00001 вместо 0,0001. Затем мы вычисляем длительность периода в секундах для старшего таймфрейма (htfPeriodSeconds) и текущего (currPeriodSeconds). Далее мы настраиваем два буфера индикатора, задаем символы стрелок (234 — вниз, 233 — вверх) и используем EMPTY_VALUE для пустых значений, чтобы на барах без сигнала ничего не отображалось. В заключение мы задаем короткое имя для окна индикатора и возвращаем INIT_SUCCEEDED. Если какой-либо этап завершится ошибкой, индикатор не загрузится, но здесь настройка достаточно проста.
//+------------------------------------------------------------------+ //| Initializes indicator buffers and settings | //+------------------------------------------------------------------+ int OnInit() { point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); htfPeriodSeconds = PeriodSeconds(InpHigherTF); currPeriodSeconds = PeriodSeconds(_Period); SetIndexBuffer(0, BufferSell, INDICATOR_DATA); PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(0, PLOT_ARROW, 234); // down arrow for sell SetIndexBuffer(1, BufferBuy, INDICATOR_DATA); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(1, PLOT_ARROW, 233); // up arrow for buy indicatorShortName = "CRT MTF (Signal+Entry)"; IndicatorSetString(INDICATOR_SHORTNAME, indicatorShortName); return(INIT_SUCCEEDED); }
Шаг 4 — загрузка данных старшего таймфрейма для получения структурного контекста
В OnCalculate сначала загружаются свечи старшего таймфрейма. Мы определяем общее количество доступных баров старшего таймфрейма с помощью функции Bars. Если баров меньше трех, мы сразу завершаем выполнение: нужны как минимум две свечи (свеча диапазона и сигнальная свеча) и еще одна для согласования. Затем мы изменяем размер массива htfCandles, чтобы он вмещал все свечи старшего таймфрейма. С помощью функций CopyTime, CopyOpen, CopyHigh, CopyLow и CopyClose мы загружаем данные во временные массивы. Затем для каждого массива устанавливается индексация как у временного ряда (индекс 0 = самый новый), чтобы согласовать ее с индексацией данных младшего таймфрейма. Наконец, для удобства доступа мы копируем значения в элементы массива структур htfCandles. При таком подходе вся история старшего таймфрейма загружается за один раз, что повышает эффективность и позволяет избежать повторного копирования на каждом тике.
//--- load higher TF data htfTotal = Bars(_Symbol, InpHigherTF); if(htfTotal < 3) return(rates_total); ArrayResize(htfCandles, htfTotal); datetime htfTimeArray[]; double htfOpen[], htfHigh[], htfLow[], htfClose[]; if(CopyTime(_Symbol, InpHigherTF, 0, htfTotal, htfTimeArray) <= 0 || CopyOpen(_Symbol, InpHigherTF, 0, htfTotal, htfOpen) <= 0 || CopyHigh(_Symbol, InpHigherTF, 0, htfTotal, htfHigh) <= 0 || CopyLow(_Symbol, InpHigherTF, 0, htfTotal, htfLow) <= 0 || CopyClose(_Symbol, InpHigherTF, 0, htfTotal, htfClose) <= 0) return(rates_total); ArraySetAsSeries(htfTimeArray, true); ArraySetAsSeries(htfOpen, true); ArraySetAsSeries(htfHigh, true); ArraySetAsSeries(htfLow, true); ArraySetAsSeries(htfClose, true); for(int i = 0; i < htfTotal; i++) { htfCandles[i].time = htfTimeArray[i]; htfCandles[i].open = htfOpen[i]; htfCandles[i].high = htfHigh[i]; htfCandles[i].low = htfLow[i]; htfCandles[i].close = htfClose[i]; }
Шаг 5 — сопоставление баров младшего таймфрейма со свечами старшего (бинарный поиск)
Чтобы определить, к какой свече старшего таймфрейма относится каждый бар младшего, мы строим карту индексов с помощью бинарного поиска. Для каждого бара младшего таймфрейма со временем bt мы находим свечу старшего таймфрейма, для которой start ≤ bt < end (где end = start + htfPeriodSeconds). Этот подход со сложностью O(log n) эффективен даже при работе с тысячами баров. Мы перебираем все бары младшего таймфрейма, выполняем бинарный поиск в массиве htfCandles и сохраняем найденный индекс в htfIndexMap[i]. Если соответствующая свеча не найдена (например, бар предшествует самому раннему бару старшего таймфрейма), мы сохраняем -1 и впоследствии пропускаем этот бар. Это сопоставление вычисляется один раз при каждом вызове OnCalculate и повторно используется во всех процедурах обнаружения сигналов и отрисовки.
//--- map lower bars to HTF indices (binary search) ArrayResize(htfIndexMap, rates_total); ArrayInitialize(htfIndexMap, -1); for(int i = 0; i < rates_total; i++) { datetime bt = time[i]; int lo = 0, hi = htfTotal - 1, found = -1; while(lo <= hi) { int mid = (lo + hi) / 2; datetime start = htfCandles[mid].time; datetime end = start + htfPeriodSeconds; if(bt >= start && bt < end) { found = mid; break; } else if(bt < start) lo = mid + 1; else hi = mid - 1; } htfIndexMap[i] = found; }
Шаг 6 — логика обнаружения сигналов CRT (основной механизм распознавания паттернов)
Индикатор проверяет каждый бар младшего таймфрейма, завершающий соответствующую свечу старшего: подтвердить паттерн CRT можно только после полного закрытия сигнальной свечи. Для этих баров мы обращаемся к предыдущей свече (rangeIdx = signalIdx + 1) и проверяем условия CRT. Для сигнала на продажу нужна бычья свеча диапазона (close > open). Сигнальная свеча должна пробить максимум диапазона, но закрыться ниже него и ниже собственной цены открытия. Ее тело должно быть небольшим (меньше диапазона, деленного на множитель тела), а минимум — оставаться выше минимума диапазона плюс смещение (диапазон, деленный на InpOffset). Для покупки условия обратные: свеча диапазона должна быть медвежьей, а сигнальная — пробить минимум диапазона, но закрыться выше него и выше собственной цены открытия. Тело сигнальной свечи должно быть небольшим, а ее максимум — оставаться ниже максимума диапазона за вычетом смещения. Эти правила отличаются математической точностью и исключают субъективную интерпретацию.
//--- Only evaluate when this lower bar is the LAST bar of its HTF candle bool isLastBarOfThisCandle = false; if(i == 0) isLastBarOfThisCandle = true; else if(htfIndexMap[i-1] != htfIdx) isLastBarOfThisCandle = true; if(!isLastBarOfThisCandle) continue; //--- CRT condition checks double rangeRange = rangeHigh - rangeLow; double sigBody = MathAbs(sigOpen - sigClose); bool isSell = false, isBuy = false; if(rangeClose > rangeOpen && sigHigh > rangeHigh && sigClose < rangeHigh && sigClose < sigOpen && sigLow > rangeOpen && sigBody < rangeRange / InpCandleBodyMultiplier && sigLow > rangeLow + (rangeRange / InpOffset)) { isSell = true; } else if(rangeClose < rangeOpen && sigLow < rangeLow && sigClose > rangeLow && sigClose > sigOpen && sigHigh < rangeOpen && sigBody < rangeRange / InpCandleBodyMultiplier && sigHigh < rangeHigh - (rangeRange / InpOffset)) { isBuy = true; }
Шаг 7 — сохранение сигналов и заполнение буферов стрелок (фиксация результатов)
При обнаружении корректного паттерна необходимо точно определить бар младшего таймфрейма, на котором следует разместить сигнал: это последний бар в пределах сигнальной свечи старшего таймфрейма. Для этого мы просматриваем бары вперед от текущего индекса i, пока не изменится индекс соответствующей свечи старшего таймфрейма. На этом последнем баре (lastIdx) завершается сигнальная свеча в представлении младшего таймфрейма. Затем мы сохраняем все необходимые сведения о сигнале (индекс свечи диапазона, индекс сигнальной свечи, направление, lastIdx, цена входа = close[lastIdx]) в динамическом массиве структур SignalInfo. Если пользователь включил отображение стрелок, мы записываем значение в соответствующий элемент буфера: для сигнала на продажу стрелка располагается выше максимума бара с небольшим отступом, а для сигнала на покупку — ниже минимума с отступом. При этом на графике появляется четкий маркер.
//--- Find the absolute last lower bar index for this signal candle int lastIdx = i; for(int j = i + 1; j < rates_total; j++) { if(htfIndexMap[j] == signalIdx) lastIdx = j; else break; } SignalInfo sig; sig.rangeHtfIdx = rangeIdx; sig.signalHtfIdx = signalIdx; sig.isSell = isSell; sig.lastSignalLowerIdx = lastIdx; sig.entryPrice = close[lastIdx]; ArrayResize(signals, ArraySize(signals) + 1); signals[ArraySize(signals) - 1] = sig; //--- Place arrow buffer if(InpDrawArrowOnSignal) { if(isSell) BufferSell[lastIdx] = high[lastIdx] + InpOffset * point; else BufferBuy[lastIdx] = low[lastIdx] - InpOffset * point; }
Шаг 8 — обработка алертов и уведомление трейдера в реальном времени
Алерты отправляются только для последнего бара (i == rates_total-1), чтобы при первой загрузке индикатора не выдавать множество уведомлений об исторических сигналах. Статическая переменная lastAlertTime хранит временную метку последнего алерта; если время бара нового сигнала отличается от нее, формируется алерт. Вспомогательная функция SendAlert проверяет входные параметры: если включены звуковые алерты, она вызывает Alert(); если включены Push-уведомления — SendNotification. Сообщение алерта содержит символ, текущий таймфрейм, тип сигнала (BUY/SELL) и время бара. Это гарантирует, что трейдер не упустит свежий сетап, даже находясь вдали от экрана.
//--- SIGNAL ALERT: when setup completes (arrow placed) //--- Only alert on the most recent bar (to avoid spamming historical alerts) if(i == rates_total - 1 && time[lastIdx] != lastAlertTime) { string signalType = (isSell) ? "SELL" : "BUY"; string alertMsg = StringFormat("CRT MTF | %s %s | %s signal at %s", Symbol(), EnumToString(_Period), signalType, TimeToString(time[lastIdx])); SendAlert(alertMsg); lastAlertTime = time[lastIdx]; }
Шаг 9 — отрисовка объектов: цветные свечи, прямоугольник диапазона и линии входа, TP и SL
Все графические построения выполняются один раз при появлении нового бара, чтобы не создавать лишние объекты и не загромождать график. Сначала мы удаляем все объекты с префиксом "CRT_" с помощью ObjectsDeleteAll. Затем мы заново формируем массив drawnSignalIds, чтобы отслеживать уже обработанные идентификаторы сигналов (заданные временем свечи диапазона). Для каждого сигнала в массиве signals мы проверяем, отображен ли уже сигнал с его signalId; если нет, отрисовываем все графические элементы. К ним относятся:
- Цветные прямоугольники позади каждого бара младшего таймфрейма, относящегося к свече диапазона (с цветом InpSellRangeColor или InpBuyRangeColor), и каждого бара сигнальной свечи (с цветом InpSellSignalColor или InpBuySignalColor). Это наглядно выделяет две ключевые свечи старшего таймфрейма.
- Полупрозрачный оранжевый прямоугольник очерчивает границы свечи диапазона старшего таймфрейма (от максимума до минимума) на всем ее временном интервале, создавая четкий ориентир в виде зоны.
- Пунктирная линия входа начинается после закрытия сигнальной свечи (lineStartTime = время сигнальной свечи + htfPeriodSeconds) и продолжается вправо на InpLineExtendBars баров младшего таймфрейма. В конце линии размещается текстовая метка ("Buy Entry" или "Sell Entry").
- Линии TP и SL (пунктирные) строятся по выбранным уровням: по умолчанию для продажи TP = минимум диапазона, SL = максимум сигнальной свечи; для покупки TP = максимум диапазона, SL = минимум сигнальной свечи. Параметр InpFlipTPSL позволяет при необходимости поменять эти значения местами.
Все объекты создаются со свойствами OBJPROP_BACK = true, чтобы располагаться позади ценовых баров, и OBJPROP_SELECTABLE = false, чтобы исключить случайное перетаскивание. Благодаря этому график остается наглядным и интерактивным.
//--- Draw objects on new bar only static datetime lastDrawTime = 0; datetime curTime = time[rates_total - 1]; if(curTime != lastDrawTime) { ObjectsDeleteAll(0, "CRT_"); lastDrawTime = curTime; } //--- Example: drawing entry line string entryLineName = "CRT_Entry_" + IntegerToString(signalId); if(ObjectFind(0, entryLineName) < 0) { ObjectCreate(0, entryLineName, OBJ_TREND, 0, lineStartTime, entryPrice, lineEndTime, entryPrice); ObjectSetInteger(0, entryLineName, OBJPROP_COLOR, InpEntryLineColor); ObjectSetInteger(0, entryLineName, OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(0, entryLineName, OBJPROP_WIDTH, 1); ObjectSetInteger(0, entryLineName, OBJPROP_BACK, false); ObjectSetInteger(0, entryLineName, OBJPROP_RAY_RIGHT, false); }
Шаг 10 — оптимизация и граничные случаи — надежность и быстродействие
Несколько проектных решений обеспечивают эффективную работу индикатора даже при большом объеме исторических данных. Во-первых, сопоставление с помощью бинарного поиска имеет сложность O(log n) для каждого бара младшего таймфрейма и позволяет избежать линейного перебора, замедляющего обработку тысяч баров. Во-вторых, мы проверяем условия CRT только на последнем баре младшего таймфрейма внутри каждой свечи старшего. Это сокращает объем вычислений максимум на 95%, поскольку большинство младших баров находятся внутри свечи и не являются последними. В-третьих, проверка дубликатов объектов с помощью массива drawnSignalIds предотвращает повторную отрисовку одного и того же сетапа на каждом тике. В-четвертых, если доступно менее трех свечей старшего таймфрейма, мы сразу завершаем обработку, предотвращая ошибки.
Обработка граничных случаев: гэпы выходного дня и нерегулярные торговые часы не нарушают работу бинарного поиска, поскольку сравнивается точное время начала и окончания. Бары младшего таймфрейма за пределами самой ранней свечи старшего таймфрейма игнорируются (htfIndexMap[i] = -1). Дополнительная возможность перестановки TP/SL реализована без деления на ноль и формирования недопустимых уровней. При этом индикатор не перерисовывается: сигналы фиксируются при закрытии сигнальной свечи, что обеспечивает надежность тестирования на исторических данных.
Тестирование и результаты
Чтобы протестировать индикатор, скомпилируйте файл CRT_MTF.mq5 в MetaEditor (нажмите F7). Добавьте скомпилированный индикатор на любой график младшего таймфрейма (M1, M5, M15, M30, H1). Индикатор автоматически загружает данные старшего таймфрейма и отображает все исторические сетапы CRT в пределах периода анализа.
Ручное тестирование на исторических данных: прокрутите график влево. Вы увидите цветные прямоугольники позади исторических свечей, задающих диапазон, и сигнальных свечей, линии входа с метками, уровни TP/SL и стрелки-маркеры точно в местах появления сигналов. Поскольку индикатор не перерисовывается, прошлые сигналы остаются неизменными — это идеально подходит для оценки эффективности стратегии на исторических данных.
Приведенная ниже GIF-анимация показывает, как при прокрутке графика к прошлым данным индикатор отображает ранее сформированные сетапы CRT.

Рис. 2. Тестирование на индексе Volatility 75 (1s), M5.
При закрытии новой сигнальной свечи индикатор выдает алерт (звуковое оповещение и/или Push-уведомление) и сразу отображает линию входа, TP, SL и стрелки. Линии продлеваются вправо на количество баров, указанное в параметре InpLineExtendBars. Индикатор протестировали на графиках M1, M5, M15, M30 и H1 со старшими таймфреймами от H1 до дневного; на объеме исторических данных до 10 000 баров снижения производительности не наблюдалось.
Заключение
Мы разработали инженерное решение задачи CRT: индикатор на языке MQL5, который загружает данные OHLC старшего таймфрейма, сопоставляет каждый бар младшего таймфрейма с соответствующей свечой старшего и отображает ее диапазон, тело и тени на активном графике. Обнаружение паттернов CRT выполняется только после закрытия сигнальной свечи старшего таймфрейма, что гарантирует отсутствие перерисовки сигналов.
Кроме того, индикатор отмечает подтвержденные входы стрелками, строит уровни входа, TP и SL по диапазону и экстремумам сигнальной свечи и выдает настраиваемые звуковые оповещения и Push-уведомления. Встроенные фильтры (размер тела свечи, смещения, исключение свечей малого диапазона и свечей на участках разрывов торговых сессий) и оптимизация производительности (сопоставление с помощью бинарного поиска и определение последнего бара) делают графическое наложение пригодным для объективного тестирования на исторических данных и совершения сделок в реальном времени.
Итак, вы получаете воспроизводимый инструмент CRT для работы на нескольких таймфреймах, основанный на правилах. Скомпилируйте прилагаемый файл CRT_MTF.mq5, добавьте полученный индикатор на любой график младшего таймфрейма, выберите старший таймфрейм и остальные настройки — и торгуйте, видя точный контекст старшего таймфрейма непосредственно на торговом графике.
.
Основные выводы
| Основные выводы | Описание |
|---|---|
| 1. Бинарный поиск для сопоставления баров | Бинарный поиск (O(log n)) при сопоставлении баров младшего таймфрейма со свечами старшего гораздо эффективнее линейного перебора, особенно при большом объеме исторических данных. Это критически важная оптимизация для любого индикатора, работающего с несколькими таймфреймами. |
| 2. Определение последнего бара для точного формирования сигнала | Сигнал CRT должен формироваться только на последнем баре младшего таймфрейма внутри сигнальной свечи старшего. Последовательный просмотр вперед до изменения индекса свечи старшего таймфрейма позволяет использовать цену закрытия этого бара как цену входа, исключая перерисовку. |
| 3. Архитектура без перерисовки для тестирования на исторических данных | Запись значений в буферы стрелок только после закрытия сигнальной свечи и использование фиксированных цен входа гарантируют неизменность исторических сигналов. Благодаря этому индикатор пригоден для надежного ручного и автоматизированного тестирования на исторических данных. |
| 4. Исключение дублирования графических объектов | Статический массив (drawnSignalIds[]) отслеживает уже отображенные сетапы и предотвращает повторное создание прямоугольников и линий на каждом тике. Это уменьшает загромождение графика и повышает производительность. |
| 5. Ограничение частоты алертов с помощью временных меток | Хранение времени последнего бара, по которому выдан алерт (lastAlertTime), и сравнение этого времени со временем текущего сигнального бара предотвращают повторные алерты по одному сетапу даже при многократном пересчете индикатора. |
| 6. Настраиваемые TP/SL на основе структуры старшего таймфрейма | Уровни TP и SL, рассчитанные непосредственно по экстремумам свечи, задающей диапазон, и сигнальной свечи (с возможностью перестановки), устраняют субъективность. Так паттерн, трактуемый по усмотрению трейдера, превращается в торговую систему на основе правил. |
| 7. Устойчивость бинарного поиска к гэпам | Использование точных временных границ (start ≤ bar time < start + period) позволяет учитывать гэпы выходного дня и нерегулярные торговые часы без нарушения сопоставления — в отличие от простого подсчета баров. |
| 8. Двухпроходная обработка в OnCalculate | Разделение отображения исторических сигналов (полный цикл) и формирования алертов в реальном времени (только последний бар) обеспечивает наглядное тестирование на исторических данных и своевременные уведомления без назойливых повторов. |
| 9. Визуальное выделение свечей старшего таймфрейма | Цветные прямоугольники позади каждого бара младшего таймфрейма, входящего в свечу диапазона или сигнальную свечу, создают наглядный контекст. Установка OBJPROP_BACK = true сохраняет читаемость ценового графика. |
| 10. Продлеваемые линии для планирования | Линии входа, TP и SL, построенные как горизонтальные трендовые линии с настраиваемым продлением вправо (InpLineExtendBars), помогают трейдерам видеть будущие ценовые цели непосредственно на графике. |
| 11. Математическое описание условий CRT | Правила продажи и покупки (бычий диапазон + более высокий максимум + закрытие ниже верхней границы диапазона + небольшое тело свечи + смещение минимума) выражаются точными неравенствами. Это исключает догадки и обеспечивает единообразное распознавание паттернов в любых рыночных условиях. |
| 12. Синхронизация данных нескольких таймфреймов | Загрузка данных старшего таймфрейма один раз за вызов OnCalculate() и их хранение в массиве пользовательских структур (HTFCandle[]) позволяют избежать избыточного копирования. Этот подход можно повторно использовать в любом индикаторе, которому требуется контекст старшего таймфрейма. |
Приложения
| Имя файла | Тип | Версия | Описание |
|---|---|---|---|
| CRT_MTF.mq5 | Индикатор | 2,90 | Полный исходный код индикатора CRT для работы на нескольких таймфреймах. Скомпилируйте код в MetaEditor и добавьте полученный индикатор на любой график младшего таймфрейма. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/22190
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Создание объектно-ориентированного механизма статистического арбитража на основе Z-показателя в MQL5
Версионирование параметров советника в MQL5 с помощью файлов
Renko внутри советника: построение по тикам без offline-графика
Создание системы торговой аналитики (Часть 2): Как фиксировать закрытые сделки и передавать данные в формате JSON с помощью MQL5
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Спасибо за код, но в нём отсутствует терминал DOWN; а в журнале записано следующее:
12.05.2026 13:22:10.388 CB_CRT_MTF (EURUSD,M1) индикатор работает слишком медленно, 2016 мс. Перепишите индикатор, пожалуйста