1 1. Carregamento de Pacotes

# Instalar pacotes necessários (execute uma vez):
# install.packages("yfR")
# install.packages("GetBCBData")
# install.packages("moments")
# install.packages("nortest")
# install.packages("ggplot2")
# install.packages("tidyr")
# install.packages("dplyr")
# install.packages("knitr")
# install.packages("zoo")
# install.packages("quadprog")
# install.packages("xts")
# install.packages("PerformanceAnalytics")

# Pacotes para coleta de dados financeiros
library(yfR)         # API otimizada para B3 (Brasil)
library(GetBCBData)  # Coleta de dados do Banco Central (SELIC)
library(quantmod)

# Pacotes para análise estatística
library(moments)     # Testes de assimetria e curtose
library(nortest)     # Testes de normalidade (Anderson-Darling, etc)

# Pacotes para visualização e manipulação
library(ggplot2)     # Gráficos
library(tidyr)       # Manipulação de dados
library(dplyr)       # Manipulação de dados
library(knitr)       # Tabelas formatadas
library(zoo)         # Séries temporais (para na.locf)
library(quadprog)    # Otimização quadrática (Fronteira Eficiente)
library(xts)         # Séries temporais estendidas
library(kableExtra)  #Para formatar tabelas em PDF

2 2. Definição de Parâmetros

# Ações a serem analisadas
acoes <- c("POMO3.SA", "AURE3.SA", "BBAS3.SA", "ITUB3.SA")
nomes_acoes <- c("POMO3", "AURE3", "BBAS3", "ITUB3")

# Período de análise
data_inicio <- as.Date("2024-11-18")
data_fim <- as.Date("2025-11-17")

# Parâmetros para VaR
investimento <- 100000  # R$ 100.000,00
confianca <- 0.99       # 99% de confiança
janela <- 252           # 252 dias úteis (1 ano)

3 3. Coleta de Dados

3.1 3.1 Preços de Fechamento das Ações (API B3 via yfR)

# Coletar dados diretamente da B3 usando yfR
# yfR é otimizado para ações brasileiras e usa cache local
cat("Coletando dados da B3...\n")
## Coletando dados da B3...
dados_b3 <- yf_get(
  tickers = acoes,
  first_date = data_inicio,
  last_date = data_fim,
  freq_data = "daily",
  thresh_bad_data = 0.75  # Permitir até 25% de dados faltantes
)

# Verificar se coletou dados
if(nrow(dados_b3) == 0){
  stop("Erro: Nenhum dado foi coletado. Verifique os tickers e datas.")
}

cat("\nDados coletados com sucesso!\n")
## 
## Dados coletados com sucesso!
cat("Total de observações:", nrow(dados_b3), "\n")
## Total de observações: 996
cat("Período:", min(dados_b3$ref_date), "a", max(dados_b3$ref_date), "\n\n")
## Período: 20045 a 20406
# Mostrar resumo dos dados coletados por ação
cat("### Resumo por Ação:\n")
## ### Resumo por Ação:
resumo_coleta <- dados_b3 %>%
  group_by(ticker) %>%
  summarise(
    N_Obs = n(),
    Primeira_Data = min(ref_date),
    Ultima_Data = max(ref_date),
    Preco_Inicial = first(price_close),
    Preco_Final = last(price_close),
    Retorno_Periodo = ((last(price_close) / first(price_close)) - 1) * 100
  )

kable(resumo_coleta, digits = 2,
      caption = "Resumo da Coleta de Dados (B3)")
Resumo da Coleta de Dados (B3)
ticker N_Obs Primeira_Data Ultima_Data Preco_Inicial Preco_Final Retorno_Periodo
AURE3.SA 249 2024-11-18 2025-11-14 9.80 11.17 13.98
BBAS3.SA 249 2024-11-18 2025-11-14 25.71 22.44 -12.72
ITUB3.SA 249 2024-11-18 2025-11-14 26.85 35.92 33.76
POMO3.SA 249 2024-11-18 2025-11-14 7.15 6.46 -9.65
# Transformar para formato wide (colunas = ações)
precos_wide <- dados_b3 %>%
  select(ref_date, ticker, price_close) %>%
  pivot_wider(names_from = ticker, values_from = price_close)

# Converter para xts (formato de séries temporais)
library(xts)
precos <- xts(precos_wide[, -1], order.by = precos_wide$ref_date)
colnames(precos) <- nomes_acoes

# Exibir primeiras linhas dos preços
cat("\n### Primeiros Preços Coletados:\n")
## 
## ### Primeiros Preços Coletados:
kable(head(data.frame(Data = index(precos), coredata(precos)), 5), 
      digits = 4,booktabs = TRUE,
      caption = "Primeiras 5 observações de Preços de Fechamento")%>%
  kableExtra::kable_styling(latex_options = c("striped", "scale_down", "HOLD_position"), full_width = FALSE)
Primeiras 5 observações de Preços de Fechamento
Data POMO3 AURE3 BBAS3 ITUB3
2024-11-18 9.80 25.71 26.8545 7.15
2024-11-19 9.89 25.85 27.1455 7.17
2024-11-21 9.73 25.26 26.7273 7.27
2024-11-22 9.94 25.60 26.8182 7.28
2024-11-25 10.38 25.75 26.8818 7.21

3.2 3.2 Dados do IBOVESPA

# Coleta do Índice Bovespa
getSymbols("^BVSP", src = "yahoo", from = data_inicio, to = data_fim)
## [1] "BVSP"
ibovespa <- Cl(BVSP)
colnames(ibovespa) <- "IBOVESPA"

3.3 3.4 Dados da SELIC

# Código da SELIC no SGS do BCB: 11 (Taxa SELIC - % a.a.)
cat("Coletando dados da SELIC do Banco Central...\n")
## Coletando dados da SELIC do Banco Central...
selic_data <- gbcbd_get_series(id = 11, 
                               first.date = data_inicio, 
                               last.date = data_fim,
                               use.memoise = FALSE)

# Converter para taxa diária equivalente
# Fórmula: (1 + taxa_anual)^(1/252) - 1
selic_data$taxa_diaria <- (1 + selic_data$value/100)^(1/252) - 1

cat("SELIC coletada com sucesso!\n")
## SELIC coletada com sucesso!
cat("Taxa SELIC atual (% a.a.):", tail(selic_data$value, 1), "\n")
## Taxa SELIC atual (% a.a.): 0.055131
cat("Taxa diária equivalente:", 
    round(tail(selic_data$taxa_diaria, 1) * 100, 4), "%\n")
## Taxa diária equivalente: 0.0002 %

4 4. Visualização dos Dados Coletados

4.1 4.1 Gráficos de Preços de Fechamento

# Converter xts para dataframe para ggplot2
precos_df <- data.frame(Data = index(precos), coredata(precos))

# Transformar para formato longo
precos_long <- precos_df %>%
  pivot_longer(cols = -Data, names_to = "Acao", values_to = "Preco")

# Gráfico de preços
ggplot(precos_long, aes(x = Data, y = Preco, color = Acao)) +
  geom_line(linewidth = 1) +
  facet_wrap(~Acao, scales = "free_y", ncol = 2) +
  labs(title = "Evolução dos Preços de Fechamento",
       subtitle = paste0("Período: ", data_inicio, " a ", data_fim),
       x = "Data",
       y = "Preço de Fechamento (R$)") +
  theme_minimal() +
  theme(legend.position = "none",
        plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5, size = 10))

4.2 4.2 Gráfico do IBOVESPA

# Gráfico do IBOVESPA
ibov_df <- data.frame(Data = index(ibovespa), IBOVESPA = coredata(ibovespa))

