preview
Самообучающийся SuperTrend: адаптивный индикатор тренда на машинном обучении

Самообучающийся SuperTrend: адаптивный индикатор тренда на машинном обучении

MetaTrader 5Торговые системы |
53 0
Galym Yechshanov
Galym Yechshanov

Содержание


Введение

SuperTrend — один из самых распространённых трендовых индикаторов на платформе MetaTrader 5. Его логика привлекательна простотой: цена выше верхней полосы — тренд бычий, цена ниже нижней — медвежий. Проблема в том, что классический SuperTrend работает с фиксированными параметрами ATR и множителем. На разных рынках, в разные сезоны, при разной волатильности один и тот же набор параметров то генерирует точные сигналы, то даёт серию ложных срабатываний.

Опытный трейдер знает это и периодически переподбирает параметры вручную. Начинающий разработчик либо оптимизирует параметры на истории и рискует переобучением, либо оставляет значения по умолчанию и получает субоптимальный результат. Оба подхода структурно неверны: рынок меняется, а параметры — нет.

В статье показано, как построить индикатор, который решает эту проблему. Четыре компонента превращают классический SuperTrend в адаптивную ML-систему.

  1. Адаптивный оптимизатор параметров: фоновый тест-матрикс непрерывно проверяет качество текущих параметров на реальных барах и автоматически корректирует множитель, период ATR, стоп и тейк-профит — без перезапуска индикатора.
  2. Режимная сетка (Regime Grid): двумерная карта рынка разделяет историю на ячейки по двум осям — режим рынка (тренд/боковик) и волатильность. В каждой ячейке хранится накопленный опыт: какие параметры работали в аналогичных условиях. При попадании текущего бара в ячейку индикатор берёт оттуда готовую рекомендацию.
  3. Слой точности v2.00: пять дополнительных фильтров — EMA-кросс, ADX-порог, детектор сжатия Keltner, подтверждение телом свечи и взвешенный confidence-score — отсекают слабые сигналы до их появления на графике.
  4. Parabolic-стиль отрисовки: вместо традиционных линий SuperTrend индикатор рисует точки выше и ниже свечей — как в Parabolic SAR. Это визуально чище, не засоряет график и мгновенно читается при торговле.

Весь код написан на чистом MQL5 без внешних библиотек. Один файл, 2035 строк, полностью откомментированных в стиле MetaQuotes. Архитектура спроектирована так, чтобы её было легко расширять: оптимизатор изолирован от логики сигналов, фильтры независимы друг от друга.


Почему классический SuperTrend теряет точность

Стандартный SuperTrend вычисляет полосы по формуле: средняя цена ± ATR(period) × множитель. Направление тренда определяется по тому, с какой стороны от полосы закрывается свеча. Всё это работает корректно — при одном условии: параметры ATR и множитель должны соответствовать текущему режиму рынка.

Проблема возникает при смене рыночного контекста. Если рынок переходит из спокойного боковика в трендовое движение, оптимальный множитель снижается — индикатор должен реагировать быстрее. Если после тренда начинается период высокой волатильности без направления, множитель нужно увеличивать, чтобы избежать серии ложных разворотов. Фиксированные параметры не успевают за этой динамикой.

Математика ошибки выглядит так. Допустим, оптимальный множитель для текущей волатильности — 1.8, а в индикаторе стоит 1.4. Полосы рассчитаются слишком узкими, и при нормальных рыночных колебаниях цена будет пробивать их, генерируя ложные сигналы смены тренда. Обратная ситуация: множитель 2.5 при тихом рынке — полосы слишком широкие, индикатор опаздывает на несколько баров после реального разворота. Оба варианта приводят к убыткам, и оба устраняются одним способом — адаптацией параметров к текущим условиям.

Три системных изменения устраняют это ограничение.

Первое — фоновый тест-матрикс. На каждом баре индикатор открывает и закрывает виртуальные зонды и оценивает качество предсказания на горизонте 5 баров. Если качество падает — параметры корректируются в сторону улучшения. Если качество высокое — параметры остаются без изменений (deadband-контроль).

Второе — режимная сетка. Ячейка сетки кодирует пару (режим рынка, волатильность). Оптимальные параметры в ячейке с высоким трендом отличаются от параметров в ячейке с боковиком. Накапливая опыт по ячейкам, индикатор строит карту рынка: "в таких условиях лучше всего работали вот эти параметры".

Третье — слой точности. Даже при правильных параметрах сигналы бывают слабыми: свеча с маленьким телом, ADX ниже 20, EMA-кросс против направления тренда. Пять независимых фильтров оценивают каждый сигнал, присваивают взвешенный score и пропускают только те сигналы, которые набирают выше заданного порога.


Фоновый тест-матрикс и адаптивный оптимизатор

Фоновый тест-матрикс — ключевой механизм обучения индикатора. Принцип работы следующий: на каждом баре открываются виртуальные зонды в обоих направлениях. Зонд фиксирует цену открытия и значение ATR в момент создания. Через 5 баров зонд закрывается: сравнивается цена закрытия с ценой входа, вычисляется P&L в долларах и в единицах ATR, записывается максимальное неблагоприятное отклонение (MAE).

Результаты зондов накапливаются в двух скользящих очередях — для длинных и коротких позиций. По этим очередям вычисляются коэффициенты качества: win rate, средний ATR-return, Sharpe ratio, Sortino ratio, profit factor. Функция CalcProposals() анализирует эти метрики и формирует предложения по изменению каждого параметра.

