Especificando as bibliotecas

library(yfR)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.2.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.0.4     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(ggthemes)
library(e1071)
library(FinTS)
## Carregando pacotes exigidos: zoo
## 
## Anexando pacote: 'zoo'
## 
## Os seguintes objetos são mascarados por 'package:base':
## 
##     as.Date, as.Date.numeric
library(WriteXLS)
library(xtable)
library(tbl2xts)
library(forecast)
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo 
## 
## Anexando pacote: 'forecast'
## 
## O seguinte objeto é mascarado por 'package:FinTS':
## 
##     Acf
library(tseries)
library(timeSeries)
## Carregando pacotes exigidos: timeDate
## 
## Anexando pacote: 'timeDate'
## 
## O seguinte objeto é mascarado por 'package:xtable':
## 
##     align
## 
## Os seguintes objetos são mascarados por 'package:e1071':
## 
##     kurtosis, skewness
## 
## 
## Anexando pacote: 'timeSeries'
## 
## O seguinte objeto é mascarado por 'package:zoo':
## 
##     time<-
## 
## O seguinte objeto é mascarado por 'package:dplyr':
## 
##     lag
## 
## Os seguintes objetos são mascarados por 'package:graphics':
## 
##     lines, points
library(xts)
## 
## ######################### Warning from 'xts' package ##########################
## #                                                                             #
## # The dplyr lag() function breaks how base R's lag() function is supposed to  #
## # work, which breaks lag(my_xts). Calls to lag(my_xts) that you type or       #
## # source() into this session won't work correctly.                            #
## #                                                                             #
## # Use stats::lag() to make sure you're not using dplyr::lag(), or you can add #
## # conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop           #
## # dplyr from breaking base R's lag() function.                                #
## #                                                                             #
## # Code in packages is not affected. It's protected by R's namespace mechanism #
## # Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning.  #
## #                                                                             #
## ###############################################################################
## 
## Anexando pacote: 'xts'
## 
## Os seguintes objetos são mascarados por 'package:dplyr':
## 
##     first, last
library(corrplot)
## corrplot 0.95 loaded
library(knitr)
library(kableExtra)
## 
## Anexando pacote: 'kableExtra'
## 
## O seguinte objeto é mascarado por 'package:dplyr':
## 
##     group_rows
library(GGally)
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
library(rugarch)
## Carregando pacotes exigidos: parallel
## 
## Anexando pacote: 'rugarch'
## 
## O seguinte objeto é mascarado por 'package:purrr':
## 
##     reduce
## 
## O seguinte objeto é mascarado por 'package:stats':
## 
##     sigma
library(WriteXLS)
library(tbl2xts)
rm(list=ls())
tickers = c("^BVSP","EWZ","BRAX11.SA")
indices_dados <- yfR::yf_get(tickers,
                             first_date = '2023-01-01',
                             last_date = '2025-01-01',
                             type_return = "log",
                             freq_data = "daily")
## 
## ── Running yfR for 3 stocks | 2023-01-01 --> 2025-01-01 (731 days) ──
## 
## ℹ Downloading data for benchmark ticker ^GSPC
## ℹ (1/3) Fetching data for ^BVSP
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 499 valid rows (2023-01-02 --> 2024-12-30)
## ✔    - got 97% of valid prices -- Youre doing good!
## ℹ (2/3) Fetching data for BRAX11.SA
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 499 valid rows (2023-01-02 --> 2024-12-30)
## ✔    - got 97% of valid prices -- Good job agariel!
## ℹ (3/3) Fetching data for EWZ
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 502 valid rows (2023-01-03 --> 2024-12-31)
## ✔    - got 100% of valid prices -- Good job agariel!
## ℹ Binding price data
## 
## ── Diagnostics ─────────────────────────────────────────────────────────────────
## ✔ Returned dataframe with 1500 rows -- Well done agariel!
## ℹ Using 209.9 kB at /tmp/RtmpFv2MSr/yf_cache for 4 cache files
## ℹ Out of 3 requested tickers, you got 3 (100%)

visualizando os dados

