English
preview
От скользящей средней с фиксированными параметрами к адаптивной фильтрации (Часть 2): Реализация индикатора SAMA_NLMS на MQL5

От скользящей средней с фиксированными параметрами к адаптивной фильтрации (Часть 2): Реализация индикатора SAMA_NLMS на MQL5

MetaTrader 5 — Индикаторы |
43 0
Ushana Kevin Iorkumbul
Ushana Kevin Iorkumbul

Введение

Вы уже знакомы с концепцией NLMS из Части 1. Однако на практике перенос уравнения обновления из вычислительного блокнота в код MQL5 часто приводит к проблемам, существенным для трейдеров и разработчиков: модель может обучаться на формирующемся баре, становиться численно неустойчивой на данных без изменений, искажаться из-за одиночных ценовых всплесков и выдавать другие значения в реальном времени после перезапуска терминала, поскольку внутреннее состояние весов зависит от того, какая именно история была обработана. В этой части SAMA рассматривается уже не просто как формула, а как программное обеспечение для реальной эксплуатации. Здесь NLMS встроен в готовый к установке на график индикатор MQL5 с корректно организованными буферами, проверкой входных параметров, ограничением ошибки на основе ATR, адаптацией μ по коэффициенту эффективности Кауфмана, затуханием весов и их необязательной нормализацией, обязательным этапом прогрева и правилом обучения только на закрытых барах. Цель ясна: предоставить файл SAMA_NLMS.mq5, который можно скомпилировать, подключить к графику и использовать в торговых сценариях без явной нестабильности или неожиданного перерисовывания.


Раздел 1: Полный код индикатора

Создайте в MetaEditor новый файл индикатора с именем SAMA_NLMS.mq5 и вставьте в него следующий код:

//+------------------------------------------------------------------+
//|                                                    SAMA_NLMS.mq5 |
//|                     Self-Adaptive Moving Average via NLMS Online |
//+------------------------------------------------------------------+
#property description "Self-Adaptive Moving Average using Normalized LMS (NLMS) algorithm."

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   1

//--- Plot 0: SAMA Line with dynamic slope color formatting
#property indicator_label1  "SAMA"
#property indicator_type1   DRAW_COLOR_LINE
#property indicator_color1  clrMediumSeaGreen,clrCrimson,clrSlateGray
#property indicator_width1  2
#property indicator_style1  STYLE_SOLID

//+------------------------------------------------------------------+
//|Enumerations for Input Transformations                            |
//+------------------------------------------------------------------+
enum ENUM_INPUT_MODE
  {
   INPUT_PRICE = 0, // Option A: Raw Prices
   INPUT_DIFF  = 1, // Option B: Differences (Delta)
   INPUT_RET   = 2  // Option C: Returns (Percentage Change)
  };

//+------------------------------------------------------------------+
//| Input Parameters                                                 |
//+------------------------------------------------------------------+
input ENUM_INPUT_MODE inp_mode            = INPUT_PRICE; // Data transformation input mode
input int             inp_filter_length   = 14;          // Filter length (Adaptive weight count)
input double          inp_learning_rate   = 0.05;        // Learning rate (Base Step Size Mu)
input bool            inp_use_close       = true;        // Price: true = Close; false = Typical (H+L+C)/3
input double          inp_epsilon         = 1e-8;        // Epsilon for numerical division safety
input double          inp_leak            = 0.0001;      // Forgetting factor / Leakage weight clamp
input bool            inp_use_atr_clamp   = true;        // Use ATR error clamping option
input int             inp_atr_period      = 14;          // ATR Period for error boundaries
input double          inp_error_atr_mult  = 3.0;         // Max allowed error multiplier (Mult * ATR)
input bool            inp_use_adaptive_lr = true;        // Dynamic step size adjustment via ER
input int             inp_er_period       = 10;          // Efficiency Ratio window period
input bool            inp_normalize_weights = true;      // Force adaptive weights sum to 1.0
input int             inp_warmup_bars     = 500;         // Historical background initialization loop

//+------------------------------------------------------------------+
//| Indicator Buffers                                                |
//+------------------------------------------------------------------+
double g_sama_buffer[];                                  // Main display buffer
double g_color_buffer[];                                 // Derivative slope color assignment matrix

//+------------------------------------------------------------------+
//| Global State Variables                                           |
//+------------------------------------------------------------------+
double g_weights[];                                      // Memory matrix containing adaptive weight vector
int    g_atr_handle = INVALID_HANDLE;                    // Managed calculation pointer handle for core system ATR
double g_atr_buffer[];                                   // Direct memory block array for streaming ATR inputs

//+------------------------------------------------------------------+
//| Helper: Get the raw localized price                              |
//+------------------------------------------------------------------+
double GetSourcePrice(const double &close[], const double &high[], const double &low[], const int i)
  {
   return(inp_use_close ? close[i] : (high[i] + low[i] + close[i]) / 3.0);
  }

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Parameter Bounds Sanitization Checking
   if(inp_filter_length < 2 || inp_learning_rate <= 0.0 || inp_learning_rate > 2.0 || inp_warmup_bars < 0 || inp_er_period < 2)
     {
      Alert("SAMA_NLMS Error: Invalid configuration values detected.");
      return(INIT_PARAMETERS_INCORRECT);
     }

//--- Initialize ATR Handle if required
   if(inp_use_atr_clamp)
     {
      g_atr_handle = iATR(_Symbol, _Period, inp_atr_period);
      if(g_atr_handle == INVALID_HANDLE)
        {
         Print("SAMA_NLMS: Failed to open system iATR structural handle.");
         return(INIT_FAILED);
        }
     }

//--- Bind structural index buffer handles to terminal engine
   SetIndexBuffer(0, g_sama_buffer, INDICATOR_DATA);
   SetIndexBuffer(1, g_color_buffer, INDICATOR_COLOR_INDEX);

//--- Shift processing view beyond window boundaries to hide processing artifacts
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, inp_filter_length + inp_warmup_bars + inp_er_period);

