English
preview
Поиск торгового преимущества (Часть 2): Использование картирования дивергенций и трансформера временного слияния (TFT) в торговом роботе

Поиск торгового преимущества (Часть 2): Использование картирования дивергенций и трансформера временного слияния (TFT) в торговом роботе

MetaTrader 5 — Трейдинг |
16 0
Stephen Njuki
Stephen Njuki

Введение

Основная и типичная сложность технического анализа заключается не в выявлении трендов, а в том, чтобы успешно преодолевать переходные периоды между ними. Как избежать ложных пробоев? Мы полагаем, что для преодоления этих переходных периодов нужно заглянуть глубже и сосредоточиться на едва уловимых, скрытых изменениях скорости движения цены, предшествующих структурным изменениям. Объединяя картирование дивергенций со слоем внимания трансформера временного слияния (TFT), мы стремимся создать надежный альтернативный показатель для прогнозирования.

i1

Возможно, именно такое сочетание расчета наклонов и нелинейного последовательного взвешивания пока не получило широкого распространения. Для применения этого подхода ко многим доступным для торговли активам, несомненно, потребуются тщательное тестирование и настройка параметров. Данная статья служит лишь отправной точкой для более глубокого понимания и объединения этих двух механизмов при разработке сигналов входа в сделку. Нашим конечным результатом является торговый робот. На мой взгляд (это не официальное заявление MetaQuotes), советники с момента их появления в MQL5 примерно в 2009 году задумывались исключительно для позиционной торговли, без возможности одновременно исполнять ордера на покупку и продажу.

На мой взгляд, название «советник» было выбрано потому, что такие программы выдают вероятностные распределения прогнозов состояния рынка и сами по себе не привязаны к непосредственному выполнению торговых операций. Торговый робот, напротив, хотя тоже может использовать вероятностные распределения, создается без использования класса CExpertSignal языка MQL5 и допускает более гибкую настройку обработки ордеров, отличающуюся от принятой в MQL5 логики работы с позициями. С 2009 года наше представление о допустимых подходах к торговле на рынке расширилось: хотя многие трейдеры и сегодня придерживаются позиционной торговли, есть и те, кто использует хеджирование. Благодаря этой дополнительной гибкости торговый робот из данной статьи в сочетании с наработками из предыдущей и, надеюсь, будущих статей может стать основой для хранилища наших наработок. Такое «хранилище» позволит трейдеру совершенствовать свое торговое преимущество, избавляя его от рискованной чрезмерной зависимости от широко используемых индикаторов и систем, сильно коррелирующих между собой.


Парадигма «Дивергенция-TFT»

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

Подходящие рынки и ниши

Парадигма «Дивергенция-TFT» явно предназначена для устранения структурных сбоев, которые могут возникать при определенных торговых профилях и макроэкономических условиях. На современных рынках электронной торговли одним из основных недостатков количественных систем является их зависимость от индикаторов, между которыми наблюдается чрезмерная корреляция. Когда весь торговый отдел или набор алгоритмов использует производные математические модели, структурно зависящие от одинаковых базовых показателей — таких как скользящие средние, дисперсия или стандартные полосы волатильности, — точки исполнения сделок, как правило, концентрируются в одних и тех же областях. Такое совпадение может создавать зоны высокой концентрации ликвидности, на которые могут целенаправленно воздействовать крупные институциональные маркетмейкерские алгоритмы. Это, в свою очередь, усиливает охоту за стопами, которая часто сопровождается сериями ложных пробоев, способных нарушить работу традиционных систем следования за трендом.

i2

Парадигму «Дивергенция-TFT» можно использовать для защиты систем от рисков, связанных со скоплениями точек исполнения, где институциональные участники ведут безудержную охоту за стопами. Таким образом, в этих условиях схема с двумя механизмами предназначена для трех отдельных рыночных ниш: фильтрации волатильных свечей, например при торговле валютными кроссами; отслеживания скрытого накопления по историческим данным, например в периоды консолидации криптовалют; улучшения соотношения риска и прибыли за счет более раннего входа в позицию при отслеживании некоррелированного импульса.

