1 Objetivo

Objetivo do artigo é criar um modelo que possa prever/ acertar a direção da volatilidade das ações de Petrobrás (PETR4.SA).

2 Metodologia

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?!

3 Coleta dos dados

# dados das acoes de PETR4
PETR4 <- tq_get('PETR4.SA', 
                from = '2010-01-01',
                to = Sys.Date(),
                get = 'stock.prices') %>%
  na.omit()

3.1 Criar as variáveis do modelo

Será utilizado algumas variáveis explicativas para o modelo, sendo elas:

  • Ratio: Proporção do preço de alta em relaçao ao preço de baixa;
  • Prop: A proporção da diferença de fechamento e abertura em relação a diferença da alta e baixa do dia;
  • Os retornos de PETR e a média móvel de 10 dias dos retornos;
  • O desvio padrão de 5 dias anteriores.
## # 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>

3.2 Distribuição da volatilidade de Petrobrás

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`.

3.3 Correlação

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.

4 Criando o modelo - Treino e teste

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

4.1 Modelo de Treino

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)

4.2 Criando a variável de direção

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)

4.3 Alvo vs Predição

resultados %>%
  ggplot(aes(x = predicao, y = Alvo))+
  geom_point(col = 'darkblue')+
  ggthemes::theme_clean()+
  labs(title = 'Volatilidade de PETR4' ,
       x = 'Valores previstos', y = 'Alvo')

4.4 Alvo vs Predição

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')

4.5 Métricas

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

4.6 Residuos

resultados$residuos <- modelo1$residuals
resultados %>%
  ggplot(aes(x = residuos, y= predicao))+
  geom_point(col = 'red')

plot(modelo1)

5 Modelo teste

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)

5.1 Criando a direção

# 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)

5.2 Resultados

# 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')

5.3 Métricas

# 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

6 Modelo Final

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')

6.1 Conclusão

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.

  • LinkedIn: Lucas de Andrade Morador