str(indices_dados)
## tibble [1,500 × 11] (S3: tbl_df/tbl/data.frame)
##  $ ticker                : chr [1:1500] "^BVSP" "^BVSP" "^BVSP" "^BVSP" ...
##  $ ref_date              : Date[1:1500], format: "2023-01-02" "2023-01-03" ...
##  $ price_open            : num [1:1500] 109734 106377 104167 105336 107642 ...
##  $ price_high            : num [1:1500] 109734 106684 105627 107743 109433 ...
##  $ price_low             : num [1:1500] 105981 103852 103915 105333 107642 ...
##  $ price_close           : num [1:1500] 106376 104166 105334 107518 108836 ...
##  $ volume                : num [1:1500] 8130500 14466700 14451200 15512000 12626600 ...
##  $ price_adjusted        : num [1:1500] 106376 104166 105334 107518 108836 ...
##  $ ret_adjusted_prices   : num [1:1500] NA -0.021 0.0112 0.0205 0.0122 ...
##  $ ret_closing_prices    : num [1:1500] NA -0.021 0.0112 0.0205 0.0122 ...
##  $ cumret_adjusted_prices: num [1:1500] 1 0.979 0.99 1.011 1.023 ...
##  - attr(*, "df_control")= tibble [3 × 5] (S3: tbl_df/tbl/data.frame)
##   ..$ ticker              : chr [1:3] "^BVSP" "BRAX11.SA" "EWZ"
##   ..$ dl_status           : chr [1:3] "OK" "OK" "OK"
##   ..$ n_rows              : int [1:3] 499 499 502
##   ..$ perc_benchmark_dates: num [1:3] 0.966 0.966 1
##   ..$ threshold_decision  : chr [1:3] "KEEP" "KEEP" "KEEP"
summary(indices_dados)
##     ticker             ref_date            price_open       
##  Length:1500        Min.   :2023-01-02   Min.   :    22.56  
##  Class :character   1st Qu.:2023-07-03   1st Qu.:    32.20  
##  Mode  :character   Median :2024-01-03   Median :   103.61  
##                     Mean   :2023-12-31   Mean   : 40378.01  
##                     3rd Qu.:2024-07-03   3rd Qu.:114801.50  
##                     Max.   :2024-12-31   Max.   :137349.00  
##                                                             
##    price_high          price_low          price_close            volume        
##  Min.   :    22.60   Min.   :    22.26   Min.   :    22.40   Min.   :       0  
##  1st Qu.:    32.45   1st Qu.:    32.00   1st Qu.:    32.25   1st Qu.:    2433  
##  Median :   104.30   Median :   102.97   Median :   103.56   Median :10258000  
##  Mean   : 40646.25   Mean   : 40123.79   Mean   : 40383.24   Mean   :11207226  
##  3rd Qu.:115507.00   3rd Qu.:113980.75   3rd Qu.:114828.25   3rd Qu.:17767750  
##  Max.   :137469.00   Max.   :136664.00   Max.   :137344.00   Max.   :65477800  
##                                                                                
##  price_adjusted      ret_adjusted_prices  ret_closing_prices  
##  Min.   :    22.03   Min.   :-0.0689929   Min.   :-6.899e-02  
##  1st Qu.:    29.30   1st Qu.:-0.0063940   1st Qu.:-6.481e-03  
##  Median :   103.56   Median : 0.0000000   Median : 0.000e+00  
##  Mean   : 40382.37   Mean   : 0.0001658   Mean   : 7.431e-05  
##  3rd Qu.:114828.25   3rd Qu.: 0.0070353   3rd Qu.: 6.987e-03  
##  Max.   :137344.00   Max.   : 0.0516589   Max.   : 5.166e-02  
##                      NA's   :3            NA's   :3           
##  cumret_adjusted_prices
##  Min.   :0.9131        
##  1st Qu.:1.0917        
##  Median :1.1689        
##  Mean   :1.1496        
##  3rd Qu.:1.2056        
##  Max.   :1.3684        
## 
head(indices_dados)

Reestruturando o data frame. Filtrando somente o preco de fechamento, data e ticker.

indices_dados[is.na(indices_dados)] = 0 #lidando com valores NA
a = c(2,1,10,6) #data, ticker, retorno e preço de fechamento 
indices_dados = indices_dados[,a]
names(indices_dados) = c("Data","Ativo","Retorno","Preco")

Plotando os gráficos

Precos

p= ggplot(indices_dados) + geom_line(mapping = aes(x=Data,y=Preco),color='palegreen4') +
  facet_wrap(~Ativo)+
  labs(x = "",y='Preco Fechamento',title="Cotacao Diaria dos Ativos",
         subtitle = "Periodo: de 01/01/2023 a 01/01/2025", 
         caption = "Fonte: Yahoo Finance")+
  theme_grey()
p

Se quisermos comparar os ativos, visualizando-os no mesmo grafico

