Article

A complete guide to OKX's API v5 upgrade

A technical introduction and guide to trading with OKX API v5 

With the introduction of OKX’s new Unified Account trading system, we've been working on upgrading our API with new features and enhancements. In this document, we will outline the major changes in v5, the latest API version, and walk traders through the various configuration parameters now available via the OKX trading API. 

Changes in API v5

Unified API across products

Unlike API v3, the endpoints in API v5, such as placing orders and retrieving positions are unified across products. 

For example, when placing an order, we only need to invoke the following URL and specify the product (i.e. instrument type) in the request body: 

POST /api/v5/trade/order

The same request and response data models are used across all instrument types in the same API, which means we no longer need to model each of the product APIs separately.

Shorter naming convention

In API v5, camelCase is used in field names with abbreviations to save bandwidth and memory consumption. 

For example:

FieldV5 APIV3 API
Currencyccycurrency
Instrument IDinstIdinstrument_id
Underlyingulyunderlying
Unrealized PnLuplunrealized_pnl

Standard WebSocket compression

Manual decompression when receiving messages in API v3 is no longer required in v5, and is replaced with the standard WebSocket extension “Per-Message Deflate."

To leverage the WebSocket compression, make sure the extension is enabled on the client side, i.e. “permessage-deflate” should be present in the request header.

Public and private WebSocket

WebSocket channels are now divided into public channels (e.g. tickers, candle) and private channels (e.g. positions, account) with different URLs.

When connecting to the public WebSocket, no login request is required before subscription or the server will reject the subscription request.

Placing orders through WebSocket

In addition to REST, now it is possible to place/amend/cancel orders through WebSocket. For details please refer to the trading section referenced in this document.

Authentication

REST authentication in API v5 is the same as in API v3 (i.e. sign in the request header). 

WebSocket authentication is also very similar to API v3 (i.e. send login request) with format changes to key-value pairs: 

{  
  "op": "login",
  "args": [
    {
      "apiKey": "975d5d66-57ce-40fb-b714-afc0b66518083",
      "passphrase": "123456",
      "timestamp": "1538054050",
      "sign": "7L+zFQ+CEgGu5rzCj4+BdV2/uUHGqddA9pI6ztsLLPs="
    }
  ]
}

Managing API keys of sub accounts

With API v5 it is possible to CRUD (create, read, update, delete) the API Keys of sub accounts when using the master account (i.e. main account).

CreatePOST /api/v5/users/subaccount/apikey
ReadGET /api/v5/users/subaccount/apikey
UpdatePOST /api/v5/users/subaccount/modify-apikey
DeletePOST /api/v5/users/subaccount/delete-apikey

For security reasons, it is strongly recommended to bind the API Keys to specific IP addresses.

Configuring accounts and sub accounts

After creating sub accounts and their API Keys, we can configure the main account and sub accounts via the API before we trade.

Account config

Account config of each account/sub account can be retrieved via the REST API:

GET /api/v5/account/config

It will return (1) account mode, (2) position mode, (3) auto borrow setting, and (4) option Greeks type.

Account mode

In OKX’s Unified Account trading system, we have (i) Simple mode, (ii) Single-currency margin mode, and (iii) Multi-currency margin mode.

We can only change these modes via the web UI as the process requires user action.

Position 

The Net position was introduced along with OKX’s Unified Account trading system and works in addition to the existing Long/Short position.

Net positionPositions can be held in one side only. Exchange will open/close the position automatically depends on the position (positive/negative) you specified
Long/Short positionPositions can be held in both sides at the same time

To change the position mode, we can invoke the following REST API. Note that all positions have to be closed first.

POST /api/v5/account/set-position-mode

Auto borrow

Auto borrow is a new feature only applicable to the Multi-currency margin mode and can only be changed via the web UI.

Option Greeks type

We can set the option Greeks type via REST API in v5 similar to v3:

POST /api/v5/account/set-greeks

Cross/isolated margin mode

