English Deutsch 日本語
preview
Статистический арбитраж с использованием коинтегрированных акций (Часть 1): тесты Энгла — Грейнджера и Йохансена

Статистический арбитраж с использованием коинтегрированных акций (Часть 1): тесты Энгла — Грейнджера и Йохансена

MetaTrader 5Торговые системы |
36 2
Jocimar Lopes
Jocimar Lopes

Введение

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

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

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

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


Успех на бэктесте, провал в реальности

Недавно мы с партнером были на неформальной встрече с трейдером, которая привыкла работать с долларовыми контрактами на бразильской бирже B3. Она сказала, что была бы рада иметь ресурсы, чтобы попробовать "какой-нибудь статарб на сельскохозяйственных товарах", но, будучи традиционным дискреционным трейдером, обученным техническому анализу и Price Action, без глубоких знаний в математике или статистике, она была уверена, что статистический арбитраж будет для неё "недосягаем". В её словах отразилось то самое распространенное убеждение, о котором мы говорили в начале: вера в то, что статистический арбитраж не под силу розничному трейдеру.

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

Нашими первыми "детскими шагами" стала статья, которую я недавно опубликовал здесь, о математике и менеджере хедж-фонда Джиме Саймонсе и его легендарном фонде Medallion. В той статье мы использовали простейшего советника (EA) для иллюстрации статистического арбитража между парами XAUEUR и XAUUSD. 

На графике ниже показаны результаты нашего бэктеста, опубликованные ранее.

Рис. 1 — Результаты бэктеста советника для парного трейдинга

Рис. 1 — Результаты бэктеста советника для парного трейдинга (предыдущая статья)

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

Рис. 2 — История отчета за две недели работы советника для парного трейдинга на демо-счете

Рис. 2 — История отчета (ReportHistory) за две недели работы советника для парного трейдинга на демо-счете

Вероятные причины

Когда мы ищем причины провала торговой стратегии, велик соблазн начать с "подгонки параметров". Но давайте попробуем подойти к этому более системно и исследуем коренные причины.

Просто две неудачные недели подряд?

Может быть, это были просто две плохие недели на рынке? Ведь даже на бэктесте у нас были периоды безубыточности и даже небольшие просадки, не так ли?

Да, но даже в наши самые худшие недели у нас никогда не было такого количества убытков подряд. В этом тесте в реальном времени наше максимальное количество побед подряд составило всего четыре, в то время как максимальное количество последовательных убытков достигло сорока двух! В то время как среднее количество побед подряд было всего единица, среднее число убытков подряд равнялось пяти. Короче говоря, мы проигрывали почти в каждой сделке!

Рис. 3 — Последовательность убытков советника для парного трейдинга на демо-счете

Рис. 3 — Последовательность убытков советника для парного трейдинга на демо-счете 

Очевидно, что-то было в корне не так; что-то сильно отличалось от условий нашего бэктеста. Более того, по определению парный трейдинг является рыночно-нейтральным, то есть он не привязан к доходности конкретных ценных бумаг.
"Парный трейдинг в своей простейшей форме — это рыночно-нейтральная инвестиционная стратегия". [Gatev et al. 2006]

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

Джим Саймонс заработал огромные деньги, пока рынки рушились во время ипотечного кризиса 2008 года. Фонд Medallion был не единственным хедж-фондом, полагавшимся на статистический арбитраж. Так почему же они зарабатывали миллионы, пока их коллег "расчленял" крах? Похоже, было что-то, что они делали лучше конкурентов. Несмотря на строгую секретность их операций, они оставили несколько ценных подсказок, которые сегодня кажутся очевидными, но часто игнорируются розничными трейдерами. Команда Medallion всегда опиралась на огромные объемы высококачественных данных. Также они инвестировали в лучшие вычислительные мощности и скорость маршрутизации ордеров.

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

Новая среда?

Может быть, дело в смене брокера и другом спреде? Для парного трейдинга в краткосрочных операциях с тысячами входов в неделю спред bid/ask легко может стать проблемой. Как вы знаете, услуги форекс-брокеров обычно оплачиваются через спред и/или комиссию, рассчитываемую от объема торгов.