//--- Allocate structural filter depth to weight vectors
   ArrayResize(g_weights, inp_filter_length);
   ArrayInitialize(g_weights, 1.0 / (double)inp_filter_length);

   IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("SAMA_NLMS(Len=%d, Leak=%.5f)", inp_filter_length, inp_leak));
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| 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[])
  {
   int structural_minimum = inp_filter_length + 2;
   if(inp_use_adaptive_lr)
      structural_minimum = MathMax(structural_minimum, inp_er_period + 2);

   if(rates_total <= (structural_minimum + inp_warmup_bars))
      return(0);

//--- Linear continuous buffer optimization strategy
   double source[], tr_input[];
   if(ArrayResize(source, rates_total) < 0 || ArrayResize(tr_input, rates_total) < 0)
      return(0);

//--- Map transformations into contiguous execution memory cache
   for(int i = 0; i < rates_total; i++)
     {
      source[i] = GetSourcePrice(close, high, low, i);
      if(i > 0)
        {
         if(inp_mode == INPUT_PRICE)
            tr_input[i] = source[i];
         else
            if(inp_mode == INPUT_DIFF)
               tr_input[i] = source[i] - source[i - 1];
            else
               if(inp_mode == INPUT_RET)
                  tr_input[i] = (source[i - 1] != 0.0) ? ((source[i] - source[i - 1]) / source[i - 1]) : 0.0;
        }
      else
         tr_input[i] = 0.0;
     }

//--- Handle core execution loops state initializations
   int start_idx;
   if(prev_calculated == 0)
     {
      ArrayInitialize(g_weights, 1.0 / (double)inp_filter_length);
      start_idx = structural_minimum;

      //--- Wipe stale arrays out of drawing tracks
      for(int i = 0; i < start_idx; i++)
        {
         g_sama_buffer[i] = EMPTY_VALUE;
         g_color_buffer[i] = EMPTY_VALUE;
        }
     }
   else
     {
      start_idx = prev_calculated - 1;
     }

//--- Safely retrieve ATR mapping parameters (Non-Series Normal Handling)
   if(inp_use_atr_clamp)
     {
      ArrayResize(g_atr_buffer, rates_total);
      if(CopyBuffer(g_atr_handle, 0, 0, rates_total, g_atr_buffer) <= 0)
         return(0);
     }

//--- Main Core Adaptive Filtering Compute Execution
   for(int i = start_idx; i < rates_total; i++)
     {
      double target = tr_input[i];

      //--- Step 1: Compute total vector transformation energy
      double energy = 0.0;
      for(int j = 0; j < inp_filter_length; j++)
        {
         double p = tr_input[i - 1 - j];
         energy += p * p;
        }

      //--- Step 2: Extract current structural adaptation state prediction
      double predicted = 0.0;
      for(int j = 0; j < inp_filter_length; j++)
        {
         predicted += g_weights[j] * tr_input[i - 1 - j];
        }

      //--- Step 3: Quantify dynamic estimation error deviation metric
      double error = target - predicted;

      //--- Strict ATR volatility boundary error calculation clamp
      if(inp_use_atr_clamp && i >= inp_atr_period)
        {
         double atr = g_atr_buffer[i];
         double max_error = inp_error_atr_mult * atr;

         if(inp_mode == INPUT_RET && source[i - 1] != 0.0)
            max_error /= source[i - 1];

         error = MathMin(MathMax(error, -max_error), max_error);
        }

      //--- Step 4: Perform optimization adjustment updates on completed intervals
      if(i < rates_total - 1)
        {
         double mu = inp_learning_rate;

         //--- Process Kaufman Efficiency Ratio Adaptive Engine
         if(inp_use_adaptive_lr && i >= inp_er_period)
           {
            double net_direction = MathAbs(source[i] - source[i - inp_er_period]);
            double net_volatility = 0.0;

            for(int k = 0; k < inp_er_period; k++)
              {
               net_volatility += MathAbs(source[i - k] - source[i - k - 1]);
              }

            double efficiency_ratio = (net_volatility > 0.0) ? (net_direction / net_volatility) : 0.0;
            mu *= (0.5 + efficiency_ratio);
           }

         //--- Standardized Classical NLMS normalization step
         double normalized_lr = mu / (inp_epsilon + energy);

         //--- Apply optimization weight corrections
         for(int j = 0; j < inp_filter_length; j++)
           {
            g_weights[j] = (1.0 - inp_leak) * g_weights[j] + (normalized_lr * error * tr_input[i - 1 - j]);
           }

         //--- Dynamic execution bounds weight normalization protection
         if(inp_normalize_weights)
           {
            double weight_sum = 0.0;
            for(int j = 0; j < inp_filter_length; j++)
               weight_sum += g_weights[j];

            if(MathAbs(weight_sum) > 1e-12)
              {
               for(int j = 0; j < inp_filter_length; j++)
                  g_weights[j] /= weight_sum;
              }
           }
        }

      //--- Step 5: Convert state matrix evaluations to absolute price coordinates
      double transformed_output = 0.0;
      if(inp_mode == INPUT_PRICE)
         transformed_output = predicted;
      else
         if(inp_mode == INPUT_DIFF)
            transformed_output = source[i - 1] + predicted;
         else
            if(inp_mode == INPUT_RET)
               transformed_output = source[i - 1] * (1.0 + predicted);

      //--- Sanitize anomalous computations
      if(!MathIsValidNumber(transformed_output))
         g_sama_buffer[i] = (i > 0) ? g_sama_buffer[i - 1] : source[i];
      else
         g_sama_buffer[i] = transformed_output;

      //--- Multi-Buffer dynamic derivative trend color classification
      if(i > structural_minimum && g_sama_buffer[i - 1] != EMPTY_VALUE)
        {
         double slope = g_sama_buffer[i] - g_sama_buffer[i - 1];
         if(slope > 0.0)
            g_color_buffer[i] = 0.0;
         else
            if(slope < 0.0)
               g_color_buffer[i] = 1.0;
            else
               g_color_buffer[i] = 2.0;
        }
      else
         g_color_buffer[i] = 2.0;
     }

   return(rates_total);
  }
//+------------------------------------------------------------------+

Справочник по входным параметрам

Прежде чем пошагово рассмотреть логику работы, разберём, за что отвечает каждый входной параметр и как он влияет на поведение фильтра. Используйте этот раздел как справочник по параметрам, к которому можно возвращаться при настройке индикатора для разных торговых инструментов.

