English
preview
Рекуррентный количественный анализ (RQA) в MQL5: Разработка полноценной библиотеки анализа

Рекуррентный количественный анализ (RQA) в MQL5: Разработка полноценной библиотеки анализа

MetaTrader 5Индикаторы |
136 0
Hammad Dilber
Hammad Dilber

Содержание

  1. Введение
  2. RQA на графике
  3. Рекуррентность: основная идея
  4. Метрики
  5. Выбор эпсилона (epsilon)
  6. Архитектура библиотеки
  7. CRQAMatrix: встраивание и рекуррентная матрица
  8. CRQAMetrics: количественная оценка структуры
  9. CRQAEpsilon: автоматический выбор порога
  10. CRQAWindow: скользящий анализ
  11. CRQA: фасадный класс
  12. Индикатор RQA
  13. Что дальше
  14. Заключение


Введение

Многие индикаторы, доступные в MetaTrader 5, являются линейными, но с их помощью трудно проверять гипотезы из нелинейной динамики, например: «возвращается ли рынок к ранее наблюдавшимся состояниям и по каким закономерностям?». Рекуррентный количественный анализ (RQA) даёт конкретные числовые метрики — DET, LAM, ENTR, TREND и другие, — которые количественно описывают детерминизм, ламинарность, сложность и нестационарность временного ряда. Однако до сих пор не было готового модульного набора средств RQA для MQL5, который можно добавить в проект MetaTrader 5, настроить параметры (встраивание, норму, эпсилон) и запускать как для однократного расчёта, так и для скользящего анализа, формирующего временной ряд метрик и выводящего их на график.

Эта статья восполняет этот пробел. В ней представлена практическая реализация на MQL5, рассчитанная на использование с любым символом и таймфреймом: модульная библиотека с понятным API, автоматический выбор эпсилона (включая бисекцию по целевому RR), механизм скользящего окна, пример скрипта и индикатор, который отображает текущие метрики RQA. Цель — повысить инженерную продуктивность: вместо повторной реализации RQA с нуля вы получаете проверенный вычислительный слой, который можно сразу включить в эксперименты, а позже — в торговую логику.


RQA на графике

Прежде чем углубляться в теорию или код, начнём с результата. На изображении ниже показан индикатор RQA, работающий на графике в реальном времени в MetaTrader 5. Он выводит пять метрик в отдельном окне под ценовым графиком: коэффициент рекуррентности (RR), детерминизм (DET), ламинарность (LAM), энтропию Шеннона (ENTR) и тренд.

Пример индикатора RQA на EURUSD

Рис. 1 — Индикатор RQA на EURUSD H1

Каждая из этих линий вычисляется по скользящему окну ценовых данных. На каждом баре библиотека берёт фрагмент последних цен закрытия, строит рекуррентную матрицу, подсчитывает содержащиеся в ней закономерности и возвращает метрики. На графике вы видите эволюцию этих метрик во времени. Всплески, падения и устойчивые уровни могут быть информативными. Но в этой статье мы не будем их интерпретировать. Сейчас важно следующее: библиотека, которую мы собираемся построить, обеспечивает именно такой результат. Остальная часть статьи объясняет, как это работает.


Рекуррентность: основная идея

RQA начинается с понятия из теории динамических систем, называемого реконструкцией фазового пространства. Эта идея, формализованная Такенсом (1981), состоит в том, что существенную динамику системы можно восстановить по одной наблюдаемой переменной, если выполнить встраивание с правильными параметрами. Для ценового временного ряда это означает преобразование последовательности цен закрытия в набор векторов более высокой размерности.

Встраивание с временной задержкой

Для временного ряда x(1), x(2), ..., x(N) мы строим векторы встраивания, объединяя каждое значение с его задержанными копиями. Для размерности встраивания m и временной задержки tau, каждый вектор встраивания выглядит так:

v(i) = [ x(i), x(i + tau), x(i + 2*tau), ..., x(i + (m-1)*tau) ]

При m = 1 и tau = 1 встраивание фактически не выполняется. Каждый «вектор» — это просто одно ценовое значение. При m = 2 и tau = 1 каждый вектор представляет собой пару последовательных цен: [x(i), x(i+1)]. Эту пару можно изобразить как точку в двумерном пространстве. Если выполнить это для всех допустимых индексов, получится траектория в фазовом пространстве. Форма этой траектории выявляет структуру, которую исходный временной ряд сам по себе не показывает.

Количество допустимых векторов встраивания равно N - (m - 1) * tau. Чем больше размерность встраивания или задержка, тем меньше векторов можно сформировать из тех же данных. Это важное практическое ограничение при работе с короткими окнами.

Встраивание с временной задержкой

Рис. 2 — Схема встраивания с временной задержкой

Матрица расстояний

После получения векторов встраивания нужен способ измерить их сходство. Мы вычисляем расстояние между каждой парой векторов v(i) и v(j). В результате получается матрица расстояний N x N. Библиотека поддерживает три нормы расстояния:

  • Евклидова норма: стандартное прямолинейное расстояние. Используется по умолчанию.
  • Максимум-норма (Чебышёва): берёт наибольшую абсолютную разницу по всем измерениям. Часто используется в литературе по RQA, поскольку задаёт окрестности в виде гиперкубов.
  • Манхэттенская норма (L1): сумма абсолютных разностей. Вычислительно дешевле евклидовой, так как не требует извлечения квадратного корня.

Пороговая обработка: рекуррентная матрица

Последний шаг — применить порог. Для заданного расстояния epsilon мы считаем два состояния рекуррентными, если расстояние между ними меньше или равно epsilon. Это превращает матрицу расстояний в бинарную матрицу: 1 — состояния повторяются, 0 — не повторяются. Эта бинарная матрица является рекуррентной матрицей, и она служит основой для всего последующего анализа.

R(i, j) = 1, если distance(v(i), v(j)) ≤ epsilon, иначе 0

Рекуррентная матрица симметрична (если состояния i и j находятся в отношении рекуррентности, то это отношение симметрично) и содержит единицы на главной диагонали (каждое состояние идентично самому себе). Если визуализировать эту матрицу как изображение с чёрными точками для 1 и белыми для 0, получится рекуррентная диаграмма. Видимые на ней закономерности и являются тем, что RQA количественно оценивает.

Построение рекуррентной матрицы

Рис. 3 — Пример рекуррентной диаграммы

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


Метрики

