Inicialmente, vamos importar as bibliotecas necessárias.
library(tseries)
library(timeSeries)
library(quantmod)
library(fGarch)
library(sn)
Vamos utilizar o getSymbols para obter os dados do Ibovespa, de janeiro de 2007 até a data presente.
ibov = getSymbols('^BVSP', src='yahoo', from= '2007-01-01', auto.assign = F)[,4]
colnames(ibov)= c('ibov')
ibov = ibov[is.na(ibov)==F] # elimina os na's da amostra
Para ver as seis primeiras observações da amostra usarei o comando head.
head(ibov)
## ibov
## 2007-01-02 45383
## 2007-01-03 44445
## 2007-01-04 44020
## 2007-01-05 42245
## 2007-01-08 42830
## 2007-01-09 42007
plot(ibov)
Agora vou calcular os retornos logaritmos do Ibov.
ret = diff(log(ibov))
colnames(ret) = c('ret')
ret = ret[is.na(ret)==F] # Drop na to work
O próximo passo é plotar os retornos e a autocorrelação desses retornos.
par(mfrow=c(2,1))
plot(ret, main='Evolução dos retornos do IBOV')
acf(ret) # There isn't autocorrelation in returns. So isn't possible predict ret
Note que os retornos não são autocorrelacionados. Logo não há uma estrutura de dependência entre eles. Sendo assim não é possivel prever tais retornos. Tente você mesmos estimar um Modelo ARIMA com esses dados. Você perceberá que os coeficientes estimados não são significativos.
Agora vamos fazer o mesmo para o quadrado dos retornos.
par(mfrow=c(2,1))
plot(ret^2, main= 'Evolução do quadrado dos retornos do IBOV')
acf(ret^2) # In squares of returns there is autocorrelation
As estatíscas descritivas do IBOV e seus retornos podem ser calculadas utilizando a função basicStats da biblioteca fGarch.
basicStats(ret)
## ret
## nobs 3277.000000
## NAs 0.000000
## Minimum -0.159930
## Maximum 0.136766
## 1. Quartile -0.008486
## 3. Quartile 0.009549
## Mean 0.000159
## Median 0.000679
## Sum 0.520295
## SE Mean 0.000318
## LCL Mean -0.000465
## UCL Mean 0.000783
## Variance 0.000332
## Stdev 0.018222
## Skewness -0.475159
## Kurtosis 9.841399
basicStats(ibov)
## ibov
## nobs 3.278000e+03
## NAs 0.000000e+00
## Minimum 2.943500e+04
## Maximum 1.195280e+05
## 1. Quartile 5.233175e+04
## 3. Quartile 6.828175e+04
## Mean 6.299761e+04
## Median 5.944500e+04
## Sum 2.065062e+08
## SE Mean 2.857887e+02
## LCL Mean 6.243727e+04
## UCL Mean 6.355795e+04
## Variance 2.677313e+08
## Stdev 1.636250e+04
## Skewness 1.221181e+00
## Kurtosis 1.387047e+00
Agora vamos estimar o modelo GARCH(1, 1). Usualmente estimamos vários modelos GARCH de ordens diferentes, calculamos o critério de informação de Akaike ou o critério de informação Bayesiano, e o modelo com o maior desses indicadores é considerado o mais adequado. No entanto, já sabemos que para séries financeiras o GARCH(1, 1) resolve bem o problema.
O modelo GARCH(p, q) pode ser escrito como:
\[ \sigma^2_t = \omega + \sum_{i=1}^p \alpha_i \epsilon^2_{t-i} + \sum_{j=1}^q \beta_j \sigma^2_{t-j}\]
Nessa primeira estimação, vou utilizar a biblioteca fGarch.
garch1 = garchFit(formula = ~garch(1,1),data = ret)
garch1@fit$matcoef
## Estimate Std. Error t value Pr(>|t|)
## mu 5.417886e-04 2.461217e-04 2.201304 2.771454e-02
## omega 7.180395e-06 1.515319e-06 4.738537 2.152672e-06
## alpha1 8.566411e-02 9.960498e-03 8.600384 0.000000e+00
## beta1 8.895676e-01 1.296377e-02 68.619516 0.000000e+00
Agora que temos um objeto chamado garch1 vamos extrair desse objeto a volatilidade estimada.
data = index(ret)
vol = garch1@sigma.t
vol = as.xts(vol, order.by = data)
par(mfrow=c(2, 2))
plot(ibov, main='Evolution of IBOV')
plot(ret, main='Evolution of IBOV returns')
plot(ret^2, main='Evolution of square \n of IBOV returns')
plot(vol, main= 'Volatility of IBOV \n returns by GARCH(1,1)')
Note que a volatilidade estimada pelo GARCH é bastante elevada já a partir de janeiro de 2020. Tal volatilidade é maior que da crise de 2008. Esse fato pode ser creditado a crise do COVID-19.
Agora vamos estimar o GARCH(1,1) utilizando a biblioteca rugarch.
library(rugarch)
spec = ugarchspec()
spec1 = ugarchspec(variance.model=list(model="sGARCH", garchOrder=c(1,1)),
mean.model=list(armaOrder=c(0,0), include.mean=TRUE),
distribution.model="norm")
garch2 = ugarchfit(spec = spec1, data= ret)
garch2@fit$matcoef
## Estimate Std. Error t value Pr(>|t|)
## mu 5.414678e-04 2.458425e-04 2.202499 2.763010e-02
## omega 7.137185e-06 1.280061e-06 5.575659 2.465949e-08
## alpha1 8.552554e-02 5.697098e-03 15.012125 0.000000e+00
## beta1 8.898728e-01 7.473327e-03 119.073185 0.000000e+00
Use o ugarchforecast para prever 5 passos a frente.
pred2 = ugarchforecast(fitORspec = garch2, n.ahead = 5)
pred2 = pred2@forecast[['sigmaFor']]
pred2
## 2020-04-07
## T+1 0.05060747
## T+2 0.05005243
## T+3 0.04950505
## T+4 0.04896524
## T+5 0.04843291
Assumimos que os retornos do IBOV tem distribuição normal para estimar o modelo GARCH(1, 1). No entanto, sabemos que isso não é verdade. Utizando o teste de Kolmogorov-Smirnov cuja hipótese nula é de normalidade na série, nota-se que tal hipótese é rejeitada.
ksnormTest(ret)
##
## Title:
## One-sample Kolmogorov-Smirnov test
##
## Test Results:
## STATISTIC:
## D: 0.4728
## P VALUE:
## Alternative Two-Sided: < 2.2e-16
## Alternative Less: < 2.2e-16
## Alternative Greater: < 2.2e-16
##
## Description:
## Wed Apr 08 14:40:55 2020 by user: user
Olhando para o histograma e para o QQ plot abaixo, nota-se que a distribuição dos retornos do IBOV possui caldas mais pesadas do que a distribuição normal teórica.
par(mfrow=c(2,1))
qqnorm(ret)
qqline(ret, col=2)
hist(ret, breaks = 35, col = 'lightgreen', main = 'Histograma dos retornos \n do IBOV')
Agora vamos supor que os dados dos retornos do IBOV tem distribuição skew normal, que tem um peso maior nas caudas, e estimar o modelo.
spec2 = ugarchspec(variance.model=list(model="sGARCH", garchOrder=c(1,1)),
mean.model=list(armaOrder=c(0,0), include.mean=TRUE),
distribution.model="snorm")
garch3 = ugarchfit(spec = spec2, data= ret)
garch3@fit$matcoef
## Estimate Std. Error t value Pr(>|t|)
## mu 4.424236e-04 2.456837e-04 1.800785 0.0717367099
## omega 6.402579e-06 1.813073e-06 3.531341 0.0004134578
## alpha1 8.222655e-02 5.697258e-03 14.432653 0.0000000000
## beta1 8.954728e-01 6.970409e-03 128.467755 0.0000000000
## skew 9.041227e-01 1.966559e-02 45.974871 0.0000000000
Fazendo a previsão para 5 pass a frente.
pred3 = ugarchforecast(fitORspec = garch3, n.ahead = 5)
pred3
##
## *------------------------------------*
## * GARCH Model Forecast *
## *------------------------------------*
## Model: sGARCH
## Horizon: 5
## Roll Steps: 0
## Out of Sample: 0
##
## 0-roll forecast [T0=2020-04-07]:
## Series Sigma
## T+1 0.0004424 0.05145
## T+2 0.0004424 0.05094
## T+3 0.0004424 0.05043
## T+4 0.0004424 0.04993
## T+5 0.0004424 0.04943
Também posso estimar o modelo GARCH utilizando a biblioteca arch do Python.
import matplotlib.pyplot as plt
from arch import arch_model
am = arch_model(r.ret, lags=1, vol='Garch')
print(f'\033[1;033m {am.fit() } ')
## Iteration: 1, Func. Count: 6, Neg. LLF: -9027.629664690852
## Iteration: 2, Func. Count: 20, Neg. LLF: -9029.000722060973
## Iteration: 3, Func. Count: 31, Neg. LLF: -9029.0969488853
## Iteration: 4, Func. Count: 46, Neg. LLF: -9029.160078156841
## Iteration: 5, Func. Count: 60, Neg. LLF: -9029.160126808416
## Iteration: 6, Func. Count: 74, Neg. LLF: -9029.160209442904
## Iteration: 7, Func. Count: 87, Neg. LLF: -9029.160212299781
## Optimization terminated successfully. (Exit mode 0)
## Current function value: -9029.160213633248
## Iterations: 9
## Function evaluations: 98
## Gradient evaluations: 7
## [1;033m Constant Mean - GARCH Model Results
## ==============================================================================
## Dep. Variable: y R-squared: -0.000
## Mean Model: Constant Mean Adj. R-squared: -0.000
## Vol Model: GARCH Log-Likelihood: 9029.16
## Distribution: Normal AIC: -18050.3
## Method: Maximum Likelihood BIC: -18025.9
## No. Observations: 3277
## Date: Wed, Apr 08 2020 Df Residuals: 3273
## Time: 14:40:58 Df Model: 4
## Mean Model
## ============================================================================
## coef std err t P>|t| 95.0% Conf. Int.
## ----------------------------------------------------------------------------
## mu 5.6235e-04 4.356e-05 12.909 4.005e-38 [4.770e-04,6.477e-04]
## Volatility Model
## ============================================================================
## coef std err t P>|t| 95.0% Conf. Int.
## ----------------------------------------------------------------------------
## omega 6.7764e-06 4.160e-10 1.629e+04 0.000 [6.776e-06,6.777e-06]
## alpha[1] 0.1000 1.482e-02 6.746 1.515e-11 [7.095e-02, 0.129]
## beta[1] 0.8800 1.197e-02 73.497 0.000 [ 0.857, 0.903]
## ============================================================================
##
## Covariance estimator: robust
##
## C:\Users\user\Anaconda3\lib\site-packages\arch\univariate\base.py:260: DataScaleWarning: y is poorly scaled, which may affect convergence of the optimizer when
## estimating the model parameters. The scale of y is 0.0003319. Parameter
## estimation work better when this value is between 1 and 1000. The recommended
## rescaling is 100 * y.
##
## This warning can be disabled by either rescaling y before initializing the
## model or by setting rescale=False.
##
## DataScaleWarning)