ggplot(ibov_df, aes(x = Data, y = IBOVESPA)) +
  geom_line(color = "darkblue", linewidth = 1) +
  labs(title = "Evolução do IBOVESPA",
       x = "Data",
       y = "Pontos") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))

5 5. Cálculo dos Retornos Logarítmicos

# Retornos logarítmicos das ações
# ln(Pt/Pt-1) - mais apropriado para análise estatística
retornos <- na.omit(diff(log(precos)))

# Retorno do IBOVESPA
retorno_ibov <- na.omit(diff(log(ibovespa)))
colnames(retorno_ibov) <- "IBOVESPA"

# Estatísticas descritivas dos retornos
estatisticas <- data.frame(
  Acao = nomes_acoes,
  Media = apply(retornos, 2, mean),
  Desvio_Padrao = apply(retornos, 2, sd),
  Minimo = apply(retornos, 2, min),
  Maximo = apply(retornos, 2, max),
  Assimetria = apply(retornos, 2, skewness),
  Curtose = apply(retornos, 2, kurtosis)
)

kable(estatisticas, digits = 4, 
      caption = "Estatísticas Descritivas dos Retornos Diários")
Estatísticas Descritivas dos Retornos Diários
Acao Media Desvio_Padrao Minimo Maximo Assimetria Curtose
POMO3 POMO3 0.0005 0.0207 -0.0631 0.0730 0.2374 3.4930
AURE3 AURE3 -0.0005 0.0177 -0.1357 0.0463 -2.0328 16.8270
BBAS3 BBAS3 0.0012 0.0129 -0.0573 0.0470 -0.2040 5.2571
ITUB3 ITUB3 -0.0004 0.0243 -0.1109 0.0720 -0.5203 5.0205

5.1 5.1 Dataframe Completo: Dados Diários Consolidados

# Criar dataframe completo com todas as informações
# Alinhar as datas (alguns dados podem ter datas diferentes)

# Preparar dados das ações
dados_completos <- data.frame(Data = index(retornos))

# Adicionar preços e retornos de cada ação
for(i in 1:length(nomes_acoes)){
  # Preços
  col_preco <- paste0("Preco_", nomes_acoes[i])
  dados_completos[[col_preco]] <- as.numeric(precos[index(retornos), i])
  
  # Retornos
  col_retorno <- paste0("Retorno_", nomes_acoes[i])
  dados_completos[[col_retorno]] <- as.numeric(retornos[, i])
}

# Adicionar IBOVESPA
dados_completos$Preco_IBOVESPA <- as.numeric(ibovespa[index(retornos)])
dados_completos$Retorno_IBOVESPA <- as.numeric(retorno_ibov)

# Adicionar SELIC (fazer merge por data)
# A SELIC pode não ter dados para todos os dias, então usamos merge
selic_diaria <- selic_data[, c("ref.date", "taxa_diaria")]
colnames(selic_diaria) <- c("Data", "SELIC_Diaria")

dados_completos <- merge(dados_completos, selic_diaria, 
                         by = "Data", all.x = TRUE)

# Preencher dias sem SELIC com última observação (forward fill)
dados_completos$SELIC_Diaria <- zoo::na.locf(dados_completos$SELIC_Diaria, 
                                              na.rm = FALSE)

# Exibir primeiras linhas
kable(head(dados_completos, 10), digits = 6, booktabs = TRUE,
      caption = "Primeiras 10 linhas do DataFrame Consolidado") %>%
  kable_styling(latex_options = c("scale_down", "striped", "HOLD_position"))
Primeiras 10 linhas do DataFrame Consolidado
Data Preco_POMO3 Retorno_POMO3 Preco_AURE3 Retorno_AURE3 Preco_BBAS3 Retorno_BBAS3 Preco_ITUB3 Retorno_ITUB3 Preco_IBOVESPA Retorno_IBOVESPA SELIC_Diaria
2024-11-19 9.89 0.009142 25.85 0.005431 27.14545 0.010775 7.17 0.002793 128197 0.003352 0.000002
2024-11-21 9.73 -0.016310 25.26 -0.023088 26.72727 -0.015525 7.27 0.013851 126922 -0.009995 0.000002
2024-11-22 9.94 0.021353 25.60 0.013370 26.81818 0.003396 7.28 0.001375 129126 0.017216 0.000002
2024-11-25 10.38 0.043314 25.75 0.005842 26.88182 0.002370 7.21 -0.009662 129036 -0.000697 0.000002
2024-11-26 10.57 0.018139 25.67 -0.003112 27.26364 0.014104 7.24 0.004152 129922 0.006843 0.000002
2024-11-27 10.14 -0.041532 25.22 -0.017686 26.57273 -0.025668 6.94 -0.042319 127669 -0.017493 0.000002
2024-11-28 9.80 -0.034106 24.48 -0.029781 25.87273 -0.026696 6.43 -0.076327 124610 -0.024252 0.000002
2024-11-29 9.86 0.006104 24.77 0.011777 25.92727 0.002106 6.38 -0.007806 125668 0.008455 0.000002
2024-12-02 9.53 -0.034041 24.57 -0.008107 25.59091 -0.013058 6.36 -0.003140 125236 -0.003444 0.000002
2024-12-03 9.62 0.009400 24.87 0.012136 25.73636 0.005668 6.36 0.000000 126139 0.007185 0.000002
# Exibir últimas linhas
kable(tail(dados_completos, 10), digits = 6, booktabs = TRUE,
      caption = "Últimas 10 linhas do DataFrame Consolidado") %>%
  kable_styling(latex_options = c("scale_down", "striped", "HOLD_position"))
Últimas 10 linhas do DataFrame Consolidado
Data Preco_POMO3 Retorno_POMO3 Preco_AURE3 Retorno_AURE3 Preco_BBAS3 Retorno_BBAS3 Preco_ITUB3 Retorno_ITUB3 Preco_IBOVESPA Retorno_IBOVESPA SELIC_Diaria
239 2025-11-03 10.83 -0.014666 22.08 0.008186 35.66 0.014690 6.47 -0.052683 150454 0.006093 0.000002
240 2025-11-04 11.11 0.025526 22.27 0.008568 35.51 -0.004215 6.55 0.012289 150704 0.001660 0.000002
241 2025-11-05 11.60 0.043160 22.61 0.015152 35.75 0.006736 6.58 0.004570 153294 0.017040 0.000002
242 2025-11-06 11.67 0.006016 22.86 0.010996 35.80 0.001398 6.38 -0.030867 153339 0.000294 0.000002
243 2025-11-07 11.33 -0.029567 22.89 0.001311 35.75 -0.001398 6.37 -0.001569 154064 0.004717 0.000002
244 2025-11-10 11.55 0.019231 22.78 -0.004817 36.03 0.007802 6.57 0.030914 155257 0.007714 0.000002
245 2025-11-11 11.53 -0.001733 23.47 0.029840 36.59 0.015423 6.50 -0.010712 157749 0.015923 0.000002
246 2025-11-12 11.45 -0.006963 22.80 -0.028962 35.70 -0.024624 6.38 -0.018634 157633 -0.000736 0.000002
247 2025-11-13 11.28 -0.014958 22.50 -0.013245 35.79 0.002518 6.39 0.001566 157162 -0.002992 0.000002
248 2025-11-14 11.17 -0.009800 22.44 -0.002670 35.92 0.003626 6.46 0.010895 157739 0.003665 0.000002
# Salvar o dataframe completo (opcional)
# write.csv(dados_completos, "dados_completos.csv", row.names = FALSE)

5.2 5.2 Visualização dos Retornos Diários

