npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@plutarc/bitmex

v0.2.0

Published

Fully-typed BitMEX REST and WebSocket client for TypeScript/JavaScript

Downloads

155

Readme

@plutarc/bitmex

A fully-typed BitMEX REST and WebSocket client for TypeScript/JavaScript. Zero runtime dependencies — uses only Node.js/Bun built-ins.

Table of Contents

Features

  • Complete REST API — All BitMEX REST endpoints with full TypeScript types
  • Real-time WebSocket — Public and private WebSocket clients with typed event emitters
  • Zero dependencies — Uses only node:crypto and node:events built-ins
  • Fully typed — Every request parameter and response field has TypeScript interfaces
  • Rate limiting — Built-in rolling window rate limiter that tracks x-ratelimit-remaining
  • Auto-retry — Exponential backoff with jitter for retryable errors (429, 5xx)
  • Auto-reconnect — WebSocket reconnection with exponential backoff
  • Ping/pong — Automatic heartbeat to keep WebSocket connections alive
  • Delta parser — Generic WebSocket state management with orderbook convenience store
  • Authentication — HMAC-SHA256 signing for both REST and WebSocket
  • Testnet support — Single flag to switch between production and testnet
  • Dual format — Ships ESM and CJS builds with full declaration files

Installation

# bun
bun add @plutarc/bitmex

# npm
npm install @plutarc/bitmex

# pnpm
pnpm add @plutarc/bitmex

Quick Start

REST Client

import { BitMEXClient } from "@plutarc/bitmex";

const client = new BitMEXClient({
  apiKey: "your-api-key",
  apiSecret: "your-api-secret",
  testnet: true, // use testnet for development
});

// Get active instruments
const instruments = await client.instrument.getActive();

// Place a limit order
const order = await client.order.place({
  symbol: "XBTUSD",
  side: "Buy",
  ordType: "Limit",
  orderQty: 100,
  price: 50000,
});

// Get open positions
const positions = await client.position.getOpen();

// Get wallet balance
const wallets = await client.user.getWallet();

Public WebSocket

import { BitMEXPublicWs } from "@plutarc/bitmex";

const ws = new BitMEXPublicWs({ testnet: true });

// Subscribe to real-time trades — fully typed callback
ws.on("trade", (action, data) => {
  for (const trade of data) {
    console.log(`${trade.side} ${trade.size} @ ${trade.price}`);
  }
});

// Subscribe to orderbook updates
ws.on("orderBookL2_25", (action, data) => {
  console.log(`Orderbook ${action}: ${data.length} entries`);
});

// Subscribe to 1-minute candles
ws.on("tradeBin1m", (action, data) => {
  for (const candle of data) {
    console.log(`${candle.symbol} O:${candle.open} H:${candle.high} L:${candle.low} C:${candle.close}`);
  }
});

// Start subscriptions and connect
ws.subscribe(["trade:XBTUSD", "orderBookL2_25:XBTUSD", "tradeBin1m:XBTUSD"]);
ws.connect();

Private WebSocket

import { BitMEXPrivateWs } from "@plutarc/bitmex";

const ws = new BitMEXPrivateWs({
  apiKey: "your-api-key",
  apiSecret: "your-api-secret",
  testnet: true,
});

// Listen for authentication success
ws.on("authenticated", () => {
  console.log("Authenticated — subscribing to private topics");
  ws.subscribe(["order", "position", "wallet", "execution"]);
});

// Order updates
ws.on("order", (action, data) => {
  for (const order of data) {
    console.log(`Order ${order.orderID}: ${order.ordStatus}`);
  }
});

// Position updates
ws.on("position", (action, data) => {
  for (const pos of data) {
    console.log(`${pos.symbol}: qty=${pos.currentQty} pnl=${pos.unrealisedPnl}`);
  }
});

// Execution (fill) updates
ws.on("execution", (action, data) => {
  for (const exec of data) {
    console.log(`Fill: ${exec.lastQty} @ ${exec.lastPx}`);
  }
});

ws.connect();

REST API Reference

Client Options

const client = new BitMEXClient({
  apiKey: "...",               // Optional — required for authenticated endpoints
  apiSecret: "...",            // Optional — required for authenticated endpoints
  testnet: false,              // Use testnet (default: false)
  baseUrl: "https://...",      // Custom base URL (overrides testnet flag)
  rateLimitPerMinute: 100,     // Self-throttle limit (default: 100, BitMEX limit: 120)
  maxRetries: 3,               // Max retries for retryable errors (default: 3)
  baseRetryDelayMs: 500,       // Base delay for exponential backoff (default: 500)
  requestTimeoutMs: 15000,     // Request timeout (default: 15000)
  authExpirySec: 5,            // Signature expiry window (default: 5)
  logger: console,             // Custom logger (default: silent)
});

