Bitget APIBitget API
UTAClassic
Legacy Doc
  • Overview
  • API Documentation
  • WebSocket
  • Agent Hub
  • SDK
  • Changelog
Copied to clipboard
Websocket
Public
Private
Reality
SBE
    SBE IntroductionSBE BBO Integration GuideSBE Level 50 Integration GuideSBE Public Trade Integration Guide
SBE

SBE Public Trade Integration Guide

Overview

FieldDescription
TopicpublicTrade
TemplateId1003
FormatSBE binary frame (opcode = 2), little-endian
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
ContentPublic platform trade records (batch push, a single frame may contain multiple trades)
Update FrequencyReal-time

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

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

Parameter Description:

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

2. Subscription Confirmation

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

3. Receiving Data

After subscription confirmation, SBE binary frames are pushed in real-time whenever a trade occurs. A single frame may contain multiple trades (batch).

4. Unsubscribe

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

SBE Message Structure

Price/Size Calculation Formula

Code
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 = 1003
schemaIduint162Schema ID
versionuint162Schema version

Message Field Definitions

Root block

idFieldTypeDescription
-messageHeaderCompositeFixed header
1priceExponentint8Price exponent
2sizeExponentint8Size exponent
100paddinguint8Padding bytes

Group: trades (id=200)

idFieldTypeDescription
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
2execIduint64Execution ID
3priceint64Trade price mantissa
4sizeint64Trade size mantissa
5sideuint8Trade direction: Buy / Sell
6isRPIuint8 (BooleanType)Retail Price Improvement flag: T / F (since schema version 4)
7stsuint64Stream service push timestamp in microseconds
8categoryuint8Product line: spot / usdt-futures / coin-futures / usdc-futures
50paddinguint8Padding bytes

Data

idFieldTypeDescription
300symbolvarString[8]Symbol name, UTF-8 format

Binary Layout Overview

Code
┌─────────────┬─────────────┬──────────────────────────────────────────┬──────────┐ │ Header (8B) │ Root (8B) │ Trades: GrpHdr(4B) + N×40B │ Symbol │ └─────────────┴─────────────┴──────────────────────────────────────────┴──────────┘

Single trade message size: 8 + 8 + 4 + 40 + 1 + len(symbol) = approximately 69 bytes
N trade message size: 8 + 8 + 4 + 40×N + 1 + len(symbol)


Trade Direction (side)

ValueNameDescription
0BuyActive buy (Taker buyer)
1SellActive sell (Taker seller)

Decoding Example

Raw Binary (single trade, hex)

Code
08 00 EB 03 01 00 04 00 <- header: blockLength=8, templateId=1003, schemaId=1, version=4 FE <- priceExponent = -2 FC <- sizeExponent = -4 00 00 00 00 00 00 <- padding6 28 00 01 00 <- trades group: entryBlockLength=40, numInGroup=1 02 80 C6 A7 86 3E 06 00 <- trades[0].ts (uint64 LE) 0F 27 00 00 00 00 00 00 <- trades[0].execId = 9999 52 39 64 00 00 00 00 00 <- trades[0].price = 6566738 88 13 00 00 00 00 00 00 <- trades[0].size = 5000 00 <- trades[0].side = 0 (Buy) 00 00 00 00 00 00 00 <- padding7 07 42 54 43 55 53 44 54 <- symbol: length=7, "BTCUSDT"

Decoded JSON

Code
{ "header": { "block_length": 8, "template_id": 1003, "schema_id": 1, "version": 4 }, "price_exponent": -2, "size_exponent": -4, "trades": [ { "ts": 1700000000000002, "exec_id": 9999, "price": "65667.38", "size": "0.5000", "side": "Buy", "isRPI": "F", "sts": 1700000000001002, "category": 1 } ], "symbol": "BTCUSDT" }

Batch Trade Decoded JSON (multiple trades)

Code
{ "header": { "block_length": 8, "template_id": 1003, "schema_id": 1, "version": 4 }, "price_exponent": -2, "size_exponent": -4, "trades": [ { "ts": 1700000000000002, "exec_id": 10001, "price": "65665.78", "size": "0.5000", "side": "Buy", "isRPI": "F", "sts": 1700000000001002, "category": 1 }, { "ts": 1700000000000003, "exec_id": 10002, "price": "65666.00", "size": "1.2000", "side": "Sell", "isRPI": "T", "sts": 1700000000001003, "category": 1 }, { "ts": 1700000000000004, "exec_id": 10003, "price": "65664.50", "size": "0.3000", "side": "Buy", "isRPI": "F", "sts": 1700000000001004, "category": 1 } ], "symbol": "BTCUSDT" }

Python Integration Example

Code
"""Bitget publicTrade 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 = "publicTrade" SIDE_MAP = {0: "Buy", 1: "Sell"} BOOLEAN_MAP = {0: "F", 1: "T"} def decode_trade(data: bytes) -> dict: """Decode Trade (templateId=1003) SBE frame""" block_length, template_id, schema_id, version = struct.unpack_from('<HHHH', data, 0) assert template_id == 1003, f"unexpected templateId: {template_id}" offset = 8 base = offset price_exp, = struct.unpack_from('<b', data, offset); offset += 1 size_exp, = struct.unpack_from('<b', data, offset); offset += 1 # Skip padding, based on blockLength offset = base + block_length # trades group entry_bl, num = struct.unpack_from('<HH', data, offset); offset += 4 has_is_rpi = version >= 4 # sinceVersion=4: entry carries the isRPI field (entryBlockLength stays fixed due to padding, so gate on schema version instead) to_dec = lambda m, e: Decimal(m) * Decimal(10) ** e trades = [] for _ in range(num): entry_start = offset ts, = struct.unpack_from('<Q', data, offset); offset += 8 exec_id, = struct.unpack_from('<Q', data, offset); offset += 8 price, = struct.unpack_from('<q', data, offset); offset += 8 size, = struct.unpack_from('<q', data, offset); offset += 8 side_raw, = struct.unpack_from('<B', data, offset); offset += 1 if has_is_rpi: is_rpi_raw, = struct.unpack_from('<B', data, offset); offset += 1 sts, = struct.unpack_from('<Q', data, offset); offset += 8 category, = struct.unpack_from('<B', data, offset); offset += 1 # Skip padding, based on entry_bl offset = entry_start + entry_bl trade = { "ts": ts, "exec_id": exec_id, "price": str(to_dec(price, price_exp)), "size": str(to_dec(size, size_exp)), "side": SIDE_MAP.get(side_raw, str(side_raw)), } if has_is_rpi: trade["isRPI"] = BOOLEAN_MAP.get(is_rpi_raw, str(is_rpi_raw)) trade["sts"] = sts trade["category"] = category trades.append(trade) # symbol (varString8) sym_len, = struct.unpack_from('<B', data, offset); offset += 1 symbol = data[offset:offset + sym_len].decode('utf-8') return { "price_exponent": price_exp, "size_exponent": size_exp, "trades": trades, "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_trade(message) print(f"\n[Trade] {msg['symbol']} ({len(msg['trades'])} trades)") for t in msg['trades']: ts_ms = t['ts'] // 1000 print(f" [{t['side']:4s}] price={t['price']} size={t['size']} " f"ts={ts_ms}ms execId={t['exec_id']}") 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())
SBE Level 50 Integration Guide
On this page
  • Overview
  • Connection
  • Subscription Flow
    • 1. Send Subscription Request
    • 2. Subscription Confirmation
    • 3. Receiving Data
    • 4. Unsubscribe
  • SBE Message Structure
    • Price/Size Calculation Formula
    • Common Message Header (8 bytes)
    • Message Field Definitions
    • Binary Layout Overview
  • Trade Direction (side)
  • Decoding Example
    • Raw Binary (single trade, hex)
    • Decoded JSON
    • Batch Trade Decoded JSON (multiple trades)
  • Python Integration Example
JSON
JSON
JSON
Javascript
Javascript
Javascript
Javascript
Javascript