wss://wss-trading.stockbit.com/ws
Optional query: ?type=chart
Sub-protocol: ["web"]
Requires a WebSocket key obtained first from the REST API (see Authentication).
message WebsocketRequest {
string user_id = 1; // User ID (e.g. "3468390")
WebsocketChannel channel = 2; // Subscription channels
string key = 3; // WebSocket key
PingRequest ping = 4; // PingRequest { string message = 1 } — sends "ping" every ~30s on chart WS
string access_token = 5; // Bearer access token
}
message WebsocketChannel {
repeated string watchlist = 1;
repeated string order_book = 2;
repeated string running_trade = 3;
bool is_hotlist = 4;
repeated string running_trade_batch = 5;
repeated string liveprice = 6;
repeated string iepiev = 7;
repeated string intraday = 8;
repeated string best_bid_offer = 9;
repeated MarketMoverWebsocketRequest market_mover = 10;
repeated OrderQueueWebsocketRequest order_queue = 11;
repeated string liveprice_v3 = 12;
repeated string order_book_v3 = 13;
repeated string intraday_v3 = 14;
}
The server expects two sequential protobuf messages on connect:
user_id + key + access_token, no channel fielduser_id + key + access_token + channel with desired stocksBoth are sent immediately without waiting for a response.
from stockbit_api.proto.stockbit_pb2 import WebsocketRequest
# Step 1: Auth-only (no channel)
auth = WebsocketRequest()
auth.user_id = "3468390"
auth.key = wskey
auth.access_token = "<bearer_token>"
# Step 2: Subscription with channels
sub = WebsocketRequest()
sub.user_id = "3468390"
sub.key = wskey
sub.access_token = "<bearer_token>"
sub.channel.liveprice.extend(["BBRI", "BBCA"])
sub.channel.running_trade_batch.extend(["BBRI"])
sub.channel.order_book.extend(["BBRI"])
await ws.send(auth.SerializeToString())
await ws.send(sub.SerializeToString())
The server uses protobuf field numbers directly in a oneof envelope (not a WebsocketSubResponse with string case):
message WebsocketWrapMessage {
oneof event {
RunningTradeBatch running_trade = 8; // Sub: running_trade_batch
LivePrice liveprice = 9; // Sub: liveprice
OrderbookFeed orderbook = 10; // Sub: order_book
}
}
| Outer Field | Tag Byte | Subscription Channel | Description |
|---|---|---|---|
8 (RunningTradeBatch) |
0x4a |
running_trade_batch (field 5) |
Batched trade executions |
9 (LivePrice) |
0x52 |
liveprice (field 6) |
Real-time stock price tick |
10 (OrderbookFeed) |
0x5a |
order_book (field 2) |
Orderbook snapshot (text format) |
Decode using WhichOneof("event"):
env = WebsocketWrapMessage()
env.ParseFromString(raw)
which = env.WhichOneof("event")
if which == "liveprice":
lp = env.liveprice
print(f"{lp.stock_code}: {lp.lastprice}")
elif which == "running_trade":
for t in env.running_trade.trades:
print(f"Trade {t.stock}: {t.price} {t.volume}")
elif which == "orderbook":
ob = env.orderbook
# Parse pipe-delimited text in ob.orderbook_data
The orderbook data at field 10 wraps a pipe-delimited text format in the orderbook_data field:
message OrderbookFeed {
string symbol = 1;
bytes orderbook_data = 2; // Pipe-delimited text
int64 sequence = 3;
int32 board = 4; // 722 = reguler
string timestamp = 5;
int64 flag = 8;
string timestamp_ns = 9;
}
Format: #O|SYMBOL|OFFER|price;lot;volume|price;lot;volume|...#O|SYMBOL|BID|price;lot;volume|...
def parse_orderbook_text(data: bytes) -> dict:
text = data.decode("utf-8", errors="replace")
entries = text.split("|")
result = {"bid": [], "offer": []}
side = None
for part in entries:
if part in ("#O", "#B"):
side = None
elif part == "OFFER":
side = "offer"
elif part == "BID":
side = "bid"
elif side and ";" in part:
parts = part.split(";")
if len(parts) >= 3:
try:
result[side].append({"price": float(parts[0]), "lot": float(parts[1]), "volume": float(parts[2])})
except ValueError:
pass
return result
?type=chart)URL: wss://wss-trading.stockbit.com/ws?type=chart
Subscription channels: liveprice (field 6) + iepiev (field 7)
sub = WebsocketRequest()
sub.user_id = uid
sub.key = wskey
sub.access_token = token
sub.channel.liveprice.append("BBCA")
sub.channel.iepiev.append("BBCA")
Periodic ping: The chart WS requires client-side pings every ~30 seconds:
ping = WebsocketRequest()
ping.ping.message = "ping" # PingRequest { string message = 1 }
await ws.send(ping.SerializeToString())
The raw ping frame is IgYKBHBpbmc= (base64) / 22 06 0a 04 70 69 6e 67 (hex).
import asyncio, websockets
from stockbit_api.proto.stockbit_pb2 import WebsocketRequest
async def connect(wskey, token, uid, stocks):
auth = WebsocketRequest()
auth.user_id = uid; auth.key = wskey; auth.access_token = token
sub = WebsocketRequest()
sub.user_id = uid; sub.key = wskey; sub.access_token = token
sub.channel.liveprice.extend(stocks)
sub.channel.running_trade_batch.extend(stocks)
sub.channel.order_book.extend(stocks)
ws = await websockets.connect("wss://wss-trading.stockbit.com/ws", subprotocols=["web"])
await ws.send(auth.SerializeToString())
await ws.send(sub.SerializeToString())
async for msg in ws:
# Parse with WebsocketWrapMessage
pass