# `DpExchange.Core.Types.Order`
[🔗](https://github.com/DistortionPoint/dp-exchange-core/blob/v0.3.116/lib/dp_exchange/core/types/order.ex#L1)

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 `t:order_type/0` or reports every `t: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".

# `order_type`

```elixir
@type order_type() ::
  :market
  | :limit
  | :stop
  | :stop_limit
  | :post_only
  | :ioc
  | :fok
  | :trailing_stop
  | :trailing_stop_limit
  | :market_on_close
  | :limit_on_close
```

# `side`

```elixir
@type side() :: :buy | :sell
```

# `status`

```elixir
@type status() ::
  :pending
  | :open
  | :partially_filled
  | :filled
  | :cancelled
  | :rejected
  | :expired
```

# `t`

```elixir
@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
}
```

# `new`

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

Builds a `t: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`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
