Com o objetivo de observar a rentabilidade de ativos que foram severamente impactados durante um período pandêmico, optamos por ações de empresas em setores que sofreram diferentes impáctos durante a pandemia de Covid 19. As ações escolhidas pelo grupo foram a Gol e a Hapvida, além da utilização da Selic como o ativo livre de risco.
Ações
Aqui está uma lista das ações das empresas escolhidas junto com o ativo livre de risco:
| Código | Nome da Empresa | Setor |
|---|---|---|
| GOLL4.SA | Gol Linhas Aereas SA | Empresas Aereas |
| HAPV3.SA | Hapvida Participações e Investimentos S.A. | Saúde Suplementar |
Hapvida Participações e Investimentos S.A. :
Perfil Corporativo: A Hapvida é uma holding domiciliada no Brasil, com sede em Fortaleza, estado do Ceará. Ela atua no setor de saúde, oferecendo planos de assistência médica e odontológica. A empresa tem se destacado como a maior empresa de planos de saúde do Brasil, conquistando todos os estados do país. Seu compromisso é assegurar o acesso à saúde de qualidade para a população.
Cotação na B3: As ações da Hapvida (código HAPV3) estão listadas no Novo Mercado da B3. A companhia realizou seu IPO em 2018 e é controlada pela Ppar Pinheiro Participações S.A., que detém mais de 77% das ações ordinárias.
Gol Linhas Aéreas S.A. :
Perfil Corporativo: A Gol é uma companhia aérea brasileira que opera voos domésticos e internacionais. Com presença relevante nas principais regiões do Brasil, a Gol é conhecida por sua conectividade e serviços de transporte aéreo. Ela desempenha um papel fundamental no setor de aviação, proporcionando mobilidade e conectando pessoas e destinos.
Atuação na Pandemia: Durante a pandemia da COVID-19, a Gol enfrentou desafios significativos, mas também adaptou suas operações para garantir a segurança dos passageiros e tripulantes. A empresa continua sendo uma das principais escolhas para viagens aéreas no Brasil.
Livre de Risco
Estabelecemos a escolha mais segura como o ativo ‘Risk Free’, sendo ele a Selic, a qual é estabelecida pelo próprio governo brasileiro.
Taxa Selic (Banco Central) A sigla Selic provém do Sistema Especial de Liquidação e de Custódia, que é uma infraestrutura do mercado financeiro administrada pelo Banco Central. Essa taxa é calculada com base nas operações de empréstimos de um dia entre os bancos, nos quais títulos públicos federais são utilizados como garantia.
| Pacote | Explicação |
|---|---|
| dplyr | Manipulação eficiente de dados |
| quantmod | Análise quantitativa de dados financeiros |
| forecast | Previsão de séries temporais |
| fImport | Importação de dados financeiros |
| tidytext | Manipulação de texto estruturado em análise de dados |
| stringr | Manipulação de strings e expressões regulares |
| ggplot2 | Criação de gráficos e plots |
| GetBCBData | API do Bacen para disponibilizar dados |
| gridExtra | Organização de plot de multiplos graficos |
packages <- c('dplyr','GetBCBData','ggplot2','stringr','tidytext','forecast','quantmod','fImport','gridExtra')
# Encontrar os pacotes que não estão instalados
to_install <- setdiff(packages, installed.packages()[, "Package"])
# Instalar os pacotes que não estão instalados
if (length(to_install) > 0) {
install.packages(to_install)
}
# Carregar todos os pacotes
for (package in packages) {
library(package, character.only = TRUE)
}
rm(package,packages)A fonte dos dados é o Yahoo Finance, que é acessado por meio do pacote “quantmod” no R.
Ao chamar:
getSymbols(symbol, src = "yahoo", from = start_date, to = end_date),
o código está solicitando ao pacote “quantmod” para baixar os dados
históricos da ação especificada usando o símbolo correspondente como um
parâmetro de entrada.
Site de acesso a plataforma: https://finance.yahoo.com
Obetendo os dados das Ações
O Código abaixo, em resumo, define os símbolos das ações, utiliza o
pacote "quantmod" para baixar os dados históricos dessas
ações do https://finance.yahoo.com e armazena-los
em objetos separados para análise posterior.
# Definir os símbolos das ações da área da saúde
chaves_acesso <- c("GOLL4.SA", "HAPV3.SA")
# Definir as datas de início e fim do período desejado
data_inicio <- as.Date("2023-05-01")
data_fim <- as.Date("2024-02-01")
# Criar um loop para baixar os dados de todas as ações
for (chave in chaves_acesso) {
# Baixar os dados da ação usando a função 'getSymbols' do 'quantmod'
getSymbols(chave, src = "yahoo", from = data_inicio, to = data_fim)
# Acessar os dados usando o nome do objeto criado pelo 'getSymbols'
assign(chave, get(chave))
# Aguardar um segundo antes de baixar o próximo símbolo
# (para evitar sobrecarga no servidor)
Sys.sleep(1)
}
# renomeando as bases
gol <- data.frame(GOLL4.SA) %>% mutate(data = index(GOLL4.SA))
hapv <- data.frame(HAPV3.SA) %>% mutate(data = index(HAPV3.SA))
# Limpando os nomes das variaveis
names(gol) <- gsub('^GOLL4\\.SA\\.','',names(gol))
names(hapv) <- gsub('^HAPV3\\.SA\\.','',names(hapv))
# pegando apenas as primeias 121 observacoes das bases
gol <- gol[1:121,]
hapv <- hapv[1:121,]
rm(GOLL4.SA,HAPV3.SA)
# Verificando os dados de uma ação específica
knitr::kable(data.frame(head(gol)),align = "c")| Open | High | Low | Close | Volume | Adjusted | data | |
|---|---|---|---|---|---|---|---|
| 2023-05-02 | 6.66 | 6.86 | 6.43 | 6.53 | 13491600 | 6.53 | 2023-05-02 |
| 2023-05-03 | 6.56 | 7.00 | 6.46 | 6.92 | 15218100 | 6.92 | 2023-05-03 |
| 2023-05-04 | 6.94 | 7.07 | 6.69 | 6.82 | 10483300 | 6.82 | 2023-05-04 |
| 2023-05-05 | 6.85 | 7.12 | 6.76 | 6.96 | 11993200 | 6.96 | 2023-05-05 |
| 2023-05-08 | 7.01 | 7.32 | 6.99 | 7.02 | 12572500 | 7.02 | 2023-05-08 |
| 2023-05-09 | 6.98 | 7.27 | 6.93 | 7.11 | 9011700 | 7.11 | 2023-05-09 |
Obtendo dados da Selic
O código a seguir, diferente do anterior, realiza a utilização de pacote que utiliza a API do Banco Central brasileiro para poder ter acesso ás suas informações
# utilizando a API do Bacen para pegar os dados
selic_data <- gbcbd_get_series(
id = 12,
first.date = data_inicio,
last.date = data_fim
)
knitr::kable(head(selic_data),align = "c")| ref.date | value | id.num | series.name |
|---|---|---|---|
| 2023-04-28 | 0.050788 | 12 | id = 12 |
| 2023-05-02 | 0.050788 | 12 | id = 12 |
| 2023-05-03 | 0.050788 | 12 | id = 12 |
| 2023-05-04 | 0.050788 | 12 | id = 12 |
| 2023-05-05 | 0.050788 | 12 | id = 12 |
| 2023-05-08 | 0.050788 | 12 | id = 12 |
Ele trás as informações já com as porcentagens trazidas à valor diário.
As bases de dados geradas pelo código fornecido contém informações de preços históricos das ações escolhidas durante o período de 2020-01-01 a 2021-01-01. Essas bases de dados contém as seguintes variáveis:
Date:
A data correspondente ao preço de fechamento da ação.
Open:
O preço de abertura da ação no dia específico.
High:
O preço mais alto alcançado pela ação durante o dia.
Low:
O preço mais baixo alcançado pela ação durante o dia.
Close:
O preço de fechamento da ação no dia específico.
Volume:
O volume de negociações da ação no dia específico.
Adjusted:
Preço ajustado que considera eventos como divisões de ações e dividendos
Os dados já vieram da fonte em formato de painel de séries temporaes (uma tabela em formato
xtspara cada ação da bolsa que foi importada), portanto, para melhor utilização das bases, foram feitas manipulações a fim de realizar o empilhamento dessas linhas para que se tenha um painel temporal das ações.
Com base nos preços diários das ações, construimos os retornos anualizados, utilizando a formula de captalização contínua a partir da seguinte formula:
\[r_{i,t}= 252 \cdot \ln \left( \frac{P_{i,\ t}}{P_{i,\ t-1}} \right)\] onde:
\(P_{i,t}\) é o preço da ação;
\(i\) no período \(t\);
\(r_{i,t}\) é o retorno da ação \(i\) no período \(t\).
Para a Selic, como seus dados já estão em porcentagem trazida a valor
diário, a formula para transforma-la em uma capitalização contínua
será:
\[r_{(t)} = 252 \ \cdot \ \ln \left( 1+ \frac
{Selic_{(t)}}{100} \right)\]
A fórmula transforma a variação percentual dos preços diários em um retorno contínuo e depois o anualiza, multiplicando pelo número de dias úteis no ano. Isso é útil para comparar retornos de diferentes ativos ao longo do tempo em uma base anualizada, mesmo que os preços sejam registrados diariamente.
Diferentemente da taxa de retorno simples, que poderia ser expressa em porcentagem, a taxa de retorno contínua é baseada no conceito de capitalização contínua, que assume que os rendimentos são reinvestidos e compostos a todo momento, não apenas no final de cada período.
Na prática, isso significa que a taxa de retorno contínua é mais precisa para cálculos financeiros que envolvem múltiplas composições ao longo do tempo. Ela é expressa em termos de logaritmos naturais (usando o símbolo: \(\ln\) ), o que facilita a manipulação matemática, especialmente quando se lida com múltiplos períodos de tempo ou composição contínua.
Um retorno contínuo não é deve ser diretamente interpretado como uma porcentagem. Para converter um retorno contínuo em uma taxa de retorno percentual aproximada, você pode usar a fórmula ( \(e^{r} - 1\) ), onde ( \(e\) ) é a base dos logaritmos naturais e ( r ) é o retorno contínuo.
Por exemplo, se o retorno contínuo anualizado for ( \(0.10\) ) (ou \(10\%\)), a taxa de retorno percentual aproximada seria ( \(e^{0.10} - 1 \approx 0.105\) ) ou 10.5%.
retornos_gol <- c()
retornos_hapvida <- c()
retornos_selic <- c()
for (i in 2:nrow(gol)) {
# calculando os retornos da GOL
retornos_gol[i] <- 252 * log(
gol$Adjusted[i] / gol$Adjusted[i-1]
)
# calculando os retornos da HAPVIDA
retornos_hapvida[i] <- 252 * log(
hapv$Adjusted[i] / hapv$Adjusted[i-1]
)
# calculando os valores da selic
retornos_selic[i] <- 252 * log( 1 + (selic_data$value[i]/100) )
}
# retirando a primeira linha da base de dados
# pois ela foi perdida no calculo dos retornos
gol <- gol[2:121,]
hapv <- hapv[2:121,]
# adicionando esses retornos nas bases de dados
gol$retornos_anualizados <- retornos_gol[2:121]
hapv$retornos_anualizados <- retornos_hapvida[2:121]
retornos_selic <- retornos_selic[-1]
# apresentando uma parte dos retornos para visualizacao
knitr::kable(
data.frame(
HAPVIDA_retornos = retornos_hapvida[2:6],
GOL_retornos = retornos_gol[2:6],
SELIC_retornos = retornos_selic[2:6]
)
)| HAPVIDA_retornos | GOL_retornos | SELIC_retornos |
|---|---|---|
| -1.8129557 | 14.618219 | 0.1279533 |
| 6.2890906 | -3.668184 | 0.1279533 |
| 7.8620028 | 5.120636 | 0.1279533 |
| -0.8615385 | 2.163101 | 0.1279533 |
| -3.4759137 | 3.210240 | 0.1279533 |
Nesta sessão iremos apresentar algumas estatísticas descritivas básicas para cada um dos conjuntos de retornos calculados anteriormente, incluindo o livre de risco. *Para isso, iremos considerar apenas as primeiras 80 observações.
Calculando uma tabela com as descritivas:
# Estatísticas descritivas dos retornos
estatisticas_gol <- summary( gol$retornos_anualizados)
estatisticas_hpv <- summary(hapv$retornos_anualizados)
# Criando a tabela das estatísticas descritivas
tabela_estatisticas <- data.frame(
Empresa = c("Gol Linhas Aéreas S.A. (GOLL4.SA)", "Hapvida Investimentos S.A. (HAPV3.SA)"),
`Média` = c(estatisticas_hpv[[4]], estatisticas_gol[[4]]),
`Mediana` = c(estatisticas_hpv[[3]], estatisticas_gol[[3]]),
`Mínimo` = c(estatisticas_hpv[[1]], estatisticas_gol[[1]]),
`Máximo` = c(estatisticas_hpv[[5]], estatisticas_gol[[5]]),
`Desvio Padrão`= c(sd(retornos_gol[-1]), sd(retornos_hapvida[-1])),
`Variância` = c( ( sd(retornos_gol[-1]) )^2 , ( sd(retornos_hapvida[-1]) )^2 )
)
# Imprimindo a tabela formatada
knitr::kable( tabela_estatisticas )| Empresa | Média | Mediana | Mínimo | Máximo | Desvio.Padrão | Variância |
|---|---|---|---|---|---|---|
| Gol Linhas Aéreas S.A. (GOLL4.SA) | 0.6871471 | 0.2644217 | -14.99519 | 4.796536 | 9.651137 | 93.14444 |
| Hapvida Investimentos S.A. (HAPV3.SA) | 0.1157403 | 0.4254359 | -33.64991 | 4.982864 | 7.507137 | 56.35711 |
\[\text{Curtose} = \frac{\sum_{i=1}^{n} (x_i - \bar{x})^4}{n \cdot s^4} - 3\] onde: - \(n\) é o número de observações;
\(x_i\) é cada observação individual;
\(xˉ\) é a média das observações;
\(s\) é o desvio padrão das observações.
# FUNCAO QUE CALCULA A CURTOSE
calculo_curtose <- function(retornos){
# Calcular a média
media <- mean(retornos)
# Calcular os desvios da média
desvios <- retornos - media
# Calcular a quarta potência dos desvios
quarta_potencia_desvios <- desvios^4
# Calcular a variância
variancia <- var(retornos)
# Calcular a curtose
curtose <- sum(quarta_potencia_desvios) / (length(retornos) * variancia^2) - 3
}
# APLICANDO A FUNCAO NOS RETORNOS
curtose_gol <- calculo_curtose(gol$retornos_anualizados)
curtose_hapvida <- calculo_curtose(hapv$retornos_anualizados)A partir dos dados, conseguimos obeter uma Curtose para
os retornos da Gol de: 0.9863222 e um valor de curtose da Hapvida de
0.4204504.
Como pode-se ver, o cálculo feito foi o da
curtose amostral a qual subtrai 3 da curtose populacional
para dar uma medida que é 0 para uma distribuição normal.
A assimetria mede o grau de simetria da distribuição. Uma distribuição simétrica tem assimetria zero, logo, quanto mais distante de zero, mais assimétrica é esta distribuição em questão.
Além disso, o sinal deste indicador também entrega informações. Uma distribuição com uma cauda longa à direita (valores maiores) terá assimetria positiva, enquanto uma distribuição com uma cauda longa à esquerda (valores menores) terá assimetria negativa.
A partir da utilização da formula: \[ g_1=\frac{n}{(n-1)(n-2)}\sum_{i=1}^{n} \left( \frac{x_i-\bar{x}}{s} \right)^3 \] onde: - \(n\) é o número de observações;
\(x_i\) é cada valor individual;
\(\bar{x}\) é a média dos valores;
\(s\) é o desvio padrão.
# FUNCAO QUE CALCULA A ASSIMETRIA
assimetria <- function(dados){
# Calcule a média dos dados
media <- mean(dados)
# Calcule o desvio padrão dos dados
desvio_padrao <- sd(dados)
# Calcule a assimetria manualmente
assimetria <- sum((dados - media)^3) / length(dados) / desvio_padrao^3
return(assimetria)
}
# Aplicando a funcao para os retornos
assim_gol <- assimetria(gol$retornos_anualizados)
assim_hpv <- assimetria(hapv$retornos_anualizados)Desta forma, obetemos um valor de Assimetria para os retornos da Gol de: -0.0611028 e um da hapvida de 0.4531635.
Para melhor visualização dos dados, fizemos alguns gráficos para mostrar o comportamento das ações:
# Gráfico no tempo dos preços de fechamento
historico_precos <- ggplot() +
geom_line(data = gol, aes(x = data, y =Close), color = "blue", linetype = "solid") +
geom_line(data = hapv, aes(x = data, y =Close), color = "red", linetype = "solid") +
labs(title = "Gráfico no Tempo dos Preços de Fechamento", x = "Data", y = "Pre") +
scale_x_date(date_labels = "%Y-%m", date_breaks = "1 month") +
scale_y_continuous() +
theme_classic()
historico_rentabilidade <- ggplot() +
geom_line(data = gol, aes(x = data, y =retornos_anualizados), color = "blue", linetype = "solid") +
geom_line(data = hapv, aes(x = data, y =retornos_anualizados), color = "red", linetype = "solid") +
labs(title = "Gráfico no Tempo dos retornos", x = "Data", y = "Retornos Anualizados") +
scale_x_date(date_labels = "%Y-%m", date_breaks = "1 month") +
scale_y_continuous() +
theme_classic()
# Gráfico estimando a distribuição dos retornos
dist_retornos <- ggplot() +
geom_density(data = gol, aes(x = retornos_anualizados, fill = "GOL Linhas Aéreas"), alpha = 0.5) +
geom_density(data = hapv, aes(x = retornos_anualizados, fill = "Hapvida"), alpha = 0.5) +
labs(title = "Estimativa da Distribuição dos Retornos", x = "Retornos Diários", y = "Densidade") +
# xlim(-120, 120) +
geom_vline(aes(xintercept = 0), color = "black", linetype = "dashed",size = 1) +
scale_fill_manual(name = "Empresa", values = c("GOL Linhas Aéreas" = "blue", "Hapvida" = "red")) +
theme_classic()
# Combinação dos gráficos lado a lado
gridExtra::grid.arrange(historico_precos, historico_rentabilidade ,dist_retornos, ncol = 1)Os Gráficos apresentados neste Documento Web são todos clicaveis
Consideirando apenas as 80 primeiras observações iniciais das duas ações escolhidas, faremos inicialmente uma figura com as combinações viáveis do retorno esperado e do desvio padrão.
Apresentando a fórmula dos retornos para uma carteira com duas ações, teremos: \[ r_p = w_g r_g + w_h r_h \]
onde:
\(w_g\) eh a proporcao das acoes da gol na carteira \(p\);
\(w_h\) sendo a proporcao das acoes da hapivida e \(w_e = 1 − w_d\);
\(r_p\) é o retorno da carteira de ações.
Existem inúmeras formas de encontrar os Retornos Esperados. Como há uma incerteza em relação ao cenário futuro, a melhor forma seria encontrarmos uma estimativa boa o suficiente e não viesada dessa esperança em questão. Mas para isso precisariamos de modelos que tornariam este trabalho muito extenso.
Por conta disso, iremos avaliar o \(E[r]\) como sendo a média simples dos retornos anualizados calculados no capítulo anterior.
•Uma medida de risco usual é o desvio-padrão, que mede a dispersão dos possíveis retornos em relação ao retorno esperado.
Covariância: A covariância entre dois vetores pode ser calculada usando a seguinte fórmula: \[Cov(r_g , r_h ) = \frac{1}{n} \sum_{i=1}^n (r_g - \bar{r_g}) (r_h - \bar{r_h}) \]
Correlação: Já a correlação, esta é calculada a partir da equação: \[ Corr(r_g,r_h) = \frac{cov(r_g,r_h)}{\sigma_{r_g} \cdot \sigma_{r_h}} \]
Aplicando a função no R:
temos a função da variância como: \[\sigma^2_p = Var[r_p] = Varr[w_hr_h+w_gr_g] = w_h^2\sigma_h^2+w_g^2\sigma_g^2 + 2w_hw_gCov(r_h,r_g) \] onde: - \(\sigma_g^2 = Var[r_g]\); - \(\sigma_h^2 = Var[r_h]\).
Percebemos neste caso que a variância da carteira diminui se o termo da covariância for negativo.
Agora, achando o Desvio Padrão utilizando a formula acima \[\sigma_p = (Var[r_p])^{1/2} = \sqrt{ \ w_h^2r_h^2+w_g^2r_g^2 + 2w_hw_gCov(r_h,r_g)} \]
Para isso, iremos criar uma tabela com os possíveis valores de de pesos das duas ações e testar suas combinações a fim de gerar um vetor com os possíveis valores de retorno esperado e desvio padrão. Com eles, iremos estabelecer o gráfico com os retornos e desvios.
Agora, iremos fazer um loop para poder calcular os valores de desvio-padrão e Retorno esperado da carteira para todos os possíveis pares de pesos \([w_g,\ w_h]\) sabendo que, \(w_g + w_h = 1\). E, a partir desses resultados, podemos encontrar o gráfico de retornos possíveis para cada desvio-padrão e construir a fronteira eficiente.:
Realizando o loop para todos os pessos possíveis:
# variaveis de armazenamento dos valores
retorno_carteira <- c()
desvio_carteira <- c()
# renomeando para melhor visualizacao
sd_g <- desvio_padrao_g
sd_h <- desvio_padrao_h
# aplicacao do loop
for ( i in 1:nrow(tabela_pesos) ) {
Wg <- tabela_pesos$w_g[i] # iterando os pesos
Wh <- tabela_pesos$w_h[i] # iterando os pesos
# utilizando a formula de retorno de p
retorno_carteira[i] <-(Wg * retorno_g) + (Wh * retorno_h)
# utilizando a formula do desvio da carteira
desvio_carteira[i] <- (
( (Wg^2 * sd_g^2) + (Wh^2 * sd_h^2) + (2*Wg*Wh*covariancia) )^(0.5)
)
}
# Criando um data.frame com os dados
tabela_retornos <- data.frame(
retorno = retorno_carteira,
desvio = desvio_carteira,
peso_gol = tabela_pesos$w_g,
peso_hpv = tabela_pesos$w_h
)
# plotando a tabela no HTML
knitr::kable( head(tabela_retornos) )| retorno | desvio | peso_gol | peso_hpv |
|---|---|---|---|
| 0.6871471 | 7.507137 | 0e+00 | 1.0000 |
| 0.6870899 | 7.506731 | 1e-04 | 0.9999 |
| 0.6870328 | 7.506324 | 2e-04 | 0.9998 |
| 0.6869757 | 7.505918 | 3e-04 | 0.9997 |
| 0.6869185 | 7.505511 | 4e-04 | 0.9996 |
| 0.6868614 | 7.505105 | 5e-04 | 0.9995 |
Agora, plotando o gráfico da fronteira eficiente, temos:
ggplot(tabela_retornos, aes(x = desvio, y = retorno)) +
geom_point(color = "darkblue", size = 1.5) +
labs(
x = "Desvio Padrão (σ)",
y = "Retorno Esperado (E[r])",
title = "Relação entre Desvio Padrão e Retorno Esperado",
subtitle = "Análise de Portfólio",
caption = "Fonte: Yahoo.finance"
) +
theme_classic() +
annotate(
"segment",
x = min(tabela_retornos$desvio)+0.1,
xend = min(tabela_retornos$desvio) + 1.2 ,
y = tabela_retornos[tabela_retornos$desvio == min(tabela_retornos$desvio),"retorno"],
yend= tabela_retornos[tabela_retornos$desvio == min(tabela_retornos$desvio),"retorno"],
arrow = arrow(length = unit(0.3, "cm"),ends = 'first', type = 'open'),
color = "black"
) +
annotate(
"text",
x = min(tabela_retornos$desvio) +1.3,
y = tabela_retornos[tabela_retornos$desvio == min(tabela_retornos$desvio),"retorno"]-0.01,
label = paste("Ponto de risco mínimo e com retorno = ",round(tabela_retornos[tabela_retornos$desvio == min(tabela_retornos$desvio),"retorno"],3)),
hjust = 0,
vjust = 0,
color = "black"
) +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(face = "italic", size = 12),
plot.caption = element_text(size = 8),
axis.title.x = element_text(face = "bold", size = 12),
axis.title.y = element_text(face = "bold", size = 12),
axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none"
) +
scale_x_continuous(labels = scales::comma)
### Observação: Click no Gráfico para
visualiza-lo
Como podemos ver no gráfico, temos a o valor com o menor desvio padrão como aquele ponto da curva que é tangenciado por uma linha vertical. Portanto, temos que a carteira de mínima variância é aquela de mínimo desvio-padrão, que seria:
\[w_g = 0.312\] \[w_h = 0.688\]
Temos algumas formas diferentes para poder encontrar a carteira de mínima variância.
Como foi visto no exercício anterior, calculamos todas os desvios possíveis para as carteiras. Dessa forma, encontramos o valor de menor risco é só achar qual par de pesos gerou o menor desvio.
Contudo, podemos também resolver esse execício através de uma ótica matemática, interpretanto as formulas como um problema de minimização da equação do desvio dado a restrição de \(m_g + m_h = 1\).
fariamos isso dessa forma:
Caso Geral: \[\sigma^2_p = w_h^2\sigma_h^2+w_g^2\sigma_g^2 + 2w_hw_gCov(r_h,r_g) \] Dado a restrição de que $w_g = 1 -w_h $, podemos substituir para encontrar:
\[\sigma^2_p = w_h^2\sigma_h^2+(1 -w_h)^2\sigma_g^2 + 2w_h(1 -w_h)Cov(r_h,r_g) \]
Portando, minimizaremos a Função escolhendo o valor de \(w_h\), aplicando a CPO: \[\frac{\partial(\sigma^2_p)}{\partial w_h} = 2w_h\sigma_h^2 - 2(1 - w_h)\sigma_g^2 + 2(1 - 2w_h)Cov(r_h,r_g)\]
Aplicando a segunda derivada desta função para saber se ela é convexa, ou seja, se sua segunda derivada é maior que zero:
\[\frac{\partial}{\partial w_h} \left[\frac{\partial(\sigma^2_p)}{\partial w_h} \right] = 2\sigma_h^2 + 2\sigma_g^2 + 2Cov(r_h,r_g) = Var[r_g-r_h]\]
\[Var[r_g-r_h] > 0\]
Como a segunda derivada da função da variância é negativa, temos que se trata de uma curva convexa e, portanto, ao acharmos o valor que torne a CPO = zero, teremos seu valor mínimo.
\[2w_h\sigma_h^2 - 2\sigma_g^2 + 2w_h\sigma_g^2 + 2Cov(r_h,r_g) - 4w_hCov(r_h,r_g) = 0\]
\[w_h^* = \frac{\sigma_g^2 - Cov(r_h,r_g)}{\sigma_h^2 + \sigma_g^2 - 2Cov(r_h,r_g)}\]
Aplicando a Fórmula em R:
# aplicacao da formula para o peso que min. o risco
w_h_menor_var <-
(desvio_padrao_g^2 - covariancia) /
(desvio_padrao_g^2 + desvio_padrao_h^2 - (2*covariancia) )
# encontrando o outro peso da acao
w_g_menor_var <- 1 - w_h_menor_varPortanto, o resultado utilizando a fórmula foi de: \[w_h^* = 0.6879856\] \[w_g^* = 0.3120144\]
\[ \begin{align} \begin{cases} E[r_p] & = 0.5088682 \\ \sigma_p^2 & = 46.8314918 \\ \sigma_p & = 6.8433538 \end{cases} \end{align} \]
Fórmula da Utilidade é: \[U = E[r_p] - \frac{1}{2}A\sigma^2_p\]
Da mesma forma que achamos a carteira de mínima variância calculando os resultados para todos os pesos possíveis, podemos forçar a maximização da utilidade dessa mesma forma:
# definindo a aversao ao risco
A <- 2.5
# vetor vazio de armazenamento das utilidades
utilidades <- c()
# Calculando a utilidade para todos os possiveis pesos
for (i in 1:nrow(tabela_pesos)) {
# definindo as variaveis pelo iterador em questao
w_g <- tabela_pesos$w_g[i]
w_h <- tabela_pesos$w_h[i]
desvio <- tabela_retornos$desvio[i]
retorno_esperado <- tabela_retornos$retorno[i]
# aplicando o calculo da utilidade para cada par de pesos
utilidades[i] <- (retorno_esperado - (0.5 * A * desvio^2) )
}
# acrescentando os valores da utilidade para cada peso
tabela_retornos$utilidades <- utilidadesComo as utilidades são calculadas por meio da subtração do retorno esperado menos um produto do desvio padrão, temos boa parte das utilidades negativas. Ainda assim, como a parte importante das funções utilidades é o seu carácter ordinal e não seu valor em si, só a utilizaremos para ver qual par de pesos que a tornou máxima.
\[Max(Utilidade) \rightarrow w_g = 0.3097; w_h = 0.6903 \]
Temos:
\[U = \color{green}{E[r_p]} - 0.5 A \color{red}{ \sigma_p^2} \] \[\color{green}{E[r_p]} = w_gE[r_g] + w_hE[r_h]\]
\[\color{red}{\sigma^2_p} = w_h^2\sigma_h^2+w_g^2\sigma_g^2 + 2w_hw_gCov(r_h,r_g) \]
Portanto, a utilidade pode ser escrita como: \[U = \color{green}{w_gE[r_g]+w_hE[r_h]} - 0.5A \color{red}{[w_g^2\sigma_g^2+w_h^2\sigma_h^2+2w_gw_hCov(r_g,r_h)]}\]
Dessa forma, quando maximizamos ela, teremos:
\[w_g^* = \frac{E[r_g] - E[r_h] + A[\sigma^2_h - 2Cov(r_D, r_E)]}{A[\sigma^2_g + \sigma^2_h - 2Cov(r_g, r_h)]}\] \[w_h^* = 1 - w_g^* = \frac{E[r_g] - E[r_h] + A[\sigma^2_g 2Cov(r_g, r_h)] }{A[\sigma^2_g + \sigma^2_h - 2Cov(r_g, r_h)]}\]
# aplicacao da formula para o peso que max a Utilidade
w_g_max_uti <-
( retorno_g - retorno_h + (A * (desvio_padrao_h^2 - covariancia)) ) /
( A * (desvio_padrao_g^2 + desvio_padrao_h^2 - (2*covariancia) ) )
# encontrando o outro peso da acao
w_h_max_uti <- 1 - w_g_max_utiDessa forma, a partir da formula, temos que os valores que maximizam a utilidade são, respectivamente:
\[Max(Utilidade) \rightarrow w_g = 0.3096785; w_h = 0.6903215 \]
\[ \begin{align} \begin{cases} E[r_p] & = 0.5101824 \\ \sigma_p^2 & = 46.8320159 \\ \sigma_p & = 6.8433921 \end{cases} \end{align} \]
Quando adicionamos o Risk Free ao modelo, de fato teremos três ativos para poder realizar o exercício de maximização da Utilidade, porém matematicamente isso ficaria muito trabalhoso. Por conta desta dificuldade, foram desenvolvidas técnicas diferentes de aplicação desses modelos os quais veremos nos exercícios abaixo.
Para podermos encontrar a carteira que maximiza a Utilidade do Consumidor, podemos utilizar a formula do índice de Sharpe a fim de encontrar o par de pesos que maximize o índice de Sharpe. Como o Sharpe é a relação entre o retorno e o risco assumido. encontrar a carteira que deixe o Sharpe mais alto é encontrar a razão que deixe o retorno mais compensatório com relação ao seu risco.
Isso por si só já nos entrega a carteira ideal para ser utilizada em combinação com o Risk Free.
dado que a \(r_f\) é a taxa do risk free, A fórmula do Índice de Sharpe pode ser dada por: \[S = \frac{E[r_c] - r_f}{\sigma_c}\]
# Risk free
risk_free <- mean(retornos_selic)
# vetor vazio de armazenamento dos Sharpes calculados
sharpe <- c()
# Calculando a utilidade para todos os possiveis pesos
for (i in 1:nrow(tabela_pesos)) {
# definindo as variaveis pelo iterador em questao
w_g <- tabela_pesos$w_g[i]
w_h <- tabela_pesos$w_h[i]
desvio <- tabela_retornos$desvio[i]
retorno_esperado <- tabela_retornos$retorno[i]
# aplicando o calculo do sharpe para cada par de pesos
sharpe[i] <- (retorno_esperado - risk_free) / (desvio)
}
# acrescentando os valores da utilidade para cada peso
tabela_retornos$sharpe <- sharpe
# pontos que maximizam o sharpe tirando aqueles abaixo da linha de min(Variancia)
tabela_acima_min_risco <- tabela_retornos[
tabela_retornos$retorno > tabela_retornos[
tabela_retornos$desvio == min(tabela_retornos$desvio),"retorno"
],
]
# encontrando pontos de maximo sharpe
w_g_max_sharpe <- tabela_acima_min_risco[
tabela_acima_min_risco$sharpe == max(tabela_acima_min_risco$sharpe),"peso_gol"
]
w_h_max_sharpe <- 1 - w_g_max_sharpeOs pesos que maximizam o sharpe forçando a maximização por loop são, respectivamente: \[Max(Sharpe) \rightarrow w_g = 0; w_h = 1 \]
Agora, iremos plotar na figura a reta que passa pelo eixo zero e corta a curva do conjunto de oportunidades no ponto de tangência:
# adicionando os pontos de sharpe maximo
x_ponto_desvio <- tabela_retornos[tabela_retornos$peso_gol == w_g_max_sharpe,"desvio"]
y_ponto_retorno <- tabela_retornos[tabela_retornos$peso_gol == w_g_max_sharpe,"retorno"]
ggplot(tabela_retornos, aes(x = desvio, y = retorno)) +
geom_point(color = "black", size = 1) +
geom_point(data = data.frame(x = x_ponto_desvio, y = y_ponto_retorno), aes(x = x, y = y), color = "darkred", size = 3) + # Adicionando o ponto
geom_segment(x = 0, y = 0, xend = 2*x_ponto_desvio, yend = 2*y_ponto_retorno, color = "red", linetype = "solid") + # Desenhando a linha da origem ao ponto vermelho
labs(
x = "Desvio Padrão (σ)",
y = "Retorno Esperado (E[r])",
title = "Relação entre Desvio Padrão e Retorno Esperado",
subtitle = "Análise de Portfólio",
caption = "Fonte: Yahoo.finance"
) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(face = "italic", size = 12),
plot.caption = element_text(size = 8),
axis.title.x = element_text(face = "bold", size = 12),
axis.title.y = element_text(face = "bold", size = 12),
axis.text.x = element_text(angle = 25, hjust = 1),
legend.position = "none"
) +
scale_x_continuous(
expand = c(0, 0),
limits = c(0, 2+ max(tabela_retornos$desvio)),
breaks = seq(0, 2+ max(tabela_retornos$desvio), by = 1)
) +
scale_y_continuous(
expand = c(0, 0),
limits = c(0, 0.2 + max(tabela_retornos$retorno)),
breaks = seq(0, 0.2 + max(tabela_retornos$retorno),by = 0.1)
)\[ \begin{align} \begin{cases} E[r_p] & = 0.6871471 \\ \sigma_p^2 & = 56.3571085 \\ \sigma_p & = 7.5071372 \\ S_p & = 0.074851 \end{cases} \end{align} \]
Quando queremos maximizar o Sharpe por meio das equações escolhendo um dos pesos, teremos:
\[\max_{w_e,w_D} S_p\] \[ \begin{align} \text{s.a.} \quad & \begin{cases} w_g + w_h = 1 \\ E[r_p] = w_gE[r_g] + w_hE[r_h] \\ \sigma_p = (\sigma^2_p)^{1/2} \\ \sigma_P^2 = w_g^2\sigma_g^2 + w_h^2\sigma_h^2+ 2w_gw_h \cdot Cov(r_g,r_h) \end{cases} \end{align} \]
Se substituirmos tudo, teremos um problema de maximização dado por: \[ \max_{w_g} \frac{(1-w_g)E[r_h]+w_gE[r_g] - r_f} {\sqrt{ [w_g^2\sigma_g^2 + (1-w_g)^2\sigma_h^2+ 2w_g(1-w_g) \cdot Cov(r_g,r_h)]}} \]
Aplicando a maximização, teremos os pesos ótimos dados pela equação: \[ w_G^* =\frac{E[R_G]\sigma_H^2 - E[R_H] Cov(R_G,R_H)} {E[R_G]\sigma_H^2 - E[R_H]\sigma_G^2 - (E[R_G] - E[R_H]) \cdot Cov(R_G,R_H)} \]
onde consideramos os retornos excedentes: \[E[R_G] = E[r_g-r_f]\] \[E[R_H] = E[r_h-r_f]\] ### 3.2.1 Aplicando a formula em R para os pontos:
# definindo variaveis
R_G <- retorno_g - risk_free
R_H <- retorno_h - risk_free
w_g_maximo_sharpe <-
( (R_G * desvio_padrao_h^2) - (R_G * covariancia) ) / # dividido por...
(
(R_G*desvio_padrao_h^2) - (R_H*desvio_padrao_g^2) -
( (R_G - R_H) * (covariancia) )
)
w_h_maximo_sharpe <- 1 - w_g_maximo_sharpeOs pesos que maximizam o sharpe por meio das equações são, respectivamente: \[Max(Utilidade) \rightarrow w_g = 0.0076013; w_h = 0.9923987 \]
Agora, tendo a carteira ótima de títulos de risco P, usaremos o grau de aversão ao risco A, para calcular a proporção ótima da carteira completa a ser investida no componente de risco
\[Max(Utilidade Risk Free)\] \[w_g = 3.0315985\times 10^{-5};\] \[w_h = 0.0039579\] \[y = 0.9960117 \] ### 3.2.3 calculando as variaveis de saída:
retorno_c <- (pesoselic * risk_free) + (pesoacoes*retorno_sharpe)
desvio_c <- ((pesoacoes^2 * desvio_sharpe^2) )^(0.5)
variancia_c <- ((pesoacoes^2 * desvio_sharpe^2) )
sharpe_c <- ( (retorno_sharpe - risk_free) / (desvio_sharpe) ) \[ \begin{align} \begin{cases} E[r_c] & = 0.1274717 \\ \sigma_c^2 & = 8.964267\times 10^{-4} \\ \sigma_c & = 0.0299404 \\ S_c & = 0.074851 \end{cases} \end{align} \]
Aplicando o retorno em excesso dos ativos comparado ao risk free
# Realizando as contas, teremos:
R_excesso_gol <- mean(gol$retornos_anualizados[80:120] - retornos_selic[80:120])
R_excesso_hpv <- mean(hapv$retornos_anualizados[80:120] - retornos_selic[80:120])\[ \begin{align} \begin{cases} E[r_g-r_f] & = -0.7796444 \\ E[r_h-r_f] & = -1.1298017 \\ \end{cases} \end{align} \] ### desvio-padrão para as 40 observações finais
# Realizando as contas, teremos:
desvio_gol <- sd(gol$retornos_anualizados[80:120] - retornos_selic[80:120])
desvio_hpv <- sd(hapv$retornos_anualizados[80:120] - retornos_selic[80:120])\[ \begin{align} \begin{cases} \sqrt{Var[r_g-r_f]} & = 9.1709857 \\ \sqrt{Var[r_h-r_f]} & = 6.8831021 \\ \end{cases} \end{align} \]
# Calculando o desvio padrão
desvio_padrao_g <- sd(gol$retornos_anualizados[80:120])
desvio_padrao_h <- sd(hapv$retornos_anualizados[80:120])
# Calculando o limite inferior do intervalo de confianca para o desvio padrao
# Usando um nível de confiança de 95%
alpha <- 0.05
n <- length(gol$retornos_anualizados[80:120])
limite_inferior_g <- desvio_padrao_g * sqrt((n - 1) / qchisq(1 - alpha/2, n - 1))
limite_inferior_h <- desvio_padrao_h * sqrt((n - 1) / qchisq(1 - alpha/2, n - 1))\[ \begin{align} \begin{cases} \text{Limite inferior Gol} & = 9.1709857 \\ \text{Limite inferior Hapivida} & = 6.8831021 \\ \end{cases} \end{align} \]
sharpe_gol <- (R_excesso_gol - risk_free) / (desvio_padrao_g)
sharpe_hpv <- (R_excesso_hpv - risk_free) / (desvio_padrao_h)\[ \begin{align} \begin{cases} \text{Índice de Sharpe Gol} & = -0.0986699 \\ \text{Índice de Sharpe Hapivida} & = -0.1823336 \\ \end{cases} \end{align} \]
# Separe os dados em observações iniciais e finais
gol_iniciais <- gol$retornos_anualizados[1:80]
hpv_iniciais <- hapv$retornos_anualizados[1:80]
# Calcule o VaR a 10% assumindo uma distribuição normal
media_gol <- mean(gol_iniciais)
media_hpv <- mean(hpv_iniciais)
desvio_padrao_gol <- sd(gol_iniciais)
desvio_padrao_hpv <- sd(hpv_iniciais)
VaR_normal_gol <- qnorm(0.1, mean = media_gol, sd = desvio_padrao_gol)
VaR_normal_hpv <- qnorm(0.1, mean = media_hpv, sd = desvio_padrao_hpv)
# Calcule o VaR a 10% usando o percentil apropriado dos retornos
VaR_percentil_gol <- quantile(gol_iniciais, 0.1)
VaR_percentil_hpv <- quantile(hpv_iniciais, 0.1)As iniciais são: \[ \begin{align} \begin{cases} \text{Iniciais Var Normal Gol} & = -12.0749811 \\ \text{Iniciais Var Normal Hapivida} & = -8.2091618 \\ \text{Iniciais Var Percentil Gol} & = -9.236938 \\ \text{Iniciais Var Percentil Hapivida} & = -7.0239081 \\ \end{cases} \end{align} \] As finais são:
# Separe os dados em observações iniciais e finais
gol_finais <- gol$retornos_anualizados[80:120]
hpv_finais <- hapv$retornos_anualizados[80:120]
# Calcule o VaR a 10% assumindo uma distribuição normal
media_gol <- mean(gol_finais)
media_hpv <- mean(hpv_finais)
desvio_padrao_gol <- sd(gol_finais)
desvio_padrao_hpv <- sd(hpv_finais)
VaR_normal_gol <- qnorm(0.1, mean = media_gol, sd = desvio_padrao_gol)
VaR_normal_hpv <- qnorm(0.1, mean = media_hpv, sd = desvio_padrao_hpv)
# Calcule o VaR a 10% usando o percentil apropriado dos retornos
VaR_percentil_gol <- quantile(gol_finais, 0.1)
VaR_percentil_hpv <- quantile(hpv_finais, 0.1)\[ \begin{align} \begin{cases} \text{Finais Var Normal Gol} & = -12.411028 \\ \text{Finais Var Normal Hapivida} & = -9.8295516 \\ \text{Finais Var Percentil Gol} & = -12.1383292 \\ \text{Finais Var Percentil Hapivida} & = -9.8608435 \\ \end{cases} \end{align} \]
# Calculando o VaR a 10% para os dados iniciais
VaR_10_gol <- quantile(gol_iniciais, 0.1)
VaR_10_hpv <- quantile(hpv_iniciais, 0.1)
# Calculando a ECCD
ECCD_gol <- mean(gol$retornos_anualizados[gol$retornos_anualizados > VaR_10_gol])
ECCD_hpv <- mean(hapv$retornos_anualizados[hapv$retornos_anualizados > VaR_10_hpv])# Calculando a ECCD para as últimas 40 observações
ECCD_final_gol <- quantile(gol_finais, 0.1)
ECCD_final_hpv <- quantile(hpv_finais, 0.1)
# Avaliando o desempenho
desempenho_gol <- ECCD_final_gol - ECCD_gol
desempenho_hpv <- ECCD_final_hpv - ECCD_hpvOs resultados são: \[ \begin{align} \begin{cases} \text{Iniciais ECCD Gol} & = 2.5742965 \\ \text{Iniciais ECCD Hapivida} & = 2.4818222 \\ \text{Finais ECCD Gol} & = -14.7126257 \\ \text{Finais ECCD Hapivida} & = -14.7126257 \\ \end{cases} \end{align} \]