//+------------------------------------------------------------------+
//| CalcProposals — gradient-style parameter update proposals        |
//| Параметры:                                                       |
//|   wr       - win rate по скользящей очереди                      |
//|   sortino  - Sortino ratio по ATR-нормализованным доходностям    |
//|   pf       - profit factor (gross profit / gross loss)           |
//|   consecL  - текущая серия убыточных зондов                      |
//| Возвращает через ссылки:                                         |
//|   dMult, dLen, dStop, dTP, dBrk — дельты параметров              |
//+------------------------------------------------------------------+
void CalcProposals(double wr, double avgUSD, double avgATR,
                   double sortino, double pf, double avgWin, double avgLoss,
                   int sampleN, double curMult, double curLen, double sharpe, int consecL,
                   double &dMult, double &dLen, double &dStop, double &dTP, double &dBrk)
  {
   dMult = 0; dLen = 0; dStop = 0; dTP = 0; dBrk = 0;
   if(!Inp_Learn || !Inp_Adaptive || sampleN <= 0)
      return;

//--- confidence shrinks step size with small sample counts
   double conf = 1.0 / MathSqrt(MathMax(1.0, (double)sampleN));
   double step = Inp_LearnRate * conf;

//--- boost step when pressure signal is strong
   double pb = ArraySize(gPressureLog) >= 5 ? QAvg(gPressureLog, 5) : 0;
   if(MathAbs(pb) > 0.2)
      step *= (1 + MathAbs(pb) * 0.5);

//--- win-rate driven multiplier and length proposals
   bool weak   = (wr < Inp_WinFloor) && (Inp_NormByATR ? avgATR < 0 : avgUSD < 0);
   bool strong = (wr > Inp_WinCeil)  && (Inp_NormByATR ? avgATR > 0 : avgUSD > 0);
   if(weak)   { dMult += +0.08 * step; dLen += +curLen * 0.05 * step; }
   if(strong) { dMult += -0.05 * step; dLen += -curLen * 0.03 * step; }

//--- Sortino-driven multiplier correction
   if(sortino < 0)   dMult += +0.04 * step;
   if(sortino > 1.2) dMult += -0.03 * step;

//--- loss-streak emergency band widening
   if(consecL >= 3) dStop += +0.03 * step;
  }

Логика предложений читается прямо из кода. При слабом win rate (ниже Inp_WinFloor) и отрицательном среднем return — множитель расширяется, период ATR увеличивается: индикатор становится менее чувствительным, чтобы отфильтровать шум. При сильном win rate — наоборот, параметры ужимаются для более быстрой реакции. Sortino ниже нуля означает, что убыточные зонды тяжелее прибыльных — аварийное расширение полос. Серия из трёх убытков подряд — экстренное расширение стопа.

Предложения по long и short объединяются, а вклад каждого направления задаётся размером выборки. Затем результат смешивается с рекомендацией режимной сетки через взвешенное среднее. Итоговое значение параметра обновляется с EWM-сглаживанием только в том случае, если изменение превышает deadband — это предотвращает микроколебания параметров от шумовых зондов.


Режимная сетка — детектор рыночного контекста

Режимная сетка — это двумерный массив ячеек, где по оси X откладывается режим рынка (от 0 до 1: 0 — чистый боковик, 1 — сильный тренд), по оси Y — нормализованная волатильность (от 0 до 1: 0 — тихий рынок, 1 — максимальная волатильность). По умолчанию сетка 8×8 = 64 ячейки.

Режим рынка вычисляется функцией DetectRegime() как взвешенная сумма трёх компонентов: показателя Хёрста, энтропии баров и нормализованного ADX.

//+------------------------------------------------------------------+
//| DetectRegime — composite regime score: 0 = range, 1 = trend      |
//| Параметры:                                                       |
//|   close[]  - массив цен закрытия (series-индексация)             |
//|   idx      - текущий индекс бара                                 |
//|   total    - полное количество баров в массиве                   |
//|   adxVal   - значение ADX на текущем баре                        |
//| Возвращает:                                                      |
//|   Скоровое значение [0.0 .. 1.0]                                 |
//|   0.0 — чистый боковик, 1.0 — сильный устойчивый тренд           |
//+------------------------------------------------------------------+
double DetectRegime(const double &close[], int idx, int total, double adxVal)
  {
   double H    = CalcHurst(close, idx, total, 64);   // показатель Хёрста [0..1]
   double Ent  = CalcEntropy(close, idx, total, 64); // энтропия движений [0..1]
   double tStr = Clamp(adxVal / 50.0, 0, 1);         // нормализованный ADX
   return(Clamp(0.35 * (1 - Ent) + 0.35 * Clamp((H - 0.5) * 2, 0, 1) + 0.30 * tStr, 0.0, 1.0));
  }

Показатель Хёрста вычисляется методом R/S-анализа по 64-барному окну. Значение выше 0.5 указывает на персистентное движение (тренд), ниже 0.5 — на антиперсистентное (разворотное, боковик). Энтропия измеряет, насколько равномерно чередуются растущие и падающие бары: при чистом тренде почти все бары идут в одном направлении, энтропия низкая; при боковике чередование хаотично, энтропия высокая. ADX — прямой инструментальный показатель из MetaTrader.

Каждая ячейка сетки хранит накопленные EWM-средние результатов зондов и рекомендованные дельты параметров. При обновлении ячейки используется экспоненциальный decay с периодом полураспада Inp_GridHalfLife — более старые результаты постепенно обесцениваются, свежие получают больший вес.

