🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@pear-protocol/exchanges-sdk

Package Overview
Dependencies
Maintainers
4
Versions
37
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@pear-protocol/exchanges-sdk

Pear Protocol Exchanges SDK

npmnpm
Version
0.5.0
Version published
Weekly downloads
667
83.24%
Maintainers
4
Weekly downloads
 
Created
Source

@pear-protocol/exchanges-sdk

Unified SDK for account state (balance, positions, per-asset leverage) across supported derivative exchanges — as a live WebSocket subscription or a stateless one-shot snapshot.

Supported Exchanges

ExchangeConnectorMarket
BinancebinanceUSDM Futures
BybitbybitLinear Perpetuals
HyperliquidhyperliquidPerpetuals
LighterlighterPerpetuals
OKXokxLinear SWAP

Installation

npm install @pear-protocol/exchanges-sdk

Quick Start

import PearSDK from '@pear-protocol/core-sdk';
import { ExchangesSDK } from '@pear-protocol/exchanges-sdk';

const sdk = new PearSDK({ /* ... */ });
const exchanges = new ExchangesSDK({
  sdk,
  options: {
    builderAddress: '0x...',
    builderFeeTenthsBps: 60,
    integratorAccountIndex: 1234,
    integratorMinPerpsMakerFee: 600,
    integratorMinPerpsTakerFee: 600,
  },
});

const connection = await exchanges.connect(tradeAccountId);

const status = await exchanges.status(connection);
if (status.status === 'warning') {
  console.warn(status.reasons);
}

const tracker = exchanges.createTracker(connection);
const syncer = exchanges.createSyncer(connection);
await tracker.start();

const offBalance = tracker.trackBalance((balance) => {
  if (!balance) return; // unsupported/unrepresentable account mode
  console.log(balance.totalEquity, balance.unrealizedPnl);
});

const offPositions = tracker.trackPosition((positions) => {
  for (const p of positions) console.log(p.symbol, p.side, p.size);
});

// Cleanup
offBalance();
offPositions();
await tracker.disconnect();

Pass demo: true to target each exchange's test or simulated-trading environment:

const exchanges = new ExchangesSDK({ sdk, demo: true });

Hyperliquid and Lighter public REST/WebSocket reads are routed to testnet. Lighter operations that require a backend-generated auth token or signature currently fail closed in demo mode because the existing supporting API signs for mainnet only; public snapshots and tracking remain available, without authenticated account-tier enrichment.

Connection

connect(tradeAccountId) fetches decrypted credentials and the trade account from the core API. It does not open a socket — the exchange WebSocket is created lazily when you call tracker.start() and torn down by tracker.disconnect(). Syncers perform one-shot REST reads. For an account snapshot with no socket, use snapshot().

The connector comes from the backend trade account.

const connection = await exchanges.connect(tradeAccountId);

connection.connector; // "hyperliquid"
connection.account.exchangeIdentifier;
connection.credentials;

For exchanges that need account health checks, pass status options when creating the SDK:

const exchanges = new ExchangesSDK({
  sdk,
  options: {
    builderAddress: '0x...',
    builderFeeTenthsBps: 60,
    integratorAccountIndex: 1234,
    integratorMinPerpsMakerFee: 600,
    integratorMinPerpsTakerFee: 600,
  },
});

Hyperliquid status requires the builder address and the minimum approved fee in tenths of a basis point. Lighter status requires the integrator account and minimum maker/taker approval caps in Lighter fee ticks (parts per million). These thresholds must match the fees configured by the trading backend.

Historical Hyperliquid fill synchronization uses the exchange's globally unique time:coin:tid identity. Existing consumers that persisted bare tid identifiers must account for the transition overlap. Synchronization also fails closed when the exchange reports a maker rebate: the current core external-fill contract accepts only non-negative fees, so the SDK will not silently turn a rebate into a cost or advance the synchronization watermark past it.

Account Status

Use status(connection) to check whether the account is ready for trading through PEAR.

const status = await exchanges.status(connection);

if (status.status === 'warning') {
  for (const reason of status.reasons) {
    console.warn(reason);
  }
}

Possible warning reasons:

ReasonMeaning
api_key_invalidAPI credentials failed the exchange account check
hyperliquid_api_wallet_invalidHyperliquid agent wallet is missing or invalid
hyperliquid_builder_fee_not_approvedHyperliquid builder fee approval is missing
hyperliquid_account_mode_check_failedHyperliquid account mode could not be read
hyperliquid_account_mode_unsupportedPortfolio Margin or deprecated DEX Abstraction cannot be valued safely
lighter_api_key_invalidLighter API key cannot create an auth token
lighter_integrator_not_approvedLighter integrator approval is missing

One-Shot Snapshot (no WebSocket)

When you only need the current balance and positions once — a request/response read rather than a live subscription — use snapshot(). It fetches over REST, opens no WebSocket, and leaves nothing running.