Order

// Get open orders
const open = await client.order.getOpen({ symbol: "XBTUSD" });

// Get order history
const history = await client.order.getHistory({ symbol: "XBTUSD", count: 100 });

// Query with filters
const orders = await client.order.get({
  symbol: "XBTUSD",
  filter: { ordStatus: "Filled" },
  count: 50,
  reverse: true,
});

// Place an order
const order = await client.order.place({
  symbol: "XBTUSD",
  side: "Buy",
  ordType: "Limit",
  orderQty: 100,
  price: 50000,
  timeInForce: "GoodTillCancel",
});

// Place bulk orders
const orders = await client.order.placeBulk([
  { symbol: "XBTUSD", side: "Buy", ordType: "Limit", orderQty: 100, price: 49000 },
  { symbol: "XBTUSD", side: "Buy", ordType: "Limit", orderQty: 100, price: 48000 },
]);

// Amend an order
const amended = await client.order.amend({
  orderID: "order-id",
  price: 51000,
});

// Amend bulk orders
const amended = await client.order.amendBulk([
  { orderID: "id-1", price: 51000 },
  { orderID: "id-2", price: 47000 },
]);

// Cancel an order
const cancelled = await client.order.cancel({ orderID: "order-id" });
// or by client order ID
const cancelled = await client.order.cancel({ clOrdID: "my-order-1" });

// Cancel all orders
await client.order.cancelAll({ symbol: "XBTUSD" });

// Dead man's switch — cancel all after timeout (ms)
await client.order.cancelAllAfter(60000); // cancel all in 60s
await client.order.cancelAllAfter(0);     // disable timer

// Close position at market
const closed = await client.order.closePosition({ symbol: "XBTUSD" });

Position

// Get open positions
const positions = await client.position.getOpen("XBTUSD");

// Get all positions (including closed)
const all = await client.position.get();

// Set leverage
await client.position.setLeverage("XBTUSD", 10);

// Set cross margin
await client.position.setIsolateMargin("XBTUSD", false);

// Set isolated margin
await client.position.setIsolateMargin("XBTUSD", true);

// Set risk limit
await client.position.setRiskLimit("XBTUSD", 5000000000);

// Transfer margin to position
await client.position.transferMargin("XBTUSD", 100000);

Instrument

// Get specific instrument
const instruments = await client.instrument.get({ symbol: "XBTUSD" });

// Get all active instruments
const active = await client.instrument.getActive();

// Get active instruments and indices
const all = await client.instrument.getActiveAndIndices();

// Get active intervals
const intervals = await client.instrument.getActiveIntervals();

// Get composite index data
const index = await client.instrument.getCompositeIndex({ symbol: ".BXBT" });

// Get all indices
const indices = await client.instrument.getIndices();

// Get USD volume summary
const volume = await client.instrument.getUsdVolume();

Trade

// Get recent trades
const trades = await client.trade.get({
  symbol: "XBTUSD",
  count: 100,
  reverse: true,
});

// Get OHLCV candles
const candles = await client.trade.getBucketed({
  binSize: "1h",
  symbol: "XBTUSD",
  count: 100,
  reverse: true,
  partial: false,
});

Order Book

// Get L2 orderbook snapshot (default 25 levels)
const book = await client.orderBook.getL2({ symbol: "XBTUSD" });

// Custom depth
const deepBook = await client.orderBook.getL2({ symbol: "XBTUSD", depth: 50 });

Execution

// Get raw executions
const execs = await client.execution.get({
  symbol: "XBTUSD",
  count: 50,
  reverse: true,
});

// Get trade history (fills only)
const fills = await client.execution.getTradeHistory({
  symbol: "XBTUSD",
  startTime: "2025-01-01T00:00:00.000Z",
});

User / Account

// Get current user
const user = await client.user.get();

// Get wallet balance
const wallets = await client.user.getWallet("USDt");

// Get wallet history
const history = await client.user.getWalletHistory({ currency: "USDt", count: 50 });

// Get wallet summary
const summary = await client.user.getWalletSummary("USDt");

// Get commission rates
const fees = await client.user.getCommission();

