Низкочастотные количественные стратегии в MetaTrader 5 (Часть 3): Система свинг-трейдинга на основе возврата к среднему значению с адаптацией к рыночным режимам
Введение
В 2002 году Марк Хельвег и Дэвид Стендаль издали небольшую книгу, которая оказала значительное влияние на рынок литературы по трейдингу: "Динамические торговые индикаторы: как добиться успеха с помощью Value Chart (график стоимости) и Price Action Profile (профиль ценового движения)" (John Wiley & Sons, Inc., 2002). Эти две концепции, представленные в книге, — Value Chart (график стоимости)© и Price Action Profile (профиль ценового движения)© — в то время были новаторскими хотя бы благодаря тому обилию информации, которым их применение позволяет дополнить традиционные графики как в дискреционных, так и в алгоритмических торговых системах.
Спустя почти десятилетие разработчик количественных систем Дэвид Абрамс и портфельный менеджер Скотт Уокер использовали методы Value Chart (график стоимости) и Price Action Profile (профиль ценового движения) для создания сложной системы свинг-трейдинга, основанной на возврате к среднему значению. В 2010 году они опубликовали описание этой системы и сообщили о впечатляющих результатах на фондовом рынке. Они назвали эту систему MR Swing.
Система MRSwing была включена в качестве компонента с долей 20 % в диверсифицированный портфель ETF с низкой корреляцией, сформированный на основе Ivy Portfolio по образцу университетских эндаумент-фондов. MR Swing увеличила среднегодовой темп роста (CAGR) совокупного портфеля в 1,5 раза, одновременно снизив максимальную просадку всего до 4 %.
Помимо хороших показателей на рынке акций, система MR Swing обладает как минимум двумя особенностями, заслуживающими нашего внимания независимо от того, о каком рынке идёт речь:
- асимметричные торговые алгоритмы для различных рыночных режимов
- показатели, адаптирующиеся к волатильности
В первой части этой серии статей, посвящённой низкочастотным количественным стратегиям, мы описали простейшую конфигурацию локального легковесного хранилища данных, удобного для OLAP. Далее мы рассмотрели первую стратегию — анализ опережения и запаздывания для высоколиквидных акций компаний полупроводниковой отрасли. Теперь мы выйдем за рамки анализа данных и рассмотрим процесс полной реализации количественной стратегии в MQL5. В данной статье мы описываем реализацию системы MR Swing на языке MQL5, а также приводим результаты её бэктестов на высоколиквидных акциях Nasdaq за последние пять лет, демонстрирующие, что система по-прежнему актуальна и в наши дни.
Начнём с краткого описания того, что такое Value Chart (график стоимости) и Price Action Profile (профиль ценового движения), а также с обзора того, как они используются в системе MR Swing.
Далее мы подробно рассмотрим индикаторы и торговую логику, составляющие эту систему, а также их реализацию на языке MQL5.
В заключение мы рассмотрим результаты двух бэктестов. Сначала мы проводим оптимизацию в режиме "Все символы из Обзора рынка", чтобы выбрать инструмент с наилучшими результатами среди шести высоколиквидных акций Nasdaq. Затем мы проводим одиночный тест на выбранном инструменте.
Что такое Value Chart (график стоимости)
Value Chart (график стоимости) — это индикатор, адаптирующийся к волатильности и основанный на расчете относительных цен. Визуально он очень похож на график традиционного осциллятора, например RSI.
Рис. 1. Дневной график акций Nvidia Corp. с индикатором Value Chart (график стоимости) (бесплатный индикатор от Флавио Хавьера Джарабека)
На приведенном выше графике свечи с минимальными ценами ниже -6,0 считаются перепроданными, а свечи с максимальными ценами выше 6,0 — перекупленными. Как уже упоминалось, его интерпретация очень похожа на интерпретацию таких осцилляторов, как RSI, а период ретроспективы и уровни перекупленности/перепроданности также являются настраиваемыми параметрами.
Основная идея индикатора Value Chart (график стоимости) заключается в разграничении цены и стоимости; поэтому графики Value Chart (график стоимости) используются вместо ценовых графиков.
"Оценка рынка зависит как от цены, так и от времени. Если бы на свободном рынке цена оставалась неизменной навечно, логично было бы предположить, что покупатели и продавцы сошлись во мнении о том, что эта цена не свидетельствует ни о перекупленности, ни о перепроданности рынка, а отражает его справедливую стоимость. Цены на рынках, на которых мы торгуем, редко остаются совершенно неизменными с течением времени; напротив, они постоянно отклоняются от справедливой стоимости как вверх, так и вниз на всех таймфреймах. Поскольку мы живем в постоянно меняющемся мире, справедливая стоимость с течением времени постоянно пересматривается. Цены на активно торгуемых рынках всегда колеблются вокруг справедливой стоимости". [ХЕЛВЕГ и СТЕНДАЛЬ, 2002]
Чтобы лучше понять, как рассчитываются цены в индикаторе Value Chart (график стоимости), можно разбить этот процесс на четыре этапа. В итоге мы получим относительные рыночные цены, скорректированные с учетом волатильности, что позволит нам определять уровни перекупленности и перепроданности, которые остаются неизменными даже при изменении рыночной волатильности с течением времени.
Сначала мы вычисляем "плавающую ось". "Плавающая ось" отражает "справедливую стоимость" рынка. Это базовая линия графика. Это не что иное, как скользящее среднее медианной цены за определенный период сглаживания. По умолчанию установлено 5 баров.

