Кодирование алгоритма случайного леса для прогнозирования дневного направления S&P500 через соотношение пут/колл.

Основная идея статьи: мы создадим алгоритм случайного леса, который предсказывает направление отношения пут/колл на завтра. Используя эту информацию, мы попытаемся предсказать завтрашнюю доходность S&P500. Следовательно, мы не будем предсказывать направление фондового рынка, скорее мы попытаемся предсказать направление временного ряда, коррелирующего с S&P500.

Во-первых, колл-опцион — это право купить определенный актив в будущем по определенной цене, а пут-опцион — это право продать определенный актив в будущем. будущее по заранее оговоренной цене.

Следовательно, когда вы покупаете колл, вы можете купить что-то позже, а когда вы покупаете пут, вы можете что-то продать позже. Каждая сделка имеет две стороны, поэтому, когда вы покупаете колл или пут, кто-то другой продает их вам. Это приводит к двум другим позициям, которые можно открывать по опционам: продажа коллов и продажа путов. Индикатор пут/колл имеет дело с покупателями опционов и измеряет количество покупателей пут, деленное на количество покупателей колл. Это дает нам представление об отношении участников рынка к указанной акции (в нашем случае это будет фондовый рынок США).

Более высокоесоотношение пут/колл означает, что больше покупателей пут (трейдеры делают ставку на падение актива), а более низкое соотношение пут/колл означает больше коллов. покупатели (трейдеры делают ставку на рост актива). Известный способ использования этого коэффициента при анализе рыночных настроений заключается в оценке следующих сценариев:

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

Предлагают ли рынки опционов информацию для прибыльной торговли спот/фьючерсами? это то, что мы увидим в этом бэк-тесте. Глядя на соотношение пут/колл, мы хотим преобразовать настроения участников рынка в торговые возможности.

План атаки

Как мы это сделаем? Сначала мы начнем с простой известной стратегии, согласно которой мы покупаем рынок, когда цена пут/колл достигает 1,2, и продаем (короткую) рынок, когда Пут/колл достигает 0,65. Период владения составит 20.

После выполнения и анализа этой стратегии мы обратимся к более продвинутой, которая является ядром этого исследования. Можно ли использовать алгоритм машинного обучения для прогнозирования завтрашнего значения соотношения пут/колл и использовать эту информацию для торговли на рынке? Посмотрим, но сначала давайте попробуем более простой ( первое) стратегия. Обратите внимание, что историческая корреляция между изменениями индекса S&P500 и изменениями соотношения пут/колл находится между -0,35 и -0,45.

Стратегия №1

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

Начнем с импорта необходимых библиотек. Обычные, необходимые каждому исследователю временных рядов.

# Importing libraries
import matplotlib.pyplot as plt
import numpy             as np
import pandas            as pd

Определим необходимые переменные. период_удержания не требует пояснений и показывает, как долго мы будем удерживать позицию во время открытия. Переменная investment — это наш начальный баланс. И, наконец, уровни поддержки и сопротивления — это просто подразумеваемые статистические экстремумы, которые используются в качестве триггеров.

holding_period = 20                    
investment     = 1000
support        = 1.2
resistance     = 0.65

Теперь, имея файл Excel с именем PCR, мы импортируем его в Python для чтения и дальнейшей очистки.

# Importing data and calculating
Data = pd.read_excel('PCR.xlsx')
Data = np.array(Data)

Файл Excel будет состоять из двух столбцов, первый из которых представляет собой соотношение пут/колл, а второй — цену закрытия SPX.

Проще говоря, мы будем покупать всякий раз, когда PCR достигает 1,20, и продавать, когда он достигает 0,65.

# Buy/Sell conditions
for i in range(len(Data)):
    try:
        if Data[i, 0] >= support and Data[i - 1, 0] < support:
            Data[i + 1, 2] = 1 # We have added a new column
        
        elif Data[i, 0] <= resistance and Data[i - 1, 0] > resistance:
            Data[i + 1, 3] = -1 # We have added a new column
            
        else:
            continue
    except IndexError:
        pass

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

