Можно ли определить подходящую торговую стратегию с помощью глубокого обучения?

ВВЕДЕНИЕ

Торговля акциями представляет собой метод использования колебаний курсов акций публично финансируемых компаний для получения прибыли. В частности, поскольку многие застряли дома во время пандемии COVID-19, это стало для многих удобной формой инвестиций с возможностью получения огромной прибыли, о чем свидетельствует 50% -ное увеличение торговли опционами на акции только в этом году [1].

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

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

В частности, если я инвестирую 10000 долларов в свои модели, может ли мой вход / выход на рынок на основе прогнозируемого процентного изменения позволить мне зарабатывать деньги?

ОБЗОР ЛИТЕРАТУРЫ

Было опубликовано множество статей, посвященных этой области [2] [3] [4], и они были неоценимы для формулировки моей статьи. Тем не менее, были некоторые комментарии к статьям, которые я отметил:

(1) Нормализация объединенных тестовых / обучающих данных или утечка тестовых данных в обученные входные данные.

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

(2) Сложность прогнозирования цены на следующий день.

Есть много статей, в которых утверждается, что у них есть какой-то способ зарабатывать деньги с помощью машинного / глубокого обучения, но проблема в том, что они испытывают трудности с получением обобщаемой прибыли, поскольку прогнозируемая цена на следующий день просто настраивается на основе сегодняшней цены. Шмальц в своей статье [5] очень ясно это объясняет.

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

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

(i) Цена отражает всю необходимую информацию.

(ii) Движение цен не является полностью случайным, и эта история в определенной степени повторяется.

Не стесняйтесь обращаться к моему репозиторию github по адресу https://github.com/yuhaolee97/stock-project за моим кодом :)

Давай начнем!

ИССЛЕДОВАТЕЛЬСКИЙ АНАЛИЗ ДАННЫХ

Сначала я решил увеличить данные об акциях Google за 10-летний период (с 31 августа 2010 г. по 1 сентября 2020 г.) с помощью Yahoo Finance API.

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

Мы отмечаем очень небольшое количество представленных функций (всего 6 столбцов). Другими словами, эти колонки сами по себе могут не дать нам очень хороших результатов для тренировки. Вот скорректированное закрытие GOOGL за последние 10 лет:

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

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

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

РАЗРАБОТКА ФУНКЦИЙ

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

Я пытаюсь включить несколько дополнительных функций, прежде чем мы начнем обучение модели:

(1) Балансовый объем

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

