Com o pacote rvest você pode baixar todo seu histórico de compra e venda de ações diretamente do CEI e passar a gerenciar sua carteira pelo R
No post anterior em B3 + rvest: Explorando dados financeiros com R - parte 1 começamos a explorar, com a ajuda do rvest, alguns dados superficiais sobre a bolsa de valores brasileira. Nesse post vamos explorar o CEI - Canal Eletrônico do Investidor, também da B3, e baixar todo o histórico de compra e venda de ações.
Como objetivo a ser alcançado, vamos tentar baixar todo o histórico de compra e venda e montar uma tabela como ponto de partida para uma gestão da própria carteira no R.
Se você investe em renda variável você certamente o faz por meio de uma corretora. A [B]³ é a instituição que centraliza toda a gestão das informações e dos contratos, e o CEI é onde o investidor pode ter acesso aos dados direto na fonte.
Dessa vez o ponto de partida da nossa fonte de dados está protegido por usuário e senha no site do CEI em https://cei.b3.com.br/CEI_Responsivo/. Como havia dito, todo investidor pode ter conta no CEI, aqui eles explicam como fazer isso.
Infelizmente não localizei nenhuma forma rápida de baixar todos os seus próprios dados em formato aberto, csv, json, xml ou algo do tipo. Sendo assim, mais uma vez vamos raspar os dados para o R. O desafio da vez é raspar dados de um site com controle de acesso (usuário e senha). Felizmente o rvest torna essa tarefa um pouco mais fácil.
Vamos usar os seguintes pacotes:
library(rvest) # para nossa raspagem
library(tidyverse) # para tudo
library(glue) # para grudar nossas strings
library(keyring)
Além dos pacotes clássicos, destaco o pacote keyring excelente pacote que ajuda a não ficar escrevendo seu usuário e senha em texto aberto no R. Veremos a seguir.
Mesmo fazendo scraping via R vamos precisar informar nosso usuário (CPF) e senha para acessar o CEI. Considerando que vamos acessar os próprios dados para gerir a própria carteira o pacote keyring atende muito bem. Ao acessar o site https://cei.b3.com.br nos deparamos com a seguinte tela:
Comecemos a preparar nosso scraping a partir desta tela. Diferente do post anterior, não vamos começar baixando os dados da tela e sim abrindo uma sessão para simular navegação na parte restrita do site e só depois baixar os dados da tela.
Vamos usar o rvest para simular uma sessão (controle de acesso com autenticação) do browser. Para isso vamos precisar preencher, via R, o usuário e senha do formulário e enviar uma requisição para que os servidores da B3 nos autentiquem e nos mande de volta um cookie. Leia mais sobre sessão, autenticação e cookies em: https://pt.wikipedia.org/wiki/Cookie_(inform%C3%A1tica), https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Cookies ou http://material.curso-r.com/scrape/#sess%C3%B5es-e-cookies
Primeiro criamos uma sessão.
url <- 'https://cei.b3.com.br'
sessao <- html_session(url)
Por ser uma página de usuário e senha, temos um formulário para preenchermos. Com o html_form()
podemos pegar os formulários disponíveis na página da sessão que abrimos.
(form_login <- html_form(sessao))
[[1]]
<form> 'aspnetForm' (POST login.aspx)
<input hidden> '__VIEWSTATE': /wEPDwUKMjEwNTg1MDYwNmRkwOWN09etib3cEcYwkEwmQuk4eGE=
<input hidden> '__VIEWSTATEGENERATOR': 803C878C
<input hidden> '__EVENTVALIDATION': /wEWBAKE+eKZBgKezL2tCQKt6MueDQKW9Z+RAXuXwk35k3s3/1HrG9WTDCFP3/l4
<input text> 'ctl00$ContentPlaceHolder1$txtLogin':
<input password> 'ctl00$ContentPlaceHolder1$txtSenha':
<input submit> 'ctl00$ContentPlaceHolder1$btnLogar': Entrar
O retorno é uma lista com o formulário de login. Reparem nos nomes dos campos disponíveis para preenchimento. Temos um input text
para preencher o usuário (CPF) e um input password
para preencher a senha. Respetivamente temos ctl00$ContentPlaceHolder1$txtLogin
e ctl00$ContentPlaceHolder1$txtSenha
para preencher.
Nesse ponto que usaremos o keyring para não deixar explícito no seu código sua senha de acesso ao CEI. Ele vai ajudar a ocultar suas senhas e usá-las de modo mais seguro.
Primeiro crie um novo “perfil” no keyring para ele armazenar sua senha de forma criptografada. Vamos chamar de cei-b3. Como o keyring vai criptografar suas senhas, você precisa definir uma senha mestra para descriptografar quando quiser. Ele vai pedir uma nova senha mestra para o perfil cei-b3. Não é a senha do CEI ainda! A partir de agora, todas as senhas que você armazenar no perfil cei-b3 você vai precisar da senha mestra que escolher nesse ponto.
# definindo uma senha mestra para armazenamento criptografado
keyring_create('cei-b3')
Em seguida defina uma entrada para a senha do seu usuário. Você pode definir a entrada que quiser. Vamos definir a entrada senha e nosso username será o próprio CPF. No lugar de 12345678900 coloque seu CPF de verdade. Agora sim o keyring vai solicitar a senha que você vai usar no CEI.
key_set('senha', username = '12345678900', keyring = 'cei-b3')
Pronto, você definiu uma senha mestra e definiu a senha do CEI para o seu CPF. Agora vamos retornar ao formulário do CEI e usar a senha de forma mais segura. Basta chamar da seguinte forma:
form_preenchido <- set_values(form_login[[1]],
'ctl00$ContentPlaceHolder1$txtLogin' = '12345678900',
'ctl00$ContentPlaceHolder1$txtSenha' = key_get('senha', '12345678900', 'cei-b3'))
Com o formulário preenchido vamos simular a submissão dos dados na mesma sessão, seria o mesmo que o “click de entrar”. A B3 vai nos autenticar e devolver a página do usuário logado.
submit_form(sessao, form_preenchido)
<session> https://cei.b3.com.br/CEI_Responsivo/home.aspx
Status: 200
Type: text/html; charset=utf-8
Size: 48490
Agora estamos na página inicial da área restrita, pós login, já com a sessão autenticada.
A página com os dados que queremos pegar está no menu Extratos e Informativos > Negociação de Ativos. Repare na URL da página, é ela que iremos usar agora. Como estamos em uma página autenticada com uma sessão, precisamos simular a navegação nesta página sem perder a sessão. Para isso usaremos o jump_to do rvest.
url_dados <- glue('{url}/CEI_Responsivo/negociacao-de-ativos.aspx')
pagina_dados <- jump_to(sessao, url_dados)
E agora temos mais um formulário a ser preenchido. Temos os seguintes campos para submeter: instituição, conta, data início e data fim. Aqui no meu caso a data início e data fim já vieram preenchidos com os períodos disponíveis.
(form_dados <- html_form(pagina_dados))
[[1]]
<form> 'aspnetForm' (POST negociacao-de-ativos.aspx)
<input hidden> 'ctl00_ContentPlaceHolder1_ToolkitScriptManager1_HiddenField':
<input hidden> '__VIEWSTATE': /wEPDwUKLTI2MTA0ODczNg9kFgJmD2QWAgIDD2QWCAIBDw8WAh4EVGV4dAUoU0FVTE8gREUgU09VWkEgR1VFUlJBIEZFUlJFSVJBIERFIENBU1RST2RkAgMPZBYCAgEPFgIeB1Zpc2libGVoZAIFD2QWBAIBDw8WAh8ABRZOZWdvY2lhw6fDo28gZGUgYXRpdm9zZGQCAw9kFgICAw8PFgIfAAUzIC8gRXh0cmF0b3MgZSBJbmZvcm1hdGl2b3MgLyBOZWdvY2lhw6fDo28gZGUgYXRpdm9zZGQCBw9kFgICBQ9kFgJmD2QWCAIDDxBkEBUGCVNlbGVjaW9uZRo4NSAtIEJURyBQQUNUVUFMIENUVk0gUy5BLhgxMDI2IC0gQkFOQ08gQlRHIFBBQ1RVQUwWMTA5OSAtIElOVEVSIERUVk0gTFREQR4zIC0gWFAgSU5WRVNUSU1FTlRPUyBDQ1RWTSBTL0EjMzg2IC0gUklDTyBJTlZFU1RJTUVOVE9TIC0gR1JVUE8gWFAVBgItMQI4NQQxMDI2BDEwOTkBMwMzODYUKwMGZ2dnZ2dnFgFmZAIFDxAPFgIeB0VuYWJsZWRoZBAVAQxTZWxlY2lvbmUuLi4VAQEwFCsDAWcWAWZkAgcPDxYCHwFnZBYEAgUPDxYCHwAFCjE5LzAzLzIwMThkZAIHDw8WAh8ABQowOS8wOS8yMDE5ZGQCCQ9kFggCAQ8PFgIfAAUKMTkvMDMvMjAxOGRkAgMPDxYCHwAFCjA5LzA5LzIwMTlkZAIFDw8WAh8ABQoxOS8wMy8yMDE4ZGQCBw8PFgIfAAUKMDkvMDkvMjAxOWRkZPqigqMb6fRqydjTFyhGIDiRNa0b
<input hidden> '__VIEWSTATEGENERATOR': B345DEBA
<input hidden> '__EVENTVALIDATION': /wEWDALKnLLZBgL5hcn2DgLcmdTJDgLHmeTJDgKA9qOrAgKzu7rVBwLSmZjKDgKW09KdCgLT7OvYBALmr6naDAK4koCdDgK0rs+gCNrac6AAGrj+8chIgEiLkj0AS/Rm
<input hidden> 'ctl00$ContentPlaceHolder1$hdnPDF_EXCEL':
<select> 'ctl00$ContentPlaceHolder1$ddlAgentes' [1/6]
<select> 'ctl00$ContentPlaceHolder1$ddlContas' [1/1]
<input text> 'ctl00$ContentPlaceHolder1$txtDataDeBolsa': 19/03/2018
<input text> 'ctl00$ContentPlaceHolder1$txtDataAteBolsa': 09/09/2019
<input submit> 'ctl00$ContentPlaceHolder1$btnConsultar': Consultar
No meu caso, abri contas em alguns bancos gratuitos de investimentos para experimentar o serviço deles. Como você pode comprar ações por diferentes bancos, e como queremos montar uma base com todas as ações, independente de corretora/banco, vamos percorrer todos as instituições para coletar todas as ações registradas.
instituicoes <- read_html(pagina_dados) %>%
html_nodes('option') %>%
html_text()
#tirando o placeholder 'Selecione...'
instituicoes <- instituicoes[!grepl("Selecione", instituicoes)]
instituicoes
[1] "85 - BTG PACTUAL CTVM S.A."
[2] "1026 - BANCO BTG PACTUAL"
[3] "1099 - INTER DTVM LTDA"
[4] "3 - XP INVESTIMENTOS CCTVM S/A"
[5] "386 - RICO INVESTIMENTOS - GRUPO XP"
Agora basta percorrer cada um dos bancos, passando o código do banco e submetendo a consulta na página para resgatar o resultado de cada página e montar um dataframe. Com um simples loop resolvemos isso.
Durante o loop faremos um controle para baixar os dados apenas das instituições que apresentam a tabela de compra e venda de ações. As instituições que você nunca comprou apresentam a mensagem “Não foram encontrados resultados para esta pesquisa”. Ao detectar a presença dessa frase, vamos assumir que não foi comprada ações nessa instituição.
As datas e o número de conta são preenchidos automaticamente, basta, portanto, enviar o código número que aparece na frente do nome de cada instituição.
Ao acessar a página que de fato apresenta os dados, serão exibidas duas tabelas, vamos raspar apenas as informações da tabela com o gigantesco id ctl00_ContentPlaceHolder1_rptAgenteBolsa_ctl00_rptContaBolsa_ctl00_pnAtivosNegociados. Dela extrairemos a tag
O resultado é o seguinte loop:
dados_totais <- data.frame()
for(i in instituicoes) {
#separando o código do banco do nome
banco <- str_split(i, ' - ', simplify = TRUE)
numero <- banco[1]
nome <- banco[2]
#o formulário espera receber a seleção do código númerico do banco
form_dados_preenchidos <- set_values(form_dados[[1]],
'ctl00$ContentPlaceHolder1$ddlAgentes' = numero)
#submetemos o formulário na mesma sessão logada
resultado <- submit_form(sessao, form_dados_preenchidos)
#caso não tenha ações, a mensagem será exibida
nao_teve_resultado <- resultado %>%
read_html() %>%
html_text() %>%
str_detect('Não foram encontrados resultados para esta pesquisa')
#caso a mensagem não apareça
if(!nao_teve_resultado) {
dados_de_um_banco <- resultado %>%
html_nodes('#ctl00_ContentPlaceHolder1_rptAgenteBolsa_ctl00_rptContaBolsa_ctl00_pnAtivosNegociados') %>%
html_node('table') %>%
html_table() %>%
.[[1]] %>%
.[-nrow(.),] #descartando a última linha da tabela por ser um totalizador
dados_de_um_banco$nome_banco <- nome #adicionado nome do banco no dataframe
#bind dos dados de compra e venda de cada banco formando um dataframe total
dados_totais <- bind_rows(dados_de_um_banco, dados_totais)
}
}
Como estou utilizando meus próprios dados, vou retirar os campos de Valor total e quantidade par questões de segurança. Vamos dar uma olhada no resultado.
dados_totais <- dados_totais %>%
select(-`Valor Total(R$)`, -Quantidade)
glimpse(dados_totais)
Observations: 44
Variables: 9
$ `Data do Negócio` <chr> "06/03/2019", "06/03/2019", "06/03…
$ `Compra/Venda` <chr> "C", "C", "C", "C", "C", "C", "C",…
$ Mercado <chr> "Mercado a Vista", "Mercado a Vist…
$ `Prazo/Vencimento` <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA…
$ `Código Negociação` <chr> "GRND3", "ITUB3", "ABEV3F", "ABEV3…
$ `Especificação do Ativo` <chr> "GRENDENE ON NM", "ITAUUNI…
$ `Preço (R$)` <chr> "8,54", "30,42", "16,54", "16,54",…
$ `Fator de Cotação` <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ nome_banco <chr> "XP INVESTIMENTOS CCTVM S/A", "XP …
head(dados_totais)
Data do Negócio Compra/Venda Mercado Prazo/Vencimento
1 06/03/2019 C Mercado a Vista NA
2 06/03/2019 C Mercado a Vista NA
3 06/03/2019 C Merc. Fracionário NA
4 06/03/2019 C Merc. Fracionário NA
5 06/03/2019 C Merc. Fracionário NA
6 06/03/2019 C Merc. Fracionário NA
Código Negociação Especificação do Ativo Preço (R$)
1 GRND3 GRENDENE ON NM 8,54
2 ITUB3 ITAUUNIBANCOON ED N1 30,42
3 ABEV3F AMBEV S/A ON 16,54
4 ABEV3F AMBEV S/A ON 16,54
5 B3SA3F B3 ON NM 31,74
6 B3SA3F B3 ON NM 31,74
Fator de Cotação nome_banco
1 1 XP INVESTIMENTOS CCTVM S/A
2 1 XP INVESTIMENTOS CCTVM S/A
3 1 XP INVESTIMENTOS CCTVM S/A
4 1 XP INVESTIMENTOS CCTVM S/A
5 1 XP INVESTIMENTOS CCTVM S/A
6 1 XP INVESTIMENTOS CCTVM S/A
tail(dados_totais)
Data do Negócio Compra/Venda Mercado Prazo/Vencimento
39 04/09/2019 C Merc. Fracionário NA
40 04/09/2019 C Merc. Fracionário NA
41 05/09/2019 C Merc. Fracionário NA
42 05/09/2019 C Merc. Fracionário NA
43 06/09/2019 C Mercado a Vista NA
44 06/09/2019 C Mercado a Vista NA
Código Negociação Especificação do Ativo Preço (R$)
39 MDIA3F M.DIASBRANCOON NM 35,50
40 MDIA3F M.DIASBRANCOON NM 35,50
41 PSSA3F PORTO SEGUROON NM 55,70
42 PSSA3F PORTO SEGUROON NM 55,70
43 ALZR11 FII ALIANZA CI 106,20
44 GGRC11 FII GGRCOVEPCI ER 141,38
Fator de Cotação nome_banco
39 1 BANCO BTG PACTUAL
40 1 BANCO BTG PACTUAL
41 1 BANCO BTG PACTUAL
42 1 BANCO BTG PACTUAL
43 1 BANCO BTG PACTUAL
44 1 BANCO BTG PACTUAL
Utilizaremos este dataframe final para os próximos posts. Para facilitar nossa vida depois, vamos estruturar uma função. Futuramente, caso os próximos resultados sejam interessantes podemos inclusive estruturar um pacote para facilitar a gestão/análise da sua carteira no R.
consulta_carteira <- function(cpf, senha) {
url <- 'https://cei.b3.com.br'
sessao <- html_session(url)
form_login <- html_form(sessao)
form_preenchido <- set_values(form_login[[1]],
'ctl00$ContentPlaceHolder1$txtLogin' = cpf,
'ctl00$ContentPlaceHolder1$txtSenha' = senha)
submit_form(sessao, form_preenchido)
url_dados <- glue('{url}/CEI_Responsivo/negociacao-de-ativos.aspx')
pagina_dados <- jump_to(sessao, url_dados)
instituicoes <- read_html(pagina_dados) %>%
html_nodes('option') %>%
html_text()
instituicoes <- instituicoes[!grepl("Selecione", instituicoes)]
form_dados <- html_form(pagina_dados)
dados_totais <- data.frame()
for(i in instituicoes) {
banco <- str_split(i, ' - ', simplify = TRUE)
numero <- banco[1]
nome <- banco[2]
form_dados_preenchidos <- set_values(form_dados[[1]],
'ctl00$ContentPlaceHolder1$ddlAgentes' = numero)
resultado <- submit_form(sessao, form_dados_preenchidos)
nao_teve_resultado <- resultado %>%
read_html() %>%
html_text() %>%
str_detect('Não foram encontrados resultados para esta pesquisa')
if(!nao_teve_resultado) {
dados_de_um_banco <- resultado %>%
html_nodes('#ctl00_ContentPlaceHolder1_rptAgenteBolsa_ctl00_rptContaBolsa_ctl00_pnAtivosNegociados') %>%
html_node('table') %>%
html_table() %>%
.[[1]] %>%
.[-nrow(.),]
dados_de_um_banco$nome_banco <- nome
dados_totais <- bind_rows(dados_de_um_banco, dados_totais)
}
}
return(dados_totais)
}
E chamamos nossa função assim
senha <- keyring::key_get('senha', 'SEU_CPF', 'cei-b3')
final <- consulta_carteira('SEU_CPF', senha)
Temos um dataframe com todo nosso histórico de compra e venda de ações de todas as corretoras utilizadas. Com este resultado poderemos partir para a gestão da carteira, avaliar os rendimentos, gestão de risco e outros controles detalhados dos seus investimentos em renda variável.
No próximo post deste assunto partiremos deste resultado para fazer algumas análises mais interessantes!
For attribution, please cite this work as
Guerra (2019, Sept. 10). Fulljoin: CEI + rvest: Explorando dados financeiros com R - parte 2. Retrieved from https://www.fulljoin.com.br/posts/2019-09-03-b3-rvest-cei/
BibTeX citation
@misc{guerra2019cei, author = {Guerra, Saulo}, title = {Fulljoin: CEI + rvest: Explorando dados financeiros com R - parte 2}, url = {https://www.fulljoin.com.br/posts/2019-09-03-b3-rvest-cei/}, year = {2019} }