# Preparar dados para visualização
retornos_df <- data.frame(Data = index(retornos), coredata(retornos))
retornos_long <- retornos_df %>%
  pivot_longer(cols = -Data, names_to = "Acao", values_to = "Retorno")

# Gráfico de retornos diários
ggplot(retornos_long, aes(x = Data, y = Retorno, color = Acao)) +
  geom_line(alpha = 0.6) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "black") +
  facet_wrap(~Acao, ncol = 2) +
  labs(title = "Retornos Logarítmicos Diários",
       subtitle = "Retorno = ln(Pt / Pt-1)",
       x = "Data",
       y = "Retorno") +
  theme_minimal() +
  theme(legend.position = "none",
        plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5, size = 10))

Interpretação: Retornos com assimetria próxima de 0 e curtose próxima de 3 indicam distribuição mais próxima da normal. Assimetria negativa indica maior probabilidade de retornos negativos extremos. Curtose > 3 indica caudas pesadas (maior probabilidade de eventos extremos).

5.3 5.3 Comparação: Retornos vs IBOVESPA

# Combinar retornos das ações com IBOVESPA
retornos_completo <- merge(retornos, retorno_ibov)
retornos_comp_df <- data.frame(Data = index(retornos_completo), 
                               coredata(retornos_completo))

# Gráficos de dispersão: Ação vs IBOVESPA
par(mfrow = c(2, 2))
for(i in 1:length(nomes_acoes)){
  plot(retornos_comp_df$IBOVESPA, retornos_comp_df[, nomes_acoes[i]],
       main = paste(nomes_acoes[i], "vs IBOVESPA"),
       xlab = "Retorno IBOVESPA",
       ylab = paste("Retorno", nomes_acoes[i]),
       pch = 16, col = rgb(0, 0, 1, 0.3))
  
  # Linha de regressão
  abline(lm(retornos_comp_df[, nomes_acoes[i]] ~ retornos_comp_df$IBOVESPA),
         col = "red", lwd = 2)
  
  # Calcular correlação
  cor_val <- cor(retornos_comp_df[, nomes_acoes[i]], 
                 retornos_comp_df$IBOVESPA)
  legend("topleft", legend = paste("Corr =", round(cor_val, 3)),
         bty = "n", cex = 1.2)
}

6 6. Testes de Normalidade

# Função para realizar múltiplos testes de normalidade
testar_normalidade <- function(dados, nome){
  cat("\n### Testes de Normalidade para", nome, "\n\n")
  
  # Teste de Shapiro-Wilk (mais poderoso para amostras pequenas)
  # H0: dados seguem distribuição normal
  # p-valor < 0.05: rejeita H0 (dados NÃO são normais)
  sw <- shapiro.test(as.numeric(dados))
  cat("**Shapiro-Wilk:**\n")
  cat("  Estatística W =", round(sw$statistic, 4), "\n")
  cat("  p-valor =", format.pval(sw$p.value, digits = 4), "\n")
  cat("  Conclusão:", ifelse(sw$p.value > 0.05, 
                             "Não rejeita normalidade", 
                             "Rejeita normalidade"), "\n\n")
  
  # Teste de Anderson-Darling (sensível às caudas)
  ad <- ad.test(as.numeric(dados))
  cat("**Anderson-Darling:**\n")
  cat("  Estatística A =", round(ad$statistic, 4), "\n")
  cat("  p-valor =", format.pval(ad$p.value, digits = 4), "\n")
  cat("  Conclusão:", ifelse(ad$p.value > 0.05, 
                             "Não rejeita normalidade", 
                             "Rejeita normalidade"), "\n\n")
  
  # Teste de Jarque-Bera (baseado em assimetria e curtose)
  jb <- jarque.test(as.numeric(dados))
  cat("**Jarque-Bera:**\n")
  cat("  Estatística JB =", round(jb$statistic, 4), "\n")
  cat("  p-valor =", format.pval(jb$p.value, digits = 4), "\n")
  cat("  Conclusão:", ifelse(jb$p.value > 0.05, 
                             "Não rejeita normalidade", 
                             "Rejeita normalidade"), "\n\n")
  
  return(c(SW = sw$p.value, AD = ad$p.value, JB = jb$p.value))
}

# Aplicar testes para cada ação
resultados_testes <- data.frame()
for(i in 1:ncol(retornos)){
  p_valores <- testar_normalidade(retornos[,i], nomes_acoes[i])
  resultados_testes <- rbind(resultados_testes, p_valores)
}
## 
## ### Testes de Normalidade para POMO3 
## 
## **Shapiro-Wilk:**
##   Estatística W = 0.9935 
##   p-valor = 0.3596 
##   Conclusão: Não rejeita normalidade 
## 
## **Anderson-Darling:**
##   Estatística A = 0.3742 
##   p-valor = 0.4135 
##   Conclusão: Não rejeita normalidade 
## 
## **Jarque-Bera:**
##   Estatística JB = 4.8417 
##   p-valor = 0.08884 
##   Conclusão: Não rejeita normalidade 
## 
## 
## ### Testes de Normalidade para AURE3 
## 
## **Shapiro-Wilk:**
##   Estatística W = 0.8742 
##   p-valor = 0.0000000000001982 
##   Conclusão: Rejeita normalidade 
## 
## **Anderson-Darling:**
##   Estatística A = 3.0263 
##   p-valor = 0.0000001288 
##   Conclusão: Rejeita normalidade 
## 
## **Jarque-Bera:**
##   Estatística JB = 2146.39 
##   p-valor = < 0.00000000000000022 
##   Conclusão: Rejeita normalidade 
## 
## 
## ### Testes de Normalidade para BBAS3 
## 
## **Shapiro-Wilk:**
##   Estatística W = 0.9732 
##   p-valor = 0.0001269 
##   Conclusão: Rejeita normalidade 
## 
## **Anderson-Darling:**
##   Estatística A = 1.1322 
##   p-valor = 0.005708 
##   Conclusão: Rejeita normalidade 
## 
## **Jarque-Bera:**
##   Estatística JB = 54.3643 
##   p-valor = 0.000000000001567 
##   Conclusão: Rejeita normalidade 
## 
## 
## ### Testes de Normalidade para ITUB3 
## 
## **Shapiro-Wilk:**
##   Estatística W = 0.9716 
##   p-valor = 0.00007327 
##   Conclusão: Rejeita normalidade 
## 
## **Anderson-Darling:**
##   Estatística A = 1.8691 
##   p-valor = 0.00008749 
##   Conclusão: Rejeita normalidade 
## 
## **Jarque-Bera:**
##   Estatística JB = 53.376 
##   p-valor = 0.000000000002568 
##   Conclusão: Rejeita normalidade
rownames(resultados_testes) <- nomes_acoes
kable(resultados_testes, digits = 4, 
      caption = "P-valores dos Testes de Normalidade")
P-valores dos Testes de Normalidade
X0.359604680190696 X0.413543625065842 X0.0888439442955237
POMO3 0.3596 0.4135 0.0888
AURE3 0.0000 0.0000 0.0000
BBAS3 0.0001 0.0057 0.0000
ITUB3 0.0001 0.0001 0.0000

Critérios de Interpretação:

  • p-valor > 0.05: Não rejeitamos H0, dados podem ser considerados normais (ao nível de 5%)
  • p-valor > 0.10: Evidência fraca contra normalidade - dados razoavelmente normais
  • p-valor entre 0.01 e 0.05: Evidência moderada contra normalidade
  • p-valor < 0.01: Forte evidência contra normalidade - dados claramente não normais

Consenso: Se pelo menos 2 dos 3 testes tiverem p-valor > 0.05, podemos considerar que a hipótese de normalidade é razoável para fins práticos.

7 6. Visualização da Distribuição

par(mfrow = c(2, 2))

for(i in 1:ncol(retornos)){
  # Q-Q Plot (comparação com distribuição normal)
  qqnorm(retornos[,i], main = paste("Q-Q Plot -", nomes_acoes[i]))
  qqline(retornos[,i], col = "red", lwd = 2)
  
  # Histograma com curva normal sobreposta
  hist(retornos[,i], breaks = 30, probability = TRUE,
       main = paste("Histograma -", nomes_acoes[i]),
       xlab = "Retorno", ylab = "Densidade", col = "lightblue")
  
  # Curva normal teórica
  curve(dnorm(x, mean = mean(retornos[,i]), sd = sd(retornos[,i])),
        add = TRUE, col = "red", lwd = 2)
}

Interpretação Q-Q Plot: Pontos próximos à linha vermelha indicam normalidade. Desvios nas caudas indicam caudas mais pesadas (curtose) ou assimétricas.

8 8. Fronteira Eficiente (Markowitz)

# Pacote para otimização de portfólio
library(quadprog)

# Matriz de covariância dos retornos
cov_matrix <- cov(retornos)

# Vetor de retornos médios
retornos_medios <- colMeans(retornos)

# Função para calcular portfólio de variância mínima
calc_portfolio <- function(retornos_alvo, retornos_medios, cov_matrix){
  n <- length(retornos_medios)
  
  # Matriz D (2x covariância para função quadrática)
  Dmat <- 2 * cov_matrix
  
  # Vetor d (zeros para minimização de variância)
  dvec <- rep(0, n)
  
  # Restrições
  # 1. Soma dos pesos = 1
  # 2. Retorno esperado = retorno alvo
  # 3. Pesos >= 0 (sem venda a descoberto)
  Amat <- cbind(rep(1, n), retornos_medios, diag(n))
  bvec <- c(1, retornos_alvo, rep(0, n))
  
  # Resolver problema de otimização quadrática
  resultado <- tryCatch({
    solve.QP(Dmat, dvec, Amat, bvec, meq = 2)
  }, error = function(e){
    return(NULL)
  })
  
  if(is.null(resultado)) return(NULL)
  
  # Retornar pesos, retorno e risco
  pesos <- resultado$solution
  retorno <- sum(pesos * retornos_medios)
  risco <- sqrt(t(pesos) %*% cov_matrix %*% pesos)
  
  return(list(pesos = pesos, retorno = retorno, risco = as.numeric(risco)))
}

# Gerar fronteira eficiente
# Retornos alvo: do mínimo ao máximo possível
retorno_min <- min(retornos_medios)
retorno_max <- max(retornos_medios)
retornos_alvo <- seq(retorno_min, retorno_max, length.out = 100)

# Calcular portfólios para cada retorno alvo
fronteira <- data.frame()

for(ret_alvo in retornos_alvo){
  portfolio <- calc_portfolio(ret_alvo, retornos_medios, cov_matrix)
  
  if(!is.null(portfolio)){
    fronteira <- rbind(fronteira, data.frame(
      Retorno = portfolio$retorno * 252,  # Anualizar (252 dias úteis)
      Risco = portfolio$risco * sqrt(252)  # Anualizar
    ))
  }
}

# Adicionar pontos das ações individuais (anualizados)
acoes_individuais <- data.frame(
  Acao = nomes_acoes,
  Retorno = retornos_medios * 252,
  Risco = apply(retornos, 2, sd) * sqrt(252)
)

# Gráfico da Fronteira Eficiente
ggplot() +
  # Fronteira eficiente
  geom_line(data = fronteira, aes(x = Risco, y = Retorno),
            color = "darkblue", linewidth = 1.5) +
  # Ações individuais
  geom_point(data = acoes_individuais, aes(x = Risco, y = Retorno, color = Acao),
             size = 4) +
  geom_text(data = acoes_individuais, aes(x = Risco, y = Retorno, label = Acao),
            vjust = -1, size = 4) +
  # Formatação dos eixos para Porcentagem com 2 casas decimais
  scale_x_continuous(labels = scales::percent_format(accuracy = 0.01)) +
  scale_y_continuous(labels = scales::percent_format(accuracy = 0.01)) +
  labs(title = "Fronteira Eficiente de Markowitz",
       subtitle = "Retornos e Riscos Anualizados",
       x = "Risco (Desvio Padrão Anualizado)",
       y = "Retorno Esperado Anualizado") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5, size = 10),
        legend.position = "right")

# Tabela com informações das ações
kable(acoes_individuais, digits = 4,
      caption = "Retorno e Risco Anualizados das Ações")
Retorno e Risco Anualizados das Ações
Acao Retorno Risco
POMO3 POMO3 0.1330 0.3293
AURE3 AURE3 -0.1382 0.2809
BBAS3 BBAS3 0.2956 0.2051
ITUB3 ITUB3 -0.1031 0.3861

Interpretação da Fronteira Eficiente:

A curva azul representa a combinação ótima de ativos que maximiza o retorno para cada nível de risco. Pontos acima da fronteira são impossíveis de alcançar com os ativos disponíveis, enquanto pontos abaixo da fronteira são ineficientes, pois existe uma combinação melhor que oferece maior retorno para o mesmo risco ou menor risco para o mesmo retorno. Investidores devem escolher portfólios ao longo da fronteira de acordo com sua tolerância ao risco, sendo que cada ponto da fronteira representa uma alocação ótima específica entre os quatro ativos analisados.

9 8.2 Fronteira Eficiente com ETF IBOVESPA

# Agora vamos expandir a análise incluindo o IBOVESPA como um ativo adicional
# Isso simula a possibilidade de investir em um ETF que replica o índice

# Combinar retornos das ações com o IBOVESPA
retornos_com_etf <- merge(retornos, retorno_ibov)
colnames(retornos_com_etf) <- c(nomes_acoes, "IBOVESPA")

# Recalcular matriz de covariância e retornos médios com ETF incluído
cov_matrix_etf <- cov(retornos_com_etf)
retornos_medios_etf <- colMeans(retornos_com_etf)

cat("\n### Matriz de Correlação com ETF IBOVESPA:\n")
## 
## ### Matriz de Correlação com ETF IBOVESPA:
cor_matrix_etf <- cor(retornos_com_etf)
kable(round(cor_matrix_etf, 3), 
      caption = "Matriz de Correlação: Ações + ETF IBOVESPA")
Matriz de Correlação: Ações + ETF IBOVESPA
POMO3 AURE3 BBAS3 ITUB3 IBOVESPA
POMO3 1.000 0.280 0.424 0.324 0.560
AURE3 0.280 1.000 0.428 0.208 0.509
BBAS3 0.424 0.428 1.000 0.354 0.749
ITUB3 0.324 0.208 0.354 1.000 0.461
IBOVESPA 0.560 0.509 0.749 0.461 1.000
# Função para calcular portfólio com ETF incluído
calc_portfolio_etf <- function(retornos_alvo, retornos_medios, cov_matrix){
  n <- length(retornos_medios)
  
  Dmat <- 2 * cov_matrix
  dvec <- rep(0, n)
  
  # Restrições: soma = 1, retorno = alvo, pesos >= 0
  Amat <- cbind(rep(1, n), retornos_medios, diag(n))
  bvec <- c(1, retornos_alvo, rep(0, n))
  
  resultado <- tryCatch({
    solve.QP(Dmat, dvec, Amat, bvec, meq = 2)
  }, error = function(e){
    return(NULL)
  })
  
  if(is.null(resultado)) return(NULL)
  
  pesos <- resultado$solution
  retorno <- sum(pesos * retornos_medios)
  risco <- sqrt(t(pesos) %*% cov_matrix %*% pesos)
  
  return(list(pesos = pesos, retorno = retorno, risco = as.numeric(risco)))
}

# Gerar nova fronteira eficiente com ETF
retorno_min_etf <- min(retornos_medios_etf)
retorno_max_etf <- max(retornos_medios_etf)
retornos_alvo_etf <- seq(retorno_min_etf, retorno_max_etf, length.out = 100)

fronteira_etf <- data.frame()

for(ret_alvo in retornos_alvo_etf){
  portfolio <- calc_portfolio_etf(ret_alvo, retornos_medios_etf, cov_matrix_etf)
  
  if(!is.null(portfolio)){
    fronteira_etf <- rbind(fronteira_etf, data.frame(
      Retorno = portfolio$retorno * 252,
      Risco = portfolio$risco * sqrt(252)
    ))
  }
}

# Adicionar ponto do ETF IBOVESPA
etf_ponto <- data.frame(
  Acao = "IBOVESPA (ETF)",
  Retorno = retornos_medios_etf["IBOVESPA"] * 252,
  Risco = sd(retorno_ibov) * sqrt(252)
)

# Combinar com ações individuais
todos_ativos <- rbind(acoes_individuais, etf_ponto)

# Gráfico comparando as duas fronteiras
ggplot() +
  # Fronteira original (só ações)
  geom_line(data = fronteira, aes(x = Risco, y = Retorno),
            color = "gray60", linewidth = 1, linetype = "dashed",
            alpha = 0.7) +
  # Nova fronteira (ações + ETF)
  geom_line(data = fronteira_etf, aes(x = Risco, y = Retorno),
            color = "darkgreen", linewidth = 1.5) +
  # Todos os ativos individuais
  geom_point(data = todos_ativos, aes(x = Risco, y = Retorno, color = Acao),
             size = 4) +
  geom_text(data = todos_ativos, aes(x = Risco, y = Retorno, label = Acao),
            vjust = -1, size = 3.5, check_overlap = TRUE) +
  scale_x_continuous(labels = scales::percent_format(accuracy = 0.01)) +
  scale_y_continuous(labels = scales::percent_format(accuracy = 0.01)) +
  labs(title = "Fronteira Eficiente: Ações vs Ações + ETF IBOVESPA",
       subtitle = "Linha verde: com ETF | Linha tracejada: apenas ações",
       x = "Risco (Desvio Padrão Anualizado)",
       y = "Retorno Esperado Anualizado") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5, size = 10),
        legend.position = "right")

Interpretação da Diversificação com ETF:

A inclusão do ETF IBOVESPA na análise expande o conjunto de oportunidades de investimento, representado pela fronteira eficiente verde. Observe que esta nova fronteira domina a fronteira original (linha tracejada cinza) em praticamente todos os pontos, demonstrando que a adição do ETF oferece combinações superiores de risco e retorno. Isto ocorre porque o ETF IBOVESPA, sendo uma cesta diversificada de ações, apresenta características de risco e retorno que complementam as ações individuais, permitindo que o investidor alcance níveis de diversificação que seriam impossíveis usando apenas as quatro ações isoladamente.

9.1 8.3 Composição Ótima: Ações + ETF

# Encontrar o portfólio de variância mínima com ETF
portfolio_min_var_etf <- fronteira_etf[which.min(fronteira_etf$Risco), ]

ret_alvo_min_etf <- portfolio_min_var_etf$Retorno / 252
portfolio_otimo_etf <- calc_portfolio_etf(ret_alvo_min_etf, retornos_medios_etf, 
                                          cov_matrix_etf)

if(!is.null(portfolio_otimo_etf)){
  composicao_etf <- data.frame(
    Ativo = c(nomes_acoes, "IBOVESPA (ETF)"),
    Peso_Percentual = portfolio_otimo_etf$pesos * 100,
    Valor_Investido = portfolio_otimo_etf$pesos * investimento
  )
  
  # Filtrar apenas ativos com peso significativo (> 0.1%)
  composicao_etf_filtrada <- composicao_etf[composicao_etf$Peso_Percentual > 0.1, ]
  
  cat("\n### PORTFÓLIO ÓTIMO: AÇÕES + ETF IBOVESPA\n\n")
  kable(composicao_etf_filtrada, digits = 2, row.names = FALSE,
        caption = "Composição Ótima com ETF (Variância Mínima)")
  
  cat("\n**Características do Portfólio Ótimo com ETF:**\n")
  cat("- Retorno esperado anualizado:", 
      round(portfolio_otimo_etf$retorno * 252 * 100, 2), "%\n")
  cat("- Risco (desvio padrão anualizado):", 
      round(portfolio_otimo_etf$risco * sqrt(252) * 100, 2), "%\n")
  cat("- Sharpe Ratio (assumindo SELIC como taxa livre de risco):",
      round((portfolio_otimo_etf$retorno * 252 - mean(selic_data$value)/100) / 
            (portfolio_otimo_etf$risco * sqrt(252)), 3), "\n\n")
}
## 
## ### PORTFÓLIO ÓTIMO: AÇÕES + ETF IBOVESPA
## 
## 
## **Características do Portfólio Ótimo com ETF:**
## - Retorno esperado anualizado: 20.35 %
## - Risco (desvio padrão anualizado): 15.4 %
## - Sharpe Ratio (assumindo SELIC como taxa livre de risco): 1.318

A composição ótima que minimiza a variância mostra como o investidor deve distribuir seus recursos entre as ações individuais e o ETF IBOVESPA. Note que a otimização matemática considera não apenas o retorno esperado e o risco de cada ativo, mas também as correlações entre eles, buscando combinações que maximizem o benefício da diversificação. Ativos com peso próximo de zero ou zero indicam que, do ponto de vista da eficiência de média-variância, eles não contribuem significativamente para melhorar a relação risco-retorno do portfólio neste ponto específico da fronteira.

10 8.4 Linha de Alocação de Capital (CAL) - Portfólio com Ativo Livre de Risco

# A Linha de Alocação de Capital conecta o ativo livre de risco (SELIC)
# ao portfólio tangente na fronteira eficiente
# Este é o portfólio de maior Índice de Sharpe

# Taxa livre de risco (SELIC média anualizada)
taxa_livre_risco <- mean(selic_data$value) / 100

cat("\n### CONSTRUÇÃO DA LINHA DE ALOCAÇÃO DE CAPITAL\n\n")
## 
## ### CONSTRUÇÃO DA LINHA DE ALOCAÇÃO DE CAPITAL
cat("Taxa Livre de Risco (SELIC média):", round(taxa_livre_risco * 100, 2), "% a.a.\n\n")
## Taxa Livre de Risco (SELIC média): 0.05 % a.a.
# Função para calcular o Índice de Sharpe
calcular_sharpe <- function(retorno, risco, rf){
  return((retorno - rf) / risco)
}

# Encontrar o portfólio tangente (maior Sharpe Ratio)
fronteira_etf$Sharpe <- sapply(1:nrow(fronteira_etf), function(i){
  calcular_sharpe(fronteira_etf$Retorno[i], fronteira_etf$Risco[i], 
                  taxa_livre_risco)
})

# Portfólio tangente
idx_tangente <- which.max(fronteira_etf$Sharpe)
portfolio_tangente <- fronteira_etf[idx_tangente, ]

cat("**Portfólio Tangente (Ótimo de Sharpe):**\n")
## **Portfólio Tangente (Ótimo de Sharpe):**
cat("- Retorno esperado:", round(portfolio_tangente$Retorno * 100, 2), "% a.a.\n")
## - Retorno esperado: 25.61 % a.a.
cat("- Risco:", round(portfolio_tangente$Risco * 100, 2), "% a.a.\n")
## - Risco: 16.91 % a.a.
cat("- Índice de Sharpe:", round(portfolio_tangente$Sharpe, 3), "\n\n")
## - Índice de Sharpe: 1.512
# Calcular composição do portfólio tangente
ret_alvo_tangente <- portfolio_tangente$Retorno / 252
portfolio_tangente_composicao <- calc_portfolio_etf(ret_alvo_tangente, 
                                                    retornos_medios_etf, 
                                                    cov_matrix_etf)

if(!is.null(portfolio_tangente_composicao)){
  composicao_tangente <- data.frame(
    Ativo = c(nomes_acoes, "IBOVESPA (ETF)"),
    Peso_Percentual = portfolio_tangente_composicao$pesos * 100,
    Valor_Investido_100k = portfolio_tangente_composicao$pesos * investimento
  )
  
  # Filtrar ativos com peso > 0.1%
  composicao_tangente_filtrada <- composicao_tangente[
    composicao_tangente$Peso_Percentual > 0.1, 
  ]
  
  kable(composicao_tangente_filtrada, digits = 2, row.names = FALSE,
        caption = "Composição do Portfólio Tangente (Máximo Sharpe)")
}
Composição do Portfólio Tangente (Máximo Sharpe)
Ativo Peso_Percentual Valor_Investido_100k
BBAS3 51.57 51570.1
IBOVESPA (ETF) 48.43 48429.9
# Criar pontos da Linha de Alocação de Capital
# CAL: E(Rp) = Rf + (E(Rt) - Rf) / σt * σp
# onde Rt é o retorno do portfólio tangente

risco_cal <- seq(0, max(fronteira_etf$Risco) * 1.2, length.out = 100)
retorno_cal <- taxa_livre_risco + 
  (portfolio_tangente$Retorno - taxa_livre_risco) / portfolio_tangente$Risco * 
  risco_cal

linha_cal <- data.frame(Risco = risco_cal, Retorno = retorno_cal)

# Ponto do ativo livre de risco
ponto_rf <- data.frame(
  Risco = 0,
  Retorno = taxa_livre_risco,
  Label = "SELIC (Livre de Risco)"
)

# Gráfico com CAL
ggplot() +
  # Fronteira eficiente
  geom_line(data = fronteira_etf, aes(x = Risco, y = Retorno),
            color = "darkgreen", linewidth = 1.3, alpha = 0.7) +
  # Linha de Alocação de Capital
  geom_line(data = linha_cal, aes(x = Risco, y = Retorno),
            color = "red", linewidth = 1.5) +
  # Portfólio tangente
  geom_point(data = portfolio_tangente, aes(x = Risco, y = Retorno),
             color = "red", size = 5, shape = 18) +
  geom_text(data = portfolio_tangente, aes(x = Risco, y = Retorno),
            label = "Portfólio\nTangente", vjust = -1.5, hjust = 0.5,
            color = "red", fontface = "bold", size = 3.5) +
  # Ativo livre de risco
  geom_point(data = ponto_rf, aes(x = Risco, y = Retorno),
             color = "blue", size = 5, shape = 17) +
  geom_text(data = ponto_rf, aes(x = Risco, y = Retorno, label = Label),
            vjust = 1.5, color = "blue", fontface = "bold", size = 3.5) +
  # Ativos individuais
  geom_point(data = todos_ativos, aes(x = Risco, y = Retorno),
             color = "gray50", size = 3, alpha = 0.6) +
  scale_x_continuous(labels = scales::percent_format(accuracy = 0.01),
                     limits = c(0, NA)) +
  scale_y_continuous(labels = scales::percent_format(accuracy = 0.01)) +
  labs(title = "Linha de Alocação de Capital (CAL)",
       subtitle = "Combinações ótimas: Portfólio Tangente + SELIC",
       x = "Risco (Desvio Padrão Anualizado)",
       y = "Retorno Esperado Anualizado") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5, size = 10))

Interpretação da Linha de Alocação de Capital:

A linha vermelha representa a Linha de Alocação de Capital, que mostra todas as combinações possíveis entre o ativo livre de risco (SELIC) e o portfólio tangente. Esta linha é fundamental porque domina toda a fronteira eficiente, exceto no ponto de tangência. Isso significa que, para qualquer nível de risco desejado, um investidor sempre obtém um retorno esperado maior investindo parte do capital na SELIC e parte no portfólio tangente, em vez de escolher qualquer outro ponto da fronteira eficiente.

O ponto de tangência (losango vermelho) representa o portfólio de ativos com risco que possui o maior Índice de Sharpe, ou seja, oferece a melhor recompensa por unidade de risco assumido acima da taxa livre de risco. Investidores mais conservadores devem alocar maior proporção na SELIC e menor no portfólio tangente, posicionando-se mais à esquerda da linha. Investidores mais arrojados fazem o oposto, podendo inclusive usar alavancagem para se posicionar além do portfólio tangente, mais à direita na linha.

10.1 8.5 Alocações Estratégicas ao Longo da CAL

# Vamos calcular algumas alocações específicas ao longo da CAL
# para diferentes perfis de investidores

cat("\n### ESTRATÉGIAS DE ALOCAÇÃO COM R$ 100.000,00\n\n")
## 
## ### ESTRATÉGIAS DE ALOCAÇÃO COM R$ 100.000,00
# Função para calcular alocação na CAL dado um nível de risco alvo
calcular_alocacao_cal <- function(risco_alvo, ret_tangente, risco_tangente, 
                                  taxa_rf, investimento_total){
  # Peso no portfólio tangente
  w_tangente <- risco_alvo / risco_tangente
  
  # Peso no ativo livre de risco
  w_rf <- 1 - w_tangente
  
  # Retorno esperado
  retorno_esperado <- w_rf * taxa_rf + w_tangente * ret_tangente
  
  return(list(
    peso_rf = w_rf,
    peso_tangente = w_tangente,
    valor_rf = w_rf * investimento_total,
    valor_tangente = w_tangente * investimento_total,
    retorno_esperado = retorno_esperado,
    risco = risco_alvo
  ))
}

# Diferentes perfis de risco (em termos de desvio padrão anualizado)
perfis <- data.frame(
  Perfil = c("Conservador", "Moderado", "Arrojado", "Muito Arrojado"),
  Risco_Alvo = c(0.05, 0.10, 0.15, 0.20)  # 5%, 10%, 15%, 20% de volatilidade
)

resultados_perfis <- data.frame()

for(i in 1:nrow(perfis)){
  alocacao <- calcular_alocacao_cal(
    perfis$Risco_Alvo[i],
    portfolio_tangente$Retorno,
    portfolio_tangente$Risco,
    taxa_livre_risco,
    investimento
  )
  
  resultados_perfis <- rbind(resultados_perfis, data.frame(
    Perfil = perfis$Perfil[i],
    Risco_Alvo = perfis$Risco_Alvo[i] * 100,
    Peso_SELIC = alocacao$peso_rf * 100,
    Peso_Portfolio_Tangente = alocacao$peso_tangente * 100,
    Valor_SELIC = alocacao$valor_rf,
    Valor_Portfolio_Tangente = alocacao$valor_tangente,
    Retorno_Esperado = alocacao$retorno_esperado * 100
  ))
}

kable(resultados_perfis, digits = 2, booktabs = TRUE, row.names = FALSE,
      caption = "Alocações Estratégicas para Diferentes Perfis de Risco")%>%
  kableExtra::kable_styling(latex_options = c("striped", "scale_down", "HOLD_position"), full_width = FALSE)
Alocações Estratégicas para Diferentes Perfis de Risco
Perfil Risco_Alvo Peso_SELIC Peso_Portfolio_Tangente Valor_SELIC Valor_Portfolio_Tangente Retorno_Esperado
Conservador 5 70.43 29.57 70427.33 29572.67 7.61
Moderado 10 40.85 59.15 40854.65 59145.35 15.17
Arrojado 15 11.28 88.72 11281.98 88718.02 22.73
Muito Arrojado 20 -18.29 118.29 -18290.70 118290.70 30.29
cat("\n**Interpretação das Alocações:**\n\n")
## 
## **Interpretação das Alocações:**
for(i in 1:nrow(resultados_perfis)){
  cat("**", resultados_perfis$Perfil[i], ":**\n")
  cat("  - Investir R$", format(round(resultados_perfis$Valor_SELIC[i], 2),
                                 big.mark = ".", decimal.mark = ","),
      "em SELIC (", round(resultados_perfis$Peso_SELIC[i], 1), "%)\n")
  cat("  - Investir R$", format(round(resultados_perfis$Valor_Portfolio_Tangente[i], 2),
                                 big.mark = ".", decimal.mark = ","),
      "no Portfólio Tangente (", round(resultados_perfis$Peso_Portfolio_Tangente[i], 1), "%)\n")
  cat("  - Retorno esperado:", round(resultados_perfis$Retorno_Esperado[i], 2), 
      "% a.a.\n")
  cat("  - Risco (volatilidade):", round(resultados_perfis$Risco_Alvo[i], 2), 
      "% a.a.\n\n")
}
## ** Conservador :**
##   - Investir R$ 70.427,33 em SELIC ( 70.4 %)
##   - Investir R$ 29.572,67 no Portfólio Tangente ( 29.6 %)
##   - Retorno esperado: 7.61 % a.a.
##   - Risco (volatilidade): 5 % a.a.
## 
## ** Moderado :**
##   - Investir R$ 40.854,65 em SELIC ( 40.9 %)
##   - Investir R$ 59.145,35 no Portfólio Tangente ( 59.1 %)
##   - Retorno esperado: 15.17 % a.a.
##   - Risco (volatilidade): 10 % a.a.
## 
## ** Arrojado :**
##   - Investir R$ 11.281,98 em SELIC ( 11.3 %)
##   - Investir R$ 88.718,02 no Portfólio Tangente ( 88.7 %)
##   - Retorno esperado: 22.73 % a.a.
##   - Risco (volatilidade): 15 % a.a.
## 
## ** Muito Arrojado :**
##   - Investir R$ -18.290,7 em SELIC ( -18.3 %)
##   - Investir R$ 118.290,7 no Portfólio Tangente ( 118.3 %)
##   - Retorno esperado: 30.29 % a.a.
##   - Risco (volatilidade): 20 % a.a.
# Detalhamento para perfil Moderado
cat("\n**Detalhamento do Perfil Moderado (R$ 100.000):**\n\n")
## 
## **Detalhamento do Perfil Moderado (R$ 100.000):**
alocacao_moderado <- calcular_alocacao_cal(0.10, portfolio_tangente$Retorno,
                                           portfolio_tangente$Risco,
                                           taxa_livre_risco, investimento)

valor_na_selic <- alocacao_moderado$valor_rf
valor_no_tangente <- alocacao_moderado$valor_tangente

cat("1. SELIC: R$", format(round(valor_na_selic, 2), 
                           big.mark = ".", decimal.mark = ","), "\n\n")
## 1. SELIC: R$ 40.854,65
cat("2. Portfólio Tangente: R$", format(round(valor_no_tangente, 2),
                                        big.mark = ".", decimal.mark = ","), 
    "distribuídos em:\n")
## 2. Portfólio Tangente: R$ 59.145,35 distribuídos em:
if(!is.null(portfolio_tangente_composicao)){
  for(j in 1:length(composicao_tangente_filtrada$Ativo)){
    valor_ativo <- composicao_tangente_filtrada$Peso_Percentual[j] / 100 * 
                   valor_no_tangente
    cat("   - ", composicao_tangente_filtrada$Ativo[j], ": R$",
        format(round(valor_ativo, 2), big.mark = ".", decimal.mark = ","),
        " (", round(composicao_tangente_filtrada$Peso_Percentual[j], 2), 
        "% do portfólio tangente)\n")
  }
}
##    -  BBAS3 : R$ 30.501,32  ( 51.57 % do portfólio tangente)
##    -  IBOVESPA (ETF) : R$ 28.644,03  ( 48.43 % do portfólio tangente)

Esta análise final demonstra como um investidor pode implementar a estratégia ótima na prática. O portfólio tangente representa a melhor combinação de ativos com risco (ações e ETF), e a proporção entre este portfólio e a SELIC determina o perfil de risco-retorno final. O investidor conservador prioriza a proteção do capital, alocando maior parte na SELIC, enquanto o arrojado busca maximizar retornos, concentrando-se no portfólio tangente. Note que todos os perfis utilizam exatamente a mesma composição de ativos com risco (o portfólio tangente), variando apenas a proporção total investida neste portfólio versus na SELIC, demonstrando a elegância e simplicidade do modelo de Markowitz com ativo livre de risco.

11 9. Cálculo do VaR Histórico

# VaR Histórico: percentil (1-confiança) dos retornos históricos
# Para 99% de confiança, pegamos o 1º percentil (piores 1% dos retornos)

calcular_var <- function(retornos_acao, investimento, confianca){
  # Percentil correspondente
  percentil <- 1 - confianca
  
  # VaR como percentil dos retornos
  var_percentual <- quantile(retornos_acao, percentil)
  
  # Perda máxima esperada em reais
  perda_maxima <- -var_percentual * investimento
  
  return(list(
    VaR_percentual = var_percentual,
    Perda_Maxima_Reais = perda_maxima,
    Percentil = percentil * 100
  ))
}

# Calcular VaR para cada ação
resultados_var <- data.frame()

for(i in 1:ncol(retornos)){
  var_resultado <- calcular_var(retornos[,i], investimento, confianca)
  
  resultados_var <- rbind(resultados_var, data.frame(
    Acao = nomes_acoes[i],
    VaR_Percentual = var_resultado$VaR_percentual,
    Perda_Maxima_R = var_resultado$Perda_Maxima_Reais,
    Investimento_R = investimento,
    Perda_Percentual = (var_resultado$Perda_Maxima_Reais / investimento) * 100
  ))
}

kable(resultados_var, digits = 2,
      caption = paste0("VaR Histórico (", confianca*100, "% de confiança) - Janela de ", 
                      janela, " dias úteis"))
VaR Histórico (99% de confiança) - Janela de 252 dias úteis
Acao VaR_Percentual Perda_Maxima_R Investimento_R Perda_Percentual
1% POMO3 -0.04 4476.15 100000 4.48
1%1 AURE3 -0.05 4812.28 100000 4.81
1%2 BBAS3 -0.03 2681.50 100000 2.68
1%3 ITUB3 -0.07 6807.77 100000 6.81

11.1 9.1 VaR do Portfólio Ótimo (Variância Mínima)

# Calcular portfólio de variância mínima global
# Este é o ponto mais à esquerda da fronteira eficiente

# Encontrar o portfólio com menor risco
portfolio_min_var <- fronteira[which.min(fronteira$Risco), ]

# Para calcular VaR do portfólio, precisamos dos pesos ótimos
# Vamos recalcular para o retorno correspondente
ret_alvo_min <- portfolio_min_var$Retorno / 252  # Desanualizar

portfolio_otimo <- calc_portfolio(ret_alvo_min, retornos_medios, cov_matrix)

if(!is.null(portfolio_otimo)){
  # Calcular retornos do portfólio
  pesos_matrix <- matrix(portfolio_otimo$pesos, ncol = 1)
  retornos_portfolio <- as.matrix(retornos) %*% pesos_matrix
  
  # Calcular VaR do portfólio
  var_portfolio <- calcular_var(retornos_portfolio, investimento, confianca)
  
  # Exibir composição do portfólio
  composicao <- data.frame(
    Acao = nomes_acoes,
    Peso = portfolio_otimo$pesos * 100,
    Valor_Investido = portfolio_otimo$pesos * investimento
  )
  
  cat("\n### PORTFÓLIO DE VARIÂNCIA MÍNIMA\n\n")
  kable(composicao, digits = 2,
        caption = "Composição do Portfólio Ótimo (Variância Mínima)")
  
  cat("\n**VaR do Portfólio Ótimo:**\n")
  cat("- Retorno esperado diário:", round(portfolio_otimo$retorno * 100, 4), "%\n")
  cat("- Risco (desvio padrão):", round(portfolio_otimo$risco * 100, 4), "%\n")
  cat("- VaR (99%):", round(var_portfolio$VaR_percentual * 100, 2), "%\n")
  cat("- Perda máxima esperada: R$", 
      format(round(var_portfolio$Perda_Maxima_Reais, 2), 
             big.mark = ".", decimal.mark = ","), "\n\n")
  
  # Comparação com investimento em ações individuais
  cat("**Comparação:** O portfólio diversificado reduz o risco comparado\n")
  cat("ao investimento em ações individuais, demonstrando o benefício da diversificação.\n")
}
## 
## ### PORTFÓLIO DE VARIÂNCIA MÍNIMA
## 
## 
## **VaR do Portfólio Ótimo:**
## - Retorno esperado diário: 0.0634 %
## - Risco (desvio padrão): 1.1982 %
## - VaR (99%): -3.08 %
## - Perda máxima esperada: R$ 3.077,68 
## 
## **Comparação:** O portfólio diversificado reduz o risco comparado
## ao investimento em ações individuais, demonstrando o benefício da diversificação.

12 10. Interpretação dos Resultados

cat("\n## INTERPRETAÇÃO DO VALUE AT RISK (VaR)\n\n")
## 
## ## INTERPRETAÇÃO DO VALUE AT RISK (VaR)
cat("Com um investimento de R$", format(investimento, big.mark = ".", decimal.mark = ","), 
    "em cada ação:\n\n")
## Com um investimento de R$ 100.000 em cada ação:
for(i in 1:nrow(resultados_var)){
  cat("### ", resultados_var$Acao[i], "\n")
  cat("- VaR (99%): ", round(resultados_var$VaR_Percentual[i] * 100, 2), "%\n")
  cat("- Perda máxima esperada: R$", 
      format(round(resultados_var$Perda_Maxima_R[i], 2), 
             big.mark = ".", decimal.mark = ","), "\n")
  cat("- Interpretação: Com 99% de confiança, a perda não deve exceder R$",
      format(round(resultados_var$Perda_Maxima_R[i], 2), 
             big.mark = ".", decimal.mark = ","),
      "em um dia de negociação.\n")
  cat("- Percentual do investimento:", 
      round(resultados_var$Perda_Percentual[i], 2), "%\n\n")
}
## ###  POMO3 
## - VaR (99%):  -4.48 %
## - Perda máxima esperada: R$ 4.476,15 
## - Interpretação: Com 99% de confiança, a perda não deve exceder R$ 4.476,15 em um dia de negociação.
## - Percentual do investimento: 4.48 %
## 
## ###  AURE3 
## - VaR (99%):  -4.81 %
## - Perda máxima esperada: R$ 4.812,28 
## - Interpretação: Com 99% de confiança, a perda não deve exceder R$ 4.812,28 em um dia de negociação.
## - Percentual do investimento: 4.81 %
## 
## ###  BBAS3 
## - VaR (99%):  -2.68 %
## - Perda máxima esperada: R$ 2.681,5 
## - Interpretação: Com 99% de confiança, a perda não deve exceder R$ 2.681,5 em um dia de negociação.
## - Percentual do investimento: 2.68 %
## 
## ###  ITUB3 
## - VaR (99%):  -6.81 %
## - Perda máxima esperada: R$ 6.807,77 
## - Interpretação: Com 99% de confiança, a perda não deve exceder R$ 6.807,77 em um dia de negociação.
## - Percentual do investimento: 6.81 %

13 9. Visualização Comparativa do VaR

# Gráfico de barras comparativo
ggplot(resultados_var, aes(x = Acao, y = Perda_Maxima_R, fill = Acao)) +
  geom_bar(stat = "identity", alpha = 0.7) +
  geom_text(aes(label = paste0("R$ ", format(round(Perda_Maxima_R, 0), 
                                              big.mark = "."))),
            vjust = -0.5, size = 4) +
  labs(title = "Comparação do VaR (99%) entre as Ações",
       subtitle = paste0("Perda máxima esperada com investimento de R$ ", 
                        format(investimento, big.mark = ".")),
       x = "Ação",
       y = "Perda Máxima Esperada (R$)") +
  theme_minimal() +
  theme(legend.position = "none",
        plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5, size = 10))

14 10. Conclusões

cat("\n## CONCLUSÕES PRINCIPAIS\n\n")
## 
## ## CONCLUSÕES PRINCIPAIS
# Ação com maior risco
acao_maior_risco <- resultados_var$Acao[which.max(resultados_var$Perda_Maxima_R)]
maior_perda <- max(resultados_var$Perda_Maxima_R)

# Ação com menor risco
acao_menor_risco <- resultados_var$Acao[which.min(resultados_var$Perda_Maxima_R)]
menor_perda <- min(resultados_var$Perda_Maxima_R)

cat("1. **Maior Risco:** ", acao_maior_risco, 
    " - Perda máxima de R$", format(round(maior_perda, 2), 
                                    big.mark = ".", decimal.mark = ","), "\n\n")
## 1. **Maior Risco:**  ITUB3  - Perda máxima de R$ 6.807,77
cat("2. **Menor Risco:** ", acao_menor_risco, 
    " - Perda máxima de R$", format(round(menor_perda, 2), 
                                    big.mark = ".", decimal.mark = ","), "\n\n")
## 2. **Menor Risco:**  BBAS3  - Perda máxima de R$ 2.681,5
cat("3. **Sobre a Normalidade:** Retornos financeiros geralmente apresentam:\n")
## 3. **Sobre a Normalidade:** Retornos financeiros geralmente apresentam:
cat("   - Caudas mais pesadas que a distribuição normal\n")
##    - Caudas mais pesadas que a distribuição normal
cat("   - Assimetria (mais retornos negativos extremos)\n")
##    - Assimetria (mais retornos negativos extremos)
cat("   - Mesmo com rejeição da normalidade, o VaR histórico é robusto\n\n")
##    - Mesmo com rejeição da normalidade, o VaR histórico é robusto
cat("4. **Limitações do VaR Histórico:**\n")
## 4. **Limitações do VaR Histórico:**
cat("   - Assume que o passado se repete\n")
##    - Assume que o passado se repete
cat("   - Não captura eventos extremos não observados no período\n")
##    - Não captura eventos extremos não observados no período
cat("   - Não mede perdas além do percentil (tail risk)\n")
##    - Não mede perdas além do percentil (tail risk)

Nota Metodológica: Este relatório utiliza o VaR Histórico não-paramétrico, que não assume normalidade dos retornos. Os testes de normalidade servem para verificar se métodos paramétricos (VaR Delta-Normal) seriam apropriados. Para gestão de risco conservadora, o VaR Histórico é preferível quando a normalidade é rejeitada.