@pear-protocol/market-sdk
v0.0.7
Published
Pear Protocol Market SDK
Readme
@pear-protocol/market-sdk
SDK for real-time market data across cryptocurrency derivative exchanges. Provides charting with multiple visualization modes (weighted ratio, price ratio, performance), aggregated order book depth, and historical funding rate analysis through a unified, exchange-agnostic interface.
Supported Exchanges
| Exchange | Connector Name |
|----------|---------------|
| Binance | binance |
| Bybit | bybit |
| Hyperliquid | hyperliquid |
| OKX | okx |
| Lighter | lighter |
Installation
npm install @pear-protocol/market-sdkQuick Start
import { Chart, FundingRate, Orderbook, CreateTransport } from '@pear-protocol/market-sdk';
const transport = CreateTransport('hyperliquid');
// Chart
const chart = new Chart({
transport,
longTokens: [{ symbol: 'BTC', weight: 50 }, { symbol: 'ETH', weight: 50 }],
shortTokens: [{ symbol: 'SOL', weight: 100 }],
candleInterval: '1h',
});
const bars = await chart.getBars('weighted-ratio', startTime, endTime);
const subId = chart.subscribeRealtimeBars('weighted-ratio', (bar) => {
console.log(bar.time, bar.open, bar.high, bar.low, bar.close);
});
// Funding rates
const fundingRate = new FundingRate({
transport,
longTokens: [{ symbol: 'ETH', weight: 0.6 }],
shortTokens: [{ symbol: 'BTC', weight: 0.4 }],
interval: '1d',
});
const rates = await fundingRate.getAssetRates('ETH');
const basketRates = await fundingRate.getBasketRates();
// Order book
const orderbook = new Orderbook({
transport,
symbol: 'BTC',
aggregation: 10,
depth: 15,
});
orderbook.subscribe((snapshot) => {
console.log('bids:', snapshot.bids, 'asks:', snapshot.asks);
});
// Cleanup
chart.unsubscribeRealtimeBars(subId);
chart.destroy();
fundingRate.destroy();
orderbook.destroy();
transport.destroy();Chart
Initialization
import { Chart, CreateTransport } from '@pear-protocol/market-sdk';
const transport = CreateTransport('binance');
const chart = new Chart({
transport,
longTokens: [{ symbol: 'BTC', weight: 60 }, { symbol: 'ETH', weight: 40 }],
shortTokens: [{ symbol: 'SOL', weight: 100 }],
candleInterval: '1h',
});ChartConfig
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| transport | Transport | — | WebSocket transport instance |
| longTokens | TokenSelection[] | [] | Tokens on the long side with weights |
| shortTokens | TokenSelection[] | [] | Tokens on the short side with weights |
| candleInterval | CandleInterval | '1h' | Candle timeframe |
TokenSelection
{ symbol: string; weight: number }CandleInterval
'1m' | '3m' | '5m' | '15m' | '30m' | '1h' | '2h' | '4h' | '8h' | '12h' | '1d' | '3d' | '1w' | '1M'Updating Tokens and Interval
Change the tokens or candle interval at any time. Both clear cached data and baseline prices.
chart.setTokens(
[{ symbol: 'BTC', weight: 100 }],
[{ symbol: 'ETH', weight: 100 }],
);
chart.setCandleInterval('15m');Fetching Historical Bars
Fetch historical OHLC bars for a given chart type and time range.
const bars = await chart.getBars('weighted-ratio', startTime, endTime);
for (const bar of bars) {
bar.time; // timestamp (ms)
bar.open; // open price
bar.high; // high price
bar.low; // low price
bar.close; // close price
}Chart Types
| Type | Description |
|------|-------------|
| 'weighted-ratio' | Geometric mean ratio using token weights as exponents |
| 'price-ratio' | Weighted sum of long prices divided by weighted sum of short prices |
| 'performance' | Percentage performance relative to the earliest bar in the range |
Single-Asset Bars
Fetch bars for one specific symbol in the configured token set.
const bars = await chart.getAssetBars('BTC', startTime, endTime);Subscribing to Real-Time Bars
Subscribe to live bar updates. The WebSocket connection is established automatically on the first subscription.
const subId = chart.subscribeRealtimeBars('weighted-ratio', (bar) => {
console.log(bar.time, bar.open, bar.high, bar.low, bar.close);
});
// Single-asset real-time bars
const assetSubId = chart.subscribeRealtimeAssetBars('ETH', (bar) => {
console.log(bar.time, bar.close);
});
// Stop receiving updates
chart.unsubscribeRealtimeBars(subId);
chart.unsubscribeRealtimeBars(assetSubId);Data Boundary
Get the earliest timestamp for which historical data is available.
const boundary = chart.getEffectiveDataBoundary(); // number | nullCache Management
// Clear all cached historical data and baseline prices
chart.clearCache();Cleanup
chart.destroy();Order Book
Initialization
The Orderbook connects to the exchange WebSocket immediately on construction and begins receiving depth updates.
import { Orderbook, CreateTransport } from '@pear-protocol/market-sdk';
const transport = CreateTransport('hyperliquid');
const orderbook = new Orderbook({
transport,
symbol: 'BTC',
aggregation: 10,
depth: 15,
});OrderbookConfig
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| transport | Transport | — | WebSocket transport instance |
| symbol | string | — | Trading pair symbol |
| aggregation | number | 0 | Bucket size for price aggregation (0 = no aggregation) |
| depth | number | 10 | Number of price levels per side |
| snapshottedPrice | number | — | Current mid-price for server-side aggregation params |
Subscribing to Updates
Subscribe to real-time order book snapshots. The callback fires on every depth update from the exchange.
const subId = orderbook.subscribe((snapshot) => {
snapshot.symbol; // "BTC"
snapshot.aggregation; // active bucket size
snapshot.ts; // last update timestamp (ms)
for (const bid of snapshot.bids) { // price-descending
bid.price; // price level
bid.size; // base-asset quantity
}
for (const ask of snapshot.asks) { // price-ascending
ask.price;
ask.size;
}
});
// Stop receiving updates
orderbook.unsubscribe(subId);Reading the Current Snapshot
Access the latest order book state without subscribing. Returns null if no data has been received yet.
const snapshot = orderbook.getSnapshot();
if (snapshot) {
console.log(snapshot.bids, snapshot.asks);
}Changing Aggregation
Change the price aggregation bucket size at any time. Listeners are notified immediately with a re-aggregated snapshot.
orderbook.setAggregation(100); // aggregate to $100 buckets
orderbook.setAggregation(0); // disable aggregation
const current = orderbook.getAggregation();Best Bid / Offer (BBO)
Access the current best bid, best ask, spread, and spread percentage. Returns null values when the book has not loaded yet.
const { bestBid, bestAsk, spread, spreadPct } = orderbook.bbo;
// bestBid: "65000.5" | null
// bestAsk: "65001.0" | null
// spread: "0.5" | null
// spreadPct: "0.00077" | nullAvailable Aggregation Levels
Compute recommended aggregation bucket sizes for a given connector and price level.
import { getAvailableAggregations } from '@pear-protocol/market-sdk';
// CEX exchanges (Binance, Bybit, OKX) — uses tick size
const levels = getAvailableAggregations('binance', { tickSize: 0.1, midPrice: 65000 });
// e.g. [0.1, 1, 10, 100, 1000]
// Hyperliquid — uses max decimal places
const levels = getAvailableAggregations('hyperliquid', { maxDecimals: 2, midPrice: 65000 });Updating Snapshotted Price
Update the mid-price used for server-side aggregation parameters (e.g. Hyperliquid).
orderbook.setSnapshottedPrice(65250.5);Cleanup
orderbook.destroy();The underlying transport is owned by the caller and must be destroyed separately.
Funding Rate
Initialization
import { FundingRate, CreateTransport } from '@pear-protocol/market-sdk';
const transport = CreateTransport('hyperliquid');
const fundingRate = new FundingRate({
transport,
longTokens: [{ symbol: 'ETH', weight: 0.6 }],
shortTokens: [{ symbol: 'BTC', weight: 0.4 }],
});FundingRateConfig
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| transport | Transport | — | Transport instance (used to determine the exchange) |
| longTokens | TokenSelection[] | [] | Tokens on the long side with weights |
| shortTokens | TokenSelection[] | [] | Tokens on the short side with weights |
Aggregation and Duration
Aggregation level and lookback duration are passed per call to getAssetRates and getBasketRates.
FundingRateAggregation
'none' | '1d' | '1w' | '1M'When set to 'none', raw funding rates are returned at each exchange's native settlement frequency (1h for Hyperliquid, 8h for Binance/Bybit/OKX). For '1d', '1w', and '1M', rates are summed within each period.
FundingRateDuration
'1w' | '1m' | '1y'The lookback window for fetching data, ending at the current time.
Minimum Duration per Aggregation
To avoid returning too few data points, each aggregation level enforces a minimum duration. If the requested duration is shorter than the minimum, it is automatically bumped up.
| Aggregation | Minimum Duration |
|-------------|------------------|
| 'none' | '1w' |
| '1d' | '1m' |
| '1w' | '1y' |
| '1M' | '1y' |
Per-Asset Funding Rates
Fetch funding rates for a single token.
// Defaults: aggregation='none', duration='1m'
const rates = await fundingRate.getAssetRates('ETH');
// Daily aggregation over the last year
const dailyRates = await fundingRate.getAssetRates('ETH', '1d', '1y');Signature
getAssetRates(
symbol: string,
aggregation?: FundingRateAggregation, // default: 'none'
duration?: FundingRateDuration, // default: '1m'
): Promise<FundingRateEntry[]>Basket Funding Rates
Compute the weighted net funding rate across all configured tokens. Positive rate = user earns funding, negative = user pays.
const basketRates = await fundingRate.getBasketRates();
const monthlyBasket = await fundingRate.getBasketRates('1M', '1y');The net rate at each time point is calculated as:
netRate = sum(shortWeight × rate) - sum(longWeight × rate)Only time points where all tokens have data are included.
Signature
getBasketRates(
aggregation?: FundingRateAggregation, // default: 'none'
duration?: FundingRateDuration, // default: '1m'
): Promise<FundingRateEntry[]>FundingRateEntry
{
time: number; // bucket start timestamp (ms)
rate: number; // summed rate for the bucket (raw rate when aggregation='none')
annualizedRate?: number; // rate annualized to a yearly figure
}For fixed aggregations the annualization multiplier is 365 (1d), 52 (1w), or 12 (1M). For 'none', the multiplier is inferred from the average gap between raw entries (MS_PER_YEAR / avgGapMs); if there are fewer than two entries to infer from, annualizedRate is omitted.
Updating Tokens
fundingRate.setTokens(
[{ symbol: 'ETH', weight: 0.5 }, { symbol: 'SOL', weight: 0.5 }],
[{ symbol: 'BTC', weight: 1.0 }],
);Cleanup
fundingRate.destroy();