Skip to main content
All trading endpoints need a key with the mm:trade scope. Placing a new order also needs trading to be available for your account. See Trading availability. Trading requests take a JSON body of at most 4 KiB and no query string. Send Content-Type: application/json whenever the request has a body.

Place an order

POST /mm/v1/orders
string
required
Your identifier for this order. 1–64 characters: a letter or digit, then letters, digits, ., _, : or -. Unique per account, permanently. See clientOrderId and idempotency.
string
required
The market, from GET /mm/v1/markets.
string
required
BUY or SELL, exactly. SELL is sell-to-close only.
string
required
LIMIT, exactly. It is the only order type.
Exact
required
Limit price as { units, decimals }, per the market’s premiumUnit. It must be a multiple of the market’s priceTick, written with the same decimals. units is a positive integer string with no sign or leading zeros.
Exact
required
Quantity in contracts as { units, decimals }. It must be a multiple of the market’s quantityStep, written with the same decimals.
Every field is required, and nothing else is accepted. An unknown field anywhere in the body, a JSON number instead of a string for units, or a lower-case side returns 400 MALFORMED_REQUEST. You never send an account, an order ID or a nonce. Orders are good until cancelled. There is no time-in-force, post-only, reduce-only or amend. To change an order, cancel it and place a new one with a new clientOrderId.

Response

  • No silent resizing. D1 accepts the full quantity you request or refuses the order. acceptedQuantity always equals requestedQuantity and reductionReason is null.
  • Immediate matching. An order that crosses the book fills immediately, fully or partly, and any remainder rests. The response shows the order after matching.
  • Fills are not in the response. Fills from the placement are already on /executions when the response arrives. Take them from there.
  • Self-trade prevention. If your order would match one of your own resting orders, it fills against other participants first. Then the rest of the incoming order is cancelled with cancelReason: SELF_TRADE_PREVENTION. Your resting order is left alone. In that case a 201 response can show status: CANCELLED.

Placement errors

A refused placement creates nothing, so its clientOrderId is not used up. Authentication, 429 and 500 responses are described in Errors.

clientOrderId and idempotency

A clientOrderId identifies one economic intent: “buy 2 of this market at 105”. D1 keeps at most one order per clientOrderId on your account, forever, no matter how many times or from how many places the request is sent. Both of your keys share the same clientOrderId space. “Terms” means instrumentId, side, type, price and quantity. A retry is answered as a duplicate even if the market has since closed, trading has been disabled, or your account has been suspended. Replays count against the placement rate limit.

When you do not know the result

A placement can time out, lose its connection or return 5xx after D1 has already created the order. It can also still be in flight and be created after you gave up waiting.
Never re-send the same intent under a new clientOrderId while the first one’s outcome is unknown. It is the only way to end up with two orders, and D1 cannot detect it for you.
Do this instead:
1

Keep the ID

Persist the clientOrderId and its terms before sending. After a timeout, mark the ID as “possibly live”.
2

Retry with the same ID

Send the identical request again. A 201 or 200 is final: that is your order. A refusal only answers that attempt. An earlier attempt with the same ID can still commit if the condition that refused this one changes.
3

Reconcile

Use GET /mm/v1/orders?state=resting and GET /mm/v1/executions to find the order and its fills. If the order shows up after all, cancel it or book its fills.
Not finding an order right after a timeout, in GET /mm/v1/orders or anywhere else, does not prove that it will never be created. Keep treating the ID as possibly live until a retry with the same ID returns the order, or until your reconciliation finds it. If you want a different order, for example at a new price, use a new clientOrderId and keep treating the old one as possibly live.

Cancel an order

POST /mm/v1/orders/{orderId}/cancel
The body is empty or {}.
Cancelling is safe to retry: a second cancel returns ALREADY_CANCELLED. If a fill and your cancel race, whichever commits first wins. A cancel removes only what is left at that moment, so check filledQuantity.

Cancel all orders

POST /mm/v1/orders/cancel-all
The body is empty, {}, or { "instrumentId": "…" }.
  • It cancels only your own resting orders, with reason USER. It never affects other participants’ orders.
  • It cancels the orders that existed when it ran. An order placed after it is not cancelled.
  • more: true means some resting orders were left. Call it again until more is false.
  • With nothing to cancel it returns cancelled: 0, so it is safe to retry.
Use cancel-all as your emergency control: on shutdown, on a lost connection to your pricing, when trading.available turns false, or whenever you are unsure what is resting.

Cancellation stays available

Cancelling reduces risk, so it remains available when placing new orders is not: Cancellation has its own rate-limit budgets, separate from placement, so heavy quoting cannot use them up. See Rate Limits.

Read orders

GET /mm/v1/orders: your orders, newest first.
string
default:"all"
all or resting. resting returns only orders still on the book (OPEN or PARTIALLY_FILLED).
string
Only return orders for this market.
integer
default:"50"
1–100.
string
nextCursor from the previous page, with the same state and instrumentId. See Pagination.
Returns { orders, nextCursor, limit, serverTimeMs }. GET /mm/v1/orders/{orderId}: one order. Returns { order, serverTimeMs }. An order that does not exist and an order that belongs to another account both return 404 NOT_FOUND. There is no lookup by clientOrderId. To find an order by your own ID, re-send the identical placement (it returns the existing order) or match clientOrderId in GET /mm/v1/orders.

Order fields

Order status

Partial fills

Each partial fill is a separate execution on /executions. Book fills from there, not by diffing filledQuantity.
0 and null mean different things. 0 is a confirmed zero. null means D1 never recorded the value, which happens only on orders created before lifecycle tracking began. D1 never fills in a missing value with a guess.

Cancel reasons

cancelReason is null unless the order was cancelled. On orders placed through this API you can expect: The full set of published values is USER, DEALER, DEALER_MASS_CANCEL, WATCHDOG, LOSS_LIMIT, ADMIN_PULL, ADMIN_HALT, EXPIRY_SOLVENCY, EXPIRY and SELF_TRADE_PREVENTION. D1 will announce any new reason as an API change. If you receive a value you do not recognise, treat the order as cancelled for an unknown reason. Do not fail.