Создание стратегии систематической торговли парами акций на Python.

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

  • ρ = -1 означает совершенно отрицательную связь. Они движутся в обратном направлении.
  • ρ = 0 означает, что между ними нет линейной зависимости.
  • ρ = 1 означает идеальную линейную связь между наборами данных. Они движутся рука об руку.

Я только что опубликовал новую книгу после успеха Новые технические индикаторы в Python. Он содержит более полное описание и добавление сложных торговых стратегий со страницей Github, посвященной постоянно обновляемому коду. Если вы считаете, что это вас заинтересует, не стесняйтесь перейти по приведенной ниже ссылке или если вы предпочитаете купить версию в формате PDF, вы можете связаться со мной в Linkedin.



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

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

Отказ от ответственности. Приведенный ниже выбор для двух связанных ценных бумаг вдохновлен доктором Эрнестом Чаном. Приведенный ниже бэк-тест не принимает во внимание транзакционные издержки, поэтому в реальности результаты могут отличаться от результатов, приведенных в конце статьи.

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

  • EWA: iShares MSCI Australia ETF стремится воспроизвести результаты инвестиций, которые соответствуют цене и доходности публично торгуемых ценных бумаг на австралийском рынке.
  • EWC: iShares MSCI Canada ETF стремится воспроизвести результаты инвестиций, которые соответствуют цене и доходности публично торгуемых ценных бумаг на канадском рынке.

Они действительно кажутся очень коррелированными, но коинтегрированы ли они? Другими словами, является ли их распространение средним и стационарным? Это то, что мы будем тестировать на исторических данных. Если ряды действительно стационарны, мы сформируем простую стратегию торговли парами на основе их спреда (игнорируя коэффициент хеджирования). Давайте рассмотрим EWA как переменную Asset_2, а EWC как переменную Asset_1 в нашем коде. Вычисленная корреляция между ними, кажется, первая открытая дверь к стратегии:

import numpy as np
np.corrcoef(Asset_1, Asset2)

Результат, который мы получим, будет около 0,85, что является довольно сильной линейной корреляцией. Затем мы отдельно проверим, являются ли два временных ряда стационарными или нет, а затем проверим, являются ли они коинтегрированными или нет. Ниже мы будем использовать statsmodels.tsa.stattools.

def stationarity(a, cutoff = 0.05):
  a = np.ravel(a)
  if adfuller(a)[1] < cutoff:
    print(‘The series is stationary’)
    print(‘p-value = ‘, adfuller(a)[1])
  else:
    print(‘The series is NOT stationary’)
    print(‘p-value = ‘, adfuller(a)[1])
stationarity(Asset_1)
stationarity(Asset_2)
def cointegration(a, b):
  if coint(a, b)[1] < 0.05:
    print(‘The series are cointegrated’)
    print(‘p-value = ‘, coint(a, b)[1])
  else:
    print(‘The series are NOT cointegrated’)
    print(‘p-value = ‘, coint(a, b)[1])
cointegration(Asset_1, Asset_2)

Приведенный выше код даст эти результаты. Естественно, мы не ожидаем, что эти два случайных временных ряда будут стационарными (мне просто нужно было оправдание, чтобы предоставить код), но нас интересует результат теста коинтеграции, который имеет p-значение меньше, чем мы выбрали отсечка (5%), тем самым отвергая нулевую гипотезу об отсутствии коинтеграции. Теперь у нас есть подтверждение для тестирования стратегии, основанной на двух активах.

Если вас также интересуют другие технические индикаторы и использование Python для создания стратегий, то моя книга-бестселлер по техническим индикаторам может вас заинтересовать:



Эта серия НЕ стационарна (Актив_1):

p-значение = 0,057

Эта серия НЕ стационарна (Актив_2):

p-значение = 0,20

Серии коинтегрированы:

p-значение = 0,048

# Another check for stationarity in the ratio between the two
Data[:, 2] = Asset_2 / Asset_1
Ratio = Data[:, 2]
stationarity(Ratio)

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

Серия стационарная

p-значение = 0,032

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

# Standardization assuming the spread
for i in range(len(Data)):
  try:
    Data[i, 3] = (Data[i — lookback:i + 1, 2].mean())
  except IndexError:
    pass
# Calculating Standard deviation
for i in range(len(Data)):
  Data[i, 4] = ((Data[i — lookback:i + 1, 2].std()))
# Standardizing
for i in range(len(Data)):
  Data[i, 5] = (Data[i, 2] — Data[i, 3]) / Data[i, 4]

Основная идея: мы протестируем торговую стратегию нейтральной по отношению к доллару пары на EWA и EWC ETFs. Как мы объяснили выше, условия тестирования на истории будут следующими:

  • Период владения = 60
  • Период ретроспективного анализа = 9
  • Верхний барьер = 2
  • Нижний барьер = -2

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

# Buy/Sell Conditions
for i in range(len(Data)):
  try:
    if Data[i, 2] <= lower_barrier and Data[i — 1, 2] > lower_barrier:
      Data[i + 1, 4] = -1
      Data[i + 1, 5] = 1
    elif Data[i, 2] >= upper_barrier and Data[i — 1, 2] < upper_barrier:
      Data[i + 1, 6] = 1
      Data[i + 1, 7] = -1
    else:
      continue
  except IndexError:
    pass
for i in range(len(Data)):
  try:
    if Data[i, 5] == 1:
      Data[i + holding_period, 8] = (Data[i + holding_period, 1] — Data[i, 1])
    if Data[i, 6] == 1:
      Data[i + holding_period, 8] = (Data[i + holding_period, 0] — Data[i, 0])
    if Data[i, 4] == -1:
      Data[i + holding_period, 9] = (Data[i, 0] — Data[i + holding_period, 0])
    if Data[i, 7] == -1:
      Data[i + holding_period, 9] = (Data[i, 1] — Data[i + holding_period, 1])
  except IndexError:
    pass

На следующем графике показана кривая собственного капитала стратегии (брутто комиссионных) и результаты (при условии, что инвестиции в размере 1 долл. США без кредитного плеча).

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