p1 = ggplot(indices_dados,aes(x=Data,y=Preco, color = factor(Ativo))) + geom_line()+
  labs(x = "",y='Preco Fechamento',title="Cotacao Diaria dos Ativos",
         subtitle = "Periodo: de 01/01/2014 a 10/01/2024", color= "Ativo",
         caption = "Fonte: B3") + theme_gray()
p1

Enquanto a bovespa é um índice de preços, os outros ativos são ETFs, e a escala é desproporcional. Usandos os log retornos é possível compará-los adequadamente.

Retornos diarios

p3 = ggplot(indices_dados) + geom_line(mapping = aes(x=Data,y=Retorno),color='tomato') +
  facet_wrap(~Ativo)+
  labs(x = "",y='Retornos',title="Retornos Diarios dos Ativos",
         subtitle = "Periodo: de 01/01/2014 a 10/01/2024", 
         caption = "Fonte: B3")+
  theme_grey()
p3

Retornos absolutos

p4 = ggplot(indices_dados) + geom_line(mapping = aes(x=Data,y=abs(Retorno)),color='orchid3') +
  facet_wrap(~Ativo)+
  labs(x = "",y='Retornos absolutos',title="Retornos Absolutos Diarios dos Ativos",
         subtitle = "Periodo: de 01/01/2014 a 10/01/2024", 
         caption = "Fonte: B3")+
  theme_grey()
p4

QQplot

p5=ggplot(indices_dados,aes(sample = Retorno)) + stat_qq()+ stat_qq_line() +
  facet_wrap(~Ativo)+
  labs(x = "Teorico",y='Amostra',title="QQplot",
         subtitle = "Periodo: de 01/01/2014 a 10/01/2024", 
         caption = "Fonte: B3")+
  theme_grey()
p5

Histograma

p6=ggplot(indices_dados) +
  geom_histogram(aes(x=Retorno, y = after_stat(density)),
                 color="grey60", fill="mintcream",linetype="solid",alpha = 0.8) +
  geom_density(aes(x = Retorno,y = after_stat(density)),color="black") +
  facet_wrap(~Ativo)+
  labs(x = "",y='Densidade',title="Histogramas",
         subtitle = "Periodo: de 01/01/2014 a 10/01/2024", 
         caption = "Fonte: B3")+
  theme_grey()
p6
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

A fim de calcular as variáveis descritivas, vamos separar o Data Frame em que cada coluna é o log retorno de um ativo.

# Listar os ativos (atualizando com os ativos do seu código)
ativos = unique(indices_dados$Ativo)

# Criar um novo DataFrame com o log-retorno de cada ativo
atv.dados = indices_dados %>% 
  select(Data, Ativo, Retorno) %>% 
  spread(key = Ativo, value = Retorno)  # Espalhando as colunas para cada ativo

# Conferindo como ficou o DataFrame
head(atv.dados)

Valores mínimo, máximo, média, mediana e quartis.

summary(atv.dados)
##       Data                ^BVSP              BRAX11.SA         
##  Min.   :2023-01-02   Min.   :-0.0319903   Min.   :-0.0326752  
##  1st Qu.:2023-07-03   1st Qu.:-0.0054890   1st Qu.:-0.0050768  
##  Median :2024-01-02   Median :-0.0000580   Median : 0.0000939  
##  Mean   :2024-01-01   Mean   : 0.0002462   Mean   : 0.0002570  
##  3rd Qu.:2024-07-02   3rd Qu.: 0.0064691   3rd Qu.: 0.0060662  
##  Max.   :2024-12-31   Max.   : 0.0419842   Max.   : 0.0423149  
##                       NA's   :17           NA's   :17          
##       EWZ            
##  Min.   :-0.0689929  
##  1st Qu.:-0.0088478  
##  Median : 0.0000000  
##  Mean   :-0.0002787  
##  3rd Qu.: 0.0088597  
##  Max.   : 0.0516588  
##  NA's   :14
atv.dados[is.na(atv.dados)] = 0
summary(atv.dados)
##       Data                ^BVSP              BRAX11.SA         
##  Min.   :2023-01-02   Min.   :-0.0319903   Min.   :-0.0326752  
##  1st Qu.:2023-07-03   1st Qu.:-0.0052995   1st Qu.:-0.0048816  
##  Median :2024-01-02   Median : 0.0000000   Median : 0.0000000  
##  Mean   :2024-01-01   Mean   : 0.0002381   Mean   : 0.0002486  
##  3rd Qu.:2024-07-02   3rd Qu.: 0.0062688   3rd Qu.: 0.0058338  
##  Max.   :2024-12-31   Max.   : 0.0419842   Max.   : 0.0423149  
##       EWZ            
##  Min.   :-0.0689929  
##  1st Qu.:-0.0085956  
##  Median : 0.0000000  
##  Mean   :-0.0002711  
##  3rd Qu.: 0.0086657  
##  Max.   : 0.0516588

