Weighted median price aggregation is how SiftingIO turns a set of disagreeing venue prices into one published fair price, and the reasoning behind it matters to anyone deciding whose market data to trust. Pull a quote for the same asset from five independent venues at the same instant and you'll get five different numbers. All of them are real in some sense. None of them is obviously the price. This post covers why venues disagree, why the obvious fix of averaging them fails exactly when you need it most, and how a volume-and-reputation-weighted median survives the failure modes that poison a mean. Every methodology claim below comes from the published data methodology page.
Why the same asset shows different prices on different venues#
Venues are independent businesses with independent order flow. Liquidity is fragmented: one venue clears most of the volume in a pair while another lists it thinly. Fees differ, participants differ, and prints propagate at different speeds, so even two healthy venues rarely agree to the tick. Then there are the unhealthy ones: a feed that stalls and keeps re-sending its last price, a matching engine that pauses under load, a thin on-chain pool where a single swap moves the quote a full percent.
For crypto, FX, and on-chain markets this fragmentation is structural. There's no official closing print and no single source of record, so "the price" is genuinely an estimation problem. The methodology page calls this the consensus regime, and it's handled differently from equities and fundamentals, where an authoritative source of record exists and the job is to stay faithful to it. Everything below concerns the consensus regime, which covers crypto, forex, commodities, and on-chain pairs.
How a naive average gets poisoned#
The arithmetic mean has a breakdown point of zero. Corrupt a single input and the output moves, no matter how many honest inputs sit around it. Here's the failure in a few lines, using quotes shaped like a BTCUSD book where one venue's feed has frozen on an old price:
import numpy as np
# Five venues, same instrument, same instant.
# The fourth venue's feed is frozen on a price from minutes ago.
prices = np.array([64712.4, 64715.1, 64709.8, 63980.0, 64713.6])
volumes = np.array([182.4, 96.7, 141.2, 3.1, 88.9]) # real traded volume
naive = prices.mean() # 64566.18
volwt = np.average(prices, weights=volumes) # 64707.97
def weighted_median(values, weights):
order = np.argsort(values)
v, w = values[order], weights[order]
return v[np.searchsorted(w.cumsum(), w.sum() / 2)]
fair = weighted_median(prices, volumes) # 64712.4
The plain mean lands about 146 dollars below where the market is actually trading, dragged there by one venue carrying 0.6 percent of the flow. Volume weighting looks like a fix and isn't: it still sits several dollars off here, and it fails harder in the opposite direction, because a large venue that glitches has its error amplified by its own volume. A frozen feed is also sneakier than a dead one. It keeps sending messages, so connection-level health checks pass. The price is simply stuck while the wider market moves, and an average has no defense against that.
How a weighted median handles outliers#
A median has a breakdown point of 50 percent. As the methodology page puts it, a majority of venues would have to be wrong, in the same direction, at the same instant, before the published price is wrong. In the example above the frozen venue can print any number it likes; the weighted median stays inside the honest cluster because an outlier can't drag the output past its neighbors.
SiftingIO's published price is the last step of a four-stage pipeline, and the earlier stages mean most bad inputs never reach the median at all.
Validate drops quotes older than a strict staleness window and discards any price more than a fixed fraction away from the cross-venue median, so gross errors die in the same tick they arrive.
Score measures each venue's deviation using the Median Absolute Deviation and the modified z-score of Iglewicz and Hoaglin (1993). MAD is itself resistant to outliers, and the modified z-score is scale-free, so a ten-dollar error on a five-figure asset and a ten-cent error on a two-dollar token are judged on the same footing.
Remember maintains a per-venue reputation as an exponentially weighted moving average of that deviation. Sustained misbehavior gets a venue quarantined, and hysteresis (a dead band between the benching threshold and the re-admission threshold) stops it from flapping in and out. This stage also runs frozen-feed detection: a source whose price stops changing while the wider market moves gets flagged even though its connection looks healthy.
Aggregate takes the survivors and computes the weighted median, where each venue's weight is its real traded volume times its reputation. Deep, reliable venues dominate, but no single venue can push the output past the venues adjacent to it. Where a venue has no comparable volume signal, it's weighted on reputation alone. The published spread is built the same defensive way: the half-spread is the larger of the weighted average of venue spreads and the cross-venue disagreement (the MAD of venue mids), so it widens automatically when venues stop agreeing, and because the quote is published as mid plus and minus half-spread it can't cross by construction.
What shows up on the wire#
A consensus price is only trustworthy if you can audit it, so quality is exposed rather than smoothed over. Every tick carries three timestamps (when the underlying source published it, when the engine ingested it, and when the engine published the aggregated value), a cross-venue consensus value derived from the MAD that tells you how tightly venues currently agree, and an explicit quality flag: Normal while a standard quorum of independent sources is contributing, Degraded with the reason exposed when fewer are available.
Reading the aggregated quote is one call:
curl -H "X-API-Key: $SIFTING_KEY" \
"https://api.sifting.io/v1/last/quote/crypto/BTCUSD"
Update cadence is a plan property: Free delivers a fresh fair price at 1 Hz, Builder at 4 Hz, Pro at 6 Hz, Ultra at 10 Hz, and Enterprise near real time. These are fair-price update rates, not execution latency figures.
The methodology page is equally explicit about limits, and they're worth respecting when you evaluate any provider. The output is a synthetic reference value, not an exchange-of-record print, and the composite spread is representative rather than executable depth. The outlier resistance is bounded to a minority of bad feeds; no median-based or mean-based estimator survives a majority of venues making the same coordinated error. The right uses are research, valuation, dashboards, alerting, and cross-checking the price your execution venue shows you, not order routing.
Common pitfalls when consuming a consensus price#
Mistaking delivery cadence for staleness. On the Free tier the fair price updates once per second, so a timestamp 900 ms old is the delivery schedule, not a degraded feed. The real staleness signal is different: when live data is older than the freshness threshold, the REST API returns a 503 with error code stale_snapshot and a body carrying last_t and server_now, so you can see exactly how far behind the snapshot is.
Rendering the price without checking the quality flag. Degraded means fewer than the standard quorum of independent sources are contributing, with a fallback to the safest available source. A dashboard that ignores the flag will display that fallback styled exactly like full consensus. Gate on it: show a warning state, or pause automated signals until the flag returns to Normal.
Backtesting fills against the composite spread. The spread never crosses by construction and widens with cross-venue disagreement, which makes it a useful health indicator and a poor execution model. Simulating fills at the reference bid and ask produces results no venue would honor. Use it the other way around, to flag when the spread your execution venue quotes looks abnormally wide or suspiciously tight.
Whose price should you trust#
For fragmented markets, arguably nobody's single number. Venues disagree for structural reasons, and the honest answer is a consensus that a minority of bad feeds can't poison, published with the timestamps and quality flags you need to verify it yourself. The full algorithm, including the weighting, the quarantine rules, and the versioning policy (thresholds are tuned continuously, but algorithm changes require a version bump), is documented on the data methodology page linked above. The free tier needs no credit card if you'd rather watch the consensus values and quality flags on live data first. Start building free
