Skip to main content

SBE BBO Integration Guide

SBE BBO Integration Guide

Overview

FieldDescription
Topicbooks1
TemplateId1002
FormatSBE binary frame (opcode = 2), little-endian
DepthProvides real-time Level 1 best bid/ask prices and corresponding order sizes
UnitsTimestamps in microseconds (µs), but only accurate to milliseconds. Append 000 to a millisecond timestamp to get the microsecond format. E.g., millisecond timestamp 1726233600001 corresponds to µs timestamp 1726233600001000
Update FrequencyReal-time (pushed on every BBO change)

Connection

  • WebSocket URL: wss://ws.bitget.com/v3/ws/public/sbe
  • Heartbeat: Send text frame "ping" every 30 seconds, server responds with "pong"
  • Message Protocol: JSON text frames during subscription phase; SBE binary frames during market data push phase, distinguished by WebSocket opCode (opCode=1 for text, opCode=2 for binary)

Subscription Flow

1. Send Subscription Request

{
"op": "subscribe",
"args": [
{
"instType": "usdt-futures",
"topic": "books1",
"symbol": "BTCUSDT"
}
]
}

Parameter Description:

ParameterTypeDescription
instTypestringProduct type: spot
usdt-futures
coin-futures
topicstringFixed value: books1
symbolstringSymbol name, e.g. BTCUSDT, ETHUSDT

2. Subscription Confirmation

{
"event": "subscribe",
"arg": {
"instType": "usdt-futures",
"topic": "books1",
"symbol": "BTCUSDT"
}
}

3. Receiving Data

After subscription confirmation, SBE binary frames are pushed in real-time whenever the BBO changes.

4. Unsubscribe

{
"op": "unsubscribe",
"args": [
{
"instType": "usdt-futures",
"topic": "books1",
"symbol": "BTCUSDT"
}
]
}

SBE Message Structure

Best bid/ask price and corresponding size for a specified trading pair.

Price/Size Calculation Formula

actual_value = mantissa × 10^exponent

Example: mantissa = 123456, exponent = -4, represents 12.3456 (actual_value = mantissa × 10 ^ exponent)

Common Message Header (8 bytes)

All SBE messages must include a fixed 8-byte header for parsing and identifying subsequent data.

FieldTypeLength (Byte)Description
blockLengthuint162Root block length
templateIduint162Channel unique identifier, fixed value = 1002
schemaIduint162Schema ID
versionuint162Schema version

Message Field Definitions

#FieldTypeDescription
-messageHeaderCompositeFixed header
1tsuint64Matching engine timestamp
µs timestamp, but only accurate to milliseconds. Append 000 to a millisecond timestamp to get the microsecond format. E.g., millisecond timestamp 1726233600001 → µs 1726233600001000
2bid1Priceint64Best bid price
3bid1Sizeint64Best bid size
4ask1Priceint64Best ask price
5ask1Sizeint64Best ask size
6priceExponentint8Price exponent
7sizeExponentint8Size exponent
8sequint64Depth sequence number, used for message ordering and packet loss detection
100paddinguint8Padding bytes
200symbolvarString[8]Symbol name, UTF-8 format

Binary Layout Overview

┌─────────────┬──────────────┬──────────┐
│ Header (8B) │ Root (56B) │ Symbol │
└─────────────┴──────────────┴──────────┘

Total message size: 8 + 56 + 1 + len(symbol) = approximately 72 bytes


Decoding Example

Raw Binary (hex)

38 00 EA 03 01 00 02 00  <- header: blockLength=56, templateId=1002, schemaId=1, version=2
01 80 C6 A7 86 3E 06 00 <- ts (uint64 LE)
4E 3C 64 00 00 00 00 00 <- bid1Price = 6569038
98 3A 00 00 00 00 00 00 <- bid1Size = 15000
52 3C 64 00 00 00 00 00 <- ask1Price = 6569042
20 4E 00 00 00 00 00 00 <- ask1Size = 20000
FE <- priceExponent = -2
FC <- sizeExponent = -4
01 00 00 00 00 00 00 00 <- seq (uint64 LE) = 1
00 00 00 00 00 00 <- padding6
07 42 54 43 55 53 44 54 <- symbol: length=7, "BTCUSDT"

Decoded JSON

{
"header": {
"block_length": 56,
"template_id": 1002,
"schema_id": 1,
"version": 2
},
"ts": 1700000000000001,
"bid1_price": "65664.78",
"bid1_size": "1.5000",
"ask1_price": "65665.78",
"ask1_size": "2.0000",
"price_exponent": -2,
"size_exponent": -4,
"seq": 1,
"symbol": "BTCUSDT"
}

Python Integration Example

"""Bitget books1 (BBO) SBE WebSocket subscription example"""
import asyncio
import json
import struct
from decimal import Decimal
import websockets

WS_URL = "wss://ws.bitget.com/v3/ws/public/sbe"
INST_TYPE = "usdt-futures"
SYMBOL = "BTCUSDT"
TOPIC = "books1"


def decode_bbo(data: bytes) -> dict:
"""Decode BestBidAsk (templateId=1002) SBE frame"""
block_length, template_id, schema_id, version = struct.unpack_from('<HHHH', data, 0)
assert template_id == 1002, f"unexpected templateId: {template_id}"

offset = 8
base = offset
ts, = struct.unpack_from('<Q', data, offset); offset += 8
bid1_price, = struct.unpack_from('<q', data, offset); offset += 8
bid1_size, = struct.unpack_from('<q', data, offset); offset += 8
ask1_price, = struct.unpack_from('<q', data, offset); offset += 8
ask1_size, = struct.unpack_from('<q', data, offset); offset += 8
price_exp, = struct.unpack_from('<b', data, offset); offset += 1
size_exp, = struct.unpack_from('<b', data, offset); offset += 1
seq, = struct.unpack_from('<Q', data, offset); offset += 8

# Skip padding, based on blockLength
offset = base + block_length

# symbol (varString8)
sym_len, = struct.unpack_from('<B', data, offset); offset += 1
symbol = data[offset:offset + sym_len].decode('utf-8')

to_dec = lambda m, e: Decimal(m) * Decimal(10) ** e

return {
"ts": ts,
"bid1_price": str(to_dec(bid1_price, price_exp)),
"bid1_size": str(to_dec(bid1_size, size_exp)),
"ask1_price": str(to_dec(ask1_price, price_exp)),
"ask1_size": str(to_dec(ask1_size, size_exp)),
"price_exponent": price_exp,
"size_exponent": size_exp,
"seq": seq,
"symbol": symbol,
}


async def main():
async with websockets.connect(WS_URL) as ws:
# Subscribe
await ws.send(json.dumps({
"op": "subscribe",
"args": [{"instType": INST_TYPE, "topic": TOPIC, "symbol": SYMBOL}]
}))
print(f"[SUB] {INST_TYPE} {TOPIC} {SYMBOL}")

# Heartbeat
async def ping_loop():
while True:
await asyncio.sleep(20)
await ws.send("ping")
print("[PING] sent")

asyncio.create_task(ping_loop())

async for message in ws:
if isinstance(message, bytes):
try:
msg = decode_bbo(message)
ts_ms = msg['ts'] // 1000
spread = Decimal(msg['ask1_price']) - Decimal(msg['bid1_price'])
print(f"\n[BBO] {msg['symbol']} ts={ts_ms}ms seq={msg['seq']}")
print(f" bid: price={msg['bid1_price']} size={msg['bid1_size']}")
print(f" ask: price={msg['ask1_price']} size={msg['ask1_size']}")
print(f" spread: {spread}")
except Exception as e:
print(f"[ERROR] {e} raw={message.hex()}")
else:
if message == "pong":
print("[PONG] received")
else:
print(f"[TEXT] {message}")


if __name__ == "__main__":
asyncio.run(main())

How was your Reading Experience with us?