When to Use
Invoke this skill when three or more independent sources quote the same instrument — Bloomberg against Refinitiv/LSEG against Polygon, Binance against Coinbase against Kraken, an exchange feed against two aggregators — and one number has to come out the other end: a mark, a signal input, a risk valuation, a limit price.
The component answers two questions that are routinely collapsed into one:
Is there a price we can use, and did anything independent actually corroborate it?
A price picked by a tie-breaker because the vendors disagreed is usable and uncorroborated. A price averaged from three vendors that agreed to within a tick is corroborated. A pipeline that reports both as "success" has thrown away the only information that made running three feeds worth paying for. MultiSourcePriceReconcilerEngine therefore returns a status and an is_cross_verified flag, and they are not the same claim.
When NOT to Use
- With exactly two sources. Outlier rejection here is a distance test against the median, and with two quotes the median sits exactly midway, so both are always equidistant — either both pass or both fail, and no outlier is ever attributable. The engine detects this and skips filtering rather than pretending. Two-source arbitration with latency-skew handling and quarantine hysteresis is
market-data-feed-arbitration-across-vendors. - As a failover controller. This reconciles the quotes it is handed. Deciding which vendor to subscribe to when one dies, and how long to wait before promoting a backup, is
vendor-outage-fallback-data-source-hierarchy. - Across unlike price bases or currencies. One vendor's last trade against another's quote midpoint diverges by roughly half a spread permanently, and the engine will dutifully report a standing divergence forever. Normalise upstream:
multi-exchange-feed-normalization,currency-pair-quoting-convention-normalization. - For instruments that can price at or below zero. Percentage-of-median deviation is undefined there, so the engine rejects non-positive quotes outright. Negative-carry calendar spreads and the April 2020 WTI settlement need an absolute-difference reconciliation instead.
- As a risk control. It emits a status; it stops nothing. Wire
is_cross_verified is Falseintograduated-response-to-data-quality-degradationorkill-switch-and-drawdown-circuit-breakersto make it act. - As a published benchmark. If the reconciled price is distributed to third parties or referenced by a financial instrument, benchmark-administration obligations may attach — see
eu-benchmark-regulation-for-strategies-referencing-indices.
Prerequisites
- Three or more genuinely independent vendors for the symbol, on the same price basis in the same currency, each separately entitled for the intended use. Two aggregators that both resell the same underlying feed are one source wearing two badges, and they will agree with each other while being wrong together.
- A single local receipt clock.
timestampandas_ofare local receipt times, never vendor- or exchange-supplied event times. Staleness is a duration; measuring it across two vendors' clocks measures their skew (clock-skew-correction-for-tick-timestamps). - A tolerance calibrated from recorded cross-vendor history for this exact vendor set and instrument, floored at one minimum price increment. Under Reg NMS Rule 612 an NMS stock quoted at or above $1.00 currently moves in $0.01 increments, so one tick exceeds 5 bps for any stock under $20 — the default
tolerance_pct=0.0005is narrower than a single lawful penny across most of that universe. - A caller that branches on
status.canonical_priceisOptional[float]and isNonewhen every quote is stale.
Workflow
-
Reject the batch before pricing it.
- Non-finite and non-positive prices are rejected at construction.
NaNfails every comparison (abs(nan - m)/m > boundisFalse), so an uncheckedNaNpasses the outlier filter untouched and is published as the canonical price. - Decision point: a quote whose
symboldiffers from the batch symbol, or a repeatedvendor_id, raises. Both are dispatcher bugs, and both produce a confident wrong answer — the second by reporting a quorum of independent sources that does not exist.
- Non-finite and non-positive prices are rejected at construction.
-
Gate on staleness before any arithmetic.
- Decision point: a stale quote inside the outlier bound cannot be filtered out later. A vendor frozen 100 seconds ago at 0.8% away survives a 1% deviation test, widens the observed spread past tolerance, and converts two fresh vendors agreeing to 2 bps into an unresolved divergence.
as_ofis required, never defaulted to the freshest quote in the batch: that default makes the newest quote zero seconds old by construction and can therefore never detect the outage where every vendor has stopped updating.- Every quote stale → no price.
canonical_priceisNone, not the last good value.
-
Attribute outliers only with enough sources.
- With fewer than three usable quotes, skip the filter and say so. Do not run it and report "0 outliers" — that reads on a dashboard as checked and clean.
- Reject quotes further than
max_deviation_pctfrom the median of the usable quotes, with the bound floored at one tick.
-
Handle the deadlock the filter can produce.
- Decision point: with an even number of quotes split into two clusters (100, 100, 105, 105) the median falls in the gap and every quote fails. That is a deadlock, not a detection. Retain the quotes, mark the result unresolved and unverified, and never silently reset the rejection list to empty — an audit trail that says "4 valid, 0 outliers" when all four were rejected is worse than no audit trail.
-
Audit agreement against the surviving quotes.
- Recompute the median over the survivors; the all-quotes median is still contaminated by whatever was just removed.
- Effective tolerance is
max(tolerance_pct, one_tick / median). Do not calibrate to the last bit: a decimal threshold such as 0.0005 is not exactly representable in binary, so exact-boundary behaviour is a coin flip.
-
Composite only when they actually agree.
- Within tolerance and two or more survivors → reliability-weighted average,
is_cross_verified=True. This is the only cross-verified outcome. - Exactly one survivor → the price is usable but
RECONCILIATION_UNCORROBORATED.
- Within tolerance and two or more survivors → reliability-weighted average,
-
Tie-break deterministically, and call it policy.
PRIORITY(lowest rank),FRESHNESS(newest),VOLUME_WEIGHTED(deepest book). Every key is a total order ending invendor_id, so the winner never depends on the caller's list order.- Decision point: a tie-broken price is
RECONCILIATION_UNRESOLVEDwithis_cross_verified=False. Preferring Bloomberg when the vendors disagree is an operator preference, not a detection of who was wrong. - A misspelt method raises. It must not degrade to "first quote in the list" — order-dependent pricing is exactly the non-determinism this skill exists to prevent.
-
Emit the audit record.
PriceReconciliationReportcarries the surviving quotes, the rejected quotes, the stale quotes, the observed spread, the effective tolerance actually applied, and the contributing vendor ids.
Full procedure: see
references/workflows.md. Standards and sourced citations: seereferences/standards.md. Printable pre-flight checklist: seeassets/checklist.md.
Common Pitfalls
NaNas a canonical price. Every threshold comparison againstNaNisFalse, so a corrupt quote is classified valid, flows into the weighted average, and poisons the output — which then comparesFalseagainst every downstream risk limit too.- Silently emptying the rejection list. Falling back to "use all quotes" when the filter rejects everything is a reasonable pricing choice and a falsified audit record. Keep the fallback; report it as unresolved.
- Reporting zero outliers when the filter never ran. With two sources the filter is structurally incapable of attributing one. "0 outliers" and "not checked" must not render identically.
- Stale quotes inside the deviation band. A frozen vendor 0.8% away survives a 1% filter, and no amount of outlier logic will catch it. Only a receipt-clock age check will.
- Substituting the last known price on a blackout. The one moment every vendor is silent is the one moment a cached price is most likely to be wrong and most likely to be traded on.
- Tolerances below one tick. 5 bps on a $12 stock is 60% of a single lawful $0.01 increment, so every legal one-cent disagreement raises a divergence — a permanent false-alarm generator across most of the sub-$20 universe.
- Duplicate vendor ids. The same feed delivered twice — a replayed message, a double subscription, a symbol-mapping collision — biases the median toward itself, double-weights the composite, and turns "3 sources agreed" into "2 sources, one counted twice".
- Order-dependent tie-breaks.
min()over a key that leaves ties returns whichever element came first. Build the quote list from adictand the canonical price becomes irreproducible run to run, which is unauditable regardless of whether it was right. - Blending disagreeing vendors.
WEIGHTED_AVERAGEacross quotes that breach tolerance manufactures a price no vendor quoted and no venue ever showed, and it looks more authoritative than either input. - Hardcoded rounding.
round(price, 4)silently returns0.0for a token quoted at 0.00002181 — a total loss of the price, not a loss of precision. - Treating divergence as proof of a bad vendor. Cross-vendor disagreement is dominated by relative latency. In adopting the Market Data Infrastructure rule the SEC described the structural gap in which proprietary-feed subscribers "receive more content-rich data faster" than consolidated-tape consumers. A feed that is merely ahead is not an outlier.
Verification
- Feed Bloomberg $100.00 (priority 1), Refinitiv $100.02 (priority 2) and a bad Polygon $105.00 and confirm Polygon is attributed as the outlier (4.98% from a median of $100.02), the survivors agree within tolerance, and the composite is $100.01 with
status == "RECONCILIATION_SUCCESS"andis_cross_verified is True. - Submit
NaN,inf,0.0and a negative price and confirm each raises at quote construction. - Submit 100, 100, 105, 105 and confirm
filter_deadlocked is True,status == "RECONCILIATION_UNRESOLVED", and that the report does not claim four valid quotes and zero outliers as a success. - Submit two quotes and confirm no outlier is attributed and the result is not cross-verified.
- Submit a quote for a different symbol, and a repeated
vendor_id, and confirm both raise. - Age every quote past
max_quote_age_secondsand confirmcanonical_price is Nonewithstatus == "RECONCILIATION_NO_USABLE_QUOTE". - Rotate the quote list and confirm both the tie-break winner and the composite price are bit-identical across every rotation.
- Reconcile two identical 0.00002181 quotes and confirm the canonical price is not
0.0. - Run
python -m unittest discover -s skills/multi-source-price-reconciliation-tie-breaking/scriptsand confirm a 100% pass rate.
Related Skills
market-data-feed-arbitration-across-vendorsvendor-outage-fallback-data-source-hierarchycross-vendor-timestamp-precision-reconciliationclock-skew-correction-for-tick-timestampsreference-data-golden-source-designationmulti-exchange-feed-normalizationgraduated-response-to-data-quality-degradationdata-vendor-cross-validation-for-backtestsmarket-data-latency-monitoring-per-vendor