TidyQuant - Analise financeira no R

Pacotes

Para realizar as análises de performance das ações negociadas na B3 vamos utilizar o pacote tidyquant.

Dados

Os dados abaixo possuem uma série de informações sobre as empresas negociadas no B3, com atributos de valor de abertura, fechamento, fechamento ajustado e volume negociado para cada dia do período entre Janeiro de 2019 e Março de 2021. Temos ainda informações sobre o setor e subsetor da economia que a empresa atua.

Para nossa análise, vamos trabalhar apenas com o valor de fechamento ajustado das ações.

O pacote TidyQuant fornece uma função bastante conveniente para baixar dados de ações com apenas o nome do ticker. A função é a tq_get e poderiamos baixar as ações da Vale, Itaú e Petrobras com o seguinte código:

tickers <- c("ITSA3.SA", "PETR3.SA", "VALE3.SA")

tickers %>% 
  tq_get(get  = "stock.prices",
         from = "2021-01-02",
         to   = "2021-3-01") %>% 
  head(10) %>% 
  knitr::kable()
## Registered S3 method overwritten by 'tune':
##   method                   from   
##   required_pkgs.model_spec parsnip
symboldateopenhighlowclosevolumeadjusted
ITSA3.SA2021-01-0412.3512.4312.0812.2413150011.99251
ITSA3.SA2021-01-0512.2412.2411.6012.1452260011.89453
ITSA3.SA2021-01-0612.1312.4312.0012.2340050011.98271
ITSA3.SA2021-01-0712.3112.7312.2412.6127850012.35503
ITSA3.SA2021-01-0812.6012.7712.2412.5023390012.24725
ITSA3.SA2021-01-1112.5012.5012.0212.0924760011.84554
ITSA3.SA2021-01-1212.0912.5411.8512.2230220011.97292
ITSA3.SA2021-01-1312.2212.3812.0612.1711740011.92393
ITSA3.SA2021-01-1412.2512.5512.1712.5522350012.29625
ITSA3.SA2021-01-1512.4312.4412.0712.0712770011.82595

Contudo, como o objetivo é analisar todas as ações que fazem parte da B3 durante o período entre 2019 e março de 2021 (o que é um procedimento bastante lento), vou importar manualmente estes dados, que foram previamente baixados utilizando a própria função tq_get, e tratados a fim de informar o setor e subsetor de atuação da empresa.

df <- read_csv("empresas_b3_2019_2021.zip") %>% 
  mutate(Data = as.Date(Data)) %>% 
  filter(Atributo == "Adj Close") %>% 
  filter(!is.na(Valor)) %>% 
  select(-Incluir)
## 
## -- Column specification --------------------------------------------------------
## cols(
##   Data = col_date(format = ""),
##   Atributo = col_character(),
##   Empresa = col_character(),
##   Valor = col_double(),
##   Setor = col_character(),
##   Subsetor = col_character(),
##   Segmento = col_character(),
##   Nome = col_character(),
##   Ticker = col_character(),
##   Incluir = col_double()
## )
df %>% 
  head() %>% 
  knitr::kable()
DataAtributoEmpresaValorSetorSubsetorSegmentoNomeTicker
2019-01-02Adj CloseCSAN3.SA34.20791Petróleo, Gás e BiocombustíveisPetróleo, Gás e BiocombustíveisExploração, Refino e DistribuiçãoCOSANCSAN3.SA
2019-01-03Adj CloseCSAN3.SA34.72534Petróleo, Gás e BiocombustíveisPetróleo, Gás e BiocombustíveisExploração, Refino e DistribuiçãoCOSANCSAN3.SA
2019-01-04Adj CloseCSAN3.SA34.52411Petróleo, Gás e BiocombustíveisPetróleo, Gás e BiocombustíveisExploração, Refino e DistribuiçãoCOSANCSAN3.SA
2019-01-07Adj CloseCSAN3.SA34.01627Petróleo, Gás e BiocombustíveisPetróleo, Gás e BiocombustíveisExploração, Refino e DistribuiçãoCOSANCSAN3.SA
2019-01-08Adj CloseCSAN3.SA34.38997Petróleo, Gás e BiocombustíveisPetróleo, Gás e BiocombustíveisExploração, Refino e DistribuiçãoCOSANCSAN3.SA
2019-01-09Adj CloseCSAN3.SA37.19751Petróleo, Gás e BiocombustíveisPetróleo, Gás e BiocombustíveisExploração, Refino e DistribuiçãoCOSANCSAN3.SA

