Esc

    Concepts

    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:

    text
    <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:

    text
    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 code

    The parser refuses each of these:

    text
    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 only

    In Rust, a Symbol is parsed, not validated. You cannot hold one in a non-canonical form. Two independent implementations that agree on this grammar therefore agree on every feed ID.

    Feed IDs

    A feed ID is 32 bytes:

    text
    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 aggregator works with Fixed: an i128 mantissa at a fixed scale of 10^-12. Division and multiplication round halves away from zero. For this reason a recorded set of observations replays to the same price, bit for bit, months later, 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:

    json
    {
      "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 and OKX quote in USDT. Coinbase and Kraken are the only venues in scope that quote natively in USD. If both of them fail, that is a distinct failure class, not "two of five sources down".

    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:

    text
    usd_price = usdt_price × price of CRYPTO.USDT/USD

    The 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

    Each node serves the feeds that its config file lists. There is no catalogue for the whole network. GET /v2/feeds on a node lists its feeds, 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

    The wire index is the bit of the venue in the source_bitmap of a price update. The indexes never change.

    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.

    Feeds that this build can serve

    The repository has a config with every feed kind that the current build can serve. This list comes from it:

    Feed Sources Note
    CRYPTO.USDT/USD Coinbase, Kraken spot The peg. Native-USD venues only.
    CRYPTO.BTC/USD Coinbase, Kraken; Binance, Bybit, OKX converted Impact price for 20000.
    CRYPTO.ETH/USD Coinbase, Kraken; Binance, Bybit, OKX converted Impact price for 20000.
    CRYPTO.SOL/USD Coinbase, Kraken; Binance, Bybit, OKX converted Impact price for 6000.
    CRYPTO.BTC/USDT Binance, Bybit, OKX spot No conversion.
    CRYPTO.BTC/USD.PERP_MARK Deribit, Kraken Futures Both quote in USD.
    CRYPTO.ETH/USD.PERP_MARK Deribit, Kraken Futures Both quote in USD.
    CRYPTO.BTC/USD.PERP_INDEX Deribit, Kraken Futures The index that the mark follows.
    CRYPTO.BTC/USDT.PERP_MARK Binance, Bybit, OKX perpetuals USDT-margined.
    CRYPTO.ETH/USDT.PERP_MARK Binance, Bybit, OKX perpetuals USDT-margined.
    CRYPTO.BTC/USDT.PERP_MID Binance, Bybit, OKX perpetual books Where the perpetuals trade.
    CRYPTO.ETH/USDT.PERP_MID Binance, Bybit, OKX perpetual books Where the perpetuals trade.
    CRYPTO.BTC/USD.FUNDING_8H Deribit only Single source on purpose.
    CRYPTO.ETH/USD.FUNDING_8H Deribit only Single source on purpose.

    The config that oracle-node init writes for a new node has one feed: CRYPTO.BTC/USDT, from Binance, Bybit and OKX spot.

    What is not served

    • OPEN_INTEREST. The symbol is valid, but no decoder in this build emits open interest. A feed for it would halt permanently.
    • 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 message does not say which interval. A median across them mixes numbers that do not compare. See Funding is not normalised.