PaperEx.MarketSnapshot (PaperEx v0.8.0)

Copy Markdown View Source

A point-in-time normalized order book snapshot.

A MarketSnapshot is the input an execution model uses to simulate a fill: it carries the resting bids and asks for one PaperEx.Instrument, in normalized form (price descending for bids, price ascending for asks).

Adapters produce snapshots from exchange-specific payloads via the PaperEx.Adapter.normalize_snapshot/2 callback. The engine never fetches a snapshot itself — this package does no IO. Callers either pass a snapshot in directly or build one from data their adapter normalized.

Fields

  • :instrument_id (required) — matches a PaperEx.Instrument.id. The engine uses this to associate the snapshot with the right position bucket.
  • :bids — list of {price, size} tuples, sorted descending by price (best bid first). Defaults to [].
  • :asks — list of {price, size} tuples, sorted ascending by price (best ask first). Defaults to [].
  • :as_ofDateTime.t() when the snapshot was observed. Defaults to DateTime.utc_now/0.
  • :metadata — free-form map. Adapters can stash raw payloads here.

Convenience accessors

  • best_bid/1, best_ask/1{price, size} of the top level on each side, or nil if that side is empty.
  • midpoint/1(best_bid_price + best_ask_price) / 2, or nil if either side is empty.
  • spread/1best_ask_price - best_bid_price, or nil if either side is empty.

The engine and the default execution model use these accessors rather than indexing the lists directly, so adapters do not need to expose extra helpers.

Sorting invariant

new/1 does not sort the lists for you; it only validates the shape. Adapters are responsible for producing already-sorted lists. That keeps new/1 cheap and the data contract explicit: a snapshot that comes off an exchange WebSocket usually arrives sorted.

Summary

Functions

Best ask level, or nil if there are no asks.

Best bid level, or nil if there are no bids.

Midpoint price (best_bid_price + best_ask_price) / 2, or nil if either side is empty.

Builds a MarketSnapshot from a map or keyword list.

Bid-ask spread best_ask_price - best_bid_price, or nil if either side is empty.

Types

level()

@type level() :: {price :: number(), size :: number()}

t()

@type t() :: %PaperEx.MarketSnapshot{
  as_of: DateTime.t(),
  asks: [level()],
  bids: [level()],
  instrument_id: term(),
  metadata: map()
}

Functions

best_ask(market_snapshot)

@spec best_ask(t()) :: level() | nil

Best ask level, or nil if there are no asks.

best_bid(market_snapshot)

@spec best_bid(t()) :: level() | nil

Best bid level, or nil if there are no bids.

midpoint(snap)

@spec midpoint(t()) :: number() | nil

Midpoint price (best_bid_price + best_ask_price) / 2, or nil if either side is empty.

new(attrs)

@spec new(keyword() | map()) :: t()

Builds a MarketSnapshot from a map or keyword list.

Required: :instrument_id. :bids and :asks default to []. Each level must be a {price, size} tuple with both numbers non-negative and size > 0.

spread(snap)

@spec spread(t()) :: number() | nil

Bid-ask spread best_ask_price - best_bid_price, or nil if either side is empty.