RQA извлекает из рекуррентной матрицы двенадцать метрик. Каждая из них отражает отдельный аспект динамики системы. Вместо разбора каждой метрики в отдельных абзацах ниже приведена компактная справочная таблица. После неё мы подробнее остановимся на самых важных метриках.

Метрика
Символ
Формула
Что измеряет
Коэффициент рекуррентности
RR
(рекуррентные точки) / (N^2 - N)
Насколько часто система возвращается к предыдущим состояниям. Более высокий RR означает большую самоподобность данных.
Детерминизм
DET
(точки на диагональных линиях >= lmin) / (все рекуррентные точки)
Доля рекуррентностей, образующих диагональные линии. Высокий DET означает предсказуемую динамику, подчинённую правилам.
Ламинарность
LAM
(точки на вертикальных линиях >= vmin) / (все рекуррентные точки)
Доля рекуррентностей, образующих вертикальные линии. Высокий LAM означает, что система задерживается в состояниях.
Время захвата
TT
Средняя длина вертикальных линий
Как долго в среднем система остаётся захваченной в состоянии.
Средняя диагональная линия
L
Средняя длина диагональных линий >= lmin
Средняя длительность предсказуемых (детерминированных) сегментов.
Максимальная диагональная линия
Lmax
Самая длинная диагональная линия (искл. главную диагональ)
Самый длинный наблюдаемый участок детерминированного поведения.
Максимальная вертикальная линия
Vmax
Самая длинная вертикальная линия
Самый длинный период, в течение которого система оставалась захваченной.
Энтропия Шеннона
ENTR
-sum(p(l) * ln(p(l)))
Сложность распределения длин диагональных линий. Более высокая энтропия означает большее разнообразие длин линий.
Дивергенция
DIV
1 / Lmax
Величина, обратная самой длинной диагонали. Приближённая оценка наибольшего показателя Ляпунова.
Отношение
RATIO
DET / RR
Детерминизм, нормированный на коэффициент рекуррентности. Устраняет влияние общей плотности рекуррентности.
Тренд
TREND
Наклон регрессии диагональной плотности рекуррентности
Дрейф или нестационарность данных. Положительный тренд означает, что рекуррентность со временем растёт.
Сложность
COMPLEXITY
RR * DET
Составная мера. Высока, когда данные одновременно сильно рекуррентны и сильно детерминированы.

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

DET (детерминизм) — возможно, самая информативная одиночная метрика. Она отвечает на вопрос: сколько из всех рекуррентных точек матрицы образуют диагональные линии длиной не меньше lmin? Диагональные линии появляются, когда два сегмента временного ряда развиваются параллельно несколько шагов. Это происходит только в детерминированных системах, где текущее состояние ограничивает следующее. Случайные данные дают разрозненные рекуррентные точки, но очень мало диагональных линий, поэтому DET остаётся низким. Трендовый или средневозвратный рынок, следующий распознаваемым закономерностям, покажет высокий DET.

LAM (ламинарность) — вертикальный аналог DET. Вертикальные линии на рекуррентной диаграмме появляются, когда система остаётся рядом с одним и тем же состоянием несколько последовательных временных шагов. В применении к рыночным данным это может соответствовать консолидации, узким диапазонам или периодам, когда цена почти не движется. Всплеск LAM часто сигнализирует, что рынок вошёл в режим консолидации.

ENTR (энтропия Шеннона) измеряет сложность распределения длин диагональных линий. Если все диагональные линии имеют одинаковую длину, энтропия низкая. Если длины распределены по многим значениям, энтропия высокая. В терминах RQA высокая энтропия указывает на систему с богатой, разнообразной детерминированной структурой, а не на простое повторение.

TREND фиксирует нестационарность. Он вычисляет наклон линейной регрессии плотности рекуррентности по диагоналям, параллельным главной диагонали. Положительный тренд означает, что близкие по времени состояния с большей вероятностью рекуррентны друг с другом, чем состояния, сильно разнесённые во времени; это признак структурного изменения или дрейфа базовой динамики.


Выбор эпсилона

Эпсилон — это порог, определяющий, что считается «достаточно близким» для рекуррентности. Это самый важный параметр в любом RQA-анализе, и неправильный выбор приводит к вводящим в заблуждение результатам. Если он слишком мал, рекуррентная диаграмма почти пуста, и метрикам нечего будет анализировать. Если слишком велик, диаграмма почти полностью заполнена; всё выглядит рекуррентным, а вся структура размывается.

Выбор эпсилона

Рис. 4 — Сравнение эпсилона

Универсального «правильного» значения не существует. Эпсилон зависит от масштаба данных, параметров встраивания и того, что вы пытаетесь обнаружить. Библиотека предоставляет четыре стратегии его выбора:

1. Фиксированное значение (EPSILON_FIXED)

Вы задаёте значение эпсилона напрямую. Это полезно, когда вы знаете масштаб данных и имеете предметно обоснованное значение. Для нормализованных или стандартизованных данных типичными начальными значениями являются 0.1–0.5.

2. Доля стандартного отклонения (EPSILON_STD_FRACTION)

Значение эпсилона задаётся как доля стандартного отклонения ряда. Например, при параметре 0.1 значение эпсилона становится равным 10% стандартного отклонения. Такой подход естественно масштабируется вместе с волатильностью. Формула проста:

epsilon = param * std(series)

3. Доля диапазона (EPSILON_RANGE_FRACTION)

Значение эпсилона задаётся как доля диапазона ряда (максимум минус минимум). При параметре 0.05 значение эпсилона равно 5% общего диапазона. Этот подход менее чувствителен к выбросам, чем метод стандартного отклонения, но может искажаться одним экстремальным значением.

epsilon = param * (max(series) - min(series))

4. Целевой коэффициент рекуррентности (EPSILON_RR_TARGET)

Это наиболее принципиальный подход. Вы задаёте целевой RR (обычно от 0.01 до 0.10), а библиотека с помощью бинарного поиска подбирает эпсилон, который даёт примерно такой коэффициент рекуррентности. Реализация не строит полную рекуррентную матрицу при каждой пробе. Вместо этого используется приближённая оценка по подвыборке: расстояния оцениваются на сетке выбранных пар точек, вычисляется доля рекуррентных пар, после чего эпсилон корректируется. После 40 итераций бисекции значение эпсилона сходится к величине, дающему очень близкий к целевому RR результат.

