Normalised order book snapshot, returned by every venue package.
bids and asks are sorted by price, best price first:
bidsdescending — highest bid firstasksascending — lowest ask first
Each level is a {price, quantity} tuple. The ordering is part of the contract, not a
convenience: a caller reading hd(bids) as the best bid is reading it correctly, and a
venue package that returns venue-order without re-sorting has broken the contract even
though every value in it is true.
:venue_time is the venue's own, used as-is, and nil where the venue publishes none
— a nil there says the venue did not date this book, which is information rather than an
omission. :observed_at is when this package read it, and is always present.
These replaced a single :timestamp in 0.2.0. See DpExchange.Core.Types.Quote's own
moduledoc for the full reasoning, and docs/design/2026-09-09_venue-time-and-observed-time.md
for the decision: the short version is that a venue publishing no time for a frame — which
Gemini's partial-depth snapshot genuinely does not — had to either lie in a field
documented as the venue's or drop real data, and one field could not tell a consumer which
had happened.
dp_exchange_schwab's Streamer book is the model for :venue_time: it reads the venue's
snapshot_time and fails closed when absent, rather than substituting.
:sequence is the venue's book sequence number where it publishes one, for callers
reconciling snapshots against a delta stream, and nil where it does not.
Summary
Types
@type t() :: %DpExchange.Core.Types.OrderBook{ asks: [level()], bids: [level()], observed_at: DateTime.t(), provider: atom() | String.t(), sequence: integer() | nil, symbol: String.t(), venue_time: DateTime.t() | nil }
Functions
Builds a t/0, failing closed if a required field is absent or nil.
@enforce_keys guards presence, not nil — see DpExchange.Core.Types.Validate.