A base de dados possui informações para 286 empresas, divididas em 11 setores e 42 subsetores. Temos informações de janeiro de 2019 a março de 2021.

df %>% summarise(`Total de Empresas` = n_distinct(Nome),
                 `Total de Setores` = n_distinct(Setor),
                 `Total de Subsetores` = n_distinct(Subsetor),
                 `Data de Início` = min(Data),
                 `Data Final` = max(Data)) %>% 
  mutate(across(everything(), as.character)) %>% 
  pivot_longer(everything()) %>% 
  rename("Atributo" = 1, "Valor" = 2) %>% 
  knitr::kable()
AtributoValor
Total de Empresas286
Total de Setores11
Total de Subsetores42
Data de Início2019-01-02
Data Final2021-03-01

Retorno Mensal

Para realizar as comparações propostas, precisamos transformar os preços de fechamento em retornos.

A função tq_transmute permite calcular o retorno mensal para o valor de fechamento ajustado da ação. Para isto, precisamos indicar qual a variável que sofrerá a transformação (Valor), qual a transformação utilizada (mutate_fun = periodReturn) e qual a periodicidade do cálculo (period = "monthly").

Vamos guardar as informações de retorno das ações no objeto Ra.

Ra <- df %>% 
  select(Data, Empresa, Valor) %>% 
  group_by(Empresa) %>% 
  tq_transmute(select = Valor,
               mutate_fun = periodReturn,
               period = "monthly",
               col_rename = "Ra")
Ra %>% 
  head() %>% 
  knitr::kable()
EmpresaDataRa
CSAN3.SA2019-01-310.2434172
CSAN3.SA2019-02-28-0.0155440
CSAN3.SA2019-03-29-0.0240274
CSAN3.SA2019-04-300.0989447
CSAN3.SA2019-05-310.0277442
CSAN3.SA2019-06-28-0.0210013

Essa base de dados possui três colunas: Data, que representa o último dia do mês, a coluna Ra, que exibe o retorno mensal e Empresa, o ticker de cada empresa.

Como os retornos normalizam os valores, podemos realizar comparações entre diferentes setores, apenas para entender como as empresas foram afetadas pela Covid-19:

Ra %>% 
  left_join(df %>% select(Empresa, Setor), by = "Empresa") %>% 
  group_by(Setor, Data) %>% 
  summarise(Ra = mean(Ra)) %>% 
  ggplot(aes(x = Data, y = Ra, color = Setor)) + 
  geom_line() + 
  geom_vline(xintercept = as.Date("2020-03-01"), linetype = 2,
             color = "grey20") +
  labs(y = "Retorno Mensal Médio", y = "data",
       title = "Retorno Mensal Médio por Setor da B3, 2019 - 2021") + 
  facet_wrap(~Setor, nrow = 3) + 
  theme_minimal() +
  theme(legend.position = "none", 
        axis.text.x = element_text(angle = 45))
## `summarise()` has grouped output by 'Setor'. You can override using the `.groups` argument.

Retorno Livre de Risco

A fim de comparar o comportamento dos retornos de cada ação, precisamos de um grupo de comparação, também chamado de baseline. Como grupo de comparação, vamos utilizar o valor negociado do Índice Bovespa (BVSP).

Podemos utilizar a função tq_get para obter os dados do ticker ^BVSP para o mesmo período de análise e transformar as informações de fechamento em retorno mensal, e unir com as informações de Ra:

Rb <- "^BVSP" %>%
  tq_get(get  = "stock.prices",
         from = "2019-01-02",
         to   = "2021-03-01") %>%
  tq_transmute(select     = adjusted, 
               mutate_fun = periodReturn, 
               period     = "monthly", 
               col_rename = "Rb")

RaRb <- 
  left_join(Ra, 
            Rb, 
            by = c("Data" = "date"))

RaRb %>% 
  head() %>% 
  knitr::kable()
EmpresaDataRaRb
CSAN3.SA2019-01-310.24341720.0701226
CSAN3.SA2019-02-28-0.0155440-0.0185843
CSAN3.SA2019-03-29-0.0240274-0.0017681
CSAN3.SA2019-04-300.09894470.0098307
CSAN3.SA2019-05-310.02774420.0070262
CSAN3.SA2019-06-28-0.02100130.0405751

Razões de Risco

Aqui vamos apresentar cinco razões de risco bastante populares: Alpha, Beta, o Desvio-padrão, R-squared e a Razão de Sharpe.

Razão de Sharpe

A Razão de Sharpe é comumente utilizada como uma medida de retorno por unidade de risco, e tem a seguinte fórmula

\[\frac{r_P - r_F}{\sigma_P}\]

onde \(r_p\) é o retorno do portfólio, \(r_F\) é a taxa livre de risco e \(\sigma_P\) é o risco do portfólio (desvio-padrão do retorno) normalizada e anualizada.

Quando maior a razão Sharpe, melhor é a combinação de risco e retorno.

Abaixo, um exemplo de como funciona a Razão de Sharp:

Portfólio APortfólio B
Retorno7,9%6,9%
Risco5,5%3,2%
Taxa livre de risco2,0%2,0%
Razão de Sharpe\(\frac{7,9\% - 2,0\%}{5,5\%} = 1,07\)\(\frac{6,9\% - 2,0\%}{3,2\%} = 1,53\)

Portanto, o portifólio B possui uma melhor performance ajustada ao risco do que o portfólio A.

Agora podemos calcular para nosso banco de dados. Vamos utilizar a função tq_performance, que toma os seguintes argumentos: o conjunto de retornos Ra; Rf, que é a taxa livre de risco, p, que é o intervalo de confiança (de 95% no nosso caso) e FUN, que é o valor do denominador (onde utilizaremos o desvio-padrão). Por simplificação vamos usar uma taxa livre de risco de zero.

Ra_Sharpe <- RaRb %>% 
  filter(!Empresa %in% c('JSLG3.SA', "TIMS3.SA","MGEL3.SA")) %>% 
  tq_performance(Ra = Ra,
                 Rb = NULL,
                 performance_fun = SharpeRatio,
                 Rf = 0,
                 p = 0.95,
                 FUN = "StdDev")

Ra_Sharpe %>% 
  head() %>% 
  knitr::kable()
EmpresaStdDevSharpe(Rf=0%,p=95%)
CSAN3.SA0.3373760
DMMO3.SA-0.0493056
ENAT3.SA0.2675225
RPMG3.SA0.1077110
PETR3.SA0.0503555
BRDT3.SA0.0333009

Podemos filtrar para os 10 ativos com maior valor da razão de Sharpe.

Ra_Sharpe %>% 
  rename(Razao_Sharpe = 2) %>% 
  arrange(desc(Razao_Sharpe)) %>% 
  head(10) %>% 
  knitr::kable()
EmpresaRazao_Sharpe
REDE3.SA0.9270783
ENEV3.SA0.7157285
BAUH4.SA0.6367148
WEGE3.SA0.5623846
MGLU3.SA0.5230350
GPCP3.SA0.5103846
BMEB3.SA0.4654880
PRIO3.SA0.4576669
KEPL3.SA0.4574844
RSUL4.SA0.4483069

CAPM

O pacote TidyQuant também permite estimar o modelo CAPM para as ações. No modelo CAPM podemos fatorar a taxa livre de risco e calcular a seguinte equação:

\[r_p - r_F = \alpha + \beta \times (b - r_F) + \epsilon\]

Para estimar o modelo CAPM, vamos utilizar a função tq_performance novamente, mas agora passando o argumento performance_fun = table.CAPM. Como visto na equação acima, ela apresenta uma série de novos parâmetros: como beta e alpha.

Mas primeiro, vamos gerar as informações da tabela CAPM:

RaRb_capm <- RaRb %>% 
  # essas empresas possuem informações faltantes e serão excluídas
  filter(!Empresa %in% c('JSLG3.SA', "TIMS3.SA","MGEL3.SA")) %>% 
  tq_performance(Ra = Ra,
                 Rb = Rb,
                 performance_fun = table.CAPM)
RaRb_capm %>% 
  head(10) %>% 
  knitr::kable()
