Esc

    Reference

    Venue reports, books and funding

    The signed VenueReport, BookUpdate and FundingUpdate payloads, and how books travel as snapshots and deltas.

    A node signs three payloads next to the price update:

    • A venue report says what the node saw for each source of one feed, and how the source counted in the price.
    • A book update carries the local book of one venue instrument, so that a client can keep its own copy.
    • A funding update carries the funding of one perpetual, as its venue states it.

    All use the same key, digest and signature format as a price update. See Signing and the price payload. A node builds venue reports and book deltas only for clients that subscribe. See Subscriptions.

    Common rules

    • All integers are big-endian. The version byte is 1.
    • Each payload is canonical. A decoder refuses any other byte form. Trailing bytes are an error.
    • Each frame is frame_type ‖ signer address[20] ‖ payload ‖ signature[65]. The signer address is an unauthenticated routing hint.
    Frame type Payload Kind
    1 PriceUpdate 0x01
    2 VenueReport 0x03
    3 BookUpdate 0x04
    4 FundingUpdate 0x05

    Venue codes are the same as the bits of the source bitmap:

    Code Venue
    0 Binance
    1 Bybit
    2 OKX
    3 Coinbase
    4 Kraken
    5 Deribit
    6 Hyperliquid
    7 Bitfinex
    8 MEXC
    9 KuCoin
    10 Bitstamp
    11 Bitget
    12 Gate
    13 Lighter
    14 dYdX

    Market codes:

    Code Market
    0 spot
    1 linear perpetual
    2 inverse perpetual

    A decoder refuses an unknown venue or market code.

    VenueReport v1

    Kind 0x03. The payload is a 55-byte header and n entries of 96 + L bytes each, where L is the length of the entry's symbol, 1 to 32.

    Offset Size Field Type Meaning
    0 1 version u8 1
    1 1 kind u8 0x03
    2 32 feed_id bytes32 The feed.
    34 8 publish_time_ns u64 When the node signed the report, unix nanoseconds.
    42 8 seq u64 For each (node, feed) pair. It is a separate counter from the price seq.
    50 4 expo i32 The feed's exponent, for every price in the entries.
    54 1 n u8 The number of entries. One entry for each configured source, in config order.

    Entry

    Each entry is 96 + L bytes, where L is the length of its symbol. Offsets are from the start of the entry.

    Offset Size Field Type Meaning
    0 1 venue u8 Venue code.
    1 1 market u8 Market code.
    2 1 L u8 Symbol length, 1–32.
    3 L symbol ASCII The venue's own symbol for the instrument, such as BTCUSDT.
    3 + L 1 status u8 How the source counted. See Source status codes.
    4 + L 2 present u16 Presence bits. Bit i is set when slot i holds a value.
    6 + L 2 weight_bps u16 The source's share of the total weight of the price, 0–10,000. It is 0 unless the source was used.
    8 + L 80 slots 10 × 8 bytes Ten optional values. An absent value is all zeros.
    88 + L 8 local_recv_ns u64 The node's receipt time of the newest observation, or 0 with no data.

    The ten slots, in order:

    Slot Presence bit Value Type Unit
    0 0x0001 best bid i64 price at expo
    1 0x0002 best ask i64 price at expo
    2 0x0004 bid size u64 base units at exponent −8
    3 0x0008 ask size u64 base units at exponent −8
    4 0x0010 impact bid i64 price at expo
    5 0x0020 impact ask i64 price at expo
    6 0x0040 mark i64 price at expo
    7 0x0080 index i64 price at expo
    8 0x0100 funding i64 the venue's own rate over its own interval, at exponent −9
    9 0x0200 venue timestamp u64 unix nanoseconds, venue clock

    Bits 10–15 of present must be 0.

    Source status codes

    Code Status Meaning
    0 used The source is in the price, with a weight.
    1 outlier Outlier rejection dropped the source.
    2 stale The newest data of the source is older than the staleness limit of the feed.
    3 crossed The book of the source is crossed.
    4 no data No data yet, or none since its connection dropped.
    5 invalid The data cannot be priced, for example a book price of zero.
    6 feed halted The source had a usable quote, but the feed halted.
    7 peg unavailable The source quotes in the feed's peg asset, and the peg has no usable price, so it dropped out.

    Report rules

    • Prices before conversion. The prices are the venue's own, before any peg conversion. The status and the weight refer to the converted price.
    • Impact prices. The impact prices are for the feed's impact_notional, from the node's local book. A book that runs out of levels gives no impact price.
    • Status. The status comes from the last evaluation of the feed. See Aggregation.
    • Symbol. 1–32 bytes of printable ASCII without spaces. It names the instrument without the node's config.
    • Canonical form. A decoder refuses an unknown venue, market or status code, a bad symbol, an unknown present bit, a non-zero absent slot, and a weight above 10,000.
    • Cadence. One report after new input, at most one a second, and one at each heartbeat. The node signs a report only for a feed that some client subscribed to. The first report goes out at the next evaluation after the subscription.
    • Sequence. The node seeds the report seq from its clock in milliseconds at startup, as for prices.

    Report size bounds

    Item Minimum Maximum
    Entries n 0 32
    Entry, 96 + L bytes 97 128
    Payload 55 4,151
    Frame, payload + 86 bytes 141 4,237

    A feed with more than 32 sources is a config error.

    Verify a report

    A consumer verifies a report like a price update: the signature, the registry, the clock and its own seq for each (node, feed) pair. The coherence check is different: each entry's local_recv_ns must not be later than publish_time_ns. A consumer does not combine reports across nodes.

    BookUpdate v1

    Kind 0x04. A book update is a snapshot of the top 50 levels on each side, or a delta: the levels that changed since the previous frame. The offsets after the symbol depend on its length L.

    Offset Size Field Type Meaning
    0 1 version u8 1
    1 1 kind u8 0x04
    2 1 venue u8 Venue code.
    3 1 market u8 Market code.
    4 1 L u8 Symbol length, 1–32.
    5 L symbol ASCII The venue's own symbol, such as BTCUSDT.
    5 + L 8 publish_time_ns u64 When the node signed the update, unix nanoseconds.
    13 + L 8 seq u64 For each (node, book) pair. A delta applies to the frame with seq − 1.
    21 + L 1 flags u8 Bit 0: a snapshot. Bit 1: venue_seq holds a value. Other bits are 0.
    22 + L 4 price_expo i32 Always −8 in v1.
    26 + L 4 size_expo i32 Always −8 in v1.
    30 + L 8 exchange_time_ns u64 The venue's timestamp on the newest book data, or 0 when the venue sends none.
    38 + L 8 local_recv_ns u64 The node's receipt time of the newest book data.
    46 + L 8 venue_seq u64 The venue's update id of the newest book data, or 0 when bit 1 is clear.
    54 + L 1 bid count u8 At most 128.
    55 + L 1 ask count u8 At most 128.
    56 + L 16 each levels price i64, size u64 The bids from the highest price, then the asks from the lowest price.

    The payload length is 56 + L + 16 × (bids + asks) bytes.

    Book rules

    • Symbol. 1–32 bytes of printable ASCII without spaces (0x21–0x7E).
    • Exponents. A decoder refuses a price or size exponent other than −8.
    • Order. Prices are strictly ordered on each side: bids descend, asks ascend. No price repeats.
    • Snapshot. A snapshot has no level with size 0.
    • Delta. A delta lists each level whose size changed, with the new size. It lists each level that left the top 50 with size 0. Size 0 removes the level.
    • Counts. A delta of a 50-level window changes at most 100 levels on a side. The limit of 128 covers that.
    • Venue seq. It names the venue book state that the node signed: Binance and Bybit u, OKX seqId, Deribit change_id, and for Hyperliquid the time of the book message, in milliseconds. The node records no venue update id for Coinbase, Kraken, Bitfinex, MEXC and KuCoin, so bit 1 is clear for their books. A zero id is real on some venues, so bit 1, not a zero, marks an absent id. An empty snapshot after a drop has none.

    Book size bounds

    Item Minimum Maximum
    Symbol length L 1 32
    Levels on each side 0 128
    Payload 57 4,184
    Frame, payload + 86 bytes 143 4,270

    How books travel

    The node keeps a local book for each venue instrument that reads depth: a source of a feed with impact_notional set and book_top in its kinds. See Aggregation. The node sends the book to clients in its own format:

    Rule Value
    Depth The top 50 levels on each side.
    Snapshot Every second for each book, whether or not a client subscribed. The next subscriber thus gets a recent snapshot at once.
    Snapshot after a disconnect At once after the venue connection drops. This snapshot is empty until new data arrives.
    Delta The levels that changed since the node's last frame for that book. The node computes it from its own last frame, not from the venue's messages. Thus the format is the same for every venue.
    Rate cap At most one frame for each book every 50 ms.
    Delta demand The node builds a delta only for a book that some client subscribed to. A skipped delta takes no seq.
    Sequence One counter for each (node, book) pair, shared by snapshots and deltas. The node seeds it from its clock in milliseconds at startup.

    A client that applies a snapshot, and then each delta in seq order, holds exactly the node's top 50 levels.

    Keep a book on the client

    Do these steps for each (node, book) pair:

    1. Verify the frame: signature, registry, clock, and a seq greater than the last one.
    2. If the frame is a snapshot, replace the book with its levels.
    3. If the frame is a delta, apply it only when its seq is the last seq plus 1. For each level, set the new size. Remove the level when the size is 0.
    4. If a seq is missing, mark the book invalid. Ignore deltas until the next snapshot.
    5. An empty snapshot means that the venue connection dropped or has no data yet. Do not use that book.

    Keep one book for each node. Never mix the levels of two nodes in one book. The Rust SDK keeps one book for each node and returns the freshest valid one. See Rust SDK. A new subscriber gets the latest snapshot and the deltas after it at once. See Fanout and backpressure.

    FundingUpdate v1

    Kind 0x05. A funding update carries one perpetual's funding. Nothing is converted: the rate is a fraction over the venue's own basis, and open interest is in the venue's own unit.

    Offset Size Field Type Meaning
    0 1 version u8 1
    1 1 kind u8 0x05
    2 1 venue u8 Venue code.
    3 1 market u8 Market code.
    4 1 L u8 Symbol length, 1–32.
    5 L symbol ASCII The venue's own symbol, such as BTC-USDT-SWAP.
    5 + L 8 publish_time_ns u64 When the node signed the update, unix nanoseconds.
    13 + L 8 seq u64 For each (node, perpetual) pair.
    21 + L 1 flags u8 A field holds a value: bit 0 predicted rate, bit 1 interval, bit 2 next funding time, bit 3 settled rate, bit 4 open interest.
    22 + L 1 basis u8 0: the fraction paid at each funding time. 1: the fraction over 8 hours (Deribit funding_8h).
    23 + L 1 open_interest_unit u8 0 base asset, 1 contracts, 2 quote currency. 0 when bit 4 is clear.
    24 + L 4 rate_expo i32 Always −12 in v1.
    28 + L 4 open_interest_expo i32 Always −8 in v1.
    32 + L 8 rate i64 The current rate.
    40 + L 8 predicted_rate i64 The venue's prediction of the next rate.
    48 + L 8 interval_ns u64 The length of one funding period.
    56 + L 8 next_funding_time_ns u64 When the current rate is paid.
    64 + L 8 settled_rate i64 The last rate that the venue settled.
    72 + L 8 settled_time_ns u64 When the settled rate was paid.
    80 + L 8 open_interest u64
    88 + L 8 exchange_time_ns u64 The venue's timestamp on the newest funding data, or 0 when the venue sends none.
    96 + L 8 local_recv_ns u64 The node's receipt time of the newest funding data.

    The payload length is 104 + L bytes: 105 to 136. A frame is 191 to 222 bytes.

    Funding fields by venue

    The node takes the fields from messages that it already reads.

    Venue, channel Rate Predicted Interval Next time Settled Open interest
    Binance perp, markPrice r – fundingInfo (REST); else 8 hours T – openInterest (REST, once a minute), base
    Bybit perp, tickers fundingRate – fundingIntervalHour nextFundingTime – openInterest, base
    OKX swap, funding-rate fundingRate nextFundingRate nextFundingTime − fundingTime fundingTime settFundingRate at prevFundingTime, once settState is settled oiCcy of open-interest, base
    Kraken Futures, ticker relative_funding_rate relative_funding_rate_prediction 1 hour next_funding_rate_time – openInterest, contracts
    Deribit perp, ticker funding_8h, basis 1 – – – – open_interest, USD for an inverse contract

    Funding rules

    • Flags. A field whose flag is clear is 0, and so is the unit without open interest. A decoder refuses anything else.
    • Whole state. Each frame carries every field that the venue sent, so a client needs only the latest frame. Bybit sends only the changed fields; the node keeps the last value of each until the connection drops.
    • Cadence. A frame goes out on a change, at most once a second for each perpetual, and at least once a minute. The node signs frames whether or not a client subscribed. After a venue connection drops, it signs nothing for that venue's perpetuals until new data arrives.
    • Age. A new subscriber gets the latest frame at once, and that frame can be a minute old. A client accepts a funding frame up to two minutes old.
    • Precision. A rate keeps 12 decimal places. OKX sends 16; the last 4 round away.
    • Binance interval. The markPrice stream does not state the interval. The node gets it from the fundingInfo REST list of Binance, when the connection opens and each hour after. A perpetual that the list leaves out pays every 8 hours. Until the first list arrives, a Binance frame has no interval_ns.

    Golden vectors

    The repository holds test vectors for the wire format: payload bytes, digests, signatures and frames. cargo test -p oracle-wire --test golden checks them, and a change in the vectors means that the wire format changed.