# Pacotes para análise estatística
library(moments) # Testes de assimetria e curtose
library(nortest) # Testes de normalidade
# 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
library(quadprog) # Otimização quadrática
library(xts) # Séries temporais estendidas
library(kableExtra) # Formatar tabelas# Ler o arquivo CSV
# Importante: ajustar o separador conforme o formato do arquivo
dados_csv <- read.csv("data.csv",
sep = ";", # Separador de colunas
dec = ",", # Separador decimal
stringsAsFactors = FALSE)
# Remover a primeira coluna vazia se existir
if(names(dados_csv)[1] == "" || names(dados_csv)[1] == "X") {
dados_csv <- dados_csv[, -1]
}
# Converter coluna de Data para formato Date
dados_csv$Data <- as.Date(dados_csv$Data, format = "%d/%m/%Y")
# Converter colunas numéricas (remover vírgulas e converter para numeric)
colunas_numericas <- c("POMO3", "AURE3", "BBAS3", "ITUB3", "IBOV", "SELIC")
for(col in colunas_numericas) {
if(col %in% names(dados_csv)) {
# Substituir vírgula por ponto e converter para numérico
dados_csv[[col]] <- as.numeric(gsub(",", ".", dados_csv[[col]]))
}
}
# Ordenar por data
dados_csv <- dados_csv[order(dados_csv$Data), ]
# Exibir informações sobre os dados carregados
cat("### Dados Carregados do CSV:\n\n")## ### Dados Carregados do CSV:
## Total de observações: 250
## Período: 20045 a 20409
## Variáveis: Data, POMO3, AURE3, BBAS3, ITUB3, IBOV, SELIC
# Exibir primeiras linhas
kable(head(dados_csv, 10), digits = 4,
caption = "Primeiras 10 linhas dos dados")| Data | POMO3 | AURE3 | BBAS3 | ITUB3 | IBOV | SELIC |
|---|---|---|---|---|---|---|
| 2024-11-18 | 6.4824 | 9.7349 | 24.0307 | 24.5180 | 127768.2 | 11.15 |
| 2024-11-19 | 6.5005 | 9.8243 | 24.1615 | 24.7836 | 128197.2 | 11.15 |
| 2024-11-21 | 6.5912 | 9.6653 | 23.6101 | 24.4018 | 126922.1 | 11.15 |
| 2024-11-22 | 6.6002 | 9.8739 | 23.9279 | 24.4848 | 129125.5 | 11.15 |
| 2024-11-25 | 6.5368 | 10.3110 | 24.0681 | 24.5429 | 129036.1 | 11.15 |
| 2024-11-26 | 6.5640 | 10.4997 | 24.4522 | 24.8915 | 129922.4 | 11.15 |
| 2024-11-27 | 6.4161 | 10.0726 | 24.0236 | 24.2607 | 127668.6 | 11.15 |
| 2024-11-28 | 5.9446 | 9.7349 | 23.3187 | 23.6216 | 124610.4 | 11.15 |
| 2024-11-29 | 5.8983 | 9.7945 | 23.5949 | 23.6714 | 125667.8 | 11.15 |
| 2024-12-02 | 5.8798 | 9.4667 | 23.4044 | 23.3788 | 125235.5 | 11.15 |
# Verificar se todas as colunas necessárias existem
colunas_necessarias <- c("Data", nomes_acoes, "IBOV", "SELIC")
colunas_faltantes <- setdiff(colunas_necessarias, names(dados_csv))
if(length(colunas_faltantes) > 0) {
cat("⚠️ AVISO: Colunas faltantes:", paste(colunas_faltantes, collapse = ", "), "\n")
# Ajustar nomes_acoes para incluir apenas as disponíveis
nomes_acoes <- intersect(nomes_acoes, names(dados_csv))
cat("Continuando com as ações disponíveis:", paste(nomes_acoes, collapse = ", "), "\n\n")
}
# Criar objetos xts para as séries temporais
# Preços das ações
precos <- xts(dados_csv[, nomes_acoes], order.by = dados_csv$Data)
colnames(precos) <- nomes_acoes
# IBOVESPA
ibovespa <- xts(dados_csv$IBOV, order.by = dados_csv$Data)
colnames(ibovespa) <- "IBOVESPA"
# SELIC (já em % a.a. no CSV)
# Converter para taxa diária equivalente
selic_anual <- dados_csv$SELIC
selic_diaria <- (1 + selic_anual/100)^(1/252) - 1
# Criar dataframe com informações da SELIC
selic_data <- data.frame(
ref.date = dados_csv$Data,
value = selic_anual,
taxa_diaria = selic_diaria
)
cat("\n### Resumo dos Dados Preparados:\n\n")##
## ### Resumo dos Dados Preparados:
## Total de ações analisadas: 4
## Ações: POMO3, AURE3, BBAS3, ITUB3
## Taxa SELIC média (% a.a.): 13.95
## Taxa SELIC atual (% a.a.): 14.9
## Taxa diária equivalente: 0.0551 %
# Resumo dos preços
resumo_precos <- data.frame(
Acao = nomes_acoes,
Preco_Inicial = as.numeric(precos[1, ]),
Preco_Final = as.numeric(precos[nrow(precos), ]),
Retorno_Periodo = ((as.numeric(precos[nrow(precos), ]) /
as.numeric(precos[1, ])) - 1) * 100
)
kable(resumo_precos, digits = 2,
caption = "Resumo dos Preços das Ações")| Acao | Preco_Inicial | Preco_Final | Retorno_Periodo |
|---|---|---|---|
| POMO3 | 6.48 | 6.33 | -2.35 |
| AURE3 | 9.73 | 11.09 | 13.92 |
| BBAS3 | 24.03 | 22.50 | -6.37 |
| ITUB3 | 24.52 | 35.64 | 45.36 |
# Converter 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: ", min(dados_csv$Data), " a ",
max(dados_csv$Data)),
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))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"))selic_df <- data.frame(Data = dados_csv$Data, SELIC = selic_anual)
ggplot(selic_df, aes(x = Data, y = SELIC)) +
geom_line(color = "darkred", linewidth = 1) +
geom_point(color = "darkred", size = 1) +
labs(title = "Evolução da Taxa SELIC",
x = "Data",
y = "Taxa SELIC (% a.a.)") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))# Retornos logarítmicos das ações
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")| Acao | Media | Desvio_Padrao | Minimo | Maximo | Assimetria | Curtose | |
|---|---|---|---|---|---|---|---|
| POMO3 | POMO3 | -0.0001 | 0.0240 | -0.1109 | 0.0720 | -0.5063 | 5.1452 |
| AURE3 | AURE3 | 0.0005 | 0.0208 | -0.0631 | 0.0730 | 0.2483 | 3.4995 |
| BBAS3 | BBAS3 | -0.0003 | 0.0176 | -0.1357 | 0.0463 | -2.0902 | 17.2573 |
| ITUB3 | ITUB3 | 0.0015 | 0.0122 | -0.0278 | 0.0470 | 0.2141 | 4.0322 |
# Criar dataframe completo
dados_completos <- data.frame(Data = index(retornos))
# Adicionar preços e retornos de cada ação
for(i in 1:length(nomes_acoes)){
col_preco <- paste0("Preco_", nomes_acoes[i])
dados_completos[[col_preco]] <- as.numeric(precos[index(retornos), i])
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
selic_subset <- selic_data[selic_data$ref.date %in% dados_completos$Data, ]
dados_completos <- merge(dados_completos,
selic_subset[, c("ref.date", "taxa_diaria")],
by.x = "Data", by.y = "ref.date", all.x = TRUE)
colnames(dados_completos)[ncol(dados_completos)] <- "SELIC_Diaria"
# Preencher NAs com última observação
dados_completos$SELIC_Diaria <- zoo::na.locf(dados_completos$SELIC_Diaria,
na.rm = FALSE)
# Exibir primeiras e últimas linhas
kable(head(dados_completos, 10), digits = 6,
caption = "Primeiras 10 linhas do DataFrame Consolidado") %>%
kable_styling(latex_options = c("scale_down", "striped"))| 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 | 6.500515 | 0.002793 | 9.824260 | 0.009142 | 24.16152 | 0.005431 | 24.78360 | 0.010775 | 128197.2 | 0.003352 | 0.00042 |
| 2024-11-21 | 6.591178 | 0.013851 | 9.665323 | -0.016310 | 23.61006 | -0.023088 | 24.40180 | -0.015525 | 126922.1 | -0.009997 | 0.00042 |
| 2024-11-22 | 6.600244 | 0.001375 | 9.873927 | 0.021353 | 23.92785 | 0.013370 | 24.48480 | 0.003396 | 129125.5 | 0.017211 | 0.00042 |
| 2024-11-25 | 6.536780 | -0.009662 | 10.311002 | 0.043314 | 24.06805 | 0.005842 | 24.54290 | 0.002370 | 129036.1 | -0.000693 | 0.00042 |
| 2024-11-26 | 6.563979 | 0.004152 | 10.499739 | 0.018139 | 24.45223 | 0.015836 | 24.89149 | 0.014104 | 129922.4 | 0.006845 | 0.00042 |
| 2024-11-27 | 6.416058 | -0.022793 | 10.072598 | -0.041532 | 24.02357 | -0.017686 | 24.26070 | -0.025668 | 127668.6 | -0.017499 | 0.00042 |
| 2024-11-28 | 5.944561 | -0.076327 | 9.734858 | -0.034106 | 23.31868 | -0.029781 | 23.62160 | -0.026696 | 124610.4 | -0.024246 | 0.00042 |
| 2024-11-29 | 5.898336 | -0.007806 | 9.794459 | 0.006104 | 23.59492 | 0.011777 | 23.67141 | 0.002106 | 125667.8 | 0.008450 | 0.00042 |
| 2024-12-02 | 5.879846 | -0.003140 | 9.466653 | -0.034041 | 23.40441 | -0.008107 | 23.37878 | -0.012439 | 125235.5 | -0.003446 | 0.00042 |
| 2024-12-03 | 5.879846 | 0.000000 | 9.556054 | 0.009400 | 23.69018 | 0.012136 | 23.51166 | 0.005668 | 126139.2 | 0.007190 | 0.00042 |
kable(tail(dados_completos, 10), digits = 6,
caption = "Últimas 10 linhas do DataFrame Consolidado") %>%
kable_styling(latex_options = c("scale_down", "striped"))| Data | Preco_POMO3 | Retorno_POMO3 | Preco_AURE3 | Retorno_AURE3 | Preco_BBAS3 | Retorno_BBAS3 | Preco_ITUB3 | Retorno_ITUB3 | Preco_IBOVESPA | Retorno_IBOVESPA | SELIC_Diaria | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 240 | 2025-11-04 | 6.55 | 0.012289 | 11.11 | 0.025526 | 22.27 | 0.008568 | 35.51 | -0.004215 | 150704.2 | 0.001660 | 0.000551 |
| 241 | 2025-11-05 | 6.58 | 0.004570 | 11.60 | 0.043159 | 22.61 | 0.015152 | 35.75 | 0.006736 | 153294.4 | 0.017042 | 0.000551 |
| 242 | 2025-11-06 | 6.38 | -0.030867 | 11.67 | 0.006016 | 22.86 | 0.010996 | 35.80 | 0.001398 | 153338.6 | 0.000288 | 0.000551 |
| 243 | 2025-11-07 | 6.37 | -0.001569 | 11.33 | -0.029567 | 22.89 | 0.001311 | 35.75 | -0.001398 | 154063.5 | 0.004716 | 0.000551 |
| 244 | 2025-11-10 | 6.57 | 0.030914 | 11.55 | 0.019231 | 22.78 | -0.004817 | 36.03 | 0.007802 | 155257.3 | 0.007719 | 0.000551 |
| 245 | 2025-11-11 | 6.50 | -0.010712 | 11.53 | -0.001733 | 23.47 | 0.029840 | 36.59 | 0.015423 | 157748.6 | 0.015919 | 0.000551 |
| 246 | 2025-11-12 | 6.38 | -0.018634 | 11.45 | -0.006963 | 22.80 | -0.028962 | 35.70 | -0.024624 | 157632.9 | -0.000734 | 0.000551 |
| 247 | 2025-11-13 | 6.39 | 0.001566 | 11.28 | -0.014958 | 22.50 | -0.013245 | 35.79 | 0.002518 | 157162.4 | -0.002989 | 0.000551 |
| 248 | 2025-11-14 | 6.46 | 0.010895 | 11.17 | -0.009800 | 22.44 | -0.002670 | 35.92 | 0.003626 | 157738.7 | 0.003660 | 0.000551 |
| 249 | 2025-11-17 | 6.33 | -0.020329 | 11.09 | -0.007188 | 22.50 | 0.002670 | 35.64 | -0.007826 | 156992.9 | -0.004739 | 0.000551 |
retornos_df <- data.frame(Data = index(retornos), coredata(retornos))
retornos_long <- retornos_df %>%
pivot_longer(cols = -Data, names_to = "Acao", values_to = "Retorno")
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))retornos_completo <- merge(retornos, retorno_ibov)
retornos_comp_df <- data.frame(Data = index(retornos_completo),
coredata(retornos_completo))
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))
abline(lm(retornos_comp_df[, nomes_acoes[i]] ~ retornos_comp_df$IBOVESPA),
col = "red", lwd = 2)
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)
}testar_normalidade <- function(dados, nome){
cat("\n### Testes de Normalidade para", nome, "\n\n")
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")
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")
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))
}
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.9723
## p-valor = 0.0000896
## Conclusão: Rejeita normalidade
##
## **Anderson-Darling:**
## Estatística A = 1.6133
## p-valor = 0.0003722
## Conclusão: Rejeita normalidade
##
## **Jarque-Bera:**
## Estatística JB = 58.3806
## p-valor = 0.0000000000002103
## Conclusão: Rejeita normalidade
##
##
## ### Testes de Normalidade para AURE3
##
## **Shapiro-Wilk:**
## Estatística W = 0.9931
## p-valor = 0.2996
## Conclusão: Não rejeita normalidade
##
## **Anderson-Darling:**
## Estatística A = 0.4199
## p-valor = 0.3233
## Conclusão: Não rejeita normalidade
##
## **Jarque-Bera:**
## Estatística JB = 5.1469
## p-valor = 0.07627
## Conclusão: Não rejeita normalidade
##
##
## ### Testes de Normalidade para BBAS3
##
## **Shapiro-Wilk:**
## Estatística W = 0.8705
## p-valor = 0.0000000000001113
## Conclusão: Rejeita normalidade
##
## **Anderson-Darling:**
## Estatística A = 3.2295
## p-valor = 0.00000004122
## Conclusão: Rejeita normalidade
##
## **Jarque-Bera:**
## Estatística JB = 2290.238
## p-valor = < 0.00000000000000022
## Conclusão: Rejeita normalidade
##
##
## ### Testes de Normalidade para ITUB3
##
## **Shapiro-Wilk:**
## Estatística W = 0.9823
## p-valor = 0.003477
## Conclusão: Rejeita normalidade
##
## **Anderson-Darling:**
## Estatística A = 0.6738
## p-valor = 0.0776
## Conclusão: Não rejeita normalidade
##
## **Jarque-Bera:**
## Estatística JB = 12.9568
## p-valor = 0.001536
## Conclusão: Rejeita normalidade
rownames(resultados_testes) <- nomes_acoes
kable(resultados_testes, digits = 4,
caption = "P-valores dos Testes de Normalidade")| X0.0000895999492476336 | X0.000372231682844326 | X0.000000000000210276240864005 | |
|---|---|---|---|
| POMO3 | 0.0001 | 0.0004 | 0.0000 |
| AURE3 | 0.2996 | 0.3233 | 0.0763 |
| BBAS3 | 0.0000 | 0.0000 | 0.0000 |
| ITUB3 | 0.0035 | 0.0776 | 0.0015 |
par(mfrow = c(2, 2))
for(i in 1:ncol(retornos)){
qqnorm(retornos[,i], main = paste("Q-Q Plot -", nomes_acoes[i]))
qqline(retornos[,i], col = "red", lwd = 2)
hist(retornos[,i], breaks = 30, probability = TRUE,
main = paste("Histograma -", nomes_acoes[i]),
xlab = "Retorno", ylab = "Densidade", col = "lightblue")
curve(dnorm(x, mean = mean(retornos[,i]), sd = sd(retornos[,i])),
add = TRUE, col = "red", lwd = 2)
}cov_matrix <- cov(retornos)
retornos_medios <- colMeans(retornos)
calc_portfolio <- function(retornos_alvo, retornos_medios, cov_matrix){
n <- length(retornos_medios)
Dmat <- 2 * cov_matrix
dvec <- rep(0, n)
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)))
}
retorno_min <- min(retornos_medios)
retorno_max <- max(retornos_medios)
retornos_alvo <- seq(retorno_min, retorno_max, length.out = 100)
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,
Risco = portfolio$risco * sqrt(252)
))
}
}
acoes_individuais <- data.frame(
Acao = nomes_acoes,
Retorno = retornos_medios * 252,
Risco = apply(retornos, 2, sd) * sqrt(252)
)
ggplot() +
geom_line(data = fronteira, aes(x = Risco, y = Retorno),
color = "darkblue", linewidth = 1.5) +
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) +
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")| Acao | Retorno | Risco | |
|---|---|---|---|
| POMO3 | POMO3 | -0.0241 | 0.3808 |
| AURE3 | AURE3 | 0.1319 | 0.3294 |
| BBAS3 | BBAS3 | -0.0666 | 0.2793 |
| ITUB3 | ITUB3 | 0.3786 | 0.1943 |
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.
# 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")| POMO3 | AURE3 | BBAS3 | ITUB3 | IBOVESPA | |
|---|---|---|---|---|---|
| POMO3 | 1.000 | 0.316 | 0.211 | 0.386 | 0.465 |
| AURE3 | 0.316 | 1.000 | 0.281 | 0.440 | 0.558 |
| BBAS3 | 0.211 | 0.281 | 1.000 | 0.447 | 0.511 |
| ITUB3 | 0.386 | 0.440 | 0.447 | 1.000 | 0.781 |
| IBOVESPA | 0.465 | 0.558 | 0.511 | 0.781 | 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.
# 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.32 %
## - Risco (desvio padrão anualizado): 15.35 %
## - Sharpe Ratio (assumindo SELIC como taxa livre de risco): 0.415
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.
# 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
## Taxa Livre de Risco (SELIC média): 13.95 % 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):**
## - Retorno esperado: 37.86 % a.a.
## - Risco: 19.43 % a.a.
## - Índice de Sharpe: 1.231
# 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)")
}| Ativo | Peso_Percentual | Valor_Investido_100k |
|---|---|---|
| ITUB3 | 100 | 100000 |
# 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.
# 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)| Perfil | Risco_Alvo | Peso_SELIC | Peso_Portfolio_Tangente | Valor_SELIC | Valor_Portfolio_Tangente | Retorno_Esperado |
|---|---|---|---|---|---|---|
| Conservador | 5 | 74.27 | 25.73 | 74265.44 | 25734.56 | 20.10 |
| Moderado | 10 | 48.53 | 51.47 | 48530.88 | 51469.12 | 26.25 |
| Arrojado | 15 | 22.80 | 77.20 | 22796.31 | 77203.69 | 32.41 |
| Muito Arrojado | 20 | -2.94 | 102.94 | -2938.25 | 102938.25 | 38.56 |
##
## **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$ 74.265,44 em SELIC ( 74.3 %)
## - Investir R$ 25.734,56 no Portfólio Tangente ( 25.7 %)
## - Retorno esperado: 20.1 % a.a.
## - Risco (volatilidade): 5 % a.a.
##
## ** Moderado :**
## - Investir R$ 48.530,88 em SELIC ( 48.5 %)
## - Investir R$ 51.469,12 no Portfólio Tangente ( 51.5 %)
## - Retorno esperado: 26.25 % a.a.
## - Risco (volatilidade): 10 % a.a.
##
## ** Arrojado :**
## - Investir R$ 22.796,31 em SELIC ( 22.8 %)
## - Investir R$ 77.203,69 no Portfólio Tangente ( 77.2 %)
## - Retorno esperado: 32.41 % a.a.
## - Risco (volatilidade): 15 % a.a.
##
## ** Muito Arrojado :**
## - Investir R$ -2.938,25 em SELIC ( -2.9 %)
## - Investir R$ 102.938,2 no Portfólio Tangente ( 102.9 %)
## - Retorno esperado: 38.56 % a.a.
## - Risco (volatilidade): 20 % a.a.
##
## **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$ 48.530,88
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$ 51.469,12 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")
}
}## - ITUB3 : R$ 51.469,12 ( 100 % 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.
# 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"))| Acao | VaR_Percentual | Perda_Maxima_R | Investimento_R | Perda_Percentual | |
|---|---|---|---|---|---|
| 1% | POMO3 | -0.07 | 6798.65 | 100000 | 6.80 |
| 1%1 | AURE3 | -0.04 | 4476.00 | 100000 | 4.48 |
| 1%2 | BBAS3 | -0.05 | 4782.39 | 100000 | 4.78 |
| 1%3 | ITUB3 | -0.03 | 2620.28 | 100000 | 2.62 |
##
## ## 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%): -6.8 %
## - Perda máxima esperada: R$ 6.798,65
## - Interpretação: Com 99% de confiança, a perda não deve exceder R$ 6.798,65 em um dia de negociação.
## - Percentual do investimento: 6.8 %
##
## ### AURE3
## - VaR (99%): -4.48 %
## - Perda máxima esperada: R$ 4.476
## - Interpretação: Com 99% de confiança, a perda não deve exceder R$ 4.476 em um dia de negociação.
## - Percentual do investimento: 4.48 %
##
## ### BBAS3
## - VaR (99%): -4.78 %
## - Perda máxima esperada: R$ 4.782,39
## - Interpretação: Com 99% de confiança, a perda não deve exceder R$ 4.782,39 em um dia de negociação.
## - Percentual do investimento: 4.78 %
##
## ### ITUB3
## - VaR (99%): -2.62 %
## - Perda máxima esperada: R$ 2.620,28
## - Interpretação: Com 99% de confiança, a perda não deve exceder R$ 2.620,28 em um dia de negociação.
## - Percentual do investimento: 2.62 %
# 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.102 %
## - Risco (desvio padrão): 1.1636 %
## - VaR (99%): -2.73 %
## - Perda máxima esperada: R$ 2.734,72
##
## **Comparação:** O portfólio diversificado reduz o risco comparado
## ao investimento em ações individuais, demonstrando o benefício da diversificação.
# 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))##
## ## 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:** POMO3 - Perda máxima de R$ 6.798,65
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:** ITUB3 - Perda máxima de R$ 2.620,28
## 3. **Sobre a Normalidade:** Retornos financeiros geralmente apresentam:
## - Caudas mais pesadas que a distribuição normal
## - Assimetria (mais retornos negativos extremos)
## - Mesmo com rejeição da normalidade, o VaR histórico é robusto
## 4. **Limitações do VaR Histórico:**
## - Assume que o passado se repete
## - Não captura eventos extremos não observados no período
## - 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.