View as Markdown

Build a trading bot

Stream prices, place orders, and handle disconnects and ambiguous fills correctly.

Stream what you need

{
  "type": "subscribe",
  "marketData": [
    { "symbol": "EURUSD", "subscriptions": [
      { "type": "quotes", "intervalInMilliseconds": 250 },
      { "type": "candles", "timeframe": "1m" }
    ] }
  ]
}

You’ll get prices and candles frames. Each prices frame also carries the account’s equity, margin and freeMargin, read from the terminal. See Streaming.

Don’t trade on stale prices

Check the health frames. If the account is stale, prices have stopped moving; if it’s disconnected, orders will fail. Pause the strategy rather than trading on a frozen quote.

Place orders without duplicates

Give every order an idempotency key derived from your strategy’s own decision id:

{
  "actionType": "ORDER_TYPE_BUY_LIMIT",
  "symbol": "EURUSD",
  "volume": 0.1,
  "openPrice": 1.0712,
  "stopLoss": 20,
  "stopLossUnits": "RELATIVE_PIPS",
  "takeProfit": 40,
  "takeProfitUnits": "RELATIVE_PIPS",
  "expiration": { "type": "ORDER_TIME_SPECIFIED", "time": "2026-09-23T18:00:00Z" },
  "clientId": "bot1_d8812"
}

Stops and targets can be absolute prices or relative (price, points, pips, account currency, or percent of balance).

Read the response properly

POST /trade returns HTTP 200 even when the broker rejects. Success codes are 10008 (placed), 10009 (done), 10010 (partial) and 10025 (no changes); anything else is a failure. Then check our outcome. If it’s unknown, the broker link dropped mid-order: retry with the same key, never a new one. See Idempotency.

After a reconnect

Resume the stream with lastSeq so you don’t miss fills. After a broker reconnect, wait for a fresh tick before sending new orders.