Затем я использовал библиотеку finta, в которой доступно почти 76 технических индикаторов. (Https://pypi.org/project/finta/) В частности, я сосредоточился на нескольких опережающих индикаторах, которые могут дать модели возможность быстро реагировать на изменения запасов:

(2) Экспоненциальная скользящая средняя (EMA)

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

(3) Полосы Боллинджера.

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

ОБРАБОТКА ДАННЫХ

После того, как мы добавим функции, пора подготовить наши данные в качестве входных данных для наших моделей!

Набор данных разделен на 70% поездов и 30% тестовых наборов. Критически важно, что это разделение поезд-тест должно выполняться при 2 условиях (1) набор поездов всегда должен быть до периода тестового набора, поскольку это прогнозирование временного ряда, и (2) это разделение должно выполняться до любой нормализации / масштабирование, чтобы избежать предвзятости упреждения.

Обратите внимание, что для большинства данных выполнение проверки K-Fold Cross было бы более идеальным, поскольку мы можем оценивать модели, используя различные складки в качестве набора для проверки. Хотя такой подход является более точным и также приводит к большей эффективности использования данных, проблема заключается в том, что использование K-Fold нарушает временную процедуру данных временных рядов, поскольку наборы данных проверки предшествуют временным рамкам точек данных поезда. Поэтому я бы просто использовал validation_split keras для извлечения последних 10% набора поездов в качестве набора проверки.

Затем мы выполняем нормализацию, что важно, когда величина столбцов разная и, следовательно, влияние изменения одного столбца более значимо, чем другого. Во время нормализации мы выполняем fit_transform для train_data, чтобы гарантировать, что все обученные данные масштабируются от 0 до 1.

normaliser = preprocessing.MinMaxScaler()
train_normalised_data = normaliser.fit_transform(train_data)
test_normalised_data = normaliser.transform(test_data)

Затем я начал формировать функции ввода и вывода для наборов поездов и тестов. Для ввода он состоит из функций за 21 предыдущий день. Я выбрал 21 день, так как это среднее количество торговых дней в месяце, которое дало бы модели достаточно возможностей для изучения. Выходными данными является 22-й день скорректированной цены закрытия.

history_points = 21
X_train = np.array([train_normalised_data[i : i + history_points].copy() for i in range(len(train_normalised_data) - history_points)])
y_train = np.array([train_normalised_data[:,0][i + history_points].copy() for i in range(len(train_normalised_data) - history_points)])
X_test = np.array([test_normalised_data[i : i + history_points].copy() for i in range(len(test_normalised_data) - history_points)])
y_test = np.array([test_data['Adj Close'][i + history_points].copy() for i in range(len(test_normalised_data) - history_points)])

ДОЛГОВРЕМЕННАЯ КРАТКАЯ ПАМЯТЬ (МОДЕЛЬ LSTM)

А теперь самое интересное !! Глубокое обучение!

Я попытаюсь использовать модель Long Short-Term Memory (LSTM), обычную рекуррентную нейронную сеть (RNN) с глубоким обучением, обычно используемую для прогнозирования данных временных рядов.

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

Важная вещь, которую следует отметить в LSTM, - это входные данные, которые должны быть в форме трехмерного вектора (образцы, временные шаги, функции). Следовательно, вход должен быть изменен, чтобы соответствовать этому.

Сначала мы импортируем все необходимые библиотеки keras и tensorflow. Обратите внимание, что мы также должны установить случайное начальное число, чтобы гарантировать воспроизводимость результатов:

import tensorflow as tf
import keras
from keras import optimizers
from keras.callbacks import History
from keras.models import Model
from keras.layers import Dense, Dropout, LSTM, Input, Activation, concatenate
import numpy as np
tf.random.set_seed(20)
np.random.seed(10)

Затем мы начнем с самого простого слоя LSTM с плотным слоем в качестве вывода. Мы запускаем его с 30 эпохами и validation_split 0,1, чтобы получить общее представление о том, насколько хорошо модель обучается.

Обратите внимание, что перемешивание выполняется только для train_data, а не для данных проверки, и что validation_split получается из последних предоставленных данных x и y, обеспечивая сохранение временного порядка данных для обучения.

lstm_input = Input(shape=(history_points, 6), name='lstm_input')
inputs = LSTM(21, name='first_layer')(lstm_input)
inputs = Dense(1, name='dense_layer')(inputs)
output = Activation('linear', name='output')(inputs)
model = Model(inputs=lstm_input, outputs=output)
adam = optimizers.Adam()
model.compile(optimizer=adam, loss='mse')
model.fit(x=X_train, y=y_train, batch_size=15, epochs=30, shuffle=True, validation_split = 0.1)

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

После этого мы прогнозируем x_test и сопоставляем результаты с фактическими результатами ниже:

Приличный! Общее направление есть, и кажется, что модель LSTM способна изучить тренд акций GOOGL. Однако RMSE довольно высока (76,976), следовательно, это не может быть хорошей моделью для прогнозирования. Простого добавления подходящих функций недостаточно для достижения оптимальных результатов в модели.

НАСТРОЙКА ГИПЕРПАРАМЕТРА / СЛОЯ

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

Настройка количества эпох - важный фактор. Увеличение числа эпох уменьшило бы ошибку, но слишком большое количество эпох привело бы к тому, что модель изучала бы шум данных поезда. Нужно обращать внимание на потерю проверки и останавливаться, когда она находится на самом низком уровне, поскольку помимо этого, мы переоснащаем наши функции только для повышения нашей точности. Я прогнал модель более 1000 эпох и построил график потерь поезда и проверки в зависимости от количества эпох:

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

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

Следует выбрать оптимальную скорость обучения, чтобы избежать ненадежных результатов обучения / невозможности обучения модели. Точно так же вертикальное наложение слоев LSTM увеличит сложность модели и, следовательно, можно надеяться, повысит точность результата.

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

lstm_input = Input(shape=(history_points, 6), name='lstm_input')
inputs = LSTM(21, name='first_layer')(lstm_input)
inputs = Dense(16, name='first_dense_layer')(inputs)
inputs = Dense(1, name='second_dense_layer')(inputs)
output = Activation('linear', name='output')(inputs)
model = Model(inputs=lstm_input, outputs=output)
adam = optimizers.Adam(lr = 0.0008)
model.compile(optimizer=adam, loss='mse')
model.fit(x=X_train, y=y_train, batch_size=15, epochs=170, shuffle=True, validation_split = 0.1)

Это позволило получить разумное среднеквадратичное значение 48,997 на тестовой выборке. Он смог относительно хорошо смоделировать рост / снижение цен.

ОГРАНИЧЕНИЯ ПЕРВОЙ МОДЕЛИ

Модель выглядит достойно! Пора начинать зарабатывать? Может возникнуть соблазн вложить деньги в мою модель (пожалуйста, не делайте этого!), И я решил проверить это.

Начав с 10000 долларов в качестве базовых денег, я создал алгоритм, который покупает как можно больше акций, когда прогнозируемое движение цены превышает 0,15% на следующий день, и продает как можно больше акций, когда прогнозируемое значение превышает 0,15%. % снижение движения цены на следующий день.

Однако с этой моделью мы фактически получаем 8424 доллара! На верхнем графике показаны покупки (зеленая стрелка) / продажи (красная стрелка), совершенные моделью на фактическом тестовом наборе, а на нижнем графике показано процентное изменение (прогноза на следующий день) за тот же период времени:

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

Ведущие модели: способность дать представление о том, как цена изменится до того, как это произойдет.

Отстающие модели: модели, которые реагируют на изменение цены только после того, как оно произошло.

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

СЕЗОННАЯ МОДЕЛЬ

Это концепция стационарности, которая имеет решающее значение для данных временных рядов. По сути, данные временных рядов будут иметь тенденции, сезонность и шум:

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

Чтобы попытаться заставить модель изучать сезонность данных, которая вызвана в основном тем, что цены на акции в конечном итоге возвращаются к стабильной точке после колебаний, превышающих среднее значение, я пытаюсь использовать разницу (P (t +1) -P ( t)) в качестве единственной входной функции. Мы надеемся, что это позволит создать ведущую модель. В то же время это препятствует тому, чтобы модель ссылалась на вчерашнюю цену, что, как мы надеемся, могло бы обойти проблему запаздывающей модели. Поговорим об убийстве 2 зайцев одним выстрелом :)

