Websocket (Python SDK)
The TurbineWSClient provides async WebSocket streaming for real-time orderbook, trade, and quick market updates.
Connection
Create a TurbineWSClient and connect using the async context manager:
import asyncio
from turbine_client.ws import TurbineWSClient
async def main():
ws = TurbineWSClient(host="https://api.turbinefi.com")
async with ws.connect() as stream:
await stream.subscribe("0xMarketId...")
async for msg in stream:
print(f"Type: {msg.type}, Market: {msg.market_id}")
asyncio.run(main())The WebSocket endpoint is wss://<host>/api/v1/stream. HTTP/HTTPS URLs are converted to WS/WSS automatically.
Constructor Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| host | str | Yes | API host URL (HTTP or WebSocket scheme) |
| reconnect | bool | No | Auto-reconnect on disconnect (default: True) |
| reconnect_delay | float | No | Initial reconnect delay in seconds (default: 1.0) |
| max_reconnect_delay | float | No | Max reconnect delay in seconds (default: 60.0) |
Subscriptions
Subscribe to a market to receive all updates (orderbook, trades, cancellations) for that market. Subscriptions are market-level — you subscribe to a market ID, not to individual channels.
# Subscribe
await stream.subscribe("0xMarketId...")
# Unsubscribe
await stream.unsubscribe("0xMarketId...")You can subscribe to multiple markets simultaneously:
market_ids = ["0xMarket1...", "0xMarket2...", "0xMarket3..."]
for market_id in market_ids:
await stream.subscribe(market_id)Convenience Aliases
These are aliases for subscribe() and exist for backwards compatibility:
await stream.subscribe_orderbook(market_id) # same as subscribe()
await stream.subscribe_trades(market_id) # same as subscribe()Receiving Messages
Async Iteration
The primary way to receive messages. Iterates indefinitely until the connection closes:
async with ws.connect() as stream:
await stream.subscribe("0x...")
async for msg in stream:
if msg.type == "orderbook":
ob = msg.orderbook
if ob and ob.bids:
print(f"Best bid: {ob.bids[0].price} (${ob.bids[0].price / 1e6:.4f})")
elif msg.type == "trade":
trade = msg.trade
if trade:
print(f"Trade: {trade.size / 1e6:.2f} @ {trade.price} (${trade.price / 1e6:.4f})")
elif msg.type == "order_cancelled":
print(f"Order cancelled in market {msg.market_id}")
elif msg.type == "quick_market":
qm = msg.quick_market
if qm:
print(f"Quick market update: {qm.market_id}")Single Receive
Receive one frame of messages (the server may batch multiple messages per frame):
messages = await stream.recv()
for msg in messages:
print(f"{msg.type}: {msg.data}")Returns: list[WSMessage] — one or more parsed messages from a single WebSocket frame.
Message Types
All messages are instances of WSMessage or its subclasses. The type field determines the message kind.
| type | Class | Property | Description |
|---|---|---|---|
| "orderbook" | OrderBookUpdate | .orderbook → OrderBookSnapshot | Full orderbook snapshot |
| "trade" | TradeUpdate | .trade → Trade | Trade execution |
| "quick_market" | QuickMarketUpdate | .quick_market → QuickMarket | Quick market state change |
| "order_cancelled" | WSMessage | .data → dict | Order cancellation |
Orderbook Updates
Sent whenever the orderbook changes for a subscribed market. Contains a full snapshot (not a diff).
if msg.type == "orderbook":
ob = msg.orderbook # OrderBookSnapshot
# Bids sorted best (highest) first
for level in ob.bids[:3]:
print(f" BID {level.price} (${level.price / 1e6:.4f}) x {level.size / 1e6:.2f}")
# Asks sorted best (lowest) first
for level in ob.asks[:3]:
print(f" ASK {level.price} (${level.price / 1e6:.4f}) x {level.size / 1e6:.2f}")
# Mid price
if ob.bids and ob.asks:
mid = (ob.bids[0].price + ob.asks[0].price) / 2
print(f" Mid: {mid:.0f} (${mid / 1e6:.4f})")Trade Updates
Sent when a trade is executed in a subscribed market.
if msg.type == "trade":
trade = msg.trade # Trade
outcome = "YES" if trade.outcome == 0 else "NO"
print(f"Trade: {trade.size / 1e6:.2f} {outcome} @ {trade.price} (${trade.price / 1e6:.4f})")
print(f" Buyer: {trade.buyer}")
print(f" Seller: {trade.seller}")
print(f" TX: {trade.tx_hash}")Quick Market Updates
Sent when a quick market's state changes (new market, resolution, etc.).
if msg.type == "quick_market":
qm = msg.quick_market # QuickMarket
print(f"Quick market: {qm.asset} ({qm.market_id})")
print(f" Strike: ${qm.start_price / 1e8:,.2f}")
print(f" Resolved: {qm.resolved}")Connection with Retry
For long-running bots, use connect_with_retry() to handle disconnections with exponential backoff:
ws = TurbineWSClient(
host="https://api.turbinefi.com",
reconnect=True,
reconnect_delay=1.0,
max_reconnect_delay=60.0,
)
stream = await ws.connect_with_retry()
await stream.subscribe("0x...")
async for msg in stream:
# Process messages
passIf the connection fails, it retries with exponential backoff (1s, 2s, 4s, ..., up to 60s). The reconnect=False setting disables retries and raises WebSocketError immediately on failure.
Closing
Close the stream or client explicitly when done:
# Close the stream
await stream.close()
# Close the client
await ws.close()The context manager (async with ws.connect()) handles cleanup automatically.
Complete Example
A bot that monitors a BTC quick market and logs orderbook + trade activity:
import asyncio
from turbine_client import TurbineClient
from turbine_client.ws import TurbineWSClient
async def main():
# Get the active BTC quick market
client = TurbineClient(host="https://api.turbinefi.com", chain_id=137)
qm = client.get_quick_market("BTC")
market_id = qm.market_id
print(f"Monitoring: {market_id}")
print(f"Strike: ${qm.start_price / 1e8:,.2f}")
# Connect to WebSocket
ws = TurbineWSClient(host="https://api.turbinefi.com")
async with ws.connect() as stream:
await stream.subscribe(market_id)
async for msg in stream:
if msg.type == "orderbook":
ob = msg.orderbook
if ob and ob.bids and ob.asks:
spread = ob.asks[0].price - ob.bids[0].price
print(
f"Book: {ob.bids[0].price} (${ob.bids[0].price / 1e6:.4f}) / "
f"{ob.asks[0].price} (${ob.asks[0].price / 1e6:.4f}) "
f"spread={spread}"
)
elif msg.type == "trade":
trade = msg.trade
if trade:
outcome = "YES" if trade.outcome == 0 else "NO"
print(
f"Trade: {trade.size / 1e6:.2f} {outcome} "
f"@ {trade.price} (${trade.price / 1e6:.4f})"
)
elif msg.type == "quick_market":
qm_update = msg.quick_market
if qm_update and qm_update.market_id != market_id:
# New market — switch subscription
await stream.unsubscribe(market_id)
market_id = qm_update.market_id
await stream.subscribe(market_id)
print(f"Switched to new market: {market_id}")
asyncio.run(main())Wire Format
The WebSocket server sends newline-delimited JSON. Each frame may contain one or more JSON objects separated by \n. The client parses these automatically into individual WSMessage objects.
Subscribe message format:
{"type": "subscribe", "marketId": "0x..."}Unsubscribe message format:
{"type": "unsubscribe", "marketId": "0x..."}