이러한 단점을 해결하는 방법이 GARCH(r, s)와 같은 이분산 모형을 이용하여 시계열의 분산 자체를 모형화 하는 방법이다. 하지만 이 경우 시계열의 전체적인 방향성을 이해하는데는 문제가 있다.
최근 알고리즘 트레이딩 분야에서 나름 주목을 받는 방법이 앞의 두 모형을 섞어 사용하는 것이다. 전체적인 방향성은 ARIMA 모형으로 찾고, 시계열의 분산을 GARCH로 분석하여 예측하는 방식이다.
다음의 ARIMA(p, d, q)를 보자.
$$
\theta_p(\mathbf{B})(1-\mathbf{B})^d x_t = \phi_q(\mathbf{B})\epsilon_t
$$
여기서 $d=0$일 경우 ARMA(p, q)와 동일하므로 다음의 식으로 나타낼 수 있다.
$$
x_t = \sum^{p}_{i=1} \alpha_i x_{t-i} + \sum^{q}_{j=1} \beta_j \epsilon_{t-j} + \epsilon_t
$$
ARMA는$\epsilon_t$로 표현하는 충격 또는 오류항이 동일한 분산을 갖는다고 정의한다. 이 부분에 GARCH(r, s)를 적용하여 $\epsilon_t$를 다음과 같이 정의한다.
$$
\epsilon_t = \sigma_t \omega_t \\
\sigma^2_t = \alpha_0 + \sum_{i=1}^r \alpha_i \epsilon_{t-i}^2 + \sum_{j=1}^s \beta_j \epsilon_{t-j}^2
$$
이렇게 정의한 후 시계열에 적용하면, 시계열의 방향성을 찾으면서 이분산 특정도 잡아낼 수 있다.
이 책에서 예제로 제시한 것은 S&P500 지수에 대한 예측과 이에 따른 전략이었다. 하지만 구글 파이낸스가 지수 정보는 제공하지 않는 관계로 구글의 주가를 가지고 전략을 테스트 해보았다. ARIMA+GARCH를 이용하여 다음날의 주가 방향을 예측하고 상승이면 long, 하락이면 short 포지션을 취한다.
그 결과는 다음의 그래프이다.
그래프의 붉은 선이 전략에 의한 수익이고, 푸른색 선이 주식을 사고 버텼을 경우의 수익이다. 2008년에서 2010년 사이 금융위기 당시 하락 추세에서 short 포지션을 잘 취하여 높은 수익을 낸다. 하지만 2012년 경 시장이 혼조세를 겪으면서 예측이 틀리고 큰 손실이 발생하는 것을 볼 수 있다. 이 부분에서 어떻게 예측력이 떨어진것인지 연구해 볼 가치가 있다고 생각한다.
ARIMA+GARCH를 이용하여 시계열을 맞추는 코드는 다음과 같으며, R을 이용한 코드이다.
library(quantmod)
library(lattice)
library(timeSeries)
library(rugarch)
# S&P 500 주가를 받고 NA는 없앰
getSymbols("GOOG", from="1990-01-01", src = "google")
gg_returns = diff(log(Cl(GOOG)))
gg_returns[as.character(head(index(Cl(GOOG)), 1))] = 0
# foreacst vector를 만들어 예측치 저장
window_len = 500
fore_len = length(gg_returns) - window_len
forecasts <- vector(mode="character", length=fore_len)
for(d in 0:fore_len)
{
# rolling window를 구함
gg_returns_offset = gg_returns[(1+d):(window_len + d)]
# fitting ARIMA model
final.aic <- Inf
final.order <- c(0, 0, 0)
for(p in 0:5)
{
for(q in 0:5)
{
if(p == 0 && q==0)
{
next
}
arima_fit = tryCatch(arima(gg_returns_offset, order = c(p, 0, q)),
error = function(err) FALSE,
warning = function(err) FALSE)
if(!is.logical(arima_fit))
{
current.aic <- AIC(arima_fit)
if(current.aic < final.aic)
{
final.aic <- current.aic
final.order <- c(p, 0, q)
final.arima <- arima(gg_returns_offset, order = final.order)
}
}
else
{
next
}
}
}
# fitting 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, gg_returns_offset, solver = 'hybrid'
), error=function(e) e, warning=function(w) w
)
# GARCH 모형이 수렴하지 않으면 방향을 "long"으로 선택
# 그렇지 않으면 수익률 예측에 따라 맞는 방향을 찾아줌
if(is(fit, "warning"))
{
forecasts[d + 1] = paste(
index(gg_returns_offset[window_len]), 1, sep=","
)
print(
paste(
index(gg_returns_offset[window_len]), 1, sep=","
)
)
}
else
{
fore = ugarchforecast(fit, n.ahead = 1)
ind = fore@forecast$seriesFor
forecasts[d + 1] = paste(
colnames(ind), ifelse(ind[1] < 0, -1, 1), sep=","
)
print(
paste(
colnames(ind), ifelse(ind[1] < 0, -1, 1), sep=","
)
)
}
}
# forecast 결과를 csv에 씀
write.csv(forecasts, file="E:/devel/R_src/AdvAlgoTrading/forecasts.csv", row.names=FALSE)
댓글 없음:
댓글 쓰기