Skip to main content

Ticker Stream

Get 24-hour statistics updated every 200ms.
{
  "method": "subscribe",
  "subscription": [{
    "type": "ticker",
    "symbol": "BTC-USD"
  }]
}
Update Frequency: Every 200ms Ticker Fields:
FieldDescription
priceChangeAbsolute price change over 24h
priceChangePercentPercentage price change over 24h
lastPriceLast traded price
highPrice24h high
lowPrice24h low
volume24h base asset volume
quoteVolume24h quote asset volume
markPriceCurrent fair/mark price
oraclePriceOracle-reported price
openInterestTotal open interest
fundingRateCurrent funding rate
regimeMarket regime indicator (-1, 0, 1)
regimeDtRegime duration in 10s intervals
regimeVolRegime-adjusted volatility
regimeMvRegime mean value
fairBookPxFair price derived from order book
fairVolFair volatility estimate
fairBiasFair price bias
timestampTimestamp (nanoseconds)

Candles Stream

Real-time candlestick data with historical backfill.
{
  "method": "subscribe",
  "subscription": [{
    "type": "candle",
    "symbol": "BTC-USD",
    "interval": "1m"
  }]
}
Supported Intervals: 10s, 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M Initial Response: Up to 5000 historical candles Updates: Real-time (10s instant; larger intervals aggregated ~10–20s) Candle Fields:
FieldDescription
tOpen timestamp (milliseconds)
TClose timestamp (milliseconds)
oOpen price
hHigh price
lLow price
cClose price
vVolume
nNumber of trades

Trades Stream

Real-time trades feed
{
  "method": "subscribe",
  "subscription": [{
    "type": "trades",
    "symbol": "BTC-USD"
  }]
}
Initial Response: None (stateless) Updates: Real-time on every fill Trade Fields:
FieldDescription
sSymbol (market)
pxExecution price
szSize traded
timeTimestamp (milliseconds)
sidetrue if taker bought, false if taker sold
makerMaker public key (base58)
takerTaker public key (base58)
reasonFill reason (optional; only present if not a normal trade, e.g. "liquidation", "adl")
liqLiquidation flag (optional; only present if true)

L2 Snapshot Stream

Periodic full order book snapshots.
{
  "method": "subscribe",
  "subscription": [{
    "type": "l2Snapshot",
    "symbol": "BTC-USD",
    "nlevels": 10,
    "aggregation": 0.5
  }]
}
Parameters:
  • nlevels (optional): Number of price levels per side
  • aggregation (optional): Price bucket size in quote currency
Update Frequency: Every 200ms Structure: levels[0] = bids (descending), levels[1] = asks (ascending)

L2 Delta Stream

Real-time incremental order book updates with initial snapshot.
L2 Delta sends an initial snapshot (latest cached book state) on subscription, then real-time delta updates for every price level change.
{
  "method": "subscribe",
  "subscription": [{
    "type": "l2Delta",
    "symbol": "BTC-USD"
  }]
}
Initial Response: Latest cached book state (if available) Updates: Real-time on every price level change Delta Format:
  • Each delta update contains changes to a single price level
  • Only ONE side (bids or asks) will have levels per update (the other is empty [])
  • levels[0] = bids (highest to lowest)
  • levels[1] = asks (lowest to highest)
  • Each level: {px, sz, n} where n is always 0 for deltas
  • sz: 0 means remove the level

Risk Metrics Stream

Subscribe to risk metrics for a symbol. Risk metrics include maintenance-margin surfaces (buy/sell grids over notional and leverage) and asset correlations.
{
  "method": "subscribe",
  "subscription": [{
    "type": "risk",
    "symbol": "BTC-USD"
  }]
}
Initial Response: Latest cached risk metrics (if available) Updates: Event-driven (only when asset risk changes) Risk Metrics Fields:
FieldDescription
symbolMarket symbol
timestampTimestamp in milliseconds
regimeRisk regime index (-12 to 12)
leverageArray of leverage knot points (e.g. 1.0 … 50.0)
notionalsArray of notional knot points (e.g. 50000 … 10000000)
buy2D array of risk points [notional_idx][leverage_idx] for buy side
sell2D array of risk points [notional_idx][leverage_idx] for sell side
corrsArray of [pair, correlation] tuples (e.g. ["BTC:ETH", 0.71])
Buy/sell point fields (each cell in the grid):
FieldDescription
mmrOStart-of-regime maintenance margin ratio
mmrEEnd-of-regime maintenance margin ratio
pProbability of remaining in the regime
Surface indexing: To get the risk point for a given notional and leverage, find the notional bracket in notionals, the leverage bracket in leverage, then use buy[notional_idx][leverage_idx] or sell[notional_idx][leverage_idx]. Interpolate between surrounding grid points if needed.

Frontend Context Stream

Aggregated market context for all symbols. Ideal for dashboard views.
{
  "method": "subscribe",
  "subscription": [{
    "type": "frontendContext"
  }]
}
Initial Response: Latest cached ticker data for all symbols Updates: Every 2 seconds Context Fields:
FieldDescription
symbolMarket symbol
volume24h trading volume (base currency)
fundingCurrent funding rate
oiOpen interest (base currency)
lastPriceLast traded price
priceChange24h price change (absolute)
priceChangePercent24h price change (percentage)

Multiple Subscriptions

Subscribe to multiple streams at once:
{
  "method": "subscribe",
  "subscription": [
    {"type": "ticker", "symbol": "BTC-USD"},
    {"type": "trades", "symbol": "BTC-USD"},
    {"type": "candle", "symbol": "BTC-USD", "interval": "1m"},
    {"type": "l2Snapshot", "symbol": "BTC-USD", "nlevels": 20},
    {"type": "l2Delta", "symbol": "BTC-USD"},
    {"type": "risk", "symbol": "BTC-USD"},
    {"type": "frontendContext"}
  ]
}
Response:
{
  "type": "subscriptionResponse",
  "topics": [
    "ticker.BTC-USD",
    "trades.BTC-USD",
    "candle.BTC-USD.1m",
    "l2snapshot.BTC-USD",
    "l2delta.BTC-USD",
    "risk.BTC-USD",
    "frontendContext"
  ]
}

Example Implementation

const WebSocket = require('ws');

const ws = new WebSocket('wss://exchange-ws1.bulk.trade');

ws.on('open', () => {
  console.log('Connected to Bulk Exchange');
  
  // Subscribe to multiple streams
  ws.send(JSON.stringify({
    method: 'subscribe',
    subscription: [
      { type: 'ticker', symbol: 'BTC-USD' },
      { type: 'trades', symbol: 'BTC-USD' },
      { type: 'frontendContext' }
    ]
  }));
});

ws.on('message', (data) => {
  const message = JSON.parse(data);
  
  if (message.type === 'subscriptionResponse') {
    console.log('Subscribed to:', message.topics);
    return;
  }
  
  switch(message.type) {
    case 'ticker':
      console.log('Ticker:', message.data.ticker);
      break;
    case 'trades':
      console.log('Trade:', message.data.trades);
      break;
    case 'frontendContext':
      console.log('Context:', message.data.ctx);
      break;
  }
});

ws.on('error', (error) => {
  console.error('WebSocket error:', error);
});