Нью-Йоркская фондовая биржа

Аналитики, трейдеры и инвесторы уже давно пытаются предсказать будущие цены на акции. Даже если вы придумаете план или модель, которая работает какое-то время, в целом она хороша до тех пор, пока по какой-то причине не перестанет работать. Был ли сбой в основных корреляциях или новые новости о скандале вызвали резкое падение цен на акции. Если бы вы могли взломать весь код, вы бы стали миллиардером примерно за то же время, которое требуется, чтобы нажать на вашу любимую программу Netflix.

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

Данные

Набор данных, с которым мы будем работать, получен с Нью-Йоркской фондовой биржи (NYSE) и представляет исторические цены и другие фундаментальные данные S&P 500 с 2010 по конец 2016 года.

Набор данных состоит из следующих файлов:

Prices.csv: необработанные ежедневные цены как есть. Большинство данных охватывают период с 2010 г. по конец 2016 г., для компаний, недавно появившихся на фондовом рынке, диапазон дат короче. Было ок. 140 дроблений акций за это время, этот набор не учитывает этого.

price-split-adjusted.csv: то же, что и цены, но добавлены корректировки для сплитов. Securities.csv: общее описание каждой компании с разделением по секторам

фундаментальные данные.csv: показателей, извлеченных из ежегодных заявок SEC 10K (2012–2016 гг.), должно быть достаточно для получения большинства популярных фундаментальных показателей.

Основное внимание мы уделим файлу price-split-adjusted.csv, так как он содержит скорректированные цены акций, которые мы будем пытаться предсказать.

Процесс

В общем, мы будем последовательно следовать процессу науки о данных, очистке, исследованию и анализу, пока не получим пригодную для использования модель. Мы будем использовать стандартный набор библиотек для очистки и исследования (Pandas, Numpy, MatPlotLib, Seaborn и некоторые другие), а основное моделирование будет выполняться с помощью Keras с небольшой помощью SciKit Learn.

В набор данных включены 503 акции с 851 264 отдельными ценами. Самая дешевая акция в S&P 500 имела минимум 1,50 за акцию, а самая дорогая акция за этот период времени составляла 1600,93 за акцию. Помимо объема, мы можем видеть, что другие показатели могут быть немного искажены, учитывая, что стандартное отклонение на самом деле больше, чем средняя цена. Одна акция торгуется по 1,50, а другая по 1600, это не совсем сравнение яблок с яблоками!

Мы можем легко увеличить масштаб одной акции и просмотреть некоторые из тех же показателей. Во-первых, мы рассмотрим Citigroup, банковскую холдинговую компанию, которая владеет Citibank, N.A. Мы сделаем два графика, один для просмотра высоких и низких цен, а другой для ежедневных объемов торгов акциями.

Интересно: похоже, что объем сделок Citigroup в день неуклонно снижался по мере того, как компания восстанавливалась после своих низких точек в этот период.

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

Предупреждение: хотя NYSE можно рассматривать как показатель общего состояния экономики, а S&P 500 можно рассматривать как общий показатель для NYSE, мы должны осознавать обобщения и их влияние на наш анализ. Наш анализ не должен быть затронут в этом отношении, но это стоит отметить для тех, кто думает о более широкой картине.

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

Посмотрите на Chipotle («CMG»)! Диапазон цен на акции увеличился с максимума в 758,60 долларов до минимума в 86 долларов. Обычно такое большое изменение здесь указывало бы на дробление акций, но отдельный файл, который использовался для анализа, уже был скорректирован с учетом дробления акций. Этот тип анализа полезен для управления рисками. Это дает риск-менеджеру представление о недавней волатильности и может позволить инвестиционному аналитику сформировать мнение о том, следует ли покупать, держать или продавать акции. Для некоторых торговых позиций и инвестиционных портфелей требуются минимальные и максимальные значения волатильности, и наблюдение таких широких колебаний может указывать на то, что краткосрочные модели, хотя и являются типичными, могут не учитывать весь риск.

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

