Architecture
One broker connection fans out to N HTTP/SSE consumers. The subscribed symbol set is configuration, not demand-driven, so reconnect simply re-sends the same list.
No Twisted
The published ctrader-open-api client is built on Twisted’s reactor, which cannot
share a process with uvicorn’s asyncio loop. This service does not depend on that
package at all: the schemas are vendored in proto/ and compiled locally, and the
wire protocol is implemented directly on asyncio — 4-byte big-endian length
prefix, ProtoMessage envelope, clientMsgId correlation, 5-second heartbeat.
Twisted is therefore absent from the dependency tree entirely, not merely unused.
tests/test_proto.py asserts "twisted" not in sys.modules so it cannot creep
back in.
The practical payoff: the sample client’s callback state machine becomes
straight-line awaits in session.py.
Backpressure
One broker connection fans out to N SSE subscribers through hub.py. Each
subscriber has a bounded queue (SUBSCRIBER_QUEUE_SIZE, default 256) and
publishing is synchronous and non-blocking — on overflow the oldest tick is
dropped, because a newer quote supersedes a stale one. A wedged or slow SSE client
can therefore never stall the reader loop.
Drops are counted per subscriber, logged on first occurrence, and reported in the
periodic status event so a consumer can tell it fell behind.
Subscriptions
The service subscribes to every symbol in SYMBOLS at startup, not on demand. The
subscribed set is a pure function of configuration rather than of live HTTP
connections, which is what makes reconnect trivially correct — it just re-sends
the same list — and keeps /v1/market-data/tick an O(1) cache read instead of a
subscribe-wait-unsubscribe round trip.
The stream replays the last known tick for each requested symbol on connect, so a client joining mid-session does not wait for the next quote on a quiet instrument.