Reference
Feeds and symbols
How the oracle names a feed, how a feed ID derives from its symbol, how numbers are encoded, and which feeds and venues exist today.
A feed is one quantity that the oracle publishes, such as the spot price of BTC in USD. A canonical symbol names each feed. The feed ID is the keccak256 hash of that symbol.
Canonical symbols
A symbol has this form:
<CLASS>.<BASE>/<QUOTE>[.<KIND>]The symbol string is the only source of identity. The rules are:
| Part | Rule |
|---|---|
CLASS |
CRYPTO is the only class. |
BASE, QUOTE |
1 to 16 characters of A–Z and 0–9, with at least one letter. 1000PEPE is valid. 123 is not. |
KIND |
Optional. One of the suffixes in the table below. No suffix means a spot price. |
| Whole string | ASCII, uppercase, no whitespace. |
| Suffix | Feed kind | Exponent |
|---|---|---|
| none | Composite spot price | −8 |
PERP_MARK |
Composite perpetual mark price | −8 |
PERP_INDEX |
Composite perpetual index price | −8 |
PERP_MID |
Composite perpetual mid, from the books of the perpetuals | −8 |
FUNDING_8H |
Funding rate normalised to 8 hours, as a fraction | −9 |
OPEN_INTEREST |
Aggregate open interest, in base units | −8 |
Valid symbols:
CRYPTO.BTC/USD spot price
CRYPTO.USDT/USD the stablecoin peg
CRYPTO.BTC/USD.PERP_MARK perpetual mark price
CRYPTO.BTC/USD.PERP_INDEX perpetual index price
CRYPTO.BTC/USDT.PERP_MID perpetual mid, from the books of the perpetuals
CRYPTO.BTC/USD.FUNDING_8H funding rate over 8 hours
CRYPTO.1000PEPE/USDT.PERP_MARK numeric prefix in the asset codeThe parser refuses each of these:
crypto.btc/usd lowercase
CRYPTO.BTC-USD wrong separator
BTC/USD no class
CRYPTO.BTC/USD.FOO unknown kind
EQUITY.AAPL/USD unknown class
CRYPTO.123/USD digits onlyTwo implementations that agree on this grammar agree on every feed ID.
Feed IDs
A feed ID is 32 bytes:
feed_id = keccak256(canonical_symbol as ASCII bytes)No registry coordinates the feed IDs. Anyone derives the same ID from the same string. The kind suffix is part of the string, so CRYPTO.BTC/USD and CRYPTO.BTC/USD.PERP_MARK have different IDs.
| Symbol | Feed ID |
|---|---|
CRYPTO.BTC/USD |
0x0523e79c7ed211911a19afaa4def68c896bc72d674bf33c5c64d4d7089e320e2 |
CRYPTO.ETH/USD |
0x2473e24bc41464f61b79547860ce69c012a19808f3057492fb5f95e22a5e40e4 |
CRYPTO.USDT/USD |
0x09c19a99408ed93ad59886c676dda3da0d1e3215c8d349a913b290ef81b676d4 |
CRYPTO.BTC/USD.PERP_MARK |
0xc159655a2f44a71fc9815e636c2b0bd999206a00ba73666124dd11e6eb6a132e |
CRYPTO.BTC/USD.FUNDING_8H |
0xc04929eacfbc432a23afbc552de5122b922f2bb7c29bf83d0418aa01498e8d76 |
Tip
Pin the symbol-to-ID mapping for the feeds that you use. Do not trust a mapping that a server gives you. Compute the ID yourself, or check it against this table.
Numbers
The oracle never uses floating point for a price.
Inside a node. The node computes with 128-bit fixed-point numbers at a scale of 10^-12, and rounds halves away from zero. A recorded set of observations therefore replays to the same price, bit for bit, on another machine.
On the wire. Each value is a mantissa and an exponent: value = mantissa × 10^expo. The node converts once, at publish time, and rounds halves away from zero.
| Field | Type | Note |
|---|---|---|
price |
i64 |
Mantissa. |
conf |
u64 |
Mantissa at the same exponent as price. Never negative. |
expo |
i32 |
−8 for prices, −9 for funding rates. |
For example, a price mantissa of 6431250000000 at exponent −8 is 64312.5. A funding rate of 0.0001 (1 bp) at exponent −9 is the mantissa 100000. Funding gets three more digits because it is a small fraction.
In JSON. Each decimal is a string, and each 64-bit time is a string. A JavaScript number loses precision past 2^53, and a nanosecond timestamp is far larger than that. A resolved price from the SDK looks like this:
{
"feed_id": "0x0523…20e2",
"symbol": "CRYPTO.BTC/USD",
"status": "ok",
"price": "86599.95",
"conf": "19.71359135",
"nodes": 3,
"halted_nodes": 0,
"concerns": [],
"publish_time_ns": "1791299498663252000",
"spread_bps": "0"
}The confidence interval
conf is a symmetric ± band around the price. It is roughly a 1σ estimate of how wrong the price can be. It is not decoration. It is the main signal that a consumer uses to decide whether to trust an update.
A wide conf is how the oracle says "the markets are dislocated" without a false price and without a halt. Each node computes conf as the widest of three terms:
- how much the venues disagree;
- the actual spread of the books;
- a floor of 1 bp of the price.
The SDK then makes conf at least half the spread between the nodes. See Aggregation and Trust model.
USDT is not USD
Binance, Bybit, OKX, KuCoin, MEXC, Bitget and Gate quote in USDT. Coinbase, Kraken and Bitstamp are the only venues in scope that quote natively in USD. If all three fail, that is a distinct failure class, not "three of ten sources down".
Bitfinex has USD books, but a Bitfinex dollar is a balance at Bitfinex, and it trades apart from a dollar on Coinbase or Kraken. On 2026-10-25, BTC and ETH cost 12 to 16 bp more in Bitfinex dollars, and Bitfinex's USDT book sat at par while the US venues quoted 0.9987. Bitfinex and Tether are one group. The feeds therefore take only Bitfinex's USDT books, converted through the peg like those of Binance, Bybit, OKX, KuCoin and MEXC.
CRYPTO.USDT/USD is therefore a feed like any other. The node prices it from the native-USD venues. A /USD feed converts each USDT-quoted source through it:
usd_price = usdt_price × price of CRYPTO.USDT/USDThe uncertainty of the peg adds to the uncertainty of the price, in quadrature. A peg that wobbles makes the whole catalogue wider. It does not misprice it silently. On a live run, with the peg at 1.0 bp and the aggregate floor at 1.0 bp, CRYPTO.BTC/USD published 1.414 bp: exactly √2 × 1 bp.
If the peg feed halts, every feed that depends on it halts too. A USDT number under a /USD symbol would be wrong by the drift of the peg, and nobody would notice.
The same books are also available without conversion, under a /USDT symbol such as CRYPTO.BTC/USDT. Those feeds carry no peg risk.
Warning
CRYPTO.USDT/USD must never come from a venue that quotes in USDT. It would then report 1.0 by construction, on exactly the day that it stops being true. The node config does not check this rule. The operator must follow it.
Feeds and venues today
Every node serves the same feeds: the network's feed set, about 240 feeds, which oracle-node init writes into each node's config. GET /v2/feeds on a node lists them, with symbol, feed ID and exponent. GET /v1/catalog on the web proxy lists what the proxy serves.
Venues
| Venue | Wire index | Spot | Mark | Index | Funding |
|---|---|---|---|---|---|
| Binance | 0 | yes | yes | yes | yes, over its own interval |
| Bybit | 1 | yes | yes | yes | yes, over its own interval |
| OKX | 2 | yes | yes | no | yes, over its own interval |
| Coinbase | 3 | yes | no | no | no |
| Kraken | 4 | yes | yes | yes | yes, hourly |
| Deribit | 5 | no | yes | yes | yes, normalised to 8 hours |
| Hyperliquid | 6 | yes | yes | yes | yes, hourly |
| Bitfinex | 7 | yes | yes | yes | yes, every 8 hours |
| MEXC | 8 | yes | yes | yes | yes, over its own interval |
| KuCoin | 9 | yes | yes | yes | yes, over its own interval |
| Bitstamp | 10 | yes | no | no | no |
| Bitget | 11 | yes | yes | yes | yes, over its own interval |
| Gate | 12 | yes | yes | yes | yes, over its own interval |
| Lighter | 13 | no | yes | yes | yes, hourly |
| dYdX | 14 | no | yes | no | yes, hourly |
The wire index is the bit of the venue in the source_bitmap of a price update. The indexes never change. Indexes 15 to 31 are not assigned. Bitstamp quotes natively in USD and names a market in lowercase, such as btcusd; the feeds read its spot books only. Bitget's USDT perpetuals count in coins; its coin-margined ones, such as BTCUSD_CM, count in USD and price in USD, so they feed CRYPTO.BTC/USD.PERP_MARK and the index with Deribit and Kraken Futures. Its interval, next funding time and last settled rate come on the socket. Gate names a market BTC_USDT; its perpetuals count in contracts of quanto_multiplier coins. Lighter's perpetuals are USDC-margined, taken at par like Hyperliquid's, and named by their market number: 1 is BTC and 0 is ETH. Its rates are in percent, which the node turns into fractions. dYdX is read from its indexer: its mark is the oracle price, taken at par in USDC, and its books are thin, so the feeds take its marks only. Hyperliquid quotes in USDC, which the feeds take at par; its index price is the venue's oracle price, and a spot pair is named by its index, such as @142 for BTC/USDC. Bitfinex names Tether UST: tBTCUST is BTC/USDT, and tBTCF0:USTF0 is the Tether-margined BTC perpetual. Its index is the spot price that the venue sends with the mark. Its live funding rate is the premium accrued in the current 8 hours beyond the venue's dead band of ±0.05%; the venue's data shows this rule, but its docs do not state it. KuCoin names BTC XBT on its perpetuals, as in XBTUSDTM, and sizes them in contracts of multiplier coins. Its socket opens with a token from a REST call. The interval of each perpetual, 1, 4 or 8 hours, its next funding time and its last settled rate come over REST once a minute. MEXC's spot socket sends protobuf, decoded with the venue's published schema; its perpetuals, BTC_USDT, count in contracts of contractSize coins, and their interval and last settled rate come over REST once a minute.
Two markets are deliberately absent:
- Coinbase perpetuals. They trade on Coinbase International. Its market-data socket needs an API key, a passphrase and an HMAC signature for each subscription. The node has no credential path for ingest, so such a config is a startup error.
- Binance coin-margined futures. They use a different host with its own contract list. The node refuses them. It does not fall back to the USD-margined host, which would stream a different contract under a name that looks correct.
The feed set
The network has four groups of feeds. The registry canister serves the whole set as a signed document, and each node takes a new version while it runs.
| Group | Sources | Note |
|---|---|---|
CRYPTO.USDT/USD |
Coinbase, Kraken spot | The peg. Native-USD venues only. |
CRYPTO.<ASSET>/USD, about 75 feeds |
Coinbase, Kraken and Bitstamp in USD; Binance, Bybit, OKX, Bitfinex, KuCoin, MEXC, Bitget and Gate through the peg; Hyperliquid in USDC at par | Spot, for the most traded assets. BTC, ETH and SOL use an impact price. |
CRYPTO.<ASSET>/USDT.PERP_MARK, about 140 feeds |
Binance, Bybit, OKX, Hyperliquid, Bitfinex, KuCoin, MEXC, Bitget, Gate, Lighter and dYdX perpetuals | USDT-margined marks. |
CRYPTO.USDC/USDT, CRYPTO.USDC/USD, 8 /USDC feeds and 6 feeds quoted in BTC or ETH |
The venues' books with no conversion. CRYPTO.USDC/USD takes Kraken's USD book and the USDT books through the peg |
For the arbitrage board. A market with $0.1M a day counts, and CRYPTO.BNB/ETH has one source. XRP/BTC and TRX/BTC are left out: at 8 decimals, one step is 6 and 24 bp. |
A few feeds have other shapes: CRYPTO.BTC/USDT and the BTC and ETH PERP_MID feeds come from the books without conversion; the BTC and ETH /USD.PERP_MARK and PERP_INDEX feeds come from Deribit and Kraken Futures, which quote in USD; the two FUNDING_8H feeds come from Deribit alone, on purpose.
A spot feed takes a venue market only with $1M or more a day, and needs three such markets, so that one quiet venue cannot halt it. A mark feed needs two.
What is not served
OPEN_INTEREST. The symbol is valid, but no feed publishes it yet. The open interest of a perpetual comes with its funding update instead; see Streams.- Funding from several venues. Only Deribit publishes a rate that is truly normalised to 8 hours. The other venues publish a rate over their own interval, and the aggregator does not convert between intervals. A median across them mixes numbers that do not compare. See Funding is not normalised.