Context
ADR-0051's derivation was ordered last, so any direct fiat book beat it. A single-venue book (one exchange's outage or thin hour sets the number, and only single-source buckets can freeze) is weaker evidence than a multi-venue USD VWAP converted at a vendor FX fixing.
Decision
- ADR-0051's composition and its flag, provenance and withheld-leg properties are unchanged. On
/v1/price,/v1/price/batchand/v1/oracle/x_last_pricethe ordering is replaced by a basis rule,Server.preferUSDAnchoredBasis. - No direct fiat book: derive.
- A direct book backed by two or more venues: serve it as observed.
- A direct book backed by one venue: serve the derivation instead, when its USD leg has at least
basisMinUSDVenues(2) venues and it is not stale (no fallback USD leg, no stale fixing). Otherwise serve the direct book. The rule only swaps one served value for a better-substantiated one; a derivation that misses, withholds or errors keeps the direct book. - A swapped row carries
usd_leg,fx_rate,fx_as_ofandtriangulated, and noconfidence, composite or divergence enrichment, because those score the displaced book. pricing_guard.disable_fiat_basis = truerestores direct-book-wins. It is a diagnostic kill switch, not a tuning knob./v1/price/tip, the series surfaces (/v1/chart,/v1/price/at), the price stream and the aggregator composite do not apply the rule (its only callers are/v1/price, its batch form and/v1/oracle/x_last_price); they keep ADR-0051 ordering. If any of them adopts a basis rule, it adopts this one, not a variant.
Invariant
- A single-venue book yields to a multi-venue fresh derivation, and a multi-venue book does not:
TestPriceThinFiatBookYieldsToUSDAnchor,TestPriceMultiVenueFiatBookIsServedAsObserved,TestPriceThinUSDLegDoesNotDisplaceThinBookininternal/api/v1/usd_anchored_fiat_basis_test.go. - A derivation miss never costs a price:
TestPriceFiatBasisMissKeepsDirectBook, same file. - Closed surfaces agree:
TestPriceBatchThinFiatBookYieldsToUSDAnchor,TestOracleXLastPriceAppliesFiatBasis, same file. - A swapped row has no thin-book enrichment:
TestPriceBasisRowCarriesNoThinBookEnrichment, same file. - The kill switch restores the direct book:
TestPriceFiatBasisKillSwitchServesThinBook, same file.
Consequences
- A currency with one direct venue is priced from the aggregated USD market and says so on the wire; single-venue fiat freezes stop reaching customers when a deep USD leg exists.
- The served price carries FX fixing lag: hourly bars bound
FXFixingLag(3h) behind the bucket (internal/storage/timescale/fx_fixings.go), so a real local move inside that lag is not reflected. Accepted for display pricing. - Until tip and the series surfaces adopt the rule, a single-venue fiat pair can read differently there than on
/v1/price. - Costs one extra USD read and one fixing bind per request that hits a single-venue book.
Evidence
Server.preferUSDAnchoredBasis in internal/api/v1/price.go, called from price.go and oracle_sep40.go; config DisableFiatBasis in internal/config/config.go; tests in internal/api/v1/usd_anchored_fiat_basis_test.go; docs/methodology/local-currency-pricing.md.