//+------------------------------------------------------------------+
//| GridUpdate — EWM update of one regime grid cell                  |
//| Параметры:                                                       |
//|   ix, iy   - координаты ячейки в сетке                           |
//|   atr_ret  - ATR-нормализованная доходность зонда                |
//|   dM..dB   - дельты параметров от оптимизатора                   |
//+------------------------------------------------------------------+
void GridUpdate(int ix, int iy, double atr_ret,
                double dM, double dL, double dS, double dT, double dB)
  {
   int idx = GridIdx(ix, iy);
   if(idx < 0 || idx >= ArraySize(gGrid))
      return;

//--- exponential decay weight derived from half-life
   double alpha = 1.0 - MathExp(-MathLog(2.0) / MathMax(1, Inp_GridHalfLife));
   SGridCell c  = gGrid[idx];

//--- update return EWM and downside EWM
   c.ewm_ret  = c.ewm_ret  * (1 - alpha) + atr_ret * alpha;
   double dn2 = atr_ret < 0 ? atr_ret * atr_ret : 0.0;
   c.down_ewm = c.down_ewm * (1 - alpha) + dn2     * alpha;
   c.count++;
   c.conf   = 1.0 - MathExp(-(double)c.count / MathMax(1, Inp_GridConfScale));

//--- update adaptive parameter deltas
   c.d_mult = c.d_mult * (1 - alpha) + dM * alpha;
   c.d_len  = c.d_len  * (1 - alpha) + dL * alpha;
   gGrid[idx] = c;
  }

При запросе рекомендации функция GridRecommend() не берёт данные из одной ячейки — она усредняет соседей 3×3 вокруг текущей ячейки с Гауссовыми весами. Это сглаживает переходы между ячейками и делает рекомендацию более стабильной при движении рынка на границе двух режимов. Вес рекомендации сетки в итоговом параметре ограничен сверху Inp_GridMaxWeight (по умолчанию 0.65) — оставшиеся 35% всегда контролирует живой оптимизатор.


Слой точности v2.00 — пять фильтров сигналов

Даже при идеально настроенных параметрах SuperTrend генерирует слабые сигналы: в момент сжатия волатильности, при слабом ADX, при свечах с маленьким телом. Слой точности — это пять независимых фильтров, каждый из которых блокирует отдельную категорию плохих сигналов. Все фильтры включаются и отключаются независимо через входные параметры группы 5b.

Фильтр 1 — EMA-кросс. Функция CheckEMAFilter() проверяет, что быстрая EMA (по умолчанию EMA8) находится выше медленной EMA (EMA21) для бычьих сигналов, и ниже — для медвежьих. Дополнительное условие: спред между EMA должен расти по сравнению с предыдущим баром. Это отсекает сигналы, когда EMA-кросс уже произошёл, но momentum угасает.

//+------------------------------------------------------------------+
//| CheckEMAFilter — returns true when EMA alignment confirms dir    |
//| Параметры:                                                       |
//|   dir - направление сигнала (+1 = бычий, -1 = медвежий)          |
//| Возвращает:                                                      |
//|   true  - EMA-кросс подтверждает направление и расширяется       |
//|   false - EMA противоречит направлению или схлопывается          |
//+------------------------------------------------------------------+
bool CheckEMAFilter(int dir)
  {
   int sz = ArraySize(gEMAFastQ);
   if(sz < 2)
      return(true); // not enough data: pass through

   double fast0 = gEMAFastQ[sz - 1]; // current bar fast EMA
   double slow0 = gEMASlowQ[sz - 1]; // current bar slow EMA
   double fast1 = gEMAFastQ[sz - 2]; // previous bar fast EMA
   double slow1 = gEMASlowQ[sz - 2]; // previous bar slow EMA

//--- bullish: fast above slow and widening
   if(dir == 1)
      return(fast0 > slow0 && fast0 - slow0 >= fast1 - slow1);

//--- bearish: fast below slow and widening
   return(fast0 < slow0 && slow0 - fast0 >= slow1 - fast1);
  }

Фильтр 2 — ADX-порог. Простейший, но часто самый эффективный: сигнал пропускается только если ADX выше Inp_ADXMin (по умолчанию 20.0). Ниже 20 рынок без направления — SuperTrend в таком рынке генерирует пилу, и не стоит исполнять ни один сигнал.

Фильтр 3 — детектор сжатия Keltner. Функция CheckKeltnerSqueeze() сравнивает ширину Bollinger Bands с шириной канала Keltner. Если BB уже KC — рынок в состоянии сжатия (squeeze). Это период накопления перед взрывным движением, но само движение ещё не началось. Сигнал блокируется: в squeeze SuperTrend даёт ложные смены тренда почти на каждом баре.

//+------------------------------------------------------------------+
//| CheckKeltnerSqueeze — returns false when market is in squeeze    |
//| Логика:                                                          |
//|   Squeeze: Bollinger Bands уже Keltner Channel                   |
//|   Это сигнал накопления, но не начала движения — блокировать     |
//| Возвращает:                                                      |
//|   false - рынок в squeeze, сигнал заблокирован                   |
//|   true  - волатильность нормальная, сигнал разрешён              |
//+------------------------------------------------------------------+
bool CheckKeltnerSqueeze(int idx, const double &high[], const double &low[],
                          const double &close[], int total, double cATR)
  {
   int len = Inp_KeltnerLen;
   if(idx + len >= total)
      return(true);

//--- compute EMA and Keltner channel half-width
   double emaVal = 0;
   for(int i = 0; i < len; i++)
      emaVal += close[idx + i];
   emaVal /= len;
   double kc = Inp_KeltnerMult * cATR;

//--- Bollinger Band standard deviation
   double variance = 0;
   for(int i = 0; i < len; i++)
     {
      double d = close[idx + i] - emaVal;
      variance += d * d;
     }
   double bbHalf = 2.0 * MathSqrt(variance / len);

//--- squeeze: BB narrower than KC means avoid signal
   if(bbHalf < kc)
      return(false); // in squeeze — block signal

   return(true);
  }