Параметр Тип По умолчанию Допустимый диапазон Практический эффект
inp_mode enum INPUT_PRICE PRICE / DIFF / RET Определяет, на чем будет обучаться фильтр. PRICE = исходная цена; DIFF = изменение от бара к бару (импульс); RET = процентная доходность (масштабно-инвариантная).
inp_filter_length int 14 ≥ 2 Количество адаптивных весов (глубина расчёта). Чем больше длина фильтра, тем сильнее сглаживание, но выше вычислительная нагрузка. SAMA сохраняет эффективность и при малой длине, поскольку адаптируется, а не увеличивает окно.
inp_learning_rate double 0,05 (0; 2,0) Базовый размер шага μ. Более высокое значение ускоряет отслеживание изменений, но повышает риск колебаний; μ ≤ 1,0 — безопасный диапазон, обеспечивающий быструю реакцию.
inp_use_close bool true — Источник цены: true = цена закрытия, false = типичная цена ((максимум + минимум + цена закрытия)/3).
inp_epsilon double 1e-8 малое значение > 0 Нижний порог для знаменателя при нормировке, предотвращающий деление на ноль. Имеет значение только тогда, когда энергия входного сигнала падает почти до нуля.
inp_leak double 0,0001 (0; ~0,01) Коэффициент забывания γ. Чем выше значение, тем быстрее веса затухают к нулю, что несколько снижает быстроту реакции, но повышает устойчивость в долгосрочной перспективе.
inp_use_atr_clamp bool true — Позволяет ограничить величину ошибки значением, кратным ATR, чтобы отдельный скачок не мог исказить веса.
inp_atr_period int 14 ≥ 1 Глубина расчёта ATR, используемого для ограничения ошибки.
inp_error_atr_mult double 3,0 &gt; 0 Допустимая величина ошибки в единицах ATR, при превышении которой применяется ограничение. Чем ниже значение, тем жёстче подавление скачков.
inp_use_adaptive_lr bool true — На каждом баре масштабирует μ с помощью коэффициента эффективности Кауфмана.
inp_er_period int 10 ≥ 2 Окно расчета коэффициента эффективности.
inp_normalize_weights bool true — При каждом обновлении приводит сумму весов вектора к 1,0, сохраняя выходное значение в масштабе цены. Настоятельно рекомендуется использовать в режиме PRICE.
inp_warmup_bars int 500 ≥ 0 Количество баров, обрабатываемых без отображения значений перед началом построения линии, чтобы скрыть переходный процесс сходимости.

Таблица 1: Справочник входных параметров

Практичный начальный набор настроек для большинства валютных пар: оставьте значения по умолчанию, но установите для inp_mode значение INPUT_RET на инструментах с сильно различающимися масштабами цен или во время сессий с низкой ликвидностью.


Раздел 2: Разбор кода — ключевые проектные решения

Оптимизированная архитектура: выполнение непосредственно в основном цикле

Все основные векторные операции выполняются непосредственно внутри основного цикла OnCalculate. Это дает два конкретных преимущества:

  • Эффективность выполнения — выполнение непосредственно в основном цикле устраняет накладные расходы на вызовы функций и необходимость во внешних кольцевых буферах, снижая загрузку ЦП при высокой частоте поступления тиков.
  • Прямая индексация со смещением — обращение к историческим барам с помощью простой арифметики индексов (i − 1 − j) позволяет избежать накладных расходов на сдвиг элементов массива и повышает стабильность вычислений.

Инициализация весов

Инициализация адаптивной модели с обнуленными состояниями приводит к нестабильному отслеживанию и медленной сходимости. На первом проходе (prev_calculated == 0) код инициализирует g_weights равномерным распределением весов, при котором каждому из N элементов присваивается значение 1/N. Это создает нейтральное начальное состояние, эквивалентное простой скользящей средней, устраняет артефакты прогрева и обеспечивает отображение графика без искажений, как только доступно достаточно баров для заданной глубины расчёта.

Стабильность в реальном времени и защита при многократных тиковых обновлениях

Условие if(i < rates_total - 1) обеспечивает обновление весов только на закрытых барах. Текущий формирующийся бар используется для прогнозирования, но никогда — для обучения. Такое разделение предотвращает дрейф весов внутри бара и устраняет искажение данных, вызванное быстрыми разнонаправленными тиковыми изменениями цены в пределах одной свечи: историческая линия остается воспроизводимой, а для текущего бара по-прежнему рассчитывается прогноз на основе уже сформированных весов.

Чувствительность к скорости обучения и границы устойчивости

Нормированная скорость обучения μ определяет быстроту реакции при отслеживании цены. Поскольку обновление нормируется по энергии входного сигнала, масштабная инвариантность шага сохраняется в силу самой структуры алгоритма. В режимах DIFF и RET это обеспечивает высокую согласованность между классами активов. В режиме INPUT_PRICE сохраняется некоторая чувствительность к масштабу цен, поскольку порог ограничения ошибки на основе ATR выражен в единицах цены. Тем не менее нормировка по энергии входного сигнала предотвращает численное переполнение как для инструментов с низким уровнем цены (EURUSD), так и для инструментов с высоким уровнем цены (золото, индексы).