In OKX’s Unified Account trading system, we have the flexibility of trading the same instrument with both cross (single-currency and multi-currency) margin and isolated (i.e. fixed) margin at the same time. 

As a result, there is no API for setting margin mode per underlying anymore. Now we specify the margin mode (i.e. trade mode) when placing an order instead.

Managing leverage

Getting leverage

We can retrieve the leverage information via the following REST API:

GET /api/v5/account/leverage-info

Currently, there is no global leverage setting, and the leverage can be set in different scopes.

For margin instrument type: 

For other instrument types: 

Setting leverage

After getting the leverage info, we can set the leverage accordingly:

POST /api/v5/account/set-leverage

With these 2 APIs, we can write a program to set the leverage of each instrument beforehand.

For example, consider the following scenario:

  • Account mode: Multi-currency Margin
  • Position mode: Net
  • Instruments we want to set leverage to 3.0:
    • BTC-USDT, EOS-USDT, LTC-BTC, LTC-USDT
    • BTC-USD-210319, BTC-USD-210326, BTC-USD-210625
    • BTC-USD-SWAP
  • Interested in cross margin mode only for above instruments

For spot/margin instruments, since the leverage is set per coin, we can extract the currency pair and set each coin accordingly, i.e. BTC, USDT, EOS, and LTC.

Sample request body of setting BTC leverage to 3.0 (applicable to selling BTC-USDT and buying LTC-BTC):

{
  "lever": "3.0",
  "mgnMode": "cross",
  "ccy": "BTC"
}

The request bodies for setting USDT, EOS, and LTC are similar.

Next, we want to set the leverage of BTC-USD-210319, BTC-USD-210326, and BTC-USD-210625. Since they share the same underlying, i.e. BTC-USD, we only need to set the leverage once with one of the instruments.

{
  "lever": "3.0",
  "mgnMode": "cross",
  "instId": "BTC-USD-210326"
}

Finally, we want to set the leverage of BTC-USD-SWAP. Despite having the same underlying (BTC-USD) as the above futures, the leverage is separated between futures and swap.

To do so, we can invoke the API with the following request body: 

{
  "lever": "3.0",
  "mgnMode": "cross",
  "instId": "BTC-USD-SWAP"
}

Now we should be all set with the above 6 API calls for the 8 instruments.

With the instructions above, users should be able to set up sub accounts with the new API, and configure accounts to suit their trading preferences.

Trading with API v5

Trade mode

With the flexibility of placing orders with cross and isolated margin mode, we need to specify trade mode (tdMode).

The following table shows tdMode values that need to be set:

Let's say we want to place the following order as an example:

  • Account mode: Multi-currency Margin
  • Position mode: Net
  • Instrument: BTC-USDT-SWAP
  • Margin Mode: Cross
  • Side: Buy (long)
  • Type: Limit
  • Price: 50,912.4 USDT
  • Amount: 1 Cont

By looking at the trading mode table above, tdMode should be set to “cross.”

To better identify the order in the system, it is recommended to provide a client-supplied order ID (clOrdId) when placing the order. The client-supplied order ID should be case-sensitive alphanumerics/alphabets beginning with a letter and going up to 32 characters.

Let’s assign clOrdId as testBTC0123 in this example.

Subscribing to orders channel

Before we place an order, we should subscribe to the orders channel with WebSocket, so that we can monitor the order state changes (e.g. live, filled) and take action if necessary (e.g. place a new order after execution).

In API v5, there are several subscription granularities when subscribing to orders channel. 

To subscribe to the above-mentioned BTC-USDT-SWAP order updates, we can send either of the following after connecting to and logging in to the private WebSocket: 

