Skip to navigation

Position Lifecycle

The sequence every integration implements, in the order the API enforces. Four rules shape it:

RuleIf you miss it
200 from POST /orders means queued, not filledYou book a position that never existed
/positions lags the fillYou read “no position” and leave the fill unprotected
Direction is the sign of size — a position has no sideYour stop sits on the wrong side and fires at once
A position with an armed trigger cannot be closedYou cannot exit until the trigger is cancelled

Step 4 folds into step 1 if you attach the stop to the entry as a bracket order: the TP/SL legs are accepted with the entry and arm themselves once it fills, so the position is never unprotected while you poll steps 2 and 3. Everything from step 5 on applies unchanged once they are armed.

1. Enter

POST /orders — one MARKET leg, limit_price as your slippage bound.

The leg returns SUBMITTED — queued, not filled.

2. Confirm the fill

Poll GET /orders/{orderId} until a terminal status.

An early 404 means not yet persisted. FILLED or PARTIALLY_FILLED_CANCELLED is a fill; avg_fill_price is the executed price.

3. Read the position

Poll GET /account/{account_id}/positions until the market appears. A single read right after the fill regularly returns an empty array on a position that exists.

Direction is the sign of size: positive long, negative short.

4. Attach the stop

POST /orders — one reduce-only TRIGGER leg with size: "0", which closes the whole position at fire time. (Skip this step if the stop was attached at entry — it is already armed, and it replaced any older whole-position stop of the same kind.)

Positionsidetrigger_pricelimit_price (fire)
LongSELLbelow markbelow the trigger
ShortBUYabove markabove the trigger
  • Derive the level from the current mark, not entry_price; a trigger already past the mark on its fire side is rejected.
  • limit_price is where the IOC fires; trigger.trigger_price is what arms it.
  • Keep the returned order_id for the modify and the exit.

5. Move the stop

POST /orders/{orderId}/modify — every field, every call; there is no partial modify.

  • Only limit_price, size, trigger_price change. Resend size: "0".
  • A stop still waiting for its entry (parent_order_id set) cannot be modified — move the entry instead, or cancel the stop and attach a new one once the entry fills.
  • client_order_id must be new and is signed here, unlike on an order leg.
  • Success returns a new order_id; the old one is dead.

An armed trigger reads SUBMITTED for its whole life. That is not a stuck order.

6. Exit

In this order:

  1. Cancel every armed trigger on the market — GET /account/position-triggers?wallet=…, then POST /orders/{orderId}/cancel with order_type: "TRIGGER" for each.
  2. Wait until they are gone — cancellation is asynchronous; poll position-triggers.
  3. Close — one reduce-only MARKET leg, side against the position, size from closeable. Reduce-only legs are exempt from min_order_value.

Closing while a trigger is still armed fails with close_trigger_coexistence. There is no atomic flip: reverse by exiting, then entering again.

Next: Account Data