np.count_nonzero(Data[:, 2])
np.count_nonzero(Data[:, 3])

Это дает нам 108длинных позиций, 23коротких позиций и 131позицию, занятых в общей сложности в период с 2006 по 2019 год (на этом бесплатные данные PCR заканчиваются). ).

Теперь, чтобы просто рассчитать прибыль по сделкам, имея в виду период удержания 20:

# Returns
for i in range(len(Data)):
    try:
        if Data[i, 2] == 1:
            Data[i + holding_period, 4] = (Data[i + holding_period, 1] - Data[i, 1])
            
        if Data[i, 3] == -1:
            Data[i + holding_period, 5] = (Data[i, 1] - Data[i + holding_period, 1]) 
            
    except IndexError:
        pass

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

Со статистикой производительности:

Profit factor        =  3.069Hit ratio            =  70.77 %Gross return         =  418.0 %Expectancy           =  32.17Realized RR          =  1.27Buy:Sell ratio       =  108 : 23Win% Buy:Sell        =  74.0 % : 52.0 %

Некоторые наблюдения, которые необходимо сделать, просто взглянув на диаграмму сигналов: количество длинных сигналов заметно выше, чем количество коротких сигналов, и качество сигнала кажется лучше. Длинные сигналы хорошо отражают дно коррекций и консолидаций. Кривая капитала стратегии в чем-то удовлетворяет, но все же не на 100% отражает реальность (знали ли мы, что эта стратегия сработает еще в 2006 году, если бы мы еще не получили результатов?), но тем не менее интересно посмотреть если он будет продолжать вести себя так в будущем. Мы также отмечаем, что с использованием 20-дневного периода удержания, а также тенденции рынков к росту, этот факт мог бы подтолкнуть нас к большей прибыльности. Тем не менее, редко можно увидеть такую ​​красивую восходящую кривую капитала.

Будет ли эта стратегия работать в обозримом будущем? Трудно сказать, но то, что наверняка, потребуется много времени, чтобы узнать. Судя по приведенным выше данным, за 13 лет у нас была только 131 сделка, что составляет 10 сделок в год. Смягчение некоторых условий даст нам 1 сделку в месяц. Возможно, было бы неплохо разделить портфель на части для активной торговли по этой стратегии, предполагая, что будет проведена дополнительная оптимизация.

Стратегия №2

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

Прежде чем мы начнем с определения нашего алгоритма и проведения обратного тестирования, интересно узнать, насколько хорошо наша модель будет работать в утопическом мире, где мы можем предсказать завтрашнее соотношение пут/колл с 100% точностью? Другими словами, зная об отрицательной корреляции и будучи уверенными в завтрашнем изменении коэффициента, насколько хорошо мы будем торговать на рынке США?

Таким образом, становится аппетитно видеть, можем ли мы предсказать соотношение пут/колл или нет, увидев эту почти идеальную кривую капитала (валовая доходность около 1200% с 2006 года и при ежедневной торговле на открытии и закрытии). выходит в конце дня). Конечно, чтобы представить ситуацию в перспективе, мы не получим такую ​​кривую капитала, и мы даже не уверены, что алгоритм сможет так хорошо предсказать PCR. Но стремление к исследованиям заставляет нас двигаться вперед. Факт: я начал писать эту статью, когда создавал алгоритм, поэтому большая часть того, что я написал до сих пор, была написана до начала тестирования на исторических данных. Теперь давайте начнем с серьезных вещей.

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

Дерево решений — это контролируемый алгоритм обучения, который подходит как для задач классификации, так и для задач регрессии благодаря своей линейной и нелинейной пригодности.

