Обзор:

  • В этой статье объясняется моя попытка предсказать, пойдет ли цена акции вверх или вниз за один день, учитывая историческую цену закрытия за предыдущие 50 дней.
  • Я преобразовываю эти 50-дневные данные временных рядов с помощью грамианских угловых полей, чтобы сделать их интерпретируемыми CNN, и маркирую это изображение с направлением движения акций на 51-й день.
  • Хотя результаты посредственные, с точностью около 58%, это новый метод решения вечной проблемы.

Примечания и предварительные условия:

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

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

from fastai.vision import *
import requests
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
from pyts.image import GramianAngularField
import pandas as pd
import numpy as np
import os.path

Настройка конвейера данных:

Данные → фрагменты временных рядов → создание изображений → помеченные изображения

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

Мы собираемся получать ежедневные данные для индекса Dow Jones с начала 2000 года. С помощью этого API от alphavantage мы получаем следующие поля: timestamp, o pen, high, low, close, и объем.

import requests
TICKER = 'DJI'
API_KEY = 'MAKE YOUR OWN ACCOUNT'
url = 'https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol='+ TICKER +'&interval=5min&apikey='+API_KEY+'&outputsize=full&datatype=csv'
r = requests.get(url, allow_redirects=True)
open('daily_'+TICKER+'.csv', 'wb').write(r.content)

Давайте быстро взглянем на данные

X = pd.read_csv('daily_'+TICKER+'.csv', index_col='timestamp')
X = X.iloc[::-1]
X.head()

Образцы фрагментов:

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

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

X.head()
n_samples, n_timestamps = 1000, 50
valid_days, valid_samples = 500, n_samples // 5
startIdx_train = np.random.randint(0, len(X) - n_timestamps - valid_days, n_samples)
df_train = []
for x in startIdx_train:
    a = X.close[x: x + n_timestamps].to_numpy()
    df_train.append(a)
df_train = np.array(df_train)
startIdx_valid = np.random.randint(len(X) - n_timestamps - valid_days, len(X) - n_timestamps , valid_samples)
df_valid = []
for x in startIdx_valid:
    a = X.close[x: x + n_timestamps].to_numpy()
    df_valid.append(a)
df_valid = np.array(df_valid)

Преобразование в изображения:

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

gasf = GramianAngularField(image_size=50, method='summation')
X_gasf_train = gasf.fit_transform(df_train)

Преобразование этого:

В это:

Пометка изображений:

Наконец, нам нужно настроить наши папки и пометить наши изображения. Мы используем каталог типа ImageNet, в котором есть папки test и valid с подпапками для двух классов: вверх и вниз. .

def getMvmt(start, end):
    if end > start:
        return "up"
    return "down"
cmap = 'rainbow'
for i in range(0,len(df_train)):
    direction = getMvmt(X.close[startIdx[i] + 49], X.close[startIdx[i] + 50])
    plt.figure(figsize=(8, 8))
    plt.imshow(X_gadf_train[i], cmap=cmap, origin='lower')
    plt.axis('off')
    plt.savefig(os.path.join("data/stocks",TICKER,"train",direction,"img" + str(i)), transparent=True, bbox_inches='tight')
    plt.close()

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

Загрузка данных:

При создании этой модели я полностью полагаюсь на библиотеку fastai.vision. Сначала мы устанавливаем путь и загружаем изображения в ImageDataBunch.

path = Path("data/stocks/"+TICKER)
data = ImageDataBunch.from_folder(path, ds_tfms=None, size=224, num_workers=4, bs=32).normalize(imagenet_stats)
data.classes

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

Обучение учащегося:

Мы создаем обучаемого на основе архитектуры ResNet50 и используем точность в качестве метрики. За 19 лет работы индекс DJI растет в 53% случаев, так что это наш базовый уровень точности.

learn.fit_one_cycle(4)

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

learn.unfreeze()
learn.lr_find()
learn.recorder.plot()
learn.fit_one_cycle(2, max_lr=slice(1e-6,3e-5))

После этой тонкой настройки мы все еще получаем уровень точности около 58–59%, что выше нашего базового уровня, но не слишком велик по сравнению с другими методами, поскольку SOTA для этого типа прогнозирования находится в середине 60-х годов. %.

Движение вперед:

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

Кредит:

  • Fast.ai за уроки и библиотеку.
  • Oguiza для своей записной книжки с оливковым маслом
  • Alphavantage для их набора данных