uralpro
uralpro личный блог
29 октября 2016, 11:19

Прибыльны ли модели ARIMA/GARCH? Часть 1

Прибыльны ли модели ARIMA/GARCH? Часть 1

Статья из блога Robot Wealth.

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

Сначала дадим несколько необходимых определений. Я не хочу воспроизводить всю теорию целиком, ниже дан краткий обзор моделирования временных серий, в частности ARIMA и GARCH моделей:

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

ARMA модель (пока без «I») — это линейная комбинация авторегрессивной (AR) модели  и скользящего среднего (МА). В AR модели предиктором является прошлое значении серии. МА модель структурно схожа с AR моделью, исключая предикторы для шума. Авторегрессивная модель со скользящей средней порядка p,q — ARMA(p,q) — это линейная комбинация упомянутых моделей и определяется как:

\begin{equation*} X_{t} = a_{1}X_{t-1} + a_{2}X_{t-2} + ... + a_{p}X_{t-p} + w_{t} + b_{1}w_{t-1} + b_{2}w_{t-2} + ... + b_{q}w_{t-q} \end{equation*}

где w_{t}  — белый шум и a_{i}b_{i}  — коэффициенты модели.

ARIMA (p,d,q) модель — это просто ARMA  модель, дифференцированная d раз — для получения стационарных серий.

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

Далее, в рамках представленного контекста, я найду  ARIMA/GARCH модель для EUR/USD пары, и использую ее в качестве основы для торговой стратегии. Параметры модели вычисляются для каждого дня с применением процедуры подгонки, эта модель затем используется для предсказания приращения следующего торгового дня и соответственного вхождения в позицию, которая сохраняется в течение дня. Если предсказание совпадает с предсказанием предыдущего дня, существующая позиция сохраняется.

Скользящее окно логарифма приращений используется для вычисления параметров оптимальной модели ARIMA/GARCH на закрытии каждого торгового дня. Процедура подгонки основана на поиске параметров, которые минимизируют AIC ( информационный критерий Аикаке), но могут быть также применены и другие методы. Например, мы можем выбрать параметры, минимизирующие BIC (байесовский иформационный критерий), который может помочь уменьшить переподгонку в сложных моделях (то есть моделях с большим числом параметров). Такая процедура нахождения параметров была предложена Michael Halls-Moore  в посте ARIMA+GARCH trading strategy for the S&P500, и я одолжил у него часть кода.

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

### ARIMA/GARCH trading model
library(quantmod)
library(timeSeries)
library(rugarch)

# get data and initialize objects to hold forecasts
EURUSD <- read.csv('EURUSD.csv', header = T)
EURUSD[, 1] < - as.Date(as.character(EURUSD[, 1]), format="%d/%m/%Y")
returns <- diff(log(EURUSD$C)) ## ttr::ROC can also be used: calculates log returns by default
window.length <- 1000
forecasts.length <- length(returns) - window.length
forecasts <- vector(mode="numeric", length=forecasts.length) 
directions <- vector(mode="numeric", length=forecasts.length) 
p.val <- vector(mode="numeric", length=forecasts.length) 

# loop through every trading day, estimate optimal model parameters from rolling window
# and predict next day's return
for (i in 0:forecasts.length) {
  roll.returns <- returns[(1+i):(window.length + i)] # create rolling window
  final.aic <- Inf
  final.order <- c(0,0,0)
  # estimate optimal ARIMA model order
  for (p in 0:5) for (q in 0:5) { # limit possible order to p,q <= 5
    if (p == 0 && q == 0) next # p and q can't both be zero
    arimaFit <- tryCatch( arima(roll.returns, order = c(p,0,q)), 
                          error = function( err ) FALSE,
                          warning = function( err ) FALSE )
    if (!is.logical( arimaFit)) {
      current.aic <- AIC(arimaFit)
      if (current.aic < final.aic) { # retain order if AIC is reduced
        final.aic <- current.aic
        final.order <- c(p,0,q)
        final.arima <- arima(roll.returns, order = final.order)
      }
    }
    else next 
  }

  # specify and fit the GARCH model
  spec = ugarchspec(variance.model <- list(garchOrder=c(1,1)),
                    mean.model <- list(
                      armaOrder <- c(final.order[1], final.order[3]), include.mean = T),
                    distribution.model = "sged")
  fit = tryCatch(ugarchfit(spec, roll.returns, solver = 'hybrid'), error = function(e) e, warning = function(w) w)
  
  # calculate next day prediction from fitted mode
  # model does not always converge - assign value of 0 to prediction and p.val in this case
  if (is(fit, "warning")) {
    forecasts[i+1] <- 0 
    print(0)
    p.val[i+1] <- 0
  }
  else {
    next.day.fore = ugarchforecast(fit, n.ahead = 1)
    x = next.day.fore@forecast$seriesFor
    directions[i+1] <- ifelse(x[1] > 0, 1, -1) # directional prediction only
    forecasts[i+1] <- x[1] # actual value of forecast
    print(forecasts[i])
    # analysis of residuals
    resid <- as.numeric(residuals(fit, standardize = TRUE))
    ljung.box <- Box.test(resid, lag = 20, type = "Ljung-Box", fitdf = 0)
    p.val[i+1] <- ljung.box$p.value
  }
}

dates <- EURUSD[, 1] 
forecasts.ts <- xts(forecasts, dates[(window.length):length(returns)])
# create lagged series of forecasts and sign of forecast
ag.forecasts <- Lag(forecasts.ts, 1)
ag.direction <- ifelse(ag.forecasts > 0, 1, ifelse(ag.forecasts < 0, -1, 0))

# Create the ARIMA/GARCH returns for the directional system
ag.direction.returns <- ag.direction * returns[(window.length):length(returns)]
ag.direction.returns[1] <- 0 # remove NA

# Create the backtests for ARIMA/GARCH and Buy & Hold
ag.curve <- log( cumprod( 1 + ag.direction.returns) )
buy.hold.ts <- xts(returns[(window.length):length(returns)], dates[(window.length):length(returns)])
buy.hold.curve <- log(cumprod(1 + buy.hold.ts))
both.curves <- cbind(ag.curve, buy.hold.curve)
names(both.curves) <- c("Strategy returns", "Buy and hold returns")

# plot both curves together
myColors <- c( "darkorange", "blue") 
plot(x = both.curves[,"Strategy returns"], xlab = "Time", ylab = "Cumulative Return",
     main = "Cumulative Returns", ylim = c(-0.25, 0.4), major.ticks= "quarters",
     minor.ticks = FALSE, col = "darkorange")
lines(x = both.curves[,"Buy and hold returns"], col = "blue")
legend(x = 'bottomleft', legend = c("Strategy", "B&H"),
       lty = 1, col = myColors)

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

Другие стратегии и алгоритмы автоматической торговли смотрите на моем сайте www.quantalgos.ru

Продолжение следует...

12 Комментариев
  • cfree0185
    29 октября 2016, 11:30
    1500 пунктов движения за 7 лет)
  • Дмитрий Ш
    29 октября 2016, 11:36
    Это круто, когда простые мои земляки переваривают неудобоваримые заморочные статьи и умудряются из них выводы делать, хоть и работающие лишь для определённых направлений.)
  • baron_samedi
    29 октября 2016, 12:31
    Спасибо за труд, такие статьи редкость!
    • Дмитрий Ш
      29 октября 2016, 13:07
      baron_samedi, За ТРУД не благодарят, он должен быть оплачен.;)
      • baron_samedi
        29 октября 2016, 16:40
        Дмитрий Ш, 
        искренне надеюсь что когда-то смогу и я оплатить труд автора, пока же у меня ничего нет, могу только поклониться.
        Но даже и «спасибо за труд» в наше стране работник  редко слышит, так как за все  «спасибо партии».
  • Growex
    29 октября 2016, 15:21
    Это на oex может работать. На FX очень сомнительно из за сильно цикличной волы. Вообще непонятно применение гарча на евродолларе. Все равно что вместо валенок перчатки имхо.

  • pamminvest
    30 октября 2016, 02:49
    очень круто. пусть я ничего и не понял. спасибо.
  • ELab
    30 октября 2016, 10:25
    какие ARIMA/GARCH — народ уже давно на поиске неэффективностей завис 
  • alexKa
    01 ноября 2016, 08:48
    не работает, уже проверял, слишком простая модель, и давно известно
  • Growex
    26 декабря 2016, 16:39
    > library(rugarch)
    Загрузка требуемого пакета: parallel
    Error in loadNamespace(j <- i[[1L]], c(lib.loc, .libPaths()), versionCheck = vI[[j]]) :
      нет пакета под названием ‘Rsolnp’
    Вдобавок: Предупреждение:
    пакет ‘rugarch’ был собран под R версии 3.3.2
    Ошибка: не удалась загрузка пакета или пространства имен для ‘rugarch’

    Какчал с зеркала офф. не хочет почему то грузить ругарч

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн