Esta é a segunda parte de uma série de 2 artigos onde ensino como programar um robô trader para a corretora Mercado Bitcoin usando Node.js. Na primeira parte foquei nos conceitos fundamentais, criação e configuração da conta na exchange, criação do projeto e codificação do algoritmo de monitoramento do mercado, você confere ela aqui. Agora faremos a codificação do nosso bot para que ele consiga comprar e vender criptomoedas.
Atenção: ganhos passados não representam garantia de ganhos futuros. Criptomoedas são investimentos de risco e portanto você pode perder muito dinheiro mesmo acreditando que sabe operar neste mercado. Eu NÃO SOU responsável e jamais serei pelas suas perdas, eu apenas ensino a criar programas de computador, não aconselho investimentos, ok?
#1 – Autenticando-se no Servidor
Abra novamente o seu projeto (sugiro estar utilizando o Visual Studio Code) e vá até o seu arquivo index.js, que é nosso módulo que guardará a lógica do nosso robô. Logo no início do arquivo index.js, vamos modificar o original para adicionar dois novos módulos que serão necessários, o crypto e o axios:
1 2 3 4 |
const axios = require('axios'); let accessToken = ""; |
O pacotes axios serve para acessar as APIs da corretora, algo que iremos fazer nesta etapa. Já a variável accessToken servirá para armazenar o token de acesso que devemos obter antes de qualquer chamada às APIs privadas. Isso porque as APIs privadas/autenticadas da MB exigem que você passe um access token no cabeçalho Authorization das requests. Assim, a nossa primeira tarefa é conseguir esse acesse token antes de prosseguir.
Volte ao index.js e crie uma nova função, que será responsável pela request de login.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
async function login() { const url = `https://api.mercadobitcoin.net/api/v4/authorize/`; const body = { login: process.env.API_KEY, password: process.env.API_SECRET }; const { data } = await axios.post(url, body); accessToken = data.access_token; console.log("Acesso autorizado!"); setTimeout(login, (data.expiration * 1000) - Date.now()); } |
Começamos definindo a URL que vamos precisar enviar a requisição, conforme documentação oficial da API. Depois, montamos o body da requisição com login e password sendo a API_KEY e a API_SECRET definidos no .env, respectivamente. Note que ensinei como obter esse par de credenciais na etapa de configuração da parte 1 deste tutorial, volte lá caso não se recorde. Por fim, enviamos a requisição POST usando o Axios.
Para entender melhor como requests HTTP funcionam, recomendo este artigo. Já para entender como o Axios funciona, recomendo o vídeo abaixo.
O retorno da request do Axios é um HTTP Response cuja propriedade data vai vir com dois campos: access_token, que guardaremos na variável adequada, e expires, que usaremos para criar um timer a fim de fazer novo login no futuro, quando nosso token vier a expirar. Via de regra, só precisamos ter um token de acesso quando a gente for enviar uma ordem, mas pensando em performance, é uma boa já termos isso antes.
Agora chame essa função de login antes da conexão de websockets.
1 2 3 4 5 6 7 |
//carregamentos de variáveis login(); //conexão de websocket |
Execute o bot com npm start e se ele imprimir que recebeu autorização, quer dizer que seu access token está seguro na memória dele e poderia ser usado agora para fazer requests em APIs autenticadas da MB.
#2 – Obtendo Account ID
Outra credencial de acesso que você vai precisar é o seu account id. Infelizmente eu não encontrei uma forma de obtê-lo no painel da MB, então teremos de fazer isso via API. Uma vez que você já tenha o access token em mãos, pode fazer uma chamada para a API de listagem de contas conforme documentação oficial. Abaixo um exemplo de função que faz isso:
1 2 3 4 5 6 7 8 9 10 |
async function getAccountId() { const url = `https://api.mercadobitcoin.net/api/v4/accounts/`; const headers = { Authorization: "Bearer " + accessToken }; const { data } = await axios.get(url, { headers }); console.log(data); process.exit(0); } |
Começamos montando a URL necessária, seguido da inclusão do access token como bearer no cabeçalho authorization, uma exigência presente em todas APIs privadas da MB. Depois fazemos o GET com as configurações supracitadas e pegamos o retorno para imprimir no console, saindo da aplicação em seguida (process.exit).
Esta função somente pode ser chamada após o login, pois depende do access token estar na memória. Assim, recomendo a seguinte estrutura para sua execução.
1 2 3 4 |
login() .then(result => getAccountId()) |
Você só vai precisar executar com esta estrutura de funções encadeadas uma única vez, aí quando imprimir seu account id no console, copie ele e cole no .env, na variável de ambiente ACCOUNT_ID. Depois, pode remover esse then do login pois não será necessário.
#3 – Comprando e Vendendo
A próxima e última etapa é criar a função que envia as ordens para a MB, conforme documentação oficial. Adicione esta função no index.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
async function newOrder(qty, side = "buy") { const url = `https://api.mercadobitcoin.net/api/v4/accounts/${process.env.ACCOUNT_ID}/${process.env.SYMBOL}/orders`; const body = { qty, side, type: "market" } const headers = { Authorization: "Bearer " + accessToken }; try { const { data } = await axios.post(url, body, { headers }); if (side === "sell") sellPrice = 0; return data; } catch (err) { console.error(err.response ? err.response.data : err.message); process.exit(0); } } |
Neste código eu inicializo uma URL para nossa request, sendo que além de uma base fixa, ela precisa do ACCOUNT_ID e do SYMBOL, que devem estar preenchidos no seu .env. Além dessa URL eu preciso montar o body da request, informando a quantidade a ser negociada, o lado (side) da operação (buy ou sell) e o tipo de ordem, que neste caso será a mercado (existem várias opções mais avançadas). Já o nosso header é um velho conhecido: authorization, passando o token de acesso como Bearer.
Com nossa request configurada, é hora de enviarmos ela com o axios.post e pegar seu retorno. Recomendo imprimir esse retorno e dar uma estudada nele depois, para ver o que dá pra fazer além do que estou mostrando aqui. Por enquanto, apenas acho interessante resetar a variável sellPrice se foi uma venda bem sucedida, para que o robô volte a monitorar os preços e possivelmente compre novamente no futuro.
Repare um ponto importante aqui que é o uso de try/catch. Isso porque qualquer erro que aconteça na chamada da API da MB causará o estouro de uma exceção que deve ser capturada e impressa no console para você investigar. Existem diversos erros possíveis de acontecer e irei listar alguns ao final deste artigo, para lhe ajudar nas investigações.
Agora com esta função pronta, basta ajustar o código de condições para incluir as chamadas à ela, passando os parâmetros mais adequados (o código abaixo é no onmessage do websocket).
1 2 3 4 5 6 7 8 9 |
if (!sellPrice && parseFloat(obj.data.sell) <= BUY_PRICE) { sellPrice = parseFloat(obj.data.sell) * PROFITABILITY; newOrder(BUY_QTY, "buy"); } else if (sellPrice && parseFloat(obj.data.buy) >= sellPrice) { newOrder(BUY_QTY, "sell"); } |
Eu não mexi nas lógicas dos ifs, apenas mudei onde antes imprimíamos apenas um compra ou venda “fake” para a execução de fato de tais ordens junto à corretora.
Para fins de teste, eu recomendo que você olhe o preço de BTC em BRL atualmente e coloque um BUY_PRICE que esteja válido, para que seu robô compre assim que for inicializado e você possa testá-lo de fato. Repare que você precisa ter saldo em conta para isso, então faça um depósito antes de colocar seu robô para rodar.
Já para os testes de venda, recomendo que ajuste seu .env com um PROFITABILITY bem baixo, tipo 0.5% ou 1%, e um preço de compra alto, para que ele compre assim que for inicializado e venda poucas horas depois, mesmo que com pouco ou nenhum lucro, apenas para fins de que consiga testar todas funcionalidades do robô antes de começar a colocar grana “de verdade”.
#4 – Erros Comuns
Existem uma série de coisas que podem dar errado quando o assunto é trades via bot. A maioria dos erros são de programação mesmo, código escrito errado ou lógica que não faz sentido. Mas tem alguns erros que dizem respeito a comunicação com a API da corretora, a construção das requests em si, e é sobre esses que quero falar nesta seção. Separei abaixo alguns erros que eu tive durante o desenvolvimento deste robô e que acho que podem acontecer com você também.
API|ROUTE_NOT_FOUND ou This route not found
Esse é de longe o erro mais comum e ele indica que você montou a URL de sua requisição errada. Revise letra a letra e no caso de URLs construídas com base em variáveis, como no caso do envio de ordens, revise se as variáveis estão corretamente preenchidas e sendo carregadas para memória.
TRADING|PLACE_ORDER|INVALID_PAIR ou The params {base} or {quote} are invalid
Esse erro acontece quando você informa um par de moedas inválido na submissão de ordens. Lembrando que o formato deve ser BTC-BRL, ou seja, primeiro a cripto, seguido de um hífen e depois a moeda fiduciária. Nunca o contrário e sempre em maiúsculo. Certifique-se também de usar apenas pares existentes na Mercado Bitcoin.
TRADING|PLACE_ORDER|API_GENERIC_ERROR ou An unexpected error has occurred
Esse é o erro mais comum e o mais difícil de rastrear o motivo pois ele pode ser por QUALQUER motivo. Os motivos pelos quais esse erro aconteceu comigo foi porque passei parâmetros incompatíveis na ordem (misturei preço com ordem a mercado, por exemplo) e também porque estava sem saldo na minha carteira para efetuar a compra.
Caso você encontre outros erros não listados aqui, consulte a documentação oficial.
Espero que tenham gostado deste tutorial e tendo qualquer dúvida, deixe nos comentários!
Quer mais algumas dicas, confira o vídeo abaixo.
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.