// Get margin status
const margin = await client.user.getMargin("USDt");

// Update preferences
await client.user.updatePreferences({ locale: "en-US" });

Funding

const funding = await client.funding.get({
  symbol: "XBTUSD",
  count: 100,
  reverse: true,
});

Insurance

const insurance = await client.insurance.get({ count: 50, reverse: true });

Liquidation

const liquidations = await client.liquidation.get({
  symbol: "XBTUSD",
  count: 100,
  reverse: true,
});

Settlement

const settlements = await client.settlement.get({ symbol: "XBTUSD" });

Stats

const stats = await client.stats.get();
const history = await client.stats.getHistory();
const usd = await client.stats.getHistoryUSD();

Quote

// Get quotes
const quotes = await client.quote.get({ symbol: "XBTUSD", count: 10 });

// Get bucketed quotes
const bucketed = await client.quote.getBucketed({
  binSize: "1h",
  symbol: "XBTUSD",
  count: 100,
});

Announcement

const announcements = await client.announcement.get();
const urgent = await client.announcement.getUrgent();

API Key

const keys = await client.apiKey.get();
const newKey = await client.apiKey.create({ name: "trading-bot" });
await client.apiKey.remove("key-id");

Leaderboard

const board = await client.leaderboard.get({ method: "notional" });
const myName = await client.leaderboard.getName();

WebSocket API Reference

WebSocket Options

// Public WebSocket — no authentication required
const pub = new BitMEXPublicWs({
  testnet: false,              // Use testnet (default: false)
  wsUrl: "wss://...",          // Custom WebSocket URL (overrides testnet flag)
  pingIntervalMs: 15000,       // Heartbeat interval (default: 15000)
  maxReconnectAttempts: 10,    // Max reconnect attempts, 0 = infinite (default: 10)
  baseReconnectDelayMs: 1000,  // Base delay for reconnect backoff (default: 1000)
  logger: console,             // Custom logger (default: silent)
});

// Private WebSocket — authentication required
const priv = new BitMEXPrivateWs({
  apiKey: "...",               // Required
  apiSecret: "...",            // Required
  testnet: false,
  authExpirySec: 5,            // Auth signature expiry (default: 5)
  // ...same options as public
});

Public Topics

Subscribe using BitMEX topic format: "table:symbol" for symbol-specific topics.

| Event | Data Type | Description | |-------|-----------|-------------| | trade | WsTrade[] | Real-time trades | | tradeBin1m | WsTradeBin[] | 1-minute OHLCV candles | | tradeBin5m | WsTradeBin[] | 5-minute OHLCV candles | | tradeBin1h | WsTradeBin[] | 1-hour OHLCV candles | | tradeBin1d | WsTradeBin[] | Daily OHLCV candles | | orderBookL2_25 | WsOrderBookL2[] | Orderbook L2 (25 levels) | | orderBookL2 | WsOrderBookL2[] | Orderbook L2 (full) | | orderBook10 | WsOrderBook10[] | Top 10 orderbook | | instrument | WsInstrument[] | Instrument updates | | funding | WsFunding[] | Funding rate updates | | liquidation | WsLiquidation[] | Liquidation orders | | settlement | WsSettlement[] | Settlement data | | insurance | WsInsurance[] | Insurance fund | | quote | WsQuote[] | Top-of-book quotes | | quoteBin1m | WsQuoteBin[] | 1-minute bucketed quotes | | quoteBin5m | WsQuoteBin[] | 5-minute bucketed quotes | | quoteBin1h | WsQuoteBin[] | 1-hour bucketed quotes | | quoteBin1d | WsQuoteBin[] | Daily bucketed quotes |

All data events receive (action: WsAction, data: T[]) where WsAction is "partial" | "insert" | "update" | "delete".

  • partial — Full snapshot (initial data load)
  • insert — New entries added
  • update — Existing entries modified
  • delete — Entries removed
ws.subscribe([
  "trade:XBTUSD",
  "orderBookL2_25:XBTUSD",
  "tradeBin1m:XBTUSD",
  "instrument:XBTUSD",
]);

Private Topics

Private topics do not require a symbol suffix — they stream all data for your account.

| Event | Data Type | Description | |-------|-----------|-------------| | order | WsOrder[] | Order status updates | | execution | WsExecution[] | Trade executions / fills | | position | WsPosition[] | Position updates | | wallet | WsWallet[] | Wallet balance changes | | margin | WsMargin[] | Margin / available balance | | transact | WsTransaction[] | Deposit/withdrawal transactions | | affiliate | WsAffiliate[] | Affiliate status |