EmpresaActivePremiumAlphaAnnualizedAlphaBetaBeta-Beta+CorrelationCorrelationp-valueInformationRatioR-squaredTrackingErrorTreynorRatio
CSAN3.SA0.40040.02910.41121.05590.90350.71680.74700.00001.46450.55800.27340.4527
DMMO3.SA-0.7680-0.0386-0.37611.79241.72761.89200.37840.0567-0.59530.14321.2901-0.3737
ENAT3.SA0.21780.01910.25561.00811.17740.45740.70350.00010.71790.49490.30340.3362
RPMG3.SA-0.11340.04550.70490.95001.0682-3.30470.15830.4400-0.06610.02511.7155-0.0222
PETR3.SA-0.1499-0.0106-0.11961.68131.61472.32370.91350.0000-0.50850.83440.2948-0.0350
BRDT3.SA-0.1319-0.0085-0.09701.15691.34280.49570.82420.0000-0.55730.67920.2366-0.0379
PRIO3.SA1.53050.09171.86632.19542.51733.41740.73690.00002.25760.54300.67800.7249
UGPA3.SA-0.2222-0.0169-0.18511.37150.91491.53460.83200.0000-0.77520.69220.2867-0.1004
LUPA3.SA-0.00580.00710.08811.41021.66831.44900.68700.0001-0.01290.47200.44880.0601
OSXB3.SA0.98300.10972.48821.37321.68424.55730.25290.21250.64450.06401.52530.7275

Agora vamos entender cada um dos parâmetros do modelo CAPM, a começar pelo Beta.

Beta (\(\beta\))

Ele representa o risco sistêmico ou a volatilidade de uma ação específica comparada com o risco sistêmico do mercado como um todo. De maneira geral, um Beta igual a 1 representa uma atividade bastante correlacionada com o mercado. Adicionar uma ação com beta igual a 1 a um portfólio não adiciona nenhum risco a ele. Um beta menor que a unidade significa que a ação é menos volátil que a média do mercado, de modo que sua inclusão reduz o risco médio do porfólio. Por exemplo, ações de empresas de utilidades públicas tendem a ter um beta baixo.

Já empresas com beta maior que a unidade indicam que o preço da ação é teoricamente mais volátil que o mercado. Se uma ação possui beta igual a 1,2 podemos assumir que ele é 20% mais volátil que o mecado. Ações de tecnologia tendem a ter maiores betas que o benchmark de mercado. Isto indica que sua adição ao portfólio aumenta o risco (mas também pode aumentar o retorno esperado).

Vamos observar os maiores valores de Beta:

RaRb_capm %>% 
  select(Empresa, Beta) %>% 
  arrange(desc(Beta)) %>%
  head(10) %>% 
  left_join(df %>% select(Empresa, Setor), by = "Empresa") %>% 
  distinct()
## # A tibble: 10 x 3
## # Groups:   Empresa [10]
##    Empresa   Beta Setor                          
##    <chr>    <dbl> <chr>                          
##  1 TXRX3.SA  6.06 Consumo Cíclico                
##  2 TCSA3.SA  5.16 Consumo Cíclico                
##  3 MWET3.SA  3.43 Bens Industriais               
##  4 CEPE3.SA  2.86 Utilidade Pública              
##  5 TEKA3.SA  2.78 Consumo Cíclico                
##  6 RCSL3.SA  2.51 Bens Industriais               
##  7 EUCA3.SA  2.49 Materiais Básicos              
##  8 PRIO3.SA  2.20 Petróleo, Gás e Biocombustíveis
##  9 CVCB3.SA  2.13 Consumo Cíclico                
## 10 VVAR3.SA  2.11 Consumo Cíclico

Podemos ver que entre as empresas com maior volatilidade, temos empresas de Petróleo, Consumo Cíclico e até mesmo uma de Utilidade Pública.

Algumas ações possuem beta negativo, isso reflete o fato de que estas empresas são espelhos do mercado. Quando o mercado está em alta, ela está em baixa, e vice-versa. Alguns exemplos:

RaRb_capm %>% 
  select(Empresa, Beta) %>% 
  arrange(Beta) %>% 
  head(10) %>% 
  left_join(df %>% select(Empresa, Setor), by = "Empresa") %>% 
  distinct()
## # A tibble: 10 x 3
## # Groups:   Empresa [10]
##    Empresa     Beta Setor              
##    <chr>      <dbl> <chr>              
##  1 ODER4.SA -3.09   Consumo não Cíclico
##  2 NORD3.SA -0.989  Bens Industriais   
##  3 CEGR3.SA -0.386  Utilidade Pública  
##  4 CEED3.SA -0.313  Utilidade Pública  
##  5 JOPA3.SA -0.301  Consumo não Cíclico
##  6 LUXM3.SA -0.265  Bens Industriais   
##  7 BMIN3.SA -0.230  Financeiro         
##  8 AHEB3.SA -0.196  Consumo Cíclico    
##  9 SLCE3.SA -0.0809 Consumo não Cíclico
## 10 REDE3.SA -0.0638 Utilidade Pública

Contudo, um valor pequeno de beta apenas informa que a volatilidade do preço é baixa. Mas uma empresa pode ter pequenas variações de preço e estar em uma tendência de longo prazo de baixa. Assim, adicionar uma ação com tendência de baixa com um beta pequeno diminui o risco do portfólio apenas se o investidor define risco estritamente em termos de volatilidade, em vez de potencial para perdas. Da mesma foram, uma ação com beta elevado mas que está em um tendência de alta vai aumentar o risco do portfólio, mas também irá adicionar valor.

Portanto é preciso utilizar o beta com cuidado, sempre em conjunto com outras medidas. Assim, o beta pode ser util para avaliar o risco de curto prazo de uma ação, não sendo muito útil quando se deseja fazer investimentos de longo prazo, uma vez que a volatilidade da ação pode mudar bastante no longo prazo.

Temos ainda:

  • O Beta Bull ou \(\beta^+\) calcula a regressão apenas para retornos de mercado positivos, de modo que trás informações no comportamento do portfólio de mercados positivos.

  • O Beta Bear ou \(\beta^-\) calcula a regressão apenas para retornos de mercado negativos.

Alpha ou Alpha de Jenson

Alpha representa o intercepto da equação de regressão no CAPM, e reflete o grau com que o retorno de uma ação está em linha (ou excede) o retorno gerado pelo mercado. Assim, o alpha representa os retornos sobre o investimento que não são o resultado de um movimento geral de mercado. O alpha de Jensen especificamente, inclui um componente de risco ajustado nas suas contas.

Uma ação com alpha igual a zero possui retornos em linha com o presente no mercado. Por outro lado, um alpha negativo indica que a ação não gera retornos na mesma taxa que o setor como um todo.

RaRb_capm %>% 
  select(Empresa, Alpha) %>% 
  arrange(desc(Alpha)) %>% 
  head(10) %>% 
  left_join(df %>% select(Empresa, Setor), by = "Empresa") %>% 
  distinct() %>% 
  knitr::kable()
EmpresaAlphaSetor
ODER4.SA0.5959Consumo não Cíclico
TXRX3.SA0.5785Consumo Cíclico
GPAR3.SA0.4351Utilidade Pública
MWET3.SA0.2814Bens Industriais
NORD3.SA0.2795Bens Industriais
TCSA3.SA0.2692Consumo Cíclico
MMXM3.SA0.2243Materiais Básicos
CEPE3.SA0.1966Utilidade Pública
TEKA3.SA0.1922Consumo Cíclico
TELB3.SA0.1709Comunicações

Tracking Error

Medidas de risco absoluto como analisadadas anteriormente calculam o risco do portfólio e do benchmarking calculados separadamente, para posterior comparação. O tracking error é um exemplo de uma medida de risco relativa, cujo foco é no retorno em excesso do portfólio contra o benchmarking. A variabilidade de retorno em excesso é calculada usando o desvio-padrão, e é chamada de tracking error.

Sua fórmula é dada por

\[TE = \sqrt{\frac{\sum_{i=1}^{n}(a_i - \bar{a})^2}{n}}\] onde \(a_i\) é o retorno em excesso no mês \(i\) e \(\bar{a}\) é a média aritmética do excesso de retorno.