Метод целевого RR используется в библиотеке по умолчанию. Целевой RR = 0.05 (5%) — распространённая рекомендация в литературе по RQA и хороший стартовый вариант для финансовых данных.


Архитектура библиотеки

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

Архитектура библиотеки

Рис. 5 — Архитектура библиотеки RQA

Файл
Класс
Назначение
RQAMatrix.mqh
CRQAMatrix
Встраивание с временной задержкой, вычисление расстояний и построение бинарной рекуррентной матрицы N x N.
RQAMetrics.mqh
CRQAMetrics
Подсчёт диагональных и вертикальных линий, энтропия Шеннона, вычисление тренда. Заполняет структуру SRQAResult всеми двенадцатью метриками.
RQAEpsilon.mqh
CRQAEpsilon
Автоматический выбор эпсилона по четырём стратегиям: фиксированное значение, доля стандартного отклонения, доля диапазона и бисекция по целевому RR.
RQAWindow.mqh
CRQAWindow
Применяет RQA в скользящем окне, формируя временной ряд структур SRQAResult. Включает статические методы извлечения отдельных метрик.
RQA.mqh
CRQA
Фасадный класс верхнего уровня. Настраивает CRQAEpsilon, CRQAMatrix и CRQAMetrics и связывает их в единый вызов Compute().

Работу классов поддерживают две структуры данных:

  • SRQAResult хранит значения всех двенадцати метрик и метод Reset(). Эту структуру заполняет CRQAMetrics, и её вы читаете после вычисления.
  • SRQAWindowResult связывает SRQAResult с индексом бара, указывая, к какому окну относятся метрики.

Два перечисления задают параметры конфигурации:

  • ENUM_RQA_NORM выбирает норму расстояния: евклидову, максимальную (Чебышёва) или манхэттенскую.
  • ENUM_EPSILON_METHOD выбирает стратегию выбора эпсилона: фиксированное значение, целевой RR, доля стандартного отклонения или доля диапазона.

Для использования библиотеки нужен только один include:

#include <RQA\RQA.mqh>

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


CRQAMatrix: встраивание и рекуррентная матрица

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

Структура класса

Класс хранит параметры встраивания, развёрнутые векторы встраивания и развёрнутую булеву рекуррентную матрицу. Оба массива хранятся как одномерные массивы с индексацией по строкам: элемент (i, j) соответствует индексу i * N + j.

class CRQAMatrix
  {
private:
   int               m_N;           // number of embedded vectors
   int               m_embDim;      // embedding dimension
   int               m_delay;       // time delay (tau)
   double            m_epsilon;     // threshold
   ENUM_RQA_NORM     m_norm;        // distance norm
   bool              m_R[];         // flattened NxN boolean matrix
   double            m_embedded[];  // flattened embedded vectors [N x embDim]
public:
   bool              Build(const double &series[], int seriesLen,
                           double epsilon,
                           int embDim      = 1,
                           int delay       = 1,
                           ENUM_RQA_NORM norm = RQA_NORM_EUCLIDEAN);
   bool              Get(int i, int j) const;
   int               Size() const;
  };

Встраивание с временной задержкой

Метод Embed() строит векторы встраивания из исходного ряда. Количество допустимых векторов равно N = seriesLen - (embDim - 1) * delay. Каждый вектор хранится как непрерывный блок из embDim значений в развёрнутом массиве m_embedded.

void CRQAMatrix::Embed(const double &series[], int seriesLen)
  {
   m_N = seriesLen - (m_embDim - 1) * m_delay;
   if(m_N <= 0)
     {
      m_N = 0;
      return;
     }
   ArrayResize(m_embedded, m_N * m_embDim);
   for(int i = 0; i < m_N; i++)
      for(int d = 0; d < m_embDim; d++)
         m_embedded[i * m_embDim + d] = series[i + d * m_delay];
  }

Шаблон индексации series[i + d * m_delay] берёт значения, разнесённые на tau. Для размерности встраивания 2 и задержки 1 вектор i содержит [series[i], series[i+1]]. Для размерности 3 и задержки 2 он содержит [series[i], series[i+2], series[i+4]]. Параметр delay управляет тем, насколько далеко во времени расположены координаты каждого вектора, что влияет на то, какую часть динамики системы захватывает каждый вектор.

Вычисление расстояния

Метод Distance() вычисляет расстояние между двумя векторами встраивания согласно выбранной норме. Все три нормы проходят по размерностям встраивания и по-разному накапливают результат.

double CRQAMatrix::Distance(int i, int j) const
  {
   double dist = 0.0;
   for(int d = 0; d < m_embDim; d++)
     {
      double diff = m_embedded[i * m_embDim + d]
                 - m_embedded[j * m_embDim + d];
      switch(m_norm)
        {
         case RQA_NORM_MAX:
            dist = MathMax(dist, MathAbs(diff));
            break;
         case RQA_NORM_MANHATTAN:
            dist += MathAbs(diff);
            break;
         case RQA_NORM_EUCLIDEAN:
         default:
            dist += diff * diff;
            break;
        }
     }
   if(m_norm == RQA_NORM_EUCLIDEAN)
      dist = MathSqrt(dist);
   return dist;
  }

Для евклидовой нормы метод накапливает квадраты разностей и в конце извлекает квадратный корень. Максимум-норма поддерживает текущее максимальное значение абсолютной разности. Манхэттенская норма напрямую суммирует абсолютные разности. Выбор нормы влияет на форму окрестности вокруг каждой точки: евклидова задаёт сферы, максимум-норма — кубы, манхэттенская — ромбы.

Построение матрицы

Метод Build() — основная точка входа. Он проверяет параметры, выполняет встраивание, выделяет логическую матрицу и заполняет её, сравнивая каждую пару векторов встраивания с эпсилоном.

bool CRQAMatrix::Build(const double &series[], int seriesLen,
                      double epsilon,
                      int embDim,
                      int delay,
                      ENUM_RQA_NORM norm)
  {
   if(seriesLen < 2 || epsilon <= 0.0
     || embDim < 1 || delay < 1)
     {
      Print("RQAMatrix::Build - invalid parameters");
      return false;
     }
   m_epsilon = epsilon;
   m_embDim  = embDim;
   m_delay   = delay;
   m_norm    = norm;
   Embed(series, seriesLen);
   if(m_N <= 0)
     {
      Print("RQAMatrix::Build - series too short");
      return false;
     }
   ArrayResize(m_R, m_N * m_N);
   for(int i = 0; i < m_N; i++)
      for(int j = 0; j < m_N; j++)
         m_R[i * m_N + j] = (Distance(i, j) <= m_epsilon);
   return true;
  }

