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

  1. Use testnet primeiro: Sempre teste estrategias na testnet antes da mainnet
  2. Comece pequeno: Inicie com tamanhos de posicao minimos
  3. Trate desconexoes: Implemente logica de reconexao para WebSocket
  4. Registre tudo: Mantenha logs detalhados para depuracao
  5. Defina limites rigidos: Implemente tamanho maximo de posicao e limites de perda diaria
  6. Monitore a latencia: Acompanhe os tempos de execucao das ordens

Limites de Taxa da API

EndpointLimite de Taxa
Info (leitura)1200/min
Exchange (escrita)600/min
WebSocket50 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.