Есть несколько вещей, которые я хотел бы отметить как интересные здесь. Сначала давайте посмотрим на EES или фонд WisdomTree U.S. SmallCap Fund. Это ETF, который стремится отслеживать результаты инвестиций приносящих прибыль компаний с малой капитализацией на фондовом рынке США. 30 января наблюдался всплеск активности с EES по сравнению с другими пятницами января. Второй паттерн, на который я хочу обратить внимание, — это последняя пятница (и рабочий день) месяца с наибольшим объемом. На самом деле это будет намного яснее, если мы преобразуем это в гистограмму.

Каким бы интересным ни было все это исследование, у нас есть достаточно хорошее представление, чтобы обратиться к Акции, которую мы будем моделировать. На самом деле мы попытаемся найти хорошую модель для прогнозирования цены Google на следующий день (тикер: «GOOG»). Мы проведем еще немного исследований, прежде чем перейдем к сути анализа.

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

Мы также должны смотреть на объем запасов для всех трех.

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

Подавляющее большинство акций торгуются в одинаковых объемах, а помимо этого у нас есть эти мега-акции с дикой ежедневной торговой активностью. Ежедневный объем говорит нам о нескольких вещах. Во-первых, размер компании, эти выбросы представляют собой многонациональные конгломераты, у которых были очень крупные эмиссии и, вероятно, также несколько дроблений акций. Крупные компании не часто быстро терпят неудачу; они также часто получали статус «дойной коровы». Эти факторы делают их привлекательными для не склонных к риску инвесторов (таких как институциональные пенсионные фонды). В зависимости от отраслевых пассатов, подобных тем, с которыми сейчас сталкиваются наши компании «FAANG» (Facebook, Apple, Amazon, Netflix и Google), это также может быть сигналом того, что эти компании могут быть подвергнуты антимонопольным юридическим действиям. На самом деле отраслевой аналитик должен знать о таких вещах, прежде чем давать какие-либо рекомендации.

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

Модель

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

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

Первым шагом будет создание DataFrame, состоящего только из данных Google Stock. Мы удалим объем и серию символов в этом кадре данных. Мы будем использовать цену закрытия в качестве нашей целевой переменной, чтобы следовать рыночным правилам. Как правило, другие цены рассматриваются только при обсуждении внутридневного движения цен. Нам также потребуется нормализовать данные, которые мы выполнили с помощью MinMaxScaler() из библиотеки sklearn.preprocessing. Это позволило получить гораздо более плавный набор ценовых кривых, который приведен ниже.

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

Первой моделью, которую мы будем использовать, будет однослойная LSTM, Long Short Term Memory — простой тип RNN, который не страдает от исчезновения/взрыва градиентов. LSTM будет служить своего рода базой для производительности других моделей. Он включает в себя способность «забывать» некоторую информацию, которая может не иметь отношения к поставленной задаче. Он постоянно обновляет внутреннее состояние модели, чтобы узнать, что следует помнить, а что следует забыть. Мы также добавляем один плотный слой в качестве последнего слоя. Это слой, который фактически будет делать прогноз, а LSTM можно рассматривать как механизм, стоящий за ним.

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

У нас также есть несколько вариантов в функции оптимизатора. Мы будем использовать среднеквадратичное распространение (RMSProp), потому что мы работаем со случаями RNN, и оно, как правило, хорошо работает с этими моделями.

# Сборка и компиляция базового LSTM
эпох = 60

model = Sequential()
model.add(LSTM(200, input_shape=(train_X.shape[1], train_X.shape[2])))
model.add(Dense(1))< br /> model.compile (потеря = 'mse', оптимизатор = 'rmsprop')

Мы также создали визуализации для моделирования ошибки тестирования и обучения.

Хотя эта модель оказалась лучше, чем я ожидал, она ни в коем случае не является точным предсказателем. Мы собираемся внести 2 основных изменения в модель и запустить версию 2. Первое изменение, которое мы внесем, — это добавление дополнительного слоя LSTM или слоев стека. Наложение слоев LSTM делает ее моделью глубокого обучения, и этот глубокий аспект обычно считается причиной успеха моделей прогнозирования. Второй аспект, который мы изменим, — это добавление некоторой регуляризации отсева. Dropout случайным образом выбирает нейроны и игнорирует их во время обучения. Это помогает уменьшить переоснащение. Мы добавим отдельные выпадающие слои после наших скрытых слоев LSTM.

