Прогнозирование цен на акции

Содержание:

  • Знакомство
  • Прогнозирование цен на акции с помощью регрессионных моделей
  • Прогнозирование цен на акции с помощью многомерного анализа
  • Прогнозирование цен на фондовые индексы с помощью LSTM
  • Прогнозирование тренда цен на акции с использованием нейронной сети LSTM с Streamlit и Render
  • Прогнозирование цен на акции с помощью LSTM и GRU
  • Прогнозирования движения цен на акции
  • Прогнозирование тенденций цен на акции с помощью Python
  • Прогнозирование цен на акции с помощью машинного обучения
  • Прогнозирование цен на акции с помощью квантового машинного обучения на Python
  • Прогнозирование цен на акции с подразумеваемой волатильностью
  • Прогнозирование цен на акции с помощью Lag-Llama Transformers
  • Прогнозирование цен на акции с помощью трансформеров

Знакомство

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

Обзор статьи

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

Постановка задачи

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

1. Использование Yahoo Finance API для извлечения данных о ценах на акции Google

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

3. Постройте модель LSTM

4. Обучите модель на существующих данных

5. Прогнозирование более поздней части набора данных с помощью модели

6. Сравните прогнозируемые значения с фактическими значениями

7. Настройте параметры модели и пользовательские входные данные для улучшения результатов

Метрика

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

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

Исследование данных

Данные по акциям Google были взяты из Yahoo Finance за период с 01.10.2004 по 01.10.2022.

def get_stock_data(symbol, start_date, end_date):
    '''
    download stock data over from yahoo api form start date to end date
    input
        stock - String representing stock symbol eg APPL
        start - datetime object represent start date; default Jan 1, 2010
        end - datetime object represent end date; default: Jan 1, 2020
    output
        historical stock data pulled from yahoo finance stock api from start to end dates
    '''
    stockData = web.DataReader(symbol, 'yahoo', start_date, end_date)
    
    return stockData

start_date = dt.datetime(2004, 10, 1)
end_date = dt.datetime(2022, 10, 1)
df_GOOG = get_stock_data('GOOG',start_date, end_date)

Описание значений, содержащихся в наборе данных, приведено в таблице 1.

Таблица 1: Описание набора данных

Глядя на подсчет, видно, что пропущенных значений нет. Кроме того, мы видим резкую разницу между средними значениями, 50% и 75%, и максимальными значениями для максимума, минимума и открытия. Раннее указание на потенциальный скачок цен на акции, который необходимо наблюдать.

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

Рисунок 1: Корреляционная матрица
Рисунок 2: Цена акции

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

Алгоритмы и методы

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

Модели LSTM имеют 3 затвора и 1 состояние:

Состояние ячейки используется для хранения «долговременной памяти». Он начинается с начала цикла, обновляется при необходимости, а затем используется вместе с входными данными для получения выходных данных.

Затвор забыть используется для обновления состояния ячейки в начале. Он принимает во внимание входные данные и «кратковременную память» из предыдущего события и соответствующим образом обновляет состояние ячейки.

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

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

Рисунок 3: Модель LSTM

Методология

Импорт соответствующих библиотек

На рисунке 4 показаны библиотеки, которые были импортированы для продолжения анализа. Библиотеки включают в себя общие дополнения, такие как pandas, numpy, seaborn, sklearn и keras. Одной из библиотек, на которую следует обратить внимание, является библиотека finta, используемая для расчета технических индикаторов, которые будут добавлены в набор данных, который будет обсуждаться позже.

import datetime as dt
from finta import TA

import pandas as pd
from pandas_datareader import data as web
import numpy as np

import matplotlib.pyplot as plt
import seaborn as sns

from sklearn import preprocessing
from sklearn.metrics import mean_squared_error
from numpy import size
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler

import tensorflow as tf

import keras
from keras.models import Sequential
from keras.models import Model
from keras.layers import Dense, Dropout, LSTM, Input, Activation, concatenate
from keras import optimizers
from keras.callbacks import History 

Предварительная обработка данных

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

После первоначального изучения набора данных было определено, что для повышения точности прогноза потребуются дополнительные функции. Функции представлены в виде технических индикаторов, используемых для измерения производительности акций различными способами. Технические индикаторы делятся на разные типы, а именно: трендовые, импульсные, волатильные и объемные. Несмотря на то, что может возникнуть соблазн включить как можно больше таких показателей, их добавление требует осторожности. Если добавить много однотипных индикаторов, модель будет страдать от мультиколлинеарности, раздувая результаты в ту или иную сторону. По этой причине рекомендуется использовать смесь типов технических индикаторов таким образом, чтобы они дополняли друг друга. Существуют различные типы стратегий, когда кто-то пытается интерпретировать фондовый рынок в попытке сделать прогнозы относительно будущей цены акций. Один из них включает использование индекса относительной силы вместе с полосами Боллинджера. Необходимо обратить внимание на наблюдение за тем, достигают ли цены какой-либо из верхних или нижних полос, одновременно следя за значением RSI, чтобы оценить, перекуплены ли акции или перепроданы. Такие индикаторы могут служить ранним сигналом для инвесторов к развороту тренда акции.

Для целей данного анализа были выбраны следующие технические индикаторы:

— Индекс относительной силы (RSI) — используется в качестве индикатора импульса, обычно используется для определения силы изменения цены

— On Balance Volume — используется в качестве индикатора объема, рассчитывающего давление на покупку и продажу акций

— Полосы Боллинджера — используется в качестве индикатора волатильности и тренда, строит три линии тренда, определяя, является ли акция перекупленной или перепроданной

После того, как все технические индикаторы будут включены, набор данных необходимо будет разделить на набор данных, используемый для обучения модели, и тот, который будет использоваться для тестирования модели и сравнения ее прогностической силы. Разделение будет следовать стандартным практикам, позволяя 70% набора данных для обучения и 30% для тестирования.

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

#Split data
train_data = df[:split_index]
test_data = df[split_index:]

#Normalize the variables
normaliser = preprocessing.MinMaxScaler(feature_range=(0,1))
#Creating a specific scaler for the y values
y_normaliser = preprocessing.MinMaxScaler(feature_range=(0,1))
#Normalizing the training dataset by using fit_transform
train_normalised = normaliser.fit_transform(train_data)

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

# split a multivariate sequence into samples
def split_sequences(sequences, n_steps):
	'''
	A method for preprocessing arrays for LSTM modelling. The sequence will be split into two:
	- A tensor containing groups of all the variables of a user-defined set of time-steps before the price to be predicted
	- A matrix containing the prices to be predicted

	Args:
	-sequences - the sequence to be pre-processed
	-n_steps - user-defined time-steps before the predicted price

	Output:
	X - the tensor
	y - the matrix 
	'''

	X, y = list(), list()
	for i in range(len(sequences)):
		# find the end of this pattern
		end_ix = i + n_steps
		# check if we are beyond the dataset
		if end_ix > len(sequences):
			break
		# gather input and output parts of the pattern
		seq_x, seq_y = sequences[i:end_ix, :-1], sequences[end_ix-1, -1]
		X.append(seq_x)
		y.append(seq_y)
	return np.array(X), np.array(y)

Модель

Построенная модель включает в себя 4 слоя LSTM, за каждым из которых следует Dropout. Все 4 слоя LSTM были смоделированы для считывания входных данных и вывода 70 признаков и имеют функцию активации relu. Оптимизатором, используемым для модели, является adam. Модель также обучена для 25 эпох с размером партии 32.

def build_lstm(X_train, y_train):

    
    '''
    A method to train an LSTM model using 4 LSTM layers, 1 Dense layer and a relu activation function

    Args:
    X_train - training variables
    y_train - training prices

    Output:
    model - a trained LSTM model used for predicting y_test
    '''
    model = Sequential()
    #Adding the first LSTM layer and some Dropout regularisation
    model.add(LSTM(units = 70, return_sequences = True, input_shape = (X_train.shape[1], 10)))
    model.add(Dropout(0.2))
    # Adding a second LSTM layer and some Dropout regularisation
    model.add(LSTM(units = 70, return_sequences = True))
    model.add(Dropout(0.2))
    # Adding a third LSTM layer and some Dropout regularisation
    model.add(LSTM(units = 70, return_sequences = True))
    model.add(Dropout(0.2))
    # Adding a fourth LSTM layer and some Dropout regularisation
    model.add(LSTM(units = 70))
    model.add(Dropout(0.2))
    # Adding the output layer
    model.add(Dense(units = 1))
    model.add(Activation('relu'))

    # Compiling the RNN
    model.compile(optimizer = 'adam', loss = 'mean_squared_error')

    # Fitting the RNN to the Training set
    model.fit(X_train, y_train, epochs = 25, batch_size = 32)

    return model

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

X_train, X_test, y_train, y_test, y_test_norm, split_index, days_prior = data_preprocessing(df_GOOG, 21, 0.7)
model = build_lstm(X_train, y_train)
predicted_stock_price = model.predict(X_test)

Фрагмент 6: Предварительная обработка, построение модели LSTM и прогнозирование цены акций

Рисунок 4: Эпохи

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

После того, как прогнозы сделаны, они сопоставляются с фактическими значениями, наблюдаемыми в наборе данных тестирования.

# Visualising the results
plt.figure(figsize=(15,8))
plt.plot(y_test_norm, color = "red", label = "Real Price - scaled")
plt.plot(predicted_stock_price, color = "blue", label = "Predicted Price - scaled")
plt.title('Google Stock Price Prediction - 21-day look-back')
plt.xlabel('Time range')
plt.ylabel('Google Stock Price')
plt.legend()
plt.show()
print("Mean absolute error of {0}".format(mean_squared_error(y_test_norm[:-20], predicted_stock_price, squared = False)))
Рисунок 5: Прогнозируемые и фактические значения

MSE = 0,9545

Несмотря на то, что MSE модели составляет около 95, видно, что модель проделала относительно приличную работу по прогнозированию направления, в котором движутся акции. Тем не менее, технические индикаторы показали хорошие результаты вплоть до падения в 2020 году. После этого можно наблюдать, что технические индикаторы теряют из виду быстрый рост акций. Это служит доказательством того, что существуют и другие факторы, влияющие на рост акций — либо другие технические индикаторы, либо внешние факторы, такие как шумиха в СМИ или феномен страха упустить прибыль и т. д.

Очищение

После наблюдения значений на рисунке 11 была предпринята попытка уточнить модель LSTM путем тонкой настройки таких параметров, как количество слоев, количество содержащихся в них единиц, количество эпох и т. д. Еще один параметр, который был изменен, — это количество дней, предшествующих прогнозу цены. Вышеуказанные результаты были достигнуты после просмотра за 21 день до прогнозируемой цены. Попытки будут предприниматься с более короткими временными рамками в 7 дней и более длительными временными рамками в 50 дней.

7 дней

Рисунок 6: Эпохи — 7 дней
Рисунок 7: Прогнозируемое и фактическое — 7 дней

МСЭ = 1,009

50 дней

Рисунок 8: Эпохи — 50 дней
Рисунок 9: Прогнозируемое и фактическое — 50 дней

МСЭ = 0,943

Из приведенных выше графиков видно, что 7-дневная модель следует той же тенденции, что и 21-дневная. Тем не менее, он имеет более высокую среднеквадратическую ошибку, что делает его немного хуже, чем 21-дневная модель. С другой стороны, 50-дневная модель дает нам лучшую среднеквадратическую ошибку, чем 21-дневная. Однако видно, что прогнозируемая линия не следует за трендом так же хорошо, как в 21-дневной модели, отставая на заметную величину. Меньшая среднеквадратическая ошибка может быть результатом того, что синяя линия имеет больше перекрывающихся частей с красной линией, что не обязательно приводит к лучшей модели, как это видно из рисунков выше.

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

Заключение

Была предпринята попытка предсказать цену акций Google на следующий день, основываясь на предыдущих n-днях. Это было достигнуто путем получения данных из API Yahoo Finances, а затем применения метода LSTM, чтобы воспользоваться его способностью манипулировать данными временных рядов.

Помимо индикаторов, которые поставлялись с набором данных от Yahoo Finance, были добавлены дополнительные технические индикаторы в попытке повысить точность прогнозов. К таким дополнениям относятся индекс относительной силы (RSI), используемый в качестве индикатора импульса, балансовый объем, используемый в качестве индикатора объема, и полосы Боллинджера, используемые в качестве индикаторов волатильности и тренда.

Используемая модель LSTM включала 4 слоя LSTM с функцией активации relu, разбивающей входы на 70 признаков. За ними последовал плотный слой, в то время как оптимизатором, который использовался, является adam.

Набор данных был разбит и преобразован в тензоры с заданной пользователем длиной, которые соответствуют количеству предыдущих дней, учитываемых прогнозом. Тестируемые длины включают 7 дней, 21 день и 50 дней. Из протестированных 21-дневный сценарий дал наилучшие результаты.

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

Улучшение

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

Ссылки:

Определение технического индикатора. (2021, 30 сентября). Инвестопедия. Проверено 20 октября 2022 года с https://www.investopedia.com/terms/t/technicalindicator.asp

Эффективное использование торговых индикаторов. (2022, 18 сентября). Инвестопедия. Проверено 20 октября 2022 года с https://www.investopedia.com/articles/trading/12/using-trading-indicators-effectively.asp

18 чемпионов трейдинга делятся своими ключами к максимальной торговой прибыли. (2021, 16 ноября). Брокерxplorer. Получено 20 октября 2022 года из https://www.brokerxplorer.com/article/bollinger-bands-and-rsi-combo-strategy-2290.

Прогнозирование цен на акции с помощью регрессионных моделей

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

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

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

https://stocknear.com

Отказ:

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

Получение набора данных

Чтобы получить ваши любимые биржевые данные, мы можем использовать библиотеку yfinance. В этом примере мы спрогнозируем цену акции «AMD».

import yfinance as yf

ticker = 'AMD'
start_date = datetime(2010,1,1)
end_date = datetime.now()
df = yf.download(ticker, start=start_date, end = end_date, interval="1d")
df = df.reset_index()

Нормализуйте свои данные

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

from sklearn.preprocessing import MinMaxScaler

dates = self.data['Date']
df = self.data['Close']
scaler= MinMaxScaler(feature_range=(0,1))
#scaler = StandardScaler()
df=scaler.fit_transform(np.array(df).reshape(-1,1))

Кроме того, вы также можете выбрать StandardScaler(), чтобы посмотреть, сможете ли вы получить лучшие результаты :).

Разделите свои данные

Чтобы обеспечить точность и эффективность нашей модели, мы разделили наши данные на отдельные обучающие и тестовые наборы — широко распространенная передовая практика в этой области. В этом конкретном случае я разделю набор данных на 95% обучающих данных и 5% тестовых данных, но не стесняйтесь корректировать соотношение в соответствии с вашими потребностями.

test_size = 0.05 # 5 % of all the data is the test data
test_split_idx = int(df.shape[0]*(1-test_size))
train_data = df[:test_split_idx].copy()
test_data = df[test_split_idx:].copy()

Подготовьте свои данные

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

Конечная цель состоит в том, чтобы предсказать цену на следующий день. Для этого мы определяем функцию «create_dataset( … )», которая может эффективно подготовить набор данных и правильно извлечь dataX (признаки) и dataY (метки).


lookback = 10

def create_dataset(dataset):
dataX, dataY = [], []
for i in range(len(dataset)-lookback-1):
a = dataset[i:(i+lookback), 0]
dataX.append(a) #features
dataY.append(dataset[i + lookback, 0]) #labels
return np.array(dataX), np.array(dataY)


X_train, y_train = create_dataset(train_data)
X_test, y_test = create_dataset(test_data)

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

Выберите модель и дайте ей потренироваться

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

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

from sklearn.linear_model import LinearRegression

model = LinearRegression()
model.fit(X_train,y_train)

Тестирование модели

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

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

#====Start-Testing====#
train_predict=model.predict(X_train)
train_predict = train_predict.reshape(-1,1)
test_predict=model.predict(X_test)
test_predict = test_predict.reshape(-1,1)

#Denormalize your data to get back to the old form close price
train_predict = scaler.inverse_transform(train_predict)
test_predict = scaler.inverse_transform(test_predict)
original_ytrain = scaler.inverse_transform(y_train.reshape(-1,1))
original_ytest = scaler.inverse_transform(y_test.reshape(-1,1))

metric = metric_score(original_ytrain, train_predict, original_ytest, test_predict)

В последней строке кода вызывается функция «metric_score( … )», обеспечивающая количественную оценку работоспособности модели при обучении и тестировании. Эта оценка основана на рассчитанной дисперсии и оценках r2, как показано в приведенном ниже коде.

from sklearn.metrics import explained_variance_score,r2_score

def metric_score(original_ytrain, train_predict, original_ytest, test_predict):
res = pd.DataFrame(index=['metrics'])
res['train_variance_score'] = explained_variance_score(original_ytrain, train_predict)
res['test_variance_score'] = explained_variance_score(original_ytest, test_predict)

res['train_r2_score'] = r2_score(original_ytrain, train_predict)
res['test_r2_score'] = r2_score(original_ytest, test_predict)
res=res.reset_index(drop=True)
return res

Если вас интересует оценка дисперсии и оценка r2, вот простое объяснение, которое должно ответить на ваш вопрос.

explained_variance_score:

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

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

r2_score:

Оценка r2 количественно оценивает соответствие регрессионной модели данным в диапазоне от 0 до 1, где 0 означает, что изменчивость не объясняется, а 1 означает, что вся изменчивость объяснена. Как правило, более высокий балл r2 означает лучшее соответствие модели, но это не является гарантией точности модели или способности обобщать прогнозы на основе новых данных.

Для модели линейной регрессии получим результат:

   train_variance_score  test_variance_score  train_r2_score  test_r2_score
0.99823 0.962397 0.99823 0.962284

Прогнозируйте цену акций на ближайшие 30 дней

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

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

from datetime import datetime, timedelta

#====Start-Predicting-N-Days====#
pred_ndays = 30
x_input=test_data[len(test_data)-lookback:].reshape(1,-1)
temp_input=list(x_input)
temp_input=temp_input[0].tolist()

model_output=[]
i=0

skip_date = self.data['Date'].iloc[-1]
check_date = correct_weekday(skip_date)

future_dates = []
while len(model_output) < pred_ndays:
while_date = correct_weekday(skip_date + timedelta(i))

if while_date != check_date:

check_date = while_date

future_dates.append(check_date)

if len(temp_input) > lookback:
x_input = np.array(temp_input[1:])
x_input = x_input.reshape(1,-1)

yhat = model.predict(x_input)

temp_input.extend(yhat.tolist())
temp_input = temp_input[1:]
model_output.extend(yhat.tolist())
else:
yhat = model.predict(x_input)
temp_input.extend(yhat.tolist())
model_output.extend(yhat.tolist())

else:
pass

i += 1

# Check if model_output has reached desired length i.e. 10 close price prediction
if len(model_output) >= pred_ndays:
break


future_dates = pd.DataFrame(future_dates)

future_dates['Date'] = future_dates[0]

new_pred_df = (np.array(model_output).reshape(-1,1)).tolist()
new_pred_df = pd.DataFrame(scaler.inverse_transform(new_pred_df).reshape(1,-1).tolist()[0])

pred_res = pd.DataFrame()
pred_res['Date'] = future_dates['Date']
pred_res['yhat'] = new_pred_df

print(pred_res)
#====End-Predicting-N-Days====#

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

def correct_weekday(select_date):
#monday is 0 and sunday is 6
state = None
if select_date.weekday() > 4:
select_date = select_date - timedelta(select_date.weekday()-4)
else:
pass
return select_date

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

Результаты:

Запуск модели, которую мы создали, предоставит вам цену закрытия AMD в течение следующих 30 дней.

  Date        yhat
0 2023-03-13 82.813671
1 2023-03-14 82.914976
2 2023-03-15 83.160334
3 2023-03-16 83.370078
4 2023-03-17 83.572198
5 2023-03-20 83.771417
6 2023-03-21 83.969681
7 2023-03-22 84.168923
8 2023-03-23 84.368702
9 2023-03-24 84.569060
......................
......................
29 2023-04-21 81.5617

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

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

Пока всё в порядке! Мы видим, что модель с высокой точностью предсказывает как обучающий, так и тестовый наборы. Что касается новых прогнозов на будущее, то модель говорит нам о том, что цена закрытия будет более стабильной и, как ожидается, составит около $82-$84 за акцию.

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

Производительность каждой модели представлена на гистограмме ниже.

Метрическая оценка трех регрессионных моделей

Заключение

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

Дюны

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

Полный код можно найти в моем репозитории github.

Прогнозирование цен на акции с помощью многомерного анализа

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

Цель этой статьи — объяснить, как многомерный анализ можно использовать для прогнозирования цен на акции. Сначала будут рассмотрены основы многомерного анализа. Затем мы обсудим, как многомерный анализ можно использовать для прогнозирования цен на акции. В заключение мы обсудим результаты исследования, в котором использовался многомерный анализ для прогнозирования цен на акции.

Приступим к программированию

# Importing dependencies

import numpy as np
np.random.seed(1)
from tensorflow import set_random_seed
set_random_seed(2)
import pandas as pd
import matplotlib.pyplot as plt
from keras.models import Sequential, load_model
from keras.layers.core import Dense
from keras.layers.recurrent import LSTM
from keras import optimizers
from keras.callbacks import EarlyStopping
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error, r2_score
from math import sqrt
import datetime as dt
import time
plt.style.use('ggplot')

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

Эта строка импортирует NumPy, мощный пакет для научных вычислений, и присваивает ему псевдоним np. С помощью NumPy вы можете манипулировать массивами и эффективно выполнять числовые операции.

np.random.seed(1): Эта строка задает начальное число для генератора случайных чисел NumPy. Начальное значение гарантирует, что NumPy генерирует одни и те же случайные числа при каждом запуске кода. В этом случае начальное значение устанавливается равным 1.

TensorFlow import set_random_seed: Эта строка импортирует функцию set_random_seed. Начальное число установлено здесь, чтобы обеспечить воспроизводимость результатов при использовании генератора случайных чисел TensorFlow.

Строка set_random_seed(2) устанавливает начальное значение генератора случайных чисел равным 2 в TensorFlow. При использовании случайных чисел в TensorFlow это помогает получать согласованные результаты, как и начальное число NumPy.

Эта строка импортирует библиотеку pandas, универсальный инструмент для манипулирования и анализа данных. Для удобства ему присвоен псевдоним pd. Для обработки структурированных данных Pandas предоставляет мощные структуры данных, такие как DataFrames.

Эта строка импортирует модуль pyplot из библиотеки matplotlib, которая является библиотекой построения графиков Python. Псевдоним plt присвоен для удобства. С помощью функций Matplotlib можно создать широкий спектр визуализаций.

Из keras.models import Sequential, load_model: Эта строка импортирует класс Sequential и функцию load_model. Помимо TensorFlow, Keras предоставляет высокоуровневый API для нейронных сетей. Загрузка предварительно обученной модели из файла выполняется с помощью функции load_model класса Sequential.

Dense импортируется из keras.layers.core, который представляет собой полносвязный слой нейронной сети. Плотные слои обычно используются в моделях глубокого обучения.

Эта строка импортирует класс LSTM из keras.layers.recurrent. LSTM — это рекуррентные нейронные сети (RNN), которые хорошо подходят для обработки последовательных данных и фиксации долгосрочных отношений.

Импорт оптимизаторов из Keras: Эта строка импортирует модуль оптимизаторов. Для обучения нейронных сетей модуль оптимизаторов предоставляет множество алгоритмов оптимизации, включая стохастический градиентный спуск (SGD), Adam, RMSprop и другие.

Из keras.callbacks import EarlyStopping: Эта строка импортирует класс обратного вызова EarlyEnding Keras. В зависимости от конкретных условий, таких как отсутствие потерь при проверке, обратный вызов EarlyStop может остановить процесс обучения на ранней стадии.

Из sklearn.preprocessing import MinMaxScaler: Эта строка импортирует класс MinMaxScaler из scikit-learn. Для масштабирования объектов MinMaxScaler используется для масштабирования входных данных от 0 до 1, обычно от 0 до 1.

Из sklearn.metrics import mean_squared_error, r2_score: эта строка импортирует mean_squared_error и r2_score из scikit-learn. Регрессионные модели обычно оцениваются с помощью этих функций. Баллы R-квадрат вычисляются с помощью функций mean_squared_error и r2_score.

Math import sqrt: Эта строка импортирует функцию sqrt. Квадратный корень из числа можно вычислить с помощью функции sqrt.

В этой строке импортируется модуль datetime, которому присваивается псевдоним dt. Datetime предоставляет классы и функции для работы с датой и временем.

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

Plot.style.use(‘ggplot’): эта строка изменяет стиль графиков matplotlib на «ggplot». Визуальная привлекательность и согласованность придаются графикам с помощью стиля «ggplot», который основан на библиотеке ggplot на языке программирования R.

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

Предварительная обработка данных

# Loading the dataset
url = '../CSV.csv'
df = pd.read_csv(url,parse_dates = True,index_col=0)
df.tail()

Код для загрузки набора данных: Переменной url назначается местоположение файла набора данных. Это может быть URL-адрес, указывающий на локальный файл, или URL-адрес, указывающий на набор данных в Интернете. В закомментированной строке вместо URL-адреса (‘.. /CSV.csv’). Под этим подразумевается локальное хранилище.

