Portfolio

Carrega os pacotes

library(tidyquant)
library(tidyverse)

Obtém os dados

Nota: conheça a sintaxe das funções utilizadas:
* map()
* reduce()
* ROC()
* to.monthly()

simbolos <- c("SPY", "EEM", "SHY", "IYR", "GLD")
simbolos_min <- tolower(simbolos)

precos <- getSymbols(simbolos, src = "yahoo",
                     from = "1990-01-01",
                     auto.assign = TRUE) %>% 
  map(~ Ad(get(.))) %>%  # quantmod::Ad() para preços 'Ajustados'; get() retorna o valor do objeto
  reduce(merge) %>% 
  `colnames<-`(simbolos_min)

# Calcula os preços no último dia de cada mês 
precos_mes <- to.monthly(precos, indexAt = "last", OHLC = FALSE)  

ret <- TTR::ROC(precos_mes)["2005/2019"]
naive <- ret[, c("spy", "shy")]
basico <- ret[, c("spy", "shy", "gld")]

Cria pesos e carteiras diferentes

peso1 <- rep(1 / (ncol(basico)), ncol(basico))
port1 <- Return.portfolio(basico, peso1) %>% 
  `colnames<-`("ret")

peso2 <- c(0.9, 0.10, 0)
port2 <- Return.portfolio(basico, weights = peso2) %>% 
  `colnames<-`("ret")

peson <- c(0.5, 0.5)
portn <- Return.portfolio(naive, peson)

port_comp <- data.frame(data = index(port1), igual = as.numeric(port1),
                        wtd = as.numeric(port2),
                        naive = as.numeric(portn)) 

port_comp %>% 
  gather(key, value, -data) %>% 
  group_by(key) %>% 
  mutate(value = cumprod(value + 1)) %>% 
  ggplot(aes(data, value * 100, color = key)) +
  geom_line() +
  scale_color_manual("", 
                     labels = c("Igual", "Naive", "Arriscado"),
                     values = c("blue", "black", "red")) +
  labs(x = "",
       y = "Índice",
       title = "Três portfolios, qual é o melhor?",
       caption = "Fonte: Yahoo, OSM estimates") +
  theme(legend.position = "top",
        plot.caption = element_text(hjust = 0))

Tabela de resumos do Portfolio

port_comp %>% 
  rename("Igual" = igual,
         "Naive" = naive,
         "Arriscado" = wtd) %>% 
  gather(Asset, value, -data) %>% 
  group_by(Asset) %>% 
  summarise(`Média (%)` = round(mean(value, na.rm = TRUE),3) * 1200,
            `Volatilidade (%)` = round(sd(value, na.rm = TRUE) * sqrt(12), 3) * 100,
            `Ajustado ao risco (%)` = round(mean(value, na.rm = TRUE) / sd(value, na.rm = TRUE) * sqrt(12), 3) * 100,
            `Cumulativo (%)` = round(prod(1 + value, na.rm = TRUE), 3) * 100) %>% 
  knitr::kable(caption = "Métricas de desempenho anualizadas") 
Métricas de desempenho anualizadas
Asset Média (%) Volatilidade (%) Ajustado ao risco (%) Cumulativo (%)
Arriscado 6.0 13.4 47.9 192.9
Igual 3.6 8.5 40.5 144.5
Naive 3.6 7.3 51.8 152.2

Funções para o cálculo de semi-variância

down_dev <- function(vec){
  mean_vec <- mean(vec, na.rm = TRUE)
  down_vec <- vec[vec < mean_vec]
  dev <- sqrt(mean((down_vec - mean_vec)^2))
  dev
}

up_dev <- function(vec){
  mean_vec <- mean(vec, na.rm = TRUE)
  up_vec <- vec[vec > mean_vec]
  dev <- sqrt(mean((up_vec - mean_vec)^2))
  dev
}

Tabela da semi-variância

port_comp %>% 
  rename("Igual" = igual,
         "Naive" = naive,
         "Arriscado" = wtd) %>% 
  gather(Asset, value, -data) %>% 
  group_by(Asset) %>% 
  summarise(`Volatilidade negativa (%)` = round(down_dev(value)*sqrt(12),3)*100,
            `Volatilidade positiva (%)` = round(up_dev(value)*sqrt(12),3)*100) %>% 
  knitr::kable(caption = "Métricas de desempenho anualizadas")
Métricas de desempenho anualizadas
Asset Volatilidade negativa (%) Volatilidade positiva (%)
Arriscado 15.8 10.9
Igual 9.1 7.8
Naive 8.5 6.0

Simulação de Portfolios

Portfolio

mean_ret <- apply(ret[-1, c("spy", "shy", "gld")], 2, mean)
cov_port <- cov(ret[-1, c("spy", "shy", "gld")])
port_exam <- data.frame(ports = colnames(port_comp)[-1],
                        ret = as.numeric(apply(port_comp[,-1],2, mean)),
                        dp = as.numeric(apply(port_comp[,-1], 2, sd)))
port_exam

Pesos aleatórios que garantem mais variação da participação das ações no portfolio

set.seed(123)
wts <- matrix(nrow = 1000, ncol = 3)

for(i in 1:1000){
  a <- runif(1,0,1)
  b <- c()
  for(j in 1:2){
    b[j] <- runif(1,0,1-sum(a,b))
  }
  if(sum(a,b) < 1) {
    inc <- (1-sum(a,b))/3
    vec <- c(a+inc, b+inc)
  } else {
    vec <- c(a,b)
  }
  wts[i,] <- sample(vec,replace = FALSE)
}

Calcula os portfolios aleatórios

port <- matrix(nrow = 1000, ncol = 2)
for(i in 1:1000){
  port[i,1] <- as.numeric(sum(wts[i,] * mean_ret))
  port[i,2] <- as.numeric(sqrt(t(wts[i,] %*% cov_port %*% wts[i,])))
}

colnames(port) <- c("retornos", "risco")
port <- as.data.frame(port)

port

Gráfico de pontos

port %>% 
  ggplot(aes(risco*sqrt(12)*100, retornos*1200)) +
  geom_point(color = "blue", size = 1.2, alpha = 0.4) +
  geom_point(data = port_exam, aes(port_exam[1,3]*sqrt(12)*100,
                            port_exam[1,2]*1200),
             color = "red", size = 6) +
  geom_point(data = port_exam, aes(port_exam[2,3]*sqrt(12)*100,
                                   port_exam[2,2]*1200),
             color = "purple", size = 7) +
  geom_point(data = port_exam, aes(port_exam[3,3]*sqrt(12)*100,
                                   port_exam[3,2]*1200),
             color = "black", size = 5) +
  scale_x_continuous(limits = c(0,14)) +
  labs(x = "Risco (%)",
       y = "Retorno (%)",
       title = "Portfolios simulados")

Portfolios dominados

naive_dom <- port %>% 
  filter(risco < port_exam[3,3]+0.0005,
         risco > port_exam[3,3]-0.0005) %>%  
  summarise(round(max(retornos) - port_exam[3,2],4)*1200+.02) %>% 
  as.numeric() 
naive_dom
## [1] 0.02
equal_dom <- port %>% 
  filter(risco < port_exam[1,3]+0.0005,
         risco > port_exam[1,3]-0.0005) %>%  
  summarise(round(max(retornos) - port_exam[1,2],3)*1200) %>% 
  as.numeric()

equal_dom
## [1] 1.2
risky_dom <- port %>% 
  filter(risco < port_exam[2,3]+0.0005,
         risco > port_exam[2,3]-0.0005) %>%  
  summarise(round(max(retornos) - port_exam[2,2],4)*1200+.02) %>% 
  as.numeric()
risky_dom
## [1] 0.26

Encontra o máximo retorno e o risco equivalente para a mesma faixa de risco

equal_max <- port %>% 
  filter(risco < port_exam[1,3]+0.0005,
         risco > port_exam[1,3]-0.0005) %>%  
  mutate(retornos = retornos*1200,
         risco = risco * sqrt(12)*100) %>% 
  arrange(desc(retornos)) %>% 
  slice(1)

equal_max

Encontra os pesos para o porfolio dominante

eq_wt <- port %>% 
  mutate(spy_wt = wts[,1],
         shy_wt = wts[,2],
         gld_wt = wts[,3],
         retornos = retornos * 1200,
         risco = risco * sqrt(12) *100) %>% 
  filter(retornos == equal_max$retornos,
         risco == equal_max$risco) %>% 
  select(spy_wt, shy_wt, gld_wt) 

Gráfico dos pesos

eq_wt %>% 
  rename("SPY" = spy_wt,
         "SHY" = shy_wt,
         "GLD" = gld_wt) %>% 
  gather(key,value) %>% 
  ggplot(aes(factor(key, level = c("SPY", "SHY", "GLD")), value*100)) +
  geom_bar(stat = 'identity', fill = "blue") +
  geom_text(aes(label = round(value,2)*100), nudge_y = 5) +
  labs(x = "Ativos",
       y = "Pesos (%)",
       title = "Pesos para um melhor resultado")