> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tplus.cx/llms.txt
> Use this file to discover all available pages before exploring further.

# Orders

> Order types, time-in-force, triggers, and validation rules.

## Order types

Tplus supports limit and market orders. Time-in-force for limit orders:

| Time-in-force             | Behavior                                                             |
| ------------------------- | -------------------------------------------------------------------- |
| `GoodTilCancel` (default) | Rests until filled or canceled. Supports `post_only`.                |
| `GoodTilDate`             | Expires at a nanosecond timestamp. Supports `post_only`.             |
| `ImmediateOrCancel`       | Matches immediately, cancels the remainder. Supports `fill_or_kill`. |

Rules:

* All orders expire 30 days after creation, regardless of time-in-force.
* Market orders never rest on the book. Quantity is specified either in base-asset units or in quote/USD terms. Base-sized market buys can carry an optional quote cap; quote-sized market sells can carry an optional base cap.
* Post-only orders are canceled immediately if they would cross the book and be immediately matched.
* Reduce-only orders are rejected if they would increase or flip the position. The OMS pre-checks IOC and market orders with a base quantity; clearing enforces the constraint on every order.
* Fill-or-kill checks that at least one completion dimension can be satisfied before matching or cancels. For capped market orders, either the target quantity/amount or the protective cap can be the completion dimension. The all-or-nothing guarantee holds through clearing (see [finality rules](/architecture#order-lifecycle)).
* Self-trade prevention is an operator opt-in, in cancel-resting mode: when enabled on a market, an incoming order never matches your own resting orders (same signer and sub-account) — the resting order is canceled with reason `SelfTradePrevented` and matching continues against other liquidity. A post-only order that crosses only your own resting orders cancels them and books; crossing anyone else's liquidity still rejects it. Distinct sub-accounts count as distinct owners. On markets without it, your own orders can match — check your open orders before submitting a crossing order.

For a quote-sized market order, the fill uses the volume-weighted price of the liquidity actually matched:

```text theme={null}
fill_vwap = quote_spent_or_received / base_filled
base_filled = quote_spent_or_received / fill_vwap
```

Example: a market buy spends `10,000 USD` and matches at a `2,500 USD` VWAP, so `base_filled = 10,000 USD / 2,500 USD = 4 ETH`. A base-sized market buy for `4 ETH` with a `9,500 USD` quote cap would stop at `3.8 ETH` at the same VWAP.

## Trigger orders

Conditional orders rest in a separate trigger book and move to the main book when a confirmed trade price crosses the trigger price.

* `PriceAbove`: releases when the price rises to the trigger (take-profit).
* `PriceBelow`: releases when the price falls to the trigger (stop-loss).

Parent-child linkage: a child trigger must be on the opposite side of its parent and from the same signer; the parent cannot itself be conditional. Child quantity activates as the parent fills.

## Closing a position

A margin position has two legs (see [margin positions](/trading/margin#creating-accounts-and-transfers)), so closing it takes two steps:

1. **Flatten the asset exposure.** Place an order on the opposite side - sell to close a long, buy to close a short - sized to the position. A market order flattens immediately; a limit or [trigger order](#trigger-orders) (stop-loss / take-profit) flattens at a price. There is no dedicated close order type and no auto-sizing: you submit the quantity. The **reduce-only** flag guarantees the order can only shrink the position - it is rejected if it would flip or increase exposure.
2. **Settle the residual quote.** Flattening the asset to zero does not remove the position; it lingers as a flat position holding its realized PnL in the quote (USD) leg. `POST /account/transfer/close-position` folds that quote into the sub-account's spot balance and removes the position. It is rejected while the asset balance is non-zero, so flatten first. The freed spot balance can then be transferred or withdrawn. If the quote balance is negative the user must have sufficient USD in their account to offset the negative balance.

To flatten an entire sub-account, `GET /positions/close-all/{user_id}/{sub_account}` returns unsigned suggested close orders for open positions in that sub-account. It is a preview: sign each returned order, submit the signed orders with `POST /orders/batch-create`, then settle each position's quote.

## Order fields

| Field                      | Notes                                                          |
| -------------------------- | -------------------------------------------------------------- |
| Signer public key          | Must match the authenticated user                              |
| Order ID                   | User-defined, unique per user, max 24 bytes                    |
| Asset                      | Market identifier; list via `GET /markets`                     |
| Price/quantity decimals    | Validated against the market config (`GET /market/{id}`)       |
| Side                       | Buy or sell                                                    |
| Order details              | Limit price or market-order quantity parameters, time-in-force |
| Trigger                    | Optional                                                       |
| Creation timestamp         | Client-set, nanoseconds                                        |
| Target                     | Sub-account index + spot-vs-margin flag                        |
| Reduce-only flag           | Optional                                                       |
| Max trading fee rate (ppm) | The order will not match at a higher fee                       |
| Protocol version           | Current protocol version constant                              |

Orders can carry additional co-signers, validated against the account's [multisig configuration](/trading/multisig).

## Managing orders

* **Replace** updates price, quantity, or trigger on a resting limit order atomically. It requires a new signature and passes a fresh margin check. Fill state of a partially-filled order is preserved.
* **Cancel** over REST returns `ORDER_NOT_FOUND` if the OMS cannot find the target order for that user, asset, and order ID; REST does not hold absent orders for later arrival. The order-control channel forwards cancels directly to the orderbook, where a cancel that reaches the fast queue before its create can be held and used to reject the later create.
* **Batch create** accepts up to 50 orders per request and returns per-order statuses.
* **Cancel-all** (all markets or one market, optional `max_ts` cutoff) and **cancel-batch** (by ID) return `202` on admission: dispatched, not done. Confirm via the orders WebSocket stream or a follow-up query. IDs not owned by the caller are silently excluded.

## Advanced edge cases

| Edge case                 | Behavior                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Batch create atomicity    | Batch create is not all-or-nothing. The OMS validates and forwards each order in sequence and returns one result per order. Orders accepted before a later rejection remain in their normal lifecycle and can later fill, rest, cancel, or roll back through clearing like any other accepted order.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| Trigger child lifecycle   | Trigger orders rest in the trigger book as `Pending`. A parent-linked trigger child starts with enabled quantity `0`; confirmed fills on the parent enable child quantity, and a child created after confirmed partial fills can receive already confirmed parent quantity while the parent remains live. When a child trigger fires, it leaves the trigger book, the orderbook attempts to cancel any still-live parent, and the child is submitted to the main book. Canceling a child before activation removes it from the trigger book. Canceling or expiring the parent does not cancel its child trigger orders; cancel TP/SL children explicitly if you no longer want them to trigger.                                                                                                            |
| Cancel/replace precedence | REST replace and REST cancel first look up the current OMS order and return `ORDER_NOT_FOUND` if it is absent. In the orderbook, cancel removes an enabled order from the main book or a pending order from the trigger book. Enabled-order replace removes the existing order and creates the replacement under the same internal ID while carrying pending and confirmed fill state; pending-trigger replace removes the existing trigger and creates the replacement in the trigger book. There is no deterministic precedence between a cancel and a replace for the same order beyond queue-arrival order. A replace that reaches the queue after its cancel fails with `ORDER_NOT_FOUND`. Control-channel cancels that arrive before their create can reject the later create as `AlreadyCancelled`. |
| Market protective caps    | Market orders never rest. A base-sized market buy stops at its base quantity or optional quote cap; a base-sized market sell stops at its base quantity. A quote-sized market sell stops at its quote target or optional base cap; a quote-sized market buy stops at its quote target. If no trade is produced, the order is canceled. Any IOC remainder is canceled after matching. FOK market orders cancel before matching if available matching levels cannot satisfy the target quantity/amount or protective cap.                                                                                                                                                                                                                                                                                    |
| Self-trade                | Self-trade prevention is an operator opt-in per orderbook, in cancel-resting mode. When enabled, a maker owned by the incoming order's signer and sub-account is canceled (`SelfTradePrevented`) instead of traded against; FOK fillability and post-only crossing checks ignore your own resting liquidity (post-only then cancels it and books). Different sub-accounts of the same signer are distinct owners. When disabled, matching does not reject same-signer maker/taker pairs — mitigate in the client by checking open orders before submitting crossing orders.                                                                                                                                                                                                                                |

## Order states

A queried order reports one of these states:

| State       | Meaning                                                                  |
| ----------- | ------------------------------------------------------------------------ |
| `Pending`   | Accepted but not yet active (e.g., a trigger order awaiting its trigger) |
| `Open`      | Resting, unfilled                                                        |
| `Partial`   | Resting, partially filled                                                |
| `Completed` | Fully filled                                                             |
| `Canceled`  | Canceled with no fills                                                   |
| `Closed`    | Canceled after a partial fill                                            |

A resting order's reported filled quantity includes an optimistic component from pending fills that can roll back; the confirmed filled quantity counts only clearing-engine-finalized fills.

## Validation

Checks run in this order:

1. OMS: rate limits (global and per token), signer identity, order ID rules, reduce-only pre-check.
2. OMS: margin pre-flight - trading capacity including the order pressure of existing open orders, at both oracle and mark price, taking the worse. Failures return `InsufficientInventory` or `InsufficientMargin`. See [Margin](/trading/margin#trading-capacity-and-order-pressure).
3. Clearing Engine: signature, overfill, order parameters, protocol caps, reduce-only, post-trade IM. Failures here arrive as rollbacks on the trade-event stream. Fills that reach clearing more than 5 minutes after matching are rolled back.

Rollback reasons a client can receive include: margin failure, pricing failure (stale oracle or circuit breaker), protocol cap breach, wrong signature, invalid order conditions, overfill, stale match, and reduce-only violation. Only the at-fault party sees its reason; counterparties see `CounterpartyAtFault`.

## Auto-reduce

If price moves make an account's [trading capacity](/trading/margin#trading-capacity-and-order-pressure) negative, the OMS reduces or cancels its open orders automatically, farthest-from-market first, with a per-sub-account cooldown. IOC/FOK and in-flight orders are never auto-reduced. Server-initiated reductions appear on the orders WebSocket stream. Market-maker accounts are exempt.

For normal limit orders in the same market, farthest-from-market means the largest percentage distance from the current market reference:

```text theme={null}
distance = abs(order_price - reference_price) / reference_price
```

Example: with a `100 USD` reference price, normal open buys at `80 USD` and `95 USD` are `20%` and `5%` away, so the `80 USD` order is reduced first. Trigger orders are prioritized by their trigger-side semantics rather than this simplified normal-order example.

## Reading orders and trades

REST reads are paginated (zero-based `page` plus `limit`): orders per user, per user and market, or by ID lookup, with an `open_only` filter; confirmed trade history per user. Reads span all of the caller's sub-accounts. Pending and rolled-back fills are visible only on the [WebSocket streams](/api-reference/websockets).