Хотя математическая граница устойчивости NLMS по скорости обучения составляет 2,0, значение μ не выше 1,0 обеспечивает быстрое и устойчивое отслеживание цены.

Таблица 2: Анализ чувствительности к нормированной скорости обучения μ (мю).

Значение μ Поведение
0,001 – 0,010 Консервативная адаптация, плавные кривые выходного сигнала
0,010 – 0,100 Сбалансированная конфигурация — оптимальная конфигурация для стандартных рыночных режимов
0,100 – 1,000 Высокая чувствительность отслеживания, точная подстройка при значительных пробоях
1,000 – 2,000 Агрессивная адаптация, приближающаяся к теоретическому пределу

Анализ чувствительности нормированной скорости обучения (μ). В данном руководстве реакция фильтра классифицируется по степени интенсивности — от консервативного сглаживания до агрессивного отслеживания. Благодаря динамической нормировке величины шага по энергии вектора цен алгоритм сохраняет устойчивость независимо от масштаба цен при работе с различными инструментами. С математической точки зрения граница устойчивости составляет 2,0. Задание значений выше этого предела может привести к колебаниям весов или взрыву градиента.


Раздел 3: Установка и отображение индикатора

  1. Откройте MetaEditor (клавиша F4 в MetaTrader 5).
  2. Выберите "Файл" → "Создать" → "Пользовательский индикатор".
  3. Назовите индикатор SAMA_NLMS, вставьте полный код и нажмите "Компилировать" (F7).
  4. В MetaTrader 5 откройте любой график, перейдите в меню "Вставка" → "Индикаторы" → "Пользовательские" и выберите SAMA_NLMS.
  5. Задайте входные параметры в диалоговом окне и нажмите "ОК".

На графике появится линия SAMA, цвет которой зависит от ее наклона. Она отображается цветом MediumSeaGreen при восходящем наклоне (восходящий тренд), цветом Crimson — при нисходящем наклоне (нисходящий тренд) и цветом SlateGray — в периоды бокового движения или переходных состояний. На первый взгляд линия будет напоминать EMA, но понаблюдайте за ее поведением при смене рыночных режимов — вы заметите, что во время сильных трендов она точнее следует за ценой, а в периоды беспорядочных колебаний ее запаздывание увеличивается.

Индикатор SAMA, нанесенный на график EURUSD с периодом H1

Рис. 1: Индикатор SAMA на графике EURUSD с таймфреймом H1 демонстрирует быстрое и точное следование за ценой во время сильного восходящего тренда и плавную стабилизацию в боковом диапазоне консолидации.


Раздел 4: Практические сценарии использования

В качестве динамического фильтра тренда

Используйте SAMA для классификации рыночных режимов по структуре движения цены. Если цена находится выше линии SAMA, ищите только возможности для открытия длинных позиций. Если цена опустится ниже этой линии, сосредоточьтесь исключительно на коротких позициях. Благодаря динамической адаптации фильтр автоматически приближается к цене при стремительных трендах. При хаотичных колебаниях цены в боковом диапазоне его линия становится более горизонтальной. Такое поведение позволяет исключить ложные смены рыночного режима по сравнению с традиционными скользящими средними с фиксированным периодом.

//+------------------------------------------------------------------+
//| Example: Querying the SAMA line from a prior indicator handle    |
//+------------------------------------------------------------------+
double sama_value[1];
if(CopyBuffer(sama_handle, 0, 1, 1, sama_value) > 0)
  {
   if(close_price > sama_value[0])
     {
      //--- Bullish regime: Allow long execution pathways only
     }
   else
     {
      //--- Bearish regime: Allow short execution pathways only
     }
  }

В качестве динамического ориентира поддержки и сопротивления

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

Сочетание со скользящей средней с фиксированным периодом для получения сигналов пересечения

Вы можете формировать надежные сигналы пересечения, сочетая SAMA со стандартной простой скользящей средней (SMA) с фиксированным периодом 50. Такая комбинация двух индикаторов формирует сигналы смены тренда, устойчивые к ложным пересечениям. Основное преимущество заключается в том, что SAMA динамически регулирует свою внутреннюю скорость следования за ценой. Скорость ее следования за рынком не остается жестко фиксированной.