Синергия

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

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

i4

Математические определения

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

Алгоритм

Этот механизм не рассматривает исходные значения индикаторов как абсолютные границы. Вместо этого он измеряет разность наклонов ценового ряда актива и вектора значений индикатора за заданное окно ретроспективного анализа N. Если обозначить через P вектор ценового ряда (например, цен закрытия баров), а через I — вектор значений индикатора (например, исходных показаний индикатора DeMarker или RSI), то сначала для цены и индикатора рассчитывается исходный наклон, характеризующий импульс, как простое конечное изменение за окно ретроспективного анализа N:

f1

f2

где:

  • Vector_0 (например, P_0, I_0) — это текущее значение соответствующего вектора (P или I).
  • Vector_N-1} (например, P_N-1, I_N-1) — это историческое значение того же вектора в начале заданного окна.
  • N — количество временных периодов (баров), определяющих длину окна ретроспективного анализа.

Поскольку масштаб цен может существенно различаться в зависимости от рассматриваемого актива (например, на рынке Форекс обычны изменения на доли пункта, а для биткоина — на тысячи долларов), исходный наклон цены необходимо нормировать относительно ее базового значения, чтобы его можно было математически сопоставлять со значениями осцилляторов с ограниченным диапазоном. Нормированный наклон цены рассчитывается по формуле:

f3

где:

  • NormSlope_P — это искомый нормированный наклон ценового ряда P.
  • Slope_P — исходный наклон ценового ряда, характеризующий импульс и определенный в предыдущих уравнениях.
  • P_N-1 — историческая цена в самом начале окна ретроспективного анализа, служащая базовым значением для этой нормализации.

Затем скрытое изменение импульса, или величину дивергенции (Delta_div), можно выделить, вычислив разность между этими двумя нормализованными траекториями:

f4

Таким образом, структурный сигнал подтверждается, если это значение дивергенции превышает заранее заданный параметр чувствительности:

f5

Сеть

Если бы в нашем распоряжении была полноценная среда промышленного применения глубокого обучения, то трансформер временного слияния (TFT) использовал бы механизмы многоголового внимания в сочетании с сетями выбора переменных для динамического определения важности последовательностей весов. В архитектуре этой упрощенной модели TFT, реализованной непосредственно на MQL5, мы лишь имитируем учет контекста последовательности, распределяя входные данные по отдельным временным состояниям. Это t_1, t_2 и t_3. Затем мы управляем вниманием. Если представить входы сети в виде векторных компонент, обозначающих отдельные временные горизонты, то:

  • t_1 — непосредственное, или текущее, временное состояние, отражающее скорость изменения в высокочастотном диапазоне
  • t_2 — предшествующее временное состояние, соответствующее нашей среднечастотной точке отсчета
  • t_3 — блок, отражающий импульс последовательности на макроуровне и соответствующий низкочастотному контексту

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

f6

где:

  • W — вектор статических весов многоголового внимания, задающий приоритеты последовательности на разных временных горизонтах.
  • q k_v_weight1 (0.55) — вес внимания, присваиваемый недавнему временному состоянию (t_1). Это обеспечивает выраженный приоритет недавних данных в векторе рыночных данных, формируемом в реальном времени.
  • q k_v_weight2 (0.30) — это вес внимания, присвоенный предыдущему состоянию (t_2), которое служит среднечастотной опорной точкой последовательности.
  • q k_v_weight3 (0.15) — это вес, присваиваемый самому отдалённому блоку импульса (t_3), который обеспечивает сети широкий исторический контекст.

Сводные оценки внимания для матрицы покупок (alpha_buy) и матрицы продаж (alpha_sell) рассчитываются путем обработки входных данных функцией, имитирующей активацию и разделяющей положительные и отрицательные компоненты импульса:

f7

f8

