Skip to Content
cTrader MarketsArchitecture

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.

Last updated on