Фьючерсы - это форма контракта, составленная между двумя сторонами для покупки или продажи количества базового актива в определенную дату в будущем. Эта дата известна как доставка или истечение срока. Когда эта дата достигается, покупатель должен доставить физическое базовое (или денежный эквивалент) продавцу по цене, согласованной на дату формирования контракта.
На практике фьючерсы торгуются на биржах (в отличие от OTC-торговли) по стандартизированным количествам и качествам базового актива. Цены маркируются на рынке каждый день. Фьючерсы невероятно ликвидны и используются в основном для спекулятивных целей.
Подробный перечень всех кодов символов, используемых для фьючерсных контрактов на различных биржах, можно найти на сайте CSI Data: Futures Factsheet.
Основное различие между фьючерсным контрактом и владением активами заключается в том, что фьючерсный контракт имеет ограниченное окно доступности в силу даты истечения срока. В любой момент будет множество фьючерсных контрактов на одном и том же базовом, все с различными датами истечения срока. Контракт с ближайшей датой истечения срока известен как ближайший контракт. Проблема, с которой мы сталкиваемся как количественные трейдеры, заключается в том, что в любой момент времени у нас есть выбор из нескольких контрактов, с которыми можно торговать. Таким образом, мы имеем дело с пересекающимся набором временных рядов, а не с непрерывным потоком, как в случае с акциями или иностранной валютой.
Целью данной статьи является описание различных подходов к построению непрерывного потока контрактов из этого набора множественных серий и выявление компромиссов, связанных с каждым методом.
Основная сложность в попытке создать непрерывный контракт из базовых контрактов с различными поставками заключается в том, что контракты не часто торгуются по одинаковым ценам. Таким образом, возникают ситуации, когда они не обеспечивают плавную сцепку от одного к другому. Это связано с эффектами контанго и бэквардации. Существуют различные подходы к решению этой проблемы, которые мы сейчас обсудим.
К сожалению, в финансовой отрасли не существует единого "стандартного" метода объединения фьючерсных контрактов. В конечном счете выбранный метод будет сильно зависеть от стратегии использования контрактов и метода исполнения.
Этот метод уменьшает разрыв между несколькими контрактами, перенося каждый контракт таким образом, что отдельные поставки плавно соединяются с соседними контрактами.
Ключевая проблема с панамским методом включает в себя введение тенденционного уклонения, которое приведет к большому дрейфу цен. Это может привести к отрицательным данным для достаточно исторических контрактов. Кроме того, происходит потеря относительных ценовых различий из-за абсолютного сдвига ценностей. Это означает, что доходы сложно рассчитать (или просто неправильно).
Подход пропорциональной корректировки аналогичен методологии корректировки распределения запасов в акциях. Вместо того, чтобы принимать абсолютное смещение в последующих контрактах, для пропорциональной корректировки цен исторических контрактов используется соотношение старых расчетных (закрытых) цен к более новым открытым ценам. Это позволяет непрерывный поток без прерывания расчета процентной доходности.
Основная проблема пропорциональной корректировки заключается в том, что любые торговые стратегии, основанные на абсолютном уровне цены, также должны быть подобным образом скорректированы для выполнения правильного сигнала.
Суть этого подхода заключается в создании непрерывного контракта из последовательных контрактов путем принятия линейно взвешенной доли каждого контракта в течение нескольких дней, чтобы обеспечить более плавный переход между каждым.
Например, рассмотрим пять дней сглаживания. Цена на день 1, P1, равна 80% от цены контракта дальнего (F1) и 20% от цены контракта ближнего (N1). Аналогично, на день 2, цена P2=0.6×F2+0.4×N2. На день 5 у нас есть P5=0.0×F5+1.0×N5=N5 и контракт затем просто становится продолжением цены ближнего. Таким образом, через пять дней контракт плавно переходит от дальнего к ближнему.
Проблема с методом ролловера заключается в том, что он требует торговли на все пять дней, что может увеличить затраты на транзакции.
Есть и другие менее распространенные подходы к проблеме, но мы будем избегать их здесь.
Остальная часть статьи будет сосредоточена на применении метода вечных рядов, так как это наиболее подходящее для обратного тестирования.
Мы собираемся соединить фьючерсный контракт на сырую нефть WTI
Для загрузки фьючерсных данных я использовал плагин Quandl. Убедитесь, что вы настроили правильную виртуальную среду Python на своей системе и установили пакет Quandl, введя следующее в терминал:
import datetime
import numpy as np
import pandas as pd
import Quandl
Основная работа выполняется в функции futures_rollover_weights. Для этого требуется дата начала (первая дата заключения близкого контракта), словарь дат расчетов по контрактам (dates_expiry), символы контрактов и количество дней для завершения контракта (по умолчанию до пяти).
def futures_rollover_weights(start_date, expiry_dates, contracts, rollover_days=5):
"""This constructs a pandas DataFrame that contains weights (between 0.0 and 1.0)
of contract positions to hold in order to carry out a rollover of rollover_days
prior to the expiration of the earliest contract. The matrix can then be
'multiplied' with another DataFrame containing the settle prices of each
contract in order to produce a continuous time series futures contract."""
# Construct a sequence of dates beginning from the earliest contract start
# date to the end date of the final contract
dates = pd.date_range(start_date, expiry_dates[-1], freq='B')
# Create the 'roll weights' DataFrame that will store the multipliers for
# each contract (between 0.0 and 1.0)
roll_weights = pd.DataFrame(np.zeros((len(dates), len(contracts))),
index=dates, columns=contracts)
prev_date = roll_weights.index[0]
# Loop through each contract and create the specific weightings for
# each contract depending upon the settlement date and rollover_days
for i, (item, ex_date) in enumerate(expiry_dates.iteritems()):
if i < len(expiry_dates) - 1:
roll_weights.ix[prev_date:ex_date - pd.offsets.BDay(), item] = 1
roll_rng = pd.date_range(end=ex_date - pd.offsets.BDay(),
periods=rollover_days + 1, freq='B')
# Create a sequence of roll weights (i.e. [0.0,0.2,...,0.8,1.0]
# and use these to adjust the weightings of each future
decay_weights = np.linspace(0, 1, rollover_days + 1)
roll_weights.ix[roll_rng, item] = 1 - decay_weights
roll_weights.ix[roll_rng, expiry_dates.index[i+1]] = decay_weights
else:
roll_weights.ix[prev_date:, item] = 1
prev_date = ex_date
return roll_weights
Теперь, когда матрица взвешивания была создана, можно применить ее к отдельным временным рядам. Основная функция загружает близкие и дальние контракты, создает единую DataFrame для обоих, строит матрицу взвешивания перевёртывания, а затем, наконец, создает непрерывную серию обеих цен, соответствующим образом взвешенных:
if __name__ == "__main__":
# Download the current Front and Back (near and far) futures contracts
# for WTI Crude, traded on NYMEX, from Quandl.com. You will need to
# adjust the contracts to reflect your current near/far contracts
# depending upon the point at which you read this!
wti_near = Quandl.get("OFDP/FUTURE_CLF2014")
wti_far = Quandl.get("OFDP/FUTURE_CLG2014")
wti = pd.DataFrame({'CLF2014': wti_near['Settle'],
'CLG2014': wti_far['Settle']}, index=wti_far.index)
# Create the dictionary of expiry dates for each contract
expiry_dates = pd.Series({'CLF2014': datetime.datetime(2013, 12, 19),
'CLG2014': datetime.datetime(2014, 2, 21)}).order()
# Obtain the rollover weighting matrix/DataFrame
weights = futures_rollover_weights(wti_near.index[0], expiry_dates, wti.columns)
# Construct the continuous future of the WTI CL contracts
wti_cts = (wti * weights).sum(1).dropna()
# Output the merged series of contract settle prices
wti_cts.tail(60)
Выход такой:
2013-10-14 102.230 2013-10-15 101.240 2013-10-16 102.330 2013-10-17 100.620 2013-10-18 100.990 2013-10-21 99,760 2013-10-22 98.470 2013-10-23 97.000 2013-10-24 97 240 2013-10-25 97.950 - Что? - Что? 2013-12-24 99,220 2013-12-26 99,550 2013-12-27 100.320 2013-12-30 99,290 2013-12-31 98 420 2014-01-02 95.440 2014-01-03 93.960 2014-01-06 93.430 2014-01-07 93.670 2014-01-08 92.330 Длина: 60, dтип: плаватель64
Следующий шаг заключается в том, чтобы выполнить это для нескольких поставок в течение разных лет, в зависимости от ваших потребностей в обратном тестировании.