Placing orders

How matching works: frequent batch auctions

MNX does not match orders the instant they arrive. Orders are collected into short batches and matched at discrete intervals (about every 200ms) — a Frequent Batch Auction (FBA). Each batch clears at a single uniform clearing price: the price that maximizes matched volume, applied to every trade in the batch. Orders eligible on each side are allocated pro rata (proportionally to their remaining size) rather than by queue position.

Practical consequences for an API client:

  • A successful POST /v0/orders means your order was validated and accepted into the book — not that it traded. Matching happens in a subsequent batch.
  • Market orders are immediate-or-cancel: whatever matches in the next batch fills, and the rest is cancelled.
  • All fills in one batch settle at the same clearing price, which may be better than your limit price.
  • Sub-batch-interval speed buys nothing; there is no race to the top of a queue within a batch.

The place-order request

POST /v0/orders (authenticated). The order must carry an EIP-712 signature — see Authentication & signing. A representative limit order:

POST /v0/orders
{
  "market_id": 1,
  "side": "LONG",
  "order_type": "LIMIT",
  "price": 2000,
  "quantity": 0.5,
  "leverage": 2,
  "expiration_seconds": 0,
  "salt": "123456789",
  "signature": "0x...",
  "signature_type": 1
}
FieldMeaning
market_idThe market to trade, from GET /v0/markets.
sideLONG (buy) or SHORT (sell).
order_typeMARKET, LIMIT, or a conditional type (below).
priceLimit price in human units (omit or 0 for market orders). Must be a multiple of the market's tick size (the smallest price increment).
quantity or quantity_e18_rawSigned order size in base units, either as a decimal quantity or as an unsigned 1e18-scaled raw integer. Placement is quantity-only: use the estimate endpoints before signing when you want to size from a USD notional, a margin percentage, Max, or a reduce percentage. Quantity must be a multiple of the step size and at least the minimum order size, except for exact reduce-only full closes.
leveragePosition leverage — how many dollars of exposure per dollar of margin (collateral) you put up. A positive whole number, capped by the market's maximum; defaults to 1.
expiration_secondsUnix timestamp (in seconds) after which the order expires, or 0 for good-til-canceled.
trigger_priceRequired for conditional order types.
reduce_onlyIf true, the order may only shrink an existing position, never open or grow one.
salt, signature, signature_typeThe EIP-712 signature material; the salt and expiration must match the values signed into the order struct.

The server validates the signature, checks the order against the market's parameters (tick size, step size, minimum and maximum order size, leverage cap, and price bands around the oracle price), and reserves margin for it. Orders that open or increase a position reserve full margin up front; orders that close an existing position reserve no margin, since they reduce exposure. The exact market parameters are returned by GET /v0/markets.

Two helper endpoints let you pre-check and size an order before signing: POST /v0/orders/calculate-quantity (legacy helper for converting a USD budget into a quantity, including an estimated clearing price) and POST /v0/orders/estimate (the sizing-intent endpoint for typed notional, typed units, available margin, Max, and reduce-position percentages). Placement still sends only the signed quantity plus order fields; do not send usd_amount, sizing_intent, sizing_value, or margin_fraction to POST /v0/orders. Use quantity_e18_raw when the exact signed quantity must avoid decimal-number rounding, such as a 100% reduce-only close of an off-step position. If both quantity forms are supplied, they must match exactly. Send size: 0 to the estimate endpoint when only maximum sizing is needed; limit-order estimates still require price.

Order types

Beyond MARKET and LIMIT, six conditional types wait for the market's mark price (used for unrealized profit and loss and conditional triggers) to cross a trigger before entering the matching pipeline:

TypeExecutes asReduce-only
STOP_MARKET / STOP_LIMITMARKET / LIMITOptional
TAKE_PROFIT_MARKET / TAKE_PROFIT_LIMITMARKET / LIMITAlways
STOP_LOSS_MARKET / STOP_LOSS_LIMITMARKET / LIMITAlways

Conditional orders rest with status PENDING_TRIGGER and reserve margin up front like any other order. Triggers are evaluated after each mark-price update. Stop and stop-loss orders trigger when the mark price moves againstthe order's direction (mark ≥ trigger for a buy, mark ≤ trigger for a sell); take-profit orders trigger on the favorable crossing (mark ≤ trigger for a buy, mark ≥ trigger for a sell). On trigger, the order becomes a regular market or limit order with status OPEN and enters the next batch.

The response

A successful placement returns the accepted order — including its order_id and order_hash, which you need for cancels and status reads — plus a fills array, an updated balance snapshot when available, and a read_after bookmark you can pass to subsequent reads for read-your-own-writes consistency (see Getting started). Because matching happens at the next batch, expect fills to arrive after placement, not in it.

Bulk placement

POST /v0/orders/place/batch accepts up to 50 signed orders in one retry-safe command. Send a stable client_request_id, an orders array, and either atomic or best_effort mode. Atomic mode means every order is accepted into the batch-auction order book together or none are; it does not guarantee that every order fills. Market orders remain immediate-or-cancel at the next auction tick.

The response returns one final balance snapshot for the whole command. Accepted items contain their order and read_after bookmark; they do not repeat the same balance or include fills, because matching happens after placement.

Cancelling orders

  • POST /v0/orders/cancel/by-hash/:order_hash — cancel one order by its hash.
  • POST /v0/orders/cancel/all — cancel all of your open orders, optionally scoped with a market_id.
  • POST /v0/orders/cancel/batch — cancel a specific list of up to 100 orderHashes in one call. Send a stable client_request_id and reuse it when retrying the same request. Authentication authorizes the cancellation; no separate cancellation signature is required.

Bulk-cancel clients must use the current request shape: client_request_id is required and the former cancelSignature field has been removed. Requests using the old strict schema are rejected.

An optional cancellation reason is limited to 200 characters; longer input is rejected. The generated endpoint reference exposes this schema limit. Keep client-supplied reasons short.

Cancel responses include an is_duplicate flag: retrying a cancel that already succeeded is safe and reports itself as a duplicate rather than failing.

Reading order status and fills

Order status is one of OPEN, FILLED, CANCELLED, EXPIRED, PENDING_TRIGGER, or CANCEL_PENDING_FINALIZE (a cancel accepted while a batch containing the order is settling).

  • GET /v0/orders/by-user/:user_id — your orders, filterable by market, status, and side.
  • GET /v0/orders/:order_id— one order's current state.
  • GET /v0/orders/:order_id/fills — the fills (individual matched executions) of one order.
  • GET /v0/orders/:order_id/events and GET /v0/users/:user_id/order-events — the lifecycle event log (CREATED, PARTIALLY_FILLED, FILLED, CANCELLED, EXPIRED).
  • GET /v0/orders/:order_id/fill-processing-status — whether fills are still being settled on-chain.

For push-based updates instead of polling, subscribe to your user topic on the WebSocket feed — see WebSockets.