Your users don’t care why a quote failed—they care that the number didn’t flash 0.00. Here’s a pragmatic, production-ready pipeline that returns the best price you can get within a strict time budget, exposes provenance, and never silently lies. 0.00 within a strict time budget provenance never silently lies Audience: engineers shipping finance dashboards, brokers, or portfolio tools. Audience: Deliverables: architecture, TypeScript code (fetch with timeout + backoff), and a reproducible test harness you can run locally. Deliverables: The failure modes we must beat Single-provider coupling. One upstream hiccup → you show 0.00 or stale data.Unbounded waits. A slow API call blocks the UI thread or burns your edge budget.Silent staleness. You cache yesterday’s price and don’t tell the user.Inconsistent math. Different providers round/adjust differently, breaking P/L. Single-provider coupling. One upstream hiccup → you show 0.00 or stale data. Single-provider coupling. 0.00 Unbounded waits. A slow API call blocks the UI thread or burns your edge budget. Unbounded waits. Silent staleness. You cache yesterday’s price and don’t tell the user. Silent staleness. Inconsistent math. Different providers round/adjust differently, breaking P/L. Inconsistent math. We’ll design for bounded latency, graceful degradation, and auditability. bounded latency graceful degradation auditability Architecture: time-boxed fan-out + LKG + provenance Imagine the following diagram (textual description): Imagine the following diagram (textual description): [ Client ] --(fetch / 1.5s budget)--> [ Edge Worker ] |-> Provider A (Yahoo) [score 0] |-> Provider B (NBBO) [score +1] |-> Provider C (CoinGecko/OHLC) [score +2] | <best valid within budget> | [ LKG cache (30s TTL) ] | returns {price, ts, source, stale} [ Client ] --(fetch / 1.5s budget)--> [ Edge Worker ] |-> Provider A (Yahoo) [score 0] |-> Provider B (NBBO) [score +1] |-> Provider C (CoinGecko/OHLC) [score +2] | <best valid within budget> | [ LKG cache (30s TTL) ] | returns {price, ts, source, stale} The edge queries providers in parallel within a strict budget (e.g., 1500 ms).We prefer the first valid response using a simple health score (recent failures raise the score; ties go to lowest score).If nothing lands in time, we return LKG (last-known-good) marked stale: true.The client renders a Provenance chip: Yahoo • 12s • Live or Cache • 45s • Stale. No black boxes. The edge queries providers in parallel within a strict budget (e.g., 1500 ms). edge strict budget We prefer the first valid response using a simple health score (recent failures raise the score; ties go to lowest score). first valid health score If nothing lands in time, we return LKG (last-known-good) marked stale: true. LKG (last-known-good) stale: true The client renders a Provenance chip: Yahoo • 12s • Live or Cache • 45s • Stale. No black boxes. Provenance chip Yahoo • 12s • Live Cache • 45s • Stale Core TypeScript: bounded fetch with backoff + jitter We’ll start with a few helpers. // utils/timeout.ts export async function fetchWithTimeout( url: string, ms: number, init: RequestInit = {} ): Promise<Response> { const ctrl = new AbortController(); const timer = setTimeout(() => ctrl.abort("time_budget_exceeded"), ms); try { const res = await fetch(url, { ...init, signal: ctrl.signal }); return res; } finally { clearTimeout(timer); } } // utils/backoff.ts export function backoffDelay(attempt: number, base = 150, cap = 1000) { // exponential backoff with full jitter const exp = Math.min(cap, base * 2 ** attempt); return Math.floor(Math.random() * exp); } // utils/timeout.ts export async function fetchWithTimeout( url: string, ms: number, init: RequestInit = {} ): Promise<Response> { const ctrl = new AbortController(); const timer = setTimeout(() => ctrl.abort("time_budget_exceeded"), ms); try { const res = await fetch(url, { ...init, signal: ctrl.signal }); return res; } finally { clearTimeout(timer); } } // utils/backoff.ts export function backoffDelay(attempt: number, base = 150, cap = 1000) { // exponential backoff with full jitter const exp = Math.min(cap, base * 2 ** attempt); return Math.floor(Math.random() * exp); } Provider adapters (pure functions) Each adapter returns a normalized quote or throws. normalized // providers/types.ts export type Quote = { symbol: string; price: number; ts: number; // epoch millis source: string; // "yahoo", "nbbo", etc. }; export type Provider = (s: string) => Promise<Quote>; // providers/yahoo.ts import { fetchWithTimeout } from "../utils/timeout"; import type { Quote, Provider } from "./types"; export const yahoo: Provider = async (symbol) => { const r = await fetchWithTimeout(`https://api.example/yahoo?symbol=${symbol}`, 700); if (!r.ok) throw new Error(`yahoo:${r.status}`); const j = await r.json(); const price = Number(j?.price); if (!Number.isFinite(price)) throw new Error("yahoo:invalid"); return { symbol, price, ts: Date.now(), source: "yahoo" }; }; // providers/types.ts export type Quote = { symbol: string; price: number; ts: number; // epoch millis source: string; // "yahoo", "nbbo", etc. }; export type Provider = (s: string) => Promise<Quote>; // providers/yahoo.ts import { fetchWithTimeout } from "../utils/timeout"; import type { Quote, Provider } from "./types"; export const yahoo: Provider = async (symbol) => { const r = await fetchWithTimeout(`https://api.example/yahoo?symbol=${symbol}`, 700); if (!r.ok) throw new Error(`yahoo:${r.status}`); const j = await r.json(); const price = Number(j?.price); if (!Number.isFinite(price)) throw new Error("yahoo:invalid"); return { symbol, price, ts: Date.now(), source: "yahoo" }; }; Duplicate for other sources (nbbo, coingecko, etc.) with their own timeouts. nbbo coingecko Circuit breaker + scoring Providers accumulate penalties on failure (simple, sticky health heuristic). // engine/quotes.ts import type { Provider, Quote } from "../providers/types"; import { backoffDelay } from "../utils/backoff"; type Health = Record<string, number>; // lower is healthier export type BudgetedOptions = { budgetMs?: number; // total time budget attempts?: number; // retries per provider lkg?: Quote | null; // last-known-good health?: Health; // provider health map }; export async function getQuoteWithBudget( symbol: string, providers: Record<string, Provider>, opts: BudgetedOptions = {} ): Promise<Quote & { stale: boolean }> { const budgetMs = opts.budgetMs ?? 1500; const attempts = opts.attempts ?? 1; const health = opts.health ?? {}; const start = Date.now(); let winner: Quote | null = null; // run providers in order of health (lowest first) const entries = Object.entries(providers).sort( ([a], [b]) => (health[a] ?? 0) - (health[b] ?? 0) ); // Launch all providers in parallel but respect total budget await Promise.any( entries.map(async ([name, p]) => { for (let i = 0; i < attempts; i++) { const elapsed = Date.now() - start; const remaining = budgetMs - elapsed; if (remaining <= 0) throw new Error("time_budget_exceeded"); try { const q = await p(symbol); // each p uses its own per-call timeout if (!Number.isFinite(q.price)) throw new Error("invalid"); if (!winner) winner = q; // first valid wins return; } catch { health[name] = (health[name] ?? 0) + 1; // penalize on fail await new Promise((r) => setTimeout(r, backoffDelay(i))); } } throw new Error(`${name}_exhausted`); }) ).catch(() => null); // If nothing landed, fall back to LKG const now = Date.now(); if (!winner && opts.lkg) { return { ...opts.lkg, source: `${opts.lkg.source}+lkg`, stale: true }; } if (!winner) { // honest sentinel; caller should render skeleton/“unavailable” return { symbol, price: NaN, ts: now, source: "unavailable", stale: true }; } // Mark staleness based on age (e.g., 30s) const stale = now - winner.ts > 30_000; return { ...winner, stale }; } // engine/quotes.ts import type { Provider, Quote } from "../providers/types"; import { backoffDelay } from "../utils/backoff"; type Health = Record<string, number>; // lower is healthier export type BudgetedOptions = { budgetMs?: number; // total time budget attempts?: number; // retries per provider lkg?: Quote | null; // last-known-good health?: Health; // provider health map }; export async function getQuoteWithBudget( symbol: string, providers: Record<string, Provider>, opts: BudgetedOptions = {} ): Promise<Quote & { stale: boolean }> { const budgetMs = opts.budgetMs ?? 1500; const attempts = opts.attempts ?? 1; const health = opts.health ?? {}; const start = Date.now(); let winner: Quote | null = null; // run providers in order of health (lowest first) const entries = Object.entries(providers).sort( ([a], [b]) => (health[a] ?? 0) - (health[b] ?? 0) ); // Launch all providers in parallel but respect total budget await Promise.any( entries.map(async ([name, p]) => { for (let i = 0; i < attempts; i++) { const elapsed = Date.now() - start; const remaining = budgetMs - elapsed; if (remaining <= 0) throw new Error("time_budget_exceeded"); try { const q = await p(symbol); // each p uses its own per-call timeout if (!Number.isFinite(q.price)) throw new Error("invalid"); if (!winner) winner = q; // first valid wins return; } catch { health[name] = (health[name] ?? 0) + 1; // penalize on fail await new Promise((r) => setTimeout(r, backoffDelay(i))); } } throw new Error(`${name}_exhausted`); }) ).catch(() => null); // If nothing landed, fall back to LKG const now = Date.now(); if (!winner && opts.lkg) { return { ...opts.lkg, source: `${opts.lkg.source}+lkg`, stale: true }; } if (!winner) { // honest sentinel; caller should render skeleton/“unavailable” return { symbol, price: NaN, ts: now, source: "unavailable", stale: true }; } // Mark staleness based on age (e.g., 30s) const stale = now - winner.ts > 30_000; return { ...winner, stale }; } Why this shape works: bounded total latency, first valid result preference, provider health nudges future calls, and a clean stale flag keeps the UI honest. bounded total latency first valid result provider health stale Client UX: provenance chip (text-described diagram) Imagine a small pill next to the price: Imagine a small pill next to the price: [ Yahoo • 12s • Live ] // green background [ Cache • 47s • Stale ] // amber background [ Yahoo • 12s • Live ] // green background [ Cache • 47s • Stale ] // amber background When a user clicks it, show a popover with source, requestId, and the time budget used—handy for bug reports and audits. source requestId time budget // ui/ProvenanceBadge.tsx export function ProvenanceBadge({ source, ts, stale }: { source: string; ts: number; stale: boolean }) { const ageSec = Math.max(0, Math.round((Date.now() - ts) / 1000)); const label = `${source} • ${ageSec}s • ${stale ? "Stale" : "Live"}`; const cls = `px-2 py-1 rounded-full text-xs ${stale ? "bg-yellow-100 text-yellow-800" : "bg-green-100 text-green-800"}`; return <span className={cls} aria-live="polite">{label}</span>; } // ui/ProvenanceBadge.tsx export function ProvenanceBadge({ source, ts, stale }: { source: string; ts: number; stale: boolean }) { const ageSec = Math.max(0, Math.round((Date.now() - ts) / 1000)); const label = `${source} • ${ageSec}s • ${stale ? "Stale" : "Live"}`; const cls = `px-2 py-1 rounded-full text-xs ${stale ? "bg-yellow-100 text-yellow-800" : "bg-green-100 text-green-800"}`; return <span className={cls} aria-live="polite">{label}</span>; } Reproducible test harness We’ll simulate providers with deterministic latency/failure. You can paste this into a Node/TS project (no network required). // harness/sim.ts import type { Provider, Quote } from "../providers/types"; import { getQuoteWithBudget } from "../engine/quotes"; function fakeProvider(name: string, opts: { latency: number; fail?: boolean; price?: number }): Provider { return async (symbol: string) => { await new Promise((r) => setTimeout(r, opts.latency)); if (opts.fail) throw new Error(`${name}_fail`); const price = opts.price ?? 100 + Math.random(); return { symbol, price, ts: Date.now(), source: name } as Quote; }; } async function main() { const providers = { yahoo: fakeProvider("yahoo", { latency: 180, price: 189.97 }), nbbo: fakeProvider("nbbo", { latency: 420, price: 189.95 }), alt: fakeProvider("alt", { latency: 1100, fail: true }), }; const out = await getQuoteWithBudget("AAPL", providers, { budgetMs: 600, attempts: 1, lkg: null, health: {} }); console.log(out); } main().catch(console.error); // harness/sim.ts import type { Provider, Quote } from "../providers/types"; import { getQuoteWithBudget } from "../engine/quotes"; function fakeProvider(name: string, opts: { latency: number; fail?: boolean; price?: number }): Provider { return async (symbol: string) => { await new Promise((r) => setTimeout(r, opts.latency)); if (opts.fail) throw new Error(`${name}_fail`); const price = opts.price ?? 100 + Math.random(); return { symbol, price, ts: Date.now(), source: name } as Quote; }; } async function main() { const providers = { yahoo: fakeProvider("yahoo", { latency: 180, price: 189.97 }), nbbo: fakeProvider("nbbo", { latency: 420, price: 189.95 }), alt: fakeProvider("alt", { latency: 1100, fail: true }), }; const out = await getQuoteWithBudget("AAPL", providers, { budgetMs: 600, attempts: 1, lkg: null, health: {} }); console.log(out); } main().catch(console.error); Expected behavior: with a 600 ms budget, yahoo wins (180 ms). If you increase yahoo latency above 600, nbbo becomes the winner. If everything fails, the function returns { price: NaN, source: "unavailable", stale: true }—which your UI should render as a skeleton or an explicit “unavailable” state. Expected behavior: yahoo yahoo nbbo { price: NaN, source: "unavailable", stale: true } Unit tests (Vitest) // harness/sim.test.ts import { describe, it, expect } from "vitest"; import { getQuoteWithBudget } from "../engine/quotes"; import type { Provider } from "../providers/types"; function p(name: string, latency: number, ok = true, price = 101): Provider { return async (s) => { await new Promise((r) => setTimeout(r, latency)); if (!ok) throw new Error(name); return { symbol: s, price, ts: Date.now(), source: name }; }; } describe("getQuoteWithBudget", () => { it("returns first valid within budget", async () => { const out = await getQuoteWithBudget("AAPL", { slow: p("slow", 500), fast: p("fast", 80, true, 190.12) }, { budgetMs: 300 }); expect(out.source).toBe("fast"); expect(out.stale).toBe(false); expect(Number.isFinite(out.price)).toBe(true); }); it("falls back to LKG when none arrive in time", async () => { const lkg = { symbol: "AAPL", price: 188.5, ts: Date.now() - 40_000, source: "cache" }; const out = await getQuoteWithBudget("AAPL", { slow: p("slow", 800) }, { budgetMs: 200, lkg }); expect(out.source).toContain("lkg"); expect(out.stale).toBe(true); expect(out.price).toBe(188.5); }); it("returns sentinel when all fail & no LKG", async () => { const out = await getQuoteWithBudget("AAPL", { a: p("a", 50, false), b: p("b", 60, false) }, { budgetMs: 150 }); expect(out.source).toBe("unavailable"); expect(isNaN(out.price)).toBe(true); }); }); // harness/sim.test.ts import { describe, it, expect } from "vitest"; import { getQuoteWithBudget } from "../engine/quotes"; import type { Provider } from "../providers/types"; function p(name: string, latency: number, ok = true, price = 101): Provider { return async (s) => { await new Promise((r) => setTimeout(r, latency)); if (!ok) throw new Error(name); return { symbol: s, price, ts: Date.now(), source: name }; }; } describe("getQuoteWithBudget", () => { it("returns first valid within budget", async () => { const out = await getQuoteWithBudget("AAPL", { slow: p("slow", 500), fast: p("fast", 80, true, 190.12) }, { budgetMs: 300 }); expect(out.source).toBe("fast"); expect(out.stale).toBe(false); expect(Number.isFinite(out.price)).toBe(true); }); it("falls back to LKG when none arrive in time", async () => { const lkg = { symbol: "AAPL", price: 188.5, ts: Date.now() - 40_000, source: "cache" }; const out = await getQuoteWithBudget("AAPL", { slow: p("slow", 800) }, { budgetMs: 200, lkg }); expect(out.source).toContain("lkg"); expect(out.stale).toBe(true); expect(out.price).toBe(188.5); }); it("returns sentinel when all fail & no LKG", async () => { const out = await getQuoteWithBudget("AAPL", { a: p("a", 50, false), b: p("b", 60, false) }, { budgetMs: 150 }); expect(out.source).toBe("unavailable"); expect(isNaN(out.price)).toBe(true); }); }); Run it Run it npm i -D vitest typescript tsx npx vitest run npm i -D vitest typescript tsx npx vitest run Production notes (learned the hard way) Time budgets must be end-to-end (edge → providers → marshaling). Don’t just set per-call timeouts; total elapsed matters.Never coerce NaN to 0.00. Return a sentinel and render it honestly.Record provenance (source, ts, requestId) for every price; it turns user bug reports into reproducible tickets.Cache with intent. LKG cache should be small (e.g., 30–60 s) and keyed by symbol + venue where applicable.Score providers. A sticky health score quickly pushes you away from flaky sources without a full breaker library.UI skeletons + chips > spinners. Show skeleton rows during refresh and clear “Stale” chips when using LKG. Time budgets must be end-to-end (edge → providers → marshaling). Don’t just set per-call timeouts; total elapsed matters. Time budgets must be end-to-end Never coerce NaN to 0.00. Return a sentinel and render it honestly. Never coerce NaN to 0.00. Record provenance (source, ts, requestId) for every price; it turns user bug reports into reproducible tickets. Record provenance source ts requestId Cache with intent. LKG cache should be small (e.g., 30–60 s) and keyed by symbol + venue where applicable. Cache with intent. symbol + venue Score providers. A sticky health score quickly pushes you away from flaky sources without a full breaker library. Score providers. UI skeletons + chips > spinners. Show skeleton rows during refresh and clear “Stale” chips when using LKG. UI skeletons + chips > spinners. What this buys you Predictable UX: Users get a number (or a truthful “stale/unavailable”) within your SLA.Operational safety: Slow or failing providers don’t stall the entire pipeline.Auditability: Every number is explainable—source + age + freshness. Predictable UX: Users get a number (or a truthful “stale/unavailable”) within your SLA. Predictable UX: Operational safety: Slow or failing providers don’t stall the entire pipeline. Operational safety: Auditability: Every number is explainable—source + age + freshness. Auditability: Next steps you can ship today Drop getQuoteWithBudget into your edge worker.Add the Provenance chip.Wire a 30–60 s LKG cache and emit “stale” when using it.Expand the test harness with your real providers behind a feature flag. Drop getQuoteWithBudget into your edge worker. getQuoteWithBudget Add the Provenance chip. Provenance Wire a 30–60 s LKG cache and emit “stale” when using it. Expand the test harness with your real providers behind a feature flag. If you want a code drop-in adapted to your stack (Vercel Edge, Cloudflare Workers, or Node), ping me your environment and symbols—you’ll get a tailored snippet. Want the companion “OpenBrokerCSV v0.1” spec + validator to make imports boringly reliable? I’ve got a minimal template and test fixtures—DM me your broker and I’ll spin up a mapping PR. Want the companion “OpenBrokerCSV v0.1” spec + validator to make imports boringly reliable? I’ve got a minimal template and test fixtures—DM me your broker and I’ll spin up a mapping PR.