Normalised order, returned by every venue package.
:created_at and :updated_at are the venue's own values where it supplies them
and nil where it does not. nil is the honest answer; a substituted local clock
would be a plausible value with the wrong meaning.
Not every venue supports every order_type/0 or reports every status/0. The
union here is what the contract can express; what a given venue can actually accept
is declared by that package's capabilities/0, and asking for one it does not support
is an error rather than a silent downgrade to the nearest available type.
Why the enforced keys still admit nil
All seven are enforced, and all but provider allow nil. That is not laxity — it is the
same rule as everywhere else here: the venue's word, or nothing.
A venue sending a status this package does not recognise gets nil, never the nearest
atom a caller might branch on. Coinbase's close_position/3 returns an order whose side
the venue never states — the venue worked the side out from a position this package did
not read — and filling in :sell because closing is usually selling is wrong exactly
where it matters, on a short.
symbol and id joined them on 2026-09-01. Robinhood acknowledges a cancel request
without describing the order it cancelled: there is an id and nothing else, and inventing
a symbol to satisfy a type would put a guess where the venue was silent.
The keys stay enforced so a constructor must decide. The types admit nil so the
decision can be "the venue did not say".
Summary
Types
@type order_type() ::
:market | :limit | :stop | :stop_limit | :post_only | :ioc | :fok
@type side() :: :buy | :sell
@type status() ::
:pending
| :open
| :partially_filled
| :filled
| :cancelled
| :rejected
| :expired
@type t() :: %DpExchange.Core.Types.Order{ average_price: Decimal.t() | nil, created_at: DateTime.t() | nil, fee: Decimal.t() | nil, fee_currency: String.t() | nil, filled_quantity: Decimal.t() | nil, id: String.t() | nil, legs: [DpExchange.Core.Types.OrderLeg.t()] | nil, order_type: order_type() | nil, price: Decimal.t() | nil, provider: atom() | String.t(), quantity: Decimal.t() | nil, side: side() | nil, status: status() | nil, stop_price: Decimal.t() | nil, symbol: String.t() | nil, time_in_force: atom() | nil, updated_at: DateTime.t() | nil }
Functions
Builds a t/0, failing closed only if :provider is absent or nil.
Every other enforced key may legitimately be nil here — see the moduledoc's "Why the
enforced keys still admit nil". @enforce_keys still guards their presence; this adds
the one field where nil was never intended, using
DpExchange.Core.Types.Validate.