В прошлой статье мы рассмотрели первый способ поиска пар🎏 для стратегии «Парного трейдинга», который работал относительно быстро, но результаты требовали тщательной обработки напильником🛠. То есть дополнительной визуальной👀 проверки графиков📈 для выбора подходящих кандидатов.
В этот раз мы рассмотрим метод поиска коинтеграции (подробнее здесь) по методу Дики-Фуллера.
📽В предыдущих сериях
В основе «Парного трейдинга» лежит предположение о «неведомой» сильной связи двух акций, цены которых движутся в одном направлении, но с разной скоростью. При поступлении сигнала, покупаем отстающую и одновременно продаем убежавшую бумаги.
История каждого актива должна удовлетворять следующим требованиям:
- Длина историй должна быть одинаковой.
- Значения должны быть переведены в относительные величины.
- Каждая история цен не должна быть стационарной.
Здесь можно ознакомиться с общими принципами стратегии и тем, что надо искать. А здесь рассмотрена подготовка историй цен акций для поиска пар.
☝Выбор акций для поиска
- Американский рынок.
- История за предыдущие 360 календарных дней.
- Цена более $10.
- Средний объем более 500 тыс. акций в день.
- ATR за 13 дней более $0.40.
Всего таких ~1500 штук, минимальная длина истории 245. После фильтрации на отсутствие коинтеграции осталось 1028 штук, что дает нам 0,5 млн. пар.
🔍2 из 3: тест Дики-Фуллера
Данный тест проверяет времянной ряд (историю изменения цены) на стационарность (наличие коинтеграции). Осуществляется проверка наличия у времянного ряда единичного корня, о чем подробнее написано в Вики. Реализован в библиотеке statsmodels.
Функция проверки стационарности:
statsmodels.tsa.stattools.adfuller(X)
Выбираем пары с оценкой ниже 5% критического порога и p-значением меньше 0,001. Код поиска пар ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
def find_cointegrated_pairs_adf(symbol_prices, need_preparation=False): # готовим матрицы для сбора оценок и p-значений n = len(symbol_prices) score_matrix = np.zeros((n, n)) pvalue_matrix = np.ones((n, n)) symbols = list(symbol_prices.keys()) pairs = [] for i in range(n): for j in range(i+1, n): S1 = symbol_prices[symbols[i]] S2 = symbol_prices[symbols[j]] # подготавливаем ряды, если надо if need_preparation: S1, S2 = prepare_vectors(S1, S2, to_performance=True) # проверяем коинтеграцию result = adfuller(S1-S2) # заполняем матрицы значений score = result[0] pvalue = result[1] crit = result[4] score_matrix[i, j] = score pvalue_matrix[i, j] = pvalue # добавляем пары без единичных корней с p-значением менее 0.001 if score < crit['5%'] and pvalue < 0.001: pairs.append((symbols[i], symbols[j], pvalue)) # сортируем пары по возрастанию p-значения sorted_pairs = sorted(pairs, key=operator.itemgetter(2)) return score_matrix, pvalue_matrix, sorted_pairs |
На февраль 2017 года метод нашел 1599 пар (~0.13%), в 4 раза меньше, чем прошлый способ. Поиск загружал все ядра процессора, кроме одного и занял почти 2 часа. Вот пять пар с наименьшим p-значением:
- XIV, SVXY
- TRN, AMAT
- IPHI, TRN
- COMM, KN
- TRN, TNA
- …
На первом месте старый знакомый (пара на $VIX), а вот дальше в каждой паре есть компания. Просмотр графиков данных пар не выявил абсолютных провалов, в отличие от прошлого раза.
🎏Проверка найденных пар
В проверке будут участвовать следующие пары:
- TRN, AMAT
- COMM, KN
- AEL, MRC
👌TRN, AMAT
TRN — диверсифицированная промышленная компания.
AMAT — технологическая компания, занимающаяся полупроводниками и дисплеями.
Спред действительно стационарен и дает сигналы для торговли.
👌COMM, KN
COMM — технологическая компания занимающаяся различными видами компьютерных сетей.
KN — технологическая компания, поставщик аудио-оборудования.
График удовлетворяет задаче и дает хорошее количество сигналов.
👌AEL, MRC
AEL — американская страховая компания.
MRC — промышленная компания, поставщик труб и сопутствующего оборудования для энергетической промышленности.
График стационарен и дает хорошее количество сигналов.
🏁Вывод
Этот способ работает примерно в два с лишним раза медленнее первого и дает значительно лучшие результаты. Для быстрого поиска большого количества пар не подходит, но если запускать его на ночь, то с утра можно приступить к анализу потенциальных кандидатов.
В следующей, заключительной, статье о поиске пар я опишу принцип, работающий в десятки раз быстрее и дающий достойные результаты, которые при желании можно дополнительно прогнать через тест Дики-Фуллера.
💬В комментариях напишите, что вы думаете о парном трейдинге. Поделитесь своим опытом и находками. Если нужен код скачивания истории цен с Quandl, тоже пишите в комментариях.
Автор Quantrum.me
Telegram-канал📣: @quantiki
Подбор и тестирование портфелей. Подключение стратегий к IB.