prices <- read.csv("IBOV.csv", header = TRUE, sep = ",", stringsAsFactors = FALSE)
prices$Date <- ymd(prices$Date)
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:
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]
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))))
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
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.
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)
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)
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)
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
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)
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.