Раздел 5: Понимание ограничений

Ни один алгоритмический индикатор не обеспечивает безупречного решения для торговли. Архитектура SAMA_NLMS обладает определёнными математическими свойствами, которые необходимо учитывать перед началом использования:

  • Сильная зависимость от траектории обучения на исторических данных: SAMA использует механизм рекурсивной онлайн-оптимизации. Точная структура матрицы весов для текущего бара полностью зависит от непрерывной последовательности исторических обновлений, обработанных с момента инициализации графика. Это создаёт важный нюанс работы индикатора, который необходимо учитывать в реальной торговле:

  • Глубина истории: при загрузке графика с 5 000 баров текущие векторы весов будут немного отличаться от тех, которые получаются при загрузке графика с 50 000 баров.
  • Смена таймфрейма: переключение на другой таймфрейм приводит к полной очистке памяти. Алгоритм должен с нуля сформировать историю адаптации на новом таймфрейме.
  • Пересчёты в терминале: если соединение с брокером прервётся и терминал принудительно обновит данные графика, внутренняя траектория изменения весов будет пересчитана с начала доступной истории.

  • Последствия для торговли и меры по снижению риска: из-за этой зависимости от траектории торговые сигналы в реальном времени могут временно расходиться с линиями индикатора при тестировании на исторических данных, если глубина истории не совпадает в точности. Для снижения этого риска в индикаторе реализован обязательный цикл инициализации, строго задаваемый параметром inp_warmup_bars. Этот параметр обязывает алгоритм обработать в фоновом режиме не менее 500 баров, прежде чем отображать значения индикатора для торговли. Этот этап прогрева в фоновом режиме стабилизирует массив весов, устраняя влияние различий в начальных условиях и обеспечивая единообразие сигналов при перезагрузках терминала.
  • Чрезмерная адаптация на рынках с низким объёмом торгов: если использовать режим исходных цен (INPUT_PRICE) в праздничные часы, когда рынок неликвиден и находится в боковом движении, веса могут слишком сильно подстраиваться под незначительный структурный шум. Эта проблема возникает из-за недостаточной дисперсии исходных данных. Эту проблему можно легко устранить, переключив режим преобразования на процентную доходность (INPUT_RET).
  • Зависимость от эпсилон для защиты от деления на ноль: на этапе нормировки NLMS скорость обучения делится на энергию входного сигнала. Если цена на рынке совсем перестаёт меняться, эта энергия приближается к нулю. Защита расчёта от критических ошибок деления на ноль полностью зависит от заданного вами значения эпсилон (inp_epsilon).


Раздел 6: Расширение возможностей индикатора

Фреймворк SAMA_NLMS.mq5 представляет собой отличную модульную основу для дальнейших количественных исследований:

  • Альтернативные конвейеры преобразования данных: перечисление inp_mode можно расширить. Вы можете легко интегрировать сложные математические преобразования. Например, перед передачей массивов в основной фильтр можно рассчитывать логарифмическую доходность цен, выполнять Z-нормализацию или удалять тренд из ценовых рядов.
  • Асимметричное штрафование ошибок: можно изменить цикл обновления весов таким образом, чтобы по-разному учитывать положительные и отрицательные ошибки. Эта настройка позволяет создать асимметричный адаптивный фильтр. Он быстрее отслеживает резкие распродажи на падающем рынке, чем постепенный рост на растущем рынке.
  • Иерархии рыночных режимов на нескольких таймфреймах: в рамках одного советника можно инициализировать несколько дескрипторов SAMA для разных таймфреймов. Данная структура позволяет использовать SAMA на графике H4 для определения направления макротренда, SAMA на графике H1 для отслеживания структурных откатов и SAMA на графике M15 для определения сигналов открытия позиций.


Заключение

После выполнения этой части у вас будет работающий индикатор SAMA_NLMS.mq5: он компилируется, отображает линию с окраской в зависимости от наклона и реализует защитные меры, необходимые для практического применения. В частности, индикатор проверяет соблюдение допустимых границ входных параметров, инициализирует веса в соответствии с нейтральным априорным распределением, обновляет веса только на закрытых барах (без обучения внутри бара), ограничивает экстремальные ошибки с помощью ATR, при необходимости нормализует вектор весов, чтобы выходные значения оставались в масштабе цен, и поддерживает масштабирование скорости обучения по коэффициенту эффективности Кауфмана. Эти меры снижают риск наиболее распространённых сбоев, но не устраняют фундаментальное свойство: веса SAMA зависят от траектории. Чтобы уменьшить расхождения между сеансами работы, используйте параметр числа баров прогрева для принудительного фонового обучения (по умолчанию — 500), отдавайте предпочтение INPUT_RET для обеспечения масштабной инвариантности на низколиквидных инструментах или инструментах с существенно различающимися масштабами цен и сохраняйте μ в диапазоне консервативных и сбалансированных значений (≤ 1,0), если вам не требуется намеренно очень агрессивное отслеживание.

