Построение системы обнаружения дивергенций (Часть II): Пользовательский индикатор Adaptive SuperTrend
Содержание
- Введение
- Представляем индикатор Adaptive SuperTrend
- Управление состоянием и устранение перерисовки
- Реализация визуального индикатора на MQL5
- Расчет индикатора SuperTrend
- Тактическое применение и интерпретация
- Настройка параметров и практические аспекты
- Заключение
Введение
В предыдущей статье мы представили MPO4 — осциллятор на основе рыночного давления, который измеряет направленное усилие, стоящее за движениями цены, взвешивая тела свечей относительно недавней волатильности. В сочетании с механизмом обнаружения опорных точек без перерисовки он позволил нам выявлять бычьи и медвежьи дивергенции. Они показывают моменты, когда давление покупателей или продавцов, лежащее в основе движения цены, начинает ослабевать перед возможным разворотом рынка.
Перед трейдерами, торгующими по системе, возникает задача: когда дивергенция предупреждает нас об угасании тренда, как скорректировать правила управления сделкой, чтобы извлечь из этого выгоду?
Многие трейдеры используют трендовые инструменты, такие как SuperTrend, для управления входами в рынок и трейлинг-стопами. Индикатор SuperTrend, бесспорно, помогает трейдерам эффективно удерживать позиции во время продолжительных движений, но его математическая модель негибка. Чтобы линия следовала за ценой, стандартный индикатор SuperTrend умножает средний истинный диапазон (ATR) на фиксированную константу (например, 3,0). При этом не учитывается, усиливается ли моментум беспрепятственно или уже близок разворот; линия неизменно сохраняет фиксированную дистанцию, пока цена резко не развернется и не пересечет ее.
Такая негибкость приводит к двум неприятным для трейдеров ситуациям:
- потеря прибыли — во время разворота цене приходится пройти огромное расстояние, чтобы достичь фиксированной линии SuperTrend, из-за чего трейдер теряет значительную часть плавающей прибыли до срабатывания трейлинг-стопа;
- поздние входы — когда начинается новый тренд, сигнал на вход срабатывает только после того, как цена уже пересекла удаленную линию SuperTrend, из-за чего трейдер упускает самую раннюю фазу нового направленного движения.
Представляем индикатор Adaptive SuperTrend
Чтобы решить эту проблему, нам необходимо связать опережающие сигналы моментума с запаздывающими трендовыми сигналами. В этой статье мы преобразуем стандартный индикатор SuperTrend в Adaptive SuperTrend, встроив модуль обнаружения дивергенций непосредственно в его расчетный алгоритм.
Логика заключается в следующем: когда на рынке наблюдается устойчивый тренд, линия SuperTrend сохраняет стандартное безопасное расстояние от цены, чтобы избежать преждевременного срабатывания стоп-лосса или смены сигнала. Когда модуль обнаруживает дивергенцию, соответствующую заданным условиям, индикатор запускает механизм сжатия. Он уменьшает множитель ATR в SuperTrend с учетом коэффициента чувствительности и приближает линию к цене.
Одно это изменение полностью меняет принцип работы индикатора. За счет сжатия полосы при дивергенции индикатор автоматически подтягивает трейлинг-стоп, чтобы защитить плавающую прибыль непосредственно перед разворотом. В то же время, поскольку линия теперь находится ближе к цене, последующий сигнал о смене тренда сработает значительно раньше, обеспечивая быстрый вход в новый тренд.
В следующих разделах мы подробно разберем математические расчеты механизма сжатия, рассмотрим проблемы хранения состояния при его реализации на MQL5 с отображением без перерисовки, а также создадим индикатор Adaptive SuperTrend.
Основная идея — зачем сжимать SuperTrend?
Чтобы понять, зачем нужен Adaptive SuperTrend, сначала нужно разобраться, в чем с математической точки зрения может быть недостаток стандартного SuperTrend. Традиционный индикатор рассчитывает свои полосы так: берется медианная цена, к которой прибавляется или из которой вычитается средний истинный диапазон (ATR), умноженный на фиксированную константу.
Верхняя и нижняя полосы определяются следующими формулами:
UpperBand = Median + (Multiplier x ATR) LowerBand = Median - (Multiplier x ATR)
Если вы зададите множителю значение 3,0, SuperTrend будет поддерживать расстояние от медианной цены, равное ровно трем значениям ATR. Пока тренд остается сильным, такой большой запас расстояния — существенное преимущество: он предотвращает преждевременное срабатывание стоп-лосса при обычном рыночном шуме и небольших откатах.
Однако движение рынка не может бесконечно оставаться ровным и четко направленным. Когда тренд начинает истощаться, движение цены часто приостанавливается: формируется округлая вершина или округлое дно, а моментум незаметно угасает. Именно для выявления таких ситуаций и создан модуль обнаружения дивергенций MPO4. Если в индикаторе SuperTrend используется фиксированный множитель 3,0, это внутреннее ослабление не учитывается. В результате ваш трейлинг-стоп остается далеко от цены, и вам приходится ждать резкого и масштабного разворота, при котором цена наконец пересечет полосу и сделка закроется.
Математика механизма сжатия
Индикатор Adaptive SuperTrend использует динамический множитель. Вместо постоянного числа в качестве множителя используется динамическая переменная, которая напрямую реагирует на давление моментума.
Когда рынок находится в нормальном состоянии, индикатор использует заданный вами стандартный базовый множитель. Однако как только модуль обнаружения дивергенций MPO4 выявляет дивергенцию, соответствующую заданным условиям, он запускает механизм сжатия и применяет определенный коэффициент чувствительности для уменьшения множителя.
Скорректированный расчет выглядит следующим образом:
DynamicMultiplier = BaseMultiplier x (1 - Sensitivity) Например, предположим, что вы торгуете со стандартным множителем 3,0 и задали чувствительности к дивергенциям значение 0,3 (снижение на 30%). Как только на графике появляется бычья или медвежья дивергенция, индикатор выполняет пересчет:
DynamicMultiplier = 3.0 x (1 - 0.3) = 2.1
Линия SuperTrend мгновенно приближается к цене: расстояние сокращается с 3,0 ATR до 2,1 ATR.
Два преимущества Adaptive SuperTrend
Защитные выходы и агрессивные входы
Такое приближение линии к цене дает трейдеру два тактических преимущества:
- Защитный трейлинг (защитные выходы): если вы держите длинную позицию и на вершине тренда появляется медвежья дивергенция, линия SuperTrend поднимается ближе к текущей цене. Ваш трейлинг-стоп автоматически подтягивается. Если разворот произойдет, как и прогнозировалось, позиция закроется по стопу со значительно большей частью плавающей прибыли, вместо того чтобы терять ее в ожидании, пока цена достигнет стандартной линии с множителем 3,0.
- Ранний вход в тренд (агрессивные входы): поскольку линия SuperTrend подтянута ближе к текущей цене, для пробоя полосы требуется гораздо меньшее движение против прежнего тренда. Это означает, что индикатор, когда происходит реальный выход в новый тренд, переключится и сформирует сигнал на вход гораздо раньше, чем стандартный SuperTrend, позволяя вам открыть позицию ближе к самой опорной точке.
Связав множитель ATR напрямую с дивергенцией моментума, мы превращаем традиционно запаздывающий индикатор в адаптивную систему управления сделками, которая действует на опережение.
Управление состоянием и устранение перерисовки
В предыдущей статье об MPO4 были рассмотрены принципы использования буферов состояния и построения осциллятора моментума без перерисовки. Нам необходимо вновь рассмотреть эти принципы, поскольку для индикатора Adaptive SuperTrend появляется новое требование. Индикатор должен сохранять значения опорных точек, отслеживать, возникла ли дивергенция, и подсчитывать количество баров с момента этого события.
Теперь задача не сводится только к отрисовке стрелок, которые затем остаются на месте. При использовании механизма сжатия линия SuperTrend должна некоторое время оставаться ближе к цене после обнаружения дивергенции, а затем возвращаться к обычному расстоянию при выполнении соответствующих условий. Если бы на следующем тике информация о дивергенции терялась, линия мгновенно возвращалась бы к исходному расстоянию от цены. Это создавало бы визуальный хаос и делало индикатор непригодным для управления сделками.
Почему обычные глобальные переменные не подходят
Распространенная ошибка при программировании на языке MQL5 — использование обычных глобальных переменных, таких как datetime LastDivTime или bool DivActive, для управления состоянием. В скрипте или советнике это будет работать, поскольку программа выполняется последовательно от начала до конца. Однако функция OnCalculate() индикатора вызывается многократно — на каждом тике и на каждом новом баре — и должна одновременно обеспечивать корректные значения для каждого бара в истории графика.
Рассмотрим такой пример: новый тик поступает, когда на графике отображаются 500 баров. Индикатор должен пересчитать значения для последнего бара и при этом обеспечить, чтобы значения для баров с индексами от 0 до 498 остались неизменными. Если в вашем коде используется одна глобальная переменная (например, bool DivActive), то при появлении дивергенции на баре с индексом 499 она принимает значение true. Но если затем индикатор пересчитает значения для бара с индексом 498 (возможно, из-за обновления графика), в расчете ошибочно будет использовано DivActive = true. В результате механизм сжатия будет применен к бару, на котором дивергенция еще не возникла. Это явление называют перерисовкой.
Как мы установили в статье об MPO4, решение заключается в том, чтобы хранить информацию о состоянии отдельно для каждого бара в буферах и переносить их значения на последующие бары.
Необходимые буферы состояния
Индикатор Adaptive SuperTrend использует шесть буферов состояния, четыре из которых уже знакомы вам по предыдущей статье, а два новых нужны для реализации механизма сжатия:
| Имя буфера | Назначение |
|---|---|
| LastPivLowPriceBuffer[] | Сохраняет цену последнего опорного минимума |
| LastPivLowOscBuffer[] | Сохраняет значение осциллятора на последнем опорном минимуме |
| LastPivHighPriceBuffer[] | Сохраняет цену последнего опорного максимума |
| LastPivHighOscBuffer[] | Сохраняет значение осциллятора на последнем опорном максимуме |
| LastTypeBuffer[] | Сохраняет тип последней дивергенции: 1 — бычья, -1 — медвежья, 0 — отсутствует |
| BarsSinceBuffer[] | Подсчитывает количество баров с момента обнаружения последней дивергенции |
Последние два буфера играют ключевую роль в механизме сжатия. При обнаружении дивергенции элементу LastTypeBuffer[i] присваивается значение 1 или -1, а элементу BarsSinceBuffer[i] — 0. На каждом последующем баре значение в буфере BarsSinceBuffer[] увеличивается на единицу.
Затем по этим значениям определяется, следует ли применять коэффициент сжатия:
//--- Shrink Logic Application int prevTrend = (int)st_trend_calc[i+1]; if(prevTrend == -1 && LastTypeBuffer[i] == 1 && BarsSinceBuffer[i] < 200) finalMultiplier = SuperTrendMultiplier * (1.0 - DivSensitivity); else if(prevTrend == 1 && LastTypeBuffer[i] == -1 && BarsSinceBuffer[i] < 200) finalMultiplier = SuperTrendMultiplier * (1.0 - DivSensitivity);
В этом примере сжатие действует не более 200 баров после возникновения дивергенции или до смены направления тренда. Такое сохранение состояния возможно только благодаря тому, что значения в буферах LastTypeBuffer и BarsSinceBuffer хранятся отдельно для каждого бара.
Механизм переноса — копирование состояния на последующие бары
В основном цикле перед расчетом значений для каждого бара индикатор копирует состояние предыдущего бара (i + 1) в элементы буферов текущего бара (i). Такой перенос гарантирует, что для каждого бара будет сохранено состояние последней дивергенции, даже если на этом баре не обнаружена новая дивергенция.
for(int i = limit; i >= 0; i--) { //--- Inherit State from previous older bar (i+1) BullArrowBuffer[i] = EMPTY_VALUE; BearArrowBuffer[i] = EMPTY_VALUE; LastTypeBuffer[i] = LastTypeBuffer[i+1]; BarsSinceBuffer[i] = BarsSinceBuffer[i+1] + 1; LastPivLowPriceBuffer[i] = LastPivLowPriceBuffer[i+1]; LastPivLowOscBuffer[i] = LastPivLowOscBuffer[i+1]; LastPivHighPriceBuffer[i] = LastPivHighPriceBuffer[i+1]; LastPivHighOscBuffer[i] = LastPivHighOscBuffer[i+1];
Обратите внимание на направление прохода в цикле: мы движемся от прошлых баров (limit) к текущему (0). Это связано с тем, что функция OnCalculate() получает массив цен, в котором индекс 0 соответствует самому последнему бару. При проходе по индексам в обратном порядке индикатор может обращаться к предыдущему по времени бару по индексу i + 1 без проверки границ массива.
Сброс состояния при смене направления тренда
Одна из малозаметных, но важных особенностей индикатора Adaptive SuperTrend — сброс сохраненных данных о дивергенции при смене направления самого тренда. Если рынок меняет направление, сохранение активного состояния прежней дивергенции вводило бы в заблуждение, поскольку сжатие предназначено для защиты позиции, открытой по прежнему тренду. Мы реализуем этот сброс с помощью кода:
//--- Reset divergence state if the trend flips if(EnableAdaptiveShrink && currTrend != (int)st_trend_calc[i+1]) { LastTypeBuffer[i] = 0; BarsSinceBuffer[i] = 9999; }
Когда бычий тренд сменяется медвежьим (или наоборот), состояние дивергенции сбрасывается, а счетчику баров с момента обнаружения дивергенции присваивается большое значение, чтобы отключить сжатие. Это гарантирует, что индикатор будет реагировать только на дивергенции, актуальные для текущего тренда.
Итоги построения индикатора без перерисовки
Стрелки дивергенции отображаются на баре с индексом pIdx = i + DivPivotLen — это та же логика отложенного подтверждения, которую мы использовали в статье об MPO4 (предыдущей статье). К моменту появления стрелки уже прошло достаточно баров для подтверждения опорной точки, поэтому сигнал не исчезнет.
Что еще более важно, сжатие применяется на основе LastTypeBuffer[i] и BarsSinceBuffer[i] — двух фиксированных исторических значений. Когда поступает новый тик и функция OnCalculate() пересчитывает значения для последнего бара, состояние, сохраненное для более старых баров, не изменяется. Можно обновить индикатор, прокрутить график или перезапустить терминал — сигналы дивергенции и связанное с ними сжатие останутся именно там, где они возникли.
На этом завершается создание основы для управления состоянием. В следующем разделе мы рассмотрим реализацию на языке MQL5 и объединим расчет MPO4, обнаружение опорных точек и логику сжатия в одном визуальном индикаторе.
Реализация визуального индикатора на MQL5
Теперь, когда изложена теория Adaptive SuperTrend и его механизма сжатия, мы приступим к созданию полноценного индикатора на языке MQL5. В отличие от осциллятора MPO4, который работает в отдельном окне, этот индикатор отображается непосредственно на ценовом графике. Он отображает цветную линию SuperTrend, расстояние которой от цены изменяется в зависимости от сигналов дивергенции, и отмечает эти сигналы стрелками непосредственно на графике.
Реализация будет состоять из трех основных слоев:
- каркас и настройка буферов — определение графических построений и входных параметров, выделение памяти;
- инициализация — подготовка хэндлов, констант и стилей отображения уровней;
- основной расчетный цикл — объединение расчета MPO4/RSI, обнаружения опорных точек, логики сжатия и расчетов SuperTrend в одном цикле с проходом по индексам в обратном порядке.
Мы подробно рассмотрим каждый слой.
Слой 1 — каркас индикатора и буферы
Первым делом необходимо объявить свойства индикатора. Поскольку мы отображаем индикатор в окне графика, мы будем использовать:
#property indicator_chart_window
Индикатор будет отображать три визуальных элемента:
- линию Adaptive SuperTrend (графическое построение DRAW_COLOR_LINE, цвет которого меняется в зависимости от направления тренда);
- стрелки бычьей дивергенции (направленные вверх);
- стрелки медвежьей дивергенции (направленные вниз).
Всего нам потребуется 17 буферов: 3 для видимых графических построений и 14 для внутренних расчетов. Директивы препроцессора:
//--- We need 17 buffers total #property indicator_buffers 17 #property indicator_plots 3 //--- Define the line style #property indicator_label1 "Adaptive SuperTrend" #property indicator_type1 DRAW_COLOR_LINE #property indicator_color1 clrLimeGreen, clrRed #property indicator_style1 STYLE_SOLID #property indicator_width1 2 //--- Plot 2: Bullish Divergence Arrow #property indicator_label2 "Bull Div" #property indicator_type2 DRAW_ARROW #property indicator_color2 clrDodgerBlue #property indicator_width2 2 //--- Plot 3: Bearish Divergence Arrow #property indicator_label3 "Bear Div" #property indicator_type3 DRAW_ARROW #property indicator_color3 clrMagenta #property indicator_width3 2
В графическом построении DRAW_COLOR_LINE используются два цвета: clrLimeGreen для восходящего тренда (индекс цвета 0) и clrRed для нисходящего тренда (индекс цвета 1). Стрелки отображаются с помощью символов шрифта Wingdings с кодами 233 (вверх) и 234 (вниз), которые мы зададим позже в функции OnInit().
Настройка входных параметров
Сначала мы настроим перечисление для выбора одного из двух осцилляторов — MPO4 или RSI. Таким образом, пользователи могут легко переключаться между этими двумя индикаторами.
//--- Enums enum ENUM_DIV_SOURCE { DIV_SRC_MPO = 0, // MPO4 Oscillator DIV_SRC_RSI = 1 // Standard RSI };
Входные параметры объединены в группы, чтобы разделить настройки осциллятора, настройки SuperTrend и параметры сжатия при дивергенции:
//--- INPUTS input group "=== Oscillator Settings ===" input ENUM_DIV_SOURCE InpSource = DIV_SRC_MPO; // Oscillator Source input int MPO_len = 6; // MPO4 Oscillator Length input int MPO_smooth = 7; // MPO4 Smoothing input int RsiPeriod = 14; // RSI Period input int DivPivotLen = 2; // Pivot length of divergence input group "=== SuperTrend Configuration ===" input int SuperTrendPeriod = 10; // SuperTrend period input double SuperTrendMultiplier = 3.0; // SuperTrend [atr] Multiplier input group "=== Divergence Shrinking ===" input bool EnableAdaptiveShrink = true; // Enable Adaptive Shrinking input double DivSensitivity = 0.3; // Shrink factor [0.0 = off, 1.0 extreme]
Перечисление ENUM_DIV_SOURCE позволяет пользователю выбирать между пользовательским осциллятором MPO4 и стандартным RSI, предоставляя выбор тем, кто предпочитает привычный индикатор моментума.
Глобальные буферы и хэндлы
Мы разделим буферы на три категории:
- буферы отображения — напрямую связаны с тремя графическими построениями;
- расчетные буферы — хранят значения MPO4, значения полос SuperTrend, состояния тренда, значения ATR и данные RSI;
- буферы управления состоянием — шесть буферов, в которых сохраняется информация об опорных точках и история дивергенций для каждого бара (как подробно описано в разделе об управлении состоянием).
//--- Indicator Buffers double ValBuffer[]; // Plot 0: Line Value double ColorBuffer[]; // Plot 0: Line Color Index double BullArrowBuffer[]; // Plot 1: Bullish Arrows double BearArrowBuffer[]; // Plot 2: Bearish Arrows //--- Hidden Calculation Buffers double st_up[], st_dn[], st_trend_calc[]; double buf_MPO_Raw[], buf_MPO_Smooth[]; double buf_RSI[]; double buf_ATR_ST[]; //--- Hidden State Memory Buffers double LastPivLowPriceBuffer[]; double LastPivLowOscBuffer[]; double LastPivHighPriceBuffer[]; double LastPivHighOscBuffer[]; double LastTypeBuffer[]; // 1=Bull, -1=Bear, 0=None double BarsSinceBuffer[]; // Tracks bars since last divergence
Кроме того, мы объявим хэндлы индикаторов ATR и RSI, а также коэффициент сглаживания.
int atrHandle; int rsiHandle; double SmoothAlpha;
Слой 2 — привязка буферов и начальная настройка в функции OnInit()
В функции OnInit() мы привязываем каждый буфер с помощью SetIndexBuffer(). Буферы отображения привязываются с типом INDICATOR_DATA, а все остальные буферы, используемые для внутренних расчетов, — с типом INDICATOR_CALCULATIONS. Такое разделение позволяет при отображении обрабатывать только три буфера графических построений, что повышает производительность.
После привязки мы задаем для всех буферов индексацию как в таймсериях с помощью ArraySetAsSeries(..., true). Таким образом, индекс 0 соответствует самому последнему бару, что упрощает логику функции OnCalculate() при проходе по индексам в обратном порядке.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Map Plot Buffers SetIndexBuffer(0, ValBuffer, INDICATOR_DATA); SetIndexBuffer(1, ColorBuffer, INDICATOR_COLOR_INDEX); SetIndexBuffer(2, BullArrowBuffer, INDICATOR_DATA); SetIndexBuffer(3, BearArrowBuffer, INDICATOR_DATA); //--- Map Hidden Calculation Buffers SetIndexBuffer(4, st_up, INDICATOR_CALCULATIONS); SetIndexBuffer(5, st_dn, INDICATOR_CALCULATIONS); SetIndexBuffer(6, st_trend_calc, INDICATOR_CALCULATIONS); SetIndexBuffer(7, buf_MPO_Raw, INDICATOR_CALCULATIONS); SetIndexBuffer(8, buf_MPO_Smooth, INDICATOR_CALCULATIONS); SetIndexBuffer(9, buf_RSI, INDICATOR_CALCULATIONS); SetIndexBuffer(10, buf_ATR_ST, INDICATOR_CALCULATIONS); //--- Map Hidden State Buffers SetIndexBuffer(11, LastPivLowPriceBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(12, LastPivLowOscBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(13, LastPivHighPriceBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(14, LastPivHighOscBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(15, LastTypeBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer(16, BarsSinceBuffer, INDICATOR_CALCULATIONS); //--- Force all buffers to behave as TimeSeries (Index 0 = Live Bar) ArraySetAsSeries(ValBuffer, true); ArraySetAsSeries(ColorBuffer, true); ArraySetAsSeries(BullArrowBuffer, true); ArraySetAsSeries(BearArrowBuffer, true); ArraySetAsSeries(st_up, true); ArraySetAsSeries(st_dn, true); ArraySetAsSeries(st_trend_calc, true); ArraySetAsSeries(buf_MPO_Raw, true); ArraySetAsSeries(buf_MPO_Smooth, true); ArraySetAsSeries(buf_ATR_ST, true); ArraySetAsSeries(LastPivLowPriceBuffer, true); ArraySetAsSeries(LastPivLowOscBuffer, true); ArraySetAsSeries(LastPivHighPriceBuffer, true); ArraySetAsSeries(LastPivHighOscBuffer, true); ArraySetAsSeries(LastTypeBuffer, true); ArraySetAsSeries(BarsSinceBuffer, true);
Затем мы задаем код стрелки с помощью PlotIndexSetInteger():
//--- Set Arrow Wingdings Codes PlotIndexSetInteger(1, PLOT_ARROW, 233); // Up arrow (Plot index 1 is the second plot) PlotIndexSetInteger(2, PLOT_ARROW, 234); // Down arrow (Plot index 2 is the third plot)
В завершение этого слоя мы создадим хэндлы индикаторов RSI и ATR и заранее рассчитаем коэффициент сглаживания альфа:
//--- Get RSI if selected if(InpSource == DIV_SRC_RSI) { rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, RsiPeriod, PRICE_CLOSE); if(rsiHandle == INVALID_HANDLE) { Print("Failed to create RSI handle"); return(INIT_FAILED); } } //--- Get ATR Handle atrHandle = iATR(_Symbol, PERIOD_CURRENT, SuperTrendPeriod); if(atrHandle == INVALID_HANDLE) { Print("Failed to create ATR handle"); return(INIT_FAILED); } SmoothAlpha = 2.0 / (MPO_smooth + 1.0); IndicatorSetString(INDICATOR_SHORTNAME, "Adaptive SuperTrend Visual"); return(INIT_SUCCEEDED); }
Слой 3 — основной расчетный цикл
Теперь, когда каркас индикатора полностью подготовлен и все буферы правильно привязаны, перейдем к функции OnCalculate(). Именно здесь происходит полный расчет индикатора Adaptive SuperTrend. Функция вызывается при каждом тике и при появлении каждого нового бара. Ее основная задача — определить, для каких баров необходимо пересчитать значения.
Для написания индикаторов крайне важно понимать назначение параметра prev_calculated. При первом вызове функции значение prev_calculated равно нулю, поэтому необходимо обработать каждый бар. При последующих вызовах параметр prev_calculated содержит количество баров, для которых значения уже были рассчитаны во время предыдущего вызова. Это позволяет нам пропускать исторические бары и обрабатывать только новые данные.
Для начала определим минимально необходимое количество баров и границу расчета:
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { if(rates_total < 300) return 0; //--- Setup price series arrays ArraySetAsSeries(open, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); //--- Determine calculation limit (i goes from limit down to 0) int limit = rates_total - prev_calculated; if(prev_calculated > 0) limit++; // Overlap 1 bar to catch crossovers int max_idx = rates_total - MPO_len - DivPivotLen - 3;
Переменная limit определяет начальный индекс расчетного цикла. Повторно обрабатывая один бар при условии prev_calculated > 0, мы надежно выявляем любые пересечения или изменения состояния. Переменная max_idx также предотвращает выход цикла за безопасные пределы исторических данных.
Инициализация значений для самого старого бара
Если мы выполняем пересчет с нуля или значение limit превышает безопасный максимум, переменной limit нужно присвоить значение max_idx и задать начальные значения для самого старого обрабатываемого бара. Сюда входят получение начального значения ATR, задание условного направления тренда и инициализация всех буферов состояния нейтральными значениями:
//--- Initialization Block for the oldest processed bar if(limit > max_idx || prev_calculated == 0) { limit = max_idx; // Safely fetch initial ATR double tempATR[]; ArraySetAsSeries(tempATR, true); if(CopyBuffer(atrHandle, 0, 0, limit + 2, tempATR) > 0) { for(int k = 0; k <= limit + 1; k++) buf_ATR_ST[k] = tempATR[k]; } //--- Seed Initial Variables at the boundary st_trend_calc[limit+1] = (close[limit+1] > open[limit+1]) ? 1 : -1; double initMedian = (high[limit+1] + low[limit+1]) / 2.0; st_up[limit+1] = initMedian + SuperTrendMultiplier * buf_ATR_ST[limit+1]; st_dn[limit+1] = initMedian - SuperTrendMultiplier * buf_ATR_ST[limit+1]; buf_MPO_Smooth[limit+1] = 0; LastTypeBuffer[limit+1] = 0; BarsSinceBuffer[limit+1] = 9999; LastPivLowPriceBuffer[limit+1] = DBL_MAX; LastPivLowOscBuffer[limit+1] = -99999; LastPivHighPriceBuffer[limit+1] = 0; LastPivHighOscBuffer[limit+1] = 99999; }
Обновление данных ATR и RSI
Перед входом в основной цикл необходимо получить актуальные данные ATR и RSI с помощью CopyBuffer(). Эти значения сохраняются в наших расчетных буферах для текущего диапазона расчета. Этот шаг гарантирует, что индикатор всегда будет работать с самыми актуальными показателями волатильности и моментума:
//--- Update ATR data for current calculation scope double liveATR[]; ArraySetAsSeries(liveATR, true); if(CopyBuffer(atrHandle, 0, 0, limit + 2, liveATR) > 0) { for(int k = 0; k <= limit + 1; k++) buf_ATR_ST[k] = liveATR[k]; } //--- Fetch RSI data if(InpSource == DIV_SRC_RSI) { double tempRSI[]; ArraySetAsSeries(tempRSI, true); if(CopyBuffer(rsiHandle, 0, 0, limit + 2, tempRSI) > 0) { for(int k = 0; k <= limit + 1; k++) buf_RSI[k] = tempRSI[k]; } }
Основной цикл — перенос состояния, дивергенция и механизм сжатия
В этом цикле индекс уменьшается от limit до 0. Для каждого бара i сначала переносится состояние предыдущего бара (i+1). Именно этот перенос состояния обеспечивает работу индикатора без перерисовки. Мы переносим на текущий бар тип дивергенции, значение счетчика баров с момента ее обнаружения и значения последних опорных точек:
for(int i = limit; i >= 0; i--) { //--- Inherit State from previous older bar (i+1) BullArrowBuffer[i] = EMPTY_VALUE; BearArrowBuffer[i] = EMPTY_VALUE; LastTypeBuffer[i] = LastTypeBuffer[i+1]; BarsSinceBuffer[i] = BarsSinceBuffer[i+1] + 1; LastPivLowPriceBuffer[i] = LastPivLowPriceBuffer[i+1]; LastPivLowOscBuffer[i] = LastPivLowOscBuffer[i+1]; LastPivHighPriceBuffer[i] = LastPivHighPriceBuffer[i+1]; LastPivHighOscBuffer[i] = LastPivHighOscBuffer[i+1]; double finalMultiplier = SuperTrendMultiplier;
Теперь мы рассчитываем значения осциллятора MPO4 и обнаруживаем дивергенции — но только если включено адаптивное сжатие. Если в качестве источника выбран MPO4, мы рассчитываем исходное значение давления по алгоритму взвешивания тел свечей из первой статьи. Если выбран RSI, мы просто копируем значение RSI из buf_RSI. Затем с помощью EMA рассчитывается сглаженное значение buf_MPO_SMOOTH[i]:
//--- MPO4 & Divergence Logic if(EnableAdaptiveShrink) { if(InpSource == DIV_SRC_MPO) { // MPO4 Math double rollingSum = 0; double sumBodies = 0; for(int k = 0; k < MPO_len; k++) sumBodies += MathAbs(close[i+k] - open[i+k]); double avgBody = (sumBodies > 0) ? sumBodies / MPO_len : Point(); for(int k = 0; k < MPO_len; k++) { int idx = i + k; double body = MathAbs(close[idx] - open[idx]); double dir = (close[idx] > open[idx]) ? 1.0 : (close[idx] < open[idx]) ? -1.0 : 0.0; double weight = (avgBody > 0) ? body / avgBody : 1.0; rollingSum += (dir * weight); } buf_MPO_Raw[i] = (rollingSum / (MPO_len * 2.0)) * 100.0; } else { // RSI Bypass buf_MPO_Raw[i] = buf_RSI[i]; } buf_MPO_Smooth[i] = (buf_MPO_Raw[i] * SmoothAlpha) + (buf_MPO_Smooth[i+1] * (1.0 - SmoothAlpha));
Опорные точки обнаруживаются с отложенным подтверждением — по тому же принципу, который мы использовали в статье об MPO4. Мы анализируем бар с индексом pIdx = i + DivPivotLen и проверяем, что с обеих сторон от него есть достаточно баров для подтверждения истинного локального экстремума. Если подтверждается опорный минимум, мы сравниваем его цену и значение осциллятора с соответствующими значениями на предыдущем опорном минимуме. Когда цена формирует более низкий минимум, а осциллятор — более высокий минимум, фиксируется бычья дивергенция. В коде элементу LastTypeBuffer[i] будет присвоено значение 1 (бычья дивергенция), счетчик баров с момента обнаружения дивергенции будет сброшен до нуля, а направленная вверх стрелка будет отображена под сигнальным баром на уровне low[pIdx] - 10 * Point():
//--- Pivot & Div Detection --- int pIdx = i + DivPivotLen; if(pIdx <= limit) { bool isPivLow = true; bool isPivHigh = true; for(int k = 1; k <= DivPivotLen; k++) { if(buf_MPO_Smooth[pIdx] > buf_MPO_Smooth[pIdx+k] || buf_MPO_Smooth[pIdx] > buf_MPO_Smooth[pIdx-k]) isPivLow = false; if(buf_MPO_Smooth[pIdx] < buf_MPO_Smooth[pIdx+k] || buf_MPO_Smooth[pIdx] < buf_MPO_Smooth[pIdx-k]) isPivHigh = false; } if(isPivLow) { if(low[pIdx] < LastPivLowPriceBuffer[pIdx] && buf_MPO_Smooth[pIdx] > LastPivLowOscBuffer[pIdx]) { LastTypeBuffer[i] = 1; // Bull Div Detected BarsSinceBuffer[i] = 0; // Reset active counter BullArrowBuffer[pIdx] = low[pIdx] - (10 * Point()); } LastPivLowPriceBuffer[i] = low[pIdx]; LastPivLowOscBuffer[i] = buf_MPO_Smooth[pIdx]; } if(isPivHigh) { if(high[pIdx] > LastPivHighPriceBuffer[pIdx] && buf_MPO_Smooth[pIdx] < LastPivHighOscBuffer[pIdx]) { LastTypeBuffer[i] = -1; // Bear Div Detected BarsSinceBuffer[i] = 0; // Reset active counter BearArrowBuffer[pIdx] = high[pIdx] - (10 * Point()); } LastPivHighPriceBuffer[i] = high[pIdx]; LastPivHighOscBuffer[i] = buf_MPO_Smooth[pIdx]; } }
Затем применяется логика сжатия. Сначала мы проверяем, находится ли текущий бар в пределах периода действия дивергенции и соответствует ли направление тренда типу дивергенции. Чтобы при бычьей дивергенции линия SuperTrend приближалась к цене на нисходящем тренде, а при медвежьей — на восходящем, базовый множитель SuperTrend умножается на (1.0 - DivSensitivity):
//--- Shrink Logic Application int prevTrend = (int)st_trend_calc[i+1]; if(prevTrend == -1 && LastTypeBuffer[i] == 1 && BarsSinceBuffer[i] < 200) finalMultiplier = SuperTrendMultiplier * (1.0 - DivSensitivity); else if(prevTrend == 1 && LastTypeBuffer[i] == -1 && BarsSinceBuffer[i] < 200) finalMultiplier = SuperTrendMultiplier * (1.0 - DivSensitivity); }
Расчет индикатора SuperTrend
После определения динамического множителя мы рассчитываем значения полос SuperTrend. В классическом алгоритме SuperTrend верхняя и нижняя полосы рассчитываются на основе медианной цены и ATR, умноженного на заданный множитель. Значение полосы с предыдущего бара переносится на текущий, когда этого требуют стандартные правила SuperTrend (чтобы избежать обратного смещения полосы):
//--- SuperTrend Calculation double median = (high[i] + low[i]) / 2.0; double basicUp = median + finalMultiplier * buf_ATR_ST[i]; double basicDn = median - finalMultiplier * buf_ATR_ST[i]; double prevUp = st_up[i+1]; double prevDn = st_dn[i+1]; double prevClose = close[i+1]; if(prevClose < prevUp && basicUp > prevUp) st_up[i] = prevUp; else st_up[i] = basicUp; if(prevClose > prevDn && basicDn < prevDn) st_dn[i] = prevDn; else st_dn[i] = basicDn; int currTrend = (int)st_trend_calc[i+1]; if(currTrend == -1 && close[i] > st_up[i]) currTrend = 1; else if(currTrend == 1 && close[i] < st_dn[i]) currTrend = -1; st_trend_calc[i] = currTrend;
Сброс состояния при смене направления тренда
Неочевидная, но важная особенность индикатора Adaptive SuperTrend — необходимость сбрасывать сохраненные данные о дивергенции при смене направления самого тренда. Если рынок меняет направление, сохранение активного состояния прежней дивергенции вводило бы в заблуждение, поскольку сжатие предназначено для защиты позиции, открытой по прежнему тренду. Мы выполняем этот сброс следующим образом:
//--- Reset divergence state if the trend flips if(EnableAdaptiveShrink && currTrend != (int)st_trend_calc[i+1]) { LastTypeBuffer[i] = 0; BarsSinceBuffer[i] = 9999; }
Задание значений для графического построения
Наконец, мы задаем значение линии и индекс цвета. Если тренд бычий (currTrend == 1), мы отображаем нижнюю полосу (st_dn[i]) с индексом цвета 0 (clrLimeGreen). Если тренд медвежий, мы отображаем верхнюю полосу (st_up[i]) с индексом цвета 1 (clrRed):
//--- Assign Final Values to Visual Plot if(currTrend == 1) { ValBuffer[i] = st_dn[i]; ColorBuffer[i] = 0; // LimeGreen } else { ValBuffer[i] = st_up[i]; ColorBuffer[i] = 1; // Red } } return rates_total;
Теперь индикатор выполняет полный расчет значений динамической линии SuperTrend, обнаруживает дивергенции, при необходимости применяет сжатие и сбрасывает состояние при смене направления тренда. Результат отображается поверх ценового графика без перерисовки и в реальном времени адаптируется к изменениям моментума.
Тактическое применение и интерпретация
После компиляции индикатора необходимо определить, как использовать его в реальной торговле. В отличие от осциллятора MPO4 из нашей предыдущей статьи, который в первую очередь служил индикатором моментума, этот индикатор объединяет следование за трендом и обнаружение дивергенций в единую визуальную систему. Чтобы извлечь из этого инструмента реальную пользу, необходимо понимать, как интерпретировать его сигналы и использовать их в торговом плане.
Индикатор предоставляет два взаимодополняющих слоя информации:
- Линия Adaptive SuperTrend — это цветная линия, которая следует за ценой и меняет цвет в зависимости от направления тренда (например, зеленый — для восходящего тренда, красный — для нисходящего). Расстояние от этой линии до цены динамически изменяется в зависимости от дивергенций, обнаруженных по индикаторам MPO4 или RSI.
- Стрелки дивергенций направлены вверх при бычьей дивергенции и вниз при медвежьей; их цвета фиксированы (синий — для бычьей, пурпурный — для медвежьей). Они отображаются непосредственно на ценовом графике рядом с соответствующими опорными точками.
Интерпретация линии Adaptive SuperTrend
Линия SuperTrend служит и динамическим уровнем поддержки или сопротивления, и ориентиром для трейлинг-стопа. При устойчивом восходящем тренде зеленая линия проходит ниже цены, обозначая уровень, который цена в идеале не должна пробивать. При нисходящем тренде красная линия проходит выше цены и служит уровнем сопротивления. Когда цена пересекает линию, направление тренда меняется, а линия меняет цвет.
Именно механизм сжатия делает эту реализацию адаптивной и отличает ее от обычного индикатора SuperTrend. При обнаружении дивергенции линия подтягивается ближе к цене. Это сжатие хорошо видно на графике и показывает, что индикатор активно управляет риском в ожидании возможного разворота.
Ниже приведен скриншот линии Adaptive SuperTrend в обычном состоянии и после сжатия с коэффициентом 0,5.

А для медвежьего сценария:

Интерпретация сигналов дивергенции с учетом контекста
Стрелки дивергенции отображаются на основе логики отложенного подтверждения. К моменту появления стрелки опорная точка уже подтверждена, а сигнал зафиксирован — он не будет перерисовываться. Благодаря этой надежности сигналы, обозначенные стрелками, подходят для тестирования на исторических данных и системной торговли по правилам.
Однако не все дивергенции одинаково значимы. Следующие рекомендации помогут отделить сигналы с высокой вероятностью отработки от шума:
Бычья дивергенция (стрелка, направленная вверх):
- Цена формирует более низкий минимум, а осциллятор — более высокий минимум.
- Наиболее эффективна, когда возникает вблизи значимого уровня поддержки или после продолжительного нисходящего тренда.
- Ее можно использовать как потенциальный сигнал для входа в длинную позицию или как предупреждение о необходимости подтянуть стоп-лоссы по открытым коротким позициям.
Медвежья дивергенция (стрелка, направленная вниз):
- Цена формирует более высокий максимум, а осциллятор — более низкий максимум.
- Наиболее эффективна вблизи уровней сопротивления или после длительного восходящего тренда.
- Ее можно использовать как потенциальный сигнал для входа в короткую позицию или для подтягивания стоп-лоссов по открытым длинным позициям.
Использование механизма сжатия для управления сделками
Механизм сжатия — отличительная особенность этого индикатора. Его можно применять для решения двух разных тактических задач.
Защита прибыли (защитное применение):
Если у вас открыта позиция и появляется дивергенция противоположного направления, механизм сжатия подтягивает линию SuperTrend ближе к цене. При этом ваш трейлинг-стоп подтягивается без ручной корректировки. Например, если у вас открыта длинная позиция и появляется медвежья дивергенция, зеленая линия поднимается, сокращая расстояние, которое должна пройти цена для срабатывания стоп-лосса. Это помогает зафиксировать прибыль до возможного разворота.
Вход по новым трендам (наступательное применение):
Поскольку механизм сжатия подтягивает линию ближе к цене, при развороте тренда для пересечения линии достаточно меньшего движения цены. Это означает, что индикатор переключится на новый тренд раньше, чем стандартный индикатор SuperTrend. Если вы используете пересечение ценой линии SuperTrend как сигнал входа, адаптивная версия позволяет открыть позицию по новому тренду ближе к опорной точке, улучшая соотношение риска и прибыли.
Совместное использование с другими фильтрами
Хотя Adaptive SuperTrend является эффективным самостоятельным инструментом, он показывает наилучшие результаты в сочетании с другими методами анализа. Вот несколько дополнительных фильтров, которые могут улучшить качество сигналов:
Подтверждение тренда: используйте скользящую среднюю или линию тренда на старшем таймфрейме, чтобы убедиться, что вы торгуете в направлении основного тренда. Сигналы дивергенции, направление которых совпадает с направлением тренда на старшем таймфрейме, надежнее.
Поддержка и сопротивление: стрелка дивергенции вблизи ключевого уровня поддержки или сопротивления имеет больший вес, чем стрелка в середине диапазона. Реакции цены на этих уровнях часто приводят к значительным разворотам.
Анализ на нескольких таймфреймах: проверьте наличие дивергенции на старшем таймфрейме для подтверждения сигнала. Например, бычья дивергенция на часовом графике, совпадающая с бычьей дивергенцией на четырехчасовом графике, является веским признаком приближающегося разворота.
Настройка параметров и практические аспекты
Значения параметров по умолчанию (MPO4 Length = 6, Smoothing = 7, Pivot Length = 2, SuperTrend Period = 10, Multiplier = 3,0, Div Sensitivity = 0,3) можно скорректировать по результатам тестирования на исторических данных и оптимизации этих параметров. Для разных рынков и таймфреймов могут быть полезны следующие корректировки:
- Скальперы могут оптимизировать эту стратегию или протестировать ее на исторических данных на младших таймфреймах (например, пятиминутном или пятнадцатиминутном).
- Внутридневные трейдеры и свинг-трейдеры также могут оптимизировать эту стратегию под старшие таймфреймы (например, часовой или четырехчасовой).
- Эта стратегия эффективна на многих рынках, включая Форекс и рынки индексов, криптовалют, сырьевых товаров и энергоносителей.
- Для более умеренного сжатия пользователи могут уменьшить значение входного параметра DivSensitivity — коэффициента сжатия — до любого подходящего значения (например, с 0,3 до 0,15). Это ослабит эффект сжатия и сделает движение линии более плавным.
- Для более агрессивного сжатия пользователи также могут увеличить значение входного параметра DivSensitivity (например, с 0,3 до 0,5), чтобы при дивергенции линия значительно приблизилась к цене, стоп-лоссы располагались ближе к цене, а входы происходили раньше.
Порог в 200 баров для значений в буфере BarsSinceBuffer был выбран как разумная верхняя граница периода действия сжатия. На практике большинство разворотов тренда или сигналов в противоположном направлении возникает в течение 20-50 баров после появления дивергенции, поэтому этот предел достигается редко. Если вы заметите, что сжатие действует слишком долго, можно изменить этот порог в участке кода, где применяется логика сжатия. Замените 200 на любое значение по вашему усмотрению:
//--- Shrink Logic Application int prevTrend = (int)st_trend_calc[i+1]; if(prevTrend == -1 && LastTypeBuffer[i] == 1 && BarsSinceBuffer[i] < 200) finalMultiplier = SuperTrendMultiplier * (1.0 - DivSensitivity); else if(prevTrend == 1 && LastTypeBuffer[i] == -1 && BarsSinceBuffer[i] < 200) finalMultiplier = SuperTrendMultiplier * (1.0 - DivSensitivity); }
Итоговые замечания о практическом применении
Adaptive SuperTrend не предсказывает разворот. Вместо этого он задает четкие формальные правила для сопровождения позиций по тренду и защиты прибыли. Благодаря механизму сжатия индикатор быстрее реагирует на изменения, чем стандартный индикатор SuperTrend, что делает его особенно полезным на волатильных рынках или при смене рыночных условий.
Для системных трейдеров индикатор предоставляет четкие сигналы без перерисовки, которые можно проверять на исторических данных и использовать в автоматизированной торговле. Для дискреционных трейдеров он служит визуальным ориентиром, который показывает изменения моментума и задает объективные уровни для размещения стоп-лоссов.
Наиболее эффективный подход — рассматривать этот индикатор как один из компонентов более широкой торговой системы. Используйте стрелки как предупреждения, линию — как ориентир для трейлинг-стопа и всегда учитывайте более широкий рыночный контекст. По мере накопления опыта Adaptive SuperTrend станет интуитивно понятной частью вашего торгового процесса, помогая торговать в направлении рынка и одновременно защищать капитал.
Заключение
Мы разработали полноценный пользовательский индикатор, объединяющий дивергенцию моментума с адаптивным следованием за трендом. Взяв за основу осциллятор давления MPO4, представленный в первой статье, мы расширили возможности его модуля обнаружения дивергенций и создали динамический индикатор SuperTrend, который активно реагирует на ослабление моментума. В результате получился инструмент, который выявляет потенциальные развороты и корректирует логику трейлинг-стопа, чтобы защищать прибыль и раньше открывать позиции по новым трендам.
Ключевое новшество — механизм сжатия. Напрямую связав множитель ATR в SuperTrend с сигналами дивергенции, мы превратили индикатор с традиционно фиксированными настройками в адаптивную систему. Когда рынок устойчив, линия SuperTrend остается на стандартном расстоянии от цены, оставляя пространство для колебаний цены в рамках тренда. Когда моментум ослабевает, линия приближается к цене, ограничивая риск по открытым позициям и позволяя раньше сигнализировать о смене направления тренда. Мы решили технические задачи реализации на языке MQL5 — организовали управление состоянием, отображение стрелок без перерисовки и проход по индексам в обратном порядке в циклах. Это обеспечило надежную работу индикатора на графиках в реальном времени.
Как и любой технический инструмент, Adaptive SuperTrend наиболее эффективен при использовании в соответствии с разумными принципами торговли. Стрелки можно использовать как условия для срабатывания оповещений, а линию — как ориентир для трейлинг-стопа. При этом всегда учитывайте более широкий рыночный контекст. Подберите параметры с учетом своего таймфрейма и допустимого уровня риска, а перед применением на реальных счетах проведите тщательное тестирование на исторических данных.
Этот индикатор связывает опережающие сигналы моментума с запаздывающими трендовыми сигналами, позволяя трейдерам быстрее реагировать на изменения при сопровождении позиций по тренду. Независимо от того, защищаете ли вы прибыль во время разворота или раньше открываете позицию в начале нового движения, Adaptive SuperTrend задает формальные правила, помогающие торговать в направлении рынка.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/23161
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Альтернатива временным барам (Часть 4): Эффективность разных типов баров в трендовых стратегиях и стратегиях возврата к среднему
Трейдинг с экономическим календарем MQL5 (Часть 11): Модульная новостная панель на Canvas
Трейдинг с экономическим календарем MQL5 (часть 12): Хранение данных в SQLite и предотвращение повторных сделок
Нейросети в трейдинге: Диффузионная генерация торговых планов (Сценарный расчёт)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования