Context
Built in 2022 while I was going deep on event-driven Django — a deliberately compact service that answers one question well: how do you push live market data to connected clients without every client hammering an external API?
The problem
A user wants to watch a crypto trading pair and know whether the latest one-minute candle closes above or below a target price. The naive approach — every browser polling the exchange — multiplies identical requests and hits rate limits fast. The interesting version moves the scheduling server-side and pushes results out.
How it works
- The browser opens a WebSocket (Django Channels) and submits a symbol and target price.
- The consumer dynamically registers a periodic Celery Beat task bound to that connection's channel group — the schedule is data, created and owned by the connection rather than hardcoded.
- Every minute, a Celery worker fetches the latest one-minute kline from the Binance API and classifies the close price against the target.
- The verdict flows through the Redis channel layer to the WebSocket group and lands in the browser as a push message — no client polling anywhere.
- The tradable-symbol list is cached in Redis and warmed on first use, so the UI never waits on the exchange for static data.
Decisions and honest limits
- Scheduling as data. Using
django-celery-beat's database-backed schedules means tasks can be created per-connection at runtime — a pattern that scales conceptually to per-user alerting systems. - Clean separation of concerns. Transport (Channels), work (Celery), and state (Redis) each stay in their lane; the WebSocket consumer never talks to Binance directly.
- Known limitations, by design of its scope: it's a single-app demo — a production version would deduplicate tasks per symbol, clean up schedules on disconnect, and add retry/backoff around the exchange client. Recognizing those gaps is part of what the project taught me.
What it demonstrates
The skeleton of a message-driven system — socket ingress, scheduled background work, queue-based fan-out — which is exactly the shape of the real-time market-data pipeline I later built professionally at Mahda.