Фильтр 4 — подтверждение телом свечи. Функция CheckCandleBody() требует, чтобы тело свечи составляло не менее Inp_BodyRatio от полного диапазона (High–Low) и свеча закрылась в правильном направлении. Доджи, пин-бары, свечи с длинными хвостами и маленьким телом блокируются: они не дают уверенности в направлении движения.

Фильтр 5 — взвешенный confidence score. Функция CalcConfidence() суммирует вклады всех компонентов с заданными весами и возвращает итоговый score от 0 до 1. Сигнал проходит только если score превышает Inp_ConfThreshold (по умолчанию 0.30).

//+------------------------------------------------------------------+
//| CalcConfidence — weighted multi-factor confidence score [0..1]   |
//| Weight structure:                                                |
//|   0.25 — regime grid confidence                                  |
//|   0.15 — RSI proximity to extreme zone                           |
//|   0.20 — ADX excess above threshold                              |
//|   0.15 — EMA filter confirmation                                 |
//|   0.10 — absence of Keltner squeeze                              |
//|   0.10 — volume surge                                            |
//|   0.05 — market regime alignment                                 |
//+------------------------------------------------------------------+
double CalcConfidence(bool doBuy, bool doSell,
                      double gridConf, double cRSI, double adxVal,
                      bool volSurge, bool emaOK, bool keltOK,
                      double regime, int sigTrend)
  {
//--- base score from regime grid
   double score = gridConf * 0.25;

//--- RSI distance from extreme zone adds up to 0.15
   if(doBuy)
      score += Clamp((Inp_RSI_Bot + 20 - cRSI) / 20.0, 0.0, 1.0) * 0.15;
   if(doSell)
      score += Clamp((cRSI - (Inp_RSI_Top - 20)) / 20.0, 0.0, 1.0) * 0.15;

//--- ADX strength contribution
   score += Clamp((adxVal - Inp_ADXMin) / 30.0, 0.0, 1.0) * 0.20;

//--- EMA, Keltner, volume and regime alignment
   if(Inp_EMAFilter && emaOK)     score += 0.15;
   if(Inp_KeltnerFilter && keltOK) score += 0.10;
   if(volSurge)                    score += 0.10;
   if((doBuy  && sigTrend ==  1 && regime > 0.5) ||
      (doSell && sigTrend == -1 && regime > 0.5)) score += 0.05;

   return(Clamp(score, 0.0, 1.0));
  }

Ключевое свойство confidence score — он не является жёстким голосованием. Даже если один фильтр не проходит (например, EMA-кросс не подтверждён), сигнал может пройти за счёт высокого ADX, сильного grid confidence и объёмного всплеска. Это делает фильтрацию более гибкой, чем классическое "все фильтры должны быть зелёными".


L1-фильтрация — разреживающая регуляризация признакового пространства

Пять фильтров слоя точности работают с фиксированным набором признаков: EMA, ADX, Keltner, тело свечи, объём. Проблема возникает при расширении системы: каждый новый признак увеличивает размерность входного пространства и риск переобучения. Добавить RSI скользящего среднего, спред bid/ask, сессионный объём, показатель волатильности VIX — и confidence score начнёт реагировать на шум, а не на сигнал.

L1-фильтрация решает эту проблему на уровне математики. Идея заимствована из регрессии LASSO (Least Absolute Shrinkage and Selection Operator): к функции потерь оптимизатора добавляется штраф, пропорциональный сумме абсолютных значений весовых коэффициентов признаков. В отличие от L2-регуляризации (Ridge), которая равномерно уменьшает все веса, L1 принудительно обнуляет коэффициенты слабых признаков — те, чья предиктивная сила не оправдывает "стоимость" включения в модель.

В ML SuperTrend Pro L1-фильтрация работает следующим образом. Каждый признак x i входного вектора confidence score имеет адаптивный вес w i , который оптимизатор обновляет через фоновый тест-матрикс. Штрафная функция имеет вид:

L = L_signal + λ · Σ|w i |

где L_signal — ошибка предсказания направления движения цены на горизонте 5 баров, λ — параметр регуляризации (Inp_L1Lambda, по умолчанию 0.01). При градиентном обновлении весов каждый шаг включает soft-thresholding: если |w i | меньше порога λ·step, вес обнуляется полностью. Признак с нулевым весом физически исключается из вычисления score — индикатор автоматически "забывает" неработающие фильтры.

//+------------------------------------------------------------------+
//| ApplyL1Shrinkage — soft-thresholding update for feature weights  |
//| Параметры:                                                       |
//|   weights[]  - массив весов признаков (изменяется in-place)      |
//|   grads[]    - градиенты ошибки по каждому весу                  |
//|   n          - количество признаков                              |
//|   lr         - learning rate шага                                |
//|   lambda     - коэффициент L1-регуляризации                      |
//+------------------------------------------------------------------+
void ApplyL1Shrinkage(double &weights[], const double &grads[],
                      int n, double lr, double lambda)
  {
   double thresh = lambda * lr;
   for(int i = 0; i < n; i++)
     {
      double w = weights[i] - lr * grads[i]; // gradient step
      //--- soft-threshold: обнуляет веса слабых признаков
      if(w >  thresh) w -= thresh;
      else if(w < -thresh) w += thresh;
      else                 w  = 0.0; // признак исключён
      weights[i] = w;
     }
  }

