Streaming
Follow trades, prices, the order book, the account, the layout and settings live over one WebSocket.
The stream is one WebSocket connection that carries every live update.
Opening it needs the market:read scope.
Connect and read events
import asyncio
from spectre_terminal import Event, Gap, SubscriptionError, Terminal
async def main():
terminal = Terminal.discover()
async with terminal.stream() as stream:
trades = await stream.subscribe("trades", "binance:futures:BTCUSDT")
async for frame in stream:
match frame:
case Event(sub=trades.sub):
for trade in frame.data:
print(trade.side, trade.price, trade.qty)
case Gap():
print("lost trades", frame.from_seq, frame.to_seq)
case SubscriptionError():
print("subscription removed", frame.code)
asyncio.run(main())The loop yields events, gaps and subscription errors. An event carries sub,
seq, ts and data. sub is the id of the subscription, and the shape of
data depends on the topic. Every frame and field is in the
stream reference.
Subscribe to a topic
trades = await stream.subscribe("trades", "binance:futures:BTCUSDT")
book = await stream.subscribe("book.top", "binance:futures:BTCUSDT", depth=5, interval_ms=200)
fills = await stream.subscribe("account.fills", connection_id=12)| Topic | What an event carries | What it needs |
|---|---|---|
trades | New trades, in order | Instrument, market:read |
ticker | The 24-hour ticker | Instrument, market:read |
mark_price | The mark price | Instrument, market:read |
book.top | The top of the order book | Instrument, market:read |
clusters | A volume cluster bucket that changed | Instrument, market:read |
account.orders | Orders and trigger orders of one instrument | Connection id, account:read |
account.positions | The position of one instrument | Connection id, account:read |
account.balances | The balance of one currency | Connection id, account:read |
account.fills | One fill | Connection id, account:read |
workspace | The whole layout of the windows | workspace:read |
settings | One settings row that was written | settings:read |
A refused request raises StreamRequestError with a code from
Errors. Data of the Moscow Exchange (MOEX) is not served.
book.top takes two optional parameters. depth is the number of levels on
each side: from 1 to 200, 50 by default. interval_ms is the shortest time
between two frames: 50 or more, 100 by default. Values out of range are
refused with invalid_request.
workspace sends the current layout as its first frame. Account topics and
settings send only changes, so read the current state first. See
Account and Settings.
Handle a gap
When your program reads slower than the data arrives, the terminal drops the frames that do not fit.
| Topic | Sign of a loss | What to do |
|---|---|---|
trades | A gap frame before the next event | Read the trades again with since_ts |
account.fills | seq skips a number | Read the fills again with since_ts |
settings | seq skips a number | Read the settings again |
| All others | None | Nothing. The next frame is the newest state, and skipped seq numbers are normal |
Pass the time of the last trade or fill you have. The argument is since_ts
in Python and sinceTs in TypeScript.
Handle a removed subscription
The terminal removes a subscription when the exchange refuses it or its
connection is switched off. The loop then yields a subscription error with
sub, code and message. Subscribe again when the cause is gone.
Reconnect
import asyncio
from spectre_terminal import StreamClosed, Terminal
async def main():
terminal = Terminal.discover()
while True:
try:
async with terminal.stream() as stream:
await stream.subscribe("trades", "binance:futures:BTCUSDT")
async for frame in stream:
print(frame)
except StreamClosed as closed:
print("stream closed", closed.code)
await asyncio.sleep(1)
asyncio.run(main())When the terminal closes the stream, the loop raises StreamClosed in Python
and StreamClosedError in TypeScript. Subscriptions do not survive, so
subscribe again after you reconnect.
The close code tells why the stream was closed, see Stream close codes.
Unsubscribe and close
await stream.unsubscribe(trades)
await stream.close()unsubscribe removes one subscription at once. Closing the stream removes
all of them and ends the loop without an error.
Limits
One token can hold 8 stream connections, and one connection 100 subscriptions. The API can open 50 instruments that are not open in a terminal window, at most 5 new ones a second.
Over a limit, subscribe fails with rate_limited. Wait a moment and try
again. See Limits.