Ele representa a diferença entre a performance observada entre uma posição (ação ou porfólio) e do seu benchmarking. Assim, mede a consistência de um porfólio contra um benchmarking ao longo de um dado período. Mesmo portfólios que são perfeitamente indexados contra um benchmarking se comportam diferentemente de um benchmarking. Assim, o tracking error é utilizado para quantificar essas diferenças.

Se o retorno do investimento for baixo mas possui um valor alto de tracking error, este é um sinal de que uma mudança de portfólio é necessária.

Se o tracking error for elevado significa que o retorno do portfólio é mais volátil ao longo do tempo e não tão consiste em bater o benchmarking.

RaRb_capm %>% 
  select(Empresa, TrackingError) %>% 
  arrange(desc(TrackingError)) %>% 
  head(10) %>% 
  left_join(df %>% select(Empresa, Setor), by = "Empresa") %>% 
  distinct() %>% 
  knitr::kable()
EmpresaTrackingErrorSetor
TXRX3.SA10.0619Consumo Cíclico
ODER4.SA9.5899Consumo não Cíclico
GPAR3.SA7.4202Utilidade Pública
TCSA3.SA5.7209Consumo Cíclico
MWET3.SA5.5682Bens Industriais
CEPE3.SA3.9775Utilidade Pública
MMXM3.SA3.6660Materiais Básicos
TELB3.SA3.3057Comunicações
TEKA3.SA3.2550Consumo Cíclico
NORD3.SA3.1530Bens Industriais

Razão de Informação

A razão de informação é similar a Razão de Sharpe, só que ao invés de comparar retornos absolutos, temos retornos em excesso, e em vez de risco absoluto, temos risco relativo (tracking error). Geralmente são calculados utilizados retornos anuais. Assim, a Razão de Informação mostra o quanto um portfólio excedeu um benchmarking.

Abaixo, um exemplo hipotético em que o índice tenha retorno anualizado de -1,5%:

PortfólioRetorno anualizadoTracking ErrorRazão de Informação
A13%8%\((13-(-1,5))/8) = 1,81\)
B8%4,5%\((8 - (-1,5))/4,5) = 2,11\)

Assim, apesar do portfólio B tem retornos menores que A, ele tem melhor RI, em parte, porque ele tem menor desvio padrão ou tracking error, que significa menos risco e mais consistência do portfólio relativo ao benchmarking.

RaRb_capm %>% 
  select(Empresa, InformationRatio) %>% 
  arrange(desc(InformationRatio)) %>% 
  head(10) %>% 
  left_join(df %>% select(Empresa, Setor), by = "Empresa") %>% 
  distinct() %>% 
  knitr::kable()
EmpresaInformationRatioSetor
ENEV3.SA4.2181Utilidade Pública
BAUH4.SA2.7556Consumo não Cíclico
GPCP3.SA2.7504Materiais Básicos
WEGE3.SA2.7054Bens Industriais
MGLU3.SA2.4541Consumo Cíclico
PRIO3.SA2.2576Petróleo, Gás e Biocombustíveis
CSNA3.SA1.7580Materiais Básicos
HAPV3.SA1.7489Saúde
IGBR3.SA1.7337Financeiro
KEPL3.SA1.6978Bens Industriais

R Quadrado

O R quadrado informa o quão correlacionado um ativo ou portfólio está com o benchmarking. R-squared mede o grau com que a performance de um portfólio pode ser atribuida a performance de um índice de benchmarking.

RaRb_capm %>% 
  select(Empresa, `R-squared`) %>% 
  arrange(desc(`R-squared`)) %>% 
  head(10) %>% 
  left_join(df %>% select(Empresa, Setor), by = "Empresa") %>% 
  distinct() %>% 
  knitr::kable()
EmpresaR-squaredSetor
PETR3.SA0.8344Petróleo, Gás e Biocombustíveis
BBAS3.SA0.8130Financeiro
SMLS3.SA0.8095Consumo Cíclico
CVCB3.SA0.8068Consumo Cíclico
VIVA3.SA0.7947Consumo Cíclico
IGTA3.SA0.7832Financeiro
JPSA3.SA0.7800Financeiro
MULT3.SA0.7759Financeiro
BBDC3.SA0.7630Financeiro
CYRE3.SA0.7475Consumo Cíclico
Robson Oliveira
Robson Oliveira
Professor de Economia