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
Your identifier for this order. 1–64 characters: a letter or digit, then letters, digits,
., _, : or -. Unique per account, permanently. See clientOrderId and idempotency.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.Quantity in contracts as
{ units, decimals }. It must be a multiple of the market’s quantityStep, written with the same decimals.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.
acceptedQuantityalways equalsrequestedQuantityandreductionReasonisnull. - 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
/executionswhen 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 a201response can showstatus: 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
AclientOrderId 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 return5xx after D1 has already created the order. It can also still be in flight and be created after you gave up waiting.
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.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
{}.
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
{}, 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: truemeans some resting orders were left. Call it again untilmoreisfalse.- With nothing to cancel it returns
cancelled: 0, so it is safe to retry.
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.
all or resting. resting returns only orders still on the book (OPEN or PARTIALLY_FILLED).{ 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
/executions. Book fills from there, not by diffing filledQuantity.
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.