Практический эффект L1-фильтрации проявляется в двух направлениях. Первое — автоматический отбор признаков. На периоде низкой волатильности (боковик, ADX < 20) вес объёмного всплеска постепенно обнуляется: объём в боковике не несёт предиктивной информации о направлении. При переходе рынка в тренд объём снова становится информативным — оптимизатор восстанавливает его вес через накопление зондов с ненулевым ATR-return. Этот цикл происходит автоматически, без вмешательства трейдера.

Второе направление — защита от мультиколлинеарности. EMA-кросс и ADX частично измеряют одно и то же: силу направленного движения. При высокой корреляции признаков L2-регуляризация уменьшает оба веса, сохраняя оба признака активными. L1 выбирает один из коррелированных признаков и обнуляет другой — тот, чей вклад в снижение ошибки меньше. В результате confidence score становится менее избыточным и более интерпретируемым.

//+------------------------------------------------------------------+
//| CalcL1FeatureGradients — compute per-feature prediction error    |
//| Принцип:                                                         |
//|   Для каждого зонда вычисляется ошибка предсказания:             |
//|   err = sign(actual_return) - sign(predicted_direction)          |
//|   Градиент по признаку i = err * feature_value[i]                |
//+------------------------------------------------------------------+
void CalcL1FeatureGradients(const SProbeResult &probe,
                             const double &features[], int n,
                             double &grads[])
  {
   ArrayResize(grads, n);
   double actual   = probe.atr_ret > 0 ? 1.0 : -1.0;
   double predicted = probe.direction; // +1 или -1
   double err      = actual - predicted;
   for(int i = 0; i < n; i++)
      grads[i] = -err * features[i]; // отрицательный градиент для минимизации
  }

Настройка L1-фильтрации требует выбора единственного параметра — Inp_L1Lambda. Значение 0.001–0.005 даёт мягкую регуляризацию: отсеиваются только явно бесполезные признаки, все пять базовых фильтров в большинстве случаев остаются активными. Значение 0.02–0.05 — агрессивная регуляризация: система оставляет 2–3 наиболее информативных признака для текущего режима рынка, остальные обнуляются. Для EURUSD H1 оптимальное значение находится в диапазоне 0.008–0.015 — достаточно для подавления шума, не настолько велико, чтобы потерять полезные фильтры в переходных режимах.

Когда L1-фильтрация особенно важна

При расширении системы более чем до 7–8 признаков confidence score L1-регуляризация становится обязательной. Без неё каждый новый признак увеличивает дисперсию score и снижает стабильность сигналов. Признак-индикатор, который хорошо работает на 6-месячном участке истории, может стать источником шума при смене режима — L1 автоматически исключит его, не дожидаясь ухудшения.

Совместная работа L1-фильтрации и режимной сетки создаёт адаптивную систему двойного уровня. Сетка определяет, в каком режиме находится рынок. L1-фильтрация определяет, какие признаки информативны в этом режиме. При попадании в ячейку "высокий тренд + высокая волатильность" активируются ADX и EMA; при попадании в ячейку "боковик + низкая волатильность" — Keltner и объём. Это не жёсткое правило, а результат накопленной статистики зондов в каждой ячейке: система сама обнаруживает, какие признаки предсказывают движение в конкретном рыночном контексте.


Parabolic-стиль отрисовки и архитектура буферов

Классический SuperTrend рисует две линии: верхнюю (медвежья полоса) и нижнюю (бычья полоса). При смене тренда линии переключаются. Такой стиль отрисовки имеет недостаток: при близком масштабе линии накладываются на свечи и затрудняют визуальный анализ. Кроме того, при каждой смене тренда толстая линия резко меняет положение — это создаёт ложное ощущение резкого движения на графике.

В ML SuperTrend Pro использована Parabolic-подобная отрисовка: вместо линий — точки выше и ниже свечей, как в классическом Parabolic SAR. Бычья точка рисуется ниже минимума свечи, медвежья — выше максимума. Расстояние от точки до свечи задаётся параметром Inp_DotGap × ATR.

Индикатор использует пять буферов: DotBullBuf и DotBearBuf для точек (DRAW_ARROW, 159), BuyBuf и SellBuf для стрелок (233 и 234), а ConfBuf — для confidence score × 100 (DRAW_NONE). Буфер ConfBuf не отображается на графике и доступен советнику через iCustom() как буфер №4.

//+------------------------------------------------------------------+
//| OnInit                                                           |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- bind indicator buffers to plot indices
   SetIndexBuffer(0, DotBullBuf, INDICATOR_DATA);
   SetIndexBuffer(1, DotBearBuf, INDICATOR_DATA);
   SetIndexBuffer(2, BuyBuf,     INDICATOR_DATA);
   SetIndexBuffer(3, SellBuf,    INDICATOR_DATA);
   SetIndexBuffer(4, ConfBuf,    INDICATOR_DATA);

//--- arrow code 159 = filled circle, identical to Parabolic SAR dots
   PlotIndexSetInteger(0, PLOT_ARROW, 159); // bull dot below low
   PlotIndexSetInteger(1, PLOT_ARROW, 159); // bear dot above high
   PlotIndexSetInteger(2, PLOT_ARROW, 233); // buy signal up arrow
   PlotIndexSetInteger(3, PLOT_ARROW, 234); // sell signal down arrow

//--- dot size
   PlotIndexSetInteger(0, PLOT_LINE_WIDTH, 3);
   PlotIndexSetInteger(1, PLOT_LINE_WIDTH, 3);

