Objetivo do artigo é criar um modelo que possa prever/ acertar a direção da volatilidade das ações de Petrobrás (PETR4.SA).
Será utilizado a volatilidade de 5 dias anteriores como Alvo e deslocado os mesmos para cinco dias anteriores. Portanto, o objetivo é de treinar o modelo para que quando as variáveis explicativas estiverem em tais condições, qual será a volatilidade cinco dias a frente?!
# dados das acoes de PETR4
PETR4 <- tq_get('PETR4.SA',
from = '2010-01-01',
to = Sys.Date(),
get = 'stock.prices') %>%
na.omit()
Será utilizado algumas variáveis explicativas para o modelo, sendo elas:
## # A tibble: 6 × 16
## symbol date open high low close volume adjusted Ratio Prop
## <chr> <date> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 PETR4.SA 2010-01-18 35.9 36.7 35.8 36.5 23168400 19.0 1.03 0.722
## 2 PETR4.SA 2010-01-19 36.5 36.7 36.2 36.4 16895900 18.9 1.01 -0.171
## 3 PETR4.SA 2010-01-20 36.1 36.2 35.3 35.5 18757500 18.4 1.03 -0.730
## 4 PETR4.SA 2010-01-21 35.6 35.7 34.3 34.3 24553000 17.8 1.04 -0.887
## 5 PETR4.SA 2010-01-22 34.1 34.8 34.1 34.8 19025100 18.1 1.02 0.865
## 6 PETR4.SA 2010-01-26 34.3 34.3 33.7 33.9 20281200 17.6 1.02 -0.565
## # … with 6 more variables: Close_Shift <dbl>, Return <dbl>, Return_p <dbl>,
## # Sd <tibble[,1]>, VOL <dbl>, Alvo <dbl>
Distribuição da volatilidade de Petrobrás desde 2010.
PETR4 %>%
ggplot(aes(x = Alvo))+
geom_histogram(fill = 'red')+
ggthemes::theme_clean()+
labs(title = 'Distribuição da Volalilidade de Petrobrás',
subtitle = 'Vol de 5 dias',
caption = 'Feito por Lucas: dados stockprices / tidyquant package')
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
O gráfico de correlação busca entender como as variáveis caminham em relação ao ALVO.
PETR4 %>%
select(-symbol, -date, -Close_Shift, -VOL, -Sd) %>%
cor() %>%
ggcorrplot::ggcorrplot(method = c('circle'), lab = T)
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
Ao criar o modelo é necessário separmos em base de teste e validação para que verificar a estabilidade do modelo. Neste caso, separamos em 70% da amostra para treino e 30% para teste.
set.seed(123)
inTrain <- createDataPartition(PETR4$Alvo,p = 0.7, list=FALSE)
training <- PETR4[inTrain,]
testing <- PETR4[-inTrain,]
dim(training); dim(testing)
## [1] 2120 16
## [1] 905 16
Nesta etapa será rodado a regressão linear com as variáves explicativas e em seguida o resultado da regressão com os coeficientes!
modelo1 <- lm(Alvo ~ Return + Return_p +Ratio + VOL,
data = training)
summary(modelo1)
##
## Call:
## lm(formula = Alvo ~ Return + Return_p + Ratio + VOL, data = training)
##
## Residuals:
## Min 1Q Median 3Q Max
## -6.6388 -0.6912 -0.1705 0.4564 14.0944
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -26.16401 1.77785 -14.717 < 2e-16 ***
## Return -0.04978 0.01035 -4.810 1.61e-06 ***
## Return_p -0.05697 0.03338 -1.707 0.088 .
## Ratio 26.99575 1.75541 15.379 < 2e-16 ***
## VOL 0.30713 0.02366 12.979 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.292 on 2115 degrees of freedom
## Multiple R-squared: 0.3764, Adjusted R-squared: 0.3753
## F-statistic: 319.2 on 4 and 2115 DF, p-value: < 2.2e-16
training$predicao = predict(modelo1, training)
Como tratamos de uma regressão linear, os resultados não são binários para que possamos calcular a acurácia e entender se o modelo acerta. No modelo de regressão linear, o resultado é o ‘y’ previsto, ou seja, a volatilidade prevista, portanto, mesmo que o modelo ele não acerte a volatilidade, desejamos criar uma variável para que possamos verificar se o modelo acerta dreção, logo, a ideia é entender se quando o ALVO é positivo, o modelo previu também um valor positivo.
# simular a medicao
mean(PETR4$Alvo)
## [1] 2.479884
training$Real_lag <- (training$Alvo / lag(training$Alvo,1)-1)
training$Previsto_lag <- (training$predicao / lag(training$predicao,1)-1)
training$StatusReal <- ifelse(training$Real_lag > 0, 1, 0)
training$StatusPrevisto <- ifelse(training$Previsto_lag > 0, 1, 0)
training$Real_dir <- ifelse(training$Alvo > 0 & training$StatusReal ==1 | training$Alvo < 0 & training$StatusReal == 0, 1,0)
training$Previsto_dir <- ifelse(training$predicao > 0 & training$StatusPrevisto ==1 | training$Alvo < 0 & training$StatusPrevisto == 0, 1,0)
training$Real_cat <- ifelse(training$Alvo > 2.480322,1,0)
training$Previsto_cat <- ifelse(training$predicao > 2.480322,1,0)
resultados = training %>%
select(date, Alvo, predicao, Real_lag, Previsto_lag, Real_dir, Previsto_dir, Real_cat, Previsto_cat)
resultados %>%
ggplot(aes(x = predicao, y = Alvo))+
geom_point(col = 'darkblue')+
ggthemes::theme_clean()+
labs(title = 'Volatilidade de PETR4' ,
x = 'Valores previstos', y = 'Alvo')
resultados %>%
ggplot(aes(x = date))+
geom_line(aes(y = Alvo, col = 'ALVO'), size = 0.5)+
geom_line(aes(y = predicao, col = 'Previsto'), size = 0.5)+
scale_colour_manual(values = c('ALVO' = 'darkblue',
'Previsto' = 'red'))+
ggthemes::theme_clean()+
labs(title = 'Volatilidade de PETR4' ,
x = 'Valores previstos', y = 'Alvo')+
scale_x_date(date_breaks = '1 year',
date_labels = '%Y')
As métricas de precisão e sensibilidade vão dizer o qual preciso é o modelo para acertar a direção!
# METRICAS
forecast::accuracy(resultados$Alvo, resultados$predicao)
## ME RMSE MAE MPE MAPE
## Test set -7.966013e-16 1.29014 0.8345207 0.0683192 33.57788
# DIRECAO DA VOLATILIDADE
table(resultados$Real_dir, resultados$Previsto_dir)
##
## 0 1
## 0 582 472
## 1 543 522
sensibilidade = 578/(578+517)*100
sensibilidade
## [1] 52.78539
# ACERTO EM RELACAO A MÉDIA DA VOLATILIDADE
table(resultados$Real_cat,resultados$Previsto_cat)
##
## 0 1
## 0 1048 290
## 1 279 503
precisao = 1040 / (1040+502)*100
precisao
## [1] 67.44488
resultados$residuos <- modelo1$residuals
resultados %>%
ggplot(aes(x = residuos, y= predicao))+
geom_point(col = 'red')
plot(modelo1)
modelo2 <- lm(Alvo ~ Return + Return_p +Ratio + Prop + VOL,
data = testing)
summary(modelo2)
##
## Call:
## lm(formula = Alvo ~ Return + Return_p + Ratio + Prop + VOL, data = testing)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.3678 -0.6480 -0.1480 0.4734 7.3937
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -2.112e+01 2.080e+00 -10.153 < 2e-16 ***
## Return -4.287e-04 1.757e-02 -0.024 0.98054
## Return_p -1.291e-01 4.164e-02 -3.100 0.00199 **
## Ratio 2.186e+01 2.054e+00 10.644 < 2e-16 ***
## Prop -2.128e-03 9.244e-02 -0.023 0.98164
## VOL 3.859e-01 2.947e-02 13.095 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.065 on 899 degrees of freedom
## Multiple R-squared: 0.4772, Adjusted R-squared: 0.4743
## F-statistic: 164.1 on 5 and 899 DF, p-value: < 2.2e-16
testing$predicao = predict(modelo2, testing)
# simular a medicao
mean(PETR4$Alvo)
## [1] 2.479884
testing$Real_lag <- (testing$Alvo / lag(testing$Alvo,1)-1)
testing$Previsto_lag <- (testing$predicao / lag(testing$predicao,1)-1)
testing$StatusReal <- ifelse(testing$Real_lag > 0, 1, 0)
testing$StatusPrevisto <- ifelse(testing$Previsto_lag > 0, 1, 0)
testing$Real_dir <- ifelse(testing$Alvo > 0 & testing$StatusReal ==1, 1,0)
testing$Previsto_dir <- ifelse(testing$predicao > 0 & testing$StatusPrevisto ==1, 1,0)
testing$Real_cat <- ifelse(testing$Alvo > 2.480322,1,0)
testing$Previsto_cat <- ifelse(testing$predicao > 2.480322,1,0)
resultados2 = testing %>%
select(date, Alvo, predicao, Real_lag, Previsto_lag, Real_dir, Previsto_dir, Real_cat, Previsto_cat)
# grafico
resultados2 %>%
ggplot(aes(x = predicao, y = Alvo))+
geom_point(col = 'darkblue')+
ggthemes::theme_clean()+
labs(title = 'Volatilidade de PETR4' ,
x = 'Valores previstos', y = 'Alvo')
# GRAFICO DA VOLATILIDADE
resultados2 %>%
ggplot(aes(x = date))+
geom_line(aes(y = Alvo, col = 'ALVO'), size = 0.5)+
geom_line(aes(y = predicao, col = 'Previsto'), size = 0.5)+
scale_colour_manual(values = c('ALVO' = 'darkblue',
'Previsto' = 'red'))+
ggthemes::theme_clean()+
labs(title = 'Volatilidade de PETR4' ,
x = 'Valores previstos', y = 'Alvo')+
scale_x_date(date_breaks = '1 year',
date_labels = '%Y')
# METRICAS
forecast::accuracy(resultados2$Alvo, resultados2$predicao)
## ME RMSE MAE MPE MAPE
## Test set -8.509146e-15 1.061103 0.7548086 -0.3808137 31.21393
# DIRECAO DA VOLATILIDADE
table(resultados2$Real_dir, resultados2$Previsto_dir)
##
## 0 1
## 0 257 202
## 1 223 222
sensibilidade2 = 254/(254+219)
sensibilidade2
## [1] 0.5369979
(254+219)/(254+219+222+208)*100 #acuraria
## [1] 52.38095
# ACERTO EM RELACAO A MÉDIA DA VOLATILIDADE
table(resultados2$Real_cat,resultados2$Previsto_cat)
##
## 0 1
## 0 455 112
## 1 120 218
precisao2 = 462 / (462+218)
precisao2
## [1] 0.6794118
modelo3 <- lm(Alvo ~ Return + Return_p +Ratio + Prop + VOL,
data = PETR4)
summary(modelo3)
##
## Call:
## lm(formula = Alvo ~ Return + Return_p + Ratio + Prop + VOL, data = PETR4)
##
## Residuals:
## Min 1Q Median 3Q Max
## -6.3286 -0.6828 -0.1748 0.4649 14.1352
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -24.41626 1.38219 -17.665 < 2e-16 ***
## Return -0.04770 0.01067 -4.471 8.07e-06 ***
## Return_p -0.08268 0.02652 -3.118 0.00184 **
## Ratio 25.23440 1.36470 18.491 < 2e-16 ***
## Prop 0.10597 0.05623 1.884 0.05959 .
## VOL 0.33091 0.01878 17.624 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.23 on 3019 degrees of freedom
## Multiple R-squared: 0.4, Adjusted R-squared: 0.399
## F-statistic: 402.5 on 5 and 3019 DF, p-value: < 2.2e-16
PETR4$predicao = predict(modelo3, PETR4)
status = 'Positivo'
# simular a medicao
mean(PETR4$Alvo)
## [1] 2.479884
PETR4$Real_lag <- (PETR4$Alvo / lag(PETR4$Alvo,1)-1)
PETR4$Previsto_lag <- (PETR4$predicao / lag(PETR4$predicao,1)-1)
PETR4$StatusReal <- ifelse(PETR4$Real_lag > 0, 1, 0)
PETR4$StatusPrevisto <- ifelse(PETR4$Previsto_lag > 0, 1, 0)
PETR4$Real_dir <- ifelse(PETR4$Alvo > 0 & PETR4$StatusReal ==1, 1,0)
PETR4$Previsto_dir <- ifelse(PETR4$predicao > 0 & PETR4$StatusPrevisto ==1, 1,0)
PETR4$Real_cat <- ifelse(PETR4$Alvo > 2.480322,1,0)
PETR4$Previsto_cat <- ifelse(PETR4$predicao > 2.480322,1,0)
resultados3 = PETR4 %>%
select(date, Alvo, predicao, Real_lag, Previsto_lag, Real_dir, Previsto_dir, Real_cat, Previsto_cat)
# grafico
resultados3 %>%
ggplot(aes(x = predicao, y = Alvo))+
geom_point(col = 'darkblue')+
ggthemes::theme_fivethirtyeight()+
labs(title = 'Volatilidade de PETR4' ,
x = 'Valores previstos', y = 'Alvo')
# GRAFICO DA VOLATILIDADE
resultados3 %>%
ggplot(aes(x = date))+
geom_line(aes(y = Alvo, col = 'ALVO'), size = 0.5)+
geom_line(aes(y = predicao, col = 'Previsto'), size = 0.5)+
scale_colour_manual(values = c('ALVO' = 'darkblue',
'Previsto' = 'red'))+
ggthemes::theme_clean()+
labs(title = 'Volatilidade de PETR4' ,
x = 'Valores previstos', y = 'Alvo')+
scale_x_date(date_breaks = '1 year',
date_labels = '%Y')
# METRICAS
forecast::accuracy(resultados3$Alvo, resultados3$predicao)
## ME RMSE MAE MPE MAPE
## Test set 6.070908e-14 1.228525 0.8120393 0.01040875 32.90537
# DIRECAO DA VOLATILIDADE
table(resultados3$Real_dir, resultados3$Previsto_dir)
##
## 0 1
## 0 857 640
## 1 764 763
sensibilidade = 851/(851+757)
sensibilidade
## [1] 0.5292289
acuracia = (857+762)/(857+762+640+764)*100
acuracia
## [1] 53.55607
# ACERTO EM RELACAO A MÉDIA DA VOLATILIDADE
table(resultados3$Real_cat,resultados3$Previsto_cat)
##
## 0 1
## 0 1491 414
## 1 395 725
precisao3 = 1490 / (1490+395)*100
precisao3
## [1] 79.04509
acuracia3 = (1490+725)/(1490+414+395+725)*100
acuracia3
## [1] 73.24735
# COEFICIENTES DA REGRESSAO
print(modelo3$coefficients)
## (Intercept) Return Return_p Ratio Prop VOL
## -24.41625643 -0.04769783 -0.08267891 25.23439923 0.10596718 0.33091172
resultados3$residuos <- modelo3$residuals
resultados3 %>%
ggplot(aes(x = residuos, y= predicao))+
geom_point(col = 'red')+
theme_light()+
labs(title = 'Valores preditos vs Residuos da regressao')
O modelo de regressão linear é simples mas que pode servir de base para análises mais complexas no futuro. O treino acima foi baseado em uma vide-aula do canal Outspoken Market do Leandro Guerra, na qual é realizado o modelo em Python e como forma de desafio fiz em R.
O modelo apresentou os resultados na média de 53,55607% de acurácia no acerto da direção que pode ser considerado baixo, entretanto, a utilização de um outro algoritmo pode dar um ganho de acurácia e lembrando que isto é apenas um exemplo.