Контракты на непрерывные фьючерсы для целей обратного тестирования

Автор:Доброта, Создано: 2019-03-18 10:48:28, Обновлено:

Краткий обзор фьючерсных контрактов

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

На практике фьючерсы торгуются на биржах (в отличие от 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 и контракт затем просто становится продолжением цены ближнего. Таким образом, через пять дней контракт плавно переходит от дальнего к ближнему.

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

Есть и другие менее распространенные подходы к проблеме, но мы будем избегать их здесь.

Формирование обратной обработки в Python и Pandas

Остальная часть статьи будет сосредоточена на применении метода вечных рядов, так как это наиболее подходящее для обратного тестирования.

Мы собираемся соединить фьючерсный контракт на сырую нефть WTI near и far (символ CL), чтобы создать непрерывный ценовой ряд. На момент написания (январь 2014), ближайший контракт - CLF2014 (январь) и дальний контракт - CLG2014 (февраль).

Для загрузки фьючерсных данных я использовал плагин 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

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


Больше