Вложенный цикл выполняет N^2 итераций, каждая из которых вызывает Distance(). Для окна в 50 баров при размерности встраивания 1 это 2 500 вызовов расстояния. Для 100 баров — 10 000. Для 200 баров — 40 000. Стоимость растёт квадратично относительно числа векторов встраивания, и это главное ограничение производительности библиотеки. Матрица хранится как плоский логический массив, а не как двумерная структура — ради эффективности памяти и потому, что MQL5 предсказуемее работает с одномерными массивами.

Метод Get() предоставляет доступ к матрице с проверкой границ:

bool CRQAMatrix::Get(int i, int j) const
  {
   if(i < 0 || i >= m_N || j < 0 || j >= m_N)
      return false;
   return m_R[i * m_N + j];
  }

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


CRQAMetrics: количественная оценка структуры

CRQAMetrics принимает построенную CRQAMatrix и извлекает из неё все двенадцать метрик. Это самый вычислительно насыщенный модуль библиотеки. Он сканирует каждую диагональ и каждый столбец матрицы, подсчитывает линейные структуры, вычисляет их распределения и получает сводные статистики.

Структура SRQAResult

Все метрики хранятся в одной структуре с методом Reset(), который обнуляет все поля. Эта структура является форматом обмена данными между модулем метрик и остальной библиотекой.

struct SRQAResult
  {
   double   RR;
   double   DET;
   double   LAM;
   double   TT;
   double   L;
   double   Lmax;
   double   Vmax;
   double   ENTR;
   double   DIV;
   double   RATIO;
   double   TREND;
   double   COMPLEXITY;
   void     Reset()
     {
      RR=0; DET=0; LAM=0; TT=0; L=0;
      Lmax=0; Vmax=0; ENTR=0; DIV=0;
      RATIO=0; TREND=0; COMPLEXITY=0;
     }
  };

Подсчёт диагональных линий

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

void CRQAMetrics::CountDiagonals(const CRQAMatrix &mat,
                                int &lineLengths[]) const
  {
   int N = mat.Size();
   ArrayResize(lineLengths, N + 1);
   ArrayInitialize(lineLengths, 0);
   for(int diag = -(N - 1); diag <= (N - 1); diag++)
     {
      if(diag == 0) continue;
      int len = 0;
      int iStart = MathMax(0, -diag);
      int iEnd   = MathMin(N - 1, N - 1 - diag);
      for(int i = iStart; i <= iEnd; i++)
        {
         int j = i + diag;
         if(mat.Get(i, j))
            len++;
         else
           {
            if(len >= m_minDiagLine && len < N)
               lineLengths[len]++;
            len = 0;
           }
        }
      if(len >= m_minDiagLine && len < N)
         lineLengths[len]++;
     }
  }

Метод проходит по всем 2*(N-1) диагоналям (N-1 выше главной диагонали и N-1 ниже). Для каждой диагонали он идёт по ячейкам, отслеживая текущую длину серии. Когда встречается нерекуррентная ячейка, накопленная серия (если она достаточно длинная) записывается в гистограмму lineLengths. Гистограмма индексируется длиной линии: lineLengths[3] показывает, сколько диагональных линий длиной 3 найдено. Условие len < N исключает любую линию, растянутую на всю ширину матрицы, что соответствовало бы тривиальному самоподобию.

Подсчёт вертикальных линий

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

void CRQAMetrics::CountVerticals(const CRQAMatrix &mat,
                                int &lineLengths[]) const
  {
   int N = mat.Size();
   ArrayResize(lineLengths, N + 1);
   ArrayInitialize(lineLengths, 0);
   for(int j = 0; j < N; j++)
     {
      int len = 0;
      for(int i = 0; i < N; i++)
        {
         if(mat.Get(i, j))
            len++;
         else
           {
            if(len >= m_minVertLine)
               lineLengths[len]++;
            len = 0;
           }
        }
      if(len >= m_minVertLine)
         lineLengths[len]++;
     }
  }

Обратите внимание, что счётчик вертикальных линий не применяет ограничение len < N, используемое диагональным счётчиком. Вертикальная линия, проходящая через всю высоту столбца, является допустимым наблюдением: это означает, что в конкретный момент времени j состояние системы было близко ко всем остальным состояниям ряда. Это необычно, но не бессмысленно.

Энтропия Шеннона

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

double CRQAMetrics::ShannonEntropy(const int &lengths[],
                                  int total) const
  {
   if(total == 0) return 0.0;
   double entr = 0.0;
   int    sz   = ArraySize(lengths);
   for(int l = 0; l < sz; l++)
     {
      if(lengths[l] > 0)
        {
         double p = (double)lengths[l] / total;
         entr -= p * MathLog(p);
        }
     }
   return entr;
  }

Функция вычисляет стандартную формулу энтропии Шеннона: H = -sum(p(l) * ln(p(l))), где p(l) — доля диагональных линий длины l. Параметр total — это общее число диагональных линий (не точек), поэтому каждое p(l) является корректной вероятностью. Используется натуральный логарифм, поэтому энтропия выражается в натах.

Вычисление тренда

Метрика TREND выявляет нестационарность, вычисляя наклон линейной регрессии плотности рекуррентности по верхним диагоналям.

double CRQAMetrics::ComputeTrend(const CRQAMatrix &mat) const
  {
   int N = mat.Size();
   if(N < 4) return 0.0;
   int numDiag = N - 1;
   double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
   for(int d = 1; d < N; d++)
     {
      int count = 0, total = N - d;
      for(int i = 0; i < total; i++)
         if(mat.Get(i, i + d)) count++;
      double density = (total > 0)
                   ? (double)count / total : 0.0;
      double x = (double)(d - numDiag / 2);
      sumX  += x;
      sumY  += density;
      sumXY += x * density;
      sumX2 += x * x;
     }
   double denom = (double)numDiag * sumX2 - sumX * sumX;
   if(MathAbs(denom) < 1e-12) return 0.0;
   return ((double)numDiag * sumXY - sumX * sumY) / denom;
  }