ws.on("authenticated", () => {
  ws.subscribe(["order", "position", "execution", "wallet", "margin"]);
});

Lifecycle Events

Both public and private WebSocket clients emit lifecycle events:

| Event | Args | Description | |-------|------|-------------| | open | — | Connection established (and authenticated for private) | | close | (code, reason) | Connection closed | | reconnecting | (attempt) | Reconnection attempt | | error | (error) | Connection or auth error | | authenticated | — | (Private only) Authentication successful |

ws.on("open", () => console.log("Connected"));
ws.on("close", (code, reason) => console.log(`Disconnected: ${code}`));
ws.on("reconnecting", (attempt) => console.log(`Reconnecting (attempt ${attempt})`));
ws.on("error", (err) => console.error("WS error:", err));

Connection Management

ws.connect();      // Establish connection
ws.disconnect();   // Gracefully disconnect (no auto-reconnect)

ws.subscribe(["trade:XBTUSD"]);    // Subscribe to topics
ws.unsubscribe(["trade:XBTUSD"]);  // Unsubscribe from topics

ws.state;               // "connecting" | "connected" | "reconnecting" | "closed"
ws.activeSubscriptions; // string[] of current subscriptions

Utilities

Currency Conversion

Helpers for BitMEX's internal representations:

import { satoshiToBtc, microUsdtToUsdt, convertWalletAmount } from "@plutarc/bitmex";

// BitMEX stores BTC as satoshi (XBt): 1 BTC = 100,000,000 satoshi
satoshiToBtc(100_000_000); // 1.0

// BitMEX stores USDT as micro-USDT (USDt): 1 USDT = 1,000,000 micro
microUsdtToUsdt(1_000_000); // 1.0

// Auto-detect by currency code
convertWalletAmount(100_000_000, "XBt"); // 1.0 BTC
convertWalletAmount(1_000_000, "USDt");  // 1.0 USDT

Delta Parser

A generic, zero-dependency delta parser that maintains local state from BitMEX WebSocket delta messages (partial/insert/update/delete). Works with any WebSocket table type.

import { DeltaParser } from "@plutarc/bitmex";
import type { WsOrderBookL2, WsOrder } from "@plutarc/bitmex";

// Create a parser for orderbook data — keys identify unique rows
const bookParser = new DeltaParser<WsOrderBookL2>({ keys: ["id"] });

ws.on("orderBookL2_25", (action, data) => {
  const snapshot = bookParser.update(action, data);
  // snapshot is the full current orderbook state
  console.log(`Book has ${snapshot.length} levels`);
});

// Works with any table — orders, positions, etc.
const orderParser = new DeltaParser<WsOrder>({ keys: ["orderID"] });

ws.on("order", (action, data) => {
  const orders = orderParser.update(action, data);
  console.log(`${orders.length} active orders`);
});

For insert-only tables like trade that grow unboundedly, use maxItems to cap the store:

import type { WsTrade } from "@plutarc/bitmex";

const tradeParser = new DeltaParser<WsTrade>({ keys: ["trdMatchID"], maxItems: 1000 });

ws.on("trade", (action, data) => {
  const trades = tradeParser.update(action, data);
  // keeps only the most recent 1000 trades
});

API:

| Method / Property | Returns | Description | |---|---|---| | update(action, data) | readonly T[] | Process a delta message, returns full current state | | snapshot | readonly T[] | Current state without processing a new message | | length | number | Number of items in the store | | clear() | void | Reset the store to empty |

OrderBook Store

A higher-level convenience store for L2 orderbook data. Maintains sorted bids and asks with common orderbook queries. Optionally wraps a DeltaParser — you can bring your own or use the built-in one.

import { OrderBookStore } from "@plutarc/bitmex";

const book = new OrderBookStore();

// Plug directly into a WebSocket event handler (update is auto-bound)
ws.on("orderBookL2_25", book.update);

// Query the book
console.log(book.bestBid());   // 50000
console.log(book.bestAsk());   // 50001
console.log(book.spread());    // 1
console.log(book.midPrice());  // 50000.5

// Get top 5 levels from each side
const { bids, asks } = book.depth(5);

// Volume at top of book
console.log(book.bidVolume(10)); // total bid size in top 10 levels
console.log(book.askVolume());   // total ask size across all levels

