Bots de Trading na Hyperliquid com Bun: Guia Completo do SDK (2026)
Hyperliquid e a DEX de perpetuos que mais cresce, processando bilhoes em volume diario com execucao em menos de um segundo. O pacote npm oficial deles facilita a construcao de bots de trading com Bun. Este guia cobre configuracao, execucao de ordens e estrategias de producao.
Comece na Hyperliquid: Cadastre-se com nosso link de indicacao para apoiar este guia e negociar com taxas reduzidas.
O que e Hyperliquid?
Hyperliquid e uma exchange descentralizada de futuros perpetuos rodando em sua propria blockchain L1. Principais caracteristicas:
- Velocidade: ~200ms de tempo de bloco, capacidade de 100k+ TPS
- Liquidez: $1B+ em volume diario, spreads reduzidos
- Sem taxas de gas: Trading e gratuito (apenas pagamentos de funding)
- Auto-custodia: Negocie diretamente da sua carteira
- Alavancagem de 50x: Nos principais pares
Configurando com Bun
Instalacao
# Create project
mkdir hyperliquid-bot && cd hyperliquid-bot
bun init -y
# Install Hyperliquid SDK
bun add hyperliquid
# Install additional dependencies
bun add dotenv
Estrutura do Projeto
hyperliquid-bot/
├── src/
│ ├── index.ts
│ ├── client.ts
│ ├── strategies/
│ │ └── grid.ts
│ └── utils/
│ └── helpers.ts
├── .env
├── package.json
└── tsconfig.json
Configuracao de Ambiente
# .env
HYPERLIQUID_PRIVATE_KEY=your_private_key_here
HYPERLIQUID_WALLET_ADDRESS=0x...
# Use mainnet or testnet
HYPERLIQUID_TESTNET=false
Configuracao do Cliente
// src/client.ts
import Hyperliquid from "hyperliquid";
import "dotenv/config";
const privateKey = process.env.HYPERLIQUID_PRIVATE_KEY;
const walletAddress = process.env.HYPERLIQUID_WALLET_ADDRESS;
const testnet = process.env.HYPERLIQUID_TESTNET === "true";
if (!privateKey || !walletAddress) {
throw new Error("Missing HYPERLIQUID_PRIVATE_KEY or HYPERLIQUID_WALLET_ADDRESS");
}
export const sdk = new Hyperliquid({
privateKey,
testnet,
walletAddress,
});
// Initialize the SDK
export async function initClient() {
await sdk.connect();
console.log(`Connected to Hyperliquid ${testnet ? "testnet" : "mainnet"}`);
return sdk;
}
Lendo Dados de Mercado
Obter Todos os Mercados
// Get available perpetual markets
async function getMarkets() {
const meta = await sdk.info.perpetuals.getMeta();
console.log("Available markets:");
for (const asset of meta.universe) {
console.log(`${asset.name}: ${asset.szDecimals} decimals, max leverage ${asset.maxLeverage}x`);
}
return meta.universe;
}
Obter Livro de Ordens
// Fetch orderbook for a symbol
async function getOrderbook(symbol: string) {
const book = await sdk.info.perpetuals.getL2Book({ coin: symbol });
console.log(`${symbol} Orderbook:`);
console.log("Bids:", book.levels[0].slice(0, 5)); // Top 5 bids
console.log("Asks:", book.levels[1].slice(0, 5)); // Top 5 asks
const bestBid = parseFloat(book.levels[0][0].px);
const bestAsk = parseFloat(book.levels[1][0].px);
const spread = ((bestAsk - bestBid) / bestBid * 100).toFixed(4);
console.log(`Spread: ${spread}%`);
return book;
}
// Usage
await getOrderbook("BTC");
Obter Estado do Usuario
// Get account balance and positions
async function getAccountState() {
const state = await sdk.info.perpetuals.getClearinghouseState({
user: walletAddress,
});
console.log("Account equity:", state.marginSummary.accountValue);
console.log("Available balance:", state.withdrawable);
console.log("\nOpen positions:");
for (const position of state.assetPositions) {
if (parseFloat(position.position.szi) !== 0) {
console.log(`${position.position.coin}:`);
console.log(` Size: ${position.position.szi}`);
console.log(` Entry: ${position.position.entryPx}`);
console.log(` Unrealized PnL: ${position.position.unrealizedPnl}`);
}
}
return state;
}
Feeds de Preco em Tempo Real
// Subscribe to price updates via WebSocket
async function subscribeToPrices(symbols: string[]) {
sdk.subscriptions.subscribeToAllMids((data) => {
for (const symbol of symbols) {
if (data.mids[symbol]) {
console.log(`${symbol}: $${data.mids[symbol]}`);
}
}
});
console.log(`Subscribed to: ${symbols.join(", ")}`);
}
// Subscribe to trades
async function subscribeToTrades(symbol: string) {
sdk.subscriptions.subscribeToTrades({ coin: symbol }, (trade) => {
console.log(`${symbol} Trade: ${trade.side} ${trade.sz} @ ${trade.px}`);
});
}
Executando Operacoes
Ordens a Mercado
// Place a market order
async function marketOrder(
symbol: string,
isBuy: boolean,
size: number
) {
const order = await sdk.exchange.placeOrder({
coin: symbol,
is_buy: isBuy,
sz: size,
limit_px: isBuy ? 999999 : 0.01, // Market order simulation
order_type: { limit: { tif: "Ioc" } }, // Immediate or cancel
reduce_only: false,
});
console.log(`Market ${isBuy ? "buy" : "sell"} ${size} ${symbol}`);
console.log("Order response:", order);
return order;
}
// Usage
await marketOrder("ETH", true, 0.1); // Buy 0.1 ETH
Ordens Limitadas
// Place a limit order
async function limitOrder(
symbol: string,
isBuy: boolean,
size: number,
price: number
) {
const order = await sdk.exchange.placeOrder({
coin: symbol,
is_buy: isBuy,
sz: size,
limit_px: price,
order_type: { limit: { tif: "Gtc" } }, // Good til cancelled
reduce_only: false,
});
console.log(`Limit ${isBuy ? "buy" : "sell"} ${size} ${symbol} @ ${price}`);
return order;
}
// Place order at 1% below current price
async function placeBuyAtDiscount(symbol: string, size: number) {
const book = await sdk.info.perpetuals.getL2Book({ coin: symbol });
const midPrice = (parseFloat(book.levels[0][0].px) + parseFloat(book.levels[1][0].px)) / 2;
const buyPrice = midPrice * 0.99; // 1% discount
return limitOrder(symbol, true, size, buyPrice);
}
Stop Loss e Take Profit
// Place a stop loss order
async function stopLoss(
symbol: string,
size: number,
triggerPrice: number
) {
const order = await sdk.exchange.placeOrder({
coin: symbol,
is_buy: false, // Sell to close long
sz: size,
limit_px: triggerPrice * 0.99, // Slightly below trigger
order_type: {
trigger: {
triggerPx: triggerPrice.toString(),
isMarket: true,
tpsl: "sl",
},
},
reduce_only: true,
});
console.log(`Stop loss set at ${triggerPrice}`);
return order;
}
// Place a take profit order
async function takeProfit(
symbol: string,
size: number,
triggerPrice: number
) {
const order = await sdk.exchange.placeOrder({
coin: symbol,
is_buy: false,
sz: size,
limit_px: triggerPrice * 1.01,
order_type: {
trigger: {
triggerPx: triggerPrice.toString(),
isMarket: true,
tpsl: "tp",
},
},
reduce_only: true,
});
console.log(`Take profit set at ${triggerPrice}`);
return order;
}
Cancelar Ordens
// Cancel a specific order
async function cancelOrder(symbol: string, orderId: number) {
const result = await sdk.exchange.cancelOrder({
coin: symbol,
o: orderId,
});
console.log(`Cancelled order ${orderId}:`, result);
return result;
}
// Cancel all orders for a symbol
async function cancelAllOrders(symbol: string) {
const openOrders = await sdk.info.perpetuals.getOpenOrders({
user: walletAddress,
});
const symbolOrders = openOrders.filter(o => o.coin === symbol);
for (const order of symbolOrders) {
await cancelOrder(symbol, order.oid);
}
console.log(`Cancelled ${symbolOrders.length} orders for ${symbol}`);
}
// Cancel all orders across all symbols
async function cancelAllOrdersGlobal() {
const result = await sdk.exchange.cancelAllOrders();
console.log("Cancelled all orders:", result);
return result;
}
Construindo um Bot de Grid Trading
// src/strategies/grid.ts
interface GridConfig {
symbol: string;
lowerPrice: number;
upperPrice: number;
gridCount: number;
totalSize: number;
}
class GridBot {
private config: GridConfig;
private gridPrices: number[] = [];
private orderSize: number;
constructor(config: GridConfig) {
this.config = config;
this.orderSize = config.totalSize / config.gridCount;
this.calculateGridPrices();
}
private calculateGridPrices() {
const { lowerPrice, upperPrice, gridCount } = this.config;
const step = (upperPrice - lowerPrice) / (gridCount - 1);
for (let i = 0; i < gridCount; i++) {
this.gridPrices.push(lowerPrice + step * i);
}
console.log("Grid prices:", this.gridPrices);
}
async placeInitialOrders() {
const book = await sdk.info.perpetuals.getL2Book({
coin: this.config.symbol
});
const midPrice = (
parseFloat(book.levels[0][0].px) +
parseFloat(book.levels[1][0].px)
) / 2;
console.log(`Current price: ${midPrice}`);
// Place buy orders below current price
// Place sell orders above current price
for (const price of this.gridPrices) {
const isBuy = price < midPrice;
await sdk.exchange.placeOrder({
coin: this.config.symbol,
is_buy: isBuy,
sz: this.orderSize,
limit_px: price,
order_type: { limit: { tif: "Gtc" } },
reduce_only: false,
});
console.log(`Placed ${isBuy ? "buy" : "sell"} @ ${price.toFixed(2)}`);
// Small delay to avoid rate limits
await Bun.sleep(100);
}
}
async monitorAndReplace() {
// Subscribe to fills and replace executed orders
sdk.subscriptions.subscribeToUserFills((fill) => {
console.log(`Fill: ${fill.side} ${fill.sz} @ ${fill.px}`);
// Place opposite order at next grid level
const fillPrice = parseFloat(fill.px);
const isBuy = fill.side === "B";
// Find next grid price
const gridIndex = this.gridPrices.findIndex(
p => Math.abs(p - fillPrice) < 1
);
if (gridIndex !== -1) {
const nextIndex = isBuy ? gridIndex + 1 : gridIndex - 1;
if (nextIndex >= 0 && nextIndex < this.gridPrices.length) {
const nextPrice = this.gridPrices[nextIndex];
// Place opposite order
sdk.exchange.placeOrder({
coin: this.config.symbol,
is_buy: !isBuy,
sz: this.orderSize,
limit_px: nextPrice,
order_type: { limit: { tif: "Gtc" } },
reduce_only: false,
});
console.log(`Replaced with ${!isBuy ? "buy" : "sell"} @ ${nextPrice}`);
}
}
});
}
}
// Usage
const gridBot = new GridBot({
symbol: "ETH",
lowerPrice: 2000,
upperPrice: 2200,
gridCount: 10,
totalSize: 1.0, // 1 ETH total
});
await gridBot.placeInitialOrders();
await gridBot.monitorAndReplace();
Gerenciamento de Risco
// Position sizing based on account risk
async function calculatePositionSize(
symbol: string,
riskPercent: number,
stopLossPercent: number
): Promise {
const state = await sdk.info.perpetuals.getClearinghouseState({
user: walletAddress,
});
const accountValue = parseFloat(state.marginSummary.accountValue);
const riskAmount = accountValue * (riskPercent / 100);
const book = await sdk.info.perpetuals.getL2Book({ coin: symbol });
const currentPrice = (
parseFloat(book.levels[0][0].px) +
parseFloat(book.levels[1][0].px)
) / 2;
// Position size = Risk Amount / (Stop Loss Distance)
const stopLossDistance = currentPrice * (stopLossPercent / 100);
const positionSize = riskAmount / stopLossDistance;
console.log(`Account: $${accountValue.toFixed(2)}`);
console.log(`Risk: ${riskPercent}% = $${riskAmount.toFixed(2)}`);
console.log(`Position size: ${positionSize.toFixed(4)} ${symbol}`);
return positionSize;
}
// Example: Risk 1% with 2% stop loss
const size = await calculatePositionSize("BTC", 1, 2);
Tratamento de Erros
// Robust order placement with retries
async function placeOrderWithRetry(
orderParams: any,
maxRetries = 3
): Promise {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const result = await sdk.exchange.placeOrder(orderParams);
if (result.response.type === "error") {
throw new Error(result.response.data);
}
return result;
} catch (error) {
console.error(`Attempt ${attempt} failed:`, error.message);
if (attempt === maxRetries) {
throw error;
}
// Exponential backoff
await Bun.sleep(1000 * Math.pow(2, attempt));
}
}
}
// Graceful shutdown
process.on("SIGINT", async () => {
console.log("\nShutting down...");
try {
await sdk.exchange.cancelAllOrders();
console.log("Cancelled all orders");
} catch (error) {
console.error("Error cancelling orders:", error);
}
process.exit(0);
});
Bot de Trading Completo
// src/index.ts
import { initClient, sdk } from "./client";
import "dotenv/config";
const walletAddress = process.env.HYPERLIQUID_WALLET_ADDRESS!;
interface BotConfig {
symbol: string;
sizeUsd: number;
takeProfitPercent: number;
stopLossPercent: number;
}
async function runBot(config: BotConfig) {
await initClient();
console.log(`Starting bot for ${config.symbol}`);
console.log(`Size: $${config.sizeUsd}`);
console.log(`TP: ${config.takeProfitPercent}%, SL: ${config.stopLossPercent}%`);
// Get current price
const book = await sdk.info.perpetuals.getL2Book({ coin: config.symbol });
const currentPrice = (
parseFloat(book.levels[0][0].px) +
parseFloat(book.levels[1][0].px)
) / 2;
const size = config.sizeUsd / currentPrice;
const tpPrice = currentPrice * (1 + config.takeProfitPercent / 100);
const slPrice = currentPrice * (1 - config.stopLossPercent / 100);
console.log(`Current price: $${currentPrice.toFixed(2)}`);
console.log(`Size: ${size.toFixed(4)} ${config.symbol}`);
console.log(`TP: $${tpPrice.toFixed(2)}, SL: $${slPrice.toFixed(2)}`);
// Place market buy
await sdk.exchange.placeOrder({
coin: config.symbol,
is_buy: true,
sz: size,
limit_px: currentPrice * 1.01,
order_type: { limit: { tif: "Ioc" } },
reduce_only: false,
});
console.log("Opened long position");
// Place TP and SL
await sdk.exchange.placeOrder({
coin: config.symbol,
is_buy: false,
sz: size,
limit_px: tpPrice,
order_type: {
trigger: {
triggerPx: tpPrice.toString(),
isMarket: true,
tpsl: "tp",
},
},
reduce_only: true,
});
await sdk.exchange.placeOrder({
coin: config.symbol,
is_buy: false,
sz: size,
limit_px: slPrice,
order_type: {
trigger: {
triggerPx: slPrice.toString(),
isMarket: true,
tpsl: "sl",
},
},
reduce_only: true,
});
console.log("Set TP and SL orders");
// Monitor position
sdk.subscriptions.subscribeToUserFills((fill) => {
console.log(`Fill: ${fill.side} ${fill.sz} @ ${fill.px}`);
if (fill.closedPnl) {
console.log(`Closed PnL: $${fill.closedPnl}`);
process.exit(0);
}
});
}
// Run
runBot({
symbol: "ETH",
sizeUsd: 100,
takeProfitPercent: 2,
stopLossPercent: 1,
});
Melhores Praticas
- Use testnet primeiro: Sempre teste estrategias na testnet antes da mainnet
- Comece pequeno: Inicie com tamanhos de posicao minimos
- Trate desconexoes: Implemente logica de reconexao para WebSocket
- Registre tudo: Mantenha logs detalhados para depuracao
- Defina limites rigidos: Implemente tamanho maximo de posicao e limites de perda diaria
- Monitore a latencia: Acompanhe os tempos de execucao das ordens
Limites de Taxa da API
| Endpoint | Limite de Taxa |
|---|---|
| Info (leitura) | 1200/min |
| Exchange (escrita) | 600/min |
| WebSocket | 50 inscricoes |
Conclusao
O pacote npm do Hyperliquid combinado com a velocidade do Bun o torna ideal para construir bots de trading. Comece com estrategias simples, implemente gerenciamento de risco adequado e sempre teste completamente na testnet antes de colocar capital. A combinacao da baixa latencia do Hyperliquid e taxas de gas zero cria oportunidades que nao seriam viaveis em outras DEXs.
Pronto para comecar a negociar? Entre na Hyperliquid com nosso link de indicacao e obtenha descontos nas taxas das suas operacoes.