Web Scraping de retornos de ações

Web Scraping de retornos de ações

Com Carteira de Mínima Variância Global

Esse artigo é 2 em 1: vamos raspar dados sobre retornos de ações e calcular os pesos de uma carteira de mínima variância global. Os pesos dessa carteira serão baseados nas ações que compõem o DIVI.

Utilizaremos os dados do site ibovx. Abaixo, segue o código para raspar os retornos no site:

suppressMessages(library(httr))
suppressMessages(library(rvest))

ticker <- read.csv("https://www.dropbox.com/s/h0a2sdt4m1g548u/DIVI.csv?dl=1",header=FALSE)
# Por algum motivo, "AESB3" não estava disponível no site.

data_inicial <- "14/04/2018"
data_final <- "23/04/2021"

for(i in 1:nrow(ticker)){

    url <- paste0("https://www.ibovx.com.br/historico-papeis-bovespa.aspx?papel=",unlist(ticker)[i],"&dtini=",data_inicial,"&dtfim=",data_final)
    sh <- GET(url = url)
    data <- read_html(sh) %>% html_nodes("div") %>% html_nodes("table") %>% html_nodes("tr") %>% html_nodes("td") %>% html_text()
    data <- data[-grep("\r\n.ibovx_bannerresponsivoabaixomenu",data)] # remover banner entre as linhas da tabela
    if(! length(data) == 0){
        data <- matrix(data[13:length(data)],ncol=9,byrow=TRUE)
        ret <- gsub("%","", data[-1,2]) 
        ret <- na.omit(as.numeric(gsub(",",".",ret)))
        ret <- matrix(ret, byrow = TRUE)
        colnames(ret) <- unlist(ticker)[i]
        attributes(ret)$na.action <- NULL

        if(i == 1){
            returnsT <- ret
        }else{
            if(nrow(ret) == nrow(returnsT)){
                returnsT <- cbind(returnsT,ret)
            }
        }
    }

    pb <- txtProgressBar(min = (1 / nrow(ticker)), max = nrow(ticker), style = 3)
    setTxtProgressBar(pb,i)

}

head(returnsT)

image.png

Para calcular dos pesos da carteira, usaremos a seguinte equação:

$$ w = \frac{\Sigma^{-1}\textbf{1}}{\textbf{1}'\Sigma^{-1}\textbf{1}}, $$

no qual \(\Sigma\) é a matriz de variância dos retornos e \(\textbf{1}\) é vetor de uns.

colnames(returnsT) = unlist(ticker)
invSig = solve(cov(returnsT))
ones = rep(1,nrow(Sigma))

w = (invSig %*% ones) %*% (1 / (t(ones) %*% invSig %*% ones)) 
w = t(t(w[sort(w, decreasing = TRUE, index.return = TRUE)$ix,]))
colnames(w) = "Peso"
w

image.png

Reproduzi apenas uma parte dos pesos, mas a soma dos pesos é igual a 1 e os valores negativos significam venda a descoberto. O destaque vai para a Taesa.

Seria interessante fazer um estudo com janelas rolantes (ou expansiva) e verificar como seria a performance de uma carteira com características semelhantes.

Até a próxima!