Python SDK
Live streams
SahiFeed: the tick stream and order updates, with reconnect and a local tick cache.
SahiFeed connects to the
tick stream
and the order-update stream.
- Callbacks run on the thread that calls
consume(), one at a time. If ticks queue up, only the newest per instrument is delivered; order updates all arrive in order. - Ticks have every field, shaped as the REST endpoints return them: an
unset field is
None, an empty side of the book[], an unset count0. - Reconnects are automatic after a drop, and subscriptions are re-sent.
- Close codes
4401,4403and4429are final:consume()raises. After a new token, build a new feed. - Logging goes to the
sahiapi.sahi.feedandsahiapi.sahi.ws_clientloggers.
Examples use api and instrument_id from Getting started,
and feed from below.
Build a feed#
SahiFeed(api: SahiAPI)
Takes a SahiAPI and copies its token. Nothing connects until the first
subscribe.
Subscribe for last price#
feed.subscribe_ltp(instrument_ids: list[int], on_data_received: Callable[[dict, dict], None] | None = None) -> None
Subscribes instruments at LTP mode. If any id is refused, the whole batch is
refused and your callback gets an error event.
callback(data, meta). meta["type"] is ltp_tick, quote_tick, depth_tick, ack or error. One callback serves all three modes.Subscribe for quotes#
feed.subscribe_quote(instrument_ids: list[int], on_data_received: Callable[[dict, dict], None] | None = None) -> None
As subscribe_ltp, at QUOTE mode: adds volume_today, the price band and
open interest.
Subscribe for depth#
feed.subscribe_depth(instrument_ids: list[int], on_data_received: Callable[[dict, dict], None] | None = None) -> None
As subscribe_ltp, at DEPTH mode: adds best_buys and best_sells.
Unsubscribe#
feed.unsubscribe(instrument_ids: list[int]) -> None
Stops ticks for these instruments and clears their cached values.
Latest cached tick#
feed.last_ltp(instrument_id: int) -> dict | None
The newest LTP tick held for an instrument, or None until one arrives. The
cache fills even without consume(), so you can poll it.
Raises#
SahiFeedNotSubscribedException: not subscribed.- The
SahiFeedExceptionthat closed the stream, once it has.
Latest cached quote#
feed.last_quote(instrument_id: int) -> dict | None
As feed.last_ltp, for QUOTE ticks.
Latest cached depth#
feed.last_depth(instrument_id: int) -> dict | None
As feed.last_ltp, for DEPTH ticks.
Order updates#
feed.subscribe_order_updates(on_data_received: Callable[[dict, dict], None], *, on_connect: Callable[[], None] | None = None) -> None
Streams every order event on the account. Updates can repeat and are not
replayed after a gap, so take a snapshot with get_order_list() in
on_connect and keep the newer last_modified_at per order: see
Stream + Snapshot.
callback(order, meta), one order object per update.Run the callbacks#
feed.consume() -> None
Blocks and runs your callbacks until stop() is called. However it ends, both
streams are stopped.
Raises#
SahiFeedAuthExpiredException: the token expired (4401).SahiFeedForbiddenException: the token lacks the scope (4403).SahiFeedConnectionException: too many connections (4429).- Anything your callback raised.
Stop both streams#
feed.stop() -> None
Closes both connections and unblocks consume(). Safe from any thread.