# Построение и компиляция двухслойного lstm
model2 = Sequential()
model2.add(LSTM(200, input_shape=(1,3),return_sequences=True))
model.add( Dropout(0.2))
model2.add(LSTM(200, input_shape=(1,3)))
model.add(Dropout(0.2))
model2.add(Dense(1 ))
model2.compile(потеря= 'mse', оптимизатор= 'rmsprop')

Опять же, ниже визуализация ошибки обучения и тестирования.

Добавление еще одного слоя и выпадение не улучшили производительность. В этом случае мы можем изменить наш метод.

Мы будем использовать другой тип RNN для нашей следующей модели: Gated Recurrent Units или GRU. Они передают внутреннее состояние на каждом временном шаге, но только ту информацию, которая важна для передачи. Он посылает вперед ворота сброса и ворота обновления. Reset Gate — это функция, которая определяет, что нужно удалить из текущего состояния, прежде чем двигаться вперед. Шлюз обновления определяет, что из предыдущего временного шага следует использовать в текущем. GRU выполняет, по сути, то же самое «забывание и запоминание», что и LSTM, просто делает это немного по-другому (LSTM включают три ворот: входные ворота, ворота забывания и выходные ворота).

# Построение и компиляция двухслойной модели GRU
model3 = Sequential()
model3.add(GRU(200, input_shape=(1,3),return_sequences=True))
model.add (Dropout(0.2))
model3.add(GRU(200, input_shape=(1,3)))
model.add(Dropout(0.2))
model3.add(Dense( 1))
model3.compile(потеря= 'mse', оптимизатор= 'rmsprop')

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

# Построение и компиляция 4-слойной модели GRU
model4 = Sequential()
model4.add(GRU(50, input_shape=(1,3),return_sequences=True))
model.add (Dropout(0.2))
model4.add(GRU(150, input_shape=(1,3),return_sequences=True))
model.add(Dropout(0.2))
model4. add(GRU(150, input_shape=(1,3), return_sequences=True))
model.add(Dropout(0.2))
model4.add(GRU(50, input_shape=(1,3) )))
model4.add(Dense(1))
model4.compile(потеря= 'mse', оптимизатор= 'rmsprop')

Определенное улучшение видно в показателях точности как для обучения, так и для тестирования. Мы попробуем еще одно изменение, чтобы увидеть, сможем ли мы улучшить его дальше. До сих пор мы использовали функции активации по умолчанию во всех наших моделях (в Keras это линейная активация). Мы можем явно определить другую функцию активации в каждом скрытом слое. Мы будем использовать сигмовидную функцию для скрытых слоев GRU, а затем линейную активацию для окончательного плотного слоя.

# Создание и компиляция 4-уровневой модели GRU, в которой мы явно меняем функцию активации на каждом уровне
model5 = Sequential()
model5.add(GRU(50, input_shape=(1,3),return_sequences= True,activation= 'сигмоид'))
model.add(Dropout(0.2))
model5.add(GRU(150, input_shape=(1,3),return_sequences=True,activation= 'сигмоид '))
model.add(Dropout(0.2))
model5.add(GRU(150, input_shape=(1,3), return_sequences=True,activation= 'сигмоид'))
> model.add(Dropout(0.2))
model5.add(GRU(50, input_shape=(1,3),activation= 'sigmoid'))
model.add(Dropout(0.2))
model5.add(Dense(1,activation = 'linear'))
model5.compile(loss= 'mse', оптимизатор= 'rmsprop')

Выводы

Теперь, когда у нас есть 5 моделей и их показатели точности, давайте посмотрим их все в одном месте, чтобы мы могли принять решение о том, какая модель лучше. Пока мы работали, мы сохранили MSE для обучения и тестирования в словаре, чтобы у нас был легкий доступ ко всему, что мы пробовали до сих пор. Хотя академическое сообщество в целом не может указать, что лучше: GRU или LSTM, на практике GRU часто имеет небольшое преимущество. Это, безусловно, имеет место в наших модельных тестах. Окончательная модель GRU показала себя лучше, чем ожидалось, с точностью прогнозирования 4,2% на тестовых данных и 1,6% на обучающих данных.

Обзор бизнеса и рекомендации

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

И если бы у нас было больше времени…

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

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