Instrument TypeInstrument Type + Underlying
(Derivatives only)
Instrument Type + Instrument ID
Request{
  "op": "subscribe",
  "args": [
    {
      "channel": "orders",
      "instType": "SWAP"
    }
  ]
}
{
  "op": "subscribe",
  "args": [
    {
      "channel": "orders",
      "instType": "SWAP",
      "uly": "BTC-USDT"
    }
  ]
}
{
  "op": "subscribe",
  "args": [
    {
      "channel": "orders",
      "instType": "SWAP",
      "instId": "BTC-USDT-SWAP"
    }
  ]
}
Successful response{
  "event": "subscribe",
  "arg": {
    "channel": "orders",
    "instType": "SWAP"
  }
}
{
  "event": "subscribe",
  "args": [
    {
      "channel": "orders",
      "instType": "SWAP",
      "uly": "BTC-USDT"
    }
  ]
}
{
  "event": "subscribe",
  "args": [
    {
      "channel": "orders",
      "instType": "SWAP",
      "instId": "BTC-USDT-SWAP"
    }
  ]
}

We can pass ANY as instType to subscribe to all product types at once as well.

Note that the orders channel does not publish any initial snapshot of your orders before the subscription. It only publishes whenever order state changes (e.g. from live to canceled).

If we want to obtain all the live orders details before subscription, we can invoke the following API:

GET /api/v5/trade/orders-pending

Placing an order

After subscribing to orders channel, we should be good to place the BTC-USDT-SWAP order.

In API v5, we can use REST or WebSocket to place orders.

REST

We can invoke the following REST API, and the server will acknowledge the request with an order ID (ordId):

REST APIPOST /api/v5/trade/order
Request body{
  "instId": "BTC-USDT-SWAP",
  "tdMode": "cross",
  "clOrdId": "testBTC0123",
  "side": "buy",
  "ordType": "limit",
  "px": "50912.4",
  "sz": "1"
}
Successful response{
  "code": "0",
  "msg": "",
  "data": [
    {
      "clOrdId": "testBTC0123",
      "ordId": "288981657420439575",
      "tag": "",
      "sCode": "0",
      "sMsg": ""
    }
  ]
}

Note that this only indicates that the exchange has received the request successfully with an order ID assigned. The order may not be live at this point in time and we should check the order state next.

WebSocket

Alternatively, we can place the order via WebSocket, which is, in theory, more efficient than using REST API with less overhead.

Since WebSocket operation is asynchronous, we will also need to provide the message ID (id) to identify the corresponding response.

After logging into the private WebSocket, we can send the following WebSocket message:

{
  "id": "NEWtestBTC0123",
  "op": "order",
  "args": [
    {
      "instId": "BTC-USDT-SWAP",
      "tdMode": "cross",
      "clOrdId": "testBTC0123",
      "side": "buy",
      "ordType": "limit",
      "px": "50912.4",
      "sz": "1"
    }
  ]
}

The server will acknowledge the request with the following sample response with the same message ID (i.e.NEWtestBTC0123), along with an order ID (ordId) assigned by the exchange: 

{
  "id": "NEWtestBTC0123",
  "op": "order",
  "data": [
    {
      "clOrdId": "",
      "ordId": "288981657420439575",
      "tag": "",
      "sCode": "0",
      "sMsg": ""
    }
  ],
  "code": "0",
  "msg": ""
}

Please note that this only indicates that the exchange has received the request successfully with an order ID assigned. The order may not be live at this point in time and we should check the order state.

Checking order state

After placing the order, we should expect a WebSocket message from the orders channel with the state “live.”

Sample message (subscribed to orders channel by instrument type + underlying):

{
  "arg": {
    "channel": "orders",
    "instType": "SWAP",
    "uly": "BTC-USDT"
  },
  "data": [
    {
      "accFillSz": "0",
      "amendResult": "",
      "avgPx": "",
      "cTime": "1615170596148",
      "category": "normal",
      "ccy": "",
      "clOrdId": "testBTC0123",
      "code": "0",
      "fee": "0",
      "feeCcy": "USDT",
      "fillPx": "",
      "fillSz": "0",
      "fillTime": "",
      "instId": "BTC-USDT-SWAP",
      "instType": "SWAP",
      "lever": "3",
      "msg": "",
      "ordId": "288981657420439575",
      "ordType": "limit",
      "pnl": "0",
      "posSide": "net",
      "px": "50912.4",
      "rebate": "0",
      "rebateCcy": "USDT",
      "reqId": "",
      "side": "buy",
      "slOrdPx": "",
      "slTriggerPx": "",
      "state": "live",
      "sz": "1",
      "tag": "",
      "tdMode": "cross",
      "tpOrdPx": "",
      "tpTriggerPx": "",
      "tradeId": "",
      "uTime": "1615170596148"
    }
  ]
}

