@pear-protocol/exchanges-sdk
Unified SDK for real-time account state (balance, positions, per-asset leverage) across supported derivative exchanges.
Supported Exchanges
| Binance | binance | USDM Futures |
| Bybit | bybit | Linear Perpetuals |
| Hyperliquid | hyperliquid | Perpetuals |
| Lighter | lighter | Perpetuals |
| OKX | okx | Linear 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 });
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) => {
console.log(balance.totalEquity, balance.unrealizedPnl);
});
const offPositions = tracker.trackPosition((positions) => {
for (const p of positions) console.log(p.symbol, p.side, p.size);
});
offBalance();
offPositions();
await tracker.disconnect();
Pass demo: true to target testnet:
const exchanges = new ExchangesSDK({ sdk, demo: true });
Connection
connect(tradeAccountId) fetches decrypted credentials and the trade account from the core API, then opens the exchange websocket.
The connector comes from the backend trade account.
const connection = await exchanges.connect(tradeAccountId);
connection.exchange;
connection.account.exchangeIdentifier;
connection.credentials;
connection.ws;
For exchanges that need account health checks, pass status options when creating the SDK:
const exchanges = new ExchangesSDK({
sdk,
options: {
builderAddress: '0x...',
integratorAccountIndex: 1234,
},
});
builderAddress is used to check Hyperliquid builder fee approval. integratorAccountIndex is used to check Lighter integrator approval.
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:
api_key_invalid | API credentials failed the exchange account check |
hyperliquid_api_wallet_invalid | Hyperliquid agent wallet is missing or invalid |
hyperliquid_builder_fee_not_approved | Hyperliquid builder fee approval is missing |
lighter_api_key_invalid | Lighter API key cannot create an auth token |
lighter_integrator_not_approved | Lighter integrator approval is missing |
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) => {
balance.totalEquity;
balance.walletBalance;
balance.unrealizedPnl;
balance.availableToTrade;
balance.initialMarginUsed;
balance.maintenanceMargin;
balance.marginRatio;
balance.accountType;
for (const a of balance.assets) {
a.asset;
a.free;
a.used;
a.total;
a.usdValue;
}
});
Positions
tracker.trackPosition((positions) => {
for (const p of positions) {
p.symbol;
p.side;
p.size;
p.entryPrice;
p.unrealizedPnl;
p.leverage;
p.marginType;
p.liquidationPrice;
}
});
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:
| Binance | <BASE><QUOTE> | BTCUSDT |
| Bybit | <BASE><QUOTE> | BTCUSDT |
| Hyperliquid | base coin | BTC |
| Lighter | base coin | BTC |
| OKX | instId | BTC-USDT-SWAP |
Snapshot Reads
Synchronous getters for the latest cached state:
tracker.getBalance();
tracker.getPositions();
tracker.getTrackedAsset('ETH');
tracker.isConnected;
tracker.isInitialized;
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];
Cleanup
await tracker.disconnect();
connection.ws.destroy();
Credentials and the trade account are fetched automatically on connect; Hyperliquid and Lighter account identifiers come from connection.account.exchangeIdentifier.