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

Введение

В настоящее время в обращении находится более 5000 различных криптовалют общей стоимостью более 2 триллионов долларов, и есть вероятность, что мысль об инвестировании в один из этих криптоактивов, по крайней мере, приходила вам в голову. И как самая известная криптовалюта, составляющая около 70% рынка, Биткойн стал очень популярной инвестицией как среди частных лиц, так и среди институциональных инвесторов. Однако крайнюю волатильность Биткойна нельзя недооценивать, и, учитывая годовую 30-дневную волатильность криптовалюты, которая недавно выросла до 14-месячного максимума, тем, кто решил торговать ею, нужны крепкие нервы.

При таком разнообразии факторов, влияющих на цену акций, от социальных и экономических событий до иррационального поведения инвесторов, даже самые передовые алгоритмы машинного обучения не смогли преодолеть сложности фондового рынка. Но, несмотря на это, искусственный интеллект играет все более важную роль в трейдинге и инвестировании, при этом на компьютеризированную высокочастотную торговлю приходится около 70% от общего объема торговли, как указано в статье Wired, ссылка на которую приведена ниже. Поэтому я подумал, что будет интересной задачей попытаться предсказать цену Биткойна, используя обширный набор инструментов машинного обучения R.



Цели проекта

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

Используемые библиотеки

Вот библиотеки, которые я импортировал для использования в блокноте:

library("anytime")
library("xts")
library("ggfortify")
library("forecast")
library("quantmod")
  • в любое время работает как конвертер общего назначения, который возвращает объект Date независимо от ввода. Он использует библиотеку Boost date_time для эффективного преобразования.
  • xts (Extensible Time Series) предлагает гибкий класс временных рядов, который предлагает множество методов для управления данными временных рядов.
  • ggfortify – это расширение ggplot2, позволяющее отображать некоторые популярные пакеты R. В нашем случае он используется для построения объектов временных рядов.
  • прогнозиспользует модель временного ряда и создает соответствующий прогноз.
  • quantmod предназначен для помощи в разработке, тестировании и развертывании статистических моделей. В нашем случае он используется для расширенного метода теста Дики-Фуллера.

Изучение и очистка данных

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



# Importing the data
train <- read.csv("data.csv", header = TRUE)
head(train)
summary(train1)
train <- na.omit(train)

Как показано на рис. 1, временная метка отображается в формате времени Unix и в текущем виде нам не нужна. Для каждой строки нам дана цена открытия, самая высокая, самая низкая и цена закрытия Биткойна.Volume_.BTC относится к количеству монет, которые были переданы из рук в руки за 24 часа, т. е. какова стоимость Биткойна. было куплено и продано в течение дня, а Объем_.Валюта — соответствующий объем торгов в долларах США. Нам также предоставляется средневзвешенная цена по объему — эталон, используемый трейдерами, который дает среднюю цену, по которой актив торговался в течение дня.

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

# Converting data for analysis
train$time <- as.POSIXct(train$Timestamp, origin = "1970-01-01", tz = "GMT")
train$time <- as.Date(train$time)
train$Weighted_Price <- as.numeric(train$Weighted_Price)
train$Volume_.BTC. <- as.numeric(train$Volume_.BTC.)
train$Volume_.Currency. <- as.numeric(train$Volume_.Currency.)
train$Timestamp = NULL
train <- train[,c(8,1,2,3,4,5,6,7)]
# Data for the price comparison at the end
testdata <- train[,5]

Чтобы преобразовать метку времени в удобный формат, я сначала использовал метод as.POSIXct, который преобразует строку даты и времени в класс POSIXct. В нем хранятся как дата, так и время с нашим связанным часовым поясом (GMT) с количеством секунд, начинающихся со стандартной даты 1 января 1970 года; хранение данных таким образом оптимизирует использование во фреймах данных и ускоряет вычисления и преобразование в другие форматы. Теперь, когда у меня были время и дата в классе POSIXct, я мог легко преобразовать их в класс Date. Затем я преобразовал числовые поля в правильный тип данных, удалил лишнее поле метки времени, а затем изменил порядок столбцов.

cp <- ggplot(train2, aes(time, Close)) + geom_line() + scale_x_date(date_breaks = "years", date_labels = "%Y" ,limits = as.Date(c("2016-01-01","2021-03-01"))) + ylab("Closing Price ($)") + xlab("Year") + ylim(0,55000)
# Plotting the Closing Price
cp + theme_bw() + labs(title="Bitcoin Closing Price") + geom_line(size = 1, colour = "red")

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

Построение модели прогнозирования

АРИМА

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

Модели ARIMA обычно выражаются как ARIMA (p, d, q) с терминами p, d и q определяется следующим образом:

  • p — количество наблюдений запаздывания, включенных в модель, и авторегрессионная часть.
  • d — количество раз, когда необработанные наблюдения различаются для получения стационарного сигнала.
  • q — размер окна скользящего среднего и количество запаздывающих членов ошибки прогноза в уравнении прогноза.

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

train_xts <- xts(train2[, -1], order.by =as.POSIXct(train2$time))
train_ts <- ts(train_xts[,4], frequency = 700,start = c(2016,4,27))
# checking for trends and seasonality
dects2 <- decompose(train_ts) #Obtaining the trends and seasonality
ap <- autoplot(dects2)
ap + theme_bw()

Сначала мне нужно было создать объект временного ряда для цены закрытия. Данные временного ряда — это просто набор наблюдений, полученных в результате повторных измерений с течением времени. Поскольку точки данных во временных рядах собираются в смежные периоды времени, существует возможность корреляции между наблюдениями; это одна из особенностей, которая отличает данные временных рядов от перекрестных данных, которые наблюдают за субъектами в один момент времени. Здесь я использовал библиотеку xts, о которой упоминал в начале, а также собственную функцию временных рядов R.

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

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

Чтобы подтвердить эту нестационарность, я использовал тест гипотезы Дики-Фуллера:

Нулевая гипотеза: ряд нестационарен.

Альтернативная гипотеза: ряд является стационарным.

#Dickey-Fuller testing
closingFigures <- xts(train[,5], order.by =as.POSIXct(train2$time))
adf.test(closingFigures, alternative='stationary')

При значении p больше 0,05 мы не можем отвергнуть нулевую гипотезу, и нестационарность ряда подтверждается. Поэтому следующим шагом было сделать ряд стационарным с помощью метода, называемого дифференцированием, в котором каждое предыдущее наблюдение вычитается из текущего наблюдения. Это достигается с помощью функции diff():

closing_diff <- diff(train[,5], differences= 2)
closing_diff <- closing_diff[!is.na(closing_diff)]

После проверки первого и второго порядков разности (и обнаружения того, что данные становятся стационарными при differences = 2), я получил параметр ARIMA: d= 2.

Графики ACF и PACF

Теперь, когда у меня была модель ARIMA(p,1,q), мне нужно было найти термин AR (p) и MAтермин (q). Это потребовало изучения графика ACF, а также графика PACF. ACF (функция автокорреляции) вычисляет и отображает корреляцию между наблюдениями временного ряда, разделенного k единицами времени, а PACF (функция частичной автокорреляции) измеряет силу связи с другими учитываемыми условиями, в этом случае другие термины представляют собой промежуточные лаги, присутствующие в модели.

acf(closing_diff)
pacf(closing_diff)

Образцы этих графиков можно использовать для определения значений p и q. Поскольку ACF отключается после 1 задержки, а PACF показывает постепенное затухание, AR(1) будет подходящим выбором для ряда. Падение на графике PACF после 9 лагов указывает на то, что MA(9) также может быть хорошим кандидатом для модели, и таким образом я пришел к модели ARIMA(1,2,9).

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

model <- arima(closingFigures, order = c(4,2,11))
model_frame <- as.data.frame(forecast(gege, h = 10))
model_frame.index.name = 'index'
comparison <- cbind(testdata, model_frame[,1])
plot(forecast(gege))
ggplot() + geom_line(data = model_frame, aes(Date, comparison[,2]), color = "a") + geom_line(data = comparison, aes(Date, comparison[,3]), color = "b") + labs(title="Forecast vs Actual Price")+ ylab("Closing Price ($)") + xlab("Date") + scale_x_date(date_labels = '%d %b %y'

Вывод

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

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