const { balance, positions } = await exchanges.snapshot(tradeAccountId);

balance?.totalEquity;
balance?.availableToTrade;
for (const p of positions) {
  console.log(p.symbol, p.side, p.leverage, p.liquidationPrice);
}

snapshot() returns the same AccountBalance / AccountPosition shapes the live tracker produces — { balance: AccountBalance | null; positions: AccountPosition[] } — so either path can feed the same consumer. balance is null when the account type cannot be represented safely. Hyperliquid currently supports Standard and Unified calculations; Portfolio Margin and deprecated DEX Abstraction fail closed and are surfaced by account status.

There is nothing to tear down: no disconnect(), no socket. This is the read a stateless caller (such as an MCP tool) wants.

Hyperliquid Unified balances come from the spot clearinghouse state and use the documented maximum per-collateral maintenance ratio. Standard balances use per-DEX margin summaries, including isolated positions.

Per-Asset Settings: snapshotAssets()

snapshot() carries leverage only for open positions. To read the currently configured leverage and margin mode for any asset — positioned or not — use snapshotAssets().

const assets = await exchanges.snapshotAssets(tradeAccountId, ['BTC', 'ETH']);

assets.BTC?.leverage; // e.g. '20'
assets.BTC?.marginType; // 'cross' | 'isolated'
assets.ETH; // null when the exchange has no readable setting for that asset

It returns Record<string, TrackedAssetInfo | null> keyed by the requested asset id — the same TrackedAssetInfo shape the live trackAsset() path emits, and the same per-exchange identifier it uses (Hyperliquid coin, Binance/Bybit symbol, OKX instId, Lighter market id).

The read is batch-first: each connector services the whole list with as few requests as its exchange allows.

ExchangeRequests for N assetsHow
Binance1all-symbols positionRisk
Lighter2account read + market defaults
OKXceil(N/20)leverage-info accepts 20 instIds per call
Bybit2 + ksettleCoin position lists; per-symbol fill-in for the k non-positioned symbols (direct per-symbol reads when N ≤ 2)
HyperliquidN (parallel)activeAssetData info request is per-coin (unauthenticated, cheap)

For Lighter, a custom margin setting on a market with no open position is not exposed by the REST account read, so it reports the market default — the same figure the live path shows before a position exists.

Tracking

Each track* method returns an unsubscribe function. Callbacks fire with the current snapshot on subscribe (if available) and on every update.

Balance

tracker.trackBalance((balance) => {
  if (!balance) return; // a live mode change can explicitly invalidate the cached balance
  balance.totalEquity;
  balance.walletBalance;
  balance.unrealizedPnl;
  balance.availableToTrade;
  balance.initialMarginUsed;
  balance.maintenanceMargin;
  balance.marginRatio;
  balance.accountType;
  balance.accountTier; // Lighter account tier when reported

  for (const a of balance.assets) {
    a.asset;         // "USDT", "USDC", ...
    a.free;
    a.used;
    a.total;
    a.usdValue;
  }
});

Positions

tracker.trackPosition((positions) => {
  for (const p of positions) {
    p.symbol;
    p.side;             // "long" | "short" | "both"
    p.size;
    p.entryPrice;
    p.unrealizedPnl;
    p.leverage;
    p.marginType;       // "cross" | "isolated"
    p.liquidationPrice; // string | null
  }
});

Closed positions (size === '0') are dropped automatically.

Per-Asset Leverage & Margin

tracker.trackAsset('ETH', (info) => {
  info.coin;
  info.leverage;
  info.marginType;
});

Asset symbol format per exchange:

ExchangeFormatExample
Binance<BASE><QUOTE>BTCUSDT
Bybit<BASE><QUOTE>BTCUSDT
Hyperliquidbase coinBTC
Lighterbase coinBTC
OKXinstIdBTC-USDT-SWAP

Cached Reads (live tracker)

Synchronous getters for the latest cached state on a running tracker:

tracker.getBalance();            // AccountBalance | null
tracker.getPositions();          // AccountPosition[]
tracker.getTrackedAsset('ETH');  // TrackedAssetInfo | null
tracker.isConnected;
tracker.isInitialized;           // true after first balance/position state (asset metadata alone does not count)

Account Type Labels

balance.accountType is an exchange-specific enum. Render with:

import { ACCOUNT_TYPE_LABELS } from '@pear-protocol/exchanges-sdk';

ACCOUNT_TYPE_LABELS[balance.accountType]; // "Cross Margin", "Portfolio Margin", ...

Cleanup

await tracker.disconnect(); // closes the WebSocket opened by start()

Credentials and the trade account are fetched automatically on connect; Hyperliquid and Lighter account identifiers come from connection.account.exchangeIdentifier.

FAQs

Package last updated on 21 Jul 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts