Engineering
53 exchange feeds, 15 of them streaming
Jordan Ellis
Senior Product Marketing Manager, CoinBeacon
We were about to publish a post called “sub-second alerts across 60+ exchanges”. Neither number survived being checked, so here is the real shape of the pipeline instead — which is a better story than the one we nearly told.
There are two alert pipelines
Conflating them is exactly how a “sub-second” claim gets made. There is an event-driven path and a scheduled path, and they share only the delivery tail.
The event-driven path handles one alert type: a plain price alert, set once, that disarms after it fires. If you asked for repeated crossings instead, your alert carries an arm-and-cooldown state machine, and that belongs to the scheduler. So the fast path is real, and it is narrow.
What a price tick travels through
Each venue has its own adapter. Prices are conflated before they are published — a price identical to the last one is dropped, with a 25-second heartbeat so a quiet book still proves it is alive — and the streaming path is micro-batched into a latest-per-symbol buffer flushed every 250 milliseconds as a single write to a Redis stream.
A single arbitrator process is the only writer of prices. Streaming writes are accepted unconditionally; a REST price is rejected if a streamed price for that symbol arrived in the last fifteen seconds, which is what stops a 30-second poll from stamping over a live tick. Downstream of that write, changed prices go onto a second stream that only the alert evaluator reads.
The evaluator reads that stream in batches and does one cheap thing first: it asks Redis how many alerts are registered against each symbol in the batch. Symbols nobody is watching cost a single round trip and are discarded. Only the survivors are loaded and evaluated, and a trigger is claimed with a conditional update so two workers cannot fire the same alert twice.
End to end on that path, the exchange-tick-to-alert-stream leg measured 221, 255, 270, 550, 1092milliseconds across five consecutive samples. That is before evaluation, before the notification queue, and before your email provider or Telegram accepts the message. “About a second to Telegram for a one-shot price alert” is defensible. “Sub-second alerts” as a blanket claim is not.

53 feeds, 15 streaming
The interesting number is not the total. It is the split. There are 53 exchanges configured, all of them enabled; 49 are carrying live price keys. Of those, fifteen have a registered websocket adapter, plus Kraken on an older streaming path — call it sixteen. Every other venue is polled: a whole-venue ticker endpoint every thirty seconds, or every ten for a dozen of them.
That distinction matters more to you than the headline count does. An alert on a streaming venue can fire about a second after the tick. An alert on a polled venue cannot beat the poll, no matter what the marketing says — worst case, your price moved twenty-nine seconds before we knew about it.
Everything else runs on a clock
The scheduler carries twenty-six cron jobs. Percentage, periodic and funding-rate alerts run once a minute. The eleven indicator jobs each run once a minute too, hand-staggered across the seconds so they never collide, and they read stored candles — which is why they exist only on the venues we ingest candles for, and why they fire on candle close rather than on tick.
One detail is worth publishing because it surprised us. The price cron is written to fire every ten seconds, and when the workers run split by role it does not do ten-second work. The scheduler enqueues rather than evaluates, and that enqueue is deduplicated by a key with a sixty-second lifetime. Measured over about forty-eight minutes on a running stack: 289 cron fires produced 44 enqueues and 244 skips. The cron price path effectively runs about once a minute, not six times.
Delivery: four channels, one at a time
Every trigger claims one of your daily notification slots atomically — the gate and the count are a single statement, so a burst cannot overshoot the cap — then pushes a job onto a queue behind an idempotency key. The notification worker reserves jobs one at a time and processes exactly one at a time. No concurrency.
Four channels: email, Telegram, Discord, webhooks. Webhooks are https-only, refuse redirects and are guarded against pointing at internal addresses. Three attempts, five seconds apart. If a channel fails permanently, every alert you have on it is migrated to email and you get one message telling you so, rather than silently receiving nothing.
What we are not claiming
- Not every feed is a stream. Sixteen of fifty-three. The rest are polled, and an alert cannot be faster than its feed.
- Indicator alerts are candle-close events, not tick events. An RSI alert on the 1h fires after the hour closes. That is the correct behaviour for an indicator and it is not a latency bug, but it is not “instant” either.
- The notification worker is deliberately serial. One job at a time is a throughput ceiling we have accepted, because ordering and idempotency were worth more than parallel sends at our volume. If that stops being true, it is the first thing that changes.
- Daily notification caps are real and they apply per user. A noisy alert can exhaust its owner’s budget for the day. That is a design choice, not a fault, and it is why percentage alerts have a cooldown.
None of this makes the product worse than the version we nearly advertised. It makes it checkable. If you set a one-shot price alert on a streaming venue, you should get it in about a second — and if you set an RSI alert on the 4h, you should expect it when the 4h closes, because that is when it becomes true.