Парный трейдинг: 2 из 3 способов поиска пар (ДФ)

В прошлой статье мы рассмотрели первый способ поиска пар🎏 для стратегии «Парного трейдинга», который работал относительно быстро, но результаты требовали тщательной обработки напильником🛠. То есть дополнительной визуальной👀 проверки графиков📈 для выбора подходящих кандидатов.

В этот раз мы рассмотрим метод поиска коинтеграции (подробнее здесь) по методу Дики-Фуллера.

📽В предыдущих сериях

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

История каждого актива должна удовлетворять следующим требованиям:

  • Длина историй должна быть одинаковой.
  • Значения должны быть переведены в относительные величины.
  • Каждая история цен не должна быть стационарной.

Здесь можно ознакомиться с общими принципами стратегии и тем, что надо искать. А здесь рассмотрена подготовка историй цен акций для поиска пар.

☝Выбор акций для поиска

  • Американский рынок.
  • История за предыдущие 360 календарных дней.
  • Цена более $10.
  • Средний объем более 500 тыс. акций в день.
  • ATR за 13 дней более $0.40.

Всего таких ~1500 штук, минимальная длина истории 245. После фильтрации на отсутствие коинтеграции осталось 1028 штук, что дает нам 0,5 млн. пар.

🔍2 из 3: тест Дики-Фуллера

Данный тест проверяет времянной ряд (историю изменения цены) на стационарность (наличие коинтеграции). Осуществляется проверка наличия у времянного ряда единичного корня, о чем подробнее написано в Вики. Реализован в библиотеке statsmodels.

Функция проверки стационарности:
statsmodels.tsa.stattools.adfuller(X)

Выбираем пары с оценкой ниже 5% критического порога и p-значением меньше 0,001. Код поиска пар ниже:

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, тоже пишите в комментариях.

🎓Обучение «Парному трейдингу» у профессионалов👍.
Александр Румянцев aka "iamraa"
Автор Quantrum.me
Интересуетесь алготрейдингом на Python? Присоединяйтесь к команде. Пишите в личку или на email.