Este trabalho é dedicado ao curso de Introdução à Ciência de Dados no Mestrado Profissional em Economia e Finanças (MFEE) da FGV - RIO, ministrado pelo professor Rafael Martins.
Nesse estudo em específico, será estudado, através de dados e análises estatísticas, o resultado de uma carteira que utiliza como base a Teoria Moderna do Portfolio na escolha dos pesos dos ativos. Para efeito de comparação, a carteira vai ser comparada com outra que pondera igualmente os ativos.
AVISO: NADA DO QUE SERÁ EXPOSTO NESSE TRABALHO SE TRATA DE UMA RECOMENDAÇÃO DE INVESTIMENTO.
Para realizar esse estudo, foram utilizados alguns pacotes em R. Antes de iniciar a busca dos dados e as análises, é necessário importar os mesmos.
library('tidyquant') #usado para buscar cotações e dados financeiros
library('tidyverse') #pacote para tratamento de dados
library('xts') #pacote de manipulação de objetos do tipo XTS
library('PerformanceAnalytics') # pacote com diversas funções de financas
library('IntroCompFinR') #pacote com mais funções de finanças
library('fPortfolio') #pacote de fronteira eficiente
library("plotly") #pacote para criação de gráficos
Primeiramente, serão definidos os principais parâmetros que vão ser utilizadas. Para esse estudo, foi delimitado o período de análise, quais serão os ativos que vão compor a carteira e o rendimento diário do ativo livre de risco (no caso do mercado brasileiro, será usado o DI do período)
data_inicio = '2018-01-01' #data de início da análise
data_fim = '2022-06-30' #data de fim da análise
tickers = c('VALE3.SA' , 'ITUB3.SA' , 'RENT3.SA' , 'ELET3.SA' , 'VIVT3.SA') #ações que serão analisadas
di_diario_periodo = 0.000218633892891118 #retorno diário médio do DI durante o período (fonte: https://www.b3.com.br/pt_br/market-data-e-indices/indices/indices-de-segmentos-e-setoriais/serie-historica-do-di.htm)
Dadas as varíaveis do estudo, será feito a importação das cotações diárias dos ativos em questão. Para isso, a biblioteca tidyquant vai ser utilizada para realizar essa busca, puxando os dados diretamente do site Yahoo Finance.
Após o download, serão feitos alguns tratamentos para melhor organização dos dados.
cotacoes = tq_get(tickers , from = data_inicio , to = data_fim) %>% #baixando as cotações usando tidyquant
dplyr::ungroup() %>% #resolve um pequeno bug da base de dados
dplyr::select(date , symbol , adjusted) %>% #seleciona as colunas que nos interessam
tidyr::pivot_wider(names_from = symbol, values_from = adjusted) #coloca os ativos em forma de colunas e os valores de preço ajustado nas linhas
cotacoes %>% rmarkdown::paged_table()
Com as cotações presentes, calcularemos o retorno discreto para cada ação, criando assim uma série histórica de retorno (mostrando quanto cada ação rendeu para cada dia no período analisado).
Esse objeto que será criado vai ser do tipo XTS (eXtensible Time Series), formato que será necessário na utilização de pacotes mais a frente.
retornos_ativos = xts::as.xts(cotacoes %>%
dplyr::select(-date), order.by = cotacoes$date) %>%
PerformanceAnalytics::Return.calculate(. ,method = 'discrete') %>%
na.omit()
retornos_ativos %>% as.data.frame(.)%>% rmarkdown::paged_table()
Para obtermos a Fronteira Eficiente e, consequentemente, a carteira ótima (carteira que maximiza o sharpe) , será necessário calcular os retornos esperados, a volatilidade diária e a matriz de covariância dos ativos.
Para o retorno esperado, será calculado a média da série de retornos. Já para a volatilidade, será obtida através do desvio padrão desses retornos.
#media e desvio padrao das acoes
media_desvpad = matrix(data = NA,
nrow = length(tickers),
ncol = 2)
for(i in c(1:length(tickers))) {
media_desvpad[i,1] = mean(retornos_ativos[,i])
media_desvpad[i,2] = sd(retornos_ativos[,i])
}
media_desvpad = as.data.frame(media_desvpad)
media_desvpad = media_desvpad %>% rename(Retorno = V1) %>%
rename(Desv = V2)
rownames(media_desvpad) = tickers
#montando matriz de covariancia
cov_acoes = cov(retornos_ativos)
#media e desvio padrao das acoes
media_desvpad
## Retorno Desv
## VALE3.SA 0.0011901301 0.02602570
## ITUB3.SA 0.0001490917 0.01980024
## RENT3.SA 0.0012833768 0.03029923
## ELET3.SA 0.0016505981 0.03605490
## VIVT3.SA 0.0005741308 0.01801585
#covariancia
cov_acoes
## VALE3.SA ITUB3.SA RENT3.SA ELET3.SA VIVT3.SA
## VALE3.SA 0.0006773370 0.0001690137 0.0002428833 0.0002873608 0.0001043847
## ITUB3.SA 0.0001690137 0.0003920497 0.0002975812 0.0003440513 0.0001274301
## RENT3.SA 0.0002428833 0.0002975812 0.0009180433 0.0004939854 0.0001658424
## ELET3.SA 0.0002873608 0.0003440513 0.0004939854 0.0012999557 0.0002092093
## VIVT3.SA 0.0001043847 0.0001274301 0.0001658424 0.0002092093 0.0003245709
Com todos os dados encontrados até então, será possível construir a Fronteira Eficiente de Markowiz. Para facilitar a construção, será usado o pacote fPortfolio.
fronteira_eficiente = fPortfolio::portfolioFrontier(data =as.timeSeries(retornos_ativos))
frontierPlot(fronteira_eficiente,
pch = 20,
cex = 1,
type = "o",
lwd = 2)
monteCarloPoints(fronteira_eficiente,
mcSteps = 5000,
pch = 20,
cex = 0.1,
col = "blue")
Após encontrar a Fronteira Eficiente, é possível encontrar a carteira ótima. Essa carteira é aquela que maximiza o sharpe, ou seja, apresenta a melhor relação risco x retorno.
Para isso, será utilizado o pacote IntroCompFinR.
carteira_otima = tangency.portfolio(
er = media_desvpad$Retorno, #retorno esperado
cov.mat = cov_acoes, #matriz de covariancia
risk.free = di_diario_periodo, #taxa livre de risco da economia
shorts = FALSE #parâmetro que proíbe o algoritmo de montar posições vendidas (shorts) na carteira
)
pesos_otimos = carteira_otima[["weights"]]
#pesos otimos (VALE3, ITUB3, RENT3, ELET3 e VIVT3)
pesos_otimos
## [1] 0.422068 0.000000 0.237144 0.303260 0.037527
#Retorno esperado carteira
carteira_otima[["er"]]
## [1] 0.001328767
#Desvio padrão
carteira_otima[["sd"]]
## [1] 0.0222835
Após encontrar os pesos dos repectivos ativos para a criação da carteira ótima, será possível fazer simulações com esse portfolio.
Primeiramente, é necessário calcular a série de retornos diários dessa carteira ótima. Para isso, basta multiplicar o retorno de cada ativo pelo respectivo peso. Além disso, vai ser considerado um rebalanceamento mensal da carteira, fazendo com que a proporção dos ativos na carteira volte à alocação ótima sempre no início do mês (simulando, por exemplo, um investidor rebalanceando sua carteira pessoal assim que receber seu salário).
Para auxiliar no cálculo, será utilizado o pacote PerformanceAnalytics.
retornos_carteira = Return.portfolio(
R = retornos_ativos , #XTS dos retornos dos ativos
weights = pesos_otimos, #vetor com os pesos da carteira otima
rebalance_on = 'months'#parametro que define a periodicidade do rebalanceamento
)
## Warning in Return.portfolio.geometric(R = R, weights = weights, wealth.index =
## wealth.index, : The weights for one or more periods do not sum up to 1: assuming
## a return of 0 for the residual weights
retornos_carteira %>% as.data.frame(.)%>% rmarkdown::paged_table()
Tendo em mãos as rentabilidades diárias, é possível calcular o retorno acumulado da carteira. Para visualizar o resultado, o pacote Plotly será usado.
chart.CumReturns(R = retornos_carteira , wealth.index = FALSE , legend.loc = 'topright' , plot.engine = "plotly")
Para podermos fazer uma comparação justa da carteira ótima com outra carteira, utilizaremos os mesmos ativos para a composição da mesma. No entanto, dessa vez será definida uma estratégia mais simples para a escolha das proporções das ações, fazendo com que todas as ações tenham o mesmo peso (Equal Weighted).
Assim como foi feito para a carteira ótima, será necessário calcular a série de retornos da carteira de pesos iguais. Nesse caso também será mantido o rebalanceamento mensal.
retornos_ew = Return.portfolio(
R = retornos_ativos ,
weights = c(0.2 , 0.2 , 0.2 , 0.2 , 0.2),
rebalance_on = 'months'
)
retornos_ew %>% as.data.frame(.)%>% rmarkdown::paged_table()
Após todos os tratamentos, será criado um único objeto XTS que auxiliará na criação dos gráficos e análise de ambas as carteiras.
retornos_carteiras = merge.xts(retornos_carteira , retornos_ew , join="inner")
colnames(retornos_carteiras) <- c("Carteira Otima", "Carteira EW")
retornos_carteiras %>% as.data.frame(.)%>% rmarkdown::paged_table()
Tendo criado um único objeto XTS, serão criados gráficos e estatísticas para auxiliar na comparação das carteiras.
Rentabilidade Acumulada
chart.CumReturns(retornos_carteiras , legend.loc = 'topright' , plot.engine = 'plotly')
Correlação
chart.Correlation(retornos_carteiras)
Boxplot
chart.Boxplot(retornos_carteiras)
Sharpes
#carteira otima
SharpeRatio(
R = retornos_carteiras$`Carteira Otima` ,
Rf = di_diario_periodo ,
FUN = 'StdDev'
)
## Carteira Otima
## StdDev Sharpe (Rf=0%, p=95%): 0.04896323
#Carteira pesos iguais
SharpeRatio(
R = retornos_carteiras$`Carteira EW` ,
Rf = di_diario_periodo ,
FUN = 'StdDev'
)
## Carteira EW
## StdDev Sharpe (Rf=0%, p=95%): 0.03941626
Volatilidades anualizadas
#carteira otima
StdDev.annualized(retornos_carteiras$`Carteira Otima` , scale=252)
## Carteira Otima
## Annualized Standard Deviation 0.3506752
#Carteira pesos iguais
StdDev.annualized(retornos_carteiras$`Carteira EW` , scale=252)
## Carteira EW
## Annualized Standard Deviation 0.2888002
Gráfico de Underwater
chart.Drawdown(retornos_carteiras , plot.engine = 'plotly')
Tendo em vista os resultados obtidos, é possível observar que a alocação que segue o modelo de Markowitz tem se mostrado possuir um melhor desempenho do que a de pesos iguais. Por mais que a carteira ótima tenha um comportamento mais arriscado (maior volatilidade anualizada e maiores drawdowns), o excesso de retorno que ela possui em relação à carteira equal weighted acabou compensando esse aumento de risco (se traduzindo em um maior sharpe).
No entanto, é importante ressaltar que o modelo de Markowitz tem suas falhas. Da forma como ele foi exposto nesse trabalho, foi levado em consideração que o retorno esperado de cada ativo se dá pela média do retorno histórico da ação. Isso acaba não sendo verdade, tendo em vista os que retornos passados não são garantia de retornos futuros. Além disso, é possível observar que a carteira ótima desconsiderou uma possível alocação em ITUB3, tendo em vista que a mesma apresentou um retorno bem inferior aos demais ativos da carteira. Porém, o futuro desempenho do ativo pode não ter sido claro no inicio da janela estudada, podendo criar assim um possível Hindsight Bias na simulação.
De forma geral, o estudo visa mostrar que a Teoria Moderna do Portfolio, quando bem utilizada, pode auxiliar os investidores a fazer uma alocação mais eficiente de sua carteira, trazendo desempenhos melhores do que alocações mais simples. Porém, como qualquer modelo, a teoria não é perfeita e suas limitações devem ser bem conhecidas para melhor utilizá-la.