Создание DataFrame: DataFrame создается путем чтения файла набора данных с помощью функции pd.read_csv(). Функция требует нескольких параметров:

Он указывает, где следует считывать файл набора данных, указывая URL-адрес или путь к файлу.

Если parse_dates=True, все столбцы, содержащие даты, анализируются как объекты datetime. В результате данными, связанными со временем, можно легко манипулировать и анализировать.

Укажите index_col=0, чтобы использовать первый столбец набора данных в качестве столбца индекса DataFrame. Данные могут быть организованы и доступны более легко, если назначены метки строк.

Функция df.tail() используется для отображения последних нескольких строк DataFrame. По умолчанию отображаются пять последних строк. Таким образом, можно быстро просмотреть самые последние точки данных набора данных и проверить, успешно ли загружен набор данных в DataFrame.

# Correlation matrix
df.corr()['Close']

Столбец «Close» DataFrame DF и все остальные столбцы коррелируют с помощью этого кода. Создается корреляционная матрица, которая показывает попарные корреляции между столбцами «Закрыть» и «Закончить».

Коэффициенты корреляции между столбцами в DataFrame вычисляются с помощью функции .corr(). После вызова df.corr() мы указываем [‘Close’], чтобы включить в матрицу корреляции только значения корреляции, связанные со столбцом ‘Close’.

Эта корреляционная матрица показывает линейную зависимость между столбцом «Закрыть» и другими столбцами в DataFrame. Значения, близкие к 1, указывают на сильную положительную корреляцию, значения, близкие к -1, указывают на сильную отрицательную корреляцию, а значения, близкие к 0, указывают на отсутствие корреляции или слабую корреляцию.

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

print(df.describe().Volume) 
df.drop(df[df['Volume']==0].index, inplace = True) #Dropping rows with volume value 0

В DataFrame df строка print(df.describe(). Volume) выводит статистическую сводку по столбцу «Объем». Функция describe() вычисляет различные статистические показатели из столбца «Объем», такие как количество, среднее значение, стандартное отклонение, минимум, квартили и максимум. Это обеспечивает обзор значений тома в DataFrame, а также их распределения и характеристик.

При удалении строк из DataFrame df, где столбец «Volume» имеет значение 0, строка df.drop(df[df[‘Volume’]==0].index, inplace=True) удаляет строки из DataFrame df. Сравнивая столбец «Объем» с 0, он идентифицирует строки со значением тома, равным 0. Логическая маска, созданная выражением df[‘Volume’]==0, выбирает строки, удовлетворяющие условию. DataFrame, содержащий только строки со значениями тома, равными 0, возвращается кодом df[df[‘Volume’]==0]. Затем можно получить индексы этих строк с помощью атрибута .index. Наконец, функция drop() вызывается на исходном DataFrame df, передавая индексы строк. Изменения DataFrame применяются непосредственно к DataFrame df, если указан inplace=True.

Сводка значений тома в DataFrame предоставляется первой строкой кода, которая выводит статистическую сводку столбца «Volume». Удаляя строки со значениями тома, равными 0, вторая строка удаляет все строки, в которых отсутствуют данные тома или недопустимые значения тома.

# Setting up an early stop
earlystop = EarlyStopping(monitor='val_loss', min_delta=0.0001, patience=80, verbose=1, mode='min')
callbacks_list = [earlystop]

В этом коде настроен механизм ранней остановки процесса обучения модели. Создается объект EarlyStop с параметрами для определения поведения механизма. Для параметра «Монитор» установлено значение «val_loss», что указывает на то, что потеря проверки будет отслеживаться, чтобы определить, следует ли остановить обучение. Минимальное улучшение потерь при проверке можно определить, установив для параметра «min_delta» значение 0,0001. Используя параметр «терпение», ожидают 80 эпох, прежде чем потеря проверки улучшится. Тренировочный процесс будет остановлен, если за это число эпох не будет наблюдаться никаких улучшений. Установив для параметра «verbose» значение 1, можно распечатать сообщения о досрочной остановке процесса. В качестве последнего шага для параметра «mode» устанавливается значение «min», что указывает на то, что потери при проверке должны быть сведены к минимуму. В callbacks_list, который представляет собой список обратных вызовов, выполняющих определенные действия во время обучения, добавляется объект EarlyStoping. Этот код предотвращает переобучение и экономит вычислительные ресурсы, автоматически останавливая процесс обучения модели, когда потеря проверки значительно не улучшается.

#Build and train the model
def fit_model(train,val,timesteps,hl,lr,batch,epochs):
X_train = []
Y_train = []
X_val = []
Y_val = []

# Loop for training data
for i in range(timesteps,train.shape[0]):
X_train.append(train[i-timesteps:i])
Y_train.append(train[i][0])
X_train,Y_train = np.array(X_train),np.array(Y_train)

# Loop for val data
for i in range(timesteps,val.shape[0]):
X_val.append(val[i-timesteps:i])
Y_val.append(val[i][0])
X_val,Y_val = np.array(X_val),np.array(Y_val)

# Adding Layers to the model
model = Sequential()
model.add(LSTM(X_train.shape[2],input_shape = (X_train.shape[1],X_train.shape[2]),return_sequences = True,
activation = 'relu'))
for i in range(len(hl)-1):
model.add(LSTM(hl[i],activation = 'relu',return_sequences = True))
model.add(LSTM(hl[-1],activation = 'relu'))
model.add(Dense(1))
model.compile(optimizer = optimizers.Adam(lr = lr), loss = 'mean_squared_error')
#print(model.summary())

# Training the data
history = model.fit(X_train,Y_train,epochs = epochs,batch_size = batch,validation_data = (X_val, Y_val),verbose = 0,
shuffle = False, callbacks=callbacks_list)
model.reset_states()
return model, history.history['loss'], history.history['val_loss']

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

Есть несколько параметров, которые можно ввести в функцию fit_model. ‘train’ и ‘val’ представляют собой наборы данных для обучения и проверки соответственно. При обучении модели на последовательных данных параметр ‘timesteps’ указывает, сколько шагов нужно учитывать. Параметр hl нейронной сети LSTM определяет количество нейронов в каждом скрытом слое. Скорость обучения оптимизатора представлена параметром ‘lr’. Кроме того, «пакет» указывает количество пакетов, используемых для обучения, а «эпохи» указывает, как часто повторяется процесс обучения.

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

Затем функция строит нейронные сети на основе LSTM. Модель начинается со слоя LSTM, который содержит то же количество нейронов, что и входные данные. Временные шаги используются в качестве входных данных для этого слоя. Еще один слой LSTM добавляется на основе значений в списке ‘hl’. На каждом из этих уровней используются функции активации ReLU. Каждый слой LSTM возвращает последовательности в качестве выходных данных для передачи в следующий слой, кроме последнего. Каждая выходная последовательность создается конечным слоем LSTM.

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

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

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

# Evaluating the model
def evaluate_model(model,test,timesteps):
X_test = []
Y_test = []

# Loop for testing data
for i in range(timesteps,test.shape[0]):
X_test.append(test[i-timesteps:i])
Y_test.append(test[i][0])
X_test,Y_test = np.array(X_test),np.array(Y_test)
#print(X_test.shape,Y_test.shape)

# Prediction Time !!!!
Y_hat = model.predict(X_test)
mse = mean_squared_error(Y_test,Y_hat)
rmse = sqrt(mse)
r = r2_score(Y_test,Y_hat)
return mse, rmse, r, Y_test, Y_hat

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

Для evaluate_model требуется три параметра. Как указывает параметр ‘model’, обучаемая модель оценивается. В этом случае параметр ‘test’ соответствует набору данных, используемому для тестирования. Параметр ‘timesteps’ указывает количество шагов, которые необходимо учитывать при создании последовательностей из тестового набора данных, которое соответствует значению, используемому во время обучения.

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

На следующем шаге модель используется для прогнозирования выходных данных каждой входной последовательности. «Время прогнозирования» относится к этому шагу.

Для измерения качества прогнозов модели используются три метрики:

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

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

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

В качестве последнего шага функция предоставляет оценку MSE, RMSE, R2, фактические выходные данные тестового набора данных и прогнозы модели.

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

# Plotting the predictions
def plot_data(Y_test,Y_hat):
plt.plot(Y_test,c = 'r')
plt.plot(Y_hat,c = 'y')
plt.xlabel('Day')
plt.ylabel('Price')
plt.title('Stock Prediction Graph using Multivariate-LSTM model')
plt.legend(['Actual','Predicted'],loc = 'lower right')
plt.show()

Визуальное представление фактических данных и прогнозов цен на акции является целью plot_data. Говоря простым языком, вот как это работает:

Для plot_data требуются два параметра. Y_test представляет массив фактических выходных значений из тестового набора данных, а Y_hat представляет массив прогнозируемых выходных значений.

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

Есть красная линия («r»), изображающая фактические значения (Y_test).

Линия «y» представляет прогнозируемое значение (Y_hat).

На оси X появится метка «День», указывающая номер дня тестового набора данных.

Ось Y представляет собой цену акций, которая обозначена как «Цена».

Модель LSTM используется для прогнозирования цены акций с помощью многомерного графика прогнозирования.

Цвета, связанные с фактическими и прогнозируемыми значениями, указаны в легенде в правом нижнем углу графика.

Затем пришло время отобразить сюжет.

В результате эта функция сравнивает фактические и прогнозируемые цены акций за тестовый период. Как правило, чем ближе строка «Предсказано» совпадает с линией «Фактическое», тем точнее прогноз.

# Plotting the training errors
def plot_error(train_loss,val_loss):
plt.plot(train_loss,c = 'r')
plt.plot(val_loss,c = 'b')
plt.ylabel('Loss')
plt.legend(['train','val'],loc = 'upper right')
plt.show()

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

В функцию plot_error передаются два параметра, train_loss и val_loss, которые представляют значения потерь при обучении и проверке соответственно.

В рамках функции выполняется анализ потерь при обучении и валидации:

С plt.plot(train_loss, c=’r’) потери при обучении отображаются в виде красной линии.

При использовании plt.plot(val_loss, c=’b’) потери при проверке отображаются в виде синей линии.

При использовании plt.ylabel(‘Loss’) ось Y графика помечается как ‘Loss’.

Plt.legend([‘train’, ‘val’], loc=’upper right’) используется для добавления легенды на график. Красная линия обозначает потерю при обучении, а синяя линия — потерю проверки в этой легенде.

Наконец, график отображается с помощью plt.show(), что позволяет пользователю просматривать и сравнивать ошибки во время обучения и проверки.

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

Построение модели

# Extracting the series
series = df[['Close','High','Volume']] # Picking the series with high correlation
print(series.shape)
print(series.tail())

Подмножества DataFrame извлекаются и отображаются с помощью следующего кода:

DataFrame ‘df’ выбирается из кода. Выбраны столбцы «Закрыть», «Высокий» и «Громкость». Столбцы «Закрытие», «Максимум» и «Объем», вероятно, содержат данные о ценах на акции, где «Закрытие» представляет цену закрытия, «Максимум» показывает самую высокую цену, а «Объем» представляет объем торгов.

В переменной с именем ‘series’ хранится извлеченная серия.

После печати формы объекта ‘series’ код печатает инструкцию ‘print(series.shape)’. При этом предоставляется информация об измерениях извлеченных данных. В DataFrame ‘series’ выходные данные фигуры содержат два значения: количество строк (наблюдений) и количество столбцов (переменных).

Последний шаг в коде выводит последние несколько строк кадра данных ‘series’ с помощью функции ‘print(series.tail()). Просматривая столбцы «Закрытие», «Максимум» и «Объем», вы можете получить представление о самых последних точках данных.

Сводка: Этот фрагмент кода выбирает столбцы из DataFrame и сохраняет их в отдельном DataFrame, называемом ‘series’. Кроме того, последние несколько строк данных отображаются вместе с формой кадра данных «ряда». Для того, чтобы понять структуру и содержание извлеченных данных, выполняются следующие шаги.

# Train Val Test Split
train_start = dt.date(1997,1,1)
train_end = dt.date(2006,12,31)
train_data = series.loc[train_start:train_end]

val_start = dt.date(2007,1,1)
val_end = dt.date(2008,12,31)
val_data = series.loc[val_start:val_end]

test_start = dt.date(2009,1,1)
test_end = dt.date(2010,12,31)
test_data = series.loc[test_start:test_end]

print(train_data.shape,val_data.shape,test_data.shape)

Код делит набор данных на три подмножества: обучение, проверка и тестирование, в соответствии с разделением обучение-проверка-тестирование. Вот как работает код на простом английском языке:

Коды устанавливают начальную и конечную даты для подмножеств поездов. В результате подмножество поездов здесь охватывает период с 31 декабря 2006 года по 1 января 1997 года.

Train_data присваиваются данные из исходного ряда, который попадает в указанный диапазон дат.

Для подмножества проверки в коде указывается диапазон дат начала и окончания, который охватывает период с 1 января 2007 г. по 31 декабря 2008 г.

Присваивая переменную val_data ряду, он извлекает данные в пределах диапазона дат проверки.

Затем в коде указываются начальная и конечная даты для тестового подмножества, охватывающие период с 1 января 2009 г. по 31 декабря 2010 г.

Переменная test_data используется для извлечения данных в диапазоне дат теста из ряда.

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

В результате этого кода набор данных делится на три подмножества в зависимости от определенных диапазонов дат: обучение, проверка и тестирование. Чтобы указать, сколько точек данных находится в каждом подмножестве, он печатает форму каждого подмножества.

# Normalisation
sc = MinMaxScaler()
train = sc.fit_transform(train_data)
val = sc.transform(val_data)
test = sc.transform(test_data)
print(train.shape,val.shape,test.shape)

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

Более подробно в коде выполняются следующие действия:

Методы машинного обучения обычно используют класс MinMaxScaler для нормализации данных.

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

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

test_data также масштабируется с помощью метода преобразования масштабирования. В результате масштабирование одинаково для всех наборов данных.

Наконец, отображаются фигуры преобразованных наборов данных train, val и test. Массивы характеризуются атрибутом формы, который указывает на количество строк и столбцов.

С помощью MinMaxScaler мы гарантируем, что все наборы данных имеют значения в одном диапазоне (от 0 до 1) для train_data, val_data и test_data. Согласованный диапазон данных может быть полезен для различных алгоритмов машинного обучения и анализа.

timesteps = 50
hl = [40,35]
lr = 1e-3
batch_size = 64
num_epochs = 250

Следующий код задает для следующих переменных определенные значения:

Переменная timesteps имеет значение 50: эта переменная представляет количество временных шагов или интервалов в последовательности данных. Каждая последовательность состоит из 50 шагов, что указывает на то, что данные организованы или проанализированы последовательно.

HL имеет значение [40, 35]: эта переменная представляет структуру или архитектуру нейронной сети. Он указывает, сколько нейронов находится в каждом скрытом слое. В этом примере присутствуют два скрытых слоя, первый слой содержит 40 нейронов, а второй слой содержит 35 нейронов.

Скорость обучения алгоритма машинного обучения представлена переменной lr, для которой задано значение 1e-3. Во время каждой итерации или обновления скорость обучения определяет размер шага или количество изменений. Наличие скорости обучения 1e-3 предполагает, что параметры модели обновляются постепенно.

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

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

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

model,train_error,val_error = fit_model(train,val,timesteps,hl,lr,batch_size,num_epochs)
plot_error(train_error,val_error)

Этот код выполняет две основные задачи. На первом этапе он вызывает функцию с именем fit_model с несколькими аргументами, включая train, val, timesteps, hl, lr, batch_size и num_epochs. Входные данные используются для обучения модели с помощью этой функции. Model, train_error и val_error — это три значения, возвращаемые этой функцией. В то время как train_error и val_error содержат информацию об ошибках или значениях потерь, полученных во время обучения на наборах данных обучения и проверки, соответственно, переменная модели представляет обученную модель.

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

mse, rmse, r2_value,true,predicted = evaluate_model(model,test,timesteps)
print('MSE = {}'.format(mse))
print('RMSE = {}'.format(rmse))
print('R-Squared Score = {}'.format(r2_value))
plot_data(true,predicted)

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

MSE, RMSE, r2_value, true и predicted — это пять значений, возвращаемых evaluate_model. Как квадратный корень из разницы между истинными и прогнозируемыми значениями, mse представляет собой среднюю квадратичную ошибку. RMS расшифровывается как среднеквадратичная ошибка, которая представляет собой квадратный корень из среднеквадратичной ошибки. В результате точность модели легче интерпретировать. Основываясь на оценке R-квадрата, r2_value показывает, насколько хорошо модель соответствует данным, со значениями в диапазоне от 0 до 1. Когда значения выше, модель может объяснить большую часть изменчивости данных. Судя по прогнозам модели на тестовом наборе данных, истинные и прогнозируемые переменные, вероятно, содержат соответствующие истинные и прогнозируемые значения.

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

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

# Save a model
#model.save('MV3-LSTM_50_[40,35]_1e-3_64.h5')
#del model # Deletes the model
# Load a model
model = load_model('MV3-LSTM_50_[40,35]_1e-3_64.h5')

#model.save(‘MV3-LSTM_50_[40,35]1e-3_64.h5’) в настоящее время закомментирован, что указывает на то, что он не активен или не выполняется. Раскомментирование этой строки сохранит модель в файл с именем «MV3-LSTM_50[40,35]_1e-3_64.h5». Модель может быть сохранена для использования в будущем без необходимости повторного обучения. Все изученные шаблоны или веса сохраняются, а также параметры и архитектура модели.

Во вторую строку, #del модели, также добавляется комментарий. Объект модели будет удален из памяти путем раскомментирования и выполнения этой строки. Если вы хотите освободить ресурсы памяти, особенно если модель велика или больше не требуется для дальнейшего анализа или прогнозирования, удаление модели может быть выгодным.

В качестве последнего шага код содержит линейную модель = load_model(‘MV3-LSTM_50_[40,35]1e-3_64.h5’). Предварительно обученная модель загружается из файла с именем «MV3-LSTM_50[40,35]_1e-3_64.h5». Загруженная модель присваивается переменной модели функцией load_model. Загрузка модели позволяет повторно использовать ранее обученную модель для составления прогнозов или проведения дальнейшего анализа без необходимости ее повторного обучения. Архитектура сохраненной модели, параметры и все изученные шаблоны восстанавливаются, поэтому можно продолжить работу над ней.

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

Перекрестная проверка

# Hyperparameters
timesteps = 50
hl = [40,35]
lr = 1e-3
batch_size = 64
num_epochs = 50

Гиперпараметры модели машинного обучения или глубокого обучения задаются этим кодом.

Этот код определяет следующие гиперпараметры:

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

Hl имеет значение [40, 35]: представляет структуру или архитектуру нейронной сети. Каждый скрытый слой определяется количеством нейронов. Здесь есть два скрытых слоя: первый слой содержит 40 нейронов, а второй слой содержит 35 нейронов.

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

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

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

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

# Extracting the series
series = df[['Close','High','Volume']] # Picking the multivariate series
print(series.shape)
print(series.tail())

Line series = df[[‘Close’, ‘High’, ‘Volume’]] извлекает подмножество столбцов из DataFrame df и назначает их ряду переменных. В частности, он выбирает столбцы «Закрыть», «Высокий» и «Объем» из DataFrame. Анализируемые данные представляют собой многомерный ряд, в котором каждая точка данных содержит информацию о цене закрытия актива, максимальной цене и объеме.

Затем print(series.shape) выводит фигуру рядового DataFrame. Атрибут shape показывает размеры DataFrame, а также количество строк и столбцов. При печати эта фигура указывает, сколько точек данных и переменных включено в извлеченный многомерный ряд.

Наконец, print(series.tail()) отображает последние несколько строк рядового DataFrame. Последние тенденции или закономерности в многомерном ряду можно увидеть с помощью функции tail(), которая извлекает только самые последние точки данных. Наблюдение за последними тенденциями или поведением в данных может быть полезно при оценке последних событий.

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

# Normalisation
sc = MinMaxScaler()
series = sc.fit_transform(series[:5400])
series

Набор данных многомерного ряда нормализуется с помощью этого кода.

Класс MinMaxScaler, который обычно используется в машинном обучении, создается с помощью строки sc = MinMaxScaler().

Series = sc.fit_transform(series[:5400]): эта строка вызывает метод fit_transform скейлера для нормализации набора данных ряда. Fit_transform вычисляет минимальные и максимальные значения на основе данных и соответствующим образом масштабирует все значения последовательно. Для нормализации ряда используются только первые 5400 точек данных (строк).

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

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

Сплит 1

#Splitting the data for initial model creation
splits = 5
split_size = 600
train = series[:3*split_size]
test = series[3*split_size:4*split_size]

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

Переменная splits имеет значение 5, что означает, что набор данных будет разбит на пять частей. Split_size указывает, сколько точек данных будет включено в каждое разделение. split_size 600 указывает на то, что в каждое разделение будет включено 600 точек данных.

Чтобы создать обучающий набор, код разбивает набор данных ряда на первые три разделения. Указав series[:3*split_size], он будет извлекать данные в три раза больше split_size. В результате в обучающий набор будет включено 1800 точек данных.

Что касается тестового набора, то он генерируется путем выбора четвертого разделения из набора данных ряда. Используя серии[3 split_size:4split_size], он извлекает данные, начиная с трехкратного split_size и заканчивая четырехкратным. В результате тестовый набор состоит из 600 точек данных, а именно от 1800 до 2400.

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

cross_val_results = list()
train_loss = pd.DataFrame()
val_loss = pd.DataFrame()
model,train_error,val_error = fit_model(train,timesteps,hl,lr,batch_size,num_epochs)
train_loss['Split1'] = train_error
val_loss['Split1'] = val_error
mse, rmse, r2_value,true,predicted = evaluate_model(model,test,timesteps)
print("Split 1")
print('MSE = {}'.format(mse))
print('RMSE = {}'.format(rmse))
print('R-Squared Score = {}'.format(r2_value))
plot_data(true,predicted)
cross_val_results.append([mse,rmse,r2_value,0])
model.save("MV3-LSTM-Split1.h5")

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

Для хранения результатов перекрестной проверки создается пустой список с именем cross_val_results. Для хранения значений потерь при обучении и проверке создаются два пустых кадра данных: train_loss и val_loss.

При передаче обучающих данных, временных шагов, скрытой структуры слоев, скорости обучения, размера пакета и количества эпох в качестве аргументов вызывается функция fit_model. Используя предоставленные входные данные, эта функция обучает модель и возвращает обученную модель, а также значения ошибок обучения и проверки. Значения потерь при обучении назначаются столбцу «Split1» кадра данных train_loss, тогда как значения потерь при проверке назначаются столбцу «Split1» кадра данных val_loss.

Чтобы оценить обученную модель на тестовом наборе данных, вызывается функция evaluate_model. К метрикам, возвращаемым этой функцией, относятся среднеквадратичная ошибка (MSE), среднеквадратичная ошибка (RMSE), оценка R-квадрата (R2), истинные значения и прогнозируемые значения. Распечатка рассчитанных метрик дает представление о производительности модели на тестовых данных. Plot_data также вызывается для визуализации истинных и прогнозируемых значений, полученных из модели.

В списке cross_val_results добавляются метрики оценки и значение заполнителя 0 для хранения результатов оценки для дальнейшего анализа. Файл с именем «MV3-LSTM-Split1.h5» сохраняется для хранения обученной модели. В дальнейшем эта сохраненная модель может быть загружена и использована для дополнительного анализа или прогнозирования.

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

Сплит 2


train = series[:4*split_size]
test = series[4*split_size:5*split_size]
X_train,Y_train = [],[]
# Loop for training data
for i in range(timesteps,train.shape[0]):
X_train.append(train[i-timesteps:i])
Y_train.append(train[i][0])
X_train,Y_train = np.array(X_train),np.array(Y_train)

start = time.time()
history = model.fit(X_train,Y_train,epochs = num_epochs,batch_size = batch_size,validation_split = 0.2,verbose = 0,
shuffle = False)
end = time.time()
train_loss["Split2"] = history.history['loss']
val_loss["Split2"] = history.history['val_loss']
mse, rmse, r2_value,true,predicted = evaluate_model(model,test,timesteps)
print("Split 2")
print('MSE = {}'.format(mse))
print('RMSE = {}'.format(rmse))
print('R-Squared Score = {}'.format(r2_value))
plot_data(true,predicted)
cross_val_results.append([mse,rmse,r2_value,end-start])
model.save("MV3-LSTM-Split2.h5")

В частности, этот код выполняет следующие операции со вторым разделением набора данных:

Назначая переменную train части набора данных серии, он выбирает часть для обучения. Эта часть, включающая точки данных от начала до четырехкратного split_size, представляет данные для обучения модели.

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

Для обучения модели инициализируются пустые списки X_train и Y_train для хранения входных последовательностей и соответствующих целевых значений.

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

Используя start = time.time(), мы можем записать время начала обучения модели и обучить ее с помощью метода fit. Модели обучаются с помощью входных последовательностей (X_train) и целевых значений (Y_train) с указанными эпохами, размерами пакетов и разделениями проверки. В объекте истории хранятся значения хода обучения и потерь.

С помощью end = time.time() можно записать время окончания обучения модели.

В train_loss DataFrame есть столбец «Split2», в котором хранятся значения потерь при обучении из объекта history. Значения потерь при проверке аналогичным образом хранятся в val_loss DataFrame.

При вызове функции evaluate_model производительность модели оценивается на тестовом наборе данных. С помощью этой функции можно вычислить такие показатели, как среднеквадратичная ошибка (MSE), среднеквадратичная ошибка (RMSE) и оценка R-квадрата (R2).

Метрики оценки (MSE, RMSE, R2) и время обучения печатаются для оценки производительности модели в тестовом наборе данных.

Затем Plot_data используется для визуализации истинных значений и прогнозируемых значений, полученных из прогнозов модели.

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

Для будущего использования в прогнозах или анализе обученная модель сохраняется как «MV3-LSTM-Split2.h5».

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

Сплит 3

train = series[:5*split_size]
test = series[5*split_size:6*split_size]
X_train,Y_train = [],[]
# Loop for training data
for i in range(timesteps,train.shape[0]):
X_train.append(train[i-timesteps:i])
Y_train.append(train[i][0])
X_train,Y_train = np.array(X_train),np.array(Y_train)

start = time.time()
history = model.fit(X_train,Y_train,epochs = num_epochs,batch_size = batch_size,validation_split = 0.2,verbose = 0,
shuffle = False)
end = time.time()
train_loss["Split3"] = history.history['loss']
val_loss["Split3"] = history.history['val_loss']
mse, rmse, r2_value,true,predicted = evaluate_model(model,test,timesteps)
print("Split 3")
print('MSE = {}'.format(mse))
print('RMSE = {}'.format(rmse))
print('R-Squared Score = {}'.format(r2_value))
plot_data(true,predicted)
cross_val_results.append([mse,rmse,r2_value,end-start])
model.save("MV3-LSTM-Split3.h5")

С третьим разделением набора данных выполняются следующие операции:

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

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

Для обучения модели инициализируются пустые списки X_train и Y_train для хранения входных последовательностей и соответствующих целевых значений.

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

Он записывается, когда начинается обучение модели, и модель обучается с помощью метода подгонки. Модель обучается путем предоставления входных последовательностей (X_train) и целевых значений (Y_train). В процессе обучения указывается количество эпох, размер пакета, разделение проверки и детализация. Ход обучения и значения потерь хранятся в объекте истории.

Обучение модели заканчивается в определенное время.

В train_loss DataFrame столбец «Split3» содержит значения потерь при обучении из объекта history. Аналогичным образом значения потерь при проверке хранятся в кадре val_loss DataFrame.

С помощью функции evaluate_model производительность модели оценивается на тестовом наборе данных. Функция вычисляет такие показатели, как среднеквадратичная ошибка (MSE), среднеквадратичная ошибка (RMSE) и оценка R-квадрата (R2).

Оценка производительности модели в тестовом наборе данных обеспечивается метриками оценки, включая MSE, RMSE и R2.

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

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

Обученная модель сохраняется как «MV3-LSTM-Split3.h5». Используя этот метод, модель может быть загружена для дальнейшего анализа или прогнозов.

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

Сплит 4

train = series[:6*split_size]
test = series[6*split_size:7*split_size]
X_train,Y_train = [],[]
# Loop for training data
for i in range(timesteps,train.shape[0]):
X_train.append(train[i-timesteps:i])
Y_train.append(train[i][0])
X_train,Y_train = np.array(X_train),np.array(Y_train)

start = time.time()
history = model.fit(X_train,Y_train,epochs = num_epochs,batch_size = batch_size,validation_split = 0.2,verbose = 0,
shuffle = False)
end = time.time()
train_loss["Split4"] = history.history['loss']
val_loss["Split4"] = history.history['val_loss']
mse, rmse, r2_value,true,predicted = evaluate_model(model,test,timesteps)
print("Split 4")
print('MSE = {}'.format(mse))
print('RMSE = {}'.format(rmse))
print('R-Squared Score = {}'.format(r2_value))
plot_data(true,predicted)
cross_val_results.append([mse,rmse,r2_value,end-start])
model.save("MV3-LSTM-Split4.h5")

В рамках четвертого разделения набора данных этот код выполняет следующие операции:

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

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

Для обучения модели используются входные последовательности (X_train) и целевые значения (Y_train). В процессе обучения необходимо указать количество эпох, размер пакета, разделение проверки и детализацию. Данные не перетасовываются. Объект, в котором хранятся значения хода обучения и потерь, называется объектом журнала.

Значения потерь хранятся в train_loss DataFrame, в частности в столбце «Split4». Кадр данных val_loss используется для хранения значений потерь при проверке.

Оценка модели: модель оценивается с использованием тестового набора данных. Такие показатели, как среднеквадратичная ошибка (MSE), среднеквадратичная ошибка (RMSE) и оценка R-квадрата (R2), вычисляются с помощью функции evaluate_model. Определяется, каковы истинные значения и каковы прогнозируемые значения.

Чтобы оценить производительность модели в тестовом наборе данных, код печатает метрики оценки, включая MSE, RMSE и R2.

Построение результатов: прогнозы модели на тестовом наборе данных визуализируются функцией plot_data.

Результаты оценки собираются в списке cross_val_results вместе с метриками (MSE, RMSE, R2). Таким образом, результаты оценки могут быть сохранены для последующего анализа.

Файл с именем «MV3-LSTM-Split4.h5» сохраняется вместе с обученной моделью. Прогнозы и дальнейший анализ могут быть выполнены с помощью этой сохраненной модели.

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

Сплит 5

train = series[:7*split_size]
test = series[7*split_size:8*split_size]
X_train,Y_train = [],[]
# Loop for training data
for i in range(timesteps,train.shape[0]):
X_train.append(train[i-timesteps:i])
Y_train.append(train[i][0])
X_train,Y_train = np.array(X_train),np.array(Y_train)

start = time.time()
history = model.fit(X_train,Y_train,epochs = num_epochs,batch_size = batch_size,validation_split = 0.2,verbose = 0,
shuffle = False)
end = time.time()
train_loss["Split5"] = history.history['loss']
val_loss["Split5"] = history.history['val_loss']
mse, rmse, r2_value,true,predicted = evaluate_model(model,test,timesteps)
print("Split 5")
print('MSE = {}'.format(mse))
print('RMSE = {}'.format(rmse))
print('R-Squared Score = {}'.format(r2_value))
plot_data(true,predicted)
cross_val_results.append([mse,rmse,r2_value,end-start])
model.save("MV3-LSTM-Split5.h5")

В этом коде подробно выполняются следующие операции, уделяя особое внимание пятому разделению набора данных:

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

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

Для обучения модели используются входные последовательности (X_train) и целевые значения (Y_train). В процессе обучения необходимо указать количество эпох, размер пакета, разделение проверки и детализацию. Данные не перетасовываются. Объект, в котором хранятся значения хода обучения и потерь, называется объектом журнала.

Значения потерь при обучении хранятся в столбце «Split5» кадра данных train_loss. Аналогичным образом, значения потерь при проверке хранятся в val_loss кадра данных.

Оценка модели: тестовый набор данных используется для оценки производительности модели. Для вычисления таких показателей, как среднеквадратичная ошибка (MSE), среднеквадратичная ошибка (RMSE) и оценка R-квадрата (R2), вызывается функция evaluate_model. Мы получаем как истинные, так и прогнозируемые значения.

Чтобы оценить производительность модели в тестовом наборе данных, код печатает метрики оценки, включая MSE, RMSE и R2.

Построение результатов: прогнозы модели на тестовом наборе данных визуализируются функцией plot_data.

Список метрик оценки (MSE, RMSE, R2) и время обучения добавляются к списку cross_val_results. Таким образом, результаты оценки могут быть сохранены для последующего анализа.

Обученная модель сохраняется в файле с именем «MV3-LSTM-Split5.h5». Прогнозы или дальнейший анализ могут быть сделаны с использованием этой сохраненной модели.

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

CV_results = pd.DataFrame(cross_val_results,columns=['MSE','RMSE','R2_Score','Train_Time'])
print("Avg. MSE = {}".format(CV_results['MSE'].mean()))
print("Avg. RMSE = {}".format(CV_results['RMSE'].mean()))
print("Avg. R2-score = {}".format(CV_results['R2_Score'].mean()))

С помощью этого кода выполняются следующие операции:

Используя список cross_val_results, мы создаем DataFrame с именем CV_results. В DataFrame есть столбцы с метками «MSE», «RMSE», «R2_Score» и «Train_Time». Существует три метрики оценки для каждого разделения перекрестной проверки (MSE, RMSE и R2), а также время, затраченное на обучение каждого разделения (список cross_val_results).

Используя столбец «MSE» CV_results DataFrame, код вычисляет среднее значение средней квадратичной ошибки (MSE). При различных разделениях перекрестной проверки среднее значение MSE измеряет производительность модели.

Вычислите среднее значение столбца «RMSE» CV_results DataFrame, чтобы вывести среднюю среднеквадратичную ошибку (RMSE). RMSE измеряет среднюю величину ошибок прогнозирования модели при перекрестной проверке.

Используя CV_results DataFrame, код извлекает столбец «R2_Score» и вычисляет средний балл R-квадрата (R2-score). Средний показатель R2 измеряет долю дисперсии в целевой переменной, которую может объяснить модель. Хорошее соответствие модели данным определяется тем, насколько хорошо она соответствует данным.

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

Прогнозирование цен на фондовые индексы с помощью LSTM

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

1. Введение2. Предварительная обработка и подготовка данных3. Построение и обучение LSTM-модели3.1 Подготовка данных для LSTM-модели3.2 Архитектура модели LSTM3.3 Компиляция и обучение модели LSTM3.4 Визуализация потерь по эпохам4. Применение обученной модели для прогнозирования4.1 Загрузка и подготовка тестовых данных4.2 Подготовка тестовых данных для LSTM4.3 Прогнозирование и обратное масштабирование4.4 Визуализация результатов5. Заключение

1. Введение

Доброго времени суток, меня зовут Шон Ю.

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

В этой статье мы рассмотрим, как использовать нейронные сети Long Short-Term Memory (LSTM), разновидность рекуррентной нейронной сети (RNN), для анализа исторических данных индексов SPY (S&P 500 ETF) и прогнозирования будущих цен на акции. Мы рассмотрим каждый шаг, включая подготовку данных, построение модели LSTM, обучение модели и составление прогнозов. К концу этой статьи у вас будет работающая LSTM-модель, способная предсказывать цены на акции SPY.

Необходимые условия
Прежде чем мы начнем, убедитесь, что на вашем компьютере установлено следующее, и импортируйте соответствующие библиотеки:!pip install tensorflow keras numpy matplotlib pandas yfinance

# Importing required libraries
import yfinance as yf
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from tensorflow import keras
import matplotlib.dates as mdates

2. Предварительная обработка и подготовка данных

В этом разделе мы начнем с получения исторических данных о ценах на акции SPY (SPDR S&P 500 ETF Trust) от Yahoo Finance. Мы разделим данные на обучающий и проверочный наборы, а затем будем использовать цены Adj Close в качестве цены акции для обучения нашей LSTM-модели.# Download historical data for SPY from Yahoo Finance
spy = yf.download(‘SPY’, start=’2010-01-01′, end=’2023-01-01′)

# Split data into training (2010-2020) and validation (2020-2023) sets
training_data = spy[‘Adj Close’][‘2010-01-01′:’2020-12-31’]
validation_data = spy[‘Adj Close’][‘2021-01-01′:’2023-01-01’]

# Use ‘Adj Close’ prices as the stock price for training
training_set = training_data.values.reshape(-1, 1)
validation_set = validation_data.values.reshape(-1, 1)

Распечатайте SPY в записной книжке Jupyter, чтобы проверить, успешно ли загружены данные.

Мы также применим масштабирование признаков с помощью MinMaxScaler, чтобы гарантировать, что все данные попадают в определенный диапазон, что важно для эффективной сходимости LSTM-моделей.# Feature scaling using MinMaxScaler
sc = MinMaxScaler(feature_range=(0, 1))
training_set_scaled = sc.fit_transform(training_set)
validation_set_scaled = sc.transform(validation_set)

3. Построение и обучение LSTM-модели

Здесь мы углубимся в суть нашей статьи — построение и обучение LSTM-модели. Мы создадим входные последовательности из обучающих данных для передачи в LSTM. Архитектура модели будет состоять из нескольких слоев LSTM с регуляризацией выпадения для предотвращения переобучения.

3.1 Подготовка данных для LSTM-модели
Перед построением LSTM-модели необходимо подготовить данные в подходящем формате. Мы создадим входные последовательности из обучающих данных для передачи в LSTM.

Входные последовательности будут состоять из исторических цен на акции, а соответствующие целевые значения будут ценами акций на следующем временном шаге. Таким образом, мы позволяем LSTM изучать временные закономерности в данных и делать соответствующие прогнозы.# Creating input sequences for training
def create_sequences(data, seq_length=60):
X = []
y = []
for i in range(seq_length, len(data)):
X.append(data[i-seq_length:i, 0])
y.append(data[i, 0])
return np.array(X), np.array(y)

X_train, y_train = create_sequences(training_set_scaled)
X_validation, y_validation = create_sequences(validation_set_scaled)

# Reshape inputs for LSTM model
X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1))
X_validation = np.reshape(X_validation, (X_validation.shape[0], X_validation.shape[1], 1))

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

Наложение нескольких слоев LSTM позволяет модели изучать сложные взаимосвязи в данных.# Building the LSTM Model
model = keras.Sequential()
model.add(keras.layers.LSTM(units=50, return_sequences=True, input_shape=(X_train.shape[1], 1)))
model.add(keras.layers.Dropout(0.2))
model.add(keras.layers.LSTM(units=50, return_sequences=True))
model.add(keras.layers.Dropout(0.2))
model.add(keras.layers.LSTM(units=50, return_sequences=True))
model.add(keras.layers.Dropout(0.2))
model.add(keras.layers.LSTM(units=50))
model.add(keras.layers.Dropout(0.2))
model.add(keras.layers.Dense(units=1))

3.3 Компиляция и обучение LSTM-модели
После того, как архитектура определена, нам нужно скомпилировать модель, указав оптимизатор и функцию потерь. Для задач прогнозирования временных рядов, таких как прогнозирование цен на акции, мы будем использовать среднеквадратичную ошибку (MSE), которая измеряет разницу между прогнозируемыми и фактическими ценами на акции. Мы также будем использовать оптимизатор adam, который является эффективным алгоритмом оптимизации, широко используемым в глубоком обучении.

Процесс обучения тщательно контролируется, а история тренировок сохраняется для дальнейшего анализа, например, построения графика потерь во время обучения.# Compiling the LSTM Model
model.compile(optimizer=’adam’, loss=’mean_squared_error’)

# Training the Model and store history
history = model.fit(X_train, y_train, epochs=100, batch_size=32, validation_data=(X_validation, y_validation))

3.4 Визуализация потерь по эпохам
Теперь, когда доступны данные о потерях для обучения и проверки, мы можем построить график потерь по эпохам. Это даст нам представление о том, насколько хорошо модель обучается на обучающих данных и есть ли какие-либо признаки переобучения или недостаточного обучения.# Plot loss and accuracy during training
plt.figure(figsize=(10, 5))
plt.plot(history.history[‘loss’], label=’Training Loss’)
plt.plot(history.history[‘val_loss’], label=’Validation Loss’)
plt.title(‘Model Loss’)
plt.xlabel(‘Epoch’)
plt.ylabel(‘Loss’)
plt.legend()
plt.show()

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

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

4. Применение обученной модели для прогнозирования

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

4.1 Загрузка и подготовка тестовых данных
Во-первых, нам нужно загрузить тестовые данные для акций SPY из Yahoo Finance. Затем мы извлечем цены Adj Close и предварительно обработаем данные, чтобы они подходили для модели LSTM.# Download test data for SPY from Yahoo Finance
spy_test = yf.download(‘SPY’, start=’2023-01-01′, end=’2023-07-31′)

# Use ‘Adj Close’ prices as the test data
real_stock_price = spy_test[‘Adj Close’].values.reshape(-1, 1)

4.2 Подготовка тестовых данных для LSTM
Чтобы использовать тестовые данные для прогнозов, нам нужно предварительно обработать их аналогично обучающим данным. Мы объединим обучающие и тестовые данные, масштабируем входные данные и создадим входные последовательности для LSTM-модели.# Preparing Test Data
dataset_total = pd.concat((spy[‘Adj Close’], spy_test[‘Adj Close’]), axis=0)
inputs = dataset_total[len(dataset_total) — len(spy_test) — 60:].values
inputs = inputs.reshape(-1, 1)
inputs = sc.transform(inputs)
X_test = []
for i in range(60, len(inputs)):
X_test.append(inputs[i-60:i, 0])
X_test = np.array(X_test)
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], 1))

4.3 Прогнозирование и обратное масштабирование
Теперь, когда мы подготовили тестовые данные, мы можем использовать нашу обученную модель LSTM для прогнозирования тестового набора. Мы изменим масштабирование прогнозируемых цен на акции, чтобы получить фактические значения цен на акции.# Making Predictions
predicted_stock_price = model.predict(X_test)
predicted_stock_price = sc.inverse_transform(predicted_stock_price)

4.4 Визуализация результатов
Наконец, мы визуализируем фактические цены акций на основе тестовых данных и прогнозируемые цены акций, сгенерированные нашей LSTM-моделью.# Prepare dates for the predicted stock prices
date_range = pd.date_range(start=’2023-01-01′, periods=len(predicted_stock_price), freq=’B’) # ‘B’ for business day frequency

# Visualizing Results with Month and Year on X-axis
plt.figure(figsize=(10, 6))
plt.plot(spy_test.index, real_stock_price, color=’black’, label=’SPY Stock Price’)
plt.plot(date_range, predicted_stock_price, color=’green’, label=’Predicted SPY Stock Price’)
plt.gca().xaxis.set_major_locator(mdates.MonthLocator(interval=3)) # Show tick marks for every 3 months
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter(‘%b %Y’)) # Format x-axis labels as ‘Jan 2023’, ‘Feb 2023’, etc.
plt.title(‘SPY Stock Price Prediction’)
plt.xlabel(‘Time’)
plt.ylabel(‘SPY Stock Price’)
plt.legend()
plt.show()

5. Заключение:

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

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

Прогнозирование тренда цен на акции с использованием нейронной сети LSTM с Streamlit и Render

Прогнозируйте динамику цен на акции с помощью Deep Learning и живой демонстрации с помощью веб-приложения Streamlit и Render Hosting

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

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

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

В этом блоге мы собираемся предсказать ценовой тренд акции/акции, используя технику глубокого обучения (LSTM), а для живой демонстрации мы создадим веб-приложение Streamlit и развернем весь проект в Render Hosting.

Таким образом, наша работа будет разделена на несколько этапов, и мы будем охватывать все этапы один за другим. Этапы:

  1. Сбор биржевых данных
  2. Исследование данных с помощью скользящих средних
  3. Разделение и масштабирование набора данных
  4. Построение модели
  5. Тестирование модели
  6. Веб-приложение Streamlit
  7. Развертывание рендеринга

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

Начнем наше путешествие с первого этапа.

Сбор биржевых данных

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

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

В каждой стране есть свои фондовые биржи, где акции котируются для торговли. Как и в Индии, есть две фондовые биржи (NSE и BSE). Итак, прежде всего, мы должны выбрать любую акцию с данными за предыдущий год для анализа и обучения нашей модели.

В этом блоге я выбрал акции под названием «Powergrid» от NSE India. Это совершенно случайный выбор. Вы можете выбрать любой запас из любой страны по вашему желанию.

Шаги для загрузки данных:

Здесь я использовал веб-сайт «Yahoo Finance» для загрузки данных Powergrid за предыдущий год. Чтобы загрузить любые данные о запасах, выполните следующие действия:

  1. Просто зайдите в «Yahoo Finance»
  2. Поиск по названию акции
  3. Скопируйте биржевой тикер (например, для меня это «POWERGRID. NS‘) [Здесь. NS — это фондовая биржа NSE в Индии. Это может быть по-разному для разных стран и запасов]
  4. Теперь запустите приведенный ниже код. Но сначала загрузите некоторые пакеты, такие как «pandas_datareader» (для чтения данных из Yahoo Finance), «yfinance» (для загрузки данных из Yahoo Finance). На самом деле я столкнулся с некоторыми проблемами с «pandas_datareader», поэтому я загрузил набор данных с помощью пакета «yfinance».
# We will be downloading data from Yahoo Finance
# Use stock tickers with .NS or
# to get the stock ticker of a stock search in Yahoo Finance
# pip instal yfinance

import datetime as dt
import yfinance as yf

stock = 'POWERGRID.NS'

# Define a start date and End Date
start = dt.datetime(2011,1,1)
end = dt.datetime(2023,3,1)

# Read Stock Price Data
df = yf.download(stock, start , end)

df.tail(10)

После выполнения приведенного выше кода у нас будет набор данных, например

             Open      High      Low      Close    Adj Close  Volume
Date
2011-01-03 73.837517 74.062515 73.387520 73.725021 48.192955 4264858
2011-01-04 73.837517 74.175018 73.312515 73.800018 48.241982 7409034
2011-01-05 73.800018 74.137520 73.275017 73.575020 48.094891 4918432
2011-01-06 73.650017 74.812515 73.350021 74.250015 48.536137 12687550
2011-01-07 74.250015 74.925018 72.862518 73.387520 47.972324 11831861
2011-01-10 73.350021 73.950020 71.962517 72.112518 47.138885 8810324
2011-01-11 72.600021 73.500015 71.400017 72.037521 47.089855 6302135
2011-01-12 72.525017 73.087517 71.625015 72.937515 47.678177 4017165
2011-01-13 72.900017 73.087517 72.337517 72.562515 47.433041 4380002
2011-01-14 72.375015 74.362518 72.187515 73.200020 47.849777 10214384

В наборе данных столбец даты объединен как индекс, поэтому мы должны оставить индекс по приведенному ниже коду

df= df.reset_index()
df.head()
     Date      Open      High       Low       Close  Adj Close Volume
0 2011-01-03 73.837517 74.062515 73.387520 73.725021 48.192955 4264858
1 2011-01-04 73.837517 74.175018 73.312515 73.800018 48.241982 7409034
2 2011-01-05 73.800018 74.137520 73.275017 73.575020 48.094891 4918432
3 2011-01-06 73.650017 74.812515 73.350021 74.250015 48.536137 12687550
4 2011-01-07 74.250015 74.925018 72.862518 73.387520 47.972324 11831861

Давайте визуализируем данные временных рядов с помощью свечей Plotly. Сначала установите plotly с помощью ‘pip install plotly’. Затем используйте приведенный ниже код

# Candlesticks

import plotly.graph_objects as go
fig = go.Figure(data=[go.Candlestick(x=df['Date'], open=df['Open'],
                high=df['High'],
                low=df['Low'],
                close=df['Close'])])

fig.update_layout(xaxis_rangeslider_visible=False)
fig.show()

Исследование данных с помощью скользящих средних

В этом блоге мы будем работать со столбцом «Закрытие» или «Цена закрытия». Вы можете работать с любым из них. Некоторые столбцы не нужны для этого проекта, поэтому мы отбросим их и построим график цены закрытия.

df =df.drop(['Date','Adj Close'], axis=1)
plt.plot(df.Close)

Как создать индикатор скользящей средней?

Мы создадим скользящие средние с данными определенного количества дней.

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

Экспоненциальная скользящая средняя (EMA) — это тип скользящей средней (MA), которая придает больший вес и значимость самым последним точкам данных. Экспоненциальная скользящая средняя также называется экспоненциально взвешенной скользящей средней.

Согласно теории магнитов в биржевой торговле, стоимость некоторых EMA замечательна. Таким образом, мы создадим EMA на 20 дней, 50 дней, 100 дней и 200 дней.

Для создания скользящих средних мы будем использовать скользящий пакет Pandas, а для EMA мы будем использовать пакет Pandas ewm

# Moving Averages
ma100 = df.Close.rolling(100).mean()
ma200 = df.Close.rolling(200).mean()

# EMA
ema20 = df.Close.ewm(span=20, adjust=False).mean()
ema50 = df.Close.ewm(span=50, adjust=False).mean()
ema100 = df.Close.ewm(span=100, adjust=False).mean()
ema200 = df.Close.ewm(span=200, adjust=False).mean()
plt.figure(figsize = (12,6))
plt.plot(df.Close)
plt.plot(ema100, 'g')
plt.plot(ema200, 'r')

Разделение и масштабирование набора данных

Мы разделим набор данных на обучающую и тестовую части.

Но почему мы это делаем?

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

Если я приведу пример, предположим, что вы студент, и вам представлен новый курс / навык. Вы должны узнать о курсе и показать свои знания.

Как вы справитесь с ситуацией?

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

Здесь мы разделим набор данных на обучающую и тестовую части. Как учебная часть похожа на подготовку студента к новому курсу, так и тестовая часть является экзаменом. Как учебная, так и тестовая части будут снова разделены на две части для каждой. Это «x_train», «y_train» и «x_test», «y_test».