Но мы можем исключить спред. Для бэктеста я намеренно использовал счет с высоким спредом — в среднем около 170 пипсов для золота и 50 пипсов для XAUEUR. Для демо-счета я перешел на счет с «сырым» спредом и комиссией, где спред составлял в среднем около 20 пипсов для XAUUSD и 10 пипсов для XAUEUR.

Поскольку на демо-счете спред был ниже, он вряд ли является виновником неудачи.

Проскальзование?

Арбитраж обычно чувствителен к таймингу, и наша стратегия парного трейдинга — не исключение. Давайте посмотрим, что говорится в соглашении о политике исполнения нашего CFD-брокера относительно времени исполнения ордеров:

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

Последним на ум приходит "обычный подозреваемый": проскальзывание. Мы отправляли мгновенные ордера по рыночной цене, и кто-то закрывал этот разрыв первым. Если дело в этом, то здесь нет ничего неправильного. Такого рода "фронтраннинг" легален. По сути, это и есть правила игры в арбитраже. Это лишь означает, что у кого-то больше и/или лучше ресурсы: мощнее оборудование, серверы в дата-центре рядом с брокером (colocation) или просто программное обеспечение, оптимизированное для высокой производительности, в то время как мы используем прототип. Вероятно, мы проигрывали гонку сразу нескольким игрокам, у каждого из которых было одно из этих преимуществ, а у некоторых — и все сразу. То есть крупные игроки просто унижали наш скромный прототип. 

В руководстве пользователя MetaTrader 5 указано, что мы можем симулировать задержки на бэктестах

"Тестер стратегий позволяет эмулировать сетевые задержки во время работы эксперта (EA), чтобы обеспечить условия тестирования, близкие к реальным. Между отправкой торгового запроса и его исполнением в тестере вставляется определенная временная задержка. За время от отправки запроса до его исполнения цена может измениться. Это позволяет пользователям оценить, как скорость обработки сделок влияет на результаты торговли.

В режиме немедленного исполнения пользователи могут дополнительно проверить реакцию советника на реквот от торгового сервера. Если разница между запрошенной ценой и ценой исполнения превышает значение отклонения (deviation), указанное в ордере, советник получает реквот."

Мы провели этот тест, установив случайную задержку... 

Рис. 4 — Настройки тестера MetaTrader 5 с симуляцией задержки

Рис. 4 — Настройки тестера MetaTrader 5 с симуляцией задержки 

... и БИНГО! Даже если проскальзывание — не единственный виновник, наш "обычный подозреваемый", похоже, является здесь главным фигурантом.

Рис. 5 — Результирующий график после запуска с симуляцией задержки

Рис. 5 — Результирующий график после запуска с симуляцией задержки 

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


Основная проблема: исполнение

Арбитраж можно использовать для извлечения прибыли, покупая и продавая что угодно: от тостеров до токенов, от сырьевых товаров до валют и всего, что находится между ними. Можно представить, что это началось еще тогда, когда люди стали обмениваться товарами — даже до появления денег в нашем современном понимании. Концепция проста: если некий гипотетический тостер недооценен на рынке А, возможно, мы сможем купить его там и продать на рынке Б, где он торгуется по "справедливой цене". Если разницы в цене достаточно для покрытия транзакционных издержек, мы можем получить прибыль с минимальным риском. 

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

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

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

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


От корреляции к коинтеграции

Ранее мы представляли финансовый рынок так, как его описывал Джим Саймонс — как загадку, находящуюся в состоянии непрерывного изменения. Загадку, которая требует от операторов постоянной адаптации для максимизации прибыли и минимизации убытков. Мы построили минимальный "портфель" из двух активов, XAUUSD и XAUEUR, и выбрали отклонение от среднего значения их котировок относительно доллара США в качестве искомой статистической взаимосвязи. Расширение спреда и последующий возврат к среднему были нашими сигналами на вход и выход соответственно.

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

Критерием, который мы использовали для выбора этих пар, была корреляция. Мы ограничили наш поиск основными валютными парами и выбрали наиболее коррелированную пару в заданном временном интервале. Теперь мы включим коинтеграцию в процесс отбора активов для нашего портфеля.

Согласно статистикам: 

