PaperEx.Execution (PaperEx v0.8.0)

Copy Markdown View Source

A first-class record of an execution attempt.

An Execution represents what the engine actually did with a PaperEx.Order — whether it filled, was missed, was skipped before reaching the book, is still pending, was cancelled, or has been closed out. Misses and skips are not absorbed into "no trade"; they are peer values to :filled, with the same first-class shape and metadata.

This is the design decision that lets a future engine support both research mode (idealized fills) and live-mirror mode (realistic execution constraints) on the same domain. See PaperEx's moduledoc for the two-modes design constraint.

Status values

  • :filled — the engine simulated a fill at :fill_price and :fill_size. A PaperEx.Position is typically created alongside (linked via :position_id).
  • :missed — the engine attempted execution but the market moved past the limit, the order book had insufficient liquidity, or the fill model otherwise declined.
  • :skipped — the engine refused to attempt execution at all, typically because a risk rule, policy gate, or pre-flight check filtered it out before reaching the book.
  • :pending — a resting order that has not yet filled, been cancelled, or expired. Engine state-transition functions may later move a :pending execution to :filled or :cancelled.
  • :cancelled — a previously-:pending order was cancelled before any fill.
  • :closed — a previously-:filled execution's associated position has been closed out (resolved, sold, expired). Mostly a convenience flag — the position's own status carries the authoritative close info.

Reason codes

:reason is intended to be a short, bounded value (atom or short string) — :price_too_high, :insufficient_liquidity, :duplicate_position, :cooldown, etc. Long error detail goes in :metadata, never in :reason. This mirrors a lesson the production source learned: long error strings in narrow status fields make analytics impossible.

Fields

  • :id — caller-assignable identifier (any term).
  • :order_id — link back to the PaperEx.Order this attempt handled. Required, but mirrors the order's :id and so may be nil when the order carried no :id (ids are optional by design).
  • :position_id — link forward to the PaperEx.Position the attempt opened or closed, when applicable. nil for :missed / :skipped / :cancelled.
  • :status (required) — one of the status atoms above.
  • :reason — short bounded reason code; atom or string. Required for :missed, :skipped, :cancelled. Optional for :filled / :pending / :closed (where the status itself is the reason).
  • :fill_price, :fill_size — populated for :filled. When :fills is non-empty, these are the volume-weighted average price and total filled size aggregated across the fills. They remain settable directly for single-level convenience.
  • :fills — optional list of PaperEx.Fill structs for multi-level fills (one fill per price level walked). Defaults to []. Multi-level fills must use this list; the scalar :fill_price / :fill_size cannot represent VWAP semantics on their own.
  • :metadata — free-form map. Long error detail belongs here, not in :reason.
  • :recorded_atDateTime.t() when the engine recorded this attempt. Defaults to DateTime.utc_now/0.

Summary

Functions

Builds an Execution from a map or keyword list.

Returns the list of valid :status atoms. Useful for callers building UI / filter helpers without re-stating the list.

Total fees across :fills. 0 when the fills list is empty.

Total filled size summed across :fills. For a single-level (scalar-only) execution — :fills == [] but :fill_size set — it falls back to that scalar size. 0 only when neither is set.

Volume-weighted average price across :fills. For a single-level (scalar-only) execution — :fills == [] but :fill_price set — it falls back to that scalar price. Returns nil only when neither is available.

Types

id()

@type id() :: term() | nil

status()

@type status() :: :filled | :missed | :skipped | :pending | :cancelled | :closed

t()

@type t() :: %PaperEx.Execution{
  fill_price: number() | nil,
  fill_size: number() | nil,
  fills: [PaperEx.Fill.t()],
  id: id(),
  metadata: map(),
  order_id: term(),
  position_id: term() | nil,
  reason: atom() | String.t() | nil,
  recorded_at: DateTime.t(),
  status: status()
}

Functions

new(attrs)

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

Builds an Execution from a map or keyword list.

Required keys: :order_id, :status. :reason is required for :missed, :skipped, and :cancelled. :fill_price and :fill_size are required for :filled.

Raises ArgumentError on shape violations.

statuses()

@spec statuses() :: [status()]

Returns the list of valid :status atoms. Useful for callers building UI / filter helpers without re-stating the list.

total_fees(execution)

@spec total_fees(t()) :: number()

Total fees across :fills. 0 when the fills list is empty.

total_size(execution)

@spec total_size(t()) :: number()

Total filled size summed across :fills. For a single-level (scalar-only) execution — :fills == [] but :fill_size set — it falls back to that scalar size. 0 only when neither is set.

vwap(execution)

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

Volume-weighted average price across :fills. For a single-level (scalar-only) execution — :fills == [] but :fill_price set — it falls back to that scalar price. Returns nil only when neither is available.