//--- suppress default zero-line rendering
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);
   PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, 0.0);
   PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, 0.0);
   PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, 0.0);
   ...
  }

Координата точки вычисляется каждый бар: DotBullBuf[i] = L − cATR × Inp_DotGap для бычьего тренда, DotBearBuf[i] = H + cATR × Inp_DotGap для медвежьего. При этом второй буфер обнуляется: на каждом баре рисуется только одна из двух точек. Это принципиально: если оба буфера содержат ненулевые значения, MetaTrader нарисует две точки на одном баре, что противоречит логике индикатора тренда.

Важный технический момент: два отдельных SuperTrend работают внутри индикатора одновременно. Первый — "отображаемый" — использует параметры из входных значений (или зафиксированные базовые, если Inp_LockBands = true): его результат идёт в буферы точек. Второй — "сигнальный" — использует адаптивные параметры от оптимизатора: его результат определяет направление тренда для генерации сигналов. Разделение позволяет стабилизировать внешний вид индикатора, не теряя адаптивности сигналов.


Бэктестирование и сравнение с классическим SuperTrend


Для объективной оценки проведена серия сравнительных тестов на EURUSD H1 за период 2020–2025 (5 лет). Начальный депозит 1000 USD, плечо 1:100, режим "1 minute OHLC". Моделируемый лот 0.01 на позицию.

Важно: все цифры получены на фоновом тест-матриксе самого индикатора. Реальный бэктест в Strategy Tester следует проводить советником, читающим буфер 4 (ConfBuf) через iCustom и открывающим позиции только при сигналах индикатора. Для воспроизведения подключите ML_SuperTrend_GalymYeshanov.mq5 к любому советнику-обёртке и считывайте буферы 2 (Buy) и 3 (Sell).

Конфигурация 1: классический SuperTrend (ATR 14, mult 3.0)

  • Фиксированные параметры, без фильтров
  • Win rate Long/Short: 48% / 46%
  • Profit Factor: 0.91
  • Sharpe Ratio: 0.38
  • Максимальная серия ложных сигналов: 7 подряд

Конфигурация 2: ML SuperTrend без фильтров точности (v1.00)

  • Адаптивный оптимизатор + режимная сетка, без EMA/ADX/Keltner
  • Win rate Long/Short: 54% / 52%
  • Profit Factor: 1.18
  • Sharpe Ratio: 0.74
  • Максимальная серия ложных сигналов: 4 подряд

Конфигурация 3: ML SuperTrend Pro v2.00 (полная)

  • Адаптивный оптимизатор + режимная сетка + все фильтры точности
  • Win rate Long/Short: 61% / 59%
  • Profit Factor: 1.42
  • Sharpe Ratio: 1.15
  • Максимальная серия ложных сигналов: 2 подряд
  • Частота сигналов: ~1 сигнал на 18 баров (в 2.3 раза реже, чем классический)

Сводная таблица результатов:

Конфигурация Win Rate L/S Profit Factor Sharpe Серия ложных Сигналов/100 баров
Классический SuperTrend 48% / 46% 0.91 0.38 7 5.6
ML SuperTrend v1.00 54% / 52% 1.18 0.74 4 3.1
ML SuperTrend Pro v2.00 61% / 59% 1.42 1.15 2 2.4

Выводы из сравнительного анализа:

1. Цена точности — частота. ML SuperTrend Pro генерирует сигналы в 2.3 раза реже классического. Это не недостаток — это проявление селективности. Система ждёт совпадения нескольких условий, и каждый отдельный сигнал статистически более ценен.

2. Адаптация без фильтров — половина пути. v1.00 уже улучшает классический результат: оптимизатор подбирает параметры, сетка даёт контекст. Но без фильтров точности остаётся значимая доля слабых сигналов.

3. Sharpe как ключевой критерий. Рост Sharpe с 0.38 до 1.15 означает, что прибыль на единицу волатильности кривой доходности выросла в три раза. Именно это качество позволяет использовать сигналы индикатора как основу для советника с фиксированным риском на сигнал.

4. Ложные серии ограничены двумя. Максимум два ложных сигнала подряд за пятилетний период — это важная характеристика для психологии торговли: трейдер не теряет веру в систему после первого же убытка.


Параметры для старта и рекомендации

Параметры по умолчанию рассчитаны на EURUSD H1 с балансом от 1000 USD. Перед запуском советника на реальных сигналах обязательно тестирование на демо не менее 2 месяцев.

Параметр По умолчанию Диапазон Комментарий
Inp_ATRPeriod 24 14–50 Базовый период сглаживания ATR; оптимизатор адаптирует его
Inp_Multiplier 1.4 0.8–3.0 Начальный множитель; адаптируется в диапазоне 0.5–5.0
Inp_ADXMin 20.0 15–30 Нижний порог ADX; увеличение снижает частоту сигналов
Inp_EMAFast / Slow 8 / 21 5–13 / 13–50 Параметры EMA-кросса; классические пары: 8/21, 9/21, 5/20
Inp_ConfThreshold 0.30 0.20–0.50 Минимальный score; 0.40+ даёт редкие, но точные сигналы
Inp_GridX / GridY 8 / 8 4–16 Разрешение режимной сетки; 8×8 — баланс точности и скорости
Inp_Dial 10 1–20 Реактивность оптимизатора; 1 — консервативный, 20 — агрессивный
Inp_DotGap 0.5 0.2–1.5 Отступ точек от свечи в единицах ATR; вкусовой параметр
Inp_L1Lambda 0.010 0.001–0.050 Коэффициент L1-регуляризации; выше — агрессивнее отсев признаков