Деревья решений разбивают набор данных на более мелкие подмножества, используя условия, чтобы окончательно получить оценку вероятности. Первое дерево называется корневым узлом, а следующие за ним (выходящие из него) называются узлами решений. Алгоритм, используемый для перебора деревьев для вычисления энтропии, называется Итеративный дихотомайзер 3 (ID3) и был изобретен Россом Куинланом. Идея здесь в том, что когда вы усредняете множество моделей, вы получаете более плавный и точный прогноз. Вот почему алгоритмы случайного леса хорошо подходят как для задач классификации, так и для задач регрессии. Алгоритм случайного леса — это не что иное, как набор деревьев решений для уменьшения переобучения и усреднения результатов, что дает нам предположительно более высокую точность. Следовательно, дерево решений — это просто случайный лес с одним деревом решений.

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

# Importing libraries
from sklearn.ensemble    import RandomForestRegressor
import matplotlib.pyplot as plt
import numpy             as np
import pandas            as pd

Переменная trees является ключом к алгоритму Random Forest, чем их больше, тем сложнее будет модель. В нашем примере мы выберем 20 деревьев. Переменная запаздывания — это то, как далеко в прошлое мы заглянем, чтобы включить его в формулу прогноза. Значение три означает, что последние 3 значения изменения PCR будут использоваться для прогнозирования завтрашнего изменения.

# Parameters
investment     = 1000                  
trees          = 20
lag            = 2

А теперь какие переменные будет использовать алгоритм Random Forest для прогнозирования завтрашнего PCR? Как было сказано чуть выше, мы будем использовать технику, называемую авторегрессией, в которой лаговые значения используются в качестве независимых переменных. Это означает, что сегодняшнее и вчерашнее изменение PCR может помочь нам спрогнозировать завтрашнее изменение (т. е. будет ли оно повышаться или понижаться по сравнению с сегодняшним значением).

Итак, учитывая следующую структуру данных:

Матрица авторегрессии на ПЦР. Крайний слева — самое последнее наблюдение. Идем налево, берем вчерашние наблюдения.

Приведенная выше матрица означает, что если мы возьмем индекс 3 (первая строка), то -0,02 можно объяснить 0,01, -0,07 и 0,06 с учетом расчетного соотношения. Другими словами, первые три столбца — это независимые переменные, которые должны нести некоторую информацию о последнем столбце.

Мы назовем первые три столбца X (независимые переменные) и последний столбец Y (независимая переменная).

# Separating the data
Determinant = len(Data.columns)
X = Data.iloc[:, 0:Determinant-1].values
Y = Data.iloc[:, -1].values
Y = np.reshape(Y, (-1, 1))

Итак, теперь давайте возьмем наборы данных X и Y и разделим их на обучающий набор и тестовый набор:

# Splitting the dataset into the Training set and Test set
X_train = X[:-projection,] #Training explanatory variables
Y_train = Y[:-projection,] #Training predictions
X_test  = X[-projection:,] #Test explanatory variables
Y_test  = Y[-projection:,] #Test predictions

Приведенный ниже код определяет функцию «Случайный лес», выбирает количество деревьев (через переменную n_estimators) и соответствует обучающему набору, чтобы получить подразумеваемую связь, которую мы надеемся сохранить в будущем.

# Fitting the model
regressor = RandomForestRegressor(n_estimators = trees)
regressor.fit(X_train, Y_train.ravel())

Что мы хотим сделать сейчас, так это взять это отношение, дать ему набор данных, аналогичный X_train, и применить прогнозы. К счастью, мы создали наш набор данных X_test со структурой вне выборки. Это должно выглядеть примерно так:

# Predicting the Test set results
Prediction = regressor.predict(X_test) #Predict over X_test
Prediction = np.reshape(Prediction, (-1, 1))
Real = Y_test

Это даст нам два набора данных: реальные изменения в PCR и наши прогнозы. Давайте объединим их вместе и рассчитаем коэффициент корреляции, чтобы дать нам представление о том, хорошо у нас дела или нет. Хотя коэффициент корреляции ничего не говорит о точности (поскольку величины могут быть достаточно большими, чтобы ее исказить)

