Skip to main content

Orders, positions & deals

The lifecycle​

  • An order is an instruction. A market order fills right away; a pending order waits for its price.
  • A filled order opens a position: an open trade with floating P/L that you modify (SL/TP) and close.
  • Every fill and close books a deal. Entry deals have direction: 0; exit deals have direction: 1 and carry the realized profit, commission and swap. Your trade history is your list of deals.

Orders are confirmed asynchronously​

This is the most important thing to understand before you write a bot.

POST /api/v1/orders/accounts/me only checks that the request is well formed, then hands the order to the trading engine. A 201 means "accepted for processing", not "filled":

{ "success": true, "code": 201, "data": "new market_order BUY 0.1 EURUSD @ 1.08234" }

The response has no order ID. The engine's result arrives on the WebSocket, usually within milliseconds:

OutcomeWhat you receive
Market order filledorder_create and order_update (status 3 filled), position_create with the new position (its open_price is your fill), and a deal_create entry. Don't depend on their exact order.
Pending order placedorder_create with the order (including its id)
Rejected by a trading rule (margin, lot size, SL/TP, market closed…)bad_request (or forbidden) with payload.reason: "order_create" and the reason in payload.message — or order_rejected with rejection_msg

So a bot should:

  1. keep the WebSocket open before it trades,
  2. send the order and mark it "in flight",
  3. resolve it on position_create / order_create / a rejection for that symbol, and
  4. if nothing arrives within a few seconds, check GET /api/v1/positions/accounts/me and GET /api/v1/orders/accounts/me.

Tag orders with a unique comment (e.g. "bot1-7f3a"): it's copied onto the order and position, so you can match events to the request that caused them. The same pattern applies to modifying and closing positions.

Place an order​

POST /api/v1/orders/accounts/me

FieldTypeRequiredNotes
symbol_idintegeryesFrom /symbols/me.
typeintegeryesSee order types.
sideintegermarket orders0 buy, 1 sell. For pending orders the side comes from type.
volumenumberyesLots. Must be within the symbol's min_value–max_value; round to step yourself.
order_pricenumberyesNever 0. Market: the price you expect (current ask to buy, bid to sell). Pending: the trigger price.
stop_lossnumber | nullnoAbsolute price. null or omitted = none.
take_profitnumber | nullnoAbsolute price. null or omitted = none.
commentstringnoCopied to the order and position. Use it to tag your bot's trades.
expiration_policyintegernoPending orders only. See expiration. Default 0 (GTC).
expiry_atintegerwith policy 2 or 3Unix time in milliseconds.
{
"symbol_id": 26100001,
"type": 0,
"side": 0,
"volume": 0.10,
"order_price": 1.08234,
"stop_loss": 1.08034,
"take_profit": 1.08634,
"comment": "bot1-7f3a"
}
Market orders fill at the market

A market order fills at the current price (ask for a buy, bid for a sell) with your account's spread. order_price is recorded but there is no slippage limit — if the price has moved, you get the new price. Check the fill in the position_create event's open_price.

Order types​

typeNameTriggers when
0MarketFills immediately. Needs side.
1Buy LimitAsk falls to order_price (below the market).
2Buy StopAsk rises to order_price (above the market).
3Sell LimitBid rises to order_price (above the market).
4Sell StopBid falls to order_price (below the market).

Types 5 and 6 (stop-limit) are accepted by the API but not executed by the engine — don't use them.

Pending orders reserve no margin until they fill. Placing one on the wrong side of the market is rejected, e.g. buy limit price 1.09 must be less than or equal 1.08234.

Expiration​

expiration_policyMeaning
0Good till cancelled (default).
1Until the end of today's session.
2Until expiry_at (at least 5 minutes from now).
3Until the end of the day given by expiry_at.

The symbol's expiration field lists which policies are allowed.

Stop loss & take profit rules​

SL and TP are absolute prices. They must be at least the symbol's stops level away from the reference price:

  • distance = stop_level × 10^-digits (for stop_level: 20 and 5 digits, 0.00020).
  • reference = the price you'd close at: the bid for a buy, the ask for a sell. For a pending order, the order's own price.
SideTake profitStop loss
Buy≥ reference + distance≤ reference − distance
Sell≤ reference − distance≥ reference + distance

A violation is rejected with a message like stop loss price 1.0820 must be less than or equal 1.08014. Round every price to the symbol's digits.

Modify a pending order​

PUT /api/v1/orders/{order_id}/accounts/me

{
"type": 1,
"order_limit_price": 1.07400,
"volume": 0.10,
"stop_loss": 1.06900,
"take_profit": 1.08900,
"expiration_policy": 0
}
Send every field

This replaces the order. Any field you leave out is cleared — omit stop_loss and the order loses its stop. Read the order first and send it back with your changes. type is required and must be 1–4.

Only orders with status 1 (placed) can be changed; others return you can't update the order at this point.

Cancel a pending order​

POST /api/v1/orders/{order_id}/accounts/me with no body. You get order_cancel on the WebSocket.

Modify a position (SL/TP)​

PUT /api/v1/positions/{position_id}/accounts/me

{ "stop_loss": 1.08100, "take_profit": 1.08700, "comment": "bot1-7f3a" }
Send both SL and TP

This also replaces: to move only the stop, send the current take_profit too, or the take profit is removed. Send null to remove one on purpose.

The SL/TP rules above apply; a violation arrives as a WebSocket bad_request with reason: "position_update".

Close a position​

POST /api/v1/positions/{position_id}/accounts/me

{ "volume": 0.10 }
  • volume is required. Send the full position volume to close it, or less for a partial close — the rest stays open under the same position_id.
  • The close fills at the current market price.
  • You get position_close and a deal_create (exit, with profit).

Close several at once with POST /api/v1/positions/close-many/accounts/me:

{ "positions": [ { "position_id": 260610016, "volume": 0 }, { "position_id": 260610017, "volume": 0.05 } ] }

Here volume: 0 means "close fully". The response reports each one: { "closed": 1, "failed": 1, "results": [ { "position_id": 260610017, "ok": false, "error": "position not found" } ] }.

Reference: enums​

Order status

ValueMeaning
0started — being processed
1placed — pending, waiting to trigger
2partially filled
3filled
4cancelled
5rejected (see rejection_msg)
6expired

GET /api/v1/orders/accounts/me returns statuses 0–2 by default; add ?active=false for 3–6.

Position status: 0 open, 1 closing, 2 closed. GET /api/v1/positions/accounts/me returns every open position; add ?active=false for closed ones (paged).

side: 0 buy, 1 sell.

Deal direction: 0 entry, 1 exit. Deal status: 0 active, 1 closing, 2 closed, 3 cancelled.

Deal / position close reason:

ValueClosed by
0you (client)
2the dealer
3stop loss
4take profit
6stop-out (margin)

Lists, paging and dates​

Order, closed-position, deal and journal lists share these query parameters:

ParameterDefaultNotes
page11-based.
limit500Up to 100000.
from—RFC 3339, e.g. 2026-06-01T00:00:00Z. Filters on creation time.
to—RFC 3339. Only applied together with from.

data is a plain array — keep paging until you get fewer than limit rows. Timestamps such as created_at are Unix nanoseconds.