Здесь «x_train» похож на учебный материал для студентов, а «x_test» — это примеры вопросов для практики. «x_test» — это экзаменационный вопросник, для которого ученик будет предсказывать ответы, а «y_test» — это ответ, по которому учитель будет сравнивать ответ ученика.

Здесь все такие названия, как обучение, тестирование, «x_train», «y_train» и «x_test», «y_test». Все это вымышленные имена, просто для лучшего понимания. Вы можете выбрать любое имя.

Теперь df.shape показывает, что набор данных содержит 3000 строк и 5 столбцов .давайте посмотрим код для процесса разделения данных

data_training = pd.DataFrame(df['Close'][0:int(len(df)*0.70)])
data_testing = pd.DataFrame(df['Close'][int(len(df)*0.70): int(len(df))])

print(data_training.shape)
print(data_testing.shape)

Таким образом, мы разделили 70% данных для обучения и 30% для тестирования. Теперь мы масштабируем набор данных с помощью пакета MinMaxScaler, который вернет масштабируемый массив данных.

from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range = (0,1))
data_training_array = scaler.fit_transform(data_training)

Теперь мы разделим обучающий набор данных на обучающую и тестовую части для обучения модели.

x_train = []
y_train = []

for i in range (100,data_training_array.shape[0]):
x_train.append(data_training_array[i-100:i])
y_train.append(data_training_array[i,0])

x_train, y_train = np.array(x_train),np.array(y_train)

Таким образом, в обучающей части модель узнает о содержании и проверит свое обучение с помощью обучающей части. И мы также меняем часть обучения и тестирования на массив numpy, так как это будет нашим входом для модели. Посмотрите индексацию в массиве, так как нам нужен 3D-массив для ввода в модель LSTM.

Сводка

Входными данными LSTM всегда является 3D-массив. (batch_size, time_steps, seq_len). Результатом LSTM может быть 2D-массив или 3D-массив в зависимости от аргумента return_sequences. Если return_sequence имеет значение False, на выходе получается 2D-массив. (batch_size, ед.). Если return_sequence имеет значение True, на выходе получается 3D-массив. batch_size, time_steps, единицы.

Построение модели

Что такое глубокое обучение с помощью нейронной сети?

Нейронные сети, также известные как искусственные нейронные сети (ИНС) или смоделированные нейронные сети (SNN), являются подмножеством машинного обучения и лежат в основе алгоритмов глубокого обучения. Их название и структура вдохновлены человеческим мозгом, имитирующим то, как биологические нейроны сигнализируют друг другу

Что такое LSTM и почему мы его используем?

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

Давайте создадим модель, но сначала установим некоторые пакеты, такие как Scikit-LearnTensorflow и Keras.

# Model building
from keras.layers import Dense, Dropout, LSTM
from keras.models import Sequential
model = Sequential()

model.add(LSTM(units = 50, activation= 'relu', return_sequences= True, input_shape= (x_train.shape[1],1)))
model.add(Dropout(0.2))

model.add(LSTM(units = 60, activation= 'relu', return_sequences= True ))
model.add(Dropout(0.3))

model.add(LSTM(units = 80, activation= 'relu', return_sequences= True ))
model.add(Dropout(0.4))

model.add(LSTM(units = 120, activation= 'relu'))
model.add(Dropout(0.5))

model.add(Dense(units = 1))
model.summary()
# Model Summary Output
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm (LSTM) (None, 100, 50) 10400

dropout (Dropout) (None, 100, 50) 0

lstm_
1 (LSTM) (None, 100, 60) 26640

dropout_1 (Dropout) (None, 100, 60) 0

lstm_2 (LSTM) (None, 100, 80) 45120

dropout_2 (Dropout) (None, 100, 80) 0

lstm_3 (LSTM) (None, 120) 96480

dropout_3 (Dropout) (None, 120) 0

dense (Dense) (None, 1) 121

=================================================================
Total params: 178,761
Trainable params: 178,761
Non-trainable params: 0
_________________________________________________________________

Скомпилируем модель с оптимизатором Адама и среднеквадратичной ошибкой в качестве функции потерь. И мы подогоним модель под 50 эпох.

model.compile(optimizer='adam', loss= 'mean_squared_error')
model.fit(x_train, y_train, epochs = 50)

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

Теперь мы протестируем модель для тестового набора данных.

Тестирование модели

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

past_100_days = data_training.tail(100)
final_df = past_100_days.append(data_testing, ignore_index=True)
input_data = scaler.fit_transform(final_df)

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

# like questions in exam
x_test=[]
# like model answer for teacher and he will compare it with students predicted values
y_test=[]

for i in range (100,input_data.shape[0]):
x_test.append(input_data[i-100:i])
y_test.append(input_data[i,0])

x_test,y_test = np.array(x_test), np.array(y_test)

Теперь мы предскажем результат по нашим тестовым данным с помощью нашей модели LSTM.

y_predicted = model.predict(x_test)

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

scaler.scale_

# output:
# array([0.00729727])
scale_factor = 1/0.00729727
y_predicted = y_predicted * scale_factor
y_test = y_test * scale_factor

Наконец, давайте построим график исходного ценового тренда и прогнозируемого ценового тренда

plt.figure(figsize=(12,6))
plt.plot(y_test, 'g', label = 'Original Price')
plt.plot(y_predicted, 'r', label = 'Predicted Price')
plt.xlabel('Time')
plt.ylabel('Price')
plt.legend()
plt.show()

Теперь давайте создадим веб-приложение с помощью Streamlit, чтобы продемонстрировать работу нашей модели.

Веб-приложение Streamlit

Прежде всего, сохраните модель и файл записной книжки python .ipynb (не обязательно).

model.save('stock_dl_model.h5')

Создайте папку и поместите файл модели. Теперь из папки открываем любую IDE. Как и здесь, я использовал VS Code. Поэтому я открыл файл с помощью VS Code.

Типа того…

Создайте новый файл и назовите его app.py так как в этом файле будут присутствовать все кодлинги. И создайте еще один файл и назовите его требованиями.txt так как все пакеты, необходимые для этого проекта, будут здесь, например:

Используйте приведенный ниже код для app.py (если какой-либо пакет не найден, установите его с помощью pip install )

# importing all packages
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import pandas_datareader as data
from keras.models import load_model
import streamlit as st

import datetime as dt
import yfinance as yf


# Define a start date and End Date
start = dt.datetime(2011,1,1)
end = dt.datetime(2023,3,1)

# streamlit web app title
st.title('Stock Trend Prediction')

# user can give any input by stock ticker but here default is POWERGRID.NS
stock = st.text_input('Enter Stock Ticker','POWERGRID.NS')

df = yf.download(stock, start , end)

# Describing Data
st.subheader('Data from Jan 2011 to Mar 2023')
st.write(df.describe())

#Visualizations with EMA 20, 50, 100, 200 Days

ema20 = df.Close.ewm(span=20, adjust=False).mean()
ema50 = df.Close.ewm(span=50, adjust=False).mean()

st.subheader('Closing Price vs Time Chart with 20 & 50 Days of Exponential Moving Avarage')
fig = plt.figure(figsize = (12,6))
plt.plot(df.Close,'y')
plt.plot(ema20, 'g', label= 'EMA of 20 Days')
plt.plot(ema50, 'r', label= 'EMA of 50 Days')
plt.xlabel('Time')
plt.ylabel('Price')
plt.legend()
st.pyplot(fig)


ema100 = df.Close.ewm(span=100, adjust=False).mean()
ema200 = df.Close.ewm(span=200, adjust=False).mean()

st.subheader('Closing Price vs Time Chart with 100 & 200 EMA')
fig = plt.figure(figsize = (12,6))
plt.plot(df.Close)
plt.plot(ema100, 'g', label= 'EMA of 100 Days')
plt.plot(ema200, 'r', label= 'EMA of 200 Days')
plt.xlabel('Time')
plt.ylabel('Price')
plt.legend()
st.pyplot(fig)


# Splitting Data into Training and Testing
data_training = pd.DataFrame(df['Close'][0:int(len(df)*0.70)])
data_testing = pd.DataFrame(df['Close'][int(len(df)*0.70): int(len(df))])

print(data_training.shape)
print(data_testing.shape)

# scaling the data
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler(feature_range = (0,1))

data_training_array = scaler.fit_transform(data_training)


# loading the model

model = load_model('stock_dl_model.h5')

# Model Testing with previous 100 days data

past_100_days = data_training.tail(100)
final_df = past_100_days.append(data_testing, ignore_index=True)
input_data = scaler.fit_transform(final_df)


x_test=[]
y_test=[]

for i in range (100,input_data.shape[0]):
x_test.append(input_data[i-100:i])
y_test.append(input_data[i,0])

x_test,y_test = np.array(x_test), np.array(y_test)

y_predicted = model.predict(x_test)

scaler = scaler.scale_
scale_factor = 1/scaler[0]
y_predicted = y_predicted * scale_factor
y_test = y_test * scale_factor

# Final PLot of Original vs Predicted price trend

st.subheader('Prediction Vs Original Trend')
fig2=plt.figure(figsize=(12,6))
plt.plot(y_test, 'g', label = 'Original Price')
plt.plot(y_predicted, 'r', label = 'Predicted Price')
plt.xlabel('Time')
plt.ylabel('Price')
plt.legend()
st.pyplot(fig2)

После написания приведенного выше кода мы можем запустить код, открыв терминал и введя streamlit run app.py

Он запустит сервер на вашем собственном ПК со ссылкой и откроется как веб-приложение в браузере по умолчанию. Но это не навсегда, когда вы выключаете компьютер, локальный URL-адрес не будет работать. Поэтому, чтобы решить эту проблему, мы должны развернуть этот проект на платформе веб-хостинга. Это Heroku, AWS, Render, Cyclic, Railways и т. Д. Здесь мы будем использовать Render Web Hosting, так как он бесплатный.

Развертывание рендеринга

Heroku была отличной платформой для бесплатного развертывания веб-приложений. Но с октября 2022 года Heroku больше не бесплатен. Студентам и мелким разработчикам иногда становится трудно оплачивать расходы. Поэтому мы должны выбрать другие платформы, которые предоставляют бесплатные услуги хостинга. Есть несколько известных платформ, таких как Render, Cyclic и Railways. Здесь мы собираемся использовать Render для хостинга нашего проекта.

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

Шаги по отправке всех файлов проекта на GitHub:

Посетите мой блог, чтобы узнать о простых шагах: «Как легко добавлять проекты на GitHub»

После отправки всех файлов проекта в репозиторий GitHub откройте учетную запись рендеринга и выберите Создать веб-службу >

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

Учебное пособие по хостингу рендеринга

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

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

Для другого раздела все будет так, как показано на рисунке ниже:

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

Развертывание завершено

Прогнозирование цен на акции с помощью LSTM и GRU

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

LSTM — это всего лишь несколько копий самого себя

Получение данных

Для начала нам нужны исторические данные о ценах на акции. Мы можем получить эти данные из таких источников, как Yahoo Finance или Alpha Vantage. В этом руководстве мы будем использовать набор данных, охватывающий цены на акции IBM с 2006 по 2018 год.import pandas as pd
df = pd.read_csv(«IBM_2006–01–01_to_2018–01–01.csv», index_col=’Date’, parse_dates=[«Date»])

Визуализация данных

Давайте визуализируем наш набор данных, чтобы понять тенденции цен на акции. Мы построим график обучающей выборки (до 2017 года) и тестового набора (2017 год и далее) отдельно.import matplotlib.pyplot as plt
# Plot the training set
df[«High»][:’2016′].plot(figsize=(16, 4), legend=True)
# Plot the test set
df[«High»][‘2017’:].plot(figsize=(16, 4), legend=True)
plt.legend([‘Training set (Before 2017)’, ‘Test set (2017 and beyond)’])
plt.title(‘IBM stock price’)
plt.show()

Изображение, чтобы прояснить, что мы собираемся сделать

Предварительная обработка данных

Прежде чем вводить данные в нашу LSTM-модель, нам нужно предварительно обработать их. Мы будем использовать масштабирование Min-Max, чтобы масштабировать цены акций в диапазоне от 0 до 1.from sklearn.preprocessing import MinMaxScaler
sc = MinMaxScaler(feature_range=(0, 1))
training_set_scaled = sc.fit_transform(trainning_set)

Создание обучающих данных

Мы создадим наши обучающие данные, создав последовательности цен на акции и соответствующие им метки. Каждая последовательность будет содержать цены акций за предыдущие 60 дней.# here we are seperating the data
trainning_set = df[:’2016′].iloc[:,1:2].values
test_set = df[‘2017’:].iloc[:,1:2].values

X_train = []
y_train = []
for i in range(60, len(training_set_scaled)):
X_train.append(training_set_scaled[i — 60:i, 0])
y_train.append(training_set_scaled[i, 0])
X_train, y_train = np.array(X_train), np.array(y_train)
X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1))

Построение модели LSTM

Теперь пришло время построить нашу LSTM-модель. Мы создадим последовательную модель с несколькими слоями LSTM и отсева для регуляризации.from keras.models import Sequential
from keras.layers import LSTM, Dropout, Dense

regressor = Sequential()
# First LSTM layer with Dropout regularisation
regressor.add(LSTM(units=100, return_sequences=True, input_shape=(X_train.shape[1],1)))
regressor.add(Dropout(0.3))

regressor.add(LSTM(units=80, return_sequences=True))
regressor.add(Dropout(0.1))

regressor.add(LSTM(units=50, return_sequences=True))
regressor.add(Dropout(0.2))

regressor.add(LSTM(units=30))
regressor.add(Dropout(0.3))

regressor.add(Dense(units=1))

regressor.compile(optimizer=’adam’,loss=’mean_squared_error’)

Обучение модели

Теперь, когда у нас есть архитектура модели, давайте обучим ее на подготовленных обучающих данных.regressor.fit(X_train, y_train, epochs=50, batch_size=32)

Составление прогнозов

Теперь мы можем делать прогнозы на основе наших тестовых данных.# pre-processing the data
dataset_total = pd.concat((df[«High»][:’2016′],df[«High»][‘2017’:]),axis=0)
inputs = dataset_total[len(dataset_total)-len(test_set) — 60:].values
inputs = inputs.reshape(-1,1)
inputs = sc.transform(inputs)

# making the test data
X_test = []
for i in range(60,len(inputs)):
X_test.append(inputs[i-60:i,0])
X_test = np.array(X_test)
X_test = np.reshape(X_test, (X_test.shape[0],X_test.shape[1],1))
predicted_stock_price = regressor.predict(X_test)
predicted_stock_price = sc.inverse_transform(predicted_stock_price)

Визуализация прогнозов

Давайте визуализируем прогнозы нашей модели в сравнении с фактическими тестовыми данными.def plot_prediction(test,prediction):
plt.plot(test,color=’red’,label=»Real IBM Stock Price»)
plt.plot(prediction, color=»blue»,label=»predicted IBM Stock price»)
plt.title(«IBM Stock Price Prediction»)
plt.xlabel(«Time»)
plt.ylabel(«IBM Stock Price»)
plt.legend()
plt.show()
# now we’ll use this function to visualize our test and predicted data

plot_prediction(test_set,predicted_stock_price)

Здесь мы видим, насколько точна наша модель

Наш сюжет со Временем.

Оценка производительности модели

Наконец, давайте оценим производительность нашей модели с помощью среднеквадратичной ошибки (RMSE).def return_rmse(test,predicted):
rmse = math.sqrt(mean_squared_error(test,predicted))
print(«The root mean sqaured error is {}.».format(rmse))

Основная средняя квадратная ошибка равна 1,3337922543779552.

Теперь попробуем с ГРУ

regressorGRU = Sequential()

regressorGRU.add(GRU(units=100, return_sequences=True, input_shape=(X_train.shape[1],1), activation=’tanh’))
regressorGRU.add(Dropout(0.3))
# Second GRU layer
regressorGRU.add(GRU(units=80, return_sequences=True, input_shape=(X_train.shape[1],1), activation=’tanh’))
regressorGRU.add(Dropout(0.2))
# Third GRU layer
regressorGRU.add(GRU(units=50, return_sequences=True, input_shape=(X_train.shape[1],1), activation=’tanh’))
regressorGRU.add(Dropout(0.1))
# Fourth GRU layer
regressorGRU.add(GRU(units=30, activation=’tanh’))
regressorGRU.add(Dropout(0.2))
# The output layer
regressorGRU.add(Dense(units=1))

# Compiling the RNN
regressorGRU.compile(optimizer=’adam’,loss=’mean_squared_error’)

# Fitting to the training set
regressorGRU.fit(X_train,y_train,epochs=50,batch_size=150)

Прогнозирование значений

X_test = []
for i in range(60,len(inputs)):
X_test.append(inputs[i-60:i,0])
X_test = np.array(X_test)
X_test = np.reshape(X_test, (X_test.shape[0],X_test.shape[1],1))
predicted_stock_price = regressorGRU.predict(X_test)
predicted_stock_price = sc.inverse_transform(predicted_stock_price)

Визуализируйте прогнозируемые и реальные данные

plot_prediction(test_set,predicted_stock_price)

ГРУ имеет сравнительно меньшую вычислительную базу и совсем недавнюю модель (2014 г.), и набирает большую популярность

Прогнозирования движения цен на акции

Ссылка на полную записную книжку: https://github.com/borisbanushev/stockpredictionai

В этом блокноте я создам полный процесс прогнозирования движения цен на акции. Следуйте за нами, и мы добьемся довольно хороших результатов. Для этого мы будем использовать генеративно-состязательную сеть (GAN) с LSTM, типом рекуррентной нейронной сети, в качестве генератора и сверточную нейронную сеть, CNN, в качестве дискриминатора. Мы используем LSTM по той очевидной причине, что пытаемся предсказать данные временных рядов. Почему мы используем GAN и, в частности, CNN в качестве дискриминатора? Это хороший вопрос: об этом будут специальные разделы позже.

Конечно, мы углубимся в детали каждого шага, но самая сложная часть — это GAN: очень сложная часть успешного обучения GAN — это получение правильного набора гиперпараметров. По этой причине мы будем использовать байесовскую оптимизацию (наряду с гауссовскими процессами) и глубокое обучение с подкреплением (DRL) для принятия решения о том, когда и как изменять гиперпараметры GAN (дилемма разведки и эксплуатации). При создании обучения с подкреплением я буду использовать самые последние достижения в этой области, такие как Rainbow и PPO.

Мы будем использовать множество различных типов входных данных. Наряду с историческими торговыми данными и техническими индикаторами акций, мы будем использовать новейшие достижения в области НЛП (с использованием «двунаправленных встраиваемых представлений от трансформеров», BERT, своего рода трансферного обучения для НЛП) для создания анализа настроений (в качестве источника для фундаментального анализа), преобразования Фурье для извлечения общих направлений тренда, многоуровневые автоэнкодеры для выявления других высокоуровневых функций, Собственные портфели для поиска коррелированных активов, авторегрессионная интегрированная скользящая средняя (ARIMA) для аппроксимации функции акций и многое другое, чтобы собрать как можно больше информации, закономерностей, зависимостей и т. д. об акциях. Как мы все знаем, чем больше (данных), тем лучше. Прогнозирование движения цен на акции — чрезвычайно сложная задача, поэтому чем больше мы знаем об акциях (с разных точек зрения), тем выше наши изменения.

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

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

1. Введение

Точное прогнозирование фондовых рынков является сложной задачей, поскольку существуют миллионы событий и предпосылок для того, чтобы конкретная акция двигалась в определенном направлении. Поэтому мы должны быть в состоянии зафиксировать как можно больше этих предварительных условий. Нам также нужно сделать несколько важных предположений: 1) рынки не на 100% случайны, 2) история повторяется, 3) рынки следуют рациональному поведению людей и 4) рынки «идеальны». И, пожалуйста, прочтите Отказ от ответственности внизу.

Попробуем спрогнозировать движение цены Goldman Sachs (NYSE: GS). Для этой цели мы будем использовать ежедневную цену закрытия с 1 января 2010 года по 31 декабря 2018 года (семь лет для целей обучения и два года для целей проверки). Мы будем использовать термины «Goldman Sachs» и «GS» взаимозаменяемо.

2. Данные

Нам нужно понять, что влияет на то, будет ли цена акций GS двигаться вверх или вниз. Это то, что думают люди в целом. Следовательно, нам нужно включить как можно больше информации (изображающей акции с разных сторон и углов). (Мы будем использовать ежедневные данные — 1 585 дней для обучения различных алгоритмов (70% имеющихся у нас данных) и прогнозировать следующие 680 дней (тестовые данные). Затем мы сравним прогнозируемые результаты с тестовыми (контрольными) данными. Каждый тип данных (мы будем называть его функцией) более подробно объясняется в последующих разделах, но, в качестве высокоуровневого обзора, мы будем использовать следующие функции:

  1. Коррелированные активы — это другие активы (любого типа, не обязательно акции, такие как сырьевые товары, валюта, индексы или даже ценные бумаги с фиксированным доходом). Большая компания, такая как Goldman Sachs, очевидно, не «живет» в изолированном мире — она зависит и взаимодействует со многими внешними факторами, включая своих конкурентов, клиентов, мировую экономику, геополитическую ситуацию, фискальную и денежно-кредитную политику, доступ к капиталу и т. д. Подробности перечислены ниже.
  2. Технические индикаторы — многие инвесторы следят за техническими индикаторами. Мы включим самые популярные индикаторы в качестве независимых функций. Среди них — 7 и 21-дневная скользящая средняя, экспоненциальная скользящая средняя, моментум, полосы Боллинджера, MACD.
  3. Фундаментальный анализ — очень важная функция, указывающая, может ли акция двигаться вверх или вниз. Есть две функции, которые можно использовать в фундаментальном анализе: 1) Анализ результатов деятельности компании с помощью отчетов 10-K и 10-Q, анализ ROE и P/E и т. Д. (Мы не будем использовать это), и 2) Новости — потенциально новости могут указывать на предстоящие события, которые потенциально могут двигать акции в определенном направлении. Мы прочитаем все ежедневные новости для Goldman Sachs и извлечем, является ли общее настроение о Goldman Sachs в этот день положительным, нейтральным или отрицательным (в виде оценки от 0 до 1). Поскольку многие инвесторы внимательно читают новости и принимают инвестиционные решения, основанные (частично, конечно) на новостях, существует несколько высокая вероятность того, что, если, скажем, новости для Goldman Sachs сегодня будут чрезвычайно позитивными, акции вырастут завтра. Один важный момент: позже мы выполним важность признака (то есть то, насколько она показательна для движения GS) абсолютно для каждого признака (включая этот) и решим, будем ли мы его использовать. Подробнее об этом позже.
    Для создания точного прогнозирования тональности мы будем использовать нейронную обработку языка (NLP). Мы будем использовать BERT — недавно анонсированный подход Google к НЛП для трансферного обучения для классификации настроений, извлечения тональности биржевых новостей.
  4. Преобразования Фурье — Наряду с дневной ценой закрытия мы создадим преобразования Фурье, чтобы обобщить несколько долгосрочных и краткосрочных трендов. Используя эти преобразования, мы устраним много шума (случайные блуждания) и создадим аппроксимации реального движения акций. Наличие аппроксимации тренда может помочь сети LSTM более точно выбирать тенденции прогнозирования.
  5. Авторегрессионная интегрированная скользящая средняя (ARIMA) — это был один из самых популярных методов прогнозирования будущих значений данных временных рядов (в эпоху до нейронных сетей). Давайте добавим его и посмотрим, станет ли он важной прогностической функцией.
  6. Многоуровневые автоэнкодеры — большинство вышеупомянутых функций (фундаментальный анализ, технический анализ и т. д.) были найдены людьми после десятилетий исследований. Но, возможно, мы что-то упустили. Возможно, есть скрытые корреляции, которые люди не могут понять из-за огромного количества точек данных, событий, активов, диаграмм и т. Д. С помощью многоуровневых автоэнкодеров (тип нейронных сетей) мы можем использовать мощь компьютеров и, вероятно, найти новые типы функций, влияющих на движение акций. Несмотря на то, что мы не сможем понять эти особенности на человеческом языке, мы будем использовать их в GAN.
  7. Глубокое неконтролируемое обучение для обнаружения аномалий в ценообразовании опционов. Мы воспользуемся еще одной функцией — на каждый день будем добавлять цену 90-дневного колл-опциона на акции Goldman Sachs. Ценообразование опционов само по себе объединяет множество данных. Цена опционного контракта зависит от будущей стоимости акции (аналитики также пытаются предсказать цену, чтобы получить наиболее точную цену для колл-опциона). Используя глубокое неконтролируемое обучение (самоорганизованные карты), мы попытаемся обнаружить аномалии в ежедневных ценах. Аномалия (например, резкое изменение ценообразования) может указывать на событие, которое может быть полезно для LSTM для изучения общей модели акций.

Далее, имея так много функций, нам нужно выполнить пару важных шагов:

  1. Выполняйте статистические проверки «качества» данных. Если данные, которые мы создаем, ошибочны, то, какими бы сложными ни были наши алгоритмы, результаты не будут положительными. Проверки включают в себя проверку того, что данные не страдают от гетероскедастичности, мультиколлинеарности или последовательной корреляции.
  2. Создайте важность функции. Если признак (например, другая акция или технический индикатор) не имеет объяснительной силы для акции, которую мы хотим предсказать, то нам нет необходимости использовать ее для обучения нейронных сетей. Мы будем использовать XGBoost (eXtreme Gradient Boosting), тип алгоритмов регрессии усиленного дерева.

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

