When 2+ data sources have a fresh price for the same symbol, the higher-quality source wins. Lower-quality ticks become "shadow" prices for cross-check, not canonical.
Why this exists: the prior behavior was that every tick from every source overwrote the canonical price. So a 12-second Stooq poll could overwrite a 1-second Coinbase WebSocket realtime price with stale data. Now: each source gets a composite quality score from built-in priority (WS > REST > refresh), recent reliability (success rate), cross-source agreement, and latency. Only ticks from the equal-or-better source replace canonical.
🏆 Source quality ranking (best → worst)
Source
Score
Priority
Reliability
Agreement
Latency
Waiting for first ticks... reload in 30s if empty.
🪞 Shadow prices (rejected as canonical, kept for cross-check)
When a lower-quality source ticks for a symbol that already has a higher-quality canonical, the new price is logged as shadow. Big gap = potential source bug.
Symbol
Canonical price
Canonical source
Shadow price
Shadow source
Gap %
No shadow prices yet. Need at least 2 sources fresh on same symbol.