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_priceand:fill_size. APaperEx.Positionis 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:pendingexecution to:filledor:cancelled.:cancelled— a previously-:pendingorder was cancelled before any fill.:closed— a previously-:filledexecution'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 thePaperEx.Orderthis attempt handled. Required, but mirrors the order's:idand so may benilwhen the order carried no:id(ids are optional by design).:position_id— link forward to thePaperEx.Positionthe attempt opened or closed, when applicable.nilfor: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:fillsis 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 ofPaperEx.Fillstructs for multi-level fills (one fill per price level walked). Defaults to[]. Multi-level fills must use this list; the scalar:fill_price/:fill_sizecannot represent VWAP semantics on their own.:metadata— free-form map. Long error detail belongs here, not in:reason.:recorded_at—DateTime.t()when the engine recorded this attempt. Defaults toDateTime.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
@type id() :: term() | nil
@type status() :: :filled | :missed | :skipped | :pending | :cancelled | :closed
Functions
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.
@spec statuses() :: [status()]
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.