print('There are {} number of days in the dataset.'.format(dataset_ex_df.shape[0]))output >>> There are 2265 number of days in the dataset.

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

2.1. Коррелированные активы

Как объяснялось ранее, мы будем использовать другие активы в качестве функций, а не только GS.

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

  • Во-первых, это компании, похожие на GS. Мы добавим, среди прочего, JPMorgan Chase и Morgan Stanley в набор данных.
  • Как инвестиционный банк, Goldman Sachs зависит от мировой экономики. Плохая или нестабильная экономика означает отсутствие слияний и поглощений или IPO и, возможно, ограниченную прибыль от собственной торговли. Именно поэтому мы включим индексы мировой экономики. Кроме того, мы включим ставку LIBOR (в долларах США и фунтах стерлингов), так как аналитики могут учитывать шоки в экономике для установления этих ставок и других ценных бумаг финансовых институтов.
  • Дневной индекс волатильности (VIX) — по причине, описанной в предыдущем пункте.
  • Составные индексы — такие как NASDAQ и NYSE (с США), FTSE100 (Великобритания), Nikkei225 (Япония), индексы Hang Seng и BSE Sensex (APAC).
  • Валюты — глобальная торговля часто отражается на том, как движутся валюты, поэтому мы будем использовать корзину валют (таких как USDJPY, GBPUSD и т. д.) в качестве функций.

В целом, у нас есть 72 других актива в наборе данных — ежедневная цена для каждого актива.

2.2. Технические индикаторы

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

""" Function to create the technical indicators """def get_technical_indicators(dataset):
# Create 7 and 21 days Moving Average
dataset['ma7'] = dataset['price'].rolling(window=7).mean()
dataset['ma21'] = dataset['price'].rolling(window=21).mean()

# Create MACD
dataset['26ema'] = pd.ewma(dataset['price'], span=26)
dataset['12ema'] = pd.ewma(dataset['price'], span=12)
dataset['MACD'] = (dataset['12ema']-dataset['26ema'])# Create Bollinger Bands
dataset['20sd'] = pd.stats.moments.rolling_std(dataset['price'],20)
dataset['upper_band'] = dataset['ma21'] + (dataset['20sd']*2)
dataset['lower_band'] = dataset['ma21'] - (dataset['20sd']*2)

# Create Exponential moving average
dataset['ema'] = dataset['price'].ewm(com=0.5).mean()

# Create Momentum
dataset['momentum'] = dataset['price']-1

return dataset

Таким образом, у нас есть технические индикаторы (включая MACD, полосы Боллинджера и т. Д.) Для каждого торгового дня. Всего у нас 12 технических индикаторов.

Давайте визуализируем последние 400 дней для этих индикаторов.

2.3. Фундаментальный анализ

Для фундаментального анализа мы проведем анализ настроений по всем ежедневным новостям о GS. Используя сигмовидную кишку в конце, результат будет от 0 до 1. Чем ближе оценка к 0 — тем негативнее новость (ближе к 1 указывает на позитивные настроения). Для каждого дня мы создадим среднесуточный балл (в виде числа от 0 до 1) и добавим его в качестве функции.

2.3.1. Двунаправленное встраивание представлений из трансформаторов — BERT

Для классификации новостей как положительных или отрицательных (или нейтральных) мы будем использовать BERT, который является предварительно обученным языковым представлением.

Предварительно обученные модели BERT уже доступны в MXNet/Gluon. Нам просто нужно их инстанцировать и добавить два (произвольного числа) Dense слоев, перейдя к софтмаксу — оценка от 0 до 1.

import bert

Углубляться в детали BERT и части НЛП не входит в рамки этой записной книжки, но у вас есть интерес, дайте мне знать — я создам новый репозиторий только для BERT, так как он определенно довольно многообещающий, когда дело доходит до задач обработки языка.

2.4. Преобразования Фурье для анализа трендов

Преобразования Фурье принимают функцию и создают серию синусоидальных волн (с различными амплитудами и рамками). В сочетании эти синусоидальные волны приближаются к исходной функции. С математической точки зрения преобразования выглядят следующим образом:

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

""" Code to create the Fuorier trasfrom  """data_FT = dataset_ex_df[['Date', 'GS']]
close_fft = np.fft.fft(np.asarray(data_FT['GS'].tolist()))
fft_df = pd.DataFrame({'fft':close_fft})
fft_df['absolute'] = fft_df['fft'].apply(lambda x: np.abs(x))
fft_df['angle'] = fft_df['fft'].apply(lambda x: np.angle(x))
plt.figure(figsize=(14, 7), dpi=100)
fft_list = np.asarray(fft_df['fft'].tolist())
for num_ in [3, 6, 9, 100]:
    fft_list_m10= np.copy(fft_list); fft_list_m10[num_:-num_]=0
    plt.plot(np.fft.ifft(fft_list_m10), label='Fourier transform with {} components'.format(num_))
plt.plot(data_FT['GS'],  label='Real')
plt.xlabel('Days')
plt.ylabel('USD')
plt.title('Figure 3: Goldman Sachs (close) stock prices & Fourier transforms')
plt.legend()
plt.show()

Как видно на рисунке 3, чем больше компонентов из преобразования Фурье мы используем, тем ближе функция аппроксимации к реальной цене акции (преобразование 100 компонентов почти идентично исходной функции — красная и фиолетовая линии почти перекрываются). Мы используем преобразования Фурье с целью извлечения долгосрочных и краткосрочных трендов, поэтому мы будем использовать преобразования с 3, 6 и 9 компонентами. Вы можете сделать вывод, что преобразование с 3 компонентами служит долгосрочным трендом.

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

2.5. ARIMA как функция

ARIMA — это метод прогнозирования данных временных рядов. Мы покажем, как его использовать, и хотя ARIMA не будет служить нашим окончательным прогнозом, мы будем использовать его как технику, чтобы немного обесшумить запас и (возможно) извлечь некоторые новые паттерны или особенности.

error = mean_squared_error(test, predictions)
print('Test MSE: %.3f' % error)output >>> Test MSE: 10.151

Как видно из рисунка 5, ARIMA дает очень хорошее приближение к реальной цене акций. Мы будем использовать прогнозируемую цену через ARIMA в качестве входной функции в LSTM, потому что, как мы упоминали ранее, мы хотим охватить как можно больше функций и закономерностей Goldman Sachs. Мы тестируем MSE (средняя квадратичная ошибка) 10,151, что само по себе неплохой результат (учитывая, что у нас есть много тестовых данных), но, тем не менее, мы будем использовать его только как функцию в LSTM.

2.6. Статистические проверки

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

2.6.1. Гетероскедастичность, мультиколлинеарность, серийная корреляция

  • Условная гетероскедастичность возникает, когда условия ошибки (разница между предсказанным регрессией значением и реальным значением) зависят от данных — например, условия ошибки растут, когда точка данных (вдоль оси x) растет.
  • Мультиколлинеарность — это когда условия ошибок (также называемые остатками) зависят друг от друга.
  • Последовательная корреляция — это когда одни данные (признаки) являются формулой (или полностью зависят) от другого признака.

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

2.7. Разработка функций

print('Total dataset has {} samples, and {} features.'.format(dataset_total_df.shape[0],                                                          dataset_total_df.shape[1]))output >>> Total dataset has 2265 samples, and 112 features.

Таким образом, после добавления всех типов данных (коррелированные активы, технические индикаторы, фундаментальный анализ, Фурье и Арима) мы получаем в общей сложности 112 функций за 2 265 дней (однако, как упоминалось ранее, только 1 585 дней предназначены для обучающих данных).

У нас также будет еще несколько функций, сгенерированных автоэнкодерами.

2.7.1. Важность функции XGBoost

Имея так много особенностей, мы должны учитывать, действительно ли все они указывают на направление, в котором будут развиваться акции GS. Например, мы включили в набор данных ставки LIBOR, выраженные в долларах США, потому что считаем, что изменения в LIBOR могут указывать на изменения в экономике, которые, в свою очередь, могут указывать на изменения в поведении акций GS. Но нам нужно протестировать. Есть много способов проверить важность признаков, но тот, который мы применим, использует XGBoost, потому что он дает один из лучших результатов как в задачах классификации, так и в задачах регрессии.

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

regressor = xgb.XGBRegressor(gamma=0.0,n_estimators=150,base_score=0.7,colsample_bytree=1,learning_rate=0.05)xgbModel = regressor.fit(X_train_FI,y_train_FI, eval_set = [(X_train_FI, y_train_FI), (X_test_FI, y_test_FI)], verbose=False)fig = plt.figure(figsize=(8,8))
plt.xticks(rotation='vertical')
plt.bar([i for i in range(len(xgbModel.feature_importances_))], xgbModel.feature_importances_.tolist(), tick_label=X_test_FI.columns)
plt.title('Figure 6: Feature importance of the technical indicators.')
plt.show()

Неудивительно (для тех, кто имеет опыт торговли акциями), что MA7, MACD и BB являются одними из важных функций.

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

2.8. Извлечение высокоуровневых объектов с помощью стековых автоэнкодеров

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

2.8.1. Функция активации — GELU (ошибка Гаусса)

Недавно была предложена GELU — Gaussian Error Linear Unites — ссылка. В статье авторы показывают несколько случаев, когда нейронные сети, использующие GELU, превосходят сети, использующие ReLU в качестве активации. gelu также используется в BERT, подходе НЛП, который мы использовали для анализа настроений на новостях.

Мы будем использовать GELU для автоэнкодеров.

Внимание: В приведенной ниже ячейке показана логика, лежащая в основе математики GELU. Это не фактическая реализация в качестве функции активации. Мне пришлось внедрить GELU внутри MXNet. Если вы будете следовать коду и измените act_type=’act_type='relu', это не сработает, если вы не измените реализацию MXNet.act_type='gelu' Сделайте запрос на вытягивание для всего проекта, чтобы получить доступ к реализации MXNet GELU.

Давайте визуализируем GELU ReLU и LeakyReLU (последний в основном используется в GAN — мы также используем его).

def gelu(x):
return 0.5 * x * (1 + math.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * math.pow(x, 3))))def relu(x):
return max(x, 0)
def lrelu(x):
return max(0.01*x, x)plt.figure(figsize=(15, 5))
plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=.5, hspace=None)ranges_ = (-10, 3, .25)plt.subplot(1, 2, 1)
plt.plot([i for i in np.arange(*ranges_)], [relu(i) for i in np.arange(*ranges_)], label='ReLU', marker='.')
plt.plot([i for i in np.arange(*ranges_)], [gelu(i) for i in np.arange(*ranges_)], label='GELU')
plt.hlines(0, -10, 3, colors='gray', linestyles='--', label='0')
plt.title('Figure 7: GELU as an activation function for autoencoders')
plt.ylabel('f(x) for GELU and ReLU')
plt.xlabel('x')
plt.legend()plt.subplot(1, 2, 2)
plt.plot([i for i in np.arange(*ranges_)], [lrelu(i) for i in np.arange(*ranges_)], label='Leaky ReLU')
plt.hlines(0, -10, 3, colors='gray', linestyles='--', label='0')
plt.ylabel('f(x) for Leaky ReLU')
plt.xlabel('x')
plt.title('Figure 8: LeakyReLU')
plt.legend()plt.show()

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

Хорошо, вернемся к автоэнкодерам, изображенным ниже (изображение только схематическое, оно не отражает реальное количество слоев, единиц и т. Д.)

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

Полный код для автоэнкодеров доступен в сопроводительном Github — ссылка вверху.

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

Заметка Еще раз, это чисто экспериментально. Я не уверен на 100%, что описанная логика выдержит. Как и все остальное в искусственном интеллекте и глубоком обучении, это искусство и требует экспериментов.

3. Генеративно-состязательная сеть (GAN)

Как работают GAN?

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

Сеть GAN состоит из двух моделей — генератора (G) и дискриминатора (D). Этапы обучения GAN:

  1. Генератор, используя случайные данные (шум, обозначаемый z), пытается «генерировать» данные, неотличимые от реальных данных или очень близкие к ним. Его цель — изучить распределение реальных данных.
  2. Случайным образом реальные или сгенерированные данные помещаются в дискриминатор, который действует как классификатор и пытается понять, поступают ли данные из генератора или являются реальными данными. D оценивает вероятности (распределения) входящей выборки по отношению к реальному набору данных. (Подробнее о сравнении двух дистрибутивов см. в разделе 3.2 ниже).
  3. Затем потери от G и D объединяются и распространяются обратно через генератор. Следовательно, потери генератора зависят как от генератора, так и от дискриминатора. Это шаг, который помогает генератору узнать о реальном распределении данных. Если генератор плохо справляется с генерацией реалистичных данных (имеющих одинаковое распределение), работу дискриминатора будет очень легко отличить от сгенерированных реальных наборов данных. Следовательно, потеря Дискриминатора будет очень маленькой. Малые потери дискриминатора приведут к большим потерям генератора (см. уравнение ниже для L (D, G)). Это делает создание дискриминатора немного сложным, потому что слишком хороший дискриминатор всегда приводит к огромным потерям генератора, что делает генератор неспособным к обучению.
  4. Процесс продолжается до тех пор, пока Дискриминатор больше не сможет отличить сгенерированные данные от реальных.

В сочетании друг с другом D и G играют в игру minmax (Генератор пытается обмануть Дискриминатор, заставляя его увеличивать вероятность на поддельных примерах, т.е. минимизировать Ez∼pz(z)[log(1−D(G(z)))]. Дискриминатор хочет отделить данные, поступающие от генератора D(G(z)), максимизируя Ex∼pr(x)[logD(x)]. Однако, разделив функции потерь, неясно, как они могут сходиться вместе (вот почему мы используем некоторые достижения по сравнению с простыми GAN, такими как GAN Вассерштейна). В целом комбинированная функция потерь выглядит следующим образом:

Примечание: Действительно полезные советы по обучению GAN можно найти здесь.

Примечание: Я не буду включать в эту записную книжку полный код, лежащий в основе частей GAN и обучения с подкреплением — будут показаны только результаты выполнения (выходные данные ячеек). Сделайте запрос на вытягивание или свяжитесь со мной для получения кода.

3.1. Зачем нужен GAN для прогнозирования фондового рынка

Генеративно-состязательные сети (GAN) в последнее время используются в основном для создания реалистичных изображений, картин и видеоклипов. Существует не так много применений GAN, используемых для прогнозирования данных временных рядов, как в нашем случае. Основная идея, однако, должна быть той же — мы хотим предсказать будущие движения акций. В будущем структура и поведение акций GS должны быть более или менее такими же (если только они не начнут работать совершенно по-другому или экономика кардинально не изменится). Следовательно, мы хотим «сгенерировать» данные на будущее, которые будут иметь такое же (не совсем то же самое, конечно) распределение, как и то, которое у нас уже есть — исторические торговые данные. Так что, по идее, это должно сработать.

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

3.2. Метрополис-Гастингс GAN и Wasserstein GAN

Примечание: Следующие несколько разделов предполагают некоторый опыт работы с GAN.

I. Метрополис-Гастингс, штат GAN

Недавнее улучшение по сравнению с традиционными GAN вышло от команды инженеров Uber и называется Metropolis-Hastings GAN (MHGAN). Идея, лежащая в основе подхода Uber, несколько похожа на другой подход, созданный Google и Калифорнийским университетом в Беркли под названием Discriminator Rejection Sampling (DRS). По сути, когда мы тренируем GAN, мы используем Дискриминатор (D) с единственной целью лучшего обучения Генератора (G). Зачастую, после обучения GAN, мы больше не используем D. MHGAN и DRS, однако, пытаются использовать D для выбора выборок, генерируемых G, которые близки к реальному распределению данных (небольшое различие между ними заключается в том, что MHGAN использует цепь Маркова Монте-Карло (MCMC) для выборки).

MHGAN берет K-образцы, сгенерированные из G (созданные из независимых шумовых входов в G — z0 в zK на рисунке ниже). Затем он последовательно проходит через K выходов (от x′0 до x′K) и, следуя правилу приемки (созданному из дискриминатора), решает, принять ли текущий образец или оставить последний принятый. Последний сохраненный выход — это тот, который считается реальным выходом G.

Примечание: MHGAN изначально реализован Uber в pytorch. Я перенес его только в MXNet/Gluon.

Примечание: Я также загружу его на Github в ближайшее время.

Рисунок 10: Визуальное представление MHGAN (из оригинального сообщения Uber).

II. Вассерштейн GAN

Обучать GAN довольно сложно. Модели могут никогда не сойтись, и легко может произойти коллапс режимов. Мы будем использовать модификацию GAN под названием Wasserstein GAN — WGAN.

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

  • Как мы знаем, основная цель GAN состоит в том, чтобы генератор начал преобразовывать случайный шум в некоторые данные, которые мы хотим имитировать. Следовательно, идея сравнения сходства между двумя распределениями очень важна в GAN. Двумя наиболее широко используемыми такими показателями являются:
  • Дивергенция KL (Куллбак-Лейблер) — DKL(p‖q)=∫xp(x)logp(x)q(x)dx. DKL равен нулю, когда p(x) равен q(x),
  • Дивергенция JS (Дженсен-Шеннон). Дивергенция JS ограничена 0 и 1 и, в отличие от дивергенции KL, симметрична и более плавна. Значительные успехи в обучении GAN были достигнуты при переключении потерь с KL на JS дивергенцию.
  • WGAN использует расстояние Вассерштейна, W(pr,pg)=1Ksup‖f‖L≤KEx∼pr[f(x)]−Ex∼pg[f(x)] (где sup означает supremum), в качестве функции потерь (также называемой расстоянием Земного движителя, потому что она обычно интерпретируется как перемещение одной кучи, скажем, песка в другую, причем обе кучи имеют разное распределение вероятностей, используя минимальную энергию во время преобразования). По сравнению с дивергенциями KL и JS метрика Вассерштейна дает плавную меру (без резких скачков дивергенции). Это делает его гораздо более подходящим для создания стабильного процесса обучения во время градиентного спуска.
  • Кроме того, по сравнению с KL и JS, расстояние Вассерштейна дифференцируется почти везде. Как мы знаем, во время обратного распространения мы дифференцируем функцию потерь, чтобы создать градиенты, которые, в свою очередь, обновляют веса. Поэтому наличие дифференцируемой функции потерь очень важно.

Несомненно, это была самая сложная часть этой записной книжки. Микширование WGAN и MHGAN заняло у меня три дня.

3.4. Генератор — однослойный RNN

3.4.1. LSTM или GRU

Как упоминалось ранее, генератор представляет собой сеть LSTM, тип рекуррентной нейронной сети (RNN). RNN используются для данных временных рядов, потому что они отслеживают все предыдущие точки данных и могут фиксировать закономерности, развивающиеся во времени. Из-за своей природы RNN часто страдают от исчезающего градиента — то есть изменения, которые получают веса во время тренировки, становятся настолько малыми, что они не меняются, что делает сеть неспособной сходиться к минимальным потерям (Иногда можно наблюдать и противоположную проблему — когда градиенты становятся слишком большими. Это называется градиентным взрывом, но решение этой проблемы довольно простое — обрезать градиенты, если они начинают превышать какое-то постоянное число, т.е. отсечение градиента). Две модификации решают эту проблему — закрытая рекуррентная единица (GRU) и долговременная краткосрочная память (LSTM). Самые большие различия между ними заключаются в следующем: 1) GRU имеет 2 шлюза (обновление и сброс), а LSTM имеет 4 (обновление, ввод, забвение и вывод), 2) LSTM поддерживает состояние внутренней памяти, а GRU — нет, и 3) LSTM применяет нелинейность (сигмоид) перед выходным вентом, GRU — нет.

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

Строго говоря, математика, лежащая в основе ячейки LSTM (ворота), такова:

где ⊙является поэлементным оператором умножения, а для всех x=[x1,x2,…,xk]⊤∈R^k две функции активации:,

3.4.2. Архитектура LSTM

Архитектура LSTM очень проста — один слой LSTM со 112 входными блоками (так как у нас 112 объектов в наборе данных) и 500 скрытых блоков, и один Dense с 1 выходом — цена на каждый день. Инициализатором является Xavier, и мы будем использовать потерю L1 (которая является средней абсолютной погрешностью при регуляризации L1 — см. раздел 3.4.5. для получения дополнительной информации о регуляризации).

Примечание — В коде вы можете видеть, что мы используем Adam (со learning rate 0,01) в качестве оптимизатора. Не обращайте на это слишком много внимания сейчас — есть раздел, специально посвященный объяснению того, какие гиперпараметры мы используем (скорость обучения исключена, так как у нас есть планировщик скорости обучения — раздел 3.4.3.) и как мы оптимизируем эти гиперпараметры — раздел 3.6.

gan_num_features = dataset_total_df.shape[1]
sequence_length = 17class RNNModel(gluon.Block):
def __init__(self, num_embed, num_hidden, num_layers, bidirectional=False, sequence_length=sequence_length, **kwargs):
super(RNNModel, self).__init__(**kwargs)
self.num_hidden = num_hidden
with self.name_scope():
self.rnn = rnn.LSTM(num_hidden, num_layers, input_size=num_embed, bidirectional=bidirectional, layout='TNC')
self.decoder = nn.Dense(1, in_units=num_hidden)

def forward(self, inputs, hidden):
output, hidden = self.rnn(inputs, hidden)
decoded = self.decoder(output.reshape((-1,self.num_hidden)))
return decoded, hidden

def begin_state(self, *args, **kwargs):
return self.rnn.begin_state(*args, **kwargs)

lstm_model = RNNModel(num_embed=gan_num_features, num_hidden=500, num_layers=1)
lstm_model.collect_params().initialize(mx.init.Xavier(), ctx=mx.cpu())
trainer = gluon.Trainer(lstm_model.collect_params(), 'adam', {'learning_rate': .01})
loss = gluon.loss.L1Loss()

Мы будем использовать 500 нейронов в слое LSTM и использовать инициализацию Ксавьера. Для регуляризации мы будем использовать L1. Давайте посмотрим, что находится внутри LSTM, напечатанного MXNet.

print(lstm_model)output >>>RNNModel(
(rnn): LSTM(112 -> 500, TNC)
(decoder): Dense(500 -> 1, linear)
)

Как мы видим, входными данными LSTM являются 112 признаков (dataset_total_df.shape[1]), которые затем входят в 500 нейронов в слое LSTM, а затем преобразуются в один выход — значение цены акции.

Логика, лежащая в основе LSTM, такова: мы берем данные за 17 (sequence_length) дней (опять же, данные представляют собой цену акций GS каждый день + все остальные характеристики на этот день — коррелированные активы, настроения и т. Д.) И пытаемся предсказать 18-й день. Затем мы перемещаем 17-дневное окно на один день и снова прогнозируем 18-е. Мы выполняем итерации по всему набору данных (конечно, партиями).

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

  • использование двунаправленного слоя LSTM — теоретически движение назад (от конца набора данных к началу) может каким-то образом помочь LSTM выяснить закономерность движения акций.
  • использование многоуровневой архитектуры RNN — наличие не только одного слоя LSTM, но и 2 и более. Это, однако, может быть опасно, так как мы можем в конечном итоге переобустроить модель, поскольку у нас не так много данных (у нас есть только данные за 1,585 дней).
  • Как уже объяснялось, ячейки ГРУ намного проще.
  • Добавление векторов внимания в RNN.

3.4.3. Планировщик скорости обучения

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

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

schedule = CyclicalSchedule(TriangularSchedule, min_lr=0.5, max_lr=2, cycle_length=500)
iterations=1500plt.plot([i+1 for i in range(iterations)],[schedule(i) for i in range(iterations)])
plt.title('Learning rate for each epoch')
plt.xlabel("Epoch")
plt.ylabel("Learning Rate")
plt.show()

3.4.4. Как предотвратить переобучение и компромисс между смещением и дисперсией

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

Мы используем несколько методов предотвращения переобучения (не только в LSTM, но и в CNN и автоэнкодерах):

  • Обеспечение качества данных. Мы уже провели статистическую проверку и убедились, что данные не страдают от мультиколлинеарности или серийной автокорреляции. Кроме того, мы выполнили проверку важности каждой функции. Наконец, первоначальный выбор функций (например, выбор коррелированных активов, технических индикаторов и т. д.) был сделан с некоторыми знаниями предметной области о механике, лежащей в основе работы фондовых рынков.
  • Регуляризация (или штраф за веса). Двумя наиболее широко используемыми методами регуляризации являются LASSO (L1) и Ridge (L2). L1 добавляет среднюю абсолютную ошибку, а L2 добавляет к потере среднюю квадратичную ошибку. Не вдаваясь в слишком много математических подробностей, основные различия заключаются в следующем: регрессия лассо (L1) выполняет как выбор переменных, так и сжатие параметров, тогда как регрессия Риджа выполняет только сжатие параметров и в конечном итоге включает все коэффициенты в модель. При наличии коррелированных переменных предпочтительным выбором может быть регрессия хребта. Кроме того, регрессия гребня лучше всего работает в ситуациях, когда оценки наименьших квадратов имеют более высокую дисперсию. Следовательно, это зависит от цели нашей модели. Влияние этих двух типов регуляризации совершенно различно. В то время как они оба наказывают большие веса, регуляризация L1 приводит к недифференцируемой функции при нуле. Регуляризация L2 благоприятствует меньшим весам, но регуляризация L1 благоприятствует весам, которые стремятся к нулю. Таким образом, с регуляризацией L1 вы можете получить разреженную модель с меньшим количеством параметров. В обоих случаях параметры регуляризованных моделей L1 и L2 «сжимаются», но в случае регуляризации L1 усадка напрямую влияет на сложность (количество параметров) модели. Точнее, регрессия гребня лучше всего работает в ситуациях, когда оценки наименьших квадратов имеют более высокую дисперсию. L1 более устойчив к выбросам, используется, когда данные разрежены, и создает важность функций. Мы будем использовать L1.
  • Отсев. Выпадающие слои случайным образом удаляют узлы в скрытых слоях.
  • Плотная-разреженная-плотная тренировка. — ссылка
  • Ранняя остановка.