After the order is filled, the following sample message is pushed with the state changed to “filled,” along with other fill-related fields. 

A Trade ID (tradeId) will also be set for this fill and can be used for reconciliation with position, which will be covered below.

{
  "arg": {
    "channel": "orders",
    "instType": "SWAP",
    "uly": "BTC-USDT"
  },
  "data": [
    {
      "accFillSz": "1",
      "amendResult": "",
      "avgPx": "50912.4",
      "cTime": "1615170596148",
      "category": "normal",
      "ccy": "",
      "clOrdId": "testBTC0123",
      "code": "0",
      "fee": "-0.1018248",
      "feeCcy": "USDT",
      "fillPx": "50912.4",
      "fillSz": "1",
      "fillTime": "1615170598021",
      "instId": "BTC-USDT-SWAP",
      "instType": "SWAP",
      "lever": "3",
      "msg": "",
      "ordId": "288981657420439575",
      "ordType": "limit",
      "pnl": "0",
      "posSide": "net",
      "px": "50912.4",
      "rebate": "0",
      "rebateCcy": "USDT",
      "reqId": "",
      "side": "buy",
      "slOrdPx": "",
      "slTriggerPx": "",
      "state": "filled",
      "sz": "1",
      "tag": "",
      "tdMode": "cross",
      "tpOrdPx": "",
      "tpTriggerPx": "",
      "tradeId": "60477021",
      "uTime": "1615170598022"
    }
  ]
}

Amending an order

Order amendment is supported for all instrument types when using API v5, allowing you to amend the price (newPx) and/or amount (newSz) of the order. The cancel on fail (cxlOnFail) parameter is also available for you to cancel the order if the amendment fails.

REST: 

POST /api/v5/trade/amend-order

WebSocket operation (op) argument: 

"op": "amend-order"

Similar to placing orders, we should expect an acknowledgement after sending the amend request through REST or WebSocket.

Note that the order cannot be amended once it is fully filled or canceled.

Canceling an order

Similarly we can cancel the order using REST or WebSocket.

REST: 

POST /api/v5/trade/cancel-order

WebSocket operation (op) argument: 

"op": "cancel-order"

An acknowledgement will be received after sending the cancel request. The order is only canceled when we receive the order update from the WebSocket orders channel with the state “canceled”.

Note that an order cannot be canceled when it is fully filled or is already canceled.

Batch operations

Batch operations are available for placing, amending, and canceling orders and supports a maximum of 20 orders at a time. The advantage of having a unified API is that the orders can be of different instrument types.

REST: 

PlacePOST /api/v5/trade/batch-orders
AmendPOST /api/v5/trade/amend-batch-orders
CancelPOST /api/v5/trade/cancel-batch-orders

WebSocket operation (op) argument: 

Place"op": "batch-orders"
Amend"op": "batch-amend-orders"
Cancel"op": "batch-cancel-orders"

The batch operation is not all-or-nothing, i.e. it allows part of the order operations to be successful. Upon receiving the acknowledgment after sending a request, we should check the individual sCode and sMsg fields for each of the orders. 

Account and positions information

Unified account

Under OKX’s Unified Account trading system, there is only one unified account shared across all instrument types. As a result there is no separate spot, margin, futures, swap or options account when trading with API v5.

WebSocket subscription

It is recommended to subscribe to the account channel using WebSocket for receiving account updates. The account channel provides the optional parameter "ccy" to specify the coin of the account we are interested in for subscription.

