NAV
中文
Java Python Go C++

Guide

A technical introduction and guide to trading with wss://ws.okx.com:8443/ws/v5/public API V5 

With the introduction of wss://ws.okx.com:8443/ws/v5/public’s new Trading 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 wss://ws.okx.com:8443/ws/v5/public 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:

Field V5 API V3 API
Currency ccy currency
Instrument ID instId instrument_id
Underlying uly underlying
Unrealized PnL upl unrealized_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": "985d5b66-57ce-40fb-b714-afc0b9787083",
        "passphrase": "123456",
        "timestamp": "1538054050",
        "sign": "7L+zFQ+CEgGu5rzCj4+BdV2/uUHGqddA9pI6ztsRRPs="
    }]
}

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).

Create POST /api/v5/users/subaccount/apikey
Read GET /api/v5/users/subaccount/apikey
Update POST /api/v5/users/subaccount/modify-apikey
Delete POST /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 wss://ws.okx.com:8443/ws/v5/public’s Trading 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 wss://ws.okx.com:8443/ws/v5/public’s Trading account trading system and works in addition to the existing Long/Short position.

Net position Positions can be held in one side only. Exchange will open/close the position automatically depends on the position (positive/negative) you specified
Long/Short position Positions 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 wss://ws.okx.com:8443/ws/v5/public’s Trading 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: 

Account Mode Margin Mode Scope
Simple Cross Per instrument
Isolated Per instrument
Simple-currency margin Cross Per instrument
Isolated Per instrument
Multi-currency margin Cross Per coin
Isolated Per instrument

For other instrument types: 

Position Mode Instrument Type Margin Mode Scope
Net Futures Cross Per underlying
Isolated Per underlying
Swap Cross Per underlying
Isolated Per underlying
Long/Short Futures Cross Per underlying
Isolated Per underlying per side
Swap Cross Per underlying
逐仓 Per underlying per side

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:

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:

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 Order ID as assigned by the client (clOrdId) when placing the order. The Client Order ID as assigned by the client 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 per request. The advantage of having a unified API is that the orders can be of different instrument types.

REST: 

Place POST /api/v5/trade/batch-orders
Amend POST /api/v5/trade/amend-batch-orders
Cancel POST /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

Trading account

Under wss://ws.okx.com:8443/ws/v5/public’s Trading account trading system, there is only one Trading 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 wss://ws.okx.com:8443/ws/v5/public. 

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 updates Updates 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 updates Updates 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 uniquely generated by combining below attributes of a position: 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:

Granularity Sample Request
Instrument type GET /api/v5/account/positions?instType=SWAP
Instrument ID GET /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:

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:

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 wss://ws.okx.com:8443/ws/v5/public continues to upgrade its Trading account trading system. Please refer to the API V5 documentation for the latest specifications.