Еще одним важным соображением при построении сложных нейронных сетей является компромисс между смещением и дисперсией. По сути, ошибка, которую мы получаем при обучении сетей, является функцией смещения, дисперсии и неустранимой ошибки — σ (ошибка из-за шума и случайности). Простейшая формула компромисса: Ошибка = смещение ^ 2 + дисперсия + σ.

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

3.5. Дискриминатор — Одномерный CNN

4.5.1. Почему CNN как дискриминатор?

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

Еще одна причина использования CNN заключается в том, что CNN хорошо работают с пространственными данными, а это означает, что точки данных, которые находятся ближе друг к другу, больше связаны друг с другом, чем точки данных, разбросанные по территории. Это справедливо и для данных временных рядов. В нашем случае каждая точка данных (для каждого объекта) относится к каждому последующему дню. Естественно предположить, что чем ближе два дня друг к другу, тем больше они связаны друг с другом. Одна вещь, которую следует учитывать (хотя и не рассматривается в этой работе), — это сезонность и то, как она может изменить (если вообще изменит) работу CNN.

Примечание: Как и во многих других разделах этой записной книжки, использование CNN для данных временных рядов является экспериментальным. Мы проверим результаты, не предоставив математических или других доказательств. И результаты могут отличаться в зависимости от разных данных, функций активации и т. Д.

3.5.1. Архитектура CNN

Не вдаваясь в полный код, мы просто покажем CNN в том виде, в котором он напечатан MXNet.

Sequential(
(0): Conv1D(None -> 32, kernel_size=(5,), stride=(2,))
(1): LeakyReLU(0.01)
(2): Conv1D(None -> 64, kernel_size=(5,), stride=(2,))
(3): LeakyReLU(0.01)
(4): BatchNorm(axis=1, eps=1e-05, momentum=0.9, fix_gamma=False, use_global_stats=False, in_channels=None)
(5): Conv1D(None -> 128, kernel_size=(5,), stride=(2,))
(6): LeakyReLU(0.01)
(7): BatchNorm(axis=1, eps=1e-05, momentum=0.9, fix_gamma=False, use_global_stats=False, in_channels=None)
(8): Dense(None -> 220, linear)
(9): BatchNorm(axis=1, eps=1e-05, momentum=0.9, fix_gamma=False, use_global_stats=False, in_channels=None)
(10): LeakyReLU(0.01)
(11): Dense(None -> 220, linear)
(12): Activation(relu)
(13): Dense(None -> 1, linear)
)

3.6. Гиперпараметры

Гиперпараметры, которые мы будем отслеживать и оптимизировать:

  • batch_size : размер партии LSTM и CNN
  • cnn_lr: уровень обучения CNN
  • strides: количество шагов в CNN
  • lrelu_alpha: альфа-версия для LeakyReLU в глобальной сети GAN
  • batchnorm_momentum: импульс для пакетной нормализации в CNN
  • padding: набивка в CNN
  • kernel_size':1: размер ядра в CNN
  • dropout в LSTM
  • filters: начальное количество фильтров

Мы обучим более 200 epochs.

4. Оптимизация гиперпараметров

После того, как GAN обучится на 200 эпохах, он запишет MAE (которая является функцией ошибки в LSTM, GG) и передаст ее в качестве значения вознаграждения обучению с подкреплением, которое решит, следует ли изменять гиперпараметры продолжения обучения с тем же набором гиперпараметров. Как описано ниже, этот подход предназначен исключительно для экспериментов с RL.

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

4.1. Обучение с подкреплением для оптимизации гиперпараметров

Почему мы используем обучение с подкреплением в оптимизации гиперпараметров? Фондовые рынки постоянно меняются. Даже если нам удастся обучить наши GAN и LSTM создавать чрезвычайно точные результаты, результаты могут быть действительны только в течение определенного периода. Это означает, что нам нужно постоянно оптимизировать весь процесс. Для оптимизации процесса мы можем:

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

Примечание: Цель всей части этой тетради, посвященной обучению с подкреплением, больше ориентирована на исследования. Мы рассмотрим различные подходы RL с использованием GAN в качестве среды. Существует множество способов, с помощью которых мы можем успешно выполнять оптимизацию гиперпараметров в наших моделях глубокого обучения без использования RL. Но… Почему бы нет.

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

4.1.1. Теория обучения с подкреплением

Не объясняя основ RL, мы перейдем к деталям конкретных подходов, которые мы здесь реализуем. Мы будем использовать алгоритмы RL без моделей по очевидной причине, что мы не знаем всей среды, поэтому нет определенной модели того, как работает среда — если бы она была, нам не нужно было бы предсказывать движения цен на акции — они просто будут следовать модели. Мы будем использовать два подразделения безмодельного RL — оптимизацию политики и Q-обучение.

  • Q-обучение — в Q-обучении мы узнаем ценность совершения действия из данного состояния. Q-значение — это ожидаемая доходность после совершения действия. Мы будем использовать Rainbow, который представляет собой комбинацию семи алгоритмов обучения Q.
  • Оптимизация политики — при оптимизации политики мы узнаем, какие действия следует предпринять из данного состояния. (если мы используем такие методы, как Actor/Critic), мы также узнаем ценность нахождения в данном состоянии. Мы будем использовать проксимальную оптимизацию политики.

Одним из важнейших аспектов построения алгоритма RL является точная установка вознаграждения. Он должен охватывать все аспекты окружающей среды и взаимодействия агента с окружающей средой. Мы определяем вознаграждение R как:

Награда = 2∗потеряG+потеряD + точностьG,

где lossG, accuracyG и lossD — потери и точность генератора, а также потери дискриминатора соответственно. Окружающая среда — это GAN и результаты обучения LSTM. Действие, которое могут предпринять различные агенты, заключается в том, как изменить гиперпараметры сетей D и G GAN.

4.1.1.1. Радуга

Что такое радуга?

Rainbow (ссылка) — это внеполитический алгоритм глубокого обучения с подкреплением, основанный на Q-обучении, объединяющий семь алгоритмов:

  • DQN. DQN — это расширение алгоритма обучения Q, которое использует нейронную сеть для представления значения Q. Подобно контролируемому (глубокому) обучению, в DQN мы обучаем нейронную сеть и пытаемся минимизировать функцию потерь. Мы обучаем сеть, случайным образом выбирая переходы (состояние, действие, вознаграждение). Слои могут быть не только полносвязными, но и сверточными, например.
  • Двойное Q-обучение. Double QL решает большую проблему в обучении Q, а именно смещение переоценки.
  • Приоритетное воспроизведение. В ванильном DQN все переходы хранятся в буфере воспроизведения, и он равномерно сэмплирует этот буфер. Однако не все переходы одинаково полезны на этапе обучения (что также делает обучение неэффективным, поскольку требуется больше эпизодов). Воспроизведение опыта с приоритетом не сэмплирует равномерно, а использует распределение, которое дает более высокую вероятность выборкам, которые имели более высокие потери Q в предыдущих итерациях.
  • Дуэльные сети. Дуэльные сети немного изменяют архитектуру Q-обучения, используя два отдельных потока (т.е. имея две разные мини-нейронные сети). Один поток предназначен для ценности, а другой для преимущества. Оба они имеют общий сверточный кодировщик. Сложность заключается в объединении потоков — для этого используется специальный агрегатор (Wang et al. 2016;).

(Преимущество, формула A(s,a)=Q(s,a)−V(s), вообще говоря, представляет собой сравнение того, насколько хорошо действие по сравнению со средним действием для конкретного состояния. Преимущества иногда используются, когда «неправильное» действие не может быть наказано отрицательным вознаграждением. Таким образом, преимущество будет пытаться еще больше вознаградить хорошие действия от средних действий.)

  • Многоступенчатое обучение. Большая разница в многоступенчатом обучении заключается в том, что оно вычисляет значения Q, используя N-шаговую отдачу (а не только отдачу от следующего шага), что, естественно, должно быть более точным.
  • Распределительный ЛЛ. Q-обучение использует среднее оценочное Q-значение в качестве целевого значения. Однако во многих случаях значения добротности могут не совпадать в разных ситуациях. Распределительный RL может непосредственно изучать (или аппроксимировать) распределение Q-значений, а не усреднять их. Опять же, математика намного сложнее, но для нас преимущество заключается в более точной выборке Q-значений.
  • Шумные сети. Basic DQN реализует простой ε-жадный механизм для исследования. Такой подход к геологоразведке временами неэффективен. Шумные сети подходят к этой проблеме путем добавления зашумленного линейного слоя. Со временем сеть научится игнорировать шум (добавляется в виде шумного потока). Но это обучение происходит с разной скоростью в разных частях пространства, что позволяет исследовать состояние.

Примечание: Оставайтесь с нами — я загружу реализацию MXNet/Gluon на Rainbow на Github в начале февраля 2019 года.

4.1.1.2. РРО

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

Почему мы используем PPO? Одним из преимуществ PPO является то, что он изучает политику напрямую, а не косвенно через значения (так как Q Learning использует Q-значения для изучения политики). Он может хорошо работать в пространствах непрерывного действия, что подходит для нашего варианта использования, и может изучать (через среднее значение и стандартное отклонение) вероятности распределения (если softmax добавляется в качестве выхода).

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

  • гораздо менее сложен, например, по сравнению с ACER, который требует дополнительного кода для сохранения корреляций вне политики, а также буфера воспроизведения, или TRPO, который имеет ограничение, наложенное на суррогатную целевую функцию (расхождение KL между старой и новой политикой). Это ограничение используется для контроля над политикой слишком больших изменений, что может привести к нестабильности. PPO сокращает вычисления (создаваемые ограничением) за счет использования обрезанной (между [1-ε, 1+ε]) суррогатной целевой функции и модификации целевой функции со штрафом за слишком большое обновление.
  • обеспечивает совместимость с алгоритмами, которые разделяют параметры между значением и функцией политики или вспомогательными потерями, по сравнению с TRPO (хотя PPO также имеют выигрыш PO в области доверия).

Примечание: Для целей нашего упражнения мы не будем слишком углубляться в исследование и оптимизацию подходов RL, PPO и других. Скорее, мы возьмем то, что доступно, и попытаемся вписаться в наш процесс оптимизации гиперпараметров для наших моделей GAN, LSTM и CNN. Код, который мы будем повторно использовать и настраивать, создан OpenAI и доступен здесь.

4.1.2. Дальнейшая работа над обучением с подкреплением

Некоторые идеи для дальнейшего изучения обучения с подкреплением:

  • Одна из первых вещей, которую я представлю дальше, — это использование дополненного случайного поиска (ссылка) в качестве альтернативного алгоритма. Авторам алгоритма (из Калифорнийского университета в Беркли) удалось достичь тех же результатов вознаграждения, что и другие современные подходы, такие как PPO, но в среднем в 15 раз быстрее.
  • Выбор функции вознаграждения очень важен. Я указал выше используемую в настоящее время функцию вознаграждения, но попробую поиграть с другими функциями в качестве альтернативы.
  • Использование Curiosity в качестве политики разведки.
  • Создание мультиагентной архитектуры, предложенной исследовательской группой по искусственному интеллекту в Беркли (BAIR) — ссылка.

4.2. Байесовская оптимизация

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

4.2.1. Гауссов процесс

5. Результат

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

  1. Сюжет после первой эпохи.
from utils import plot_predictionplot_prediction('Predicted and Real price - after first epoch.')

2. Сюжет после 50 эпох.

plot_prediction('Predicted and Real price - after first 50 epochs.')
plot_prediction('Predicted and Real price - after first 200 epochs.')

RL длится десять эпизодов (мы определяем эпозид как один полный тренинг GAN по 200 эпохам).

plot_prediction('Final result.')

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

6. Что дальше?

  • Далее я постараюсь создать среду RL для тестирования торговых алгоритмов, которые решают, когда и как торговать. Выходные данные GAN будут одним из параметров среды.

7. Отказ от ответственности

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

Прогнозирование тенденций цен на акции с помощью Python

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

Если вы хотите изучить продвинутые методы с еще более высокой точностью и дополнительными параметрами, вы также можете ознакомиться с такими платформами, как Stock Telegraph. Они используют схожие модели, обеспечивая более комплексный подход к прогнозированию цен на акции. Итак, приступим!

Зачем использовать Python для прогнозирования цен на акции?

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

  • Получите представление о движении рынка.
  • Принимайте инвестиционные решения на основе данных.
  • Визуализируйте будущие сценарии цен на акции.

Настройка среды

Прежде чем мы начнем, убедитесь, что в вашей виртуальной среде установлены следующие библиотеки:

  • requests: Для получения биржевых данных.
  • pandas: Для манипулирования данными.
  • prophet: Для прогнозирования цен на акции.

Вы можете установить их с помощью pip:pip install requests pandas prophet

Получение биржевых данных

Для начала нам понадобятся исторические данные по акциям. В этом руководстве мы будем использовать API подготовки к финансовому моделированию (FMP).

Financial Modeling Prep выделяется как одна из самых надежных и удобных платформ для получения финансовых данных. И вот почему:

  • Точность: FMP славится своими точными и актуальными данными, гарантируя, что вы основываете свои прогнозы на точной информации.
  • Простота использования: Благодаря простому API даже новички могут получать данные без каких-либо заминок.
  • Исчерпывающие данные: от исторических цен на акции до финансовых отчетов, FMP предлагает широкий спектр финансовых данных, что делает его универсальным магазином для аналитиков.
  • Доступность: По сравнению с другими платформами, FMP предлагает конкурентоспособные цены, гарантируя, что как профессионалы, так и любители могут получить доступ к качественным данным без больших затрат.

Начало работы с FMP

  • Зарегистрируйтесь: Перейдите на веб-сайт Financial Modeling Prep и зарегистрируйте бесплатную учетную запись. Они также предлагают премиум-планы с дополнительными преимуществами, поэтому выбирайте то, что лучше всего соответствует вашим потребностям.
  • Сгенерируйте ключ API: После входа в систему перейдите в раздел API. Здесь вы можете сгенерировать свой уникальный ключ API, который позволит вам получать данные непосредственно в вашу среду Python.

Код:import requests

symbol = «AAPL»
from_date = «2022-01-01»
to_date = «2023-08-01»
url = f»https://financialmodelingprep.com/api/v3/historical-price-full/{symbol}?from={from_date}&to={to_date}&apikey={FMP_API_KEY}»

def fetch_data(url):
response = requests.get(url).json()
return response

Предварительная обработка данных

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

Код:import pandas as pd

def preprocess_data(data):
df = pd.DataFrame(data[«historical»])
df.reset_index(inplace=True)
df.rename(columns={‘date’: ‘ds’, ‘adjClose’: ‘y’}, inplace=True)
return df

Обучение модели

Теперь, когда наши данные готовы, давайте обучим нашу модель Prophet.

Кто такой Пророк?

Prophet — это надежный инструмент прогнозирования, который может обрабатывать ежедневные данные о запасах и учитывать праздники и выходные.

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

Код:from prophet import Prophet

def train_prophet_model(data):
model = Prophet(
changepoint_prior_scale=0.05,
holidays_prior_scale=15,
seasonality_prior_scale=10,
weekly_seasonality=True,
yearly_seasonality=True,
daily_seasonality=False
)
model.add_country_holidays(country_name=’US’)
model.fit(data)
return model

Генерация и визуализация прогноза

Теперь давайте сформируем прогноз на следующий год и визуализируем его.

Код:def generate_forecast(model, periods=365):
future = model.make_future_dataframe(periods=periods)
forecast = model.predict(future)
return forecast

def plot_forecast(model, forecast):
model.plot(forecast)

Вывод в Matplotlib Chart

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

Точность прогноза можно повысить, улучшив гиперпараметры.

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

Давайте углубимся и посмотрим на прогнозные графики для AAPL и MSFT от Stocks Telegraph:

Собираем все воедино

Давайте объединим все наши функции и спрогнозируем динамику цены акций Apple (AAPL) с 2022-01-01 по 2023-08-01.

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

Код:import os

import pandas as pd
import requests
from prophet import Prophet

FMP_API_KEY = os.getenv(«FMP_API_KEY»)

def fetch_data(url):
«»»Fetches data from the given URL and returns the JSON response.

Args:
url (str): The URL to fetch data from.

Returns:
dict: JSON response from the API.
«»»
response = requests.get(url).json()
return response

def preprocess_data(data):
«»»Preprocesses the historical data for compatibility with Prophet model.

Args:
data (dict): The historical data in dictionary format.

Returns:
pd.DataFrame: Processed DataFrame with renamed columns.
«»»
df = pd.DataFrame(data[«historical»])
df.reset_index(inplace=True)
df.rename(columns={«date»: «ds», «adjClose»: «y»}, inplace=True)
return df

def train_prophet_model(data):
«»»Trains a Prophet model on the provided data.

Args:
data (pd.DataFrame): Processed DataFrame with ‘ds’ and ‘y’ columns.

Returns:
Prophet: Trained Prophet model.
«»»
model = Prophet(
changepoint_prior_scale=0.05,
holidays_prior_scale=15,
seasonality_prior_scale=10,
weekly_seasonality=True,
yearly_seasonality=True,
daily_seasonality=False,
)
model.add_country_holidays(country_name=»US»)
model.fit(data)
return model

def generate_forecast(model, periods=365):
«»»Generates forecasts using the trained Prophet model.

Args:
model (Prophet): Trained Prophet model.
periods (int): Number of periods to forecast into the future.

Returns:
pd.DataFrame: DataFrame containing forecasted values.
«»»
future = model.make_future_dataframe(periods=periods)
forecast = model.predict(future)
return forecast

def plot_forecast(model, forecast):
«»»Plots the forecast generated by the Prophet model.

Args:
model (Prophet): Trained Prophet model.
forecast (pd.DataFrame): DataFrame containing forecasted values.
«»»
model.plot(forecast)

def main():
symbol = «AAPL»
from_date = «2022-01-01»
to_date = «2023-08-01»
URL = f»https://financialmodelingprep.com/api/v3/historical-price-full/{symbol}?from={from_date}&to={to_date}&apikey={FMP_API_KEY}»

# Fetch data
data = fetch_data(URL)

# Preprocess data
df = preprocess_data(data)

# Train Prophet model
model = train_prophet_model(df)

# Generate forecast
forecast = generate_forecast(model)

# Plot forecast
plot_forecast(model, forecast)

if __name__ == «__main__»:
main()

Прогнозирование цен на акции с помощью машинного обучения

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

Цель проекта

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

Стратегия решения проблемы

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

Описание входных данных

Как упоминалось ранее, мы обратились к Yahoo Finance за историческими данными об акциях, используя yfinance API для загрузки информации, связанной с акциями. Определение каждого входного сигнала представлено в таблице ниже.

Предварительная обработка данных

Данные, полученные из Yahoo Finance, уже имеют формат временных рядов, в котором даты выступают в качестве индекса. Однако проблема возникает из-за того, что фондовый рынок работает исключительно в рабочие дни, что приводит к пробелам в наборе данных из-за выходных и праздничных дней. В таблице ниже мы видим, что временной ряд скачет с 2022-01-07 на 2022-01-10, пропуская выходные.

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

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

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

Фрагмент для масштабирования данных: Мы использовали MinMaxScaler для выполнения операции масштабирования. Для масштабирования обучающих данных мы использовали fit_transform. Что касается тестовых данных, мы применили исключительно преобразование, избегая процесса подгонки, чтобы предотвратить утечку данных.

Выбор объектов и дальнейшее изменение формы данных

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

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

Дальнейшая визуализация, подтверждающая, что другие функции сильно коррелируют с целевой переменной «Adj Close». Что касается функции «Объем», мы решили исключить ее из нашей модели машинного обучения из-за ее слабой корреляции -0,3 с другими переменными. Более того, эти признаки исключаются, поскольку значительное их число становится известно только после определения целевой переменной. Эта неотъемлемая недоступность делает их неэффективными для прогностических выводов.

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

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

Выбор и оценка модели

Наше путешествие привело нас к изучению двух мощных типов рекуррентных нейронных сетей (RNN): долговременной краткосрочной памяти (LSTM) и закрытой рекуррентной единицы (GRU). Эти RNN хорошо подходят для последовательных данных, таких как временные ряды.

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

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

Архитектура модели — LSTM

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

Мы хотели, чтобы наша модель LSTM работала максимально точно. Итак, мы протестировали его с различными настройками с помощью GridSearch. Это помогло нам найти лучший способ настроить модель для прогнозирования цен на акции. Найдя эти лучшие настройки, мы соответствующим образом скорректировали модель. Затем мы использовали эту хорошо настроенную модель, чтобы увидеть, насколько хорошо она прогнозирует цены на акции с использованием выбранных настроек. Таким образом, мы убедились, что наша модель работает наилучшим образом.

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

Точно так же мы применили идентичный процесс к модели ГРУ; однако модель LSTM показала себя немного лучше, что привело к более низкому баллу MAPE. Это небольшое преимущество в производительности привело нас к выбору модели LSTM в качестве окончательного выбора для нашего проекта прогнозирования цен на акции.

Метрики и обоснование

Успех проекта зависит от выбора метрик оценки. В качестве основного показателя была выбрана средняя абсолютная процентная ошибка (MAPE). MAPE вычисляет процентную разницу между прогнозируемыми и фактическими значениями, обеспечивая интуитивную меру точности прогнозирования.

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

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

Точно так же мы следовали той же процедуре для модели ГРУ. Однако результаты оказались не такими многообещающими, как те, которые были достигнуты с моделью LSTM. Модель LSTM дала оценку 0,5800, а модель GRU — 0,58648. Обе модели показали себя примерно одинаково, при этом модель LSTM немного опередила с небольшим отрывом.

Результаты и ключевые идеи

Кульминацией проекта является оценка производительности модели. Благодаря тщательному тестированию и оценке модель LSTM достигла MAPE 0,58, что является многообещающим показателем ее прогностических возможностей.

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

Заключение

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

Будущие усовершенствования

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

Прогнозирование цен на акции с помощью квантового машинного обучения на Python

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

Чтобы облегчить этот проект, мы будем использовать конечную точку Historical API, предлагаемую Financial Modeling Prep (FMP), для получения надежных и точных данных, что очень важно. С учетом сказанного, давайте углубимся в статью.

Импорт данных

Начнем с импорта необходимых библиотек для нашего анализа. Эти библиотеки предоставят основные инструменты, необходимые для изучения и реализации нашего проекта.import numpy as np
import pandas as pd
import requests
import json
import tensorflow as tf
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from sklearn.metrics import mean_squared_error
from qiskit import QuantumCircuit
from qiskit.circuit.library import PauliFeatureMap
from qiskit.algorithms.optimizers import ADAM
from qiskit.circuit import Parameter
from qiskit.primitives import Sampler

Мы настроили нашу среду, установив библиотеку Qiskit для работы с сетями квантовых вычислений, а также другие важные библиотеки. Для извлечения данных мы будем использовать конечную точку API исторических данных, предоставленную Financial Modeling Prep.

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

Теперь мы собираемся извлечь исторические данные следующим образом:api_url = «https://financialmodelingprep.com/api/v3/historical-price-full/AAPL?apikey=YOUR API KEY»

# Make a GET request to the API
response = requests.get(api_url)

# Check if the request was successful (status code 200)
if response.status_code == 200:
# Parse the response JSON
data = response.json()
else:
print(f»Error: Unable to fetch data. Status code: {response.status_code}»)

data

Замените YOUR API KEYAPI на секретный ключ API, который вы можете получить, создав учетную запись FMP. Выходные данные представляют собой ответ JSON, который выглядит следующим образом:

Ответ API исторических данных

Введение в квантовые вычисления

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

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

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

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

Операторы в квантовых вычислениях

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

который, по сути, имеет дело с подсчетом по модулю 2.

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

Возьмем квантовый вентиль НЕ, который называется:

В качестве примера. Примените его к кубиту первоначально в состоянии ∣0⟩, и оператор перевернет его на ∣1⟩ , а если вы примените его снова, он вернется к ∣0⟩. Это немного похоже на подбрасывание монетки.

Есть также ворота Адамара (H), которые делают что-то действительно крутое. Применяя его к кубиту, изначально находящемуся в состоянии ∣0⟩ помещаем его в эту специальную смесь состояний 0 и 1 одновременно, чтобы математически показать, что H работает с |0⟩ и преобразует его в стандартную суперпозицию базисных состояний:

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

Теперь давайте поговорим о вентили Controlled-NOT (CNOT). Он работает на двух кубитах. Если первый кубит равен ∣1⟩, он переворачивает второй кубит с ∣0⟩ на ∣1⟩ или наоборот. Это похоже на квантовый переключатель, который зависит от состояния первого кубита.

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

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

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

Каждый из них характеризуется унитарной матрицей, как показано на рисунке ниже:

Давайте немного углубимся в эти ворота вращения. Рассматривать:

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

и

Вентили функционируют аналогично, внося вращения вокруг разных осей.

Значение этих ворот заключается в их параметризованном характере. Изменяя входной параметр θ, мы, по сути, вводим настраиваемый элемент в наши квантовые алгоритмы. Эти вентили служат основополагающими компонентами для построения квантовой нейронной сети, интегрированной в наш Проект.

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

Квантовые схемы

Квантовые алгоритмы можно рассматривать как серию операций, выполняемых над квантовым состоянием, представленных такими выражениями, как:

Эти алгоритмы преобразуются в квантовые схемы, как показано на рисунке ниже. На этом рисунке алгоритм начинается с начального состояния |q_0 q_1⟩ = |00⟩ и заканчивается измерением, результатом которого является либо |00⟩, либо |11⟩ с равной вероятностью 0,5, записанное в классические биты (строка c).

В квантовой схеме каждая горизонтальная линия соответствует одному кубиту, а вентили применяются последовательно до тех пор, пока не будет произведено измерение. Важно отметить, что циклы в квантовой программе не допускаются. Особым типом квантовой схемы является вариационная квантовая схема (VQC). Примечательно, что VQC включает в себя параметризованные вентили, такие как вышеупомянутые R_x(θ), R_y(θ), R_z(θ).

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

Квантовое машинное обучение

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

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

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

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

Квантовая нейронная сеть

Квантовая нейронная сеть (QNN) обычно состоит из трех основных слоев:

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

2. Слой Ansatz: Сердце QNN, этот слой содержит вариационную квантовую схему, повторяющуюся L раз для классической симуляции L сетевых слоев. Он отвечает за обработку и манипулирование квантовой информацией.

3. Выходной слой: Этот слой выполняет измерения на кубитах, обеспечивая окончательный ожидаемый результат.

Для входного слоя мы используем метод кодирования тензорного произведения. Он включает в себя простой вентиль вращения по оси X для каждого кубита, где параметр элемента задается путем масштабирования классических данных до диапазона [-π, π]. Несмотря на то, что это быстрый и простой метод кодирования (O(1)), у него есть ограничения. Количество необходимых кубитов линейно масштабируется в зависимости от входных классических данных. Чтобы решить эту проблему, мы вводим обучаемые параметры для масштабирования и смещения во входных данных, повышая гибкость квантового внедрения. На рисунке 3 показан пример входного слоя для сети с 3 кубитами, где классические данные:

, входные параметры шкалы:

и параметры смещения:

вступают в игру.

Создание квантовых внедрений

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

В нашем проекте мы используем анзац реальных амплитуд, выбор, вдохновленный его успехом в различных областях, таких как оценка политики для обучения и классификации с квантовым подкреплением. Этот анзац инициируется с полным вращением параметризованных вентилей X/Y/Z, сродни квантовой версии весовых коэффициентов связи. Затем следует серия элементов CNOT, расположенных в кольцевой структуре для облегчения передачи информации кубита. На рисунке 4 представлено визуальное представление того, как реализован этот анзац, служащий квантовым эквивалентом сетевого уровня для 3-кубитной сети.

Если говорить прямо, то квантовый сетевой слой в нашей работе включает в себя набор параметров, в сумме в 3 раза превышающих число кубитов (3*n), где ‘n’ представляет собой количество кубитов в квантовой сети.

Автодром Анстаз

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

Выход сети определяется путем вычисления математического ожидания этого наблюдаемого над нашим квантовым состоянием. Это выражается как ⟨ψ|σ_z|ψ⟩, где ⟨ψ| обозначает комплексную сопряженность |ψ⟩. Результат находится в диапазоне [-1, 1].

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

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

Обучение предлагаемой нами квантовой нейронной сети (QNN) происходит на обычном процессоре с использованием классических алгоритмов, таких как оптимизатор Адама. Центральный процессор обрабатывает вычисления градиента с помощью традиционных правил распространения, в то время как на квантовом процессоре (QPU) мы вычисляем градиент с помощью правила сдвига параметров. Это немного похоже на двойную систему, в которой центральный процессор управляет основным обучением, а QPU вступает в игру для конкретных квантовых вычислений.

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

Поскольку квантовая нейронная сеть (QNN) работает как модель прямого распространения, наш начальный шаг включает в себя определение временного горизонта, обозначаемого как T. Чтобы адаптировать данные временных рядов для QNN, мы преобразуем их в табличный формат. Здесь целью является значение временного ряда в момент времени t, обозначаемое как x(t), в то время как входные данные охватывают значения x(t-1), x(t-2), …, x(t-T). Такая реструктуризация облегчает понимание моделью временных отношений в данных, позволяя делать прогнозы на основе прошлых значений.

Формирование QNN с помощью Qiskit

Извлечение данных и их предварительная обработка

Во-первых, мы получаем данные с помощью исторической конечной точки API данных, предоставляемой Financial Modeling Prep следующим образом:api_url = «https://financialmodelingprep.com/api/v3/historical-price-full/AAPL?apikey=YOUR API KEY»

# Make a GET request to the API
response = requests.get(api_url)

# Check if the request was successful (status code 200)
if response.status_code == 200:
# Parse the response JSON
data = response.json()
else:
print(f»Error: Unable to fetch data. Status code: {response.status_code}»)

df = pd.json_normalize(data, ‘historical’, [‘symbol’]) #convert into a datframe
df.tail()

На выходе получается кадр данных Pandas, который выглядит примерно так (перед этим обязательно замените YOUR API YOUR API KEY на ваш секретный ключ API):

Исторические данные AAPL

Из этого изобилия данных мы будем использовать цену открытия в качестве временной переменной, и мы будем работать с 500 точками данных, каждая из которых представляет ежедневные цены открытия, и размер нашего окна для прогноза будет равен 2.final_data = df[[‘open’, ‘date’]][0:500] #forming filtered dataframe
input_sequences = []
labels = []

#Creating input and output data for time series forecasting
for i in range(len(final_data[‘open’])):
if i > 1:
labels.append(final_data[‘open’][i])
input_sequences.append(final_data[‘open’][i-2:i+1].tolist())

#creating train test split
x_train = np.array(input_sequences[0:400])
x_test = np.array(input_sequences[400:])
y_train = np.array(labels[0:400])
y_test = np.array(labels[400:])

Теперь давайте построим график полученных данных.import matplotlib.pyplot as plt
plt.style.use(‘ggplot’)

# Convert the ‘date’ column to datetime format
df[‘date’] = pd.to_datetime(df[‘date’])

# Plotting the time series data
plt.figure(figsize=(10, 6))
plt.plot(df[‘date’][0:500], df[‘open’][0:500], marker=’o’, linestyle=’-‘)

# Adding labels and title
plt.xlabel(‘date’)
plt.ylabel(‘open’)
plt.title(‘Time Series Data’)

# Display the plot
plt.grid(True)
plt.show()

Ниже приведены выходные данные:

График исторических данных AAPL

Квантовая нейронная сеть

Характерная карта Паули:num_features = 2
feature_map = PauliFeatureMap(feature_dimension = num_features, reps = 2)
optimizer = ADAM(maxiter = 100)

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

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

Квантовая схема:def ans(n, depth):
qc = QuantumCircuit(n)
for j in range(depth):
for i in range(n):
param_name = f’theta_{j}_{i}’
theta_param = Parameter(param_name)
qc.rx(theta_param, i)
qc.ry(theta_param, i)
qc.rz(theta_param, i)
for i in range(n):
if i == n-1:
qc.cnot(i, 0)
else:
qc.cnot(i, i+1)
return qc

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

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

Инициализация канала Anstaz:ansatz = ans(num_features, 5) #anstaz(num_qubits=num_features, reps=5)

#creating train test split
x_train = np.array(input_sequences[0:400])
x_test = np.array(input_sequences[400:])
y_train = np.array(labels[0:400])
y_test = np.array(labels[400:])

Теперь перейдем к созданию вариационной квантовой цепи (VQC), которая функционирует как нейронная сеть. В эту схему мы включаем как анзац, определяющий структуру нашей квантовой нейронной сети, так и кодировку наших признаков. Для этого мы импортируем модуль VQC из qiskit_machine_learning.classifiers. VQC инкапсулирует аспекты квантовой обработки нашей нейронной сети, а его интеграция с Qiskit упрощает реализацию алгоритмов квантового машинного обучения.

ВКК:vqr = VQR(
feature_map = feature_map,
ansatz = ansatz,
optimizer = optimizer,
)

vqr.fit(x_train,y_train)
vqr_mse = mean_squared_error(y_test, vqr.predict(x_test))

# Calculate root mean squared error
vqr_rmse = np.sqrt(vqr_mse)

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

Классическая нейронная сеть

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

Классическая ИНС:model = Sequential()
model.add(Dense(64,activation = ‘relu’, input_shape = (4,)))
model.add(Dense(1))

model.compile(optimizer = ‘adam’, loss = ‘mean_squared_error’)

model.fit(x_train, y_train, epochs = 20, batch_size = 32, validation_data = (x_test,y_test))

loss = model.evaluate(x_test, y_test)
prediction = model.predict(x_test)

ann_mse = mean_squared_error(y_test, prediction.flatten())
ann_rmse = np.sqrt(ann_mse)

Результат и сравнение

Ниже приведены результаты:
1)QNN: < 3,5 RMSE
2)ANN: > 3.6 RMSE

В этой оценке очевидно, что квантовая нейронная сеть (QNN) показала многообещающие результаты, превзойдя искусственную нейронную сеть (ИНС). Тем не менее, важно признать, что полученные значения среднеквадратичной ошибки (RMSE) могут не соответствовать желаемому уровню удовлетворенности. Основная цель этого эксперимента заключалась в том, чтобы подчеркнуть потенциал квантовых вычислений, продемонстрировав их способность генерировать превосходные результаты и создавать модели, применимые для коммерческого использования.

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

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

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

Заключение

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

Прогнозирование цен на акции с подразумеваемой волатильностью

Что такое подразумеваемая волатильность?

Прежде всего, что такое подразумеваемая волатильность?

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

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

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

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

Например, опционный контракт на акции стоимостью 20 долларов США с подразумеваемой волатильностью 10% указывает на то, что в 68% случаев базовая цена акций должна составлять от 18 до 22 долларов США по истечении срока действия.

Почему подразумеваемая волатильность влияет на премии по опционам?

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

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

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

Какие факторы влияют на подразумеваемую волатильность?

Теперь мы знаем, что более высокая подразумеваемая волатильность означает более высокую премию за контракт, но что вызывает рост подразумеваемой волатильности?

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

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

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

Использование API опционов Intrinio в режиме реального времени для прогнозирования цен на акции

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

Я проведу вас через это — шаг за шагом.

Шаг 1: Получение необходимых данных о запасах и опционах

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

Эти функции используют API Intrinio для извлечения этих входных данных для нашей формулы. Функция _latest_stock_price возвращает последнюю цену акций для данной акции.

С полным текстом кода можно ознакомиться здесь.

Функция _options_expirations_list возвращает список всех предстоящих дат истечения срока действия акции. Количество экспираций опционов может широко варьироваться в зависимости от самой акции.

Популярные акции, такие как SPDR S & P 500 ETF Trust (SPY), имеют ежедневные, еженедельные, ежемесячные и годовые даты истечения срока действия, тогда как менее популярные ценные бумаги, такие как Realty Income Corporation (O) и другие менее ликвидные, менее волатильные и непопулярные акции, могут иметь всего несколько дат истечения срока действия.

С полным текстом кода можно ознакомиться здесь.

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

С полным текстом кода можно ознакомиться здесь.

Шаг 2: Рассчитайте верхний и нижний ценовой диапазон для каждой ценной бумаги

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

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

Математика, происходящая в нашей _stock_standard_deviation_range, относительно проста. Сначала мы используем пакет DateTime Python, чтобы определить количество дней до истечения срока действия, а затем делим дни до истечения срока действия на календарные дни в году (365) и захватываем квадратный корень из этого числа.

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

Затем мы умножаем квадратный корень дифференциала дней экспирации на подразумеваемую волатильность для этой даты истечения срока действия и текущую цену исполнения опциона. Произведением этого конечного умножения является ожидаемое +/- однократное движение цены стандартного отклонения.

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

С полным текстом кода можно ознакомиться здесь.

Шаг 3: Итерация и вычисление прогнозируемого диапазона для всех экспираций.

Наша конечная функция _option_forecast_dataset собирает все части вместе и будет перебирать список дат истечения срока действия опционов для конкретного тикера.

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

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

С полным текстом кода можно ознакомиться здесь.

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

Кроме того, сравнивая прогнозируемые ценовые диапазоны AAPL с TSLA, вы можете увидеть влияние более высокой волатильности и сложность прогнозирования далекого будущего для высоковолатильных акций.

В заключение

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

Чтобы получить доступ к полному коду, обратитесь к нашему GitHub.

Прогнозирование цен на акции с помощью Lag-Llama Transformers

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

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

Знакомство с лаг-ламой

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

Архитектура Лаг-Ламы из бумаги

Lag-Llama учится выводить распределение по значениям следующего временного шага на основе запаздывающих входных объектов. Входными данными для модели является токен одномерного временного ряда i с заданным временным шагом x. Здесь c относится ко всем дополнительным ковариатам, используемым вместе со значением на временном шаге t, который включает в себя |Л| задержки, функции даты и времени F и сводная статистика. Входные сигналы проецируются через слои декодера M, замаскированные. Затем признаки пропускаются через распределительную головку и обучаются для прогнозирования параметров прогнозируемого распределения следующего временного шага.

Процесс токенизации с бумажных носителей

Lag-Llama выделяется своей надежной методологией предварительного обучения. Используя обширный корпус данных временных рядов из различных областей, Lag-Llama проходит предварительную тренировку для захвата сложных временных закономерностей. Такой подход наделяет Лаг-Ламу мощными возможностями обобщения с нулевым выстрелом.

Теперь давайте посмотрим, насколько хорошо Lag-Llama справляется с задачей прогнозирования цен на акции технологических гигантов на основе исторических данных.

Прогнозирование

Примечание: Все команды были протестированы в Google Colab. Замените «/content» на корневую папку. Используйте ‘!’ для выполнения команд bash в ячейках, где это необходимо.

Сначала клонируйте и установите необходимые пакеты из репозитория GitHub с моделью Lag-Llama.

git clone https://github.com/time-series-foundation-models/lag-llama/
cd /content/lag-llama
pip install -r requirements.txt --quiet

Затем загрузите предварительно обученные модельные веса с HuggingFace 🤗.

huggingface-cli download time-series-foundation-models/Lag-Llama lag-llama.ckpt --local-dir /content/lag-llama

Импортируйте необходимые пакеты и объект, который можно использовать для прогнозирования.LagLlamaEstimator

from itertools import islice

import matplotlib.dates as mdates
import pandas as pd
import torch
from gluonts.dataset.pandas import PandasDataset
from gluonts.dataset.repository.datasets import get_dataset
from gluonts.evaluation import Evaluator, make_evaluation_predictions
from lag_llama.gluon.estimator import LagLlamaEstimator
from matplotlib import pyplot as plt

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


def _get_lag_llama_predictions(dataset, prediction_length, num_samples=100):
ckpt = torch.load("lag-llama.ckpt", map_location=torch.device("cuda:0"))
estimator_args = ckpt["hyper_parameters"]["model_kwargs"]

estimator = LagLlamaEstimator(
ckpt_path="lag-llama.ckpt",
prediction_length=prediction_length,

# pretrained length
context_length=32,

# estimator arguments
input_size=estimator_args["input_size"],
n_layer=estimator_args["n_layer"],
n_embd_per_head=estimator_args["n_embd_per_head"],
n_head=estimator_args["n_head"],
scaling=estimator_args["scaling"],
time_feat=estimator_args["time_feat"],
batch_size=1,
num_parallel_samples=100,
)

lightning_module = estimator.create_lightning_module()
transformation = estimator.create_transformation()
predictor = estimator.create_predictor(transformation, lightning_module)

forecast_it, ts_it = make_evaluation_predictions(
dataset=dataset, predictor=predictor, num_samples=num_samples
)
forecasts = list(forecast_it)
tss = list(ts_it)

return forecasts, tss

Теперь установите Yahoo Finance для загрузки цен на акции.

# using the latest version available
pip install yfinance==0.2.37

Затем загрузите цены закрытия по указанным биржевым символам.

import yfinance as yf


# adjust the tickers, period, and frequency as needed
stock_prices = (
yf.Tickers("aapl amd amzn crm goog meta msft nvda tsla")
.history(period="max", start="2013-01-01")
.Close
.ffill()
)
stock_prices

Типа того.

Сглаживайте цены и рассчитывайте доходность.

# adjust the smoothing window as needed
stock_returns = stock_prices.rolling(5).mean().pct_change().dropna()

Создайте набор данных.

def _get_lag_llama_dataset(dataset):
# avoid mutations
dataset = dataset.copy()

# convert numerical columns to `float32`
for col in dataset.columns:
if dataset[col].dtype != "object" and not pd.api.types.is_string_dtype(
dataset[col]
):
dataset[col] = dataset[col].astype("float32")

# create a `PandasDataset`
backtest_dataset = PandasDataset(dict(dataset))
return backtest_dataset


backtest_dataset = _get_lag_llama_dataset(dataset=stock_returns)
prediction_length = 60 # prediction length
num_samples = 1060 # sampled from the distribution for each timestep

И выполнить вывод с нулевым выстрелом.

forecasts, tss = _get_lag_llama_predictions(
backtest_dataset, prediction_length, num_samples
)

Теперь постройте прогнозы модели на этом наборе данных.

plt.figure(figsize=(20, 15))
date_formater = mdates.DateFormatter("%b, %d")
plt.rcParams.update({"font.size": 15})

# iterate through the series, plot the predicted samples
for idx, (forecast, ts) in islice(enumerate(zip(forecasts, tss)), 9):
ax = plt.subplot(3, 3, idx + 1)

plt.plot(
ts[(-4 * prediction_length):].to_timestamp(),
label="target",
)
forecast.plot(color="g")
plt.xticks(rotation=60)
ax.xaxis.set_major_formatter(date_formater)
ax.set_title(forecast.item_id)

plt.gcf().tight_layout()
plt.legend()
plt.show()

Ожидайте этого.

Целевая доходность акций (синяя) и прогнозируемая (зеленая)

Или с точки зрения кумулятивной доходности или прибылей и убытков (pnl).

Целевой pnl акций (синий) в сравнении с прогнозируемым (зеленый)

Но это еще не все.

Тестирование на истории

Прогнозы — это хорошо, но что, если бы мы торговали ими? Проведем тестирование модели на истории с использованием подхода скользящего окна, где каждый квартал веса портфеля сбрасываются. Затем мы запускаем модель прогнозирования доходности в следующем квартале. Если прогнозируемая совокупная доходность больше нуля, покупаем актив; Если меньше, продаем. Конечные веса пересчитываются в единицу в абсолютном выражении. Учитываются комиссии и короткие сборы.

Тогда выясняется, что нулевое прогнозирование в трейдинге имеет мало смысла.

Торговля с нуля

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

Веса больше нуля, пересчитанные в единицу.

Но это не всё. Попробуем усовершенствовать модель с помощью тонкой настройки.

Тонкая настройка

Выполните тонкую настройку базовой модели Lag-Llama с помощью нескольких изменений, связанных с данными.

ckpt = torch.load("lag-llama.ckpt", map_location="cuda")
estimator_args = ckpt["hyper_parameters"]["model_kwargs"]

estimator = LagLlamaEstimator(
ckpt_path="lag-llama.ckpt",
prediction_length=prediction_length,
context_length=32,

# adjust as needed

# distr_output="neg_bin",
# scaling="mean",
nonnegative_pred_samples=True,
aug_prob=0,
lr=5e-4,

# estimator args
input_size=estimator_args["input_size"],
n_layer=estimator_args["n_layer"],
n_embd_per_head=estimator_args["n_embd_per_head"],
n_head=estimator_args["n_head"],
time_feat=estimator_args["time_feat"],

# rope_scaling={
# "type": "linear",
# "factor": max(
# 1.0, (context_length + prediction_length) / estimator_args["context_length"]
# ),
# },

batch_size=64,
num_parallel_samples=num_samples,
trainer_kwargs={
"max_epochs": 50,
}, # <- lightning trainer arguments
)

Обучите предиктор.

# e.g., using the first 5 years of data
# we will then test on the rest of the dataset
train_dataset = _get_lag_llama_dataset(stock_returns.iloc[:252*5])
predictor = estimator.train(
train_dataset,
cache_data=True,
shuffle_buffer_length=1000,
)

Обновите функцию прогнозирования с помощью точно настроенного предиктора.

def _get_lag_llama_predictions(
dataset, prediction_length, num_samples=100, predictor=None,
):
ckpt = torch.load(
"lag-llama.ckpt", map_location=torch.device("cuda:0")
)
estimator_args = ckpt["hyper_parameters"]["model_kwargs"]

estimator = LagLlamaEstimator(
ckpt_path="lag-llama.ckpt",
prediction_length=prediction_length,

# pretrained length
context_length=32,

# estimator args
input_size=estimator_args["input_size"],
n_layer=estimator_args["n_layer"],
n_embd_per_head=estimator_args["n_embd_per_head"],
n_head=estimator_args["n_head"],
scaling=estimator_args["scaling"],
time_feat=estimator_args["time_feat"],
batch_size=1,
num_parallel_samples=100,
)

lightning_module = estimator.create_lightning_module()
transformation = estimator.create_transformation()
predictor = (
predictor
if predictor is not None
else estimator.create_predictor(transformation, lightning_module)
)

forecast_it, ts_it = make_evaluation_predictions(
dataset=dataset, predictor=predictor, num_samples=num_samples
)
forecasts = list(forecast_it)
tss = list(ts_it)

return forecasts, tss

Теперь построим график прогнозов, сгенерированных тонко настроенной моделью, на тестовом наборе данных, например, после обучения на данных за первые 5 лет: .stock_returns.iloc[252*5:]

Целевая доходность акций (синяя) и прогнозируемая (зеленая)
Целевой pnl акций (синий) в сравнении с прогнозируемым (зеленый)

Теперь выглядит намного лучше. Давайте снова сделаем бэктест великолепным.

Тонкая настройка торговли

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

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

Источник

  1. Model Weights
  2. Colab Demo 1: Прогнозирование с нулевым выбросом
  3. Colab Demo 2: Предварительная тонкая настройка
  4. GitHub
  5. Paper

Прогнозирование цен на акции с помощью трансформеров

Введение: Эволюция прогнозирования цен на акции

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

Среди различных методов машинного обучения внедрение архитектур нейронных сетей ознаменовало значительный скачок в точности прогнозирования и возможностях обработки. В последнее время трансформеры, лежащие в основе больших языковых моделей (LLM), стали передовой технологией в этой области. Первоначально разработанные для задач обработки естественного языка, трансформеры превосходно справляются с последовательными данными благодаря своим сложным механизмам внимания. Это делает их исключительно подходящими для анализа временных рядов, таких как прогнозирование цен на акции, в которых традиционно доминируют сети Long Short-Term Memory (LSTM). Несмотря на то, что LSTM были основным продуктом благодаря своей способности фиксировать временные зависимости, они все чаще рассматриваются как менее эффективные по сравнению с возможностями параллельной обработки и масштабируемостью трансформеров.

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

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

Вы можете ознакомиться с моей записной книжкой для этого проекта здесь.
Давайте перейдем к коду!

Импорт библиотек

import math
import numpy as np
import pandas as pd

import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.callbacks import ModelCheckpoint
from tensorflow.keras.layers import Input, Dense, Dropout, LayerNormalization, MultiHeadAttention, Add, GlobalAveragePooling1D

import yfinance as yf

Использование финансовых индикаторов для расширенного прогнозного анализа

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

Полосы Боллинджера

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

Индекс относительной силы (RSI)

Индекс относительной силы (RSI) — это осциллятор импульса, который измеряет скорость и изменение движения цены. RSI колеблется между нулем и 100. Традиционно, по словам Уайлдера, RSI считается перекупленным, когда выше 70, и перепроданным, когда ниже 30. Этот индикатор помогает трейдерам определять потенциальные точки разворота, основываясь на внутренней силе движения цены актива.

Скорость изменения (ROC)

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

def calculate_bollinger_bands(data, window=10, num_of_std=2):
"""Calculate Bollinger Bands"""
rolling_mean = data.rolling(window=window).mean()
rolling_std = data.rolling(window=window).std()
upper_band = rolling_mean + (rolling_std * num_of_std)
lower_band = rolling_mean - (rolling_std * num_of_std)
return upper_band, lower_band

