We built an intelligence API that serves regime, novelty, and correlation data for any of 19,172 symbols in under 120ms using pipeline-batched Redis fetches and a two-round-trip pattern.
<120ms
Cache hit latency
<800ms
Cold fetch latency
2
Redis round trips (SCAN + pipeline)
209,033
Regime keys with TTL fixed
CHAPTER 01
The Argus intelligence layer computed continuously against 670 million rows of market data spanning 19,172 symbols. The output needed to reach two consumers: an internal Apex trading system that required regime data within 100 milliseconds, and an external-facing API serving paying Avo subscribers.
The storage problem was this: regime data for 209,033 Redis keys, novelty scores for tens of thousands of symbols, and correlation pairs for thousands of symbol combinations were computed on a rolling basis and stored in Redis. The API layer needed to resolve arbitrary user-supplied symbols to the correct Redis keys, assemble multi-namespace data into a coherent response, and serve it with appropriate tier-based cache TTLs, all without blocking on N+1 key lookups.
A secondary problem was TTL integrity. The argus-regime service wrote regime hashes to Redis via hset_multiple() but was missing the subsequent EXPIRE call. Without explicit TTLs, 209,033 keys accumulated as permanent entries. A pipeline failure would leave stale regime data in Redis indefinitely, silently serving incorrect regimes to Apex without any staleness signal.
CHAPTER 02
The API was built as a Next.js App Router route at /api/intelligence/[symbol]. It accepted any symbol in several formats: bare tickers, exchange-prefixed crypto pairs, and user-facing shorthand. A normalization step translated these to the Redis key format used internally.
The route performed three parallel SCAN operations against Redis, one per intelligence namespace, then batched all key fetches into a single pipeline. This avoided N individual GET calls and limited the number of round trips to two: one SCAN phase and one pipeline fetch.
Tier-based cache TTL derived the Cache-Control header from the user's tier. Free-tier responses received a shorter s-maxage than paid tiers. This was enforced at the CDN layer without additional server-side logic.
ARCHITECTURE OVERVIEW
PRESENTATION
Next.js 15 (App Router)
API LAYER
Redis 7.2 (SCAN, HGETALL, pipeline)
auth + rate limit + versioning
SERVICES
ClickHouse 26.3
DATABASE
Clerk 6 (tier-based rate limiting)
QUEUE
TypeScript 5.4
CHAPTER 03
Symbol normalization: the normalizeSymbol function translated user-facing symbols to internal Redis key format. BTC-USD and BTC both resolved to BINANCE:BTC-USDT. Equity symbols were passed through unchanged.
Pipeline batching: all resolved keys across all three namespaces were queued in a single Redis pipeline. hgetall for regime keys (stored as hashes), get for novelty and correlation keys (stored as JSON strings). Results were consumed sequentially from the pipeline response array.
The missing EXPIRE call in argus-regime/src/main.rs was identified and patched: a single redis.expire(&redis_key, 86400) call after every redis.hset_multiple(). The remediation of existing keys used a Redis SCAN loop to set a 24-hour TTL on all 209,033 existing permanent keys. Verification confirmed zero keys with a -1 TTL after the remediation.
The route returned 503 if Redis was unavailable. It returned 404 only if all three namespaces returned zero results. Partial responses returned 200 with null for missing fields. This prevented false 404s for symbols that had regime data but were not yet in the novelty pipeline.
TECH STACK
CHAPTER 04
The intelligence API served requests in under 120 milliseconds for cache hits and under 800 milliseconds for cold fetches against a fully populated Redis instance. The two-round-trip pattern reduced Redis connections by approximately 90% compared to individual key lookups.
The 209,033 regime keys with enforced 24-hour TTLs self-expired on pipeline failure rather than accumulating stale state. Before the fix, a 2-day pipeline outage would have left 209,033 stale regime classifications in Redis with no indicator. After the fix, the same outage would result in empty cache returns within 24 hours.
<120ms
Cache hit latency
<800ms
Cold fetch latency
2
Redis round trips (SCAN + pipeline)
209,033
Regime keys with TTL fixed
CHAPTER 05
DECISION · 01
Pipeline-based key fetching is non-negotiable at this key count. A symbol with regime data across 8 timeframes, a novelty score, and 20 correlation pairs would generate 29 individual Redis GET calls without pipelining. Pipeline batching collapsed this to 2 round trips per request regardless of key count.
DECISION · 02
Missing EXPIRE calls are silent data corruption. The regime writer had been in production for weeks before the missing EXPIRE call was identified. There was no error, no warning. The keys simply never expired. Any Redis writer that does not enforce TTLs should be treated as incorrect, not merely incomplete.
DECISION · 03
Normalizing user-facing symbols to internal key formats should be centralized. The first version required callers to know the internal format. This was a leaky abstraction that broke every time the internal key schema changed. The normalizeSymbol function was extracted as a standalone module once the pattern appeared in three different routes.
START A PROJECT
We build fast. Most projects ship in under two weeks. Start with a free 30-minute discovery call.
Start a ProjectWe launched a multi-tenant market intelligence SaaS serving computed signals from 425M rows, with all API routes under 500ms cold and unit economics positive from customer one.
425M+ ClickHouse rows at launch
Read case study →
PlatformsWe debugged 65 compounding bugs across seven subsystems of a live trading engine, fixed a score overflow that silently blocked all dark_matter_rs signals, and cut Redis memory from 11.8GB to 7.15GB.
65 Bugs fixed in one session
Read case study →
PlatformsWe built a retail investor dashboard serving live fund performance from a paper trading account, with compliance banners enforced as server-side dependencies and JavaScript bundle under 120KB.
7 Pages built and deployed
Read case study →