Desvio padrão, variância, curtose e assimetria.

resultados <- sapply(atv.dados, function(x) {
  if(is.numeric(x)) {
    list(
      desvio_padrao = sd(x, na.rm = TRUE),
      variancia = var(x, na.rm = TRUE),
      curtose = kurtosis(x, na.rm = TRUE),
      assimetria = skewness(x, na.rm = TRUE)
    )
  }
})
print(resultados)
## $Data
## NULL
## 
## $`^BVSP`
## $`^BVSP`$desvio_padrao
## [1] 0.009315028
## 
## $`^BVSP`$variancia
## [1] 8.676974e-05
## 
## $`^BVSP`$curtose
## [1] 0.8474032
## attr(,"method")
## [1] "excess"
## 
## $`^BVSP`$assimetria
## [1] 0.1076411
## attr(,"method")
## [1] "moment"
## 
## 
## $BRAX11.SA
## $BRAX11.SA$desvio_padrao
## [1] 0.009134101
## 
## $BRAX11.SA$variancia
## [1] 8.34318e-05
## 
## $BRAX11.SA$curtose
## [1] 1.154936
## attr(,"method")
## [1] "excess"
## 
## $BRAX11.SA$assimetria
## [1] -0.006789242
## attr(,"method")
## [1] "moment"
## 
## 
## $EWZ
## $EWZ$desvio_padrao
## [1] 0.01490216
## 
## $EWZ$variancia
## [1] 0.0002220743
## 
## $EWZ$curtose
## [1] 0.9773584
## attr(,"method")
## [1] "excess"
## 
## $EWZ$assimetria
## [1] -0.228032
## attr(,"method")
## [1] "moment"
ajustar_modelos_garch <- function(serie_retornos, nome_ativo) {
  modelos <- list(
    garch.norm = ugarchspec(
      variance.model = list(model = "sGARCH", garchOrder = c(1,1)),
      mean.model = list(armaOrder = c(0,0), include.mean = FALSE),
      distribution.model = "norm"
    ),
    garch.std = ugarchspec(
      variance.model = list(model = "sGARCH", garchOrder = c(1,1)),
      mean.model = list(armaOrder = c(0,0), include.mean = FALSE),
      distribution.model = "std"
    ),
    egarch.norm = ugarchspec(
      variance.model = list(model = "eGARCH", garchOrder = c(1,1)),
      mean.model = list(armaOrder = c(0,0), include.mean = FALSE),
      distribution.model = "norm"
    ),
    egarch.std = ugarchspec(
      variance.model = list(model = "eGARCH", garchOrder = c(1,1)),
      mean.model = list(armaOrder = c(0,0), include.mean = FALSE),
      distribution.model = "std"
    ),
    gjr.norm = ugarchspec(
      variance.model = list(model = "gjrGARCH", garchOrder = c(1,1)),
      mean.model = list(armaOrder = c(0,0), include.mean = FALSE),
      distribution.model = "norm"
    ),
    gjr.std = ugarchspec(
      variance.model = list(model = "gjrGARCH", garchOrder = c(1,1)),
      mean.model = list(armaOrder = c(0,0), include.mean = FALSE),
      distribution.model = "std"
    )
  )

  resultados <- list()
  for (nome_modelo in names(modelos)) {
    cat("Ajustando modelo:", nome_modelo, "para", nome_ativo, "\n")
    fit <- ugarchfit(spec = modelos[[nome_modelo]], data = serie_retornos)
    resultados[[nome_modelo]] <- fit
  }

  return(resultados)
}
modelo_bvsp <- ajustar_modelos_garch(atv.dados$`^BVSP`, "^BVSP")
## Ajustando modelo: garch.norm para ^BVSP 
## Ajustando modelo: garch.std para ^BVSP 
## Ajustando modelo: egarch.norm para ^BVSP 
## Ajustando modelo: egarch.std para ^BVSP 
## Ajustando modelo: gjr.norm para ^BVSP 
## Ajustando modelo: gjr.std para ^BVSP
modelo_ewz <- ajustar_modelos_garch(atv.dados$EWZ, "EWZ")
## Ajustando modelo: garch.norm para EWZ 
## Ajustando modelo: garch.std para EWZ 
## Ajustando modelo: egarch.norm para EWZ 
## Ajustando modelo: egarch.std para EWZ 
## Ajustando modelo: gjr.norm para EWZ 
## Ajustando modelo: gjr.std para EWZ
modelo_brax <- ajustar_modelos_garch(atv.dados$`BRAX11.SA`, "BRAX11.SA")
## Ajustando modelo: garch.norm para BRAX11.SA 
## Ajustando modelo: garch.std para BRAX11.SA 
## Ajustando modelo: egarch.norm para BRAX11.SA 
## Ajustando modelo: egarch.std para BRAX11.SA 
## Ajustando modelo: gjr.norm para BRAX11.SA 
## Ajustando modelo: gjr.std para BRAX11.SA
comparar_modelos <- function(modelos_fit) {
  sapply(modelos_fit, function(fit) {
    c(
      LogLikelihood = likelihood(fit),
      AIC = infocriteria(fit)[1],
      BIC = infocriteria(fit)[2],
      Persistence = persistence(fit)
    )
  }) %>% t() %>% round(4)
}
comparar_modelos(modelo_bvsp)
##             LogLikelihood     AIC     BIC Persistence
## garch.norm       1690.235 -6.5397 -6.5150      0.9926
## garch.std        1691.678 -6.5414 -6.5085      0.9922
## egarch.norm      1692.820 -6.5458 -6.5129      0.9955
## egarch.std       1693.484 -6.5445 -6.5034      0.9953
## gjr.norm         1694.087 -6.5507 -6.5178      0.9990
## gjr.std          1694.549 -6.5486 -6.5075      0.9990
comparar_modelos(modelo_ewz)
##             LogLikelihood     AIC     BIC Persistence
## garch.norm       1438.867 -5.5654 -5.5407      0.9926
## garch.std        1443.841 -5.5808 -5.5479      0.9977
## egarch.norm      1440.808 -5.5690 -5.5361      0.1757
## egarch.std       1445.276 -5.5825 -5.5413      0.1127
## gjr.norm         1438.838 -5.5614 -5.5285      0.9974
## gjr.std          1443.809 -5.5768 -5.5356      0.9985
comparar_modelos(modelo_brax)
##             LogLikelihood     AIC     BIC Persistence
## garch.norm       1701.410 -6.5830 -6.5583      0.9914
## garch.std        1704.556 -6.5913 -6.5584      0.9922
## egarch.norm      1703.455 -6.5870 -6.5541      0.9945
## egarch.std       1705.493 -6.5911 -6.5499      0.9950
## gjr.norm         1705.171 -6.5937 -6.5608      0.9988
## gjr.std          1706.752 -6.5959 -6.5548      0.9986