def calculate_rsi(data, window=10):
"""Calculate Relative Strength Index"""
delta = data.diff()
gain = delta.clip(lower=0)
loss = -delta.clip(upper=0)
avg_gain = gain.rolling(window=window, min_periods=1).mean()
avg_loss = loss.rolling(window=window, min_periods=1).mean()
rs = avg_gain / avg_loss
rsi = 100 - (100 / (1 + rs))
return rsi

def calculate_roc(data, periods=10):
"""Calculate Rate of Change."""
roc = ((data - data.shift(periods)) / data.shift(periods)) * 100
return roc

Фокус на Big Tech

В быстро развивающемся технологическом секторе такие компании, как Meta, Apple, Microsoft, Amazon и Alphabet (обычно называемые MAMAA), выделяются не только своим размером, но и значительным влиянием на мировые рынки. Фокусируя нашу прогностическую модель на этих лидерах отрасли, мы стремимся уловить тенденции и закономерности, наиболее актуальные для крупного технологического сектора. Этот стратегический выбор позволяет нам разработать модель, которая может быть обобщена для компаний с аналогичным положением, предлагая понимание движений некоторых из наиболее влиятельных игроков на фондовом рынке.

# List of tickers for the big tech companies, forming the MAMAA group
tickers = ['META', 'AAPL', 'MSFT', 'AMZN', 'GOOG']

Стандартизация и улучшение данных для обеспечения точности прогнозирования

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

Обработка данных

Извлечение данных: Мы загружаем исторические данные по акциям за 60 дней с 5-минутными интервалами с помощью yfinance. Эти данные с высоким разрешением помогают фиксировать внутридневную волатильность и тенденции.

Проектирование признаков:

  • Полосы Боллинджера: Рассчитываем полосы Боллинджера для оценки волатильности. Ширина между верхней и нижней полосами (width) используется в качестве признака, дающего представление о волатильности рынка.
  • Цена закрытия и объем: Мы напрямую используем цену закрытия и объем торгов в качестве характеристик, которые являются основополагающими для большинства торговых моделей.
  • Разница в цене: Мы вычисляем разницу (diff) между текущей ценой закрытия и предыдущим закрытием, фиксируя непосредственные движения цены.
  • Процентное изменение: Процентное изменение цены закрытия по сравнению с предыдущим закрытием (percent_change_close) выделяет скорость изменения цены, служа индикатором импульса.

Нормализация:

  • Чтобы убедиться, что все признаки вносят равный вклад в процесс обучения модели, мы стандартизируем каждый признак, вычитая среднее значение и деля его на стандартное отклонение (mean std).
  • Мы сохраняем эти статистические параметры (среднее значение и стандартное отклонение) для каждого признака. Это имеет решающее значение для последующего «размасштабирования» прогнозов модели, чтобы интерпретировать их в исходных масштабах.
ticker_data_frames = []
stats = {}
for ticker in tickers:

# Download historical data for the ticker
data = yf.download(ticker, period="60d", interval="5m")

# Calculate the daily percentage change
close = data['Close']
upper, lower = calculate_bollinger_bands(close, window=14, num_of_std=2)
width = upper - lower
rsi = calculate_rsi(close, window=14)
roc = calculate_roc(close, periods=14)
volume = data['Volume']
diff = data['Close'].diff(1)
percent_change_close = data['Close'].pct_change() * 100

# Create a DataFrame for the current ticker and append it to the list
ticker_df = pd.DataFrame({
ticker+'_close': close,
ticker+'_width': width,
ticker+'_rsi': rsi,
ticker+'_roc': roc,
ticker+'_volume': volume,
ticker+'_diff': diff,
ticker+'_percent_change_close': percent_change_close,
})

MEAN = ticker_df.mean()
STD = ticker_df.std()

# Keep track of mean and std
for column in MEAN.index:
stats[f"{column}_mean"] = MEAN[column]
stats[f"{column}_std"] = STD[column]

# Normalize the training features
ticker_df = (ticker_df - MEAN) / STD

ticker_data_frames.append(ticker_df)

Организация статистических данных для ссылки на модель

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

Хранение и просмотр статистики

Статистические параметры — среднее значение и стандартное отклонение — для каждого признака изначально хранятся в словаре для удобства доступа и манипуляций. Чтобы упростить использование этих параметров на этапах обучения и прогнозирования нашей модели, мы преобразуем этот словарь в Pandas DataFrame. Это преобразование не только структурирует данные, но и облегчает быстрый поиск и операции со статистическими данными.

# Convert the dictionary containing feature statistics to a DataFrame for easier access
stats = pd.DataFrame([stats], index=[0])

# Display the DataFrame to verify its structure
stats.head()

Консолидация данных для комплексного анализа

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

Консолидация и очистка данных

  1. Конкатенация: Мы объединяем кадры данных для каждого тикера по столбцам. Это означает, что функции каждого тикера будут располагаться рядом, что позволит нашей модели получить доступ ко всем релевантным точкам данных по разным акциям в рамках одной и той же входной модели.
  2. Работа с бесконечными значениями: При обработке данных, особенно после нормализации, можно столкнуться с бесконечными значениями из-за деления на ноль или других аномалий. Мы заменяем эти бесконечные значения на NaN (Not a Number), чтобы они не влияли на последующие вычисления.
  3. Удаление отсутствующих данных: Затем мы удаляем все строки, содержащие значения NaN. Этот шаг имеет решающее значение для поддержания качества набора данных, гарантируя, что модель обучается только на полных и точных данных.
  4. Предварительный просмотр данных: Наконец, чтобы убедиться, что наши данные хорошо структурированы и готовы к моделированию, мы выводим первые несколько строк. Этот предварительный просмотр помогает убедиться в том, что процессы выравнивания и очистки данных были выполнены правильно.
df = pd.concat(ticker_data_frames, axis=1)
df.replace([np.inf, -np.inf], np.nan, inplace=True)
df.dropna(inplace=True)
df.head()

Подготовка меток данных для прогнозного моделирования

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

Создание меток и выравнивание данных

  1. Создание этикеток: Мы создаем метки, сдвигая весь DataFrame вверх на одну позицию. Этот сдвиг выравнивает каждую строку с данными последующего временного шага, которые модель попытается спрогнозировать.
  2. Обеспечение целостности данных: После сдвига данных последняя строка в DataFrame не будет иметь соответствующей будущей точки данных, которая могла бы выступать в качестве метки. Чтобы сохранить целостность данных и избежать обучения на неполных данных, мы удаляем последнюю строку как из набора признаков (df), так и из меток.
# Shift the dataframe up by one to align current features with the next step's outcomes
labels = df.shift(-1)

# Remove the last row from both the features and labels to maintain consistent data pairs
df = df.iloc[:-1]
labels = labels.iloc[:-1]

Структурирование данных в последовательности для прогнозирования временных рядов

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

Создание последовательности и подготовка этикеток

  1. Определение длины последовательности: Мы определяем длину последовательности равной 24, что соответствует 2 часам данных с 5-минутными интервалами. Эта длина выбрана для того, чтобы предоставить модели достаточно свежих исторических данных для обнаружения краткосрочных трендов и закономерностей.
  2. Генерация последовательности и меток:
  • Функция create_sequences конструирует последовательности заданной длины и подготавливает соответствующие метки. Мы стремимся предсказать движение цены акций через час после окончания каждой последовательности, поэтому дополнительные 12 шагов (представляющие 60 минут) выходят за пределы длины последовательности.
  • Каждый массив надписей включает в себя не только целевое значение, но и среднее значение и стандартное отклонение набора признаков. Это включение имеет решающее значение для более поздних этапов, когда нам нужно масштабировать прогнозы до их первоначального масштаба для точной интерпретации и оценки

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

SEQUENCE_LEN = 24  # 2 hours of data at 5-minute intervals

def create_sequences(data, labels, mean, std, sequence_length=SEQUENCE_LEN):
sequences = []
lab = []
data_size = len(data)

# Loop to create each sequence and its corresponding label
for i in range(data_size - (sequence_length + 13)): # Ensure we have data for the label
if i == 0:
continue
sequences.append(data[i:i + sequence_length]) # The sequence of data
lab.append([labels[i-1], labels[i + 12], mean[0], std[0]]) # The label and scaling factors

return np.array(sequences), np.array(lab)

Генерация обучающих последовательностей для каждого тикера

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

Генерация последовательности для каждого тикера

  1. Извлечение данных: Для каждого тикера мы извлекаем несколько признаков из DataFrame. Эти функции включают цену закрытия, ширину полосы Боллинджера, RSI, ROC, объем торгов, разницу в ценах закрытия и процентное изменение цен закрытия. Это ключевые индикаторы, которые могут помочь предсказать движение цен на акции.
  2. Объединение данных: Извлеченные данные объединяются в единый массив для каждого тикера. Эта комбинация позволяет нам более эффективно обрабатывать данные и гарантирует, что все соответствующие функции будут включены в процесс генерации последовательности.
  3. Создание последовательности и метки:
  • Используя ранее определенную create_sequences, мы генерируем последовательности объединенных данных. Эта функция также подготавливает соответствующие метки, используя будущую цену закрытия в качестве цели для прогнозирования.
  • Последовательности включают коэффициенты масштабирования (среднее значение и стандартное отклонение), гарантируя, что мы можем вернуть прогнозы модели к исходной шкале для точной оценки и интерпретации.
sequences_dict = {}
sequence_labels = {}
for ticker in tickers:

# Extract close and volume data for the ticker
close = df[ticker+'_close'].values
width = df[ticker+'_width'].values
rsi = df[ticker+'_rsi'].values
roc = df[ticker+'_roc'].values
volume = df[ticker+'_volume'].values
diff = df[ticker+'_diff'].values
pct_change = df[ticker+'_percent_change_close'].values

# Combine close and volume data
ticker_data = np.column_stack((close,
width,
rsi,
roc,
volume,
diff,
pct_change))

# Generate sequences
attribute = ticker+"_close"
ticker_sequences, lab = create_sequences(ticker_data,
labels[attribute].values[SEQUENCE_LEN-1:],
stats[attribute+"_mean"].values,
stats[attribute+"_std"].values)

sequences_dict[ticker] = ticker_sequences
sequence_labels[ticker] = lab

Агрегирование данных по тикакерам для унифицированного обучения модели

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

Агрегирование последовательностей и меток

Ниже приведена пошаговая разбивка того, как объединить последовательности и метки из всех тикеров в консолидированный набор данных для обучения:

  1. Агрегация данных: Перебирая каждый тикер, мы дополняем эти списки последовательностями и метками, сгенерированными для каждой акции, эффективно объединяя все данные в единый набор данных.
  2. Преобразование в массивы numpy: После агрегирования данных мы преобразуем списки последовательностей и меток в массивы Numpy. Это преобразование имеет решающее значение для совместимости с TensorFlow и Keras, которые предпочитают входные данные массива Numpy для обучения и оценки модели. Это также способствует эффективному обращению и вычислениям во время тренировочного процесса.
# Combine data and labels from all tickers
all_sequences = []
all_labels = []

for ticker in tickers:
all_sequences.extend(sequences_dict[ticker])
all_labels.extend(sequence_labels[ticker])

# Convert to numpy arrays
all_sequences = np.array(all_sequences)
all_labels = np.array(all_labels)

Подготовка разбиений набора данных для обучения и оценки модели

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

Перетасовка и разделение набора данных

  1. Установка случайного начального числа: Мы устанавливаем случайное начальное значение для обеспечения воспроизводимости. Это означает, что случайные процессы будут вести себя одинаково при каждом запуске кода, что важно для отладки и сравнения производительности модели при разных запусках.
  2. Перетасовка данных: Мы перемешиваем последовательности и соответствующие им метки, чтобы рандомизировать порядок. Такая рандомизация помогает предотвратить любые систематические ошибки, которые могут возникнуть из-за порядка, в котором данные были первоначально обработаны или собраны.
  3. Разделение данных: Сначала мы определяем размер обучающей выборки как 90% от общего объема данных. Оставшиеся 10% будут разделены на валидационные и тестовые наборы.
  4. Создание валидационных и тестовых наборов: Проверочный набор устанавливается равным 50 % от оставшихся данных. Остальное зарезервировано в качестве тестового набора. Такое разделение обеспечивает хороший баланс, позволяя получить достаточно данных как для проверки, так и для окончательной оценки.
np.random.seed(42)
shuffled_indices = np.random.permutation(len(all_sequences))
all_sequences = all_sequences[shuffled_indices]
all_labels = all_labels[shuffled_indices]

train_size = int(len(all_sequences) * 0.9)

# Split sequences
train_sequences = all_sequences[:train_size]
train_labels = all_labels[:train_size]

other_sequences = all_sequences[train_size:]
other_labels = all_labels[train_size:]

shuffled_indices = np.random.permutation(len(other_sequences))
other_sequences = other_sequences[shuffled_indices]
other_labels = other_labels[shuffled_indices]

val_size = int(len(other_sequences) * 0.5)

validation_sequences = other_sequences[:val_size]
validation_labels = other_labels[:val_size]

test_sequences = other_sequences[val_size:]
test_labels = other_labels[val_size:]

Теперь, когда мы подготовили наши данные, давайте построим нашу модель трансформатора

Построение модели трансформатора для прогнозирования временных рядов

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

Архитектура модели трансформатора

Функция энкодера трансформатора:

  • Функция энкодера составляет основу нашей модели Transformer. Он состоит из нескольких ключевых операций, которые обрабатывают входные последовательности.
  • Нормализация слоя: нормализует входной слой для ускорения обучения и стабилизации сети.
  • Multi-Head Attention: Позволяет модели совместно обрабатывать информацию из разных подпространств представления в разных позициях, захватывая различные аспекты входных данных.
  • Feed Forward Network: Простая нейронная сеть, применяемая к каждой позиции отдельно и идентично, расширяющая возможности модели по обработке сложных шаблонов.
def transformer_encoder(inputs, head_size, num_heads, ff_dim, dropout=0):
# Attention and Normalization
x = LayerNormalization(epsilon=1e-6)(inputs)
x = MultiHeadAttention(key_dim=head_size, num_heads=num_heads, dropout=dropout)(x, x)
x = Add()([x, inputs])

# Feed Forward Part
y = LayerNormalization(epsilon=1e-6)(x)
y = Dense(ff_dim, activation="relu")(y)
y = Dropout(dropout)(y)
y = Dense(inputs.shape[-1])(y)
return Add()([y, x])

Построение полной модели:

  • Модель построена путем наложения нескольких слоев трансформаторного энкодера для углубления сети, что повышает ее способность обучаться на данных.
  • Слой Global Average Pooling следует за блоками Transformer для уменьшения размерности выходных данных, делая модель более управляемой и фокусируясь на наиболее важных функциях.
  • Последний выходной слой представляет собой плотный слой с линейной функцией активации, предназначенный для прогнозирования непрерывных значений, таких как цены на акции.
def build_transformer_model(input_shape, head_size, num_heads, ff_dim, num_layers, dropout=0):
inputs = Input(shape=input_shape)
x = inputs
for _ in range(num_layers):
x = transformer_encoder(x, head_size, num_heads, ff_dim, dropout)
x = GlobalAveragePooling1D()(x)
x = LayerNormalization(epsilon=1e-6)(x)
outputs = Dense(1, activation="linear")(x)
return Model(inputs=inputs, outputs=outputs)

Параметры модели и компиляция:

  • Мы указываем параметры модели, такие как входная форма, размер головки, количество головок, размерность сети прямой связи, количество слоев и частота выпадения.
  • Эти параметры имеют решающее значение, поскольку они определяют, насколько глубокой и сложной является модель, влияя на ее способность к обучению и обобщению.
input_shape = train_sequences.shape[1:]
head_size = 256
num_heads = 16
ff_dim = 1024
num_layers = 12
dropout = 0.20

model = build_transformer_model(input_shape, head_size, num_heads, ff_dim, num_layers, dropout)
model.summary()

Реализация пользовательской функции потерь и метрики точности

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

Пользовательская функция потерь MAE и точность направления

  1. Пользовательская функция потерь MAE: Пользовательская функция потерь MAE вычисляет среднюю абсолютную ошибку между прогнозируемым следующим значением и фактическим следующим значением. Эта функция потерь помогает свести к минимуму ошибку прогнозирования с точки зрения фактических значений цены.
  2. Метрика точности направления:
  • Эта метрика вычисляет точность, с которой модель предсказывает направление движения цены, а не точные значения. Он сравнивает знак истинного изменения цены (вверх или вниз) со знаком прогнозируемого изменения.
  • Метрика важна для торговых стратегий, где правильное предсказание направления движения цены может принести значительную прибыль.
  • Примечание: Точность направления — это причина, по которой мы должны иметь среднее значение и стандартное отклонение с нашей меткой.
def custom_mae_loss(y_true, y_pred):
y_true_next = tf.cast(y_true[:, 1], tf.float64) # Extract the true next values, scaled
y_pred_next = tf.cast(y_pred[:, 0], tf.float64) # Extract the predicted next values, scaled
abs_error = tf.abs(y_true_next - y_pred_next) # Calculate the absolute error
return tf.reduce_mean(abs_error) # Return the mean of these errors

def dir_acc(y_true, y_pred):
mean, std = tf.cast(y_true[:, 2], tf.float64), tf.cast(y_true[:, 3], tf.float64) # Retrieve scaling factors
y_true_prev = (tf.cast(y_true[:, 0], tf.float64) * std) + mean # Un-scale previous true price
y_true_next = (tf.cast(y_true[:, 1], tf.float64) * std) + mean # Un-scale next true price
y_pred_next = (tf.cast(y_pred[:, 0], tf.float64) * std) + mean # Un-scale predicted next price

true_change = y_true_next - y_true_prev # Calculate true change
pred_change = y_pred_next - y_true_prev # Calculate predicted change

correct_direction = tf.equal(tf.sign(true_change), tf.sign(pred_change)) # Check if the signs match
return tf.reduce_mean(tf.cast(correct_direction, tf.float64)) # Return the mean of correct directions

Оптимизация обучения с помощью обратных вызовов и планирования темпов обучения

Чтобы максимизировать производительность нашей модели Transformer и обеспечить надежное обучение, мы реализуем несколько обратных вызовов TensorFlow. К ним относятся механизмы сохранения наиболее эффективных моделей и динамической регулировки скорости обучения во время обучения. Такие методы помогают улучшить возможности сходимости и обобщения модели.

Обратные вызовы контрольных точек модели

  1. Сохранение наилучшей модели обучения: Мы используем ModelCheckpoint для автоматического сохранения весовых коэффициентов модели, которые достигают наивысшей «точности направления» во время обучения. Это гарантирует, что мы сохраним наилучшую итерацию модели, потенциально предотвращая переобучение обучающих данных.
  2. Сохранение наилучшей модели валидации: Точно так же мы устанавливаем еще одну контрольную точку для этапа проверки. Это позволяет сохранить итерацию модели, которая лучше всего работает в проверочном наборе данных в соответствии с метрикой «точность направления». Это очень важно для настройки модели, чтобы она хорошо работала на невидимых данных.
  3. Динамическая регулировка скорости обучения:
  • Корректировка скорости обучения во время обучения может существенно повлиять на динамику обучения модели и конечную производительность. Мы реализуем настраиваемый планировщик скорости обучения, который:
  • Линейно разогревает скорость обучения на начальном этапе.
  • Содержит максимальную скорость обучения для фазы сохранения, если она определена.
  • Применяет косинусное затухание скорости обучения, чтобы плавно свести ее к минимуму, помогая модели точно настроить свои весовые коэффициенты в более поздние эпохи.
# Define a callback to save the best model
checkpoint_callback_train = ModelCheckpoint(
"transformer_train_model.keras", # Filepath to save the best model
monitor="dir_acc", #"loss", # Metric to monitor
save_best_only=True, # Save only the best model
mode="max", # Minimize the monitored metric
verbose=1, # Display progress
)

# Define a callback to save the best model
checkpoint_callback_val = ModelCheckpoint(
"transformer_val_model.keras", # Filepath to save the best model
monitor="val_dir_acc", #"val_loss", # Metric to monitor
save_best_only=True, # Save only the best model
mode="max", # Minimize the monitored metric
verbose=1, # Display progress
)

def get_lr_callback(batch_size=16, mode='cos', epochs=500, plot=False):
lr_start, lr_max, lr_min = 0.0001, 0.005, 0.00001 # Adjust learning rate boundaries
lr_ramp_ep = int(0.30 * epochs) # 30% of epochs for warm-up
lr_sus_ep = max(0, int(0.10 * epochs) - lr_ramp_ep) # Optional sustain phase, adjust as needed

def lrfn(epoch):
if epoch < lr_ramp_ep: # Warm-up phase
lr = (lr_max - lr_start) / lr_ramp_ep * epoch + lr_start
elif epoch < lr_ramp_ep + lr_sus_ep: # Sustain phase at max learning rate
lr = lr_max
elif mode == 'cos':
decay_total_epochs, decay_epoch_index = epochs - lr_ramp_ep - lr_sus_ep, epoch - lr_ramp_ep - lr_sus_ep
phase = math.pi * decay_epoch_index / decay_total_epochs
lr = (lr_max - lr_min) * 0.5 * (1 + math.cos(phase)) + lr_min
else:
lr = lr_min # Default to minimum learning rate if mode is not recognized

return lr

if plot: # Plot learning rate curve if plot is True
plt.figure(figsize=(10, 5))
plt.plot(np.arange(epochs), [lrfn(epoch) for epoch in np.arange(epochs)], marker='o')
plt.xlabel('Epoch')
plt.ylabel('Learning Rate')
plt.title('Learning Rate Scheduler')
plt.show()

return tf.keras.callbacks.LearningRateScheduler(lrfn, verbose=True)

Обучение модели трансформатора

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

Процесс обучения модели

  1. Настройка параметров обучения: Мы определяем BATCH_SIZE и EPOCHS, которые имеют решающее значение для определения того, как данные подаются в модель и сколько раз весь набор данных проходит через модель соответственно.
  2. Подгонка модели:
  • Для запуска процесса обучения используется метод fit на нашем модельном объекте. Указываем обучающие данные, соответствующие метки, проверочные данные и количество эпох.
  • Обратные вызовы, включая контрольные точки модели и корректировки скорости обучения, передаются для мониторинга и улучшения процесса обучения по мере необходимости.
BATCH_SIZE = 64  # Number of training examples used to calculate each iteration's gradient
EPOCHS = 100 # Total number of times the entire dataset is passed through the network

model.fit(
train_sequences, # Training features
train_labels, # Training labels
validation_data=(validation_sequences, validation_labels), # Validation data
epochs=EPOCHS, # Number of epochs to train for
batch_size=BATCH_SIZE, # Size of each batch
shuffle=True, # Shuffle training data before each epoch
callbacks=[checkpoint_callback_train, checkpoint_callback_val, get_lr_callback(batch_size=BATCH_SIZE, epochs=EPOCHS)] # Callbacks for saving models and adjusting learning rate
)

Оценка характеристик модели трансформатора

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

Оценка модели и расчет метрик

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

  1. Оценка модели на тестовых данных: Мы используем метод evaluate для оценки производительности модели на тестовом наборе данных. Этот метод вычисляет потери и любые другие метрики, которые мы определили ранее, такие как точность направления, в этом новом наборе данных.
  2. Расчет дополнительных метрик: Чтобы получить более полное представление о предсказательной силе модели, мы вычисляем статистику R-квадрат, которая дает меру того, насколько хорошо наблюдаемые результаты воспроизводятся моделью, на основе доли общего изменения результатов, объясняемых моделью.
model.load_weights("transformer_val_model.keras")  # Load the best model from the validation phase
accuracy = model.evaluate(test_sequences, test_labels)[1] # Evaluate the model on the test data
print(accuracy)

from sklearn.metrics import r2_score

predictions = model.predict(test_sequences) # Make predictions on the test dataset
r2 = r2_score(test_labels[:, 1], predictions[:, 0]) # Calculate R-squared value
print(f"R-squared: {r2}")

Заключение и дальнейшие направления

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

Достижения

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

Проблемы и ограничения

  • Аппаратные и временные ограничения: Несмотря на многообещающие улучшения, дальнейшее обучение было ограничено из-за ограничений в доступных вычислительных ресурсах и времени. Такие ограничения часто встречаются в реальных сценариях и должны быть спланированы соответствующим образом.
  • Сходимость: Модель не полностью сходилась в пределах выделенных 100 эпох, что позволяет предположить, что дополнительные эпохи могут привести к дальнейшему повышению точности.

Рекомендации для дальнейшей работы

  1. Расширенное обучение: Если позволяют ресурсы, в будущих экспериментах следует рассмотреть возможность увеличения количества эпох. Это может помочь модели полностью сойтись и потенциально выявить более тонкие закономерности в данных.
  2. Оптимизация аппаратного обеспечения: использование более мощных вычислительных ресурсов или облачных платформ может смягчить аппаратные ограничения, что позволит проводить более обширное обучение и эксперименты.
  3. Настройка гиперпараметров: эксперименты с различными архитектурами моделей, скоростями обучения и другими гиперпараметрами могут еще больше повысить производительность модели.
  4. Проектирование признаков: Включение дополнительных функций или эксперименты с различными комбинациями существующих индикаторов могут дать новые сведения и повысить точность прогнозирования.

Заключительные мысли

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

Вы можете ознакомиться с моей записной книжкой для этого проекта здесь.

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник