@pear-protocol/market-sdk
v0.3.0
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();Transport
CreateTransport(connector) builds the connection every feature shares. Pass it to Chart,
Orderbook, and FundingRate, then destroy() it once when you are done.
Lighter rate limits
Lighter grants a wider rate limit to requests it can attribute to an account, on public market data
as well as private endpoints. Supply a token provider and this SDK sends it on Lighter's candle and
funding reads (authorization header) and on its candle and order-book subscriptions (auth field):
import { CreateTransport } from '@pear-protocol/market-sdk';
import { retrieveLighterAuthToken } from '@pear-protocol/exchanges-sdk';
const transport = CreateTransport('lighter', {
authToken: () => retrieveLighterAuthToken(sdk, credentials, false),
});This SDK never holds Lighter credentials — the caller mints the token and owns its cache.
retrieveLighterAuthToken is the same cache the account trackers use, so both SDKs share one token.
Everything is optional. Omit authToken, or resolve undefined for a visitor with no connected
account, and every request goes out anonymously. A failed mint logs a warning and falls back to
anonymous rather than breaking the market data.
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();