Here is a sample request and response after connecting to and logging into the private WebSocket:

AccountAccount with Specific Coin
Request{
  "op": "subscribe",
  "args": [
    {
      "channel": "account"
    }
  ]
}
{
  "op": "subscribe",
  "args": [
    {
      "channel": "account",
      "ccy": "BTC"
    }
  ]
}
Successful response{
  "event": "subscribe",
  "arg": {
    "channel": "account"
  }
}
{
  "event": "subscribe",
  "arg": {
    "channel": "account",
    "ccy": "BTC"
  }
}

Initial snapshot

Unlike the orders channel, the account channel will publish an initial snapshot for the coins with a non-zero balance, i.e. non-zero equity, available equity or available balance.

Let’s assume we have a non-zero balance on BTC and USDT and the account is set to Multi-currency Margin mode. We should expect the following sample message from the account channel:

AccountAccount with Specific Coin
{
  "arg": {
    "channel": "account"
  },
  "data": [
    {
      "adjEq": "30979.1086748182657014",
      "details": [
        {
          "availBal": "",
          "availEq": "18962.59868274799",
          "ccy": "USDT",
          "crossLiab": "0",
          "disEq": "18978.5272656414983116",
          "eq": "18962.59868274799",
          "frozenBal": "0",
          "interest": "0",
          "isoEq": "0",
          "isoLiab": "0",
          "liab": "0",
          "mgnRatio": "",
          "ordFrozen": "0",
          "upl": "0"
        },
        {
          "availBal": "",
          "availEq": "0",
          "ccy": "BTC",
          "crossLiab": "0.509575622217854",
          "disEq": "-25408.4180739947324516",
          "eq": "-0.5096053466363398",
          "frozenBal": "0",
          "interest": "0.0000297244184858",
          "isoEq": "0",
          "isoLiab": "0",
          "liab": "0.509575622217854",
          "mgnRatio": "",
          "ordFrozen": "0",
          "upl": "0"
        }
      ],
      "imr": "8469.4726913315758219",
      "isoEq": "0",
      "mgnRatio": "39.9556239578938079",
      "mmr": "762.252542219842",
      "totalEq": "44480.5383005753085878",
      "uTime": "1615190165641"
    }
  ]
}
{
  "arg": {
    "channel": "account",
    "ccy": "BTC"
  },
  "data": [
    {
      "adjEq": "30979.1086748182657014",
      "details": [
        {
          "availBal": "",
          "availEq": "0",
          "ccy": "BTC",
          "crossLiab": "0.509575622217854",
          "disEq": "-25408.4180739947324516",
          "eq": "-0.5096053466363398",
          "frozenBal": "0",
          "interest": "0.0000297244184858",
          "isoEq": "0",
          "isoLiab": "0",
          "liab": "0.509575622217854",
          "mgnRatio": "",
          "ordFrozen": "0",
          "upl": "0"
        }
      ],
      "imr": "8469.4726913315758219",
      "isoEq": "0",
      "mgnRatio": "39.9556239578938079",
      "mmr": "762.252542219842",
      "totalEq": "44480.5383005753085878",
      "uTime": "1615190165641"
    }
  ]
}

Subsequent updates

Subsequently we will receive account updates driven by the following:

Event-driven updatesUpdates driven by events such as placing and canceling orders. Multiple events (e.g., multiple orders being executed at the same time) may be aggregated into one single account update. 

Only data of the affected coin will be published, including when the coin balance changes to zero.
Fixed time updatesUpdates pushed at a regular interval (10 seconds as of writing).

Similar to the initial snapshot, all coins (or specified coins with the ccy parameter) with non-zero balance will be pushed.

REST

Alternatively we can still invoke the REST API to get the balance for coins with non-zero balance:

GET /api/v5/account/balance

We can pass an optional parameter using "ccy" with a single currency (e.g. BTC) or multiple currencies (no more than 20) separated with commas (e.g. BTC,USDT,ETH). For example: 

GET /api/v5/account/balance?ccy=BTC,USDT,ETH

Unlike the account channel in WebSocket, however, the coin balance, regardless of zero balance or not, will always be returned if it is specified using the "ccy" parameter in the REST API, as long as we have possessed that coin before.

Max available tradable amount

With auto borrow enabled in Multi-currency Margin mode, you can buy/sell the instrument exceeding your available cash balance of that coin by borrowing from OKX. 

In this case it is quite useful to retrieve the max available tradable amount of the instrument including the available equity and loanable amount from exchange.

For this we can poll the following REST API in regular interval:

GET /api/v5/account/max-avail-size

Here is a sample request and response for BTC-USDT with cross margin mode under Multi-currency Margin mode:

RequestGET /api/v5/account/max-avail-size?instId=BTC-USDT&tdMode=cross
Successful Response{
  "code": "0",
  "data": [
    {
      "availBuy": "213800.4239369798722052",
      "availSell": "1.3539405224369181",
      "instId": "BTC-USDT"
    }
  ],
  "msg": ""
}

For spot instruments, availBuy is in quote currency and availSell is in base currency.

The above response means a maximum of 213,800.42 USDT is available to buy BTC-USDT, and a maximum of 1.35394052 BTC is available to sell BTC-USDT. This should be the same as the amount you see when trading on the web UI.

Positions

In API v5 positions API is also unified across different products. It is recommended to retrieve the positions data using WebSocket.

WebSocket subscription

Similar to the orders channel, In API v5 there are several subscription granularities when subscribing to the positions channel. 

To subscribe to the above BTC-USDT-SWAP position updates, we can send one of the following after connecting to and logging into the private WebSocket: 

Instrument TypeInstrument Type + Underlying
(Derivatives only)
Instrument Type + Instrument ID
Request{
  "op": "subscribe",
  "args": [
    {
      "channel": "positions",
      "instType": "SWAP"
    }
  ]
}
{
  "op": "subscribe",
  "args": [
    {
      "channel": "positions",
      "instType": "SWAP",
      "uly": "BTC-USDT"
    }
  ]
}
{
  "op": "subscribe",
  "args": [
    {
      "channel": "positions",
      "instType": "SWAP",
      "instId": "BTC-USDT-SWAP"
    }
  ]
}
Successful response{
  "event": "subscribe",
  "arg": {
    "channel": "positions",
    "instType": "SWAP"
  }
}
{
  "event": "subscribe",
  "args": [
    {
      "channel": "positions",
      "instType": "SWAP",
      "uly": "BTC-USDT"
    }
  ]
}
{
  "event": "subscribe",
  "args": [
    {
      "channel": "positions",
      "instType": "SWAP",
      "instId": "BTC-USDT-SWAP"
    }
  ]
}

We can pass ANY as instType to subscribe to positions of all product types at once as well.

Initial snapshot

The positions channel will publish an initial snapshot for the non-zero positions, i.e. pos > 0 or pos < 0. 

Continuing with the BTC-USDT-SWAP cross margin order (with position net mode) example in the earlier section, the following sample message is expected (subscribed by instrument type+ underlying):

{
  "arg": {
    "channel": "positions",
    "instType": "SWAP",
    "uly": "BTC-USDT"
  },
  "data": [
    {
      "adl": "2",
      "availPos": "",
      "avgPx": "50912.4",
      "cTime": "1615170596148",
      "ccy": "USDT",
      "imr": "165.15734103333082",
      "instId": "BTC-USDT-SWAP",
      "instType": "SWAP",
      "interest": "0",
      "last": "51000",
      "lever": "3",
      "liab": "",
      "liabCcy": "",
      "liqPx": "",
      "margin": "",
      "mgnMode": "cross",
      "mgnRatio": "0",
      "mmr": "1.98188809239997",
      "optVal": "",
      "pTime": "1615196199624",
      "pos": "1",
      "posCcy": "",
      "posId": "287999792370819074",
      "posSide": "net",
      "tradeId": "60477021",
      "uTime": "1615170598022",
      "upl": "0.4520230999924388",
      "uplRatio": "0.0027394232555804"
    }
  ]
}