где:

  • alpha_buy — это сводная оценка внимания для матрицы покупок, получаемая суммированием положительных компонентов импульса.
  • alpha_sell — это общая оценка внимания для продажи, получаемая суммированием абсолютных величин отрицательных компонентов импульса.
  • n обозначает индекс конкретного временного интервала, который обрабатывается (n = 1 для t_1, n = 2 для t_2, n = 3 для t_3).
  • t_n — входящий сигнал дивергенции для n-го состояния.
  • max(0, t_n) — это фильтр активации, выделяющий только положительные (бычьи) значения. При этом отрицательные входные значения обнуляются.
  • |min(0, t_n)| — это фильтр активации, который выделяет только отрицательные (медвежьи) значения и возвращает их абсолютную величину.
  • W_n — соответствующий фиксированный вес многоголового внимания из вектора приоритетов W, присвоенный n-му состоянию.

Итоговая граница принятия решения обеспечивает строгое соблюдение условия взаимоисключения. Это означает, что сигнал пробоя возникает только тогда, когда оценка внимания для одного направления превышает заданный порог, а для противоположного остается ниже него:

f9


Реализация на MQL5

Чтобы перейти от абстрактной математики к работающей исполняемой модели, необходимо реализовать математические принципы в коде. В этом разделе мы последовательно разберем реализацию парадигмы «дивергенция — TFT» на MQL5. Каждая функция, каждый массив переменных и каждая логическая граница специально спроектированы так, чтобы сохранять целостность модели и ее торговое преимущество, описанные выше. Кроме того, мы стараемся минимизировать потребление памяти при выполнении.

Обоснование выбора индикаторов

При всем многообразии доступных технических индикаторов почему мы выбрали именно пару из RSI Уэллса Уайлдера и индикатора DeMarker? В нашем выборе всегда есть доля субъективности, а подход на основе дивергенций опирается на выявление структурных аномалий в соотношении цены и импульса. Тем не менее, не все индикаторы импульса одинаковы. Мы вполне осознанно сочетаем индикатор DeMarker с индексом относительной силы (RSI), поскольку они отражают «ортогональные измерения» скорости изменения рыночной цены.

F. Индекс относительной силы Уэллса Уайлдера — это непосредственная мера величины скорости изменения цены. Он сравнивает средний прирост цены за последовательные периоды со средним снижением за тот же интервал за заданный временной интервал. Поскольку RSI в значительной степени основан на ценах закрытия, он также служит косвенным показателем согласованной рыночной оценки на конец периода. Он отслеживает общую скорость развития тренда.

В отличие от него, индикатор Томаса Демарка основан на ином принципе. Этот индикатор ориентирован на экстремумы внутри периода, прежде всего на соотношение текущих максимума и минимума с максимумом и минимумом предыдущего периода. Он пытается оценить давление со стороны покупателей и продавцов, определяя, насколько текущий бар выходит за пределы структурных экстремумов предыдущего бара. Поскольку индикатор DeMarker слабо зависит от итоговой цены закрытия, он служит высокочувствительным косвенным показателем внутридневной ценовой напряженности и истощения движения.

Объединяя эти два индикатора, наш алгоритмический движок получает трехмерную картину рынка. RSI показывает скорость на макроуровне — скорость изменения согласованной рыночной оценки на закрытии, а индикатор DeMarker отражает истощение на микроуровне, то есть ослабление предельного давления покупателей или продавцов. Когда оба этих независимых друг от друга индикатора одновременно показывают расхождение с наклоном ценового ряда, статистическая вероятность ложного сигнала значительно снижается. По сути, в таком случае мы наблюдали бы уже не просто аномалию цен закрытия, а замедление движения рыночной цены, что создает идеальные условия для нашего метода картирования дивергенций.

Алгоритм картирования дивергенций

Первый компонент этой модели — механизм, отвечающий за выявление скрытых изменений импульса на основе рыночной цены. Этим занимается метод «CalculateDivergenceShift». Эта функция принимает исходные рыночные данные, зависящие от масштаба, и преобразует их в нормализованную разность, которую можно оценивать независимо от торгуемого актива.

//+------------------------------------------------------------------+
//| Mathematical Engine: Divergence Mapping (Slope Differential)     |
//+------------------------------------------------------------------+
double CalculateDivergenceShift(double &priceData[], double &indicatorData[])
  {
   int N = ArraySize(priceData);
   if(N < 2)
      return 0.0;

Эта функция принимает два динамических массива: «priceData» и «indicatorData». Важно отметить, что эти массивы передаются по ссылке, поэтому функция работает с исходными данными без создания их копии. При систематической торговле на низких таймфреймах передача больших массивов исторических данных может приводить к значительным накладным расходам памяти и задержкам в вычислениях. Это связано с тем, что при каждом вызове функции весь массив дублировался бы в памяти. Передача по ссылке позволяет алгоритму напрямую обращаться к адресам массивов в памяти, обеспечивая выполнение за микросекунды, что важно при обработке быстро поступающих тиковых данных и данных ценовых баров. Целое число N задает размер нашего динамического окна ретроспективного анализа. Начальная проверка «if(N &lt; 2)» служит защитной мерой: для вычисления наклона необходимы как минимум две точки во времени. Если буфер не заполняется, функция безопасно завершает работу, возвращая нейтральное нулевое значение.

//--- Calculate slope (momentum) of Price vs Indicator
   double priceSlope = (priceData[0] - priceData[N - 1]) / N;
   double indSlope = (indicatorData[0] - indicatorData[N - 1]) / N;

В приведенном выше листинге алгоритм вычисляет линейную аппроксимацию векторов. Самое последнее значение всегда хранится под индексом 0, а самое старое значение в окне — под индексом «N-1». Вычитая значение на дальней границе окна из текущего значения и деля результат на временной интервал «N», мы получаем исходный наклон. Таким образом, мы получаем абсолютную скорость изменения. Однако здесь может возникнуть серьезное математическое противоречие: значение «indSlope» ограничено диапазоном осциллятора — либо [0, 100], либо [0.0, 1.0]. С другой стороны, на «priceSlope» такие ограничения не распространяются: его значение зависит от торгуемого актива. Расчет наклона для такого волатильного актива, как BTC, может дать исходное значение порядка сотен, тогда как аналогичный расчет для пары GBPUSD — значение, близкое к 0.00001.

//--- Normalize Price Slope to match generic indicator scale roughly
   double normPriceSlope = priceSlope / priceData[N - 1];

Чтобы устранить это несоответствие размерностей, значение «priceSlope» необходимо существенно нормализовать. Если разделить исходную цену на историческую цену на границе окна с индексом N-1, мы сможем преобразовать абсолютное изменение цены в стандартизированную процентную разность. Наш подход исключает влияние номинальной стоимости актива, оставляя чистое представление геометрической скорости. Этот нормированный наклон теперь «сопоставим по размерности» с наклоном индикатора, о котором мы упоминали ранее.

//--- Differential represents the divergence (Hidden Momentum Shift)
   double divergenceShift = indSlope - normPriceSlope;
   return divergenceShift;
  }

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

Сеть упрощенной модели TFT

Выявив аномалии в структуре цены, мы теперь переходим к добавлению контекста к исходному потоку ценовых данных. В крупных сетях глубокого обучения трансформер временного слияния (TFT) использует отбор переменных и многоголовое внимание для обработки длинных последовательностей данных. Мы хотим воспроизвести часть этих механизмов средствами MQL5 в облегченной реализации, не требующей интеграции с внешней средой Python. Функция EvaluateTFTNetwork будет служить точной упрощенной моделью TFT, использующей имитируемые веса внимания.

