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

An open position in a derivative — distinct from a balance, and not derivable from one.

## A position is not a balance

A balance says how much of an asset an account holds. A position says what exposure it has
taken, at what price, financed how, and how far it is from being liquidated. A spot venue
has balances and no positions; a perpetuals venue has both, and they answer different
questions. Collapsing them would leave a caller unable to ask the only question that
matters on a leveraged book: how much room is left.

## `:side` is explicit, and `:quantity` is always positive

Venues disagree about how to say "short". Some send a negative quantity, some a side
field, some both — and a package that guessed would produce a position that is exactly
backwards while every number in it stays plausible. **A sign convention is not a fact
about the market; it is a fact about one venue's JSON**, and it does not belong in the
contract.

So `:quantity` is the size, always positive, and `:side` is `:long` or `:short`. A venue
sending `-0.2` converts on the way in and says which it meant.

## Realised and unrealised are never added together

`:realised_pnl` has happened; `:unrealised_pnl` is a mark-to-market opinion that changes
with the next tick and may never be realised. A single "pnl" field would invite a caller
to book profit it does not have.

## `:liquidation_price` being `nil` does not mean safe

It means **the venue did not say**. On a leveraged position that is the single most
consequential unknown in this struct, and reading `nil` as "no liquidation risk" is the
substitution this contract is written against. A caller that needs the number and finds
`nil` must treat the position as un-assessed, not as safe.

# `side`

```elixir
@type side() :: :long | :short
```

# `t`

```elixir
@type t() :: %DpExchange.Core.Types.Position{
  average_cost: Decimal.t() | nil,
  instrument_type: atom() | nil,
  leverage: Decimal.t() | nil,
  liquidation_price: Decimal.t() | nil,
  mark_price: Decimal.t() | nil,
  notional_value: Decimal.t() | nil,
  provider: atom(),
  quantity: Decimal.t(),
  realised_pnl: Decimal.t() | nil,
  side: side(),
  symbol: String.t(),
  unrealised_pnl: Decimal.t() | nil,
  venue_time: DateTime.t() | nil
}
```

# `from_signed_quantity`

```elixir
@spec from_signed_quantity(Decimal.t()) :: {side(), Decimal.t()}
```

Normalises a venue's signed quantity into `{side, positive_quantity}`.

Provided here so the convention is applied identically everywhere. A venue that sends a
side field of its own should use that instead and ignore this — **this is for venues whose
only statement of direction is the sign**, and it exists because getting it wrong produces
a position that is exactly backwards and looks entirely normal.

Zero is `:long` by convention, and a zero-quantity position should generally not be built
at all: a closed position is not an open one.

# `new`

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

Builds a `t:t/0`, failing closed if a required field is absent or `nil`.

`@enforce_keys` guards presence, not `nil` — see `DpExchange.Core.Types.Validate`.

Also raises on a `:quantity` of zero or less (the size is always positive; a flat account
holds no position), and on a `:side` that is not `:long` or `:short`. A position built `:buy` is
read by a consumer matching the two sides as no side at all, or as the wrong one, and
the contract's whole point is that the sign never decides direction twice.

---

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