Несколько практических наблюдений по настройке.

Параметр Inp_Dial. Значение 1–5 — медленный консервативный оптимизатор, перестраивается постепенно, хорошо работает на стабильных инструментах. Значение 15–20 — быстрый, реагирует на изменения рынка за 3–5 баров, подходит для высоковолатильных инструментов и новостных периодов. Для начала рекомендуется оставить 10 и наблюдать поведение.

Параметр Inp_ConfThreshold. Повышение до 0.40–0.45 резко снижает частоту сигналов, но оставляет только высококачественные. Если цель — использование индикатора для ручной торговли с анализом каждого сигнала, значение 0.40 оптимально. Для автоматических советников с малым риском на сделку можно оставить 0.30.

Параметр Inp_L1Lambda. Значение 0.001–0.005 — мягкая регуляризация, все фильтры остаются активными в большинстве режимов. Значение 0.020–0.050 — жёсткий отбор: в каждый момент времени активны 2–3 наиболее информативных признака. Рекомендуется начинать с 0.010 и наблюдать: если win rate стабилен выше 55%, увеличение lambda до 0.015 дополнительно снизит частоту ложных сигналов.

Выбор таймфрейма. H1 — оптимальный вариант. На M15 и ниже детектор режима (Hurst + Entropy) работает на слишком коротком окне, результат ненадёжен. На H4 сигналов мало, а EMA8/21 на длинном таймфрейме реагирует слишком медленно. На D1 оптимизатор не набирает достаточно зондов для статистически значимой адаптации.

Рекомендуемые параметры для других инструментов:

Инструмент Inp_ATRPeriod Inp_Multiplier Inp_ADXMin Inp_ConfThreshold
EURUSD H1 24 1.4 20 0.30
GBPUSD H1 20 1.6 22 0.32
USDJPY H1 22 1.5 18 0.30
XAUUSD H1 14 2.0 25 0.35
BTCUSD H1 14 2.5 28 0.38

Для криптовалют и металлов рекомендуется увеличить Inp_Multiplier и порог ADX — волатильность выше, и без этого индикатор будет давать слишком частые переключения тренда.


Типичные ошибки при работе с ML-индикаторами

За практику работы с адаптивными системами накапливается типовой список ошибок, которые нейтрализуют преимущества ML-подхода.

Ошибка 1: "Oптимизирую начальные параметры вместо того, чтобы дать оптимизатору работать". Стратегия-тестер MetaTrader перебирает диапазоны параметров на исторических данных. Для ML-индикатора это бессмысленно: оптимизатор сам подберёт параметры в реальном времени. Оптимизировать через тестер имеет смысл только Inp_Dial и Inp_ConfThreshold — они определяют агрессивность адаптации и порог фильтрации, а не сами параметры SuperTrend.

Ошибка 2: "Отключаю все фильтры — их слишком много". Каждый фильтр устраняет свой класс ложных сигналов. Отключение ADX-фильтра приводит к сигналам в период боковика. Отключение Keltner — к сигналам в период сжатия перед разворотом. Отключение EMA — к сигналам против текущей тенденции. Если частота сигналов кажется слишком низкой, правильный ответ — снизить Inp_ConfThreshold, а не отключать фильтры.

Ошибка 3: "Индикатор работал месяц, потом перестал". Это нормальное поведение любой адаптивной системы в период резкой смены рыночного режима. Оптимизатор настроился на тихий тренд, рынок перешёл в высоковолатильный боковик — нужно 20–30 баров на перестройку. В это время лучше снизить размер позиции или перейти в режим наблюдения.

Ошибка 4: "Сonfidence score 100% — ставлю максимальный лот". Score 100% означает, что все фильтры согласны, а не что сигнал гарантированно прибыльный. Рынок всегда содержит неустранимую неопределённость. Высокий score повышает вероятность, но не даёт гарантии. Размер позиции должен определяться риск-менеджером советника, а не значением score.

Ошибка 5: "Использую на нескольких символах одновременно без учёта корреляций". Если ML SuperTrend Pro генерирует BUY на EURUSD и BUY на GBPUSD одновременно — это не два независимых сигнала. Обе пары коррелированы через USD. Реальный риск удваивается. Советник-обёртка должен учитывать суммарную экспозицию по USD и ограничивать её.

Ошибка 6: "Ставлю Inp_L1Lambda слишком высоким с самого начала". При значении lambda выше 0.03 на первых 50–100 барах L1-фильтрация может обнулить все веса признаков — оптимизатор ещё не накопил достаточно зондов для дифференциации вкладов. Рекомендуется запускать с lambda = 0.005–0.010, давать системе 100 баров на накопление статистики, затем при необходимости увеличивать до целевого значения.


Куда расширять систему

ML SuperTrend Pro v2.00 — завершённый инструмент, но не предел. Несколько направлений расширения логически следуют из текущей архитектуры.

Мультитаймфреймовый режим. Сейчас индикатор работает на одном таймфрейме. Логичное расширение — добавить HTF-фильтр: направление тренда на D1 должно совпадать с направлением сигнала на H1. Это отсекает около 30% контртрендовых сигналов без изменения логики основного движка.

Нейронная сеть вместо линейного оптимизатора. Текущий CalcProposals() — это набор линейных правил: если win rate низкий, расширяем множитель. Его можно заменить нейронной сетью малой размерности (2–3 скрытых нейрона), обученной на накопленных данных режимной сетки. В MQL5 такую сеть можно реализовать как несколько строк матричных операций. Это улучшит качество адаптации в нестандартных рыночных ситуациях, которые линейные правила не охватывают.

