Interesting approach to tackling the 0.00 issue with a layered fallback system and clear UX cues. How do you see this method adapting when dealing with real-time crypto data that changes multiple times per second?
[DISCUSS] The “Never 0.00” Challenge — design a resilient price pipeline (client → edge) together
Pocket PortfolioLeader
posted
3 min read
0 Comments
Pocket Portfolio
•
Thanks Muzzamil—totally agree: crypto ticks can change many times per second.
Here’s a simple pattern that scales without going HFT and is easy to copy:
TL;DR (3 layers)
Ingest (fast)
- Subscribe to 2–3 WS feeds + keep HTTP as backup.
- Normalize each tick:
{sym, price, ts, src, seq}and store last-known-good (LKG).
Coalesce (edge, 100–250 ms frames)
- Every
frameMspick the freshest tick per symbol from the buffer. - If no new tick for
rotateMs(e.g., 1 s) → rotate provider. - If no tick for
staleMs(e.g., 2 s) → serve LKG withstale:true.
- Every
Deliver (human-speed)
- Send to clients via SSE/WebSocket at ~4–5 Hz (humans can’t read 20 Hz).
- UI shows a tiny “stale” chip if
stale:true. - If gap >10 s, gray out the cell.
This keeps ingestion real-time, but the UI calm and trustworthy.
Drop-in edge sketch (TypeScript)
// constants you can tune per market
const frameMs = 200; // coalesce window (5 Hz)
const rotateMs = 1000; // try next provider if no fresh tick in 1s
const staleMs = 2000; // mark stale after 2s without a tick
type Tick = { sym: string; price: number; ts: number; src: 'p1'|'p2'|'p3'; seq: number };
const buf = new Map<string, Tick>(); // newest per sym during frame
const lkg = new Map<string, Tick>(); // last-known-good
// ingest from multiple WS feeds (pseudo)
function onProviderTick(t: Tick) {
if (!Number.isFinite(t.price)) return;
const prev = buf.get(t.sym);
if (!prev || t.ts > prev.ts) buf.set(t.sym, t); // keep latest in frame
}
// coalesce + provider rotation + stale logic
setInterval(async () => {
const now = Date.now();
for (const [sym, latest] of buf) {
const lastGood = lkg.get(sym);
const age = now - latest.ts;
let out: Tick & { stale: boolean } =
age <= staleMs ? { ...latest, stale: false }
: { ...(lastGood ?? latest), stale: true };
// rotate if no fresh tick recently (edge-side policy)
if (age > rotateMs) rotateProvider(sym); // implement round-robin/health check
lkg.set(sym, { sym: out.sym, price: out.price, ts: out.ts, src: out.src, seq: out.seq });
sendToClients(sym, out); // SSE/WS push to all subscribers
buf.delete(sym);
}
}, frameMs);
// HTTP fallback if WS is down
async function httpPoll(sym: string) {
try {
const r = await fetch(`${P1}/q?symbol=${sym}`, { signal: Abort(1400) });
if (r.ok) onProviderTick({ ...(await r.json()), src: 'p1', seq: inc(sym) });
} catch { /* ignore, coalesce loop handles stale + LKG */ }
}
Client side: render updates, and when stale:true, show “Last update 2.3 s • p2” (tooltip: “provider fallback active”). Never render 0.00.
Why this works (future-proof)
- Real-time in, human-rate out: we ingest all ticks but render at a stable frame rate.
- Deterministic failover: rotation on silence, not just errors.
- Explicit uncertainty:
stale:truebeats silently wrong values. - Portable: works for equities/FX/crypto—just tweak
frameMs/rotateMs/staleMs.
If you want, I can post a tiny repo with this coalescer + SSE server so folks can fork and plug in their providers.
Please log in to add a comment.
Please log in to comment on this post.
More Posts
chevron_left