prices <- read.csv("IBOV.csv", header = TRUE, sep = ",", stringsAsFactors = FALSE)
prices$Date <- ymd(prices$Date)

Questão 1

Utilize as séries de preços diários entre o primeiro dia de 2018 e o primeiro dia de 2020.

prices1 <- prices %>% dplyr::filter(Date >= "2018-01-01" & Date <= "2019-12-31") %>% 
  dplyr::select(!Date)

Vamos buscar pares cointegrados, candidatos a uma estratégia de pairs trading (arbitragem estatística). O teste de Engle-Granger é uma forma de encontrar estes pares. Dadas duas séries temporais X e Y , os passos são os seguintes:

A

Verifique que ambas as séries são \(I(1)\).

Para definir se a série é ou não estacionária, iremos utilizar o teste de Dickey-Fuller Aumentado. Nesse teste, a hipótese nula é de não estacionaridade. Assim, rejeitar a nula significa considerar a série como sendo estacionária.

pvalores_adf <- lapply(prices1, function(x) adf.test(x)$p.value < 0.05)
pvalores_adf <- do.call("c", pvalores_adf)
pvalores_adf[pvalores_adf]
## BRFS3 PETR3 
##  TRUE  TRUE

Como podemos ver, temos apenas 2 ativos estacionários. Por esse motivo, eliminamos tais ativos da nossa base de dados.

prices1 <- prices1[,-pvalores_adf]

B

Encontre os coeficientes \(\alpha\) e \(\beta\) da regressão linear:

\[ Y = \alpha + \beta X + \epsilon \]

coef_regres <- lapply(prices1, function(x) as.numeric(coef(lm(prices1$PETR3 ~ x))))

C

Calcule a série temporal de resíduos \(\epsilon\) e verifique se ela é \(I(0)\). Caso sim, as séries são cointegradas.

serie_estac <- vector(length = length(prices1))
resd_df <- as.data.frame(matrix(ncol = ncol(prices1), nrow = nrow(prices1)))
for (i in seq_along(prices1)) {
  resd <- residuals(lm(prices1$PETR3 ~ prices1[,i]))
  resd_df[[i]] <- resd
  
  serie_estac[i] <- adf.test(resd)$p.value < 0.05
}
##  BBAS3  BBDC4  BRAP4  BRFS3  CRFB3  CMIG4  HGTX3  CPFE3  CVCB3  EMBR3  EGIE3 
##   TRUE   TRUE   TRUE   TRUE   TRUE   TRUE   TRUE   TRUE   TRUE   TRUE   TRUE 
##  FLRY3  GOLL4  HYPE3  IRBR3  ITUB4 KLBN11  BRDT3  PETR3  PETR4 SANB11  SUZB3 
##   TRUE   TRUE   TRUE   TRUE   TRUE   TRUE   TRUE   TRUE   TRUE   TRUE   TRUE 
## TAEE11 
##   TRUE

D

Encontre 3 pares cointegrados com o ativo PETR3 e indique a equação de regressão nestes casos. Plote também os gráficos dos resíduos, incluindo uma linha horizontal com a média dos mesmos.

Temos, ao todo, 23 ações que estão cointegradas com PETR3. Como o exercício pede para apresentarmos gráficos, temos uma óbvia restrição de espaço. Por isso, iremos trabalhar apenas com BBAS3, BBDC4, BRAP4, BRFS3, CRFB3 e CMIG4. Como a apresentação da equação é repetitiva, iremos apresentá-la apenas para os três primeiros ativos.

\[ PETR3 = 8.11 + 0.45 \times BBAS3 + \epsilon \]

\[ PETR3 = 4.13 + 0.83 \times BBDC4 + \epsilon \]

\[ PETR3 = 1.75 + 0.8 \times BRAP4 + \epsilon \]

Vamos definir uma estratégia de investimento na qual abrimos uma operação se os resíduos alcançarem a banda vermelha (\(\mu \pm 2 \times \sigma\)) e encerramos quando atingir a média (linha em azul). Antes de irmos para os resultados, temos que lembrar que nossos dados estão enviesados, dado que utilizamos a série completa para calcular a média e os desvios padrões.

Com isso em mente, podemos perceber que a estratégia teve fortes perdas próximo ao período 100 para os pares BBAS3, BBDC4, BRFS3 e CMIG4. Além disso, a estratégia não teria entregado bons resultados com o par BRFS3. Entretanto, para os outros pares, teríamos uma boa estratégia de investimento.

Questão 2

Nesta questão vamos simular o modelo de Markowitz com rebalanceamento diário e sem recálculo da estratégia. Estes conceitos são explicados logo ali abaixo, neste mesmo documento. O universo de ativos será composto por todos os ativos disponibilizados (ativos do iBov + BOVA11). Utilize os dados do primeiro dia de 2019 até o primeiro dia de 2020 como in-sample e do primeiro dia de 2020 até o final como out-of-sample. Calcule o \(\mu\) e \(\sum\) através dos dados históricos in-sample. Encontre:

prices <- read.csv("IBOV.csv", header = TRUE, sep = ",", stringsAsFactors = FALSE)
prices$Date <- ymd(prices$Date)
in_sample <- prices %>% dplyr::filter(Date >= "2019-01-01" & Date <= "2019-12-31") %>% 
  dplyr::select(!Date)
out_sample <- prices %>% dplyr::filter(Date >= "2020-01-01") %>% 
  dplyr::select(!Date)

in_sample <- as.data.frame(apply(in_sample, 2, function(x) diff(x)/x[-length(x)])) # Retornos
out_sample <- as.data.frame(apply(out_sample, 2, function(x) diff(x)/x[-length(x)])) # Retornos
matrizcov <- cov(in_sample)
mu <- apply(in_sample, 2, mean)

A

O portfolio de variância mínima global p1. Calcule \(\mu_{p1}\) e \(\sigma_{p1}\), e plote um gráfico de barras com os pesos de todos os ativos i onde \(w_i ≥ 0.0001\) ou \(w_i ≤ −0.0001\).

Nessa seção utilizamos os códigos disponibilizado pelo professor Robert Aldo Iquiapaza na discplina Métodos Computacionais Aplicados ao Mercado de Capitais.

icov <- solve(matrizcov) # inversa das covariâncias
uns <- rep(1,length(mu))
pvm <- icov %*% uns
pvm <- pvm/sum(pvm) #PVM
pvm1 <- data.frame(Ativos = rownames(pvm), Pesos = pvm)

B

O portfolio de variância mínima global, sem shorting, p2. Calcule \(\mu_{p2}\) e \(\sigma_{p2}\), e plote um gráfico de barras com os pesos de todos os ativos i onde \(w_i ≥ 0.0001\).

ndm <- dim(in_sample)

nObs <- ndm[1]
nAtivos <- ndm[2]

opt.restrics <- matrix(c(rep(1,ndm[2]), # soma de pesos = 1
                          diag(1,ndm[2])), # restrição w_i >= 0 ˜
                          nrow=ndm[2]+1, byrow=TRUE)

opt.lad <- matrix(c(1,rep(0.0000000,ndm[2])))

opt.igu <- 1 # a 1a restr é '=', as outras '>=' 

zeros <- array(0, dim = c(nAtivos,1))# vetor de nAtivos x 1 de 0s

solu.minvol <- solve.QP(matrizcov, zeros, t(opt.restrics), opt.lad, meq = opt.igu)

C

O portfolio eficiente sem shorting e com retorno esperado mínimo 0.3%, p3. Novamente, calcule \(\mu_{p3}\) e \(\sigma_{p3}\) e plote um gráfico de barras com os pesos de todos os ativos i onde \(w_i ≥ 0.0001\).

Como não soube operacionalizar essa otimização, vamos considerar apenas o portfólio eficiente simples. Para evitarmos pesos negativos iremos substituir os pesos negativos por 0 e depois reescalonar.

excr <- mu - 0 #retornos em excesso
ptan <- icov %*% excr

for (i in 1:nrow(ptan)) {
  if(ptan[i,1] < 0){
    ptan[i,1] <- 0
  }
}

ptan <- ptan/sum(ptan) #PT

D

Calcule as séries out-of-sample dos três portfólios assumindo um investimento inicial de R$1. Plote um gráfico comparativo com a performance dos mesmos no período out-of-sample. Inclua também a série do índice iBov - normalize o iBov para começar de 1.

retornos_ports <- function(pesos, retornos){
  ret <- xts::xts(retornos[,-1], retornos$Date)
  
  port <- PerformanceAnalytics::Return.portfolio(ret, weights = pesos)
  
  return(port)
}
in_pvm1 <- retornos_ports(pvm1$Pesos, in_sample_with_dates)
in_pvm2 <- retornos_ports(pvm2$Pesos, in_sample_with_dates)
in_ptan <- retornos_ports(ptan$Pesos, in_sample_with_dates)
out_pvm1 <- retornos_ports(pvm1$Pesos, out_sample_with_dates)
out_pvm2 <- retornos_ports(pvm2$Pesos, out_sample_with_dates)
out_ptan <- retornos_ports(ptan$Pesos, out_sample_with_dates)

ports <- list(in_pvm1, in_pvm2, in_ptan, out_pvm1, out_pvm2, out_ptan)

E

Calcule o retorno médio e o desvio padrão diários das séries out-of-sample para cada portfolio. Estes valores estão condizentes com os valores calculados in-sample?

ports <- lapply(ports, function(x) c(mean(x, na.rm = TRUE), sd(x, na.rm = TRUE)))

Como podemos perceber, temos uma diferença bastante grande entre os resultados dos portfólios in sample e out of sample. Primeiro, podemos destacar uma queda brusca no retorno médio: do campo positivo para o negativo em todos os casos. Além disso, temos um aumento da volatilidade em mais de 3 vezes. Ou seja, por mais que o modelo de otimização encontre o portfólio de mínima variância e o tangente, ele é pouco útil para construção real de portfólios.