Контрольный список перед практическим применением: скомпилируйте и загрузите SAMA_NLMS, убедитесь, что на формирующемся баре обучение не выполняется, проверьте поведение при повторном подключении или смене таймфрейма (используйте этап прогрева) и оцените чувствительность к параметрам (inp_mode, inp_filter_length, inp_learning_rate — скорость обучения, inp_leak, inp_error_atr_mult). В заключительной части мы количественно оценим, влияют ли эти особенности адаптации на результаты торговли: создадим советник для сравнительного тестирования SAMA и SMA/EMA/KAMA при согласованных параметрах, экспортируем диагностические данные в CSV и запустим конвейер анализа на Python для сравнения с базовыми вариантами и пошагового walk-forward-тестирования на нескольких активах.


Программа, использованная в статье:

# Название Тип Описание
1 SAMA_NLMS.mq5 Пользовательский индикатор Исходный код пользовательского индикатора, реализующего механизм адаптивной фильтрации на основе нормализованного метода наименьших средних квадратов. В нём предусмотрены встроенное ограничение ошибки с помощью ATR, режимы преобразования входных данных и динамическое масштабирование скорости обучения с использованием коэффициента эффективности Кауфмана.

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/23033

Прикрепленные файлы |
SAMA_NLMS.mq5 (11.79 KB)
Паттерн "Репозиторий" в MQL5: Абстрагирование доступа к торговой истории для обеспечения тестируемости логики советника Паттерн "Репозиторий" в MQL5: Абстрагирование доступа к торговой истории для обеспечения тестируемости логики советника
Прямые вызовы API торговой истории MQL5 внутри аналитических компонентов создают скрытые зависимости от терминала, из-за которых изолированное тестирование невозможно на уровне архитектуры. В данной статье создается слой абстракции ITradeRepository с реализациями CLiveTradeRepository и CMockTradeRepository, позволяющий одному и тому же аналитическому модулю и панели кривой средств одинаково работать как с текущими данными торгового счета, так и с детерминированным набором данных в памяти. Внедрение репозитория устраняет прямую связанность с API, позволяет проводить проверку в автономном режиме и при смене источника данных ограничивает необходимые изменения одним классом реализации.
Нейросети в трейдинге: AlphaGen — поиск торговых факторов (генерация формул) Нейросети в трейдинге: AlphaGen — поиск торговых факторов (генерация формул)
Продолжаем адаптацию AlphaGen для MetaTrader 5. В первой части мы реализовали правила, определяющие допустимые продолжения формулы. Теперь разбираем, как генератор OpenCL применяет эти правила и по оценкам модели выбирает токены, а вычислитель проверяет готовое выражение и рассчитывает значения торгового фактора по барам. Показываем, как по статусу результата отличить корректный ноль от пропуска из-за нехватки истории и распознать ошибку вычисления.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Обобщенный пул объектов в MQL5: Снижение накладных расходов на выделение памяти в высокочастотных индикаторах Обобщенный пул объектов в MQL5: Снижение накладных расходов на выделение памяти в высокочастотных индикаторах
В высокочастотных индикаторах MQL5, создающих объекты при каждом тике, накапливаются накладные расходы на выделение памяти и возникает джиттер времени выполнения в OnCalculate(). В этой статье создается обобщенный пул объектов на основе шаблона класса с использованием массива индексов списка свободных элементов, обеспечивающего выполнение операций Acquire() и Release() за время O(1). Архитектура предусматривает защиту от повторного возврата объекта в пул, строгое разделение состояния полезных данных и метаданных пула в методе Reset(), а также список свободных элементов фиксированной вместимости без резервного выделения памяти в куче. Пользовательский индикатор сравнивает два варианта обработки и с помощью GetMicrosecondCount() измеряет разницу накладных расходов на тик.