После этого я обучил данные на моделях LSTM. В частности, я решил использовать больше слоев LSTM. Вертикальное наложение слоев LSTM увеличит сложность модели и, как мы надеемся, повысит точность результата. Обратите внимание, что я использовал только 25 эпох, так как после этого потери при проверке резко возрастают.

После дальнейшего поиска по сетке мне удалось получить модель с хорошей производительностью со среднеквадратичным отклонением 45,273 на проверочном наборе с использованием следующих слоев:

lstm_input = Input(shape=(data_set_points, 1), name='lstm_input')
inputs = LSTM(21, name='first_layer', return_sequences = True)(lstm_input)
inputs = Dropout(0.1, name='first_dropout_layer')(inputs)
inputs = LSTM(64, name='lstm_1')(inputs)
inputs = Dropout(0.05, name='lstm_dropout_1')(inputs)
inputs = Dense(32, name='first_dense_layer')(inputs)
inputs = Dense(1, name='dense_layer')(inputs)
output = Activation('linear', name='output')(inputs)
model = Model(inputs=lstm_input, outputs=output)
adam = optimizers.Adam(lr = 0.002)
model.compile(optimizer=adam, loss='mse')
model.fit(x=X_train, y=y_train, batch_size=15, epochs=25, shuffle=True, validation_split = 0.1)

Следует отметить 2 важных момента. Во-первых, нужно поставить return_sequences = True для нескольких слоев LSTM, чтобы затем можно было передать вывод на следующий уровень. Во-вторых, для повышения точности я последовательно добавлял слои исключения после каждого слоя LSTM, что помогло бы уменьшить переоснащение, к чему нейронные сети очень склонны.

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

Во-вторых, казалось, что модель смогла изучить некоторые ведущие особенности. При t = 20 происходит прогнозируемое падение цен, которое отражается падением при t = 25, а после этого прогнозируемое повышение также можно увидеть с t = 30 до t = 40, что отражено на реальном синем графике. Прогнозируемое падение с t = 550 было также отражено при t = 600. Этот результат показал, что удаление тренда могло снизить точность, но могло дать представление о лучших моментах для входа / выхода на фондовый рынок.

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

Из этого я заработал в общей сложности 7683 доллара на предсказании, что по сравнению со стратегией покупки и удержания принесло 6558 долларов, что означает прибыль в 1135 долларов! Потрясающие! Другими словами, хотя он не очень хорошо моделировал величину, ему удалось предоставить опережающие индикаторы для изменений цен, которые позволили моему алгоритму получить прибыль.

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

Замечательный! Я получил улучшение с RMSE 24.014! Это показывает, что модель способна довольно точно предсказывать правильное направление из-за постоянной сезонности. Это демонстрирует, что обучение на разнице, а не на величине на самом деле приводит к более высокой точности и лучшей способности генерировать потенциально ведущую модель.

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

ЗАКЛЮЧЕНИЕ И ВЫВОДЫ

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

КРЕДИТЫ

Я хотел бы искренне поблагодарить Мин Яна и HeiCoders Academy (https://heicodersacademy.com) за то, что они потратили много времени на наставничество, советы и вклад в формулировку этой статьи.

ВАЖНЫЙ ОТКАЗ

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

Это моя первая статья на Medium, поэтому я приветствую любые отзывы об улучшении моих моделей / будущих направлениях :))

ССЫЛКИ

[1] https://www.cnbc.com/2020/12/04/pandemic-induced-options-trading-craze-shows-no-signs-of-slowing-down.html

[2] https://towardsdatascience.com/getting-rich-quick-with-machine-learning-and-stock-market-predictions-696802da94fe

[3] https://towardsdatascience.com/aifortrading-2edd6fac689d

[4] https://towardsdatascience.com/stock-price-prediction-based-on-deep-learning-3842ef697da0

[5] https://towardsdatascience.com/using-neural-networks-to-predict-stock-prices-dont-be-fooled-c43a4e26ae4e

[6] https://medium.com/@Currie32/predicting-the-stock-market-with-the-news-and-deep-learning-7fc8f5f639bc

[7] https://towardsdatascience.com/pandemics-impact-financial-markets-9a4feb6951f5