"Корреляция фиксирует только краткосрочные взаимосвязи, в то время как коинтеграция фиксирует равновесные отношения в долгосрочной перспективе. Две переменные могут быть сильно коррелированы, но не коинтегрированы, и наоборот." [Alexander, 2001]

Коэффициент корреляции говорит нам о том, насколько цены XAUUSD и XAUEUR склонны одновременно двигаться в одном направлении. Тест на коинтеграцию же обнаружит эквивалентность между активами, которая сохраняется в долгосрочном периоде. Кроме того, при использовании коэффициента корреляции мы ограничены двумя активами, но при тестировании на коинтеграцию это ограничение не применяется — это означает, что мы можем тестировать несколько кандидатов в портфель одновременно. Это делает тест на коинтеграцию идеальным для наших целей.

Как трейдерам или разработчикам советников, нам не обязательно знать всю математику, стоящую за тестами на коинтеграцию. Поскольку мы будем использовать интеграцию Python для MetaTrader 5 для анализа данных и выбора портфеля, мы можем использовать готовые Python-функции из специализированных библиотек. Кроме того, ИИ-ассистенты (включая встроенные в MetaTrader 5 GPT), могут помочь сэкономить время при разработке простых скриптов для этих целей.

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

Если вы хотите углубиться в тему, я бы рекомендовал заглянуть в книгу, процитированную выше (автор Кэрол Александер), а именно в главу 12, которая полностью посвящена коинтеграции. В этой книге госпожа Александер прослеживает путь от истории использования коинтеграции в финансах до её применимости на различных рынках, включая Forex. Её учебник является очень известным справочным пособием в этой области.

Давайте посмотрим, как мы можем применить два наиболее рекомендуемых теста на коинтеграцию — тест Энгла — Грейнджера и тест Йохансена — к различным классам активов и как интерпретировать их результаты.


Тест Энгла — Грейнджера на коинтеграцию

Тест на коинтеграцию Энгла — Грейнджера можно использовать для оценки уровня коинтеграции между двумя активами. Его цель — ответить на вопрос: "Разделяют ли эти два актива долгосрочные равновесные отношения?" С точки зрения трейдеров и разработчиков советников, этот тест скажет нам, обладает ли спред между двумя активами свойством возврата к среднему. Однако ответ не является бинарным ("да" или "нет"). Подобно коэффициенту корреляции Пирсона, который мы рассматривали ранее, тест возвращает относительное значение (p-value). Его следует сравнивать с результатами других пар, чтобы определить, какая из них является наиболее интегрированной. 

P-value — это вероятность того, что мы наблюдаем такие данные при условии, что коинтеграция ОТСУТСТВУЕТ. То есть, чем меньше p-value, тем лучше. Существуют общепринятые пороговые значения: p-value < 0.05: обычно принимается как умеренный сигнал коинтеграции. p-value < 0.01: сильный сигнал и p-value > 0.05: сигнал об отсутствии коинтеграции. Как и всегда в трейдинге, вам следует тонко настраивать эти пороги под ваш конкретный сценарий использования.

Для выполнения обоих тестов на коинтеграцию в коде Python используется известная библиотека statsmodels: 

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

from datetime import datetime, timedelta
import MetaTrader5 as mt5
import pandas as pd
import numpy as np
from statsmodels.tsa.stattools import coint
import matplotlib.pyplot as plt

# connect to MetaTrader 5 terminal
if not mt5.initialize(login=********, server="MetaQuotes-Demo",password="********"):
    print("initialize() failed, error code =",mt5.last_error())
    quit()

# Forex majors - check your Market Watch names
symbols = ['EURUSD', 'GBPUSD', 'USDJPY', 'AUDUSD', 'USDCAD', 'NZDUSD', 'USDCHF']

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

# define the timeframe and the number of days
timeframe = mt5.TIMEFRAME_D1  # Daily
n_days = 600
utc_to = datetime.now()
utc_from = utc_to - timedelta(days=n_days)

Мы будем использовать переменные utc_from и utc_to для запроса котировок символов с помощью функции mt5.copy_rates_range за период в 600 торговых дней. В одном году примерно 250 торговых дней.

# download historical data for each symbol
data = {}