Для каждого диагонального смещения d (от 1 до N-1) метод вычисляет долю рекуррентных точек на этой диагонали. Диагонали рядом с главной соответствуют близким временным индексам. Далёкие от главной диагонали сравнивают состояния, сильно разнесённые во времени. Линейная регрессия подгоняет прямую к этим значениям плотности как функции центрированного смещения. Положительный наклон означает, что близкие состояния более рекуррентны, чем далёкие, указывая на изменение динамики системы во времени.

Основной метод Compute

Метод Compute() объединяет всё вместе. Он подсчитывает рекуррентные точки (исключая главную диагональ), запускает оба счётчика линий и вычисляет все двенадцать метрик.

bool CRQAMetrics::Compute(const CRQAMatrix &mat,
                         SRQAResult &result) const
  {
   result.Reset();
   int N = mat.Size();
   if(N < 2) return false;
   //--- 1. Recurrence Rate
   long recCount = 0;
   long total    = (long)N * N - N;
   for(int i = 0; i < N; i++)
      for(int j = 0; j < N; j++)
         if(i != j && mat.Get(i, j))
            recCount++;
   result.RR = (total > 0)
              ? (double)recCount / total : 0.0;
   //--- 2. Diagonal metrics
   int diagLengths[];
   CountDiagonals(mat, diagLengths);
   long  diagPoints = 0, totalDiagLines = 0;
   int   lmax = 0;
   int   sz = ArraySize(diagLengths);
   for(int l = m_minDiagLine; l < sz; l++)
     {
      if(diagLengths[l] > 0)
        {
         diagPoints     += (long)l * diagLengths[l];
         totalDiagLines += diagLengths[l];
         if(l > lmax) lmax = l;
        }
     }
   result.Lmax = (double)lmax;
   result.DIV  = (lmax > 0) ? 1.0 / lmax : 0.0;
   result.DET  = (recCount > 0)
                ? (double)diagPoints / recCount : 0.0;
   result.L    = (totalDiagLines > 0)
                ? (double)diagPoints / totalDiagLines : 0.0;
   result.ENTR = ShannonEntropy(diagLengths,
                              (int)totalDiagLines);
   //--- 3. Vertical metrics
   int vertLengths[];
   CountVerticals(mat, vertLengths);
   long vertPoints = 0, totalVertLines = 0;
   int  vmax = 0;
   int  vsz = ArraySize(vertLengths);
   for(int l = m_minVertLine; l < vsz; l++)
     {
      if(vertLengths[l] > 0)
        {
         vertPoints     += (long)l * vertLengths[l];
         totalVertLines += vertLengths[l];
         if(l > vmax) vmax = l;
        }
     }
   result.Vmax = (double)vmax;
   result.LAM  = (recCount > 0)
                ? (double)vertPoints / recCount : 0.0;
   result.TT   = (totalVertLines > 0)
                ? (double)vertPoints / totalVertLines : 0.0;
   //--- 4. Derived
   result.RATIO      = (result.RR > 1e-12)
                      ? result.DET / result.RR : 0.0;
   result.COMPLEXITY = result.RR * result.DET;
   result.TREND      = ComputeTrend(mat);
   return true;
  }

Метод выполняется в четыре этапа. Сначала он подсчитывает все рекуррентные точки (исключая главную диагональ) для вычисления RR. Затем запускает CountDiagonals() и получает DET, L, Lmax, ENTR и DIV из гистограммы диагональных линий. Затем запускает CountVerticals() и получает LAM, TT и Vmax. На четвёртом этапе вычисляются три производные метрики: RATIO, COMPLEXITY и TREND. Полное число в знаменателе для RR равно N^2 - N (все внедиагональные ячейки), то есть главная диагональ исключается из расчёта коэффициента рекуррентности.


CRQAEpsilon: автоматический выбор порога

CRQAEpsilon — статический служебный класс. Он не хранит состояние. Вы вызываете CRQAEpsilon::Select() с рядом, методом и параметром, а он возвращает вычисленный эпсилон. Класс также содержит три закрытых вспомогательных метода: SeriesStdDev(), SeriesRange() и ApproxRR().

Метод Select

double CRQAEpsilon::Select(const double &series[], int N,
                          ENUM_EPSILON_METHOD method,
                          double param)
  {
   switch(method)
     {
      case EPSILON_FIXED:
         return param;
      case EPSILON_STD_FRACTION:
         return param * SeriesStdDev(series, N);
      case EPSILON_RANGE_FRACTION:
         return param * SeriesRange(series, N);
      case EPSILON_RR_TARGET:
        {
         double lo = 0.0;
         double hi = SeriesRange(series, N);
         if(hi < 1e-12) return 1e-6;
         for(int iter = 0; iter < 40; iter++)
           {
            double mid = (lo + hi) * 0.5;
            double rr = ApproxRR(series, N, mid, 1, 1);
            if(rr < param) lo = mid;
            else           hi = mid;
           }
         return (lo + hi) * 0.5;
        }
     }
   return param;
  }

Метод FIXED и методы на основе долей сводятся к простым однострочным вычислениям. Метод RR_TARGET сложнее. Он инициализирует диапазон поиска от 0 до полного диапазона ряда, затем выполняет 40 итераций бисекции. На каждом шаге он оценивает RR в середине диапазона эпсилон с помощью ApproxRR(). Если оценочный RR ниже целевого, нижняя граница повышается (нужен больший эпсилон). Если выше — уменьшается верхняя граница. После 40 итераций интервал сжимается в 2^40 раз (примерно в триллион), поэтому результат получается очень точным.

Приближённый коэффициент рекуррентности

Строить полную матрицу N x N только для оценки RR во время бисекции было бы расточительно. Вместо этого ApproxRR() отбирает сетку пар точек, вычисляя расстояния только по равномерно разнесённым индексам.

double CRQAEpsilon::ApproxRR(const double &series[], int N,
                           double epsilon,
                           int embDim, int delay)
  {
   int samples = MathMin(N, 200);
   int M = N - (embDim - 1) * delay;
   if(M <= 0 || samples < 2) return 0.0;
   long rec = 0, total = 0;
   int step = MathMax(1, M / samples);
   for(int i = 0; i < M; i += step)
     for(int j = 0; j < M; j += step)
       {
        if(i == j) continue;
        double dist = 0;
        for(int d = 0; d < embDim; d++)
          {
           double diff = series[i + d * delay]
                       - series[j + d * delay];
           dist += diff * diff;
          }
        dist = MathSqrt(dist);
        if(dist <= epsilon) rec++;
        total++;
       }
   return (total > 0) ? (double)rec / total : 0.0;
  }

