La ruta estándar para conectarse a la API de Binance con Node.js es: generar una firma HMAC-SHA256 utilizando el módulo nativo crypto junto con axios o undici para enviar peticiones, o utilizar directamente librerías de la comunidad como node-binance-api o binance-api-node. Este artículo proporciona código completo para el encapsulamiento nativo, comparación de SDKs populares, suscripciones WebSocket y tipos de TypeScript, todo listo para ser ejecutado con npm install. Si aún no tienes una cuenta de Binance, puedes completar el registro en el sitio oficial de Binance; los nuevos usuarios pueden abrir una cuenta mediante el registro gratuito.
I. Tabla comparativa de SDKs para Node.js
| Nombre de la librería | Autor | Descargas semanales | Características | Escenario recomendado |
|---|---|---|---|---|
| node-binance-api | jaggedsoft | 30k+ | Funcionalidades completas, estilo callback | Validación rápida de scripts |
| binance-api-node | Ashlar | 50k+ | Basado en Promises, amigable con TypeScript | Entornos de producción con TypeScript |
| @binance/connector | Oficial | 40k+ | Encapsulamiento ligero y delgado | Seguir de cerca las actualizaciones de la API |
| ccxt | Comunidad | 80k+ | Unificado para múltiples exchanges | Trading algorítmico multiplataforma |
| Desarrollo propio axios + crypto | — | — | Control total y extremo | Market making de alta frecuencia |
II. Firma nativa con crypto + encapsulamiento con axios
1. Inicialización del proyecto
mkdir binance-node && cd binance-node
npm init -y
npm install axios dotenv
Archivo .env:
BINANCE_API_KEY=tu_api_key_aqui
BINANCE_SECRET_KEY=tu_secret_key_aqui
2. Encapsulamiento del núcleo de firma
src/client.js:
const crypto = require('crypto');
const axios = require('axios');
require('dotenv').config();
const API_KEY = process.env.BINANCE_API_KEY;
const SECRET_KEY = process.env.BINANCE_SECRET_KEY;
const BASE_URL = 'https://api.binance.com';
function sign(params) {
const query = new URLSearchParams(params).toString();
return crypto
.createHmac('sha256', SECRET_KEY)
.update(query)
.digest('hex');
}
async function request(method, path, params = {}, signed = false) {
const headers = {};
if (signed) {
params.timestamp = Date.now();
params.recvWindow = 5000;
params.signature = sign(params);
headers['X-MBX-APIKEY'] = API_KEY;
}
try {
const config = {
method,
url: `${BASE_URL}${path}`,
headers,
timeout: 10000,
};
if (method === 'GET' || method === 'DELETE') {
config.params = params;
} else {
config.data = new URLSearchParams(params).toString();
config.headers['Content-Type'] = 'application/x-www-form-urlencoded';
}
const r = await axios(config);
return r.data;
} catch (err) {
if (err.response) {
const { code, msg } = err.response.data;
throw new Error(`Binance ${code}: ${msg}`);
}
throw err;
}
}
module.exports = { request, sign };
3. Llamadas a cuenta y mercado
src/account.js:
const { request } = require('./client');
async function getAccount() {
return request('GET', '/api/v3/account', {}, true);
}
async function getPrice(symbol) {
const r = await request('GET', '/api/v3/ticker/price', { symbol });
return parseFloat(r.price);
}
async function getDepth(symbol, limit = 20) {
return request('GET', '/api/v3/depth', { symbol, limit });
}
async function placeLimitOrder(symbol, side, quantity, price) {
return request('POST', '/api/v3/order', {
symbol,
side,
type: 'LIMIT',
timeInForce: 'GTC',
quantity,
price,
}, true);
}
async function cancelOrder(symbol, orderId) {
return request('DELETE', '/api/v3/order', { symbol, orderId }, true);
}
module.exports = { getAccount, getPrice, getDepth, placeLimitOrder, cancelOrder };
4. Ejemplo de ejecución
index.js:
const { getAccount, getPrice, placeLimitOrder } = require('./src/account');
(async () => {
try {
const account = await getAccount();
const nonZero = account.balances.filter(b => parseFloat(b.free) > 0);
console.log('Saldos no nulos:');
nonZero.forEach(b => console.log(` ${b.asset}: ${b.free}`));
const btc = await getPrice('BTCUSDT');
console.log(`Precio actual de BTC: $${btc}`);
// Colocar una orden limitada (un -10% del precio actual para evitar ejecución inmediata en test)
const testPrice = (btc * 0.9).toFixed(2);
const order = await placeLimitOrder('BTCUSDT', 'BUY', '0.001', testPrice);
console.log(`Orden ${order.orderId} creada con éxito`);
} catch (e) {
console.error('Fallo:', e.message);
}
})();
Ejecución:
node index.js
III. Solución rápida con node-binance-api
1. Instalación
npm install node-binance-api
2. Ejemplo de uso
const Binance = require('node-binance-api');
const binance = new Binance().options({
APIKEY: process.env.BINANCE_API_KEY,
APISECRET: process.env.BINANCE_SECRET_KEY,
useServerTime: true, // Sincronización automática con la hora del servidor
recvWindow: 10000,
test: false, // true para habilitar la Testnet
});
// Consultar saldos
binance.balance((error, balances) => {
if (error) return console.error(error);
Object.keys(balances).forEach(asset => {
const available = parseFloat(balances[asset].available);
if (available > 0) {
console.log(`${asset}: ${available}`);
}
});
});
// Compra a mercado
binance.marketBuy('BTCUSDT', 0.001, (error, response) => {
if (error) return console.error(error.body);
console.log('Compra a mercado exitosa:', response);
});
// Orden limitada
binance.buy('BTCUSDT', 0.001, 60000, { type: 'LIMIT' }, (error, response) => {
if (error) return console.error(error.body);
console.log('Orden limitada colocada:', response.orderId);
});
// Estilo Promise
(async () => {
const ticker = await binance.prices('BTCUSDT');
console.log('Precio actual:', ticker.BTCUSDT);
const candles = await binance.candlesticks('BTCUSDT', '1h', false, { limit: 100 });
console.log('K-lines recientes:', candles.length);
})();
IV. binance-api-node (Promise / TypeScript)
1. Instalación
npm install binance-api-node
npm install -D typescript @types/node
2. Ejemplo en TypeScript
import Binance, { Binance as BinanceClient, OrderType, OrderSide } from 'binance-api-node';
const client: BinanceClient = Binance({
apiKey: process.env.BINANCE_API_KEY!,
apiSecret: process.env.BINANCE_SECRET_KEY!,
});
async function main() {
// Inferencia automática de tipos
const time = await client.time();
console.log('Hora del servidor:', new Date(time));
const account = await client.accountInfo();
const nonZero = account.balances.filter(b => parseFloat(b.free) > 0);
console.log(`Posees ${nonZero.length} tipos de activos`);
const depth = await client.book({ symbol: 'BTCUSDT', limit: 20 });
console.log(`Bid 1: ${depth.bids[0].price}, Ask 1: ${depth.asks[0].price}`);
// Orden con verificación de tipos
const order = await client.order({
symbol: 'BTCUSDT',
side: OrderSide.BUY,
type: OrderType.LIMIT,
timeInForce: 'GTC',
quantity: '0.001',
price: '60000.00',
});
console.log('ID de la orden:', order.orderId);
}
main().catch(console.error);
V. Suscripción en tiempo real con WebSocket (librería ws)
const WebSocket = require('ws');
function subscribeTickers(symbols) {
const streams = symbols.map(s => `${s.toLowerCase()}@ticker`).join('/');
const url = `wss://stream.binance.com:9443/stream?streams=${streams}`;
function connect() {
const ws = new WebSocket(url);
ws.on('open', () => console.log(`Suscrito a ${symbols.length} pares de trading`));
ws.on('message', (raw) => {
const { stream, data } = JSON.parse(raw);
console.log(`${data.s}: Último precio ${data.c}, cambio 24h ${data.P}%`);
});
ws.on('pong', () => console.log('Pong recibido'));
ws.on('close', () => {
console.log('Conexión perdida, reconectando en 5s');
setTimeout(connect, 5000);
});
ws.on('error', (err) => console.error('Error de WS:', err.message));
// Enviar ping cada 3 minutos para mantener la conexión activa
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) ws.ping();
}, 180000);
}
connect();
}
subscribeTickers(['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT']);
VI. Ejemplo en Node.js para la API de Futuros (Futures)
const crypto = require('crypto');
const axios = require('axios');
const FAPI = 'https://fapi.binance.com';
function signFutures(params, secret) {
const query = new URLSearchParams(params).toString();
return crypto.createHmac('sha256', secret).update(query).digest('hex');
}
async function futuresPlaceOrder(apiKey, secret, symbol, side, quantity, price) {
const params = {
symbol,
side,
type: 'LIMIT',
timeInForce: 'GTC',
quantity: quantity.toString(),
price: price.toString(),
timestamp: Date.now(),
};
params.signature = signFutures(params, secret);
const r = await axios.post(
`${FAPI}/fapi/v1/order`,
new URLSearchParams(params).toString(),
{
headers: {
'X-MBX-APIKEY': apiKey,
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
return r.data;
}
async function futuresSetLeverage(apiKey, secret, symbol, leverage) {
const params = { symbol, leverage, timestamp: Date.now() };
params.signature = signFutures(params, secret);
const r = await axios.post(
`${FAPI}/fapi/v1/leverage`,
new URLSearchParams(params).toString(),
{ headers: { 'X-MBX-APIKEY': apiKey } }
);
return r.data;
}
VII. Preguntas frecuentes FAQ
P1: ¿Es suficiente la precisión de Date.now() en Node.js?
R: Sí. Date.now() en Node.js devuelve un timestamp en milisegundos (13 dígitos), que es exactamente lo que requiere la API de Binance. Sin embargo, en entornos de contenedores, asegúrate de que el reloj del host esté sincronizado (se recomienda usar chronyd o systemd-timesyncd).
P2: ¿Qué es mejor para la API de Binance, axios o node-fetch?
R: Axios es ideal para REST por defecto (análisis JSON automático, reintentos de errores, pool de conexiones); fetch (nativo en Node 18+) es más ligero pero requiere manejo manual de errores. Para escenarios de alta frecuencia, se recomienda undici o http.Agent nativo, ya que el rendimiento es un 30% superior al de axios.
P3: ¿Cómo evitar el uso de as any en TypeScript?
R: Utiliza binance-api-node, que incluye definiciones de tipos completas; o genera tus propios tipos basados en la especificación OpenAPI oficial usando openapi-typescript. Evita encapsulamientos genéricos como request<any>.
P4: ¿Qué precauciones debo tomar con el demonio pm2?
R: Si se pierde la conexión WebSocket, el bucle de eventos de Node no se cerrará y pm2 no reiniciará automáticamente el proceso. Se recomienda implementar la reconexión interna en el código (ver sección V) y configurar pm2 con max_memory_restart: '500M' para prevenir fugas de memoria.
P5: ¿Cómo limitar la frecuencia (rate limit) en Node.js?
R: Utiliza la librería bottleneck:
const Bottleneck = require('bottleneck');
const limiter = new Bottleneck({
minTime: 10, // Intervalo mínimo de 10ms
maxConcurrent: 20, // Máximo 20 peticiones concurrentes
});
const wrappedGet = limiter.wrap(axios.get);
Después de revisar la solución para Node.js, vuelve a la navegación de categorías para ver tutoriales de SDK en otros lenguajes en la sección de «Integración API».