Рис. 2. Формула «плавающей оси»
"(...) вместо того, чтобы строить график цены относительно нуля, необходимо строить график цен открытия, максимальных цен, минимальных цен и цен закрытия относительно подвижной (плавающей) оси, которая призвана отражать справедливую стоимость. Поскольку справедливая стоимость отражает уровень цены, на котором совершает сделки большинство покупателей и продавцов, тщательно выбранное скользящее среднее ценовой динамики должно отражать справедливую стоимость на любом рынке. Это скользящее среднее называется плавающей осью". [HELWEG и STENDHAL, 2002]
Затем мы рассчитываем единицу динамической волатильности, которая корректирует относительную цену с учетом постоянно меняющейся волатильности. Для этого берется среднее значение суточного диапазона колебаний цен за тот же период сглаживания и умножается на постоянную величину.

Рис. 3. Формула расчёта единицы динамической волатильности
Авторы книги предлагают использовать значение константы альфа, равное 0,20, тогда как авторы MR Swing используют значение 0,16.
Теперь нам нужна относительная цена. Это просто разница между фактической ценой и плавающей осью. Таким образом, относительная максимальная цена будет равна (максимальная цена — плавающая ось), относительная минимальная цена — (минимальная цена — плавающая ось) и так далее.
Наконец, мы можем рассчитать цену для индикатора Value Chart (график стоимости), разделив относительные цены на единицу динамической волатильности.
Максимальная цена Value Chart (график стоимости) = (максимальная цена − плавающая ось) / единица динамической волатильности
Минимальная цена Value Chart (график стоимости) = (минимальная цена − плавающая ось) / единица динамической волатильности
На графике индикатора значение "ноль" соответствует справедливой стоимости. Стандартные пороговые значения, используемые для определения уровней перекупленности/перепроданности, составляют 4 и 8. Эти значения, как правило, соответствуют одному и двум стандартным отклонениям (Std Dev) от справедливой стоимости.
Value Chart (график стоимости) в MR Swing
В стратегии MR Swing индикатор Value Chart (график стоимости) используется для выявления потенциальных точек входа на откатах на бычьем рынке; период сглаживания составляет 5 баров, а константа единицы динамической волатильности — 0,16. Система отслеживает Value Chart (график стоимости), построенный по минимумам баров, а не по ценам закрытия или медианным ценам. Потенциальный сигнал входа появляется только тогда, когда индикатор Value Chart (график стоимости) минимумов достигает определённого уровня перепроданности, равного -7,5. Этот уровень сохраняет статистическую значимость как на рынках с высокой, так и с низкой волатильностью, где статические уровни перекупленности/перепроданности могут терять точность.
MR Swing рассматривает бычий и медвежий рынки как асимметричные. Авторы отмечают, что на бычьих рынках, как правило, наблюдаются быстрые и резкие коррекции, за которыми обычно следует медленное повышение цен. Поскольку индикатор Value Chart (график стоимости) позволяет определить, когда цена значительно отклонилась от своей краткосрочной плавающей оси, он помогает выявлять такие быстрые откаты.
Что такое Price Action Profile (профиль ценового движения)?
"Price Action Profile (профиль ценового движения)" — это инструмент анализа рынка, который "отображает и описывает историческое распределение и частоту ценовой активности на Value Chart (графике стоимости)". Его можно рассматривать как статистическое дополнение к Value Chart (графику стоимости). Он позволяет определить, как часто рыночная цена находится в заданных относительных ценовых интервалах.
Ниже представлена типичная визуализация Price Action Profile (профиля ценового движения). Иллюстрация взята из оригинальной книги.

Рис. 4. Типичная визуализация Price Action Profile (профиля ценового движения)
Price Action Profile (профиль ценового движения) систематизирует ценовые данные в виде гистограммы частот, напоминающей типичную колоколообразную кривую нормального распределения. Анализируя это распределение, мы можем определить конкретные диапазоны на Value Chart (графике стоимости) для справедливой стоимости, а также для уровней перекупленности и перепроданности. Поскольку профили, построенные на основе Value Chart (графика стоимости), остаются относительно стабильными на разных рынках и в различных режимах волатильности, мы можем оценить будущее распределение ценовых данных на основе исторической выборки. Есть основания полагать, что динамика цен в будущем будет соответствовать историческому распределению. Это объективная, поддающаяся количественной оценке информация, которую можно интерпретировать только одним способом, что помогает исключить эмоциональную предвзятость. Чтобы помочь понять процесс построения гистограммы, авторы включили в свою книгу последовательность из четырёх шагов, представленную ниже.

Рис. 5. Price Action Profile (профиль ценового движения), построенный на основе одного ценового бара на графике Value Chart (график стоимости)
На рисунке 5 заштрихованный столбец (слева) представляет собой Price Action Profile (профиль ценового движения), построенный на основе одного ценового бара на графике Value Chart (график стоимости) (справа), отражающего один торговый день. На рисунках 6, 7 и 8 ниже мы видим поэтапное построение одного и того же Price Action Profile (профиля ценового движения) на основе двух, трёх и четырёх ценовых баров графика Value Chart (график стоимости) соответственно.

Рис. 6. Price Action Profile (профиль ценового движения), построенный на основе двух ценовых баров на графике Value Chart (график стоимости)

Рис. 7. Price Action Profile (профиль ценового движения), построенный на основе трёх ценовых баров на графике Value Chart (график стоимости)

Рис. 8. Price Action Profile (профиль ценового движения), построенный на основе четырёх ценовых баров на графике Value Chart (график стоимости)
Если мысленно накопить на гистограмме данные четырёхсот или четырёх тысяч ценовых баров графика Value Chart (график стоимости), можно получить представление о её окончательном виде, показанном на рисунке 4.
Price Action Profile (профиль ценового движения) в системе MR Swing
В части системы MR Swing, предназначенной для медвежьего режима, функция "процентильный ранг", используемая осциллятором Дэвида Варади (DVO) для нормализации данных, представляет собой математическую реализацию Price Action Profile (профиля ценового движения). Эта функция принимает текущие ценовые соотношения и ранжирует их относительно значений за период ретроспективы, чтобы определить их относительную частоту. Это гарантирует, что сигналы о перекупленности/перепроданности основываются на исторической вероятности, а не на статических ценовых уровнях. Кроме того, функция расчёта процентильного ранга гарантирует, что сигнал, сгенерированный в условиях низкой волатильности, будет иметь такую же статистическую значимость, как и сигнал, сгенерированный в условиях высокой волатильности. Price Action Profile (профиль ценового движения) позволяет с уверенностью утверждать, что точки входа характеризуются низким уровнем риска. Он указывает на то, что ценовая активность на уровне -7,5 статистически редка и что, судя по историческим данным, разворот вероятен. Такая статистическая уверенность крайне важна для предусмотренных системой входов по лимитным ордерам с высокой вероятностью успеха.
Торговая система MR Swing
Канал гистерезиса
Рынок постоянно меняется. Это основная предпосылка, на которой основана система MR Swing. Пытаться предугадать следующее изменение бессмысленно. Принимать эффективные торговые решения в постоянно меняющихся условиях — вот задача трейдера. Чтобы решить эту задачу, система MR Swing при каждом тике анализирует текущий рыночный режим и применяет разные торговые алгоритмы для бычьего и медвежьего режимов.
В основе инструмента, используемого для количественной оценки текущего рыночного режима, лежит сложная концепция — гистерезис, заимствованная из физики.
"Гистерезис — это естественное явление, которое наблюдается в магнетизме, упругости, митозе клеток и теории управления (например, в термостатах). Эти системы демонстрируют зависимость от траектории развития, при которой текущее состояние зависит от пути, пройденного для его достижения. Система обладает памятью, и воздействие текущего входного сигнала проявляется лишь по истечении определенного времени или при выходе за порог диапазона. Мы считаем, что рынкам также требуется время, чтобы отреагировать на новую информацию, и что при такой реакции учитывается недавняя динамика рынка. Наш фильтр рыночного режима переключается в медвежий режим только после того, как цена закрытия окажется ниже нижней границы канала. [ABRAMS and WALKER, 2010, с. 8]".
Идея этого канала гистерезиса, или трендового канала, заключается в создании буфера между сменами рыночного режима, чтобы уменьшить число ложных сигналов о его смене, также известных как "ложные срабатывания (пила)". На практике система реализована с помощью трендового канала на основе 200-дневной простой скользящей средней.
Его реализацию вы найдете в заголовочном файле MRSwing.mqh.
class CMRSwing { private: //--- Persistent state int m_regime; // 1: Bull, -1: Bear double m_rho;
Значение канала гистерезиса сохраняется в переменной m_rho. В то время как обычная 200-дневная простая скользящая средняя (SMA) рассчитывается на основе одной из цен OHLC, чаще всего цены закрытия, канал гистерезиса одновременно учитывает максимальные и минимальные цены. Этот буфер между максимальными и минимальными ценами создает порог, который смещается только в направлении текущего рыночного режима (вверх в бычьем режиме и вниз в медвежьем). Другими словами, это модель, учитывающая только приращения и игнорирующая небольшие коррекции, что обеспечивает своего рода память и помогает сократить ложные срабатывания (пила) при смене рыночного режима — те назойливые ложные сигналы, к которым мы так привыкли в традиционных системах на основе скользящих средних.
//--- 1. Hysteresis Channel for(int i = start; i < rates_total; i++) { if(i < 200) continue; double mu_l = SMA(low, 200, i); double mu_h = SMA(high, 200, i); if(i == 200) { m_rho = mu_l; m_regime = 1; } if(m_regime == 1) m_rho = MathMax(mu_l, m_rho); else m_rho = MathMin(mu_h, m_rho); if(close[i] > m_rho) m_regime = 1; else if(close[i] < m_rho) m_regime = -1; }
Сначала мы вычисляем простые скользящие средние как для максимальных цен баров (mu_h), так и для минимальных цен баров (mu_l) за период в 200 дней. Значение канала изменяется только в направлении сформировавшегося тренда. Это зависит от текущего рыночного режима. На бычьем рынке это максимальное значение текущего или предыдущего бара. Это ограничение означает, что при восходящем тренде значение канала может только увеличиваться или оставаться неизменным. Во время коррекций канал никогда не опускается. На медвежьем рынке это минимум текущего или предыдущего бара. Это означает, что при нисходящем тренде значение канала может только уменьшаться или оставаться неизменным. Смена рыночного режима допускается только тогда, когда цена закрытия находится выше или ниже текущего значения канала.
Поскольку смена тренда признаётся только при закрытии цены за пределами канала максимальных и минимальных цен, новые ценовые данные начинают влиять на результат лишь после выхода цены за установленный порог диапазона. Это позволяет надёжнее выявлять основные тренды, чем при использовании одной традиционной скользящей средней.
Осциллятор Дэвида Варади (DVO)
Осциллятор Дэвида Варади (DVO), используемый в медвежьем рыночном режиме, представляет собой простой и эффективный ценовой осциллятор с устранённым трендом. Функция "процентильный ранг", используемая DVO для нормализации данных, представляет собой математическую реализацию Price Action Profile (профиля ценового движения), поскольку она позволяет количественно оценить частоту и распределение ценовой активности. Эта функция ранжирует текущие ценовые соотношения относительно значений за период ретроспективы, чтобы определить их относительную частоту, поэтому сигналы перекупленности и перепроданности основаны на исторической вероятности, а не на фиксированных ценовых уровнях. В данной реализации период ретроспективы по умолчанию составляет 252 дня.
Ниже приведена обобщённая формула DVO, представленная авторами MR Swing.

Рис. 9. Формула DVO, представленная Абрамсом и Уокером (2010)
Коэффициенты w — это веса, применяемые к ценам OHLC. Обратите внимание, что мы можем использовать любую комбинацию весов OHLC. Здесь есть большой потенциал для оптимизации. В данной реализации мы используем вариант DV2 (см. ниже), в котором веса применяются только к максимальной и минимальной ценам.
s — это тоже вес, но он применяется по дням, то есть во времени. Авторы называют это "плотностью". Он определяет, какой вес присваивается каждому из N предыдущих баров.
N — период сглаживания, который по умолчанию составляет 5 торговых дней.
M — это период ретроспективы. По умолчанию этот период составляет 252 торговых дня, то есть один год.
Обратите внимание: для расчёта DVO с помощью процентильного ранга сначала необходимо определить величину Theta.
double GetDVO(const double &close[], const double &high[], const double &low[], int index) { return DVO(close, high, low, 5, 252, index, m_theta_buffer); }
Theta представляет собой взвешенную сумму отношений цены закрытия к взвешенной комбинации цен OHLC. Авторы используют вариант DVO (DV2).
"DV2 — это один из вариантов настройки, изначально разработанный для SPY (веса за последние два дня распределялись в соотношении 50/50)".
В алгоритме DV2 используется вектор весов w = [0,5; 0,5; 0; 0]. Это означает:
Максимальная цена (w0): вес 0,5
Минимальная цена (w1): вес 0,5
Цена открытия (w2): вес 0
Цена закрытия (w3): вес 0
![]()
Рис. 10. Формула DV2 с векторами весов
//--- 2. DVO Pre-calc (Theta buffer) for(int i = start; i < rates_total; i++) { double mid = (high[i] + low[i]) * 0.5; double ratio = (mid != 0) ? close[i] / mid : 1.0; m_dv2_temp[i] = (i > 0) ? ratio * 0.5 + ((close[i - 1] / ((high[i - 1] + low[i - 1]) * 0.5))) * 0.5 : ratio; m_theta_buffer[i] = SMA(m_dv2_temp, 5, i); }
Итак, эта конфигурация DV2, разработанная для ETF на индекс S&P 500, отслеживает отношение цены закрытия к торговому диапазону текущего дня.
Наконец, Theta (исходное значение) нормируется с помощью функции процентильного ранга за период ретроспективы. Статистики называют это непараметрическим методом: он не предполагает, что цены подчиняются нормальному (гауссовому) распределению.
//+------------------------------------------------------------------+ //| David Varadi Oscillator (DVO) | //+------------------------------------------------------------------+ double DVO(const double &close[], const double &high[], const double &low[], int smooth_period, int rank_period, int index, const double &theta_buffer[]) { if(index < rank_period + smooth_period) return(EMPTY_VALUE); double last_theta = theta_buffer[index]; int count = 0; for(int i = 0; i < rank_period; i++) { if(theta_buffer[index - i] < last_theta) count++; } return((double)count / (rank_period - 1)); }
Индикатор DVO генерирует сигналы возврата к среднему значению в медвежьем режиме. Значение менее 0,40 является сигналом на покупку; значение выше 0,70 — сигналом на продажу.
Осциллятор краткосрочного объема и цены (SVAPO)
Осциллятор краткосрочного объема и цены (SVAPO), разработанный Сильвеном Вервоортом, используется для выявления точек истощения рынка в бычьем режиме путем сочетания краткосрочных ценовых трендов с наклоном кривой объема во времени.
Показатель SVAPO рассчитывается посредством сглаживания и взвешивания по объему.
Сначала мы сглаживаем ценовой тренд. Используется сглаженная цена закрытия Хейкин-Аши, которая затем обрабатывается с помощью тройной экспоненциальной скользящей средней (TEMA) с периодом 0,625 * Period.
//--- 3. SVAPO Implementation for(int i = start; i < rates_total; i++) { double ap = (open_p[i] + high[i] + low[i] + close[i]) / 4.0; m_ha_open[i] = (i > 0) ? 0.5 * (ap + m_ha_open[i - 1]) : ap; m_ha_cl[i] = 0.25 * (ap + m_ha_open[i] + MathMax(ap, MathMax(high[i], m_ha_open[i])) + MathMin(ap, MathMin(low[i], m_ha_open[i]))); } CalculateTEMABuffer(m_ha_cl, m_ema1_hac, m_ema2_hac, m_ema3_hac, m_hac, (int)(0.625 * 8), rates_total, prev_calculated);
Затем мы сглаживаем тренд объема. К коэффициенту наклона линейной регрессии объема за указанный период (по умолчанию N=8) применяется метод TEMA.
double vol_slope[]; ArrayResize(vol_slope, rates_total); for(int i = start; i < rates_total; i++) vol_slope[i] = LinearRegressionSlope(volume, 8, i); CalculateTEMABuffer(vol_slope, m_ema1_vt, m_ema2_vt, m_ema3_vt, m_v_trend, 8, rates_total, prev_calculated);
Мы определяем тренд с помощью сглаженных свечей Хейкин-Аши. Если значения ценового тренда и тренда объема выше соответствующих значений на предыдущем баре, день считается "растущим"; если же оба значения ниже, день считается "падающим".
double delta_temp[]; ArrayResize(delta_temp, rates_total); for(int i = start; i < rates_total; i++) { if(i < 40) continue; double v_avg = SMA(volume, 40, i - 1); double vc = (volume[i] < 2.0 * v_avg) ? volume[i] : 2.0 * v_avg; bool up = (m_hac[i] > m_hac[i - 1] * (1.001)) && (m_v_trend[i] >= m_v_trend[i - 1]) && (m_v_trend[i - 1] >= m_v_trend[i - 2]); bool dn = (m_hac[i] < m_hac[i - 1] * (0.999)) && (m_v_trend[i] <= m_v_trend[i - 1]) && (m_v_trend[i - 1] <= m_v_trend[i - 2]); delta_temp[i] = up ? vc : (dn ? -vc : 0); double sum_delta = 0; for(int k = 0; k < 8; k++) sum_delta += delta_temp[i - k]; m_delta_sum[i] = sum_delta / (v_avg + 1.0); }
Система суммирует объем по растущим дням и вычитает объем по падающим дням за рассматриваемый период, а затем нормирует результат по среднему объему. Значение SVAPO представляет собой TEMA этой суммарной дельты объема.
CalculateTEMABuffer(m_delta_sum, m_ema1_sv, m_ema2_sv, m_ema3_sv, m_svapo, 8, rates_total, prev_calculated);
Показатель SVAPO используется для выявления истощения бычьего рынка. Таким образом, вокруг осциллятора формируются динамические полосы на основе стандартного отклонения значения SVAPO за период в 100 баров. Верхняя полоса определяется как 1,5 × StdDev(SVAPO,100). Сигнал об истощении появляется, когда на закрытии бара значение SVAPO оказывается выше верхней полосы.
Индикатор SVAPO вызывается из основного файла MRSwingEA.mq5.
double svapo_val = ExtStrategy.GetSVAPO(last_bar); //--- SVAPO Upper Band //--- We need a buffer of SVAPO to calculate StdDev double svapo_hist[]; ArrayResize(svapo_hist, rates_total); for(int i = 0; i < rates_total; i++) svapo_hist[i] = ExtStrategy.GetSVAPO(i); double upper_band = 1.5 * StdDev(svapo_hist, 100, last_bar);
Функция GetSVAPO объявлена в заголовочном файле стратегии.
double GetSVAPO(int index) { return (index >= 0 && index < ArraySize(m_svapo)) ? m_svapo[index] : EMPTY_VALUE; }
Сигнал об истощении не приводит к немедленному выходу из рынка. Вместо этого, как только SVAPO пересекает верхнюю полосу снизу вверх, система выставляет лимитный ордер в расчете на исполнение по наибольшей из максимальных цен последних двух баров, чтобы максимизировать цену выхода на пике.
Входы
Определив текущий рыночный режим с помощью канала гистерезиса, система применяет разные торговые стратегии для бычьего и медвежьего режимов.
Бычий режим
По словам авторов, при разработке системы MR Swing для бычьего режима они вдохновлялись известной стратегией свинг-трейдинга "Тройной экран" Элдера. Сначала на старшем таймфрейме/экране определяется преобладающий тренд; в данном случае для этого используется описанный выше канал гистерезиса. Затем на младшем таймфрейме/экране получают сигнал для входа на откате, который определяется порогом перепроданности индикатора Value Chart (график стоимости). Наконец, для определения оптимальной точки входа используется ещё более младший таймфрейм/экран; в данном случае это реализовано с помощью лимитных ордеров, выставляемых по наименьшей из минимальных цен последних двух баров.
Эту логику вы найдете в основном файле MRSwingEA.mq5.
//--- 3. Bull Regime (Swing) else if(regime == 1) { if(v_low < -7.5) { double limit_price = MathMin(rates[last_bar].low, rates[last_bar - 1].low); //--- Buy Limit pending order ExtTrade.BuyLimit(InpLotSize, limit_price, _Symbol); } if(is_long && svapo_val > upper_band) { double limit_exit = MathMax(rates[last_bar].high, rates[last_bar - 1].high); ExtTrade.SellLimit(InpLotSize, limit_exit, _Symbol); } if(is_short) ExtTrade.PositionClose(_Symbol); }
Медвежий режим
В медвежьем режиме система MR Swing использует краткосрочную стратегию возврата к среднему значению с осциллятором Дэвида Варади (DVO). Авторы системы MR Swing отмечают, что, хотя двухпериодный индекс относительной силы RSI(2) вполне подходит для торговли на возврате к среднему значению на дневных данных, функция процентильного ранга, используемая DVO для нормализации дневных данных, играет ключевую роль в адаптации к изменениям рыночной волатильности. Функция процентильного ранга обеспечивает формирование сигналов перепроданности или перекупленности относительно текущей волатильности, а не абсолютных ценовых уровней.
//--- 2. Bear Regime (Mean Reversion) if(regime == -1) { if(dvo_val < 0.40 && InpMode != MODE_CONSERVATIVE) { if(!is_long) ExtTrade.Buy(InpLotSize, _Symbol); } else if(dvo_val > 0.70) { if(!is_short) ExtTrade.Sell(InpLotSize, _Symbol); } //--- Exit Bear if(is_long && dvo_val > 0.50) ExtTrade.PositionClose(_Symbol); if(is_short && dvo_val < 0.50) ExtTrade.PositionClose(_Symbol); }
Выходы
Как и логика входов, логика закрытия позиций зависит от рыночного режима. При свинг-трейдинге на бычьем рынке система проверяет наличие признаков истощения рынка с помощью разработанного Вервоортом индикатора "Осциллятор краткосрочного объема и цены (SVAPO)". Сигнал на выход возникает, когда SVAPO пересекает свою верхнюю полосу, однако выход выполняется не по текущей рыночной цене. Вместо этого лимитный ордер выставляется по наибольшей из максимальных цен последних двух баров. Дополнительно для защиты прибыли система может использовать трейлинг-стоп на уровне простой скользящей средней минимальных цен последних двух баров.
При торговле на возврате к среднему значению на медвежьем рынке выход из позиции зависит от режима настройки системы.
Конфигурация MR Swing
Система MR Swing предусматривает три режима настройки. Они зависят от того, разрешено ли системе открывать сделки против тренда или она должна торговать исключительно в направлении доминирующего рыночного режима.
- Агрессивный
- Промежуточный
- Консервативный
В агрессивном режиме система постоянно находится в рынке, удерживая либо длинную, либо короткую позицию. В этом режиме система открывает все сделки против тренда независимо от режима. Выход из длинной позиции происходит при появлении сигнала на продажу (DVO > 70 %), а выход из короткой позиции — при появлении сигнала на покупку (DVO < 40 %).
- Медвежий режим: система открывает как длинные, так и короткие позиции по правилам возврата к среднему значению.
- Бычий режим: система открывает как длинные, так и короткие позиции по правилам свинг-трейдинга.
При работе в промежуточном режиме система допускает торговлю против тренда только в периоды повышенной волатильности на медвежьем рынке. Система не открывает короткие позиции на бычьем рынке.
- Медвежий режим: система открывает как длинные, так и короткие позиции по правилам возврата к среднему значению.
- Бычий режим: система открывает только длинные позиции по правилам свинг-трейдинга; короткие позиции на бычьем рынке не открываются.
В консервативном режиме система торгует только в направлении доминирующего рыночного режима и никогда не открывает сделки против тренда.
- Медвежий режим: система открывает только короткие позиции по правилам возврата к среднему значению; длинные позиции не открываются.
- Бычий режим: система открывает только длинные позиции по правилам свинг-трейдинга; короткие позиции не открываются.
Если рыночный режим меняется при наличии открытых позиций, они не закрываются сразу. Вместо этого правила выхода из рынка меняются в соответствии с новым рыночным режимом. Например, предположим, что мы удерживаем длинную позицию на медвежьем рынке, то есть торгуем по правилам возврата к среднему значению. Если цена закрытия оказывается выше канала гистерезиса, то, согласно правилам системы, рынок переходит в бычий режим. Система перестанет искать сигнал выхода из нашей длинной позиции на основе DVO. Она будет ждать сигнала истощения от SVAPO и исполнит лимитный ордер на уровне наибольшей из максимальных цен последних двух баров.
Бэктест
Мы провели бэктест с оптимизацией по акциям NVDA, AAPL, MSFT, GOOG, AMZN и META за последние пять лет, стремясь получить наилучший коэффициент Шарпа. В ходе бэктестов мы использовали агрессивный режим MR Swing.

Рис. 11. Настройки бэктеста для выбора символов с наилучшим коэффициентом Шарпа

Рис. 12. Результаты бэктеста для шести крупнейших технологических компаний, котирующихся на Nasdaq, с сортировкой по максимальному коэффициенту Шарпа
Затем мы провели бэктест по символу AMZN.US, который показал наилучший коэффициент Шарпа.

Рис. 13. Настройки бэктеста для одного тестового прогона по акциям AMZN за последние пять лет

Рис. 14. Динамика баланса/капитала в ходе одного тестового прогона по акциям AMZN за последние пять лет
Рис. 15. Статистика одного тестового прогона по акциям AMZN за последние пять лет
Рис. 16. Время входов в позиции по результатам одного тестового прогона по акциям AMZN за последние пять лет

Рис. 17. Корреляция показателей MFE и MAE в рамках одного тестового прогона по акциям AMZN за последние пять лет
Рис. 18. Время удержания позиций по результатам одного тестового прогона по акциям AMZN за последние пять лет
Вот код советника MRSwingEA.mq5. Ознакомьтесь с заголовочным файлом стратегии и файлами индикаторов в разделе "Вложения" внизу этой статьи.
//+------------------------------------------------------------------+ //| MRSwingEA.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property strict #include <Trade\Trade.mqh> #include "MRSwing.mqh" enum ENUM_MR_SWING_MODE { MODE_AGGRESSIVE, MODE_INTERMEDIATE, MODE_CONSERVATIVE }; //--- Input parameters input ENUM_MR_SWING_MODE InpMode = MODE_AGGRESSIVE; input double InpLotSize = 1.0; input int InpMagic = 123456; //--- Global variables CMRSwing *ExtStrategy; CTrade ExtTrade; int ExtLastBarTime; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { ExtStrategy = new CMRSwing(); ExtTrade.SetExpertMagicNumber(InpMagic); ExtLastBarTime = 0; return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { delete ExtStrategy; } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { MqlRates rates[]; ArraySetAsSeries(rates, false); int copied = CopyRates(_Symbol, _Period, 0, 1000, rates); if(copied < 252) return; int rates_total = copied; int last_bar = rates_total - 1; if(rates[last_bar].time <= ExtLastBarTime) return; // Only run on new bar ExtLastBarTime = (int)rates[last_bar].time; ExtStrategy.Calculate(rates, rates_total, 0); int regime = ExtStrategy.GetRegime(); double close[], high[], low[]; ArrayResize(close, rates_total); ArrayResize(high, rates_total); ArrayResize(low, rates_total); for(int i = 0; i < rates_total; i++) { close[i] = rates[i].close; high[i] = rates[i].high; low[i] = rates[i].low; } double dvo_val = ExtStrategy.GetDVO(close, high, low, last_bar); double v_low, v_high; ValueCharts(high, low, close, 5, last_bar, v_low, v_high); double svapo_val = ExtStrategy.GetSVAPO(last_bar); //--- SVAPO Upper Band //--- We need a buffer of SVAPO to calculate StdDev double svapo_hist[]; ArrayResize(svapo_hist, rates_total); for(int i = 0; i < rates_total; i++) svapo_hist[i] = ExtStrategy.GetSVAPO(i); double upper_band = 1.5 * StdDev(svapo_hist, 100, last_bar); //--- Strategy Logic --- //--- 1. Position Info bool is_long = PositionSelectByMagic(_Symbol, InpMagic) && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY; bool is_short = PositionSelectByMagic(_Symbol, InpMagic) && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL; //--- 2. Bear Regime (Mean Reversion) if(regime == -1) { if(dvo_val < 0.40 && InpMode != MODE_CONSERVATIVE) { if(!is_long) ExtTrade.Buy(InpLotSize, _Symbol); } else if(dvo_val > 0.70) { if(!is_short) ExtTrade.Sell(InpLotSize, _Symbol); } //--- Exit Bear if(is_long && dvo_val > 0.50) ExtTrade.PositionClose(_Symbol); if(is_short && dvo_val < 0.50) ExtTrade.PositionClose(_Symbol); } //--- 3. Bull Regime (Swing) else if(regime == 1) { if(v_low < -7.5) { double limit_price = MathMin(rates[last_bar].low, rates[last_bar - 1].low); //--- Buy Limit pending order ExtTrade.BuyLimit(InpLotSize, limit_price, _Symbol); } if(is_long && svapo_val > upper_band) { double limit_exit = MathMax(rates[last_bar].high, rates[last_bar - 1].high); ExtTrade.SellLimit(InpLotSize, limit_exit, _Symbol); } if(is_short) ExtTrade.PositionClose(_Symbol); } //--- Variations if(InpMode == MODE_INTERMEDIATE) { if(regime == -1 && (is_long || is_short)) ExtTrade.PositionClose(_Symbol); } if(InpMode == MODE_CONSERVATIVE) { if(regime == 1 && is_short) ExtTrade.PositionClose(_Symbol); if(regime == -1 && is_long) ExtTrade.PositionClose(_Symbol); } } //+------------------------------------------------------------------+ //| PositionSelectByMagic helper | //+------------------------------------------------------------------+ bool PositionSelectByMagic(string symbol, int magic) { for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { if(PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_MAGIC) == magic) return(true); } } return(false); }
Заключение
В этой статье мы представили торговую систему MR Swing — количественную систему свинг-трейдинга, основанную на возврате к среднему значению и использующую индикаторы Value Chart (график стоимости) и Price Action Profile (профиль ценового движения). Мы описали перенос этой системы на MQL5 и привели результаты бэктеста на акциях AMZN за последние пять лет. Результаты бэктеста показывают, что, хотя с момента публикации системы её авторами прошло более пятнадцати лет, её принципы остаются актуальными.
Стоит отметить, что бэктесты проводились с параметрами по умолчанию и без какой-либо оптимизации, за исключением выбора торгового инструмента из числа наиболее ликвидных технологических акций, торгуемых на Nasdaq. Таким образом, остаётся широкое поле для экспериментов с аналогичными торговыми инструментами, различными рынками, другими классами активов, а также с комбинациями параметров советника и индикаторов.
Литература
Helweg, Mark W., and David C. Stendahl. Dynamic Trading Indicators: Winning with Value Charts and Price Action Profile. John Wiley & Sons, Inc., 2002
Abrams, David, and Scott Walker. MR Swing: A Quantitative System for Mean-Reversion and Swing Trading in Market Regimes. Mar. 2010
| Имя файла | Описание |
|---|---|
| Experts/MRSwing/MRSwingEA.mq5 | Файл советника MR Swing |
| Include/MRSwing/MRSwing.mqh | Заголовочный файл стратегии |
| Include/MRSwing/Indicators.mqh | Заголовочный файл индикаторов |
| MRSwingEA.Daily.20210101_20260430.030.ini | Настройки бэктеста для оптимизации в режиме "Все символы из Обзора рынка" |
| MRSwingEA.AMZN.US.Daily.20210101_20260430.030.ini | Настройки бэктеста для AMZN.US |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/22783
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.