Метод ограничивает число отсчётов 200, затем вычисляет шаг так, чтобы равномерно распределить отсчёты по данным. Для ряда в 1000 баров шаг равен 5, поэтому оценивается сетка 200 x 200 пар (40 000 сравнений) вместо полной 1 000 000. Этого достаточно для хорошей оценки RR и точной сходимости бисекции. Обратите внимание, что ApproxRR всегда использует евклидову норму независимо от нормы, настроенной в матрице. Это осознанное упрощение ради скорости при поиске эпсилона. Финальное построение матрицы будет использовать настроенную норму.


CRQAWindow: скользящий анализ

Один расчёт RQA даёт один набор метрик для одного блока данных. Чтобы отслеживать, как динамика меняется во времени, нужен оконный RQA: фиксированное окно сдвигается по ряду, для каждой позиции вычисляется RQA и формируется временной ряд метрик. Этим занимается CRQAWindow.

Конфигурация

Класс хранит все параметры, необходимые для каждого расчёта RQA в окне: размер окна, шаг, эпсилон, размерность встраивания, задержку, норму и минимальные длины линий. Метод Run() принимает полный ряд, перебирает окна и заполняет массив структур SRQAWindowResult.

bool CRQAWindow::Run(const double &series[], int seriesLen,
                     SRQAWindowResult &results[])
  {
   if(seriesLen < m_windowSize)
     {
      Print("RQAWindow::Run - series shorter than window");
      return false;
     }
   CRQAMatrix  mat;
   CRQAMetrics mtr(m_minDiagLine, m_minVertLine);
   int numWindows = (seriesLen - m_windowSize) / m_step + 1;
   ArrayResize(results, numWindows);
   double slice[];
   ArrayResize(slice, m_windowSize);
   int idx = 0;
   for(int start = 0;
       start + m_windowSize <= seriesLen;
       start += m_step)
     {
      for(int k = 0; k < m_windowSize; k++)
         slice[k] = series[start + k];
      double eps = (m_epsilon > 0.0)
                  ? m_epsilon : 0.1;
      if(!mat.Build(slice, m_windowSize, eps,
                   m_embDim, m_delay, m_norm))
        {
         results[idx].barIndex = start;
         results[idx].metrics.Reset();
         idx++;
         continue;
        }
      results[idx].barIndex = start;
      mtr.Compute(mat, results[idx].metrics);
      idx++;
     }
   ArrayResize(results, idx);
   return true;
  }

На каждой итерации фрагмент ряда копируется в локальный массив, для него строится рекуррентная матрица, вычисляются метрики, а результат сохраняется вместе с индексом начального бара. Если построение матрицы для конкретного окна не удалось (что не должно происходить в нормальных условиях), метрики сбрасываются в ноль, и цикл продолжается. Объекты CRQAMatrix и CRQAMetrics переиспользуются между итерациями, а каждый вызов Build() перезаписывает предыдущую матрицу.

Статические методы извлечения

После завершения Run() у вас есть массив структур SRQAWindowResult. Чтобы передать отдельные метрики в буферы индикатора или в дальнейший анализ, класс предоставляет статические методы извлечения, которые извлекают одну метрику в обычный массив double.

void CRQAWindow::ExtractRR(const SRQAWindowResult &r[],
                          double &out[])
  {
   int n = ArraySize(r);
   ArrayResize(out, n);
   for(int i = 0; i < n; i++)
      out[i] = r[i].metrics.RR;
  }

Для каждой часто используемой метрики есть отдельный метод извлечения: ExtractRR, ExtractDET, ExtractLAM, ExtractTT, ExtractENTR, ExtractLmax и ExtractTREND. Все они следуют одному шаблону. Такая архитектура отделяет извлечение данных от вычисления, поэтому вызывающий код может выбирать только нужные метрики без повторного расчёта.


CRQA: фасадный класс

Класс CRQA — удобная оболочка. Он хранит CRQAMatrix, CRQAMetrics и всё состояние конфигурации в одном объекте. Вы настраиваете его, вызываете Compute() и читаете результаты. Не нужно вручную связывать построение матрицы и вычисление метрик.

CRQA rqa;
rqa.SetEmbedding(2, 1);
rqa.SetNorm(RQA_NORM_EUCLIDEAN);
rqa.SetEpsilonAuto(EPSILON_RR_TARGET, 0.05);
if(rqa.Compute(close, copied))
   rqa.PrintSummary();

Метод Compute() сначала проверяет, нужно ли автоматически выбирать эпсилон. Если выбранный метод отличается от EPSILON_FIXED, он вызывает CRQAEpsilon::Select() для определения порога. Затем строит рекуррентную матрицу и вычисляет все метрики за один проход.

bool CRQA::Compute(const double &series[], int N)
  {
   m_computed = false;
   m_result.Reset();
   if(N < 4)
     {
      Print("CRQA::Compute - series too short (min 4)");
      return false;
     }
   double eps = m_epsilon;
   if(m_epsilonMethod != EPSILON_FIXED)
      eps = CRQAEpsilon::Select(series, N,
                 m_epsilonMethod, m_epsilonParam);
   m_epsilon = eps;
   if(!m_matrix.Build(series, N, eps,
                      m_embDim, m_delay, m_norm))
      return false;
   if(!m_metrics.Compute(m_matrix, m_result))
      return false;
   m_computed = true;
   return true;
  }

После того как Compute() вернул true, к отдельным метрикам можно обращаться через именованные методы доступа (RR(), DET(), LAM() и т. д.) или получить всю структуру SRQAResult через GetResult(). Метод PrintSummary() выводит все двенадцать метрик в журнал Experts в форматированном блоке, что полезно при разработке и отладке.

Пример скрипта

Скрипт RQA_Example.mq5 демонстрирует и фасадный интерфейс, и оконный анализ в одном файле. Он копирует цены закрытия с текущего графика, запускает полный расчёт RQA через фасадный интерфейс CRQA, печатает результаты, затем запускает оконный анализ и выводит метрики первого и последнего окна.