//+------------------------------------------------------------------+
//| AI Engine: Temporal Fusion Transformer Proxy (Attention Network) |
//+------------------------------------------------------------------+
int EvaluateTFTNetwork(double t1_signal, double t2_signal, double t3_signal)
  {
//--- Simulating multi-head attention weights based on sequence importance
   double q_k_v_weight1 = 0.55; // Most recent temporal priority
   double q_k_v_weight2 = 0.30; // Mid temporal priority
   double q_k_v_weight3 = 0.15; // Furthest temporal priority

Наша сеть принимает на вход три отдельных временных состояния: t_1, t_2 и t_3. Они отражают соответственно недавнюю или текущую скорость изменения, предшествующий среднечастотный импульс и широкий исторический контекст. Веса q_k_v_weight помогают имитировать скалярные произведения матриц запросов, ключей и значений, используемых в полноценном блоке трансформера. Распределение этих весов всегда асимметрично. Самому недавнему состоянию t_1 присваивается вес 0.55, что создает сильное смещение в пользу недавних данных. Текущая структурная напряженность рынка часто оказывается наиболее значимой переменной с вероятностной точки зрения. Убывающие веса 0.30 и 0.15 для более давних состояний обеспечивают сети необходимый исторический контекст, не позволяя при этом «устаревшим» данным преобладать над потоком данных в реальном времени.

//--- Contextualizing outputs through a simulated activation
   double attentionBuy  = 0.0;
   double attentionSell = 0.0;
   if(t1_signal > 0)
      attentionBuy += t1_signal * q_k_v_weight1;
   else
      attentionSell += MathAbs(t1_signal) * q_k_v_weight1;
   if(t2_signal > 0)
      attentionBuy += t2_signal * q_k_v_weight2;
   else
      attentionSell += MathAbs(t2_signal) * q_k_v_weight2;
   if(t3_signal > 0)
      attentionBuy += t3_signal * q_k_v_weight3;
   else
      attentionSell += MathAbs(t3_signal) * q_k_v_weight3;

Эта последовательность операций реализует упрощенную функцию активации, которая в общих чертах имитирует выпрямленный линейный элемент (ReLU), сохраняя при этом направленное смещение. Сеть задействует две отдельные конкурирующие матрицы внимания: одну для суммирования бычьих сигналов, другую — для суммирования медвежьих сигналов. Каждый временной сигнал подвергается оценке. Если сигнал оказывается положительным, это указывает на наличие бычьей дивергенции. Величина этого сигнала умножается на соответствующий вес внимания, а затем прибавляется к накопленному значению в матрице «attentionBuy». Когда сигнал отрицательный, его абсолютная величина вычисляется с помощью функции «MathAbs()» и добавляется в конкурирующую матрицу «attentionSell». Благодаря такому строгому разделению наша сеть может отдельно отслеживать нарастание бычьего давления на протяжении всей последовательности, а не просто усреднять сигналы, рискуя потерять информацию о лежащем в их основе движении цены.

   bool buyBreakout = (attentionBuy >= InpAttentionThresh);
   bool sellBreakout = (attentionSell >= InpAttentionThresh);
   if(buyBreakout && !sellBreakout)
      return 1;
   if(sellBreakout && !buyBreakout)
      return -1;
   return 0;
  }

Заключительная часть приведенной выше функции служит границей принятия решения. Накопленные оценки внимания в обеих матрицах сравниваются с порогом, заданным параметром «InpAttentionThresh». Здесь с помощью условия взаимоисключения реализован важнейший защитный механизм. Чтобы наша сеть выдала сигнал на покупку (вернув 1), логическая переменная buyBreakout должна иметь значение «true», а переменная «sellBreakout» — одновременно значение «false». В периоды хаотичных рыночных движений и очень высокой волатильности значения в матрицах внимания для покупки и продажи могут одновременно резко возрастать. Такая распространенная ситуация, особенно при публикации важных макроэкономических новостей, приводит к тому, что наша сеть распознает «структурный хаос». При таком хаосе оба выходных сигнала — на покупку и на продажу — нейтрализуются, а возвращаемое значение устанавливается равным нулю.