O melhor modelo para o Ibovespa

forecast_garch <- ugarchforecast(modelo_bvsp$gjr.std, n.ahead = 10)
plot(forecast_garch, which = 1)  # Série

plot(forecast_garch, which = 3)  # Sigma

O modelo prevê volatilidade elevada e estável nos dias subsequentes, com retornos médios próximos de zero.

O melhor modelo para o MSCI Brazil

forecast_garch <- ugarchforecast(modelo_ewz$egarch.std, n.ahead = 10)
plot(forecast_garch, which = 1)  # Série

plot(forecast_garch, which = 3)  # Sigma

O modelo prevê maior incerteza do que a modelagem para o ibovespa. O fato de ser Egarch captura a assimetria de picos negativos, e a incerteza pode estar relacionada à influência de eventos internacionais.

O melhor modelo para o índice IBrX-100

forecast_garch <- ugarchforecast(modelo_brax$gjr.std, n.ahead = 10)
plot(forecast_garch, which = 1)  # Série

plot(forecast_garch, which = 3)  # Sigma

Nos retornos previstos, as bandas de incerteza são semelhantes às do ibovespa, e o histórico de falta de picos indica volatilidade estável, pelo menos no curto prazo. A previsão da volatilidade condicional indica um valor menor que o dos outros índices, o que sugere um comportamento mais previsível. Estruturlmente, o IBrX-100 é mais diversificado, e por isso menos concentrado em empresas como a petrobras e vale. Isso reforça a ideia de que ele pode ser usado como índice de referência do mercado brasileiro.