Subsequent Updates

Similar to the account channel, subsequently we will receive positions updates driven by the following:

Event-driven updatesUpdates driven by events such as opening and closing positions. Multiple events (e.g., multiple orders being executed at the same time) may be aggregated into one single position update. 

Only affected position data will be published, including when the position is closed (i.e., changes to zero).
Fixed time updatesUpdates pushed at a regular interval (10 seconds as of writing).

All non-zero positions that matched the subscription granularity will be pushed.

Position ID

You may notice that there is a Position ID (posId) field included in each of the position data, which can be used as an optional query parameter when invoking the REST API as shown in the following section.

This field is generated by mgnMode + posSide + instId + ccy and is included in the data for you to uniquely identify the position in the same account, and it does not change after you close and reopen the position.

REST

Alternatively we can still invoke the REST API for non-zero positions:

GET /api/v5/account/positions

The following granularity is available:

GranularitySample Request
Instrument typeGET /api/v5/account/positions?instType=SWAP
Instrument IDGET /api/v5/account/positions?instId=BTC-USDT-SWAP
Position ID (single)GET /api/v5/account/positions?posId=287999792370819074
Position ID (multiple, max 20)GET /api/v5/account/positions?posId=287999792370819074,289098391880081414

Unlike the positions channel in WebSocket, position, whether it is closed or not, will always be returned if it is specified in the parameter posId in the REST API, as long as we have opened that position before.

Reconciliation between fill and positions

With the introduction of trade ID (tradeId) in the positions channel, it is now possible to reconcile order fill (from orders channel) and positions. One of the use cases of this is that we may want to derive the position from order fills.

A new order fill always comes with a newer trade ID, thus we can make use of this to match the relevant position/order fill, and to compare which data is newer by comparing the number of trade ID. 

Yet there are few pitfalls:

  • Not every order update can be matched to positions update as multiple position changes can be aggregated into one message — i.e., only last tradeId is received.
  • Liquidation or ADL does not generate an order update (as the order is owned by system).
  • Positions update caused by liquidation or ADL does not update tradeId.

To reconcile between fill and positions properly, we need to consider the above pitfalls and compare the position (or make use of position updated timestamp (uTime)) apart from comparing tradeId.

Let’s have a look into the following example sequence. Assume all are on the same instrument with the same margin mode and the position is in net mode.

Seq.ChannelDataReconciled Position
1orderfillSz=20, side=buy, tradeId=15020
2positionspos=20, tradeId=150, uTime=161485975163620
3positionspos=18, tradeId=151, uTime=161485975263718
4orderfillSz=2, side=sell, tradeId=15118
5orderfillSz=3, side=sell, tradeId=15615
6orderfillSz=1, side=sell, tradeId=15814
7positionspos=10, tradeId=163, uTime=161485975503710
8orderfillSz=1, side=sell, tradeId=15910
9orderfillSz=3, side=sell, tradeId=16310
10positionspos=10, tradeId=163, uTime=161485975503710
11positionspos=6, tradeId=163, uTime=16148665474306

We can observe that:

  • We receive single positions update #7 with tradeId=163, which means order updates with tradeId<=163 can be ignored when reconciling the position — i.e., order update #8 and #9 are ignored in this case.
  • Positions update #10 has the same tradeId and pos (as well as uTime) as #7, we can assume #10 is from fixed time update in a regular interval.
  • Positions update #11 is with the same tradeId=163 but a different position (and newer uTime), we can assume this is triggered by partial liquidation or ADL.

In this guide, we've gone over the changes in API v5 and how it can be used to configure accounts and sub accounts. We've also shown how traders can retrieve account and position data using the WebSocket, and finally how to trade and reconcile positions.

The specifics in this document are likely to change as OKX continues to upgrade its Unified Account trading system. Please refer to the API v5 documentation for the latest specifications.


Related articles
View more
View more