// Sorted level arrays
book.bids; // OrderBookLevel[] sorted desc by price (best bid first)
book.asks; // OrderBookLevel[] sorted asc by price (best ask first)

Composability — use as much or as little as you need:

import { DeltaParser, OrderBookStore } from "@plutarc/bitmex";
import type { WsOrderBookL2 } from "@plutarc/bitmex";

// 1. Batteries included — OrderBookStore manages everything
const book = new OrderBookStore();
ws.on("orderBookL2_25", book.update);

// 2. DeltaParser only — feed into your own store (zustand, redux, etc.)
const parser = new DeltaParser<WsOrderBookL2>({ keys: ["id"] });
ws.on("orderBookL2_25", (action, data) => {
  const snapshot = parser.update(action, data);
  useMyStore.setState({ orderbook: snapshot });
});

// 3. Shared DeltaParser + OrderBookStore
const parser = new DeltaParser<WsOrderBookL2>({ keys: ["id"] });
const book = new OrderBookStore({ parser });
ws.on("orderBookL2_25", book.update);
// Access parser.snapshot for raw data, book.bids/asks for sorted view

// 4. Neither — handle raw deltas yourself
ws.on("orderBookL2_25", (action, data) => { /* your own logic */ });

API:

| Method / Property | Returns | Description | |---|---|---| | update(action, data) | void | Process a delta message (auto-bound arrow function) | | bids | readonly OrderBookLevel[] | All bids sorted descending by price | | asks | readonly OrderBookLevel[] | All asks sorted ascending by price | | bestBid() | number \| undefined | Highest bid price | | bestAsk() | number \| undefined | Lowest ask price | | spread() | number \| undefined | Best ask minus best bid | | midPrice() | number \| undefined | Average of best bid and best ask | | depth(n) | { bids, asks } | Top N levels from each side | | bidVolume(n?) | number | Total bid size (optionally top N levels only) | | askVolume(n?) | number | Total ask size (optionally top N levels only) | | clear() | void | Reset the book to empty |

Authentication Helpers

For advanced usage:

import { sign, getSignedHeaders, signWs } from "@plutarc/bitmex";

// REST API signature
const sig = sign(apiSecret, "GET", "/api/v1/order", expires, "");

// Full signed headers
const headers = getSignedHeaders(apiKey, apiSecret, "GET", "/api/v1/order", "");

// WebSocket auth signature
const wsSig = signWs(apiSecret, expires);

Error Handling

All REST errors throw typed error classes:

import { BitMEXApiError, BitMEXRateLimitError } from "@plutarc/bitmex";

try {
  await client.order.place({ /* ... */ });
} catch (err) {
  if (err instanceof BitMEXRateLimitError) {
    console.log(`Rate limited — retry after ${err.retryAfterMs}ms`);
  } else if (err instanceof BitMEXApiError) {
    console.log(`API error ${err.status}: ${err.errorMessage}`);
    console.log(`Retryable: ${err.retryable}`);
  }
}

Retryable errors (automatically retried up to maxRetries times):

  • 429 Too Many Requests
  • 5xx Server errors
  • Network/timeout errors

Non-retryable errors (thrown immediately):

  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found

Rate Limiting

The client self-throttles to stay within BitMEX rate limits:

  • Default: 100 requests/minute (BitMEX limit is 120)
  • Tracks the x-ratelimit-remaining response header from BitMEX
  • Pauses requests when approaching the limit (5-request buffer)
  • Configurable via rateLimitPerMinute option

Testnet

All clients support testnet with a single flag:

// REST
const client = new BitMEXClient({ testnet: true });
// → https://testnet.bitmex.com

// WebSocket
const ws = new BitMEXPublicWs({ testnet: true });
// → wss://ws.testnet.bitmex.com/realtime

Get testnet API keys at testnet.bitmex.com.

Custom Logger

Pass any object implementing { debug, info, warn, error } methods. By default the client is silent.

// Use console
const client = new BitMEXClient({ logger: console });

// Use pino
import pino from "pino";
const client = new BitMEXClient({ logger: pino({ name: "bitmex" }) });

// Use winston
import winston from "winston";
const client = new BitMEXClient({ logger: winston.createLogger({ /* ... */ }) });

Development

# Install dependencies
bun install

# Type check
bun run typecheck

# Run tests
bun test

# Build
bun run build

# Lint & format
bun run lint
bun run format

Resources

License

MIT — see LICENSE for details.