Curso
Neste post, vou mostrar como usar R para coletar as ações listadas na loyal3, buscar dados históricos no Yahoo e, em seguida, executar uma estratégia simples de trading algorítmico. Ao longo do caminho, você vai aprender um pouco de web scraping, a chamar uma API de finanças e a usar um htmlwidget para criar um gráfico interativo de séries temporais.
Para este post, um "trading algo" é definido como um conjunto de regras que disparam um evento de compra ou venda, e não como um modelo preditivo ou uma previsão de séries temporais. Esse é o tipo mais simples de trading algorítmico, mas se você quiser se aprofundar em finanças com R, recomendo o curso da DataCamp sobre modelagem de uma estratégia de trading quantitativa em R.
Contexto
Em 2015, comecei a investir um pouco na loyal3. O serviço deles é diferente e um ótimo lugar para dar os primeiros passos no mundo dos investimentos. Em vez de cobrar do investidor pelas ordens, a loyal3 cobra das empresas para listá-las na plataforma. A premissa é que pessoas que gostam do serviço de uma empresa também comprariam suas ações e, com isso, se tornariam grandes defensores da marca. Para deixar a plataforma ainda mais interessante, você pode comprar frações de ações. Assim, dá para entrar naquela ação da Amazon de $800 investindo só $10, e comprar mais $10 sempre que sobrar um dinheirinho no fim do mês. Claro que há custos de fricção, já que você opera em janelas e sua carteira fica limitada a cerca de 70 ações, mas a loyal3 é uma forma divertida e barata de explorar o universo de renda variável. Dá para colocar dinheiro de verdade no jogo a partir de apenas $10!
Para ficar claro, eu tenho as contas tradicionais de aposentadoria e investimento, mas gosto da interface limpa do app da loyal3 e da ausência de taxas. Acabo checando minha carteira "diversão" da loyal3 com mais frequência do que meus fundos, simplesmente porque é fácil e divertido ver o desempenho das ações que eu mesmo escolhi.
Preparando seu workspace
Para começar, carregue as bibliotecas no seu ambiente. Quase sempre uso rvest para web scraping hoje em dia. Existem outros pacotes que funcionam, como o RSelenium, mas gosto da facilidade de execução do rvest.
O segundo pacote, pbapply, é opcional: ele apenas adiciona uma barra de progresso às funções apply. Como você pode raspar centenas de páginas, uma barra de progresso ajuda a estimar o tempo.
Em seguida, TTR é um pacote que comecei a explorar. Ele é usado para construir "regras técnicas de trading" (Technical Trading Rules). Embora você vá aprender um trading algo simples neste post, o pacote TTR faz cálculos mais sofisticados e vale a pena conhecer.
A biblioteca dygraphs é um wrapper de uma biblioteca JavaScript de gráficos rápida e open source. É um dos htmlwidgets que deixam os gráficos em R mais dinâmicos e incorporados a um arquivo HTML, em vez de uma imagem estática. Por fim, o pacote lubridate é usado para facilitar a manipulação de datas.
library(rvest)
library(pbapply)
library(TTR)
library(dygraphs)
library(lubridate)
Coleta de dados
Todas as ações da loyal3 estão listadas em uma única página. Antes de buscar os preços diários de cada ação para montar seu algoritmo de trading, você precisa coletar todos os tickers disponíveis. Primeiro, declare stock.list como uma string de URL. Depois, use read_html() para que sua sessão do R crie uma sessão de internet e colete todo o HTML da página como um conjunto de nós XML. O CSS da página tem um ID chamado ".company-name". Use isso como parâmetro ao chamar html_nodes() para selecionar apenas os dados XML associados a esse nó. Por fim, use html_text() para coletar os valores de texto dos nomes das empresas.
stock.list<-'https://www.loyal3.com/stocks'
stocks<-read_html(stock.list)
stocks.names<-html_nodes(stocks,'.company-name')
stocks.names<-html_text(stocks.names)
Para ver as ações disponíveis na loyal3, você pode imprimir o objeto stocks.names no console. Ele retorna os nomes das empresas como um vetor de texto.
stocks.names
Para pesquisar os preços, primeiro você precisa do ticker. No site da loyal3, é possível clicar no card da empresa para abrir uma página com o ticker e outras informações.
Usando html_nodes() em stocks, você puxa todos os nós marcados com um “a”. Em HTML, a tag <a> define um hyperlink, usado para ligar uma página a outra. Dentro da tag de link, o atributo “href” se refere ao endereço URL exato. Assim, html_attr() extrai a URL de TODOS os links da página se você passar “href”.
Depois de inspecionar manualmente, encontrei que os links da 54ª à 123ª posição na página representam as páginas das empresas necessárias para raspar o ticker. A última linha usa paste0() para concatenar a URL base ’http://www.loyal3.com` com as páginas específicas de cada empresa, como “/WALMART”. Exemplo: http://www.loyal3.com/WALMART:
loyal.links<-html_nodes(stocks, "a")
loyal.links<-html_attr(loyal.links, "href")
stock.links<-paste0('http://www.loyal3.com',loyal.links[54:123])
Em cada página de empresa há uma descrição, um preço de fechamento recente e o ticker. Todas as páginas seguem o mesmo padrão, então a função personalizada get.ticker() pode ser usada para extrair o ticker.
Dentro da página da empresa, há uma tabela chamada “ticker-price”. A função navega até a página, identifica a tabela, extrai o texto com html_text() e, por fim, usa sub() com a expressão regular ^([[:alpha:]]*).* e \\1 para manter apenas os caracteres alfabéticos. O resultado é que quaisquer caracteres especiais, como $, e números, como o preço de fechamento, são removidos. À medida que a função lê cada uma das 70 páginas, ela coleta apenas o ticker da ação.
get.ticker<-function(url){
x<-read_html(url)
x<-html_node(x,'.ticker-price')
x<-html_text(x)
x<-sub("^([[:alpha:]]*).*", "\\1", x)
return(x)
}
Com a função pronta, use pblapply() para aplicá-la a cada um dos stock.links, que contêm as páginas das empresas. O objeto resultante, stock.tickers, é uma lista de tickers, em que cada elemento corresponde a uma empresa.
stock.tickers<-pblapply(stock.links,get.ticker)
Uma forma de transformar uma lista em um objeto “plano” é com do.call(). Aqui, você aplica rbind para empilhar cada elemento da lista em um único vetor. Por fim, você cria um data frame com o símbolo e o nome da empresa.
stock.ticks<-do.call(rbind,stock.tickers)
stock.ticks<-data.frame(symbol=stock.ticks,name=stocks.names)
Para manter a consistência da análise, talvez você queira limitar a quantidade de histórico coletado de cada ação. A função Sys.Data() vai armazenar um objeto de data como ano, mês e dia. Usar anos como inteiro é uma forma de subtrair um período específico do objeto start.date.
start.date<-Sys.Date()
end.date<-Sys.Date()-years(3)
Para obter dados do Yahoo Finance, o objeto de data precisa ser convertido em caracteres simples, sem hífens. Usar a função de substituição global gsub() em start.date e end.date muda a classe e simultaneamente remove os hífens. Dentro de gsub(), passe o padrão a ser procurado, depois o texto de substituição. Aqui, o padrão de substituição é vazio, entre aspas. O último parâmetro é o objeto ao qual gsub() será aplicado.
start.date<-gsub('-','', start.date)
end.date<-gsub('-','', end.date)
A função getYahooData() do TTR aceita um símbolo e uma data inicial e final. Ela retorna um data frame com informações de séries temporais. Cada linha é uma data e as colunas trazem dados como preço de abertura (Open), máxima (High), mínima (Low) e fechamento (Close) do papel. Como você vai consultar várias empresas, use lapply() ou pblapply(). Passe o vetor de símbolos, depois a função getYahooData() e as datas. Os objetos de date são reciclados a cada chamada de getYahooData() para cada símbolo.
stocks.ts<-pblapply(stock.ticks$symbol,getYahooData,end.date, start.date)
Para facilitar a navegação na lista retornada, stocks.ts, você pode nomear os elementos. Usando names no objeto stocks.ts, defina os nomes como o vetor original $symbol.
names(stocks.ts)<-stock.ticks$symbol
Ao trabalhar com listas grandes, gosto de inspecionar o objeto resultante para garantir que o resultado faz sentido. Agora que os elementos têm nomes, você pode referenciá-los diretamente. Neste exemplo, vamos ver as 6 primeiras linhas de AMC Entertainment Holdings (AMC). Usar head() na lista referenciando $AMC retorna um recorte da série temporal dessa ação:
head(stocks.ts$AMC)
Analisando os dados das ações
Quando ouço comentaristas de finanças, eles frequentemente se referem a gráficos. Apesar do high frequency trading e da gestão ativa feita por outros, muitos investidores pessoa física ainda olham gráficos para obter insights. O objeto de série temporal pode ser rapidamente exibido usando plot. Passe a lista referenciando o elemento nomeado, como $AMC, e depois a coluna que você quer exibir, aqui $Close.
plot(stocks.ts$AMZN$Close)

