Skip to Content

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)
TopicWhat an event carriesWhat it needs
tradesNew trades, in orderInstrument, market:read
tickerThe 24-hour tickerInstrument, market:read
mark_priceThe mark priceInstrument, market:read
book.topThe top of the order bookInstrument, market:read
clustersA volume cluster bucket that changedInstrument, market:read
account.ordersOrders and trigger orders of one instrumentConnection id, account:read
account.positionsThe position of one instrumentConnection id, account:read
account.balancesThe balance of one currencyConnection id, account:read
account.fillsOne fillConnection id, account:read
workspaceThe whole layout of the windowsworkspace:read
settingsOne settings row that was writtensettings: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.

TopicSign of a lossWhat to do
tradesA gap frame before the next eventRead the trades again with since_ts
account.fillsseq skips a numberRead the fills again with since_ts
settingsseq skips a numberRead the settings again
All othersNoneNothing. 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.

On this page