Comparison = np.concatenate((Prediction_LR, RealLR), axis = 1)
np.corrcoef(Comparison[:,0], Comparison[:,1])

Результат даст нам

# 0.343

Что неплохо для начала. Но насколько хорошо это будет, если мы попробуем торговать S&P500? Давай продолжим. Приведенный ниже код просто применяет стратегию, которая берет наши прогнозы PCR, размещает их параллельно с изменениями в SPX с учетом временной погрешности, а затем проверяет, связан ли отрицательный прогноз до начала дня с положительное изменение SPX в конце дня или нет?

DataSPX = pd.read_excel('PCR.xlsx')
    DataSPX = np.array(DataSPX)
    DataSPX = DataSPX[:, 1]
    DataSPX = np.reshape(DataSPX, (-1, 1))
    DataSPX = pd.DataFrame(DataSPX)
    DataSPX = DataSPX.diff()
    DataSPX = DataSPX.iloc[1:,].values
    
    DataSPX  = DataSPX[-projection:,]
    
    Combined = np.concatenate((Prediction_LR, DataSPX), axis = 1)
    Combined = adder(Combined, 4) #Function to add 4 columns (Check end of article)
    
    for i in range(len(Combined)):
        if Combined[i, 0] < 0:
            Combined[i, 2] = 1
        if Combined[i, 0] > 0:
            Combined[i, 3] = -1
    
    for i in range(len(Combined)):
        if Combined[i, 2] == 1 and Combined[i, 1] > 0:
            Combined[i, 4] = Combined[i, 1]
        if Combined[i, 2] == 1 and Combined[i, 1] < 0:
            Combined[i, 4] = Combined[i, 1]
        if Combined[i, 3] == -1 and Combined[i, 1] > 0:
            Combined[i, 5] = -Combined[i, 1]
        if Combined[i, 3] == -1 and Combined[i, 1] < 0:
            Combined[i, 5] = abs(Combined[i, 1])

Profit factor        =  1.209Hit ratio            =  51.4 %Gross return         =  83.0 %Expectancy           =  1.67Realized RR          =  1.14

Хорошей новостью об алгоритме является то, что он не страдает какой-либо предвзятостью. Соотношение длинных и коротких позиций составляет около 1,0 (это означает, что ордеров на покупку столько же, сколько ордеров на продажу).

Заключение

После проведения бэк-тестов мы можем сделать следующие наблюдения:

  • Идеальное предсказание соотношения пут/колл даст нам утопическую индексную стратегию.
  • Результаты первой стратегии показали, что PCR может добавить некоторую ценность в торговле, если он близок к экстремуму. Сигналы, а также производительность показывают некоторые временные возможности. Однако длительное преобладание делает базовую стратегию ПЦР необъективной. Я бы порекомендовал сохранить его как инструмент, помогающий в принятии решений. Например, трейдер хочет открыть длинную позицию на фондовом рынке США, а PCR показывает верхние экстремальные значения. Это может быть подтверждением существующей судимости.
  • Результаты второй стратегии показали, что оптимизация необходима для улучшения модели, поскольку чистый случайный лес, состоящий всего из 10 деревьев, мало что дает для прогнозирования направления фондового рынка с помощью PCR.
  • Что еще? Библиотека Sklearn имеет множество алгоритмов регрессии. Мы не ограничены только использованием алгоритма Random Forest. Мы можем попробовать столько, сколько захотим, и усреднить их прогнозы, чтобы создать еще лучшую модель. Возможности безграничны.
# The adder function seen above. It adds a number of desired columns to a datasetdef adder(Data, times):
    
    for i in range(1, times + 1):
    
        z = np.zeros((len(Data), 1), dtype = float)
        Data = np.append(Data, z, axis = 1)return Data

Если вы нашли эту статью полезной, рассмотрите возможность оставить чаевые. Спасибо за внимание и удачи в поиске работы!

Купите мне кофе, чтобы я мог сделать операцию моей кошке: https://www.buymeacoffee.com/botservices