Desde a primeira vez que escrevi a primeira parte deste tutorial de criação de um bot para comprar e vender Bitcoins e outras criptomoedas usando a API da Binance, a valorização da moeda tem sofrido uma grande correção, o que permite a novos entrantes aproveitarem a próxima onda de valorização.
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. Agora faremos a codificação do nosso bot para que ele consiga comprar e vender criptomoedas conforme a sua estratégia de trading.
Vamos lá!
#1 – Estratégia de Trade
Existem infinitas estratégias de trading possíveis mas todas elas, de uma forma ou de outra, buscam comprar barato para vender caro. Assim, a estratégia mais simples que você pode colocar no seu robô é você definir um gatilho de preço para comprar e outro para vender, já considerando um lucro em cima do preço que pagou. Esses testes, que chamei de gatilhos, devem ser posicionados dentro da função disparada pelo evento onmessage do nosso objeto ws, assim, será testado toda vez que uma nova cotação chegar até nosso robô, o que acontece a cada segundo.
Abaixo um exemplo de teste de preço para compra e venda:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
ws.onmessage = (event) => { console.clear(); const obj = JSON.parse(event.data); console.log(`Symbol: ${obj.s}`); console.log(`Ask Price: ${obj.a}`); const currentPrice = parseFloat(obj.a); if (currentPrice < 16000) { console.log("Bom para comprar") } else if (currentPrice >= 21000) { console.log("Bom para vender") } else console.log("Esperando..."); } |
Repare que eu tive de converter o obj.a para um número com casas decimais (float) antes de poder fazer a comparação nos ifs e que comparei se o preço estava bom para comprar, vender ou se não estava bom na verdade. Aqui coloquei dois valores arbitrários, não se apegue a eles mas sim à sua análise do mercado para definir o melhor momento de entrar em uma posição naquele par de moedas. Já a saída você pode facilmente automatizar a lógica usando a variável PROFITABILITY que deixamos no .env em conjunto com o preço que compramos a moeda, como abaixo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
let sellPrice = 0; const profitability = parseFloat(process.env.PROFITABILITY); ws.onmessage = (event) => { console.clear(); const obj = JSON.parse(event.data); console.log(`Symbol: ${obj.s}`); console.log(`Ask Price: ${obj.a}`); const currentPrice = parseFloat(obj.a); if (sellPrice === 0 && currentPrice < 16000) { console.log("Bom para comprar"); sellPrice = currentPrice * profitability; } else if (sellPrice !== 0 && currentPrice >= sellPrice) { console.log("Bom para vender"); sellPrice = 0; } else console.log("Esperando..."); } |
Repare que usei uma variável de controle sellPrice (preço de venda) fora do evento onmessage para que ela não seja inicializada a cada vez que o evento for disparado. Essa variável, inicialmente em 0, será usada nos testes para ver se estamos aguardando uma compra (sellPrice === 0) ou se estamos aguardando uma venda (sellPrice !== 0). Uma vez que tenhamos comprado, eu pego o preço atual e multiplico ele pelo coeficiente de lucratividade que desejo (PROFITABILITY), configurado no .env do nosso robô e carregado com o process.env.
Assim, uma vez que eu tenha um sellPrice definido para vender o ativo que comprei, posso compará-lo com o preço atual a cada atualização do mercado e assim que ele estiver de acordo, a venda será realizada.
Inicialmente coloquei apenas marcações de compra e venda, mas a seguir vamos fazê-las de fato.
#2 – Compra e Venda
Para comprar e vender nós precisaremos usar as APIs da Binance.
Diferente das streams, as APIs são para que possamos enviar comandos para a exchange ou, em alguns casos, pegar dados muito específicos que não vem até nós automaticamente. Dada a nossa necessidade atual, nós vamos usar a api de orders para posicionar novas ordens de compra e de venda. A parte boa é que uma vez que criarmos uma função de envio de ordem, ela servirá para qualquer tipo de ordem que venhamos a querer submeter no futuro.
O primeiro passo para criação da nossa funcionalidade de submissão de ordens é você revisar o seu .env para se certificar que tanto as URLs quanto as chaves estão devidamente configuradas. Enquanto as streams são públicas, a API de order exige a apresentação de credenciais para ser usada, por isso essa preocupação com estas informações.
Para fazer as chamadas à API usaremos o pacote Axios, que é muito popular para JavaScript. Caso nunca tenha ouvido falar, recomendo o vídeo abaixo.
Este pacote já foi instalado na parte 1 do tutorial, então nesta segunda parte usaremos ele e outro pacote chamado crypto, sendo que este segundo é nativo do Node.js e não precisa de instalação. Dito isso, vamos começar carregando estes dois pacotes e declarando a estrutura básica da função newOrder dentro do próprio index.js, ao final do mesmo.
1 2 3 4 5 6 7 8 |
const axios = require('axios'); const crypto = require('crypto'); async function newOrder(quantity, side) { } |
Todos os códigos que faremos a seguir vão estar dentro da função newOrder que será responsável por configurar e submeter a ordem para a Binance conforme os parâmetros que ela receber e as regras gerais de submissão.
O primeiro passo é montar a querystring com os parâmetros da nossa ordem. Uma querystring é uma string em formato de consulta web, ou dando um exemplo concreto: ?variavel1=valor1&variavel2=valor2. Certamente você já viu isso nas barras de endereço do navegador por aí, é um formato muito popular para passar parâmetros na Internet.
Para chegar nessa querystring precisamos primeiro montar um objeto JavaScript, preencher suas propriedades e assiná-l0 criptograficamente, sendo esta última etapa uma exigência da Binance. Abaixo um exemplo de configuração e assinatura criptográfica dentro do padrão HMAC-SHA256 (padrão Binance).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
const data = { symbol: process.env.SYMBOL, side, type: 'MARKET', quantity, timestamp: Date.now(), recvWindow: 60000//máximo permitido, default 5000 }; const signature = crypto .createHmac('sha256', process.env.SECRET_KEY) .update(`${new URLSearchParams(data)}`) .digest('hex'); const newData = { ...data, signature }; const qs = `?${new URLSearchParams(newData)}`; |
Na primeira linha nós carregamos os dados iniciais do objeto, pegando o SYMBOL (par de moedas) do .env, o side que foi recebido como parâmetro na função (BUY ou SELL), o tipo de ordem (MARKET por padrão), a quantidade que vai ser negociada (em string, para não termos problemas de precisão), a hora da sua máquina e a janela de tolerância para execução de ordens. (recvWindow). Essas são as informações mínimas necessárias para submeter uma ordem.
Depois, iniciamos o processo de geração da assinatura criptográfica onde usando o pacote crypto definimos o algoritmo como sha256 e a chave como sendo o nosso SECRET_KEY (que configuramos no .env). Os dados que precisam ser assinados são os do objeto data, no formato de querystring, motivo pelo qual usamos o URLSearchparams. Por fim, converto o resultado que é originalmente binário para uma string hexadecimal (hex).
Com os dados da ordem mais a assinatura criptográfica, podemos montar a querystring final que será enviada na requisição, via Axios, como no código abaixo.
1 2 3 4 5 6 7 8 9 10 11 12 |
try { const result = await axios({ method: 'POST', url: `${process.env.API_URL}/v3/order${qs}`, headers: { 'X-MBX-APIKEY': process.env.API_KEY } }); console.log(result.data); } catch (err) { console.error(err); } |
Para quem conhece o mínimo de HTTP a chamada do Axios é bem autoexplicativa. Definimos o method como POST, a URL é montada com a API_URL )do .env) mais o endpoint de submissão de ordens e a querystring (qs) que montamos antes. Nos headers precisamos também adicionar o X-MBX-APIKEY informando a nossa API_KEY do .env. Dessa forma, quando a requisição chegar na Binance ela vai saber de qual cliente é, indo verificar na assinatura se realmente foi enviada por aquele cliente.
E com isso nós temos pronta a função de envio de ordens. Agora podemos voltar à função que processa as estratégias e chamar esta nova função sempre que for necessário comprar ou vender, como abaixo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
ws.onmessage = (event) => { console.clear(); const obj = JSON.parse(event.data); console.log(`Symbol: ${obj.s}`); console.log(`Ask Price: ${obj.a}`); const currentPrice = parseFloat(obj.a); if (sellPrice === 0 && currentPrice < 16000) { newOrder("0.01", "BUY"); sellPrice = currentPrice * profitability; } else if (sellPrice !== 0 && currentPrice >= sellPrice) { newOrder("0.01", "SELL"); sellPrice = 0; } else console.log("Waiting..."); } |
Se você colocar para rodar agora (ajustando o preço de compra de acordo), assim que a condição for verdadeira você verá no console a ordem sendo executada, como abaixo.
Agora é só esperar o mercado voltar a subir que quando o sell price for atingido, o bot vai fazer a venda automaticamente para você.
E com isso finalizamos a segunda parte do nosso tutorial. No guia de estudos de cripto e blockchain você encontra muitos outros tutoriais bacanas de programação ligados a este universo, incluindo esse aqui sobre RSI!
Curtiu o post? Que tal aprender a construir um robô multi-moedas profissional para a maior exchange do mundo? Clique no banner abaixo e saiba mais!
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.