O gráfico acima é estático e pouco interessante.
Vamos usar uma biblioteca JavaScript para criar um gráfico explorável. No trecho abaixo, você vai notar o operador “%>%” ou pipe. O pipe é uma ótima forma de escrever código conciso. Ele encaminha um objeto para a próxima função sem forçar você a reescrever o nome do objeto, como fizemos antes.
Neste exemplo, você cria um dygraph referenciando a ação do Twitter, $TWTR, e depois a coluna a ser plotada, $Close. Dentro de dygraph, main adiciona um título. Usando o “%>%”, todo esse objeto é encaminhado para a próxima função, dyRangeSelector(). Você pode definir um intervalo de datas padrão usando c() com uma data inicial e final como strings. O objeto HTML resultante é uma série temporal dinâmica da ação do Twitter com um controle deslizante de datas na parte inferior.
Lembre-se: para trocar a ação exibida, altere o ticker na lista stocks.ts e o título do gráfico.
dygraph(stocks.ts$TWTR$Close, main = "TWTR Stock Price") %>%
dyRangeSelector(dateWindow = c("2013-12-18", "2016-12-30"))
Uma estratégia simples: seguir a tendência
Traders de alta frequência e hedge funds usam modelos sofisticados e abordagens baseadas em regras para executar ordens. Se quiser aprender mais, recomendo visitar www.quantopian.com para abordagens avançadas. Para caminhos mais simples, comece por esta página em www.Investopedia.com.
No código abaixo, você vai visualizar uma estratégia simples de momentum. Basicamente, queremos calcular as médias móveis de 200 e 50 dias do preço de uma ação. Em qualquer dia em que a média de 50 dias estiver acima da média de 200 dias, você compraria ou manteria a posição. Nos dias em que a média de 200 dias estiver acima da de 50 dias, você venderia. Essa estratégia é chamada de seguir a tendência (trend following). A relação positiva ou negativa entre as duas médias móveis no tempo representa o momentum da ação.
O pacote TTR fornece SMA() para calcular a média móvel simples. No trecho abaixo, examinamos os primeiros 6 valores das médias móveis de 200 e 50 dias do Twitter. SMA() funciona recebendo a série temporal do papel e uma coluna específica, como Close. É um vetor de preços de fechamento da TWTR. O segundo parâmetro é um inteiro que representa o número de observações da média móvel. Sem usar head(), SMA() retorna todos os valores.
head(SMA(stocks.ts$TWTR$Close, 200))
head(SMA(stocks.ts$TWTR$Close, 50))
Agora que você examinou a função de média móvel em detalhe, precisamos aplicá-la às 70 ações. stocks.ts é uma lista com 70 data frames, cada um com dados de uma ação. A quarta coluna de cada data frame contém o preço de fechamento que vamos usar nas médias.
A função personalizada mov.avgs() recebe um data frame de uma ação para calcular as médias móveis. A primeira linha seleciona os preços de fechamento, indexando [,4] para criar stock.close. Em seguida, a função usa ifelse para checar o número de linhas do data frame. Especificamente, se nrow for menor que (2*260), a função cria um data frame de médias móveis com “NA”.
Escolhi esse número porque há cerca de 250 pregões por ano, então isso verifica se a série tem cerca de 2 anos ou mais. Às vezes a loyal3 tem acesso a IPOs e, se a ação for recente, não haverá dados suficientes para a média de 200 dias. Porém, se nrow for maior que 2*260, a função cria um data frame com os dados originais, além das médias móveis de 200 e 50 dias como novas colunas. Usando colnames, defino os nomes das colunas. A última parte usa complete.cases para checar valores na coluna da média de 200 dias. Linhas sem valor são removidas do resultado final.
mov.avgs<-function(stock.df){
stock.close<-stock.df[,4]
ifelse((nrow(stock.df)<(2*260)),
x<-data.frame(stock.df, 'NA', 'NA'),
x<-data.frame(stock.df, SMA(stock.close, 200), SMA(stock.close, 50)))
colnames(x)<-c(names(stock.df), 'sma_200','sma_50')
x<-x[complete.cases(x$sma_200),]
return(x)
}
Com a função mov.avgs() em mãos, use pblapply() para adicionar os cálculos de médias móveis a cada um dos 70 data frames.
stocks.ts<-pblapply(stocks.ts, mov.avgs)
Use o código abaixo para visualizar as médias móveis de uma ação em um dygraph. Mais uma vez, o código usa o operador “%>%” para encadear funções. A função dygraph() recebe o data frame stocks.ts$FOX. Especificamente, o data frame é indexado por nome de coluna com c('sma_200','sma_50'). Esse objeto é passado para dySeries() nas 2 linhas seguintes. Você pode referenciar colunas pelo nome, então cada dySeries() plota uma linha para “sma_50” e “sma_200” nas linhas 2 e 3. O objeto é encaminhado novamente a dyRangeSelector() para ajustar a altura do seletor. Por fim, adicionei sombreamento para marcar períodos em que você compraria/manteria a posição e períodos em que deveria vender ou ficar de fora, dependendo da sua posição.
dygraph(stocks.ts$FOX[,c('sma_200','sma_50')],main = 'FOX Moving Averages') %>%
dySeries('sma_50', label = 'sma 50') %>%
dySeries('sma_200', label = 'sma 200') %>%
dyRangeSelector(height = 30) %>%
dyShading(from = '2016-4-28', to = '2016-7-27', color = '#CCEBD6') %>%
dyShading(from = '2016-7-28', to = '2016-12-30', color = '#FFE6E6')
Este é o resultado final em uma série temporal interativa.
Conclusão
Como um trader algorítmico iniciante, você não precisa plotar todas as 70 ações. Em vez disso, execute o código diariamente e adicione uma forma programática de identificar as ações que se encaixam no método baseado em regras: “compre se a média móvel de 50 dias estiver acima da média de 200 dias”. Ao revisar o gráfico anterior, a faixa verde é um período em que você compraria a ação de FOX. A faixa vermelha representa o momento de vender e não reentrar.
Como o gráfico é interativo, você pode usar o slider para ajustar a janela visualizada. Com base nesse algo simples, talvez agora seja um bom momento para comprar FOX! Em 30 de dezembro de 2016, a média móvel de 50 dias ficou $0,01 acima da média de 200 dias!
Claro, lembre-se: todo investimento pode perder valor. Para aprender mais sobre finanças e trading algorítmico, confira os cursos da DataCamp aqui.
Dê uma olhada também no nosso tutorial Basic Programming Skills in R.