#property script_show_inputs
#include <RQA\RQA.mqh>
input int    InpBars    = 100;
input int    InpEmbDim  = 2;
input int    InpDelay   = 1;
input double InpEpsilon = 0.0;
void OnStart()
  {
   double close[];
   int copied = CopyClose(_Symbol, _Period,
                          0, InpBars, close);
   if(copied < InpBars)
     { Print("Not enough bars"); return; }
   CRQA rqa;
   rqa.SetEmbedding(InpEmbDim, InpDelay);
   rqa.SetNorm(RQA_NORM_EUCLIDEAN);
   if(InpEpsilon > 0.0)
      rqa.SetEpsilon(InpEpsilon);
   else
      rqa.SetEpsilonAuto(EPSILON_RR_TARGET, 0.05);
   if(!rqa.Compute(close, copied))
     { Print("RQA failed"); return; }
   rqa.PrintSummary();
   //--- Windowed analysis
   CRQAWindow win;
   win.SetWindow(30, 5);
   win.SetEmbedding(InpEmbDim, InpDelay);
   win.SetEpsilon(rqa.Epsilon());
   SRQAWindowResult results[];
   if(win.Run(close, copied, results))
     {
      double rrSeries[], detSeries[];
      CRQAWindow::ExtractRR(results, rrSeries);
      CRQAWindow::ExtractDET(results, detSeries);
      int n = ArraySize(results);
      PrintFormat("Windows: %d", n);
      if(n >= 2)
        {
         PrintFormat("First - RR=%.4f DET=%.4f",
                    rrSeries[0], detSeries[0]);
         PrintFormat("Last  - RR=%.4f DET=%.4f",
                    rrSeries[n-1], detSeries[n-1]);
        }
     }
  }

Сначала скрипт запускает фасадный интерфейс CRQA на полном 100-барном ряду с автоматическим выбором эпсилон, настроенным на целевой RR 5%. Он печатает сводку. Затем создаёт CRQAWindow с окном 30 баров и шагом 5 баров, повторно используя значение эпсилона, вычисленное классом-оболочкой. Оконный запуск формирует серию результатов, из которых извлекаются RR и DET, после чего печатаются первое и последнее значения. Это показывает оба шаблона использования: однократный анализ и скользящий анализ.


Индикатор RQA

Файл RQA_Indicator.mq5 превращает библиотеку в инструмент для графика в реальном времени. Он выводит пять метрик отдельными линиями в подокне: RR, DET, LAM, ENTR и TREND. Каждая метрика рисуется цветной линией, обновляющейся на каждом новом баре.

Настройка буферов и графиков

#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   5
#property indicator_label1  "RR"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_width1  2
#property indicator_label2  "DET"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrLimeGreen
#property indicator_width2  2
#property indicator_label3  "LAM"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrOrange
#property indicator_width3  2
#property indicator_label4  "ENTR"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrViolet
#property indicator_width4  2
#property indicator_label5  "TREND"
#property indicator_type5   DRAW_LINE
#property indicator_color5  clrRed
#property indicator_width5  1

Объявляются пять буферов индикатора — по одному для каждой метрики. Все они рисуются линиями в отдельном окне. Цвета выбраны для визуального контраста: синий для RR, зелёный для DET, оранжевый для LAM, фиолетовый для ENTR и красный для TREND.

Входные параметры

#include <RQA\RQA.mqh>
input int    InpWindowSize   = 50;
input int    InpStep         = 1;
input int    InpEmbDim       = 1;
input int    InpDelay        = 1;
input double InpEpsilon      = 0.0;
input double InpEpsilonParam = 0.05;
input ENUM_EPSILON_METHOD InpEpsMethod
                           = EPSILON_RR_TARGET;
input ENUM_RQA_NORM InpNorm
                           = RQA_NORM_EUCLIDEAN;
input int    InpMinDiag      = 2;
input int    InpMinVert      = 2;

Индикатор делает все параметры RQA настраиваемыми. InpWindowSize задаёт, сколько баров использует каждый расчёт RQA. InpStep задаёт, на сколько баров окно продвигается между расчётами (значение 1 даёт вывод на каждом баре). InpEpsilon = 0.0 включает автоматический выбор эпсилона с использованием InpEpsMethod и InpEpsilonParam. Входные параметры-перечисления для метода эпсилон и нормы расстояния отображаются как выпадающие списки в диалоге настроек индикатора, предоставляя доступ ко всему диапазону опций без редактирования кода.

Логика OnCalculate

int OnCalculate(const int rates_total,
               const int prev_calculated,
               const datetime &time[],
               const double &open[],
               const double &high[],
               const double &low[],
               const double &close[],
               const long &tick_volume[],
               const long &volume[],
               const int &spread[])
  {
   if(rates_total < InpWindowSize + 10)
      return 0;
   CRQAWindow win;
   win.SetWindow(InpWindowSize, InpStep);
   win.SetEmbedding(InpEmbDim, InpDelay);
   win.SetNorm(InpNorm);
   win.SetMinLines(InpMinDiag, InpMinVert);
   //--- выбор эпсилона
   if(InpEpsilon > 0.0)
      win.SetEpsilon(InpEpsilon);
   else
     {
      double fullSeries[];
      ArrayResize(fullSeries, rates_total);
      for(int i = 0; i < rates_total; i++)
         fullSeries[i] = close[i];
      double autoEps = CRQAEpsilon::Select(
         fullSeries, rates_total,
         InpEpsMethod, InpEpsilonParam);
      win.SetEpsilon(autoEps);
     }
   //--- build price array
   double prices[];
   ArrayResize(prices, rates_total);
   for(int i = 0; i < rates_total; i++)
      prices[i] = close[i];
   //--- run rolling RQA
   SRQAWindowResult results[];
   if(!win.Run(prices, rates_total, results))
      return prev_calculated;
   int nRes = ArraySize(results);
   for(int k = 0; k < nRes; k++)
     {
      int bar = results[k].barIndex
              + InpWindowSize - 1;
      if(bar < rates_total)
        {
         BufferRR[bar]    = results[k].metrics.RR;
         BufferDET[bar]   = results[k].metrics.DET;
         BufferLAM[bar]   = results[k].metrics.LAM;
         BufferENTR[bar]  = results[k].metrics.ENTR;
         BufferTREND[bar] = results[k].metrics.TREND;
        }
     }
   return rates_total;
  }

Индикатор присваивает метрики каждого окна последнему бару этого окна (barIndex + InpWindowSize - 1). Это правильное соглашение для использования в реальном времени: метрики на баре B отражают только данные от баров B - windowSize + 1 до B, без заглядывания вперёд. Размер окна 50 и шаг 1 означают, что каждый бар начиная с индекса 49 получает собственный расчёт RQA.

