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

Другие сообщения:

  1. Простое прогнозирование временных рядов (и сделанные ошибки)
  2. Корректное прогнозирование одномерных временных рядов + бэктестинг
  3. Многомерное прогнозирование временных рядов
  4. Прогнозирование волатильности и нестандартные убытки
  5. Многозадачное и мультимодальное обучение
  6. Оптимизация гиперпараметров
  7. Улучшение классических стратегий с помощью нейронных сетей
  8. Вероятностное программирование и пиропрогнозы

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

Как всегда, можно сразу перейти к коду.

Подготовка данных

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

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

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

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

Но мы собираемся нормализовать каждое измерение временного окна независимо:

for i in range(0, len(data_original), STEP): 
    try:
        o = openp[i:i+WINDOW]
        h = highp[i:i+WINDOW]
        l = lowp[i:i+WINDOW]
        c = closep[i:i+WINDOW]
        v = volumep[i:i+WINDOW]
        o = (np.array(o) - np.mean(o)) / np.std(o)
        h = (np.array(h) - np.mean(h)) / np.std(h)
        l = (np.array(l) - np.mean(l)) / np.std(l)
        c = (np.array(c) - np.mean(c)) / np.std(c)
        v = (np.array(v) - np.mean(v)) / np.std(v)

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

x_i = closep[i:i+WINDOW]
y_i = closep[i+WINDOW+FORECAST]
last_close = x_i[-1]
    next_close = y_i
if last_close < next_close:
    y_i = [1, 0]
else:
    y_i = [0, 1]

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

Архитектура нейронной сети

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

Код нашей сети на сегодня выглядит так:

model = Sequential()
model.add(Convolution1D(input_shape = (WINDOW, EMB_SIZE),
                        nb_filter=16,
                        filter_length=4,
                        border_mode='same'))
model.add(BatchNormalization())
model.add(LeakyReLU())
model.add(Dropout(0.5))
model.add(Convolution1D(nb_filter=8,
                        filter_length=4,
                        border_mode='same'))
model.add(BatchNormalization())
model.add(LeakyReLU())
model.add(Dropout(0.5))
model.add(Flatten())
model.add(Dense(64))
model.add(BatchNormalization())
model.add(LeakyReLU())
model.add(Dense(2))
model.add(Activation('softmax'))

Единственное отличие архитектуры от самого первого поста - это изменение переменной EMB_SIZE на 5 в нашем случае.

Тренировочный процесс

Скомпилируем модель:

opt = Nadam(lr=0.002)
reduce_lr = ReduceLROnPlateau(monitor='val_acc', factor=0.9, patience=30, min_lr=0.000001, verbose=1)
checkpointer = ModelCheckpoint(filepath="model.hdf5", verbose=1, save_best_only=True)
model.compile(optimizer=opt, 
              loss='categorical_crossentropy',
              metrics=['accuracy'])
history = model.fit(X_train, Y_train, 
          nb_epoch = 100, 
          batch_size = 128, 
          verbose=1, 
          validation_data=(X_test, Y_test),
          callbacks=[reduce_lr, checkpointer],
          shuffle=True)

И проверим работоспособность:

Из графиков ясно видно, что сеть обучена адекватно (для очень зашумленных данных), потеря обучающей выборки уменьшалась со временем, а точность - увеличивалась. И, что самое главное, по сравнению с одномерными временными рядами из предыдущего поста мы улучшили производительность с 58% до почти 65% точности!

Чтобы проверить переоснащение, мы также можем построить матрицу путаницы:

from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
model.load_weights("model.hdf5")
pred = model.predict(np.array(X_test))
C = confusion_matrix([np.argmax(y) for y in Y_test], [np.argmax(y) for y in pred])
print C / C.astype(np.float).sum(axis=1)

и мы получим:

[[ 0.75510204  0.24489796]
[ 0.46938776  0.53061224]]

который показывает, что мы прогнозируем движение «ВВЕРХ» с точностью 75% и «ВНИЗ» с точностью 53%, и эти результаты, конечно, могут быть сбалансированы для тестового набора данных.

А что насчет регресса?

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

К сожалению, для возвратов он по-прежнему работает плохо:

Для предсказания значения цены закрытия ситуация не лучше:

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

Выводы

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

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

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

Будьте на связи!

P.S.
Следите за мной также в Facebook, чтобы увидеть статьи AI, которые слишком короткие для Medium, Instagram для личных вещей и Linkedin!