for symbol in symbols:
    # Make sure the symbol is available in Market Watch
    mt5.symbol_select(symbol, True)
    
    # Get historical rates
    rates = mt5.copy_rates_range(symbol, timeframe, utc_from, utc_to)
    
    if rates is None or len(rates) == 0:
        print(f"No data for {symbol}.")
        continue

    df = pd.DataFrame(rates)
    df['time'] = pd.to_datetime(df['time'], unit='s')
    df.set_index('time', inplace=True)
    data[symbol] = df['close']

В этом цикле for мы запрашиваем цены закрытия для каждого символа, преобразуем их в объект Pandas DataFrame и устанавливаем массив time в качестве индекса нашей таблицы данных.

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

# Store cointegration test results
results = []

# Test all unique pairwise combinations for cointegration
pairs = [(a, b) for i, a in enumerate(data.columns) for j, b in enumerate(data.columns) if i < j]

print("Cointegration test results (Engle-Granger):")
for a, b in pairs:
    score, pvalue, _ = coint(data[a], data[b])
    results.append((a, b, pvalue))
    print(f"{a} vs {b} | p-value: {pvalue:.4f}")

Индекс (['EURUSD', 'GBPUSD', 'USDJPY', 'AUDUSD', 'USDCAD', 'NZDUSD', 'USDCHF'], dtype='object')
Результаты теста на коинтеграцию (Энгл — Грейнджер):

EURUSD vs GBPUSD | p-value: 0.3183
EURUSD vs USDJPY | p-value: 0.6990
EURUSD vs AUDUSD | p-value: 0.9308
EURUSD vs USDCAD | p-value: 0.9206
EURUSD vs NZDUSD | p-value: 0.9741
EURUSD vs USDCHF | p-value: 0.4342
GBPUSD vs USDJPY | p-value: 0.3273
GBPUSD vs AUDUSD | p-value: 0.7995
GBPUSD vs USDCAD | p-value: 0.6264
GBPUSD vs NZDUSD | p-value: 0.7810
GBPUSD vs USDCHF | p-value: 0.0238
USDJPY vs AUDUSD | p-value: 0.6299
USDJPY vs USDCAD | p-value: 0.5620
USDJPY vs NZDUSD | p-value: 0.6398
USDJPY vs USDCHF | p-value: 0.2377
AUDUSD vs USDCAD | p-value: 0.1260
AUDUSD vs NZDUSD | p-value: 0.2920
AUDUSD vs USDCHF | p-value: 0.5980
USDCAD vs NZDUSD | p-value: 0.0052
USDCAD vs USDCHF | p-value: 0.8574
NZDUSD vs USDCHF | p-value: 0.6384

Наиболее коинтегрированная пара: USDCAD and NZDUSD (p = 0.0052)

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

Рис. 6 — График коинтегрированного спреда между USDCAD и NZDUSD

Рис. 6 — График коинтегрированного спреда между USDCAD и NZDUSD со средним значением и двумя стандартными отклонениями

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

Коэффициент хеджирования

Обратите внимание на значение -1.54, использованное при расчете долгосрочного спреда:

Spread = USDCAD - (-1.54) * NZDUSD

или

USDCAD + 1.54 * NZDUSD

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

Спред фиксирует относительную неверную оценку активов. На языке парного трейдинга мы бы сказали: когда спред поднимается выше +2 STD, он исторически высок, и мы можем рассмотреть возможность продажи спреда. И наоборот, когда спред опускается ниже -2 STD, он исторически низок, и мы можем рассмотреть возможность покупки спреда.

Чтобы сконструировать рыночно-нейтральную пару для торговли на основе этих данных: на каждую 1 единицу объема по USDCAD, которую мы покупаем (long), мы должны одновременно продать 1.54 единицы NZDUSD (или наоборот, в зависимости от направления сигнала). Затем, когда спред возвращается к среднему значению, мы выходим из сделки, закрывая обе позиции.

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


Тест Йохансена на коинтеграцию

Тест на коинтеграцию Йохансена призван ответить на тот же вопрос, что и тест Энгла — Грейнджера: разделяют ли эти активы долгосрочные равновесные отношения? На практике и для наших целей основное различие заключается в том, что тест Йохансена может оценивать более двух активов одновременно и определять, обладают ли эти N активы общим спредом, возвращающимся к среднему значению. Его можно представить как мощный инструмент для оценки возможной коинтеграции в группе акций одного сектора, например, нефтегазовых компаний, технологических гигантов и так далее. Это позволяет нам находить три, четыре или более акций (или любых других активов), которые связаны общим долгосрочным равновесием.

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

Тест выдает два основных статистических показателя: статистика следа и критическое значение.

Для каждого возможного ранга ($0, 1, 2, ..., N-1, где N — количество рядов) вы получаете одно значение статистики следа. Чем выше значение Trace Statistic, тем сильнее доказательства наличия коинтеграции. Критическое значение — это порог, с которым сравнивается статистика следа. Оно зависит от:

  • Количества оцениваемых переменных;
  • Длины выборки;
  • Уровня значимости (90%, 95% или 99\%).

В данном конкретном случае под количеством переменных мы понимаем количество тестируемых торговых инструментов (символов).

# Johansen Test
from datetime import datetime, timedelta
import MetaTrader5 as mt5
import pandas as pd
import numpy as np
from statsmodels.tsa.stattools import coint
from statsmodels.tsa.vector_ar.vecm import coint_johansen
import matplotlib.pyplot as plt
# Create a matrix for the test
log_prices = data.apply(np.log)

johansen_result = coint_johansen(log_prices, det_order=0, k_ar_diff=1)

# Trace statistics and critical values
print("\nJohansen Test Results (Trace Statistic):")
for i, stat in enumerate(johansen_result.lr1):
    cv = johansen_result.cvt[i, 1]  # 5% critical value
    print(f"Rank {i}: Trace Stat = {stat:.2f} | 5% CV = {cv:.2f} | {'Significant' if stat > cv else 'Not significant'}")

Вы можете просмотреть результаты для других уровней значимости, установив соответствующие индексы:
johansen_result.cvt[i, 0] для 90%
johansen_result.cvt[i, 2] для 99%

Для уровня доверия 95% (критическое значение 5%) ниже приведены наши результаты для основных валютных пар за тот же период и на том же таймфрейме (D1), который мы использовали для теста Энгла — Грейнджера.

Индекс (['EURUSD', 'GBPUSD', 'USDJPY', 'AUDUSD', 'USDCAD', 'NZDUSD', 'USDCHF'], dtype='object')

Результаты теста Йохансена (Trace Statisitc):

Rank 0: Trace Stat = 105.25 | 5% CV = 125.62 | Not significant
Rank 1: Trace Stat = 68.10 | 5% CV = 95.75 | Not significant
Rank 2: Trace Stat = 42.01 | 5% CV = 69.82 | Not significant
Rank 3: Trace Stat = 25.83 | 5% CV = 47.85 | Not significant
Rank 4: Trace Stat = 11.73 | 5% CV = 29.80 | Not significant
Rank 5: Trace Stat = 5.74 | 5% CV = 15.49 | Not significant
Rank 6: Trace Stat = 0.58 | 5% CV = 3.84 | Not significant

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

Однако если мы протестируем две известные акции — Google (GOOGL) и Nvidia (NVDA) — на таймфрейме H1 (см. ниже), мы получим статистически значимый результат коинтеграции для того же периода и доверительного интервала.

Index(['NVDA', 'GOOGL'], dtype='object')
Количество наблюдений: 5769
Количество переменных: 2

Результаты теста Йохансена (Trace Statisitc):Rank 0: Trace Stat = 18.71 | 5% CV = 15.49 | Significant
Rank 1: Trace Stat = 0.29 | 5% CV = 3.84 | Not significant

Наиболее коинтегрированная пара (Энгл — Грейнджер): NVDA и GOOGL | p-value: 0.0824

Рис. 7 — График коинтегрированного спреда между NVDA и GOOGL

Рис. 7 — График коинтегрированного спреда между NVDA и GOOGL со средним значением и двумя стандартными отклонениями

Хорошо, у нас есть значимая коинтеграция для GOOGL и NVDA согласно тесту Йохансена, но при этом p-value теста Энгла — Грейнджера выше 0,05, что сигнализирует об отсутствии коинтеграции. Почему результаты противоречат друг другу? Что это значит?

Давайте запустим тот же скрипт, просто изменив порядок символов на противоположный, и вы увидите одну интересную разницу между тестами Энгла — Грейнджера и Йохансена.

Index(['GOOGL', 'NVDA'], dtype='object')
Количество наблюдений: 5769
Количество переменных: 2

Результаты теста Йохансена (Trace Statisitc):Rank 0: Trace Stat = 18.71 | 5% CV = 15.49 | Significant
Rank 1: Trace Stat = 0.29 | 5% CV = 3.84 | Not significant

Наиболее коинтегрированная пара (Энгл — Грейнджер): GOOGL и NVDA | p-value: 0.0403

Рис. 8 — График коинтегрированного спреда между GOOGL и NVDA

Рис. 8 — График коинтегрированного спреда между GOOGL и NVDA со средним значением и двумя стандартными отклонениями

Как вы можете видеть, тест Энгла — Грейнджера чувствителен к порядку активов, в то время как на результаты теста Йохансена эта смена не повлияла. Если вы углубитесь в тонкости теста Йохансена, то узнаете, что в некоторых случаях он также может проявлять чувствительность к порядку — в частности, на малых выборках или если результаты находятся на грани статистической значимости. Но, как правило, он теоретически инвариантен к порядку. Если вы обнаружите, что он возвращает противоречивые результаты после смены порядка, первым делом попробуйте увеличить объем данных в вашей выборке. Именно поэтому мы заменили таймфрейм D1 (дневной) на H1 (часовой) — чтобы предоставить тесту больший объем данных для анализа.

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


Тестирование стационарности спреда

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

В той же библиотеке Python, которую мы использовали для тестов на коинтеграцию, есть готовые функции для проверки стационарности: Расширенный тест Дики — Фуллера ("ADF") и тест Квятковского — Филлипса — Шмидта — Шина ("KPSS").

from statsmodels.tsa.stattools import adfuller

(...)

adf_result = adfuller(spread)

ADF Test on Spread:
 ADF Statistic : -3.0946
 p-value         : 0.0270
 Critical Values:
    1%: -3.4315
    5%: -2.8620
    10%: -2.5670

✅ Спред стационарен (нулевая гипотеза отвергается).

from statsmodels.tsa.stattools import kpss

(...)

def run_kpss(series, regression='c'):
    statistic, p_value, lags, crit_values = kpss(series, regression=regression, nlags='auto')

(...)

# Run KPSS test on the residuals
run_kpss(spread, regression='c')  # 'c' = test for level stationarity (use 'ct' for trend)

KPSS Test on Spread:
 KPSS Statistic : 2.2702
 p-value          : 0.0100
 Lags Used      : 44
 Critical Values:     10% : 0.347
    5% : 0.463
    2.5% : 0.574
    1% : 0.739

❌ Спред НЕ стационарен (нулевая гипотеза о стационарности отвергается).

Похоже, мы снова получили противоречивые результаты. ADF говорит, что спред стационарен, а KPSS с этим не согласен. Без паники! Разработчики statsmodels предоставили четкое объяснение того, как интерпретировать любую комбинацию результатов этих тестов. Я приведу цитату из документации statsmodels, потому что, на мой взгляд, сложно выразиться еще лаконичнее и понятнее.

"Случай 1: Оба теста говорят "не стационарен- Ряд не стационарен.

"Случай 2: Оба теста говорят "стационарен"- Ряд стационарен.

Случай 3: KPSS — "стационарен", ADF — "нет": Ряд стационарен относительно тренда. Нужно удалить тренд, чтобы сделать его строго стационарным. Ряд с удаленным трендом проверяется на стационарность.

Случай 4: KPSS указывает на нестационарность, а ADF указывает на стационарность — ряд является стационарным в разностях. Для приведения ряда к стационарности необходимо использовать взятие разностей. Полученный в результате разностей ряд проверяется на стационарность."

Теперь мы понимаем, что наш спред является стационарным в разностях, а не строго стационарным. Что это значит? Это означает, что временной ряд может иметь дрейф, но сами изменения стабильны. Его можно преобразовать в строго стационарный временной ряд, просто взяв разность между последовательными значениями. Неудивительно, что эта техника называется дифференцированием. Она удаляет тренды и сезонность, делая наш временной ряд готовым к более эффективному анализу и прогнозированию.

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

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


Заключение

В этой статье мы сделали еще один шаг в создании нашего фреймворка для статистического арбитража, ориентированного на обычного розничного трейдера. Мы добавили еще два инструмента в наш статистический инструментарий для формирования портфеля: тесты на коинтеграцию Энгла — Грейнджера и Йохансена. Мы разобрали, чего ожидать от каждого из них и как проводить базовую интерпретацию их результатов. Кроме того, мы узнали, как проверять стационарность спреда с помощью расширенного теста Дики — Фуллера ("ADF") и теста Квятковского — Филлипса — Шмидта — Шина ("KPSS").

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

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

Ваши знания о рынках бесценны, потому что вся сложная работа в области математики и статистики уже проделана за вас высококвалифицированными специалистами. Эти наработки доступны бесплатно во многих библиотеках с открытым исходным кодом, включая библиотеки MQL5 на платформе MetaTrader 5, а также благодаря бесценным ИИ-ассистентам, которые помогут с правильной реализацией. 

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

Список литературы

  • Alexander, C. (2001). Market Models: A Guide to Financial Data Analysis, Wiley.
  • Engle, R. F., & Granger, C. W. J. (1987). Co-integration and error correction: representation, estimation, and testing. Econometrica.
  • Gatev, E., Goetzmann, W. N., & Rouwenhorst, K. G. (2006). "Pairs Trading: Performance of a Relative-Value Arbitrage Rule." Review of Financial Studies.
  • Johansen, S. (1988). Statistical analysis of cointegration vectors. Journal of Economic Dynamics and Control.
Вложенный файл Описание
coint.ipynb Упрощенный ноутбук Jupyter с кодом на Python для запуска одиночного теста Энгла — Грейнджера для пары торговых инструментов.
coint_googl_nvda.ipynb  Расширенный ноутбук Jupyter, содержащий код для запуска тестов Энгла — Грейнджера и Йохансена для (теоретически) неограниченного количества инструментов, а также тест на стационарность спреда.
helper_quotes_to_db.ipynb  Вспомогательный файл для сохранения загруженных котировок в базу данных SQLite3. Это также учебное пособие Jupyter кодом Python. Его цель — избежать избыточных загрузок и обеспечить возможность работы в офлайн-режиме.

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

Прикрепленные файлы |
coint.ipynb (132.59 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (2)
Zhuo Kai Chen
Zhuo Kai Chen | 9 июл. 2025 в 07:35
Интересная статья! Я уже работал над подобными проектами и тоже столкнулся с проблемой в живом исполнении :)
Cyberdude
Cyberdude | 9 дек. 2025 в 15:16
Zhuo Kai Chen #:
Интересная статья! Я уже работал над подобными проектами и тоже столкнулся с проблемой в живом исполнении :)
Я также перепробовал множество вариаций Statistical Arbitrage и долгое время пытался создать прибыльный советник. Но, как мы знаем, исполнение - это самый большой недостаток для розничных трейдеров. Поэтому я искренне убежден, что ни один розничный трейдер никогда не сможет стать прибыльным с помощью стратегии Statistical Arbitrage. Это просто невозможно.

Но статья написана хорошо. Новички в этой теме смогут легко ее прочесть.
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Внедрение в MQL5 практических модулей из других языков (Часть 04): Модули time, date и datetime из Python Внедрение в MQL5 практических модулей из других языков (Часть 04): Модули time, date и datetime из Python
В отличие от MQL5, язык программирования Python предлагает контроль и гибкость, когда речь заходит о работе со временем и управлении им. В этой статье мы реализуем модули, аналогичные модулям в языке MQL5 для более удобной обработки дат и времени, как в Python.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Торговые инструменты на MQL5 (Часть 15): Эффекты размытия холста, рендеринг теней и плавная прокрутка колесом мыши Торговые инструменты на MQL5 (Часть 15): Эффекты размытия холста, рендеринг теней и плавная прокрутка колесом мыши
В этой статье мы выполняем улучшение панели холста на MQL5 с помощью новейших визуальных эффектов, включая градиенты размытия для эффекта наложения тумана, рендеринг теней для заголовков и рисование со сглаживанием для получения более плавных линий и кривых. Мы добавим плавную прокрутку колесом мыши на текстовой панели, которая не влияет на масштаб графика, что технически является улучшением.