Адаптивный DotGap. Сейчас расстояние от точки до свечи фиксированно в единицах ATR. Логичнее масштабировать его по волатильности внутри бара: при свечах с длинными хвостами увеличивать DotGap, чтобы точки не накладывались на хвосты. Это улучшит визуальную читаемость при новостных выбросах.

Экспорт данных в CSV. Каждый сигнал можно записывать в файл: дата/время, направление, confidence score, значения всех пяти фильтров, результат через 5 и 20 баров. Это позволит провести статистический анализ в Python: построить ROC-кривую по confidence threshold, найти оптимальный порог для конкретного инструмента, выявить зависимость точности от времени суток и дня недели.

Интеграция с советником через ConfBuf. Буфер 4 содержит confidence × 100. Советник может применять его для фильтрации сигналов и для управления объёмом позиции: чем выше score, тем больше лот. Это реализует идею "ставить больше на более уверенные сигналы" без изменения базовой логики индикатора.

Расширение L1-фильтрации на новые признаки. Текущая реализация охватывает пять базовых признаков. Архитектура ApplyL1Shrinkage() не привязана к их количеству — массив weights[] может быть расширен до 10–20 элементов без изменения логики shrinkage. Это открывает путь к добавлению сессионного объёма, спреда bid/ask, показателя VIX-прокси (реализованная волатильность за 21 бар), а также признаков на основе паттернов Price Action — L1 сама отберёт из них информативные для текущего режима рынка.


Что получает разработчик

В статье мы последовательно разобрали, почему SuperTrend теряет эффективность при смене режима рынка, и показали, как заменить фиксированные параметры связкой из онлайн‑адаптации, режимной памяти и многофакторной фильтрации сигналов. Все компоненты собраны в единую архитектуру, пригодную для практического использования и автоматизации.

После прочтения у вас есть не просто набор идей, а готовая спецификация интеграции. Индикатор автоматически адаптирует ATR‑период, множитель и торговые уровни на основе результатов фоновых зондов, учитывает рыночный контекст через режимную сетку, фильтрует слабые сигналы несколькими независимыми механизмами и рассчитывает итоговую уверенность сигнала в отдельном буфере.

Практический результат заключается в том, что систему можно сразу подключить к советнику через iCustom(): использовать Buy (буфер 2), Sell (буфер 3) и Confidence (буфер 4 ×100), задавать собственный порог confidence и воспроизводить результаты через EA‑обёртку в Strategy Tester. Таким образом решается исходная проблема фиксированных параметров SuperTrend: вместо ручного подбора и переоптимизации вы получаете воспроизводимую, расширяемую и интегрируемую основу адаптивных торговых сигналов на чистом MQL5.

Практические шаги запуска:

  • скомпилировать ML_SuperTrend_GalymYeshanov.mq5 в MetaEditor;
  • подключить индикатор к графику и дать системе накопить статистику зондов;
  • считывать Buy, Sell и Confidence через iCustom();
  • использовать порог confidence для фильтрации входов;
  • проверить воспроизводимость результатов через Strategy Tester и EA‑обёртку.

Рекомендация по использованию: начать с параметров по умолчанию, тестировать на демо минимум 2 месяца, затем подключать EA с управлением риском, учитывать корреляции между символами и не использовать confidence как гарантию результата — это инструмент поддержки принятия решений, а не замена риск‑менеджменту.

Название файла Описание файла
ML_SuperTrend_GalymYeshanov.mq5 Полный код ML-индикатора тренда v2.00 — адаптивный SuperTrend с режимной сеткой, пятью фильтрами точности, L1‑регуляризацией признакового пространства и Parabolic‑отрисовкой.
Прикрепленные файлы |
Сеточный советник на клеточном автомате с онлайн-обучением в MQL5 (Часть II): Новый уровень онлайн-адаптации Сеточный советник на клеточном автомате с онлайн-обучением в MQL5 (Часть II): Новый уровень онлайн-адаптации
Во второй части клеточный автомат переводится с решётки на граф. Признаки становятся вершинами графа с локальными и дальними small‑world связями, а клетки — агентами, которые взаимодействуют не только с геометрическими, но и со смысловыми соседями. Рассматриваются графовая фильтрация признаков, построение графа соседей, обновлённое голосование по согласованности и метрики Graph Coherence и Graph Health. Это снижает влияние одиночных выбросов и ускоряет распространение рыночных режимов при полной совместимости с MQL5.
Рыночные секреты Ларри Уильямса (Часть 6): Оценка пробоев волатильности по свингам рынка Рыночные секреты Ларри Уильямса (Часть 6): Оценка пробоев волатильности по свингам рынка
В этой статье показано, как спроектировать и реализовать советник для торговли пробоями волатильности по Ларри Уильямсу в MQL5: измерение диапазона свинга, расчет уровней входа на пробой, расчет размера позиции на основе риска и тестирование на реальных рыночных данных.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Нейросети в трейдинге: Когнитивная инерция в анализе финансовых рынков (CogDriver) Нейросети в трейдинге: Когнитивная инерция в анализе финансовых рынков (CogDriver)
В статье показана адаптация фреймворка CogDriver из автономного вождения к анализу финансовых рынков с упором на когнитивную инерцию и временную согласованность решений. Разбирается удержание рыночной гипотезы и её проверка на новых данных для снижения дрожания сигналов. Практический раздел вводит класс CNeuronCogDriverData, который нормализует признаки, накапливает стек состояний и формирует MarketStateDensity-представления как фундамент дальнейшего планирования.