Режимы работы

Чтобы можно было тщательно тестировать архитектуру и проверять ее компоненты по отдельности, предусмотрено 7 режимов работы. Режим задается значением перечисления «EMode», а соответствующая ему логика выполняется с помощью оператора switch в обработчике «OnTick()».

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Execute only on a new bar
   datetime timeCurrentBar = iTime(_Symbol, _Period, 0);
   if(timeCurrentBar == timeLastBar)
      
      return;
   int signal = 0; // 0 = No signal, 1 = Buy, -1 = Sell
//--- Route execution to the selected function mode
   switch(InpMode)
     {
      case MODE_DIVERG_PRICE:
         signal = ExecuteDivergencePrice();
         break;
      case MODE_DIVERG_DEMARKER:
         signal = ExecuteDivergenceDeMarker();
         break;
      case MODE_DIVERG_RSI:
         signal = ExecuteDivergenceRSI();
         break;
      case MODE_DIVERG_COMBINED:
         signal = ExecuteDivergenceCombined();
         break;
      case MODE_PRICE_ACTION_TFT:
         signal = ExecutePriceActionTFT();
         break;
      case MODE_INDICATOR_PATTERN:
         signal = ExecuteIndicatorPatternTFT();
         break;
      case MODE_HYBRID_FULL:
         signal = ExecuteHybridFull();
         break;
     }
//--- Process Trading Signals
   if(signal != 0)
     {
      ManagePositions(signal);
      timeLastBar = timeCurrentBar;
     }
  }

Перед вызовом алгоритма или сети функция «OnTick()» применяет строгую блокировку по времени. Мы сравниваем время текущего бара со значением «timeLastBar», и торговый робот гарантирует, что ресурсоемкие вычисления выполняются только один раз при открытии каждого нового бара. Это можно считать базовым требованием для большинства надежных торговых роботов. Без этого пришлось бы выполнять сложное картирование дивергенций на каждом входящем тике, что потребовало бы значительных ресурсов; кроме того, из-за «постоянной перерисовки сигналов» новые сигналы то появлялись бы, то исчезали, делая работу системы крайне нестабильной. Обработка при открытии бара означает, что наша модель опирается только на окончательно закрытые ценовые бары, то есть на неизменяемые исторические данные. Затем наша модель реализует эту логику в одном из семи доступных режимов. Один из них — базовый режим дивергенции, представленный ниже:

//+------------------------------------------------------------------+
//| Signal Generator: Mode 2 (MODE_DIVERG_RSI)                       |
//| Measures momentum divergence between Close price and RSI.        |
//+------------------------------------------------------------------+
int ExecuteDivergenceRSI()
  {
   double rsiVals[], closePrices[];
   ArrayResize(rsiVals, InpLookbackWindow);
   ArrayResize(closePrices, InpLookbackWindow);
   if(CopyBuffer(hRSI, 0, 0, InpLookbackWindow, rsiVals) < InpLookbackWindow)
      return 0;
   if(CopyClose(_Symbol, _Period, 0, InpLookbackWindow, closePrices) < InpLookbackWindow)
      return 0;
   double shift = CalculateDivergenceShift(closePrices, rsiVals);
   if(shift > InpDivergThreshold)
      return 1;
   if(shift < -InpDivergThreshold)
      return -1;
   return 0;
  }

В приведенном выше режиме «ExecuteDivergenceRSI» мы сначала задаем размеры динамических массивов в соответствии с выбранным во входных параметрах окном ретроспективного анализа. Затем функция «CopyBuffer» безопасно получает данные индикатора непосредственно через системный хендл, а «CopyClose» — вектор цен. Если данные не удается скопировать корректно, функция безопасно прерывается. Если выполнение не прерывается, массивы передаются в модуль картирования дивергенций, а полученный в итоге сдвиг сравнивается с порогом, заданным во входных параметрах. Однако, пожалуй, настоящие возможности нашей модели можно продемонстрировать в режиме 7:

//+------------------------------------------------------------------+
//| Signal Generator: Mode 6 (MODE_HYBRID_FULL)                      |
//| Blends full window divergence outputs across indicators via the  |
//| TFT network or mathematical threshold filtering.                 |
//+------------------------------------------------------------------+
int ExecuteHybridFull()
  {
   double closePrices[], rsiValues[], deMarkerValues[];
   ArrayResize(closePrices, InpLookbackWindow);
   ArrayResize(rsiValues, InpLookbackWindow);
   ArrayResize(deMarkerValues, InpLookbackWindow);
   if(CopyClose(_Symbol, _Period, 0, InpLookbackWindow, closePrices) < InpLookbackWindow)
      return 0;
   if(CopyBuffer(hRSI, 0, 0, InpLookbackWindow, rsiValues) < InpLookbackWindow)
      return 0;
   if(CopyBuffer(hDeMarker, 0, 0, InpLookbackWindow, deMarkerValues) < InpLookbackWindow)
      return 0;
   double pDiverg = CalculateDivergenceShift(closePrices, closePrices); // Price auto-divergence
   double rDiverg = CalculateDivergenceShift(closePrices, rsiValues);
   double dDiverg = CalculateDivergenceShift(closePrices, deMarkerValues);
   if(InpUseTFT)
     {
      //--- Feed mapping into the Neural Net
      return EvaluateTFTNetwork(dDiverg, rDiverg, pDiverg);
     }
   else
     {
      //--- Pure mathematical execution
      if(dDiverg > InpDivergThreshold && rDiverg > InpDivergThreshold)
         return 1;
      if(dDiverg < -InpDivergThreshold && rDiverg < -InpDivergThreshold)
         return -1;
     }
   return 0;
  }

В этом режиме алгоритм и сеть полностью объединяются. Вместо подачи исходных ценовых данных в нейронную сеть функция «ExecuteHybridFull» использует алгоритм как слой предварительной обработки и формирования признаков. Он вычисляет три отдельных профиля дивергенции: автодивергенцию самой цены, дивергенцию скорости RSI на макроуровне, а также истощение по индикатору DeMarker. Затем эти три значения, очищенные от шума, передаются в упрощенную модель TFT в качестве t_1, t_2 и t_3. По сути, наша сеть больше не анализирует ценовые графики, а тщательно изучает веса внимания конкурирующих аномалий в рыночной цене. Таким образом, сеть служит слоем проверки всех пробоев, передаваемых алгоритмом картирования дивергенций.

Затем только валидные сигналы передаются в функцию «ManagePositions()», которая также управляет исполнением ордеров.


Анализ после оптимизации

После этапа оптимизации мы проверяем эту модель с помощью форвардного тестирования. Мы используем валютную пару GBPJPY, а период оптимизации — с 01.01.2025 по 01.01.2026. Форвардное тестирование проводится за период с 01.01.2026 по 01.05.2026. Мы настраивали или оптимизировали следующие ключевые параметры:

  • Размер окна для картирования дивергенций (InpLookbackWindow)
  • Минимальная разность наклонов для подтверждения пробоя (InpDivergThreshold)
  • Порог оценки внимания для генерации сигналов (InpAttentionThresh)
  • Коэффициент затухания веса для исторических данных последовательности (InpSeqWeighting)

Впоследствии им были присвоены значения 44, 0.012, 0,6 и 0.3. Отчет о тестировании на исторических данных за период оптимизации выглядел следующим образом:

r_b

А отчет о последующем форвардном тестировании за первые четыре месяца 2026 года выглядел следующим образом:

r_f

Тестирование проводится на двухчасовом таймфрейме. Если провести тестирование за оба периода — оптимизации и форвардного тестирования, — мы получим следующую кривую эквити:

c

Результаты важного для нашей оценки форвардного тестирования можно обобщить с помощью пяти ключевых показателей, представленных в таблице ниже:

сводка

Если подробнее проанализировать эти результаты, охватывающие периоды оптимизации и форвардного тестирования, можно составить представление о потенциальных ограничениях нашей модели при торговле парой GBPJPY. По итогам 268 сделок наша модель обеспечила чистую прибыль в размере 2 625,80 долларов при общем профит-факторе 1,25. Еще одним важным параметром оптимизации, который мы настраивали, но не указали выше, было использование сети TFT: включать ее или нет. Интересно, что при лучших найденных настройках наша TFT не использовалась, а значит, все дополнительные усилия, вложенные в этот второй фильтр, оказались невостребованными! Однако это тестирование охватывает очень короткий период и лишь один торговый инструмент, поэтому, как всегда, я настаиваю: прежде чем выплескивать ребенка вместе с водой, следует провести тесты на других инструментах и более длительных временных интервалах.


Заключение:

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

Тем не менее, тщательная оптимизация на нескольких периодах и форвардное тестирование на паре GBPJPY на 2-часовом таймфрейме заставили нас трезво оценить ситуацию: при оптимальных параметрах, подобранных генетическим алгоритмом, сеть внимания упрощенной модели TFT вообще не использовалась. Модель опиралась исключительно на чисто математический механизм выявления дивергенций, что позволило совершить 268 сделок с чистой прибылью в 2600 долларов и просадкой чуть более 8 %. Однако этот результат не обязательно служит основанием для отказа от сети: скорее, он может указывать на то, что специализированные слои внимания требуют более широкой оценки на разных торговых инструментах и более длительных исторических окон, чтобы избежать ситуаций, когда алгоритм оптимизации отдает предпочтение более простым линейным структурам, а не чувствительным матрицам, учитывающим время.

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

имя Описание
02 Divergence TFT DeMarker RSI.mq5 Торговый робот с реализацией модели
i.set Входные параметры для оптимизации при тестировании

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

Прикрепленные файлы |
i.set (1.61 KB)
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Конструирование признаков для машинного обучения (Часть 10): Tесты на структурные сдвиги в MQL5 Конструирование признаков для машинного обучения (Часть 10): Tесты на структурные сдвиги в MQL5
Мы перенесли тесты на структурные сдвиги из главы 17 AFML в MQL5 в виде единого подключаемого файла CStructuralBreaks, предоставляющего советникам шесть признаков, индексируемых по барам: статистику CSW и ее критическое значение, тест Дики — Фуллера типа Чоу (DFC), SADF со скользящим окном с заданной глубиной истории (по умолчанию 252 бара), SM-Exp и SM-Power. В SADF используются скользящие окна с вычислительной сложностью O(L²), что позволяет выполнять расчеты в реальном времени. Сопутствующий индикатор StructuralBreaksViewer строит графики всех рядов данных с возможностью настройки видимости каждого ряда и опциональной нормализацией по z-оценке. SB_EMPTY отмечает недопустимые значения для обеспечения безопасной интеграции.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Архитектура машинного обучения для MetaTrader 5 (Часть 19): Режимы бэггинга Архитектура машинного обучения для MetaTrader 5 (Часть 19): Режимы бэггинга
Мы проверяем утверждение AFML о том, что последовательный бутстрэп снижает корреляцию между деревьями в ансамбле бэггинга на метках метода тройного барьера с перекрывающимися временными интервалами, раздельно исследуя два фактора: число наблюдений в выборке и правило отбора. Бэггинг с одной и той же базовой моделью — деревом решений — применяется в четырёх режимах формирования выборки строк и оценивается на данных EURUSD за 2022–2023 годы по уникальности сформированных выборок, корреляции между деревьями, AUC и калибровке вероятностных прогнозов. Снижение корреляции почти полностью достигается за счёт ограничения max_samples до уровня средней уникальности; последовательный бутстрэп даёт лишь небольшой дополнительный эффект. Завышение OOB-оценки наиболее выражено при последовательном бутстрэпе с полным числом извлечений.