RQA Indicator GBPUSD Example

Рис. 6 — Индикатор RQA на GBPUSD M30


Что дальше

В этой статье был создан набор средств. Теперь у нас есть полноценная модульная библиотека RQA для MQL5, способная вычислять двенадцать метрик по любому ценовому ряду, с автоматическим выбором эпсилон и поддержкой скользящего окна. Индикатор выводит эти метрики на график в реальном времени.

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

  • Использовать пороги DET и LAM для классификации рыночных режимов (трендовый, боковой, переходный).
  • Создать советник (Expert Advisor), который адаптирует параметры стратегии на основе скользящих метрик RQA.
  • Объединять метрики RQA с традиционными индикаторами для создания отфильтрованных сигналов входа и выхода.
  • Использовать кросс-рекуррентный количественный анализ (CRQA) между двумя связанными инструментами для обнаружения расхождения или сближения их динамики.

Библиотека проектировалась с учётом этих применений. Модульная архитектура означает, что можно использовать фасадный интерфейс CRQA для быстрых экспериментов, перейти на уровень CRQAMatrix и CRQAMetrics для пользовательского анализа или расширить CRQAWindow для более сложных схем оконной обработки. Основа уже готова.


Заключение

Мы построили специализированный набор средств RQA для MetaTrader 5: модульную документированную библиотеку MQL5, примеры кода и индикатор. В состав решения входят:

  • Библиотеку, предоставляющую единый вызов (CRQA) и модульные компоненты (CRQAMatrix, CRQAMetrics, CRQAEpsilon, CRQAWindow), которые отвечают за встраивание, вычисление расстояний, построение рекуррентной матрицы, извлечение метрик (12 метрик), выбор эпсилона (фиксированное значение, доля стандартного отклонения, доля диапазона, целевой RR) и скользящий анализ.
  • Пример скрипта, демонстрирующий однократное и оконное использование, а также индикатор, выводящий RR, DET, LAM, ENTR и TREND в отдельном окне.

Этот набор средств предназначен как вычислительная основа, а не как торговая система. Остаются важные практические ограничения: рекуррентная матрица имеет сложность O(N^2), поэтому применимые размеры окна обычно ограничены примерно 100–200 отсчётами; автоматического выбора размерности встраивания (например, false nearest neighbors (ложных ближайших соседей) пока нет, как и модуля кросс-рекуррентности. Это осознанные компромиссы, позволяющие сохранить код понятным и производительным для использования в MetaTrader 5 в реальном времени.

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

Название файла
Тип Описание
 1  RQA.mqh  include-файл  Основной include-файл и фасадный класс CRQA
 2 RQAMatrix.mqh
 include-файл Класс CRQAMatrix: встраивание и построение рекуррентной матрицы
 3 RQAMetrics.mqh
 include-файл Класс CRQAMetrics и структура SRQAResult: все двенадцать метрик RQA
 4 RQAEpsilon.mqh
 include-файл Класс CRQAEpsilon: автоматический выбор эпсилона по четырём стратегиям
 5 RQAWindow.mqh
 include-файл Класс CRQAWindow и структура SRQAWindowResult: анализ в скользящем окне
 6 RQA_Example.mq5
 Скрипт Пример скрипта, демонстрирующий фасадный интерфейс CRQA и оконный анализ
 7 RQA_Indicator.mq5
 Индикатор Индикатор, выводящий RR, DET, LAM, ENTR и TREND в отдельном окне

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

Прикрепленные файлы |
RQA.mqh (6.9 KB)
RQAMatrix.mqh (5.66 KB)
RQAMetrics.mqh (9.07 KB)
RQAEpsilon.mqh (4.93 KB)
RQAWindow.mqh (7.28 KB)
RQA_Example.mq5 (2.64 KB)
RQA_Indicator.mq5 (5.23 KB)
Архитектура машинного обучения для MetaTrader 5 (Часть 14): Моделирование транзакционных издержек для разметки методом тройного барьера в MQL5 Архитектура машинного обучения для MetaTrader 5 (Часть 14): Моделирование транзакционных издержек для разметки методом тройного барьера в MQL5
В статье заданные вручную предположения об издержках в разметке методом тройного барьера заменяются измеренными данными. Скрипт MQL5 собирает у брокера распределение спреда, ставки свопа и свойства символа, а модель Python преобразует эти данные в min_ret, откалиброванный с учётом условий брокера, который можно передать в get_events(). После этого метки учитывают фактические издержки полного цикла сделки для выбранного инструмента и периода удержания позиции.
Разработка динамического мультивалютного советника (Часть 5): Скальпинг и свинг-трейдинг Разработка динамического мультивалютного советника (Часть 5): Скальпинг и свинг-трейдинг
В этой части рассматривается, как разработать динамический мультивалютный советник, способный адаптироваться к режимам скальпинга и свинг-трейдинга. В ней рассматриваются структурные и алгоритмические различия в генерации сигналов, исполнении сделок и управлении рисками, благодаря которым советник может гибко переключаться между стратегиями в зависимости от рыночного поведения и входных параметров.
Построение моделей волатильности в MQL5 (Часть II): Реализация моделей GJR-GARCH и TARCH Построение моделей волатильности в MQL5 (Часть II): Реализация моделей GJR-GARCH и TARCH
В статье реализуются GJR-GARCH и TARCH в библиотеке волатильности MQL5 и объясняется, почему учёт асимметрии даёт преимущества по сравнению со стандартными ARCH/GARCH. Рассматриваются формулировка моделей, параметризация и использование через производные классы и скрипты. Читатели получают примеры кода для калибровки и одношагового прогнозирования на реальных данных для управления рисками и диагностики моделей.
Разработка инструментария для анализа Price Action (Часть 50): Создание модуля согласования сигналов RVGI, CCI и SMA на MQL5 Разработка инструментария для анализа Price Action (Часть 50): Создание модуля согласования сигналов RVGI, CCI и SMA на MQL5
Многим трейдерам сложно распознавать настоящие развороты. В этой статье представлен советник, который объединяет RVGI, CCI (±100) и трендовый фильтр SMA, формируя единый четкий сигнал разворота. Советник включает панель на графике, настраиваемые алерты и полный исходный файл, готовый к немедленной загрузке и тестированию.