NAV
curl python

Change Log

Introduction

OX.FUN offers a REST API and streaming WebSocket API that allows traders and developers to integrate their algorithms, trading strategies, risk management systems, and order execution software with our trading platform. OX.FUN's API emphasizes performance and security, enabling users to access the full range of our platform's features: order placement, account/subaccount management, and market data.

REST API

OX.FUN's REST API allows users to execute trades, manage their accounts, and access market data. With extensive documentation and sample python code, the REST API is easy to use and can be integrated with a wide variety of trading software. The REST API also offers SSL/TLS connections, rate limiting, and IP whitelisting, ensuring that all communication between the user and the server is secure.

WebSocket API

OX.FUN's WebSocket API provides real-time market data and order updates with ultra-low latency. With the WebSocket API, users can subscribe to real-time price updates and order book changes, enabling them to make faster and more informed trading decisions. The Websocket API also offers a real-time streaming interface for order placement and cancellation, enabling users to respond to market changes quickly and efficiently.

To get started register for a TEST account at stg.ox.fun or a LIVE account at ox.fun

API Key Management

An API key is required to make an authenticated API command. API keys (public and corresponding secret key) can be generated via the OX.FUN GUI within a clients account.

By default, API Keys are read-only and can only read basic account information, such as positions, orders, and trades. They cannot be used to trade such as placing, modifying or cancelling orders.

If you wish to execute orders with your API Key, clients must select the Can Trade permission upon API key creation.

API keys are also only bound to a single sub-account, defined upon creation. This means that an API key will only ever interact and return account information for a single sub-account.

Rate Limit

OX.FUN's APIs allows our clients to access and control their accounts or view our market data using custom-written software. To protect the performance of the system, we impose certain limits:

Type Limit
Rest API 100 per second
Rest API 2500 per 5 mins
Initialising Websocket Connection 200 per minute
Websocket API (Auth) 50 per second
Websocket API (No Auth) 1 per second

Websocket API

Subscription request format

{
  "op": "<value>",
  "tag": "<value>",
  "args": ["<value1>", "<value2>",.....]
}

Subscription success response format

{
  "event": "<opValue>",
  "success": True,
  "tag": "<value>",
  "channel": "<argsValue>",
  "timestamp": "1592491945368"
}

Subscription failure response format

{
  "event": "<opValue>",
  "success": False,
  "tag": "<value>",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "timestamp": "1592498745368"
}

Command request format

{
  "op": "<value>",
  "tag": "<value>",
  "data": {"<key1>": "<value1>",.....}
}

Command success response format

{
  "event": "<opValue>",
  "success": True
  "tag": "<value>",
  "timestamp": "1592498745368",
  "data": {"<key1>": "<value1>",.....}
}

Command failure response format

{
  "event": "<opValue>",
  "success": False,
  "message": "<errorMessage>",
  "code": "<codeCode>",
  "timestamp": "1592498745368",
  "data": {"<key1>": "<value1>",.....}
}

TEST site

LIVE site

OX.FUN's application programming interface (API) provides our clients programmatic access to control aspects of their accounts and to place orders on OX.FUN. The API is accessible via WebSocket connection to the URIs listed above. Commands, replies, and notifications all traverse the WebSocket in text frames with JSON-formatted payloads.

Websocket commands can be sent in either of the following two formats:

For subscription based requests

{"op": "<value>", "args": ["<value1>", "<value2>",.....]}

op: can either be:

args: the value(s) will be the instrument ID(s) or asset ID(s), for example:

All other commands

{"op": "<command>", "data": {"<key1>":"<value1>",.....}}

op: can be:

data: JSON string of the request object containing the required parameters

Further information regarding the error codes and corresponding error messages from a failed subscription or order command request can be found in a later section of this documentation Error Codes.

Authentication

Request format

{
  "op": "login",
  "tag": "<value>",
  "data": {
            "apiKey": "<string>",
            "timestamp": "<string>",
            "signature": "<string>"
          }
}
import websockets
import asyncio
import time
import hmac
import base64
import hashlib
import json

api_key = 'API-KEY'
api_secret = 'API-SECRET'
ts = str(int(time.time() * 1000))
sig_payload = (ts+'GET/auth/self/verify').encode('utf-8')
signature = base64.b64encode(hmac.new(api_secret.encode('utf-8'), sig_payload, hashlib.sha256).digest()).decode('utf-8')

msg_auth = \
{
  "op": "login",
  "tag": 1,
  "data": {
           "apiKey": api_key,
           "timestamp": ts,
           "signature": signature
          }
}

async def subscribe():
    async with websockets.connect('wss://stgapi.ox.fun/v2/websocket') as ws:
        await ws.send(json.dumps(msg_auth))
        while ws.open:
            resp = await ws.recv()
            print(resp)

asyncio.get_event_loop().run_until_complete(subscribe())
const CryptoJS = require("crypto-js");
const WebSocket = require('ws');

var apiKey = "API-KEY";
var secretKey = "API-SECRET";
const ts = '' + Date.now();

var sign = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(ts +'GET/auth/self/verify', secretKey));
var msg = JSON.stringify({  
                            "op": "login",
                            "tag": 1,
                            "data": {
                              "apiKey": apiKey,
                              "timestamp": ts,
                              "signature": sign
                            }
                          });

var ws = new WebSocket('wss://stgapi.ox.fun/v2/websocket');

ws.onmessage = function (e) {
  console.log('websocket message from server : ', e.data);
};

ws.onopen = function () {
    ws.send(msg);
};

Success response format

{
  "event": "login",
  "success": true,
  "tag": "<value>",
  "timestamp": "1592491803978"
}
{
  "event": "login",
  "success": true,
  "tag": "1",
  "timestamp": "1592491808328"
}
{
  "event": "login",
  "success": true,
  "tag": "1",
  "timestamp": "1592491808329"
}

Failure response format

{
  "event": "login",
  "success": false,
  "code": "<errorCode>",
  "message": "<errorMessage>",
  "tag": "1",
  "timestamp": "1592492069732"
}
{
  "event": "login",
  "success": false,
  "code": "<errorCode>",
  "message": "<errorMessage>",
  "tag": "1",
  "timestamp": "1592492031972"
}
{
  "event": "login",
  "success": false,
  "code": "<errorCode>",
  "message": "<errorMessage>",
  "tag": "1",
  "timestamp": "1592492031982"
}

The Websocket API consists of public and private methods. The public methods do not require authentication. The private methods requires an authenticated websocket connection.

To autenticate a websocket connection a "login" message must be sent containing the clients signature.

The signature is constructed using a HMAC SHA256 operation to get a hash value, which in turn requires the clients API Secret as the key and a constructed message string as the value for the HMAC operation. This hash value is then encoded as a BASE-64 value which becomes the signature used for authentication.

API keys (public and corresponding secret key) can be generated via the GUI within the clients account.

The message string used in the HMAC SHA256 operation is constructed in the following way:

The signature can therefore be summarised by the following:

Request Parameters

Parameter Type Required Description
op STRING Yes 'login'
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
data DICTIONARY object Yes
apiKey STRING Yes Clients public API key, visible in the GUI when created
timestamp STRING Yes Current millisecond timestamp
signature STRING Yes Base64(HmacSHA256(current_ms_timestamp + 'GET/auth/self/verify', API-Secret))

Session Keep Alive

To maintain an active WebSocket connection it is imperative to either be subscribed to a channel that pushes data at least once per minute (Depth) or periodically send a text “ping” (without the quotation mark) to the server within an minute and then the server will reply text “pong” (without quotation mark) to you.

Self Trade Prevention Modes

Self trade prevention (STP) helps traders avoid costly self-matching within an account and between subaccounts. There are 4 STP modes available to traders NONE, EXPIRE_MAKER, EXPIRE_TAKER, EXPIRE_BOTH.

NONE - has no protection meaning self-matching is possible.

EXPIRE_MAKER - cancels the resting (would-be maker) order regardless of its STP mode, the aggressing order continues to match and/or is placed into the book.

EXPIRE_TAKER - cancels the aggressing order to avoid self-matching, acts similar to an IOC that is cancelled in the event of a self-match.

EXPIRE_BOTH - cancels both the agressing order and the resting (would-be maker) order regardless of the resting order's STP mode.

Note that the STP system uses the aggressing (would-be taker) order to decide which action to take, for example:

Placing an EXPIRE_TAKER order which collides with a resting EXPIRE_MAKER order causes the aggressing EXPIRE_TAKER order to be cancelled, while the EXPIRE_MAKER order will remain.

Order Commands

Place Limit Order

Request format

{
  "op": "placeorder",
  "tag": 123,
  "data": {
            "timestamp": 1638237934061,
            "recvWindow": 500,
            "clientOrderId": 1,
            "marketCode": "BTC-USD-SWAP-LIN",
            "side": "BUY",
            "orderType": "LIMIT",
            "quantity": 1.5,
            "timeInForce": "GTC",
            "price": 9431.48
          }
}
import websockets
import asyncio
import time
import hmac
import base64
import hashlib
import json

api_key = ''
api_secret = ''
ts = str(int(time.time() * 1000))
sig_payload = (ts+'GET/auth/self/verify').encode('utf-8')
signature = base64.b64encode(hmac.new(api_secret.encode('utf-8'), sig_payload, hashlib.sha256).digest()).decode('utf-8')

auth = \
{
  "op": "login",
  "tag": 1,
  "data": {
           "apiKey": api_key,
           "timestamp": ts,
           "signature": signature
          }
}
place_order = \
{
  "op": "placeorder",
  "tag": 123,
  "data": {
            "timestamp": 1638237934061,
            "recvWindow": 500,
            "clientOrderId": 1,
            "marketCode": "BTC-USD-SWAP-LIN",
            "side": "BUY",
            "orderType": "LIMIT",
            "quantity": 1.5,
            "timeInForce": "GTC",
            "price": 9431.48
          }
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            if 'nonce' in data:
                    await ws.send(json.dumps(auth))
            elif 'event' in data and data['event'] == 'login':
                if data['success'] == True:
                    await ws.send(json.dumps(place_order))
            elif 'event' in data and data['event'] == 'placeorder':
                continue

asyncio.get_event_loop().run_until_complete(subscribe())

Success response format


{
  "event": "placeorder",
  "submitted": True,
  "tag": "123",
  "timestamp": "1592491945248",
  "data": {
            "clientOrderId": "1",
            "marketCode": "BTC-USD-SWAP-LIN",
            "side": "BUY",
            "orderType": "LIMIT",
            "quantity": "1.5",
            "timeInForce": "GTC",
            "orderId": "1000000700008",
            "price": "9431.48",
            "limitPrice": "9431.48",
            "source": 0
          }
}

Failure response format

{
  "event": "placeorder",
  "submitted": False,
  "tag": "123",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "timestamp": "1592491945248",
  "data": {
            "clientOrderId": "1",
            "marketCode": "BTC-USD-SWAP-LIN",
            "side": "BUY",
            "orderType": "LIMIT",
            "quantity": "1.5",
            "timeInForce": "GTC",
            "price": "9431.48",
            "limitPrice": "9431.48",
            "source": 0
          }
}

Requires an authenticated websocket connection. Please also subscribe to the User Order Channel to receive push notifications for all message updates in relation to an account or sub-account (e.g. OrderOpened, OrderMatched etc......).

Request Parameters

Parameter Type Required Description
op STRING Yes placeorder
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
data DICTIONARY object Yes
clientOrderId ULONG No Client assigned ID to help manage and identify orders with max value 9223372036854775807
marketCode STRING Yes Market code e.g. BTC-USD-SWAP-LIN
orderType STRING Yes LIMIT
price FLOAT Yes Price
quantity FLOAT Yes Quantity (denominated by contractValCurrency)
displayQuantity FLOAT NO If given, the order becomes an iceberg order, and denotes the quantity to show on the book
side STRING Yes BUY or SELL
timeInForce ENUM No
  • GTC (Good-till-Cancel) - Default
  • IOC (Immediate or Cancel, i.e. Taker-only)
  • FOK (Fill or Kill, for full size)
  • MAKER_ONLY (i.e. Post-only)
  • MAKER_ONLY_REPRICE (Reprices order to the best maker only price if the specified price were to lead to a taker trade)
timestamp LONG No In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.
recvWindow LONG No In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.
selfTradePreventionMode STRING No NONE, EXPIRE_MAKER, EXPIRE_TAKER, EXPIRE_BOTH

Place Market Order

Request format

{
  "op": "placeorder",
  "tag": 123,
  "data": {
            "timestamp": 1638237934061,
            "recvWindow": 500,
            "clientOrderId": 1,
            "marketCode": "ETH-USD-SWAP-LIN",
            "side": "SELL",
            "orderType": "MARKET",
            "quantity": 5
          }
}
import websockets
import asyncio
import time
import hmac
import base64
import hashlib
import json

api_key = '' 
api_secret = ''
ts = str(int(time.time() * 1000))
sig_payload = (ts+'GET/auth/self/verify').encode('utf-8')
signature = base64.b64encode(hmac.new(api_secret.encode('utf-8'), sig_payload, hashlib.sha256).digest()).decode('utf-8')

auth = \
{
  "op": "login",
  "tag": 1,
  "data": {
           "apiKey": api_key,
           "timestamp": ts,
           "signature": signature
          }
}
place_order = \
{
  "op": "placeorder",
  "tag": 123,
  "data": {
            "timestamp": 1638237934061,
            "recvWindow": 500,
            "clientOrderId": 1,
            "marketCode": "ETH-USD-SWAP-LIN",
            "side": "SELL",
            "orderType": "MARKET",
            "quantity": 5
          }
}


url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)

            if 'nonce' in data:
                    await ws.send(json.dumps(auth))
            elif 'event' in data and data['event'] == 'login':
                if data['success'] == True:
                    await ws.send(json.dumps(place_order))
            elif 'event' in data and data['event'] == 'placeorder':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "event": "placeorder",
  "submitted": True,
  "tag": "123",
  "timestamp": "1592491945248",
  "data": {
            "clientOrderId": "1",
            "marketCode": "ETH-USD-SWAP-LIN",
            "side": "SELL",
            "orderType": "MARKET",
            "quantity": "5",
            "orderId": "1000000700008",
            "limitPrice": "1700.00",
            "source": 0
          }
}

Failure response format

{
  "event": "placeorder",
  "submitted": False,
  "tag": "123",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "timestamp": "1592491503359",
  "data": {
            "clientOrderId": "1",
            "marketCode": "ETH-USD-SWAP-LIN",
            "side": "SELL",
            "orderType": "MARKET",
            "quantity": "5",
            "limitPrice": "1700.00",
            "source": 0
          }
}

Requires an authenticated websocket connection. Please also subscribe to the User Order Channel to receive push notifications for all message updates in relation to an account or sub-account (e.g. OrderOpened, OrderMatched etc......).

Request Parameters

Parameter Type Required Description
op STRING Yes placeorder
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
data DICTIONARY object Yes
clientOrderId ULONG No Client assigned ID to help manage and identify orders with max value 9223372036854775807
marketCode STRING Yes Market code e.g. BTC-USD-SWAP-LIN
orderType STRING Yes MARKET
quantity FLOAT Yes Quantity (denominated by contractValCurrency), not required if an amount is provided
amount STRING NO An amount of USDT can be specified instead of a quantity. Only valid for spot market buy orders
side STRING Yes BUY or SELL
timestamp LONG NO In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.
recvWindow LONG NO In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.
selfTradePreventionMode STRING No NONE, EXPIRE_MAKER, EXPIRE_TAKER, EXPIRE_BOTH

Place Stop Limit Order

Request format

{
  "op": "placeorder",
  "tag": 123,
  "data": {
            "timestamp": 1638237934061,
            "recvWindow": 500,
            "clientOrderId": 1,
            "marketCode": "ETH-USD-SWAP-LIN",
            "side": "BUY",
            "orderType": "STOP_LIMIT",
            "quantity": 10,
            "timeInForce": "MAKER_ONLY_REPRICE",
            "stopPrice": 100,
            "limitPrice": 120
         }
}
import websockets
import asyncio
import time
import hmac
import base64
import hashlib
import json

api_key = ''
api_secret = ''
ts = str(int(time.time() * 1000))
sig_payload = (ts+'GET/auth/self/verify').encode('utf-8')
signature = base64.b64encode(hmac.new(api_secret.encode('utf-8'), sig_payload, hashlib.sha256).digest()).decode('utf-8')

auth = \
{
  "op": "login",
  "tag": 1,
  "data": {
           "apiKey": api_key,
           "timestamp": ts,
           "signature": signature
          }
}
place_order = \
{
  "op": "placeorder",
  "tag": 123,
  "data": {
            "timestamp": 1638237934061,
            "recvWindow": 500,
            "clientOrderId": 1,
            "marketCode": "ETH-USD-SWAP-LIN",
            "side": "BUY",
            "orderType": "STOP_LIMIT",
            "quantity": 10,
            "timeInForce": "MAKER_ONLY_REPRICE",
            "stopPrice": 100,
            "limitPrice": 120
         }
}


url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)

            if 'nonce' in data:
                    await ws.send(json.dumps(auth))
            elif 'event' in data and data['event'] == 'login':
                if data['success'] == True:
                    await ws.send(json.dumps(place_order))
            elif 'event' in data and data['event'] == 'placeorder':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "event": "placeorder",
  "submitted": True,
  "tag": "123",
  "timestamp": "1607639739098",
  "data": {
            "clientOrderId": "1",
            "marketCode": "ETH-USD-SWAP-LIN",
            "side": "BUY",
            "orderType": "STOP_LIMIT",
            "quantity": "10",
            "timeInForce": "MAKER_ONLY_REPRICE",
            "price": "120",
            "limitPrice": "120",
            "stopPrice": "100",
            "orderId": "1000000700008",
            "source": 0
            "triggerType": "MARK_PRICE"
          }
}

Failure response format

{
  "event": "placeorder",
  "submitted": False,
  "tag": "123",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "timestamp": "1592491503359",
  "data": {
            "clientOrderId": "1",
            "marketCode": "ETH-USD-SWAP-LIN",
            "side": "BUY",
            "orderType": "STOP_LIMIT",
            "quantity": "10",
            "timeInForce": "MAKER_ONLY_REPRICE",
            "price": "120",
            "stopPrice": "100",
            "limitPrice": "120",
            "source": 0,
            "triggerType": "MARK_PRICE"
          }
}

Requires an authenticated websocket connection. Please also subscribe to the User Order Channel to receive push notifications for all message updates in relation to an account or sub-account (e.g. OrderOpened, OrderMatched etc......).

Request Parameters

Parameters Type Required Description
op STRING Yes placeorder
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
data DICTIONARY object Yes
clientOrderId ULONG No Client assigned ID to help manage and identify orders with max value 9223372036854775807
marketCode STRING Yes Market code e.g. ETH-USD-SWAP-LIN
orderType STRING Yes STOP_LIMIT for stop-limit orders
quantity FLOAT Yes Quantity (denominated by contractValCurrency)
side STRING Yes BUY or SELL
limitPrice FLOAT Yes Limit price for the stop-limit order.

For BUY the limit price must be greater or equal to the stop price.

For SELL the limit price must be less or equal to the stop price.

stopPrice FLOAT Yes Stop price for the stop-limit order.

Triggered by the best bid price for the SELL stop-limit order.

Triggered by the best ask price for the BUY stop-limit order.

timeInForce ENUM No
  • GTC (Good-till-Cancel) - Default
  • IOC (Immediate or Cancel, i.e. Taker-only)
  • FOK (Fill or Kill, for full size)
  • MAKER_ONLY (i.e. Post-only)
  • MAKER_ONLY_REPRICE (Reprices order to the best maker only price if the specified price were to lead to a taker trade)
timestamp LONG NO In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.
recvWindow LONG NO In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.
selfTradePreventionMode STRING No NONE, EXPIRE_MAKER, EXPIRE_TAKER, EXPIRE_BOTH

Place Stop Market Order

Stop market orders are only available in Perp markets.

Request format

{
  "op": "placeorder",
  "tag": 123,
  "data": {
            "timestamp": 1679907302693,
            "recvWindow": 500,
            "clientOrderId": 1679907301552,
            "marketCode": "BTC-USD-SWAP-LIN",
            "side": "SELL",
            "orderType": "STOP_MARKET",
            "quantity": 0.012,
            "stopPrice": 22279.29
         }
}
import websockets
import asyncio
import time
import hmac
import base64
import hashlib
import json

api_key = ''
api_secret = ''
ts = str(int(time.time() * 1000))
sig_payload = (ts+'GET/auth/self/verify').encode('utf-8')
signature = base64.b64encode(hmac.new(api_secret.encode('utf-8'), sig_payload, hashlib.sha256).digest()).decode('utf-8')

auth = \
{
  "op": "login",
  "tag": 1,
  "data": {
           "apiKey": api_key,
           "timestamp": ts,
           "signature": signature
          }
}
place_order = \
{
  "op": "placeorder",
  "tag": 123,
  "data": {
            "timestamp": 1679907302693,
            "recvWindow": 500,
            "clientOrderId": 1679907301552,
            "marketCode": "BTC-USD-SWAP-LIN",
            "side": "SELL",
            "orderType": "STOP_MARKET",
            "quantity": 0.001,
            "stopPrice": 22279.29
         }
}


url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)

            if 'nonce' in data:
                    await ws.send(json.dumps(auth))
            elif 'event' in data and data['event'] == 'login':
                if data['success'] == True:
                    await ws.send(json.dumps(place_order))
            elif 'event' in data and data['event'] == 'placeorder':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "event": "placeorder",
  "submitted": True,
  "tag": "123",
  "timestamp": "1607639739098",
  "data": {
            "clientOrderId": "1679907301552",
            "marketCode": "BTC-USD-SWAP-LIN",
            "side": "SELL",
            "orderType": "STOP_MARKET",
            "quantity": "0.012",
            "timeInForce": "IOC",
            "price": "25000",
            "limitPrice": "25000",
            "stopPrice": "22279.29",
            "orderId": "1000001680990",
            "triggerType": "MARK_PRICE",
            "source": 0
          }
}

Failure response format

{
  "event": "placeorder",
  "submitted": False,
  "tag": "123",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "timestamp": "1679907302693",
  "data": {
           "clientOrderId": "1679907301552",
           "marketCode": "BTC-USD-SWAP-LIN",
           "side": "SELL",
           "orderType": "STOP_MARKET",
           "quantity": "0.012",
           "timeInForce": "IOC",
           "price": "25000",
           "limitPrice": "25000",
           "stopPrice": "22279.29"
           "triggerType": "MARK_PRICE",
           "source": 0
          }
}

Requires an authenticated websocket connection. Please also subscribe to the User Order Channel to receive push notifications for all message updates in relation to an account or sub-account (e.g. OrderOpened, OrderMatched etc......).

Request Parameters

Parameters Type Required Description
op STRING Yes placeorder
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
data DICTIONARY object Yes
clientOrderId ULONG No Client assigned ID to help manage and identify orders with max value 9223372036854775807
marketCode STRING Yes Market code e.g. BTC-USD-SWAP-LIN
orderType STRING Yes STOP_MARKET
quantity FLOAT Yes Quantity (denominated by contractValCurrency)
side STRING Yes BUY or SELL
stopPrice FLOAT Yes Stop price for the stop-market order.

Triggered by the best bid price for the SELL stop-market order.

Triggered by the best ask price for the BUY stop-market order.

timestamp LONG NO In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.
recvWindow LONG NO In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.
selfTradePreventionMode STRING No NONE, EXPIRE_MAKER, EXPIRE_TAKER, EXPIRE_BOTH

Place Batch Orders

Request format

{
  "op": "placeorders",
  "tag": 123,
  "dataArray": [{
                  "timestamp": 1638237934061,
                  "recvWindow": 500,
                  "clientOrderId": 1,
                  "marketCode": "ETH-USD-SWAP-LIN",
                  "side": "BUY",
                  "orderType": "LIMIT",
                  "quantity": 10,
                  "timeInForce": "MAKER_ONLY",
                  "price": 100
                }, 
                {
                  "timestamp": 1638237934061,
                  "recvWindow": 500,
                  "clientOrderId": 2,
                  "marketCode": "BTC-USDT",
                  "side": "SELL",
                  "orderType": "MARKET",
                  "quantity": 0.2
                }]
}
import websockets
import asyncio
import time
import hmac
import base64
import hashlib
import json

api_key = ''
api_secret = ''
ts = str(int(time.time() * 1000))
sig_payload = (ts+'GET/auth/self/verify').encode('utf-8')
signature = base64.b64encode(hmac.new(api_secret.encode('utf-8'), sig_payload, hashlib.sha256).digest()).decode('utf-8')

auth = \
{
  "op": "login",
  "tag": 1,
  "data": {
           "apiKey": api_key,
           "timestamp": ts,
           "signature": signature
          }
}
place_batch_order =\
{
  "op": "placeorders",
  "tag": 123,
  "dataArray": [{
                  "timestamp": 1638237934061,
                  "recvWindow": 500,
                  "clientOrderId": 1,
                  "marketCode": "ETH-USD-SWAP-LIN",
                  "side": "BUY",
                  "orderType": "LIMIT",
                  "quantity": 10,
                  "timeInForce": "MAKER_ONLY",
                  "price": 100
                },
                {
                  "timestamp": 1638237934061,
                  "recvWindow": 500,
                  "clientOrderId": 2,
                  "marketCode": "BTC-USDT",
                  "side": "SELL",
                  "orderType": "MARKET",
                  "quantity": 0.2
                }]
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)
            if 'nonce' in data:
                    await ws.send(json.dumps(auth))
            elif 'event' in data and data['event'] == 'login':
                if data['success'] == True:
                    await ws.send(json.dumps(place_batch_order))
            elif 'event' in data and data['event'] == 'placeorder':
                continue

asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "event": "placeorder",
  "submitted": True,
  "tag": "123",
  "timestamp": "1607639739098",
  "data": {
            "clientOrderId": "1",
            "marketCode": "ETH-USD-SWAP-LIN",
            "side": "BUY",
            "orderType": "LIMIT",
            "quantity": "10",
            "timeInForce": "MAKER_ONLY",
            "price": "100",
            "limitPrice": "100",
            "orderId": "1000003700008",
            "source": 0
          }
}

AND

{
  "event": "placeorder",
  "submitted": True,
  "tag": "123",
  "timestamp": "1607639739136",
  "data": {
            "clientOrderId": "2",
            "marketCode": "BTC-USDT",
            "side": "SELL",
            "orderType": "MARKET",
            "quantity": "0.2",
            "orderId": "1000004700009",
            "limitPrice": "20000",
            "source": 0
          }
}

Failure response format

{
  "event": "placeorder",
  "submitted": False,
  "tag": "123",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "timestamp": "1592491503359",
  "data": {
            "clientOrderId": "1",
            "marketCode": "ETH-USD-SWAP-LIN",
            "side": "BUY",
            "orderType": "LIMIT",
            "quantity": "10",
            "timeInForce": "MAKER_ONLY",
            "price": "100",
            "limitPrice": "100",
            "source": 0
          }
}

AND

{
  "event": "placeorder",
  "submitted": False,
  "tag": "123",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "timestamp": "1592491503457",
  "data": {
            "clientOrderId": "2",
            "marketCode": "BTC-USDT",
            "side": "SELL",
            "orderType": "MARKET",
            "quantity": "0.2",
            "limitPrice": "20000",
            "source": 0
          }
}

Requires an authenticated websocket connection. Please also subscribe to the User Order Channel to receive push notifications for all message updates in relation to an account or sub-account (e.g. OrderOpened, OrderMatched etc......).

All existing single order placement methods are supported:-

The websocket reply from the exchange will repond to each order in the batch separately, one order at a time, and has the same message format as the reponse for the single order placement method.

Request Parameters

Parameters Type Required Description
op STRING Yes placeorders
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
dataArray LIST of dictionaries Yes A list of orders with each order in JSON format, the same format/parameters as the request for placing a single order. The max number of orders is still limited by the message length validation so by default up to 20 orders can be placed in a batch, assuming that each order JSON has 200 characters.
timestamp LONG NO In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.
recvWindow LONG NO In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.

Cancel Order

Request format

{
  "op": "cancelorder",
  "tag": 456,
  "data": {
            "marketCode": "BTC-USD-SWAP-LIN",
            "orderId": 12
          }
}
import websockets
import asyncio
import time
import hmac
import base64
import hashlib
import json

api_key = ''
api_secret = ''
ts = str(int(time.time() * 1000))
sig_payload = (ts+'GET/auth/self/verify').encode('utf-8')
signature = base64.b64encode(hmac.new(api_secret.encode('utf-8'), sig_payload, hashlib.sha256).digest()).decode('utf-8')

auth = \
{
  "op": "login",
  "tag": 1,
  "data": {
           "apiKey": api_key,
           "timestamp": ts,
           "signature": signature
          }
}
cancel_order = \
{
  "op": "cancelorder",
  "tag": 456,
  "data": {
            "marketCode": "BTC-USD-SWAP-LIN",
            "orderId": 12
          }
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)

            if 'nonce' in data:
                    await ws.send(json.dumps(auth))
            elif 'event' in data and data['event'] == 'login':
                if data['success'] == True:
                    await ws.send(json.dumps(cancel_order))
            elif 'event' in data and data['event'] == 'cancelorder':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "event": "cancelorder",
  "submitted": True,
  "tag": "456",
  "timestamp": "1592491173964",
  "data": {
            "marketCode": "BTC-USD-SWAP-LIN",
            "clientOrderId": "1",
            "orderId": "12"
          }
}

Failure response format

{
  "event": "cancelorder",
  "submitted": False,
  "tag": "456",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "timestamp": "1592491173964",
  "data": {
            "marketCode": "BTC-USD-SWAP-LIN",
            "orderId": "12"
          }
}

Requires an authenticated websocket connection. Please also subscribe to the User Order Channel to receive push notifications for all message updates in relation to an account or sub-account (e.g. OrderClosed etc......).

This command can also be actioned via the trading GUI using the Cancel button next to an open order in the Open Orders blotter for both Spot and Derivative markets.

Request Parameters

Parameters Type Required Description
op STRING Yes cancelorder
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
data DICTIONARY object Yes
marketCode STRING Yes Market code e.g. BTC-USD-SWAP-LIN
orderId INTEGER Yes Unique order ID from the exchange

Cancel Batch Orders

Request format

{
  "op": "cancelorders",
  "tag": 456,
  "dataArray": [{
                  "marketCode": "BTC-USD-SWAP-LIN",
                  "orderId": 12
                },
                {
                  "marketCode": "BCH-USDT",
                  "orderId": 34
                }]
}
import websockets
import asyncio
import time
import hmac
import base64
import hashlib
import json

api_key = ''
api_secret = ''
ts = str(int(time.time() * 1000))
sig_payload = (ts+'GET/auth/self/verify').encode('utf-8')
signature = base64.b64encode(hmac.new(api_secret.encode('utf-8'), sig_payload, hashlib.sha256).digest()).decode('utf-8')

auth = \
{
  "op": "login",
  "tag": 1,
  "data": {
           "apiKey": api_key,
           "timestamp": ts,
           "signature": signature
          }
}
cancel_batch_order = \
{
  "op": "cancelorders",
  "tag": 456,
  "dataArray": [{
                  "marketCode": "BTC-USD-SWAP-LIN",
                  "orderId": 12
                },
                {
                  "marketCode": "BCH-USDT",
                  "orderId": 34
                }]
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)

            if 'nonce' in data:
                    await ws.send(json.dumps(auth))
            elif 'event' in data and data['event'] == 'login':
                if data['success'] == True:
                    await ws.send(json.dumps(cancel_batch_order))
            elif 'event' in data and data['event'] == 'cancelorder':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "event": "cancelorder",
  "submitted": True,
  "tag": "456",
  "timestamp": "1592491173964",
  "data": {
            "marketCode": "BTC-USD-SWAP-LIN",
            "clientOrderId": "1",
            "orderId": "12"
          }
}

AND

{
  "event": "cancelorder",
  "submitted": True,
  "tag": "456",
  "timestamp": "1592491173978",
  "data": {
            "marketCode": "BCH-USDT",
            "orderId": "34"
          }
}

Failure response format

{
  "event": "cancelorder",
  "submitted": False,
  "tag": "456",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "timestamp": "1592491173964",
  "data": {
            "marketCode": "BTC-USD-SWAP-LIN",
            "orderId": "12"
          }
}

AND

{
  "event": "cancelorder",
  "submitted": False,
  "tag": "456",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "timestamp": "1592491173989",
  "data": {
            "marketCode": "BCH-USDT",
            "orderId": "12"
          }
}

Requires an authenticated websocket connection. Please also subscribe to the User Order Channel to receive push notifications for all message updates in relation to an account or sub-account (e.g. OrderClosed etc......).

Request Parameters

Parameters Type Required Description
op STRING Yes cancelorders
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
dataArray LIST of dictionaries Yes A list of orders with each order in JSON format, the same format/parameters as the request for cancelling a single order. The max number of orders is still limited by the message length validation so by default up to 20 orders can be placed in a batch, assuming that each order JSON has 200 characters.

Modify Order

Request format

{
  "op": "modifyorder",
  "tag": 1,
  "data": {
            "timestamp": 1638237934061,
            "recvWindow": 500,
            "marketCode": "BTC-USD-SWAP-LIN",
            "orderId": 888,
            "side": "BUY",
            "price": 9800,
            "quantity": 2
          }
}
import websockets
import asyncio
import time
import hmac
import base64
import hashlib
import json

api_key = ''
api_secret = ''
ts = str(int(time.time() * 1000))
sig_payload = (ts+'GET/auth/self/verify').encode('utf-8')
signature = base64.b64encode(hmac.new(api_secret.encode('utf-8'), sig_payload, hashlib.sha256).digest()).decode('utf-8')

auth = \
{
  "op": "login",
  "tag": 1,
  "data": {
           "apiKey": api_key,
           "timestamp": ts,
           "signature": signature
          }
}
modify_order = \
{
  "op": "modifyorder",
  "tag": 1,
  "data": {
            "timestamp": 1638237934061,
            "recvWindow": 500,
            "marketCode": "BTC-USD-SWAP-LIN",
            "orderId": 888,
            "side": "BUY",
            "price": 9800,
            "quantity": 2
          }
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)
            if 'nonce' in data:
                    await ws.send(json.dumps(auth))
            elif 'event' in data and data['event'] == 'login':
                if data['success'] == True:
                    await ws.send(json.dumps(modify_order))
            elif 'event' in data and data['event'] == 'modifyorder':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "event": "modifyorder",
  "submitted": True,
  "tag": "1",
  "timestamp": "1592491032427",
  "data":{
      "clientOrderId": "1",
      "orderId": "888",
      "side": "BUY",
      "quantity": "2"
      "price": "9800",
      "limitPrice": "9800",
      "orderType": "LIMIT",
      "marketCode": "BTC-USD-SWAP-LIN"
  }
}

Failure response format

{
  "event": "modifyorder",
  "submitted": False,
  "tag": "1",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "timestamp": "1592491032427",
  "data": {
            "orderId": "888",
            "side": "BUY",
            "quantity": "2",
            "price": "9800",
            "limitPrice": "9800",
            "marketCode": "BTC-USD-SWAP-LIN"
          }
}

Requires an authenticated websocket connection. Please also subscribe to the User Order Channel to receive push notifications for all message updates in relation to an account or sub-account (e.g. OrderModified etc......).

Currently only LIMIT orders are supported by the modify order command.

Modified orders retain their original orderId.

Request Parameters

Parameters Type Required Description
op STRING Yes modifyorder
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
data DICTIONARY object Yes
marketCode STRING Yes Market code e.g. BTC-USD-SWAP-LIN
orderId INTEGER Yes Unique order ID from the exchange
side STRING No BUY or SELL
price FLOAT No Price for limit orders
quantity FLOAT No Quantity (denominated by contractValCurrency)
timestamp LONG No In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.
recvWindow LONG No In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.

Modify Batch Orders

Request format

{
  "op": "modifyorders",
  "tag": 123,
  "dataArray": [{
                  "timestamp": 1638237934061,
                  "recvWindow": 500,
                  "marketCode": "ETH-USD-SWAP-LIN",
                  "side": "BUY",
                  "orderID": 304304315061932310,
                  "price": 101,
                }, 
                {
                  "timestamp": 1638237934061,
                  "recvWindow": 500,
                  "marketCode": "BTC-USDT",
                  "orderID": 304304315061864646,
                  "price": 10001,
                  "quantity": 0.21
                }]
}
import websockets
import asyncio
import time
import hmac
import base64
import hashlib
import json

api_key = ''
api_secret = ''
ts = str(int(time.time() * 1000))
sig_payload = (ts+'GET/auth/self/verify').encode('utf-8')
signature = base64.b64encode(hmac.new(api_secret.encode('utf-8'), sig_payload, hashlib.sha256).digest()).decode('utf-8')

auth = \
{
  "op": "login",
  "tag": 1,
  "data": {
           "apiKey": api_key,
           "timestamp": ts,
           "signature": signature
          }
}
modify_batch_order = \
{
  "op": "modifyorders",
  "tag": 123,
  "dataArray": [{
                  "timestamp": 1638237934061,
                  "recvWindow": 500,
                  "marketCode": "ETH-USD-SWAP-LIN",
                  "side": "BUY",
                  "orderID": 304304315061932310,
                  "price": 101,
                }, 
                {
                  "timestamp": 1638237934061,
                  "recvWindow": 500,
                  "marketCode": "BTC-USDT",
                  "orderID": 304304315061864646,
                  "price": 10001,
                  "quantity": 0.21
                }]
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)
            if 'nonce' in data:
                    await ws.send(json.dumps(auth))
            elif 'event' in data and data['event'] == 'login':
                if data['success'] == True:
                    await ws.send(json.dumps(modify_batch_order))
            elif 'event' in data and data['event'] == 'modifyorder':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "event": "modifyorder",
  "submitted": True,
  "tag": "123",
  "timestamp": "1607639739098",
  "data": {
            "clientOrderId": "100"
            "orderId": "304304315061932310",
            "side": "BUY",
            "quantity": "5",
            "price": "101",
            "limitPrice": "101",
            "orderType": "LIMIT",
            "marketCode": "ETH-USD-SWAP-LIN"
          }
}

AND

{
  "event": "modifyorder",
  "submitted": True,
  "tag": "123",
  "timestamp": "1607639739136",
  "data": {
            "clientOrderId": "200"
            "orderId": "304304315061864646",
            "side": "SELL",
            "quantity": "0.21",
            "price": "10001",
            "limitPrice": "10001",
            "orderType": "LIMIT",
            "marketCode": "BTC-USDT"
          }
}

Failure response format

{
  "event": "placeorder",
  "submitted": False,
  "tag": "123",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "timestamp": "1592491503359",
  "data": {
            "orderID": "304304315061932310",
            "side": "BUY",
            "price": "101",
            "limitPrice": "101",
            "marketCode": "ETH-USD-SWAP-LIN"
          }
}

AND

{
  "event": "placeorder",
  "submitted": False,
  "tag": "123",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "timestamp": "1592491503457",
  "data": {
            "orderID": "304304315061864646",
            "quantity": "0.21",
            "price": "10001",
            "limitPrice": "10001",
            "marketCode": "BTC-USDT"
          }
}

Requires an authenticated websocket connection. Please also subscribe to the User Order Channel to receive push notifications for all message updates in relation to an account or sub-account (e.g. OrderOpened, OrderMatched etc......).

The websocket responses from the exchange will come separately for each order in the batch, one order at a time, and the message has the same format as the single modifyorder method.

Request Parameters

Parameters Type Required Description
op STRING Yes modifyorders
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
dataArray LIST of dictionaries Yes A list of orders with each order in JSON format, the same format/parameters as the request for modifying a single order. The max number of orders is still limited by the message length validation so by default up to 20 orders can be modified in a batch, assuming that each order JSON has 200 characters.
timestamp LONG No In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.
recvWindow LONG No In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected.

Subscriptions - Private

All subscriptions to private account channels requires an authenticated websocket connection.

Multiple subscriptions to different channels both public and private can be made within a single subscription command:

{"op": "subscribe", "args": ["<value1>", "<value2>",.....]}

Balance Channel

Request format

{
  "op": "subscribe",
  "args": ["balance:all"],
  "tag": 101
}

OR

{
  "op": "subscribe", 
  "args": ["balance:USDT", "balance:OX", ........], 
  "tag": 101
}
import websockets
import asyncio
import time
import hmac
import base64
import hashlib
import json

api_key = ''
api_secret = ''
ts = str(int(time.time() * 1000))
sig_payload = (ts+'GET/auth/self/verify').encode('utf-8')
signature = base64.b64encode(hmac.new(api_secret.encode('utf-8'), sig_payload, hashlib.sha256).digest()).decode('utf-8')

auth = \
{
  "op": "login",
  "tag": 1,
  "data": {
           "apiKey": api_key,
           "timestamp": ts,
           "signature": signature
          }
}
balance = \
{
  "op": "subscribe",
  "args": ["balance:all"],
  "tag": 101
}
url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)
            if 'nonce' in data:
                    await ws.send(json.dumps(auth))
            elif 'event' in data and data['event'] == 'login':
                if data['success'] == True:
                    await ws.send(json.dumps(balance))
            elif 'event' in data and data['event'] == 'balance':
                 continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "success": True, 
  "tag": "101", 
  "event": "subscribe", 
  "channel": "<args value>", 
  "timestamp": "1607985371401"
}

Balance channel format

{
  "table": "balance",
  "accountId": "<Your account ID>",
  "timestamp": "1599693365059",
  "tradeType": "STANDARD",
  "data":[
      {
          "total": "10000",
          "reserved": "1000",
          "instrumentId": "USDT",
          "available": "9000",
          "locked": "0"
          "quantityLastUpdated": "1599694369431",
       },
       {
          "total": "100000",
          "reserved": "0",
          "instrumentId": "OX",
          "available": "100000",
          "locked": "0"
          "quantityLastUpdated": "1599694343242",
        }
  ]
}

Channel Update Frequency : On update

The websocket will reply with the shown success response format for subscribed assets with changed balances.

If a subscription has been made to balance:all, the data array in the message from this balance channel will contain a JSON list, otherwise the data array will contain a single JSON corresponding to one spot asset per asset channel subscription.

Request Parameters

Parameters Type Required Description
op STRING Yes subscribe
args LIST Yes balance:all or a list of individual assets balance:<assetId>
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32

Channel Update Fields

Fields Type Description
table STRING balance
accountId STRING Account identifier
timestamp STRING Current millisecond timestamp
tradeType STRING LINEAR, STANDARD, PORTFOLIO
data LIST of dictionaries
total STRING Total spot asset balance
reserved STRING Reserved asset balance for working spot and repo orders
instrumentId STRING Base asset ID e.g. BTC
available STRING Remaining available asset balance (total - reserved)
locked STRING Temporarily locked asset balance
quantityLastUpdated STRING Millisecond timestamp

Position Channel

Request format

{
  "op": "subscribe", 
  "args": ["position:all"], 
  "tag": 102
}

OR

{
  "op": "subscribe",
  "args": ["position:BTC-USD-SWAP-LIN", "position:BCH-USD-SWAP-LIN", ........], 
  "tag": 102
}
import websockets
import asyncio
import time
import hmac
import base64
import hashlib
import json

api_key = ''
api_secret = ''
ts = str(int(time.time() * 1000))
sig_payload = (ts+'GET/auth/self/verify').encode('utf-8')
signature = base64.b64encode(hmac.new(api_secret.encode('utf-8'), sig_payload, hashlib.sha256).digest()).decode('utf-8')

auth = \
{
  "op": "login",
  "tag": 1,
  "data": {
           "apiKey": api_key,
           "timestamp": ts,
           "signature": signature
          }
}
position = \
{
  "op": "subscribe", 
  "args": ["position:all"], 
  "tag": 102
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)
            if 'nonce' in data:
                    await ws.send(json.dumps(auth))
            elif 'event' in data and data['event'] == 'login':
                if data['success'] == True:
                    await ws.send(json.dumps(position))
            elif 'event' in data and data['event'] == 'position':
                 continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "success": True, 
  "tag": "102", 
  "event": "subscribe", 
  "channel": "<args value>", 
  "timestamp": "1607985371401"
}

Position channel format

{
  "table": "position",
  "accountId": "<Your account ID>",
  "timestamp": "1607985371481",
  "data":[ {
              "instrumentId": "ETH-USD-SWAP-LIN",
              "quantity" : "0.1",
              "lastUpdated": "1616053755423",
              "contractValCurrency": "ETH",
              "entryPrice": "1900.0",
              "positionPnl": "-566.80",
              "estLiquidationPrice": "0",
              "margin": "0",
              "leverage": "0"
            },
            {
              "instrumentId": "ETH-USD-SWAP-LIN",
              "quantity" : "50.54",
              "lastUpdated": "1617099855968",
              "contractValCurrency": "ETH",
              "entryPrice": "2000.0",
              "positionPnl": "1220.9494164000000",
              "estLiquidationPrice": "1317.2",
              "margin": "0",
              "leverage": "0"
            },
            ...
          ]
}

Channel Update Frequency : real-time, on position update

The websocket will reply with the shown success response format for each position channel which has been successfully subscribed to.

If a subscription has been made to position:all, the data array in the message from this position channel will contain a JSON list. Each JSON will contain position details for a different instrument. Otherwise the data array will contain a single JSON corresponding to one instrument.

Request Parameters

Parameters Type Required Description
op STRING Yes subscribe
args LIST Yes position:all or a list of individual instruments position:<instrumentId>
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32

Channel Update Fields

Fields Type Description
table STRING position
accountId STRING Account identifier
timestamp STRING Current millisecond timestamp
data LIST of dictionaries
instrumentId STRING e.g. ETH-USD-SWAP-LIN
quantity STRING Position size (+/-)
lastUpdated STRING Millisecond timestamp
contractValCurrency STRING Base asset ID e.g. ETH
entryPrice STRING Average entry price of total position (Cost / Size)
positionPnl STRING Postion profit and lost in OX
estLiquidationPrice STRING Estimated liquidation price, return 0 if it is negative(<0)
margin STRING Currently always reports 0
leverage STRING Currently always reports 0

Order Channel

Request format

{
  "op": "subscribe", 
  "args": ["order:all"], 
  "tag": 102
}

OR

{
  "op": "subscribe", 
  "args": ["order:OX-USDT", "order:ETH-USD-SWAP-LIN", .....], 
  "tag": 102
}
import websockets
import asyncio
import time
import hmac
import base64
import hashlib
import json

api_key = ''
api_secret = ''
ts = str(int(time.time() * 1000))
sig_payload = (ts+'GET/auth/self/verify').encode('utf-8')
signature = base64.b64encode(hmac.new(api_secret.encode('utf-8'), sig_payload, hashlib.sha256).digest()).decode('utf-8')

auth = \
{
  "op": "login",
  "tag": 1,
  "data": {
           "apiKey": api_key,
           "timestamp": ts,
           "signature": signature
          }
}
order = \
{
  "op": "subscribe", 
  "args": ["order:all"], 
  "tag": 102
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)
            if 'nonce' in data:
                    await ws.send(json.dumps(auth))
            elif 'event' in data and data['event'] == 'login':
                if data['success'] == True:
                    await ws.send(json.dumps(order))
            elif 'event' in data and data['event'] == 'order':
                 continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "success": True, 
  "tag": "102", 
  "event": "subscribe", 
  "channel": "<args value>", 
  "timestamp": "1607985371401"
}

Channel Update Frequency : real-time, on order update

Every order update for a particular sub-account will be relayed to all of its active connections. This implies that for each sub-account, the order responses for all connected markets will be sent to all active subscriptions, even if a specific connection is only subscribed to a single market.

Request Parameters

Parameters Type Required Description
op STRING Yes subscribe
args LIST Yes order:all or a list of individual markets order:<marketCode>
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32

OrderOpened

OrderOpened message format - LIMIT order

{
  "table": "order",
  "data": [ 
      {
          "notice": "OrderOpened",
          "accountId": "<Your account ID>",
          "clientOrderId": "16",
          "orderId" : "123",
          "price": "9600",
          "limitPrice": "9600",
          "quantity": "2",
          "remainQuantity": "2",
          "amount": "0.0",
          "side": "BUY",
          "status": "OPEN",
          "marketCode": "BTC-USD-SWAP-LIN",
          "timeInForce": "MAKER_ONLY",
          "timestamp": "1594943491077",
          "orderType": "LIMIT",
          "isTriggered": "False",
          "displayQuantity": "2"
       }
  ]
}

OrderOpened message format - STOP MARKET order

{
  "table": "order",
  "data": [
      {
          "accountId": "<Your account ID>", 
          "clientOrderId": "1", 
          "orderId": "1000021706785", 
          "price": "12000.0", 
          "quantity": "0.001", 
          "amount": "0.0", 
          "side": "BUY", 
          "status": "OPEN", 
          "marketCode": "BTC-USD-SWAP-LIN", 
          "timeInForce": "IOC", 
          "timestamp": "1680042503604", 
          "remainQuantity": "0.001", 
          "stopPrice": "10000.0", 
          "limitPrice": "12000.0", 
          "notice": "OrderOpened", 
          "orderType": "STOP_MARKET", 
          "isTriggered": "false", 
          "triggerType": "MARK_PRICE", 
          "displayQuantity": "0.001"
      }
  ]
}

Channel Update Fields

Fields Type Description
table STRING order
data LIST of dictionary
notice STRING OrderOpened
accountId STRING Account identifier
clientOrderId STRING Client assigned ID to help manage and identify orders with max value 9223372036854775807
orderId STRING Unique order ID from the exchange
price STRING Limit price submitted (only applicable for LIMIT order types)
quantity STRING Quantity submitted
amount STRING "0.0" if not provided in the request
side STRING BUY or SELL
status STRING Order status
marketCode STRING Market code e.g. OX-USDT
timeInForce STRING Client submitted time in force, GTC by default
timestamp STRING Current millisecond timestamp
remainQuantity STRING Working quantity
orderType STRING LIMIT, STOP_LIMIT, or STOP_MARKET
stopPrice STRING Stop price submitted (only applicable for STOP order types)
limitPrice STRING Limit price submitted
isTriggered STRING False or True
triggerType STRING Stops are triggered on MARK_PRICE
displayQuantity STRING Quantity displayed in the book, primarily used for iceberg orders, otherwise echos the quantity field

OrderClosed

OrderClosed message format - LIMIT order

{
  "table": "order", 
  "data": [
      {
          "accountId": "<Your account ID>", 
          "clientOrderId": "1", 
          "orderId": "1000021764611", 
          "price": "20000.0", 
          "quantity": "0.001", 
          "amount": "0.0", 
          "side": "BUY", 
          "status": "CANCELED_BY_USER", 
          "marketCode": "BTC-USD-SWAP-LIN", 
          "timeInForce": "GTC", 
          "timestamp": "1680043402806", 
          "remainQuantity": "0.001", 
          "limitPrice": "20000.0", 
          "notice": "OrderClosed", 
          "orderType": "LIMIT", 
          "isTriggered": "false", 
          "displayQuantity": "0.001"
      }
  ]
}

OrderClosed message format - STOP LIMIT order

{
  "table": "order", 
  "data": [
      {
          "accountId": "<Your account ID>", 
          "clientOrderId": "1", 
          "orderId": "1000021852415", 
          "price": "21000.0", 
          "quantity": "0.001", 
          "amount": "0.0", 
          "side": "BUY", 
          "status": "CANCELED_BY_USER", 
          "marketCode": "BTC-USD-SWAP-LIN", 
          "timeInForce": "GTC", 
          "timestamp": "1680044038047", 
          "remainQuantity": "0.001", 
          "stopPrice": "20000.0", 
          "limitPrice": "21000.0", 
          "notice": "OrderClosed", 
          "orderType": "LIMIT", 
          "isTriggered": "true", 
          "triggerType": "MARK_PRICE", 
          "displayQuantity": "0.001"
      }
  ]
}


There are multiple scenarios in which an order is closed as described by the status field in the OrderClosed message. In summary orders can be closed by:-

Channel Update Fields

Fields Type Description
table STRING order
data LIST of dictionary
notice STRING OrderClosed
accountId STRING Account identifier
clientOrderId STRING Client assigned ID to help manage and identify orders with max value 9223372036854775807
orderId STRING Unique order ID from the exchange
price STRING Limit price of closed order (only applicable for LIMIT order types)
quantity STRING Original order quantity of closed order
amount STRING "0.0" if not provided in the request
side STRING BUY or SELL
status STRING
  • CANCELED_BY_USER
  • CANCELED_BY_MAKER_ONLY
  • CANCELED_BY_FOK
  • CANCELED_ALL_BY_IOC
  • CANCELED_PARTIAL_BY_IOC
marketCode STRING Market code e.g. BTC-USD-SWAP-LIN
timeInForce STRING Time in force of closed order
timestamp STRING Current millisecond timestamp
remainQuantity STRING Historical remaining order quantity of closed order
stopPrice STRING Stop price of closed stop order (only applicable for STOP order types)
limitPrice STRING Limit price
ordertype STRING LIMIT or STOP_LIMIT
isTriggered STRING False or True
triggerType STRING Stops are triggered on MARK_PRICE
displayQuantity STRING Quantity displayed in the book, primarily used for iceberg orders, otherwise echos the quantity field

OrderClosed Failure

OrderClosed failure message format

{
  "event": "CANCEL", 
  "submitted": False, 
  "message": "Order request was rejected : REJECT_CANCEL_ORDER_ID_NOT_FOUND", 
  "code": "100004", 
  "timestamp": "0", 
  "data": {
            "clientOrderId": 3,
            "orderId": 3330802124194931673, 
            "displayQuantity": 0.0, 
            "lastMatchPrice": 0.0, 
            "lastMatchQuantity": 0.0, 
            "lastMatchedOrderId": 0, 
            "lastMatchedOrderId2": 0, 
            "matchedId": 0, 
            "matchedType": "MAKER", 
            "remainQuantity": 0.0, 
            "side": "BUY", 
            "status": "REJECT_CANCEL_ORDER_ID_NOT_FOUND", 
            "timeCondition": "GTC", 
            "marketCode": "BTC-USD-SWAP-LIN", 
            "timestampEpochMs": 1615377638518, 
            "orderType": "LIMIT",
            "price": 0.0, 
            "quantity": 0.0, 
            "isTriggered": False
          }
}

This order message can occur if:-

Channel Update Fields

Fields Type Description
event STRING
submitted BOOL
message STRING
code STRING
timestamp STRING
data LIST of dictionary
clientOrderId STRING Client assigned ID to help manage and identify orders with max value 9223372036854775807
orderId STRING Unique order ID from the exchange
displayQuantity DECIMAL
lastMatchPrice DECIMAL
lastMatchQuantity DECIMAL
lastMatchedOrderId DECIMAL
lastMatchedOrderId2 DECIMAL
matchedId DECIMAL
matchedType STRING
remainQuantity DECIMAL Historical remaining quantity of the closed order
side STRING
status STRING
timeCondition STRING
marketCode STRING
timestampEpochMs LONG
orderType STRING
price DECIMAL LIMIT or STOP_LIMIT
quantity DECIMAL
isTriggered BOOL False or True

OrderModified

OrderModified message format

{
  "table": "order", 
  "data": [
      {
          "accountId": "<Your account ID>",
          "clientOrderId": "1",
          "orderId": "1000021878849",
          "price": "30.0",
          "quantity": "0.001",
          "amount": "0.0",
          "side": "BUY",
          "status": "OPEN",
          "marketCode": "BTC-USD-SWAP-LIN",
          "timeInForce": "GTC",
          "timestamp": "1680044356374",
          "remainQuantity": "0.001",
          "limitPrice": "30.0",
          "notice": "OrderModified",
          "orderType": "LIMIT",
          "isTriggered": "false",
          "displayQuantity": "0.001"
      }
  ]
}

As described in a previous section Order Commands - Modify Order, the Modify Order command can potentially affect the queue position of the order depending on which parameter of the original order has been modified. Orders retain their orderIds after modification.

Channel Update Fields

Fields Type Description
table STRING order
data LIST of dictionary
notice STRING OrderModified
accountId STRING Account identifier
clientOrderId STRING Client assigned ID to help manage and identify orders with max value 9223372036854775807
orderId STRING Unique order ID from the exchange
price STRING Limit price of modified order (only applicable for LIMIT order types)
quantity STRING Quantity of modified order
remainQuantity STRING Working quantity
amount STRING "0.0" if not provided in the request
side STRING BUY or SELL
status STRING Order status
marketCode STRING Market code e.g. BTC-USD-SWAP-LIN
timeInForce STRING Client submitted time in force, GTC by default
timestamp STRING Current millisecond timestamp
orderType STRING LIMIT or STOP_LIMIT
stopPrice STRING Stop price of modified order (only applicable for STOP order types)
limitPrice STRING Limit price of modified order
isTriggered STRING False or True
triggerType STRING Stops are triggered on MARK_PRICE
displayQuantity STRING Quantity displayed in the book, primarily used for iceberg orders, otherwise echos the quantity field

OrderModified Failure

OrderModified failure message format

{
  "event": "AMEND", 
  "submitted": False, 
  "message": "Order request was rejected : REJECT_AMEND_ORDER_ID_NOT_FOUND", 
  "code": "100004", 
  "timestamp": "0", 
  "data": {
            "clientOrderId": 3, 
            "orderId": 3330802124194931673, 
            "displayQuantity": 917.5, 
            "lastMatchPrice": 0.0, 
            "lastMatchQuantity": 0.0, 
            "lastMatchedOrderId": 0, 
            "lastMatchedOrderId2": 0, 
            "matchedId": 0, 
            "matchedType": "MAKER", 
            "remainQuantity": 0.0, 
            "side": "BUY", 
            "status": "REJECT_AMEND_ORDER_ID_NOT_FOUND", 
            "timeCondition": "GTC", 
            "marketCode": "BTC-USD-SWAP-LIN", 
            "timestampEpochMs": 1615377638518, 
            "orderType": "LIMIT", 
            "price": 22, 
            "quantity": 917.5, 
            "isTriggered": False
          }
}

This order message can occur if an order has already been matched by the time the modify order command is recieved and processed by the exchange which means this order is no longer active and therefore cannot be modified. For more error messages and code, you can see them here Error Codes

Channel Update Fields

Fields Type Description
event STRING
submitted BOOL
message STRING
code STRING
timestamp STRING
data LIST of dictionary
clientOrderId STRING Client assigned ID to help manage and identify orders with max value 9223372036854775807
orderId STRING Unique order ID from the exchange
displayQuantity DECIMAL
lastMatchPrice DECIMAL
lastMatchQuantity DECIMAL
lastMatchedOrderId DECIMAL
lastMatchedOrderId2 DECIMAL
matchedId DECIMAL
matchedType STRING
remainQuantity DECIMAL
side STRING
status STRING
timeCondition STRING
marketCode STRING
timestampEpochMs LONG
orderType STRING
price DECIMAL
quantity DECIMAL
isTriggered BOOL

OrderMatched

OrderMatched message format

{
  "table": "order", 
  "data": [
      {
          "accountId": "<Your account ID>",
          "clientOrderId": "1680044888401",
          "orderId": "1000021937814",
          "price": "27282.73",
          "quantity": "0.004",
          "amount": "0.0",
          "side": "BUY",
          "status": "FILLED", 
          "marketCode": "BTC-USD-SWAP-LIN",
          "timeInForce": "GTC",
          "timestamp": "1680044888565",
          "matchId": "300016799886154670",
          "matchPrice": "27279.39",
          "matchQuantity": "0.001",
          "orderMatchType": "TAKER",
          "remainQuantity": "0.0",
          "limitPrice": "27282.73",
          "notice": "OrderMatched",
          "orderType": "LIMIT",
          "fees": "0.019095573", 
          "feeInstrumentId": "USDT",
          "isTriggered": "false",
          "displayQuantity": "0.004"
      }
  ]
}

Channel Update Fields

Fields Type Required
table STRING order
data LIST of dictionary
notice STRING OrderMatched
accountId STRING Account identifier
clientOrderId STRING Client assigned ID to help manage and identify orders with max value 9223372036854775807
orderId STRING Unique order ID from the exchange
price STRING Limit price submitted (only applicable for LIMIT order types)
stopPrice STRING Stop price submitted (only applicable for STOP order types)
limitPrice STRING Limit price submitted
quantity STRING Order quantity submitted
amount STRING "0.0" if not provided in the request
side STRING BUY or SELL
status STRING FILLED or PARTIAL_FILL
marketCode STRING Market code i.e. BTC-USD-SWAP-LIN
timeInForce STRING Client submitted time in force (only applicable for LIMIT and STOP LIMIT order types)
timestamp STRING Millisecond timestamp of order match
matchID STRING Exchange match ID
matchPrice STRING Match price of order from this match ID
matchQuantity STRING Match quantity of order from this match ID
orderMatchType STRING MAKER or TAKER
remainQuantity STRING Remaining order quantity
orderType STRING
  • LIMIT
  • MARKET
triggered STOP_LIMIT and STOP_MARKET orders are converted to LIMIT and MARKET orders respectively
fees STRING Amount of fees paid from this match ID
feeInstrumentId STRING Instrument ID of fees paid from this match ID
isTriggered STRING False (or True for STOP order types)
triggerType STRING Stops are triggered on MARK_PRICE
displayQuantity STRING Quantity displayed in the book, primarily used for iceberg orders, otherwise echos the quantity field

Subscriptions - Public

All subscriptions to public channels do not require an authenticated websocket connection.

Multiple subscriptions to different channels both public and private can be made within a single subscription command:

{"op": "subscribe", "args": ["<value1>", "<value2>",.....]}

Fixed Size Order Book

Request format

{
  "op": "subscribe",
  "tag": 103,
  "args": ["depthL10:BTC-USD-SWAP-LIN"]
}
import websockets
import asyncio
import json

orderbook_depth = \
{
  "op": "subscribe",
  "tag": 103,
  "args": ["depthL10:BTC-USD-SWAP-LIN"]
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)
            if 'nonce' in data:
                await ws.send(json.dumps(orderbook_depth))
            elif 'success' in data and data['success'] == 'True':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
    "success": true,
    "tag": "103",
    "event": "subscribe",
    "channel": "depthL10:BTC-USD-SWAP-LIN",
    "timestamp": "1665454814275"
}

Order Book depth channel format

{
    "table": "depthL10",
    "data": {
        "seqNum": 2166539633781384,
        "asks": [
            [
                19024.0,
                1.0
            ],
            [
                19205.0,
                4.207
            ],
            [
                19395.0,
                8.414
            ]
        ],
        "bids": [
            [
                18986.0,
                1.0
            ],
            [
                18824.0,
                4.207
            ],
            [
                18634.0,
                8.414
            ]
        ],
        "marketCode": "BTC-USD-SWAP-LIN",
        "timestamp": "1665454814328"
    },
    "action": "partial"
}

Channel Update Frequency: 100ms

This order book depth channel sends a snapshot of the entire order book every 50ms.

Request Parameters

Parameters Type Required Description
op STRING Yes subscribe
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
args LIST Yes List of individual markets <depth>:<marketCode> e.g: [depthL10:BTC-USD-SWAP-LIN], valid book sizes are: depthL5 depthL10 depthL25

Channel Update Fields

Fields Type Description
table STRING depthL10
data DICTIONARY
seqNum INTEGER Sequence number of the order book snapshot
asks LIST of floats Sell side depth;
  1. price
  2. quantity
bids LIST of floats Buy side depth;
  1. price
  2. quantity
marketCode STRING marketCode
timestamp STRING Millisecond timestamp
action STRING

Full Order Book

Request format

{
  "op": "subscribe",
  "tag": 103,
  "args": ["depth:BTC-USD-SWAP-LIN"]
}
import websockets
import asyncio
import json

orderbook_depth = \
{
  "op": "subscribe",
  "tag": 103,
  "args": ["depth:BTC-USD-SWAP-LIN"]
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)
            if 'nonce' in data:
                await ws.send(json.dumps(orderbook_depth))
            elif 'success' in data and data['success'] == 'True':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
    "success": true,
    "tag": "103",
    "event": "subscribe",
    "channel": "depth:BTC-USD-SWAP-LIN",
    "timestamp": "1665454814275"
}

Order book depth channel format

{
    "table": "depth",
    "data": {
        "seqNum": 2166539633781384,
        "asks": [
            [
                19024.0,
                1.0
            ],
            [
                19205.0,
                4.207
            ],
            [
                19395.0,
                8.414
            ]
        ],
        "bids": [
            [
                18986.0,
                1.0
            ],
            [
                18824.0,
                4.207
            ],
            [
                18634.0,
                8.414
            ]
        ],
        "checksum": 3475315026,
        "marketCode": "BTC-USD-SWAP-LIN",
        "timestamp": 1665454814328
    },
    "action": "partial"
}

Channel Update Frequency: 100ms

This order book depth channel sends a snapshot of the entire order book every 100ms.

Request Parameters

Parameters Type Required Description
op STRING Yes subscribe
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
args LIST Yes List of individual markets <depth>:<marketCode> e.g: [depth:BTC-USD-SWAP-LIN]

Channel Update Fields

Fields Type Description
table STRING depth
data DICTIONARY
seqNum INTEGER Sequence number of the order book snapshot
asks LIST of floats Sell side depth;
  1. price
  2. quantity
bids LIST of floats Buy side depth;
  1. price
  2. quantity
checksum INTEGER checksum
marketCode STRING marketCode
timestamp INTEGER Millisecond timestamp
action STRING

Incremental Order Book

Request format

{
    "op": "subscribe",
    "tag": "test1",
    "args": [
        "depthUpdate:BTC-USD-SWAP-LIN"
    ]
}

Success response format

{
    "success": true,
    "tag": "test1",
    "event": "subscribe",
    "channel": "depthUpdate:BTC-USD-SWAP-LIN",
    "timestamp": "1665456142779"
}

** depth update channel format**

{
    "table": "depthUpdate-diff",
    "data": {
        "seqNum": 2166539633794590,
        "asks": [],
        "bids": [],
        "checksum": 364462986,
        "marketCode": "BTC-USD-SWAP-LIN",
        "timestamp": "1665456142843"
    },
    "action": "increment"
}
{
    "table": "depthUpdate",
    "data": {
        "seqNum": 2166539633794591,
        "asks": [
            [
                19042.0,
                1.0
            ]
        ],
        "bids": [
            [
                19003.0,
                1.0
            ]
        ],
        "checksum": 2688268653,
        "marketCode": "BTC-USD-SWAP-LIN",
        "timestamp": "1665456142843"
    },
    "action": "partial"
}

Channel Update Frequency: 100ms

Incremental order book stream

Usage Instructions: 1. Connect to websocket wss://api.ox.fun/v2/websocket 2. Subscribe to depthUpdate and you will get a message reply saying your subscription is successful 3. Afterwards you will get a snapshot of the book with table:depthUpdate 4. If you receive a reply with table:depthUpdate-diff first, keep it locally and wait for snapshot reply in step 3 5. The first incremental depthUpdate-diff message should have the same seqNum as the depthUpdate snapshot 6. After that, each new incremental update should have an incrementally larger seqNum to the previous update 7. The data in each event represents the absolute quantity for a price level. 8. If the quantity is 0, remove the price level.

Request Parameters

Parameters Type Required Description
op STRING Yes subscribe
tag INTEGER or STRING No If given it will be echoed in the reply
args LIST Yes List of individual markets <depthUpdate>:<marketCode> e.g: ["depthUpdate:BTC-USD-SWAP-LIN"]

Channel Update Fields

Fields Type Description
table STRING depthUpdate-diff depthUpdate
data DICTIONARY
seqNum INTEGER Sequence number of the order book snapshot
asks LIST of floats Sell side depth;
  1. price
  2. quantity
bids LIST of floats Buy side depth;
  1. price
  2. quantity
marketCode STRING marketCode
checksum LONG
timestamp STRING Millisecond timestamp
action STRING partial increment

Best Bid/Ask

Request format

{
    "op": "subscribe",
    "tag": "test1",
    "args": [
        "bestBidAsk:BTC-USD-SWAP-LIN"
    ]
}

Success response format

{
    "success": true,
    "tag": "test1",
    "event": "subscribe",
    "channel": "bestBidAsk:BTC-USD-SWAP-LIN",
    "timestamp": "1665456882918"
}

** depth update channel format**

{
    "table": "bestBidAsk",
    "data": {
        "ask": [
            19045.0,
            1.0
        ],
        "checksum": 3790706311,
        "marketCode": "BTC-USD-SWAP-LIN",
        "bid": [
            19015.0,
            1.0
        ],
        "timestamp": "1665456882928"
    }
}

Channel Update Frequency: Real-time

This websocket subscription streams the best bid and ask in real-time. Messages are pushed on every best bid/ask update in real-time

Request Parameters

Parameters Type Required Description
op STRING Yes subscribe
tag INTEGER or STRING No If given it will be echoed in the reply
args LIST Yes List of individual markets <bestBidAsk>:<marketCode> e.g: ["bestBidAsk:BTC-USD-SWAP-LIN"]

Channel Update Fields

Fields Type Description
table STRING bestBidAsk
data DICTIONARY
ask LIST of floats Sell side depth;
  1. price
  2. quantity
bid LIST of floats Buy side depth;
  1. price
  2. quantity
checksum LONG
marketCode STRING marketCode
timestamp STRING Millisecond timestamp

Trade

Request format

{
  "op": "subscribe",
  "tag": 1,
  "args": ["trade:BTC-USD-SWAP-LIN"]
}
import websockets
import asyncio
import json

trade = \
{
  "op": "subscribe",
  "tag": 1,
  "args": ["trade:BTC-USD-SWAP-LIN"]
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)
            if 'nonce' in data:
                await ws.send(json.dumps(trade))
            elif 'success' in data and data['success'] == 'True':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "event": "subscribe", 
  "channel": ["trade:BTC-USD-SWAP-LIN"], 
  "success": True, 
  "tag": "1", 
  "timestamp": "1594299886880"
}

Trade channel format

{
  "table": "trade",
  "data": [ {
              "side": "buy",
              "tradeId": "2778148208082945",
              "price": "5556.91",
              "quantity": "5",
              "matchType": "MAKER",
              "marketCode": "BTC-USD-SWAP-LIN",
              "timestamp": "1594299886890"
            } ]
}

Channel Update Frequency: real-time, with every order matched event

This trade channel sends public trade information whenever an order is matched on the order book.

Request Parameters

Parameters Type Required Description
op STRING Yes subscribe
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
args LIST Yes list of individual markets trade:<marketCode>

Channel Update Fields

Fields Type Description
table STRING trade
data LIST of dictionary
tradeId STRING Transaction Id
price STRING Matched price
quantity STRING Matched quantity
matchType STRING TAKER or MAKER, orders that match via the implied mechanism show as MAKERs in their respective markets
side STRING Matched side
timestamp STRING Matched timestamp
marketCode STRING Market code

Ticker

Request format

{
  "op": "subscribe", 
  "tag": 1,
  "args": ["ticker:all"]
}

OR

{
  "op": "subscribe", 
  "tag": 1,
  "args": ["ticker:OX-USDT", ........]
}
import websockets
import asyncio
import json

ticker = \
{
  "op": "subscribe",
  "tag": 1,
  "args": ["ticker:all"]
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)
            if 'nonce' in data:
                await ws.send(json.dumps(ticker))
            elif 'success' in data and data['success'] == 'True':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "event": "subscribe", 
  "channel": "<args value>",
  "success": True,
  "tag": "1",
  "timestamp": "1594299886890"
}

Channel update format

{
    "table": "ticker",
    "data": [
        {
            "last": "0",
            "open24h": "2.80500000",
            "high24h": "3.39600000",
            "low24h": "2.53300000",
            "volume24h": "0",
            "currencyVolume24h": "0",
            "openInterest": "0",
            "marketCode": "1INCH-USDT",
            "timestamp": "1622020931049",
            "lastQty": "0",
            "markPrice": "3.304",
            "lastMarkPrice": "3.304",
            "indexPrice": "3.304"
        },
        {
            "last": "0",
            "open24h": "2.80600000",
            "high24h": "3.39600000",
            "low24h": "2.53300000",
            "volume24h": "0",
            "currencyVolume24h": "0",
            "openInterest": "0",
            "marketCode": "1INCH-USD-SWAP-LIN",
            "timestamp": "1622020931046",
            "lastQty": "0",
            "markPrice": "3.304",
            "lastMarkPrice": "3.304",
            "indexPrice": "3.304"
        },
        ...
    ]
}

Channel Update Frequency: 500 ms

The ticker channel pushes live price and volume information about the contract.

The websocket will reply with the shown success response format for each ticker channel which has been successfully subscribed to.

The data array in the message from this ticker channel will contain a single JSON corresponding to one ticker subscription.

If you subcribe "ticker:all", you would get one whole message containing all markets.

Request Parameters

Parameters Type Required Description
op STRING Yes subscribe
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
args LIST Yes ticker:all or a list of individual markets ticker:<marketCode>

Channel Update Fields

Fields Type Description
table STRING ticker
data LIST of dictionary
marketCode STRING Market code
last STRING Last traded price
markPrice STRING Mark price
open24h STRING 24 hour rolling opening price
volume24h STRING 24 hour rolling trading volume in OX
currencyVolume24h STRING 24 hour rolling trading volume in Contracts
high24h STRING 24 hour highest price
low24h STRING 24 hour lowest price
openInterest STRING Open interest in Contracts
lastQty STRING Last traded price amount
timestamp STRING Millisecond timestamp
lastMarkPrice STRING Previous mark price reading
indexPrice STRING Index price

Candles

Request format

{
  "op": "subscribe", 
  "tag": 1,
  "args": ["candles60s:BTC-USD-SWAP-LIN"]
}
import websockets
import asyncio
import json

candles = \
{
  "op": "subscribe",
  "tag": 1,
  "args": ["candles60s:BTC-USD-SWAP-LIN"]
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)
            if 'nonce' in data:
                await ws.send(json.dumps(candles))
            elif 'success' in data and data['success'] == 'True':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "event": "subscribe", 
  "channel": ["candles60s:BTC-USD-SWAP-LIN"], 
  "success": True, 
  "tag": "1", 
  "timestamp": "1594313762698"
}

Channel update format

{
  "table": "candle60s",
  "data": [ {
              "marketCode": "BTC-USD-SWAP-LIN",
              "candle": [
                "1594313762698", //timestamp
                "9633.1",        //open
                "9693.9",        //high
                "9238.1",        //low
                "9630.2",        //close
                "45247",         //volume in OX
                "5.3"            //volume in Contracts
              ]
          } ]
}

Channel Update Frequency: 500ms

Granularity: 60s, 180s, 300s, 900s, 1800s, 3600s, 7200s, 14400s, 21600s, 43200s, 86400s

The candles channel pushes candlestick data for the current candle.

Request Parameters

Parameters Type Required Description
op STRING Yes subscribe
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
args LIST Yes list of individual candle granularity and market candles<granularity>:<marketCode>

Channel Update Fields

Fields Type Description
table STRING candles<granularity>
data LIST of dictionary
marketCode STRING Market code
candle LIST of strings
  1. timestamp
  2. open
  3. high
  4. low
  5. close
  6. volume in counter currency
  7. volume in base currency

Liquidation RFQ

Request format

{
  "op": "subscribe", 
  "tag": 1,
  "args": ["liquidationRFQ"]
}
import websockets
import asyncio
import json

liquidation = \
{
  "op": "subscribe", 
  "tag": 1,
  "args": ["liquidationRFQ"]
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)
            if 'nonce' in data:
                await ws.send(json.dumps(liquidation))
            elif 'success' in data and data['success'] == 'True':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "event": "subscribe", 
  "channel": "liquidationRFQ", 
  "success": True, 
  "tag": "1", 
  "timestamp": "1613774604469"
}

Channel update format

{
  "table": "liquidationRFQ",
  "data": [ {
              "marketCode": "BTC-USD-SWAP-LIN"
              "timestamp": "1613774607889"
          } ]
}

Channel Update Frequency: real-time, whenever there is an upcoming liquidation.

The liquidation RFQ (request for quotes) channel publishes a message 500ms before a liquidation event is due to occur. A liquidation event can be classed as one of the following:-

The message will contain the market code and is designed to give liquidity providers and traders an opportunity to make a 2-way market for the upcoming liquidation event.

Request Parameters

Parameters Type Required Description
op STRING Yes subscribe
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
args Single element LIST Yes liquidationRFQ

Channel Update Fields

Fields Type Description
table STRING liquidationRFQ
data LIST of dictionary
marketCode STRING Market code of liquidation
timestamp STRING Millisecond timestamp

Market

Request format

{
  "op": "subscribe", 
  "tag": 1,
  "args": ["market:all"]
}

OR

{
  "op": "subscribe", 
  "tag": 1,
  "args": ["market:OX-USDT", ........]
}
import websockets
import asyncio
import json

market = \
{
  "op": "subscribe",
  "tag": 1,
  "args": ["market:all"]
}

url= 'wss://stgapi.ox.fun/v2/websocket'
async def subscribe():
    async with websockets.connect(url) as ws:
        while True:
            if not ws.open:
                print("websocket disconnected")
                ws = await websockets.connect(url)
            response = await ws.recv()
            data = json.loads(response)
            print(data)
            if 'nonce' in data:
                await ws.send(json.dumps(market))
            elif 'success' in data and data['success'] == 'True':
                continue
asyncio.get_event_loop().run_until_complete(subscribe())

Success response format

{
  "event": "subscribe", 
  "channel": "<args value>",
  "success": True,
  "tag": "1",
  "timestamp": "1594299886890"
}

Channel update format

{
  "table": "market",
  "data": [ 
      {
          "marketId": "3001000000000",
          "marketCode": "OX-USDT",
          "name": "OX/USDT Spot",
          "referencePair": "OX/USDT",
          "base": "OX",
          "counter": "USDT",
          "type": "SPOT",
          "exclusive": "false",
          "tickSize": "0.001",
          "qtyIncrement": "0.1",
          "marginCurrency": "USDT",
          "contractValCurrency": "OX", 
          "upperPriceBound": "0.0495",
          "lowerPriceBound": "0.041",
          "marketPrice": "0.045",
      }, 
      ........
   ]
}

Channel Update Frequency: 1s

The market channel pushes live information about the market such as the current market price and the lower & upper sanity bounds as well as reference data related to the market.

The websocket will reply with the shown success response format for each market which has been successfully subscribed to.

If a subscription has been made to market:all, the data array in the message from this channel will contain a JSON list of all markets. Each JSON will contain information for each market seperately. Otherwise the data array will contain a single JSON corresponding to one market per market channel subscription.

Request Parameters

Parameters Type Required Description
op STRING Yes subscribe
tag INTEGER or STRING No If given it will be echoed in the reply and the max size of tag is 32
args LIST Yes market:all or a list of individual markets market:<marketCode>

Channel Update Fields

Fields Type Description
table STRING market
data LIST of dictionaries
marketPrice STRING Mark price
qtyIncrement STRING Quantity increment
upperPriceBound STRING Upper sanity price bound
lowerPriceBound STRING Lower sanity price bound
counter STRING
type STRING
marketId STRING
referencePair STRING
tickSize STRING Tick size
marketPriceLastUpdated STRING Millisecond timestamp
contractValCurrency STRING
name STRING
marketCode STRING
marginCurrency STRING
base STRING

Other Responses

By subscribing to an authenticated websocket there may be instances when a REST method will also generate a websocket reponse in addition to the REST reply. There are also some GUI commands which will generate a websocket reponse.

Cancel All Open Orders

Success response format

{
  "event": "CANCEL",
  "submitted": True,
  "timestamp": "1612476498953"
}

Documentation for the REST method for cancelling all open orders for an account can be found here Cancel All Orders.

In both these instances a successful action will generate the shown repsonse in an authenticated websocket.

This action can also be executed via the trading GUI using the Cancel All button on the Open Orders blotter for both Spot and Derivative markets.

Error Codes

Failure response format

{
  "event": "<opValue>",
  "message": "<errorMessage>",
  "code": "<errorCode>",
  "success": False
}

Both subscription and order command requests sent via websocket can be rejected and the failure response will return an error code and a corresponding error message explaining the reason for the rejection.

Code Error Message
05001 Your operation authority is invalid
20000 Signature is invalid
20001 Operation failed, please contact system administrator
20002 Unexpected error, please check if your request data complies with the specification.
20003 Unrecognized operation
20005 Already logged in
20006 Quantity must be greater than zero
20007 You are accessing server too rapidly
20008 clientOrderId must be greater than zero if provided
20009 JSON data format is invalid
20010 Either clientOrderId or orderId is required
20011 marketCode is required
20012 side is required
20013 orderType is required
20014 clientOrderId is not long type
20015 marketCode is invalid
20016 side is invalid
20017 orderType is invalid
20018 timeInForce is invalid
20019 orderId is invalid
20020 stopPrice or limitPrice is invalid
20021 price is invalid
20022 price is required for LIMIT order
20023 timestamp is required
20024 timestamp exceeds the threshold
20025 API key is invalid
20026 Token is invalid or expired
20027 The length of the message exceeds the maximum length
20028 price or stopPrice or limitPrice must be greater than zero
20029 stopPrice must be less than limitPrice for Buy Stop Order
20030 limitPrice must be less than stopPrice for Sell Stop Order
20031 The marketCode is closed for trading temporarily
20032 Failed to submit due to timeout in server side
20033 triggerType is invalid
20034 The size of tag must be less than 32
20034 The size of tag must be less than 32
20050 selfTradePreventionMode is invalid
300001 Invalid account status xxx, please contact administration if any questions
300011 Repo market orders are not allowed during the auction window
300012 Repo bids above 0 and offers below 0 are not allowed during the auction window
100005 Open order not found
100006 Open order is not owned by the user
100008 Quantity cannot be less than the quantity increment xxx
100015 recvWindow xxx has expired
200050 The market is not active
710001 System failure, exception thrown -> xxx
710002 The price is lower than the minimum
710003 The price is higher than the maximum
710004 Position quantity exceeds the limit
710005 Insufficient margin
710006 Insufficient balance
710007 Insufficient position
000101 Internal server is unavailable temporary, try again later
000201 Trade service is busy, try again later

REST API V3

TEST SITE

LIVE SITE

OX.FUN offers a powerful RESTful API to empower traders.

RESTful Error Codes

Code Description
429 Rate limit reached
10001 General networking failure
20001 Invalid parameter
30001 Missing parameter
40001 Alert from the server
50001 Unknown server error
20031 The marketCode is closed for trading temporarily

Rate Limits

Each IP is limited to:

Certain endpoints have extra IP restrictions:

Affected APIs:

Rest Api Authentication

Request

{
    "Content-Type": "application/json",
    "AccessKey": "<string>",
    "Timestamp": "<string>",
    "Signature": "<string>",
    "Nonce": "<string>"
}
import requests
import hmac
import base64
import hashlib
import datetime
import json


# rest_url = 'https://api.ox.fun'
# rest_path = 'api.ox.fun'

rest_url = 'https://stgapi.ox.fun'
rest_path = 'stgapi.ox.fun'

api_key = "API-KEY"
api_secret = "API-SECRET"

ts = datetime.datetime.utcnow().isoformat()
nonce = 123
method = "API-METHOD"

# Optional and can be omitted depending on the REST method being called 
body = json.dumps({'key1': 'value1', 'key2': 'value2'})

if body:
    path = method + '?' + body
else:
    path = method

msg_string = '{}\n{}\n{}\n{}\n{}\n{}'.format(ts, nonce, 'GET', rest_path, method, body)
sig = base64.b64encode(hmac.new(api_secret.encode('utf-8'), msg_string.encode('utf-8'), hashlib.sha256).digest()).decode('utf-8')

header = {'Content-Type': 'application/json', 'AccessKey': api_key,
          'Timestamp': ts, 'Signature': sig, 'Nonce': str(nonce)}

resp = requests.get(rest_url + path, headers=header)
# When calling an endpoint that uses body
# resp = requests.post(rest_url + method, data=body, headers=header)
print(resp.json())

Public market data methods do not require authentication, however private methods require a Signature to be sent in the header of the request. These private REST methods use HMAC SHA256 signatures.

The HMAC SHA256 signature is a keyed HMAC SHA256 operation using a client's API Secret as the key and a message string as the value for the HMAC operation.

The message string is constructed as follows:-

msgString = f'{Timestamp}\n{Nonce}\n{Verb}\n{URL}\n{Path}\n{Body}'

Component Required Example Description
Timestamp Yes 2020-04-30T15:20:30 YYYY-MM-DDThh:mm:ss
Nonce Yes 123 User generated
Verb Yes GET Uppercase
Path Yes stgapi.ox.fun
Method Yes /v3/positions Available REST methods
Body No marketCode=BTC-oUSD-SWAP-LIN Optional and dependent on the REST method being called

The constructed message string should look like:-

2020-04-30T15:20:30\n 123\n GET\n stgapi.ox.fun\n /v3/positions\n marketCode=BTC-oUSD-SWAP-LIN

Note the newline characters after each component in the message string. If Body is omitted it's treated as an empty string.

Finally, you must use the HMAC SHA256 operation to get the hash value using the API Secret as the key, and the constructed message string as the value for the HMAC operation. Then encode this hash value with BASE-64. This output becomes the signature for the specified authenticated REST API method.

The signature must then be included in the header of the REST API call like so:

header = {'Content-Type': 'application/json', 'AccessKey': API-KEY, 'Timestamp': TIME-STAMP, 'Signature': SIGNATURE, 'Nonce': NONCE}

Account & Wallet - Private

GET /v3/account

Get account information

Request

GET v3/account?subAcc={subAcc},{subAcc}

Successful response format

{
    "success": true,
    "data": [
        {
            "accountId": "21213",
            "name": "main",
            "accountType": "PORTFOLIO",
            "balances": [
                {
                    "asset": "OX",
                    "total": "100000",
                    "available": "100000",
                    "reserved": "0",
                    "lastUpdatedAt": "1593627415234"
                },
                {
                    "asset": "USDT",
                    "total": "1585.890",
                    "available": "325.890",
                    "reserved": "1260.0",
                    "lastUpdatedAt": "1593627415123"
                }
            ],
            "positions": [
                {
                    "marketCode": "BTC-USD-SWAP-LIN", 
                    "baseAsset": "BTC", 
                    "counterAsset": "USD", 
                    "position": "0.00030", 
                    "entryPrice": "43976.700", 
                    "markPrice": "43788.1", 
                    "positionPnl": "-5.6580", 
                    "estLiquidationPrice": "2.59", 
                    "lastUpdatedAt": "1637876701404",
                }
            ],
            "collateral": "100000.0",
            "notionalPositionSize": "1313.643",
            "portfolioVarMargin": "131.3643",
            "maintenanceMargin": "65.68215",
            "marginRatio": "0.065682",
            "riskRatio": "761.241829",
            "liquidating": false,
            "feeTier": "2",
            "createdAt": "1611665624601"
        }
    ]
}
Request Parameter Type Required Description
subAcc STRING NO Name of sub account. If no subAcc is given, then the response contains only the account linked to the API-Key. Multiple subAccs can be separated with a comma, maximum of 10 subAccs, e.g. subone,subtwo
Response Field Type Description
accountId STRING Account ID
name STRING Account name
accountType STRING Account type LINEAR, STANDARD, PORTFOLIO
balances LIST of dictionaries
asset STRING Asset name
total STRING Total balance
available STRING Available balance
reserved STRING Reserved balance
lastUpdatedAt STRING Last balance update timestamp
positions LIST of dictionaries Positions - only returned if the account has open positions
marketCode STRING Market code
baseAsset STRING Base asset
counterAsset STRING Counter asset
position STRING Position size
entryPrice STRING Entry price
markPrice STRING Mark price
positionPnl STRING Position PNL
estLiquidationPrice STRING Estimated liquidation price
lastUpdatedAt STRING Last position update timestamp
marginBalance STRING [Currently Unavailable] Appears in the position section only for positions using isolated margin. Isolated margin + Unrealized position PnL
maintenanceMargin STRING [Currently Unavailable] Appears in the position section only for positions using isolated margin
marginRatio STRING [Currently Unavailable] Appears in the position section only for positions using isolated margin
leverage STRING [Currently Unavailable] Appears in the position section only for positions using isolated margin
collateral STRING Total collateral balance
notionalPositionSize STRING Notional position size in OX
portfolioVarMargin STRING Initial margin
maintenanceMargin STRING Maintenance margin. The minimum amount of collateral required to avoid liquidation
marginRatio STRING Margin ratio. Orders are rejected/cancelled if the margin ratio reaches 50, and liquidation occurs if the margin ratio reaches 100
riskRatio STRING Ignore.
liquidating BOOL Available values: true and false
feeTier STRING Fee tier
createdAt STRING Timestamp indicating when the account was created

GET /v3/account/names

Get sub account information

Request

GET v3/account/names

Successful response format

{
    "success": true,
    "data": [  
        {
          "accountId": "21213",
          "name": "Test 1"
        }, 
        {
          "accountId": "21214",
          "name": "Test 2"
        }
    ] 
}
Response Field Type Description
accountId STRING Account ID
name STRING Account name

GET /v3/wallet

Get account or sub-account wallet

Request

GET v3/wallet?subAcc={name1},{name2}&type={type}&limit={limit}&startTime={startTime}&endTime={endTime}

Successful response format

{
    "success": true,
    "data": [
        {
             "accountId": "21213",
             "name": "main",
             "walletHistory": [
                  {
                    "id": "810583329159217160",
                    "asset": "USDT",
                    "type": "DEPOSIT", 
                    "amount": "10",
                    "createdAt": "162131535213"  
                  }     
             ]
        }
    ]
}
Request Parameter Type Required Description
subAcc STRING NO Max 5
type STRING NO DEPOSIT, WITHDRAWAL, etc, default return all, most recent first
limit LONG NO Default 200, max 500
startTime LONG NO Millisecond timestamp. Default 24 hours ago. startTime and endTime must be within 7 days of each other. startTime is INCLUSIVE
endTime LONG NO Millisecond timestamp. Default time now. startTime and endTime must be within 7 days of each other. endTime is INCLUSIVE
Response Field Type Description
accountId STRING Account ID
name STRING Account name
walletHistory LIST of dictionaries
id STRING A unique ID
amount STRING Amount
asset STRING Asset name
type STRING
createdAt/lastUpdatedAt STRING Millisecond timestamp created time or updated time

POST /v3/transfer

Sub-account balance transfer

Request

POST /v3/transfer
{
    "asset": "USDT",
    "quantity": "1000",
    "fromAccount": "14320",
    "toAccount": "15343"
}

Successful response format

{
    "success": true,
    "data": {
        "asset": "USDT", 
        "quantity": "1000",
        "fromAccount": "14320",
        "toAccount": "15343",
        "transferredAt": "1635038730480"
    }
}
Request Parameter Type Required Description
asset STRING YES
quantity STRING YES
fromAccount STRING YES
toAccount STRING YES
Response Field Type Description
asset STRING
quantity STRING
fromAccount STRING
toAccount STRING
transferredAt STRING Millisecond timestamp

GET /v3/transfer

Sub-account balance transfer history

Request

GET /v3/transfer?asset={asset}&limit={limit}&startTime={startTime}&endTime={endTime}

Successful response format

{
    "success": true,
    "data": [
        {
            "asset": "USDT", 
            "quantity": "1000",
            "fromAccount": "14320",
            "toAccount": "15343",
            "id": "703557273590071299",
            "status": "COMPLETED",
            "transferredAt": "1634779040611"
        }
    ]
}
Request Parameter Type Required Description
asset STRING NO Default all assets
limit LONG NO Default 50, max 200
startTime LONG NO Millisecond timestamp. Default 24 hours ago. startTime and endTime must be within 7 days of each other. startTime is INCLUSIVE
endTime LONG NO Millisecond timestamp. Default time now. startTime and endTime must be within 7 days of each other. endTime is INCLUSIVE
Response Field Type Description
asset STRING
quantity STRING
fromAccount STRING
toAccount STRING
id STRING
status STRING
transferredAt STRING Millisecond timestamp

GET /v3/balances

Request

GET /v3/balances?subAcc={name1},{name2}&asset={asset}

Successful response format

{
    "success": true,
    "data": [
        {
            "accountId": "21213",
            "name": "main",
            "balances": [
               {
                   "asset": "USDT",
                   "total": "4468.823",              
                   "available": "4468.823",        
                   "reserved": "0",
                   "lastUpdatedAt": "1593627415234"
               },
               {
                   "asset": "OX",
                   "total": "100000.20",              
                   "available": "100000.20",         
                   "reserved": "0",
                   "lastUpdatedAt": "1593627415123"
               }
            ]
        }
    ]
}
Request Parameter Type Required Description
asset STRING NO Default all assets
subAcc STRING NO Name of sub account. If no subAcc is given, then the response contains only the account linked to the API-Key. Multiple subAccs can be separated with a comma, maximum of 10 subAccs, e.g. subone,subtwo
Response Field Type Description
accountId STRING Account ID
name STRING The parent account is named "main" and comes first
balances LIST of dictionaries
asset STRING Asset name
total STRING Total balance (available + reserved)
available STRING Available balance
reserved STRING Reserved balance
lastUpdatedAt STRING Timestamp of updated at

GET /v3/positions

Request

GET /v3/positions?subAcc={name1},{name2}&marketCode={marketCode}

Successful response format

{
  "success": True,
  "data": [
      {
        "accountId": "1234",
        "name": "main",
        "positions": [
            {
              "marketCode": "BTC-USD-SWAP-LIN",
              "baseAsset": "BTC",
              "counterAsset": "USD",
              "position": "-0.00030",
              "entryPrice": "43976.7",
              "markPrice": "43706.3",
              "positionPnl": "8.112",
              "estLiquidationPrice": "23611539.7",
              "lastUpdatedAt": "1673231134601",
            }
        ]
      }
  ]
}

Returns position data

Request Parameter Type Required Description
marketCode STRING NO Default all markets
subAcc STRING NO Name of sub account. If no subAcc is given, then the response contains only the account linked to the API-Key. Multiple subAccs can be separated with a comma, maximum of 10 subAccs, e.g. subone,subtwo
Response Fields Type Description
accountId STRING Account ID
name STRING The parent account is named "main" and comes first
positions LIST of dictionaries
marketCode STRING Contract symbol, e.g. 'BTC-oUSD-SWAP-LIN'
baseAsset STRING
counterAsset STRING
position STRING Position size, e.g. '0.94'
entryPrice STRING Average entry price
markPrice STRING
positionPnl STRING Postion profit and lost
estLiquidationPrice STRING Estimated liquidation price, return 0 if it is negative(<0)
lastUpdated STRING Timestamp when position was last updated
marginBalance STRING [Currently Unavailable] Appears in the position section only for positions using isolated margin. Isolated margin + Unrealized position PnL
maintenanceMargin STRING [Currently Unavailable] Appears in the position section only for positions using isolated margin
marginRatio STRING [Currently Unavailable] Appears in the position section only for positions using isolated margin
leverage STRING [Currently Unavailable] Appears in the position section only for positions using isolated margin

GET /v3/funding

Get funding payments by marketCode and sorted by time in descending order.

Request

GET v3/funding?marketCode={marketCode}&limit={limit}&startTime={startTime}&endTime={endTime}
Request Parameters Type Required Description
marketCode STRING NO e.g. BTC-oUSD-SWAP-LIN
limit LONG NO default is 200, max is 500
startTime LONG NO Millisecond timestamp. Default 24 hours ago. startTime and endTime must be within 7 days of each other. startTime is INCLUSIVE
endTime LONG NO Millisecond timestamp. Default time now. startTime and endTime must be within 7 days of each other. endTime is EXCLUSIVE

SUCCESSFUL RESPONSE

{
    "success": true,
    "data": [
        {
            "id": "810583329213284361",
            "marketCode": "BTC-oUSD-SWAP-LIN",
            "payment": "-122.17530872",
            "fundingRate": "-0.00005",
            "position": "-61.093",
            "indexPrice": "39996.5",
            "createdAt": "1627617632190"
        }
    ]
}
Response Fields Type Description
id STRING A unique ID
marketCode STRING Market code
payment STRING Funding payment
fundingRate STRING Funding rate
position STRING Position
indexPrice STRING index price
createdAt STRING Timestamp of this response

Deposits & Withdrawals - Private

GET /v3/deposit-addresses

Deposit addresses

Request

GET /v3/deposit-addresses?asset={asset}&network={network}

Successful response format

{
    "success": true,
    "data": {
        "address":"0xD25bCD2DBb6114d3BB29CE946a6356B49911358e"
    }
}
Request Parameter Type Required Description
asset STRING YES
network STRING YES
Response Field Type Description
address STRING Deposit address
memo STRING Memo (tag) if applicable

GET /v3/deposit

Deposit history

Request

GET /v3/deposit?asset={asset}&limit={limit}&startTime={startTime}&endTime={endTime}

Successful response format

{
    "success": true,
    "data": [
        {
            "asset": "USDT",
            "network": "ERC20",
            "address": "0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B",
            "quantity": "100.0",
            "id": "651573911056351237",
            "status": "COMPLETED",
            "txId": "0x2b2f01a3cbe5165c883e3b338441182f309ddb8f504b52a2e9e15f17ea9af044",
            "creditedAt": "1617940800000"
        }
    ]
}
Request Parameter Type Required Description
asset STRING NO Default all assets
limit LONG NO Default 50, max 200
startTime LONG NO Millisecond timestamp. Default 24 hours ago. startTime and endTime must be within 7 days of each other. startTime is INCLUSIVE
endTime LONG NO Millisecond timestamp. Default time now. startTime and endTime must be within 7 days of each other. endTime is INCLUSIVE
Response Field Type Description
asset STRING
network STRING
address STRING Deposit address
memo STRING Memo (tag) if applicable
quantity STRING
id STRING
status STRING
txId STRING
creditedAt STRING Millisecond timestamp

GET /v3/withdrawal-addresses

Withdrawal addresses

Request

GET /v3/withdrawal-addresses?asset={asset}&network={network}

Successful response format

{
    "success": true,
    "data": [
        {
            "asset": "USDT",
            "network": "ERC20",
            "address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
            "label": "farming",
            "whitelisted": true
        }
    ]
}

Provides a list of all saved withdrawal addresses along with their respected labels, network, and whitelist status

Request Parameter Type Required Description
asset STRING NO Default all assets
network STRING NO Default all networks
Response Field Type Description
asset STRING
network STRING
address STRING
memo STRING Memo (tag) if applicable
label STRING Withdrawal address label
whitelisted BOOL

GET /v3/withdrawal

Withdrawal history

Request

GET /v3/withdrawal?id={id}&asset={asset}&limit={limit}&startTime={startTime}&endTime={endTime}

Successful response format

{
    "success": true,
    "data": [
        {
            "id": "651573911056351237",
            "asset": "USDT",
            "network": "ERC20",
            "address": "0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B",
            "quantity": "1000.0",
            "fee": "0.000000000",
            "status": "COMPLETED",
            "txId": "0x2b2f01a3cbe5165c883e3b338441182f309ddb8f504b52a2e9e15f17ea9af044",
            "requestedAt": "1617940800000",
            "completedAt": "16003243243242"
        }
    ]
}
Request Parameter Type Required Description
id STRING NO
asset STRING NO Default all assets
limit LONG NO Default 50, max 200
startTime LONG NO Millisecond timestamp. Default 24 hours ago. startTime and endTime must be within 7 days of each other. This filter applies to "requestedAt". startTime is INCLUSIVE
endTime LONG NO Millisecond timestamp. Default time now. startTime and endTime must be within 7 days of each other. This filter applies to "requestedAt". endTime is INCLUSIVE
Response Field Type Description
id STRING
asset STRING
network STRING
address STRING
memo STRING Memo (tag) if applicable
quantity STRING
fee STRING
status STRING COMPLETED, PROCESSING, IN SWEEPING, PENDING, ON HOLD, CANCELED, or FAILED
txId STRING
requestedAt STRING Millisecond timestamp
completedAt STRING Millisecond timestamp

POST /v3/withdrawal

Withdrawal request

Request

POST /v3/withdrawal
{
    "asset": "USDT",
    "network": "ERC20",
    "address": "0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B",
    "quantity": "100",
    "externalFee": true,
    "tfaType": "GOOGLE",
    "code": "743249"
}

Successful response format

{
    "success": true,
    "data": {
        "id": "752907053614432259",
        "asset": "USDT",
        "network": "ERC20",
        "address": "0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B",
        "quantity": "100.0",
        "externalFee": true,
        "fee": "0",
        "status": "PENDING",
        "requestedAt": "1617940800000"
    }
}

Withdrawals may only be initiated by API keys that are linked to the parent account and have withdrawals enabled. If the wrong 2fa code is provided the endpoint will block for 10 seconds.

Request Parameter Type Required Description
asset STRING YES
network STRING YES
address STRING YES
memo STRING NO Memo is required for chains that support memo tags
quantity STRING YES
externalFee BOOL YES If false, then the fee is taken from the quantity, also with the burn fee for asset SOLO
tfaType STRING NO GOOGLE, or AUTHY_SECRET, or YUBIKEY
code STRING NO 2fa code if required by the account
Response Field Type Description
id STRING
asset STRING
network STRING
address STRING
memo STRING
quantity STRING
externalFee BOOL If false, then the fee is taken from the quantity
fee STRING
status STRING
requestedAt STRING Millisecond timestamp

GET /v3/withdrawal-fee

Withdrawal fee estimate

Request

GET /v3/withdrawal-fee?asset={asset}&network={network}&address={address}&memo={memo}&quantity={quantity}&externalFee={externalFee}

Successful response format

{
    "success": true,
    "data": {
        "asset": "USDT",
        "network": "ERC20",
        "address": "0xEf1c6E67703c7BD7107eed8303Fbe6EC2554BF6B",
        "quantity": "1000.0",
        "externalFee": true,
        "estimatedFee": "0.01"
    }
}
Request Parameter Type Required Description
asset STRING YES
network STRING YES
address STRING YES
memo STRING NO Required only for 2 part addresses (tag or memo)
quantity STRING YES
externalFee BOOL NO Default false. If false, then the fee is taken from the quantity
Response Field Type Description
asset STRING
network STRING
address STRING
memo STRING Memo (tag) if applicable
quantity STRING
externalFee BOOL If false, then the fee is taken from the quantity
estimatedFee STRING

Orders - Private

GET /v3/orders/status

Get latest order status

Request

GET /v3/orders/status?orderId={orderId}&clientOrderId={clientOrderId}

Successful response format

{
    "success": true,
    "data": {
        "orderId": "1000387920513",
        "clientOrderId": "1612249737434",
        "marketCode": "OX-USDT",
        "status": "FILLED",
        "side": "BUY",
        "price": "0.0200",
        "isTriggered": false,
        "remainQuantity": "0",
        "totalQuantity": "12",
        "cumulativeMatchedQuantity": "12",
        "avgFillPrice": "0.0200",
        "orderType": "LIMIT",
        "timeInForce": "GTC",
        "source": "11",
        "createdAt": "1655980336520",
        "lastModifiedAt": "1655980393780",
        "lastMatchedAt": "1655980622848"
    }
}
Request Parameter Type Required Description
orderId LONG YES if no clientOrderId Order ID
clientOrderId LONG YES if no orderId Client assigned ID to help manage and identify orders with max value 9223372036854775807
Response Field Type Description
orderId STRING Order ID
clientOrderId STRING Client assigned ID to help manage and identify orders with max value 9223372036854775807
marketCode STRING Market code
status STRING Available values: CANCELED, OPEN, PARTIAL_FILL, FILLED
side STRING Side of the order, BUY or SELL
price STRING Price or limit price in the case of a STOP order
stopPrice STRING Trigger price for a STOP order
amount STRING Amount (only allow amount field when market is spot and direction is BUY)
displayQuantity STRING
triggerType STRING
isTriggered BOOL true for a STOP order
remainQuantity STRING Remaining quantity
totalQuantity STRING Total quantity
cumulativeMatchedQuantity STRING Cumulative quantity of the matches
avgFillPrice STRING Average of filled price
fees LIST of dictionaries Overall fees with instrument ID, if you don't hold enough OX to cover the fee then USDT will be charged instead
orderType STRING Type of the order, availabe values: MARKET, LIMIT, STOP_LIMIT,STOP_MARKET
timeInForce STRING Client submitted time in force.
  • GTC (Good-till-Cancel) - Default
  • IOC (Immediate or Cancel, i.e. Taker-only)
  • FOK (Fill or Kill, for full size)
  • MAKER_ONLY (i.e. Post-only)
  • MAKER_ONLY_REPRICE (Reprices order to the best maker only price if the specified price were to lead to a taker trade)
source STRING Source of the request, available values: 0, 2, 10, 11, 13, 22, 31, 32, 33, 101, 102, 103, 104, 108, 111, 150.

Enumeration: 0: GUI, 2: Borrow, 11: REST, 13: Websocket, 22: Delivery, 31: Physical settlement, 32: Cash settlement, 33: transfer, 101: Automatic borrow, 102: Borrow position liquidation, 103: Position liquidation, 104: Liquidation revert, 108: ADL, 111: Automatic repayment, 150: BLP assignment

createdAt STRING Millisecond timestamp of the order created time
lastModifiedAt STRING Millisecond timestamp of the order last modified time
lastMatchedAt STRING Millisecond timestamp of the order last matched time
canceledAt STRING Millisecond timestamp of the order canceled time

GET /v3/orders/working

Returns all the open orders of the account connected to the API key initiating the request.

Request

GET /v3/orders/working?marketCode={marketCode}&orderId={orderId}&clientOrderId={clientOrderId}

Successful response format

{
  "success": True,
  "data": [
      {
        "orderId": "1000026408953",
        "clientOrderId": "1",
        "marketCode": "BTC-USDT",
        "status": "OPEN",
        "side": "BUY",
        "price": "1000.0",
        "isTriggered": True,
        "quantity": "10.0",
        "remainQuantity": "10.0",
        "matchedQuantity": "0.0",
        "orderType": "LIMIT",
        "timeInForce": "GTC",
        "source": "11",
        "createdAt": "1680113440852",
        "lastModifiedAt": "1680113440875"
      }
  ]
}


Request Parameter Type Required Description
marketCode STRING NO default most recent orders first
orderId LONG NO Client assigned ID to help manage and identify orders
clientOrderId LONG NO Client assigned ID to help manage and identify orders with max value 9223372036854775807
Response Field Type Description
orderId STRING Order ID
clientOrderId STRING Client assigned ID to help manage and identify orders with max value 9223372036854775807
marketCode STRING Market code
status STRING Available values: OPEN, PARTIALLY_FILLED
side STRING Side of the order, BUY or SELL
price STRING Price or limit price in the case of a STOP order
stopPrice STRING Trigger price for a STOP order
isTriggered BOOL Returns true if a STOP order has been triggered
quantity STRING Quantity
remainQuantity STRING Remaining quantity
matchedQuantity STRING Matched Quantity
amount STRING Amount (only allow amount field when market is spot and direction is BUY)
displayQuantity STRING
triggerType STRING
orderType STRING Type of the order, availabe values: MARKET, LIMIT, STOP_LIMIT,STOP_MARKET
timeInForce STRING Client submitted time in force.
  • GTC (Good-till-Cancel) - Default
  • IOC (Immediate or Cancel, i.e. Taker-only)
  • FOK (Fill or Kill, for full size)
  • MAKER_ONLY (i.e. Post-only)
  • MAKER_ONLY_REPRICE (Reprices order to the best maker only price if the specified price were to lead to a taker trade)
source STRING Source of the request, available values: 0, 2, 10, 11, 13, 22, 31, 32, 33, 101, 102, 103, 104, 108, 111, 150.

Enumeration: 0: GUI, 2: Borrow, 11: REST, 13: Websocket, 22: Delivery, 31: Physical settlement, 32: Cash settlement, 33: transfer, 101: Automatic borrow, 102: Borrow position liquidation, 103: Position liquidation, 104: Liquidation revert, 108: ADL, 111: Automatic repayment, 150: BLP assignment

createdAt STRING Millisecond timestamp of the order created time
lastModifiedAt STRING Millisecond timestamp of the order last modified time
lastMatchedAt STRING Millisecond timestamp of the order last matched time

POST /v3/orders/place

Request

POST /v3/orders/place
{
    "recvWindow": 20000, 
    "responseType": "FULL", 
    "timestamp": 1615430912440, 
    "orders": [
         {
             "clientOrderId": 1612249737724, 
             "marketCode": "BTC-USD-SWAP-LIN", 
             "side": "SELL", 
             "quantity": "0.001", 
             "timeInForce": "GTC", 
             "orderType": "LIMIT", 
             "price": "50007"
         }, 
         {
             "clientOrderId": 1612249737724, 
             "marketCode": "BTC-USD-SWAP-LIN", 
             "side": "BUY", 
             "quantity": "0.002", 
             "timeInForce": "GTC", 
             "orderType": "LIMIT", 
             "price": "54900"
         }
    ]
}

Successful response format

{
    "success": true,
    "data": [
       {
            "code": "710006",
            "message": "FAILED balance check as balance (0E-9) < value (0.001)",
            "submitted": false,
            "clientOrderId": "1612249737724",
            "marketCode": "BTC-USD-SWAP-LIN",
            "side": "SELL",
            "price": "52888.0",
            "quantity": "0.001",   
            "orderType": "LIMIT",
            "timeInForce": "GTC",
            "createdAt": "16122497377340",
            "source": "0"
        },
        {
            "notice": "OrderOpened", 
            "accountId": "1076", 
            "orderId": "1000132664173",
            "submitted": true,
            "clientOrderId": "1612249737724",
            "marketCode": "BTC-USD-SWAP-LIN",
            "status": "OPEN",
            "price": "23641.0",
            "stopPrice": null,
            "isTriggered": false,
            "quantity": "0.01",
            "amount": "0.0",
            "remainQuantity": null,
            "matchId": null,
            "matchPrice": null, 
            "matchQuantity": null, 
            "feeInstrumentId": null,
            "fees": null,
            "orderType": "LIMIT", 
            "timeInForce": "GTC", 
            "createdAt": "1629192975532",       
            "lastModifiedAt": null,         
            "lastMatchedAt": null   
        }
    ]
}

Place orders.

Request Parameters Type Required Description
recvWindow LONG NO In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected. If timestamp is provided without recvWindow, then a default recvWindow of 1000ms is used. If recvWindow is provided with no timestamp, then the request will not be rejected. If neither timestamp nor recvWindow are provided, then the request will not be rejected
timestamp STRING YES In milliseconds. If an order reaches the matching engine and the current timestamp exceeds timestamp + recvWindow, then the order will be rejected.
responseType STRING YES FULL or ACK
orders LIST YES
clientOrderId ULONG YES Client assigned ID to help manage and identify orders with max value 9223372036854775807
marketCode STRING YES Market code
side STRING YES BUY or SELL
quantity STRING YES Quantity
amount STRING NO Amount (only allow amount field when market is spot and direction is BUY)
displayQuantity STRING NO displayQuantity (For an iceberg order, pass both quantity and displayQuantity fields in the order request.)
timeInForce STRING NO Default GTC
orderType STRING YES LIMIT or MARKET or STOP_LIMIT or STOP_MARKET
price STRING NO Limit price for the limit order
stopPrice STRING NO Stop price for the stop order
limitPrice STRING NO Limit price for the stop limit order
selfTradePreventionMode STRING No NONE, EXPIRE_MAKER, EXPIRE_TAKER, EXPIRE_BOTH for more info check here Self Trade Prevention Modes
Response Fields Type Description
notice STRING OrderClosed or OrderMatched or OrderOpened
accountId STRING Account ID
code STRING Error code
message STRING Error message
submitted BOOL Denotes whether the order was submitted to the matching engine or not
orderId STRING
clientOrderId STRING Client assigned ID to help manage and identify orders with max value 9223372036854775807
marketCode STRING
status STRING Order status
side STRING SELL or BUY
price STRING
stopPrice STRING
isTriggered BOOL false (can be true for STOP order types)
quantity STRING
amount STRING
displayQuantity STRING
remainQuantity STRING Remaining quantity
matchId STRING
matchPrice STRING
matchQuantity STRING Matched quantity
feeInstrumentId STRING Instrument ID of fees paid from this match ID
fees STRING Amount of fees paid from this match ID
orderType STRING MARKET or LIMIT or STOP_LIMIT or or STOP_MARKET
triggerType STRING
timeInForce STRING
source STRING Source of the request, available values: 0, 2, 10, 11, 13, 22, 101, 102, 103, 104, 111.

Enumeration: 0: GUI, 2: Borrow, 11: REST, 13: Websocket, 22: Delivery, 101: Automatic borrow, 102: Borrow position liquidation, 103: Contract liquidation, 104: Liquidation revert, 111: Automatic repayment

createdAt STRING Millisecond timestamp of the order created time
lastModifiedAt STRING Millisecond timestamp of the order last modified time
lastMatchedAt STRING Millisecond timestamp of the order last matched time

DELETE /v3/orders/cancel

Request

DELETE /v3/orders/cancel
{
    "recvWindow": 200000, 
    "responseType": "FULL", 
    "timestamp": 1615454880374, 
    "orders": [
        {
            "marketCode": "BTC-USD-SWAP-LIN", 
            "orderId": "304384250571714215", 
            "clientOrderId": 1615453494726
        }, 
        {
            "marketCode": "BTC-USD-SWAP-LIN", 
            "clientOrderId": 1612249737724
        }
    ]
}

Successful response format

{
    "success": true,
    "data": [
        {
            "notice": "OrderClosed", 
            "accountId": "12005486", 
            "orderId": "304384250571714215",
            "submitted": true,
            "clientOrderId": "1615453494726", 
            "marketCode": "BTC-USD-SWAP-LIN", 
            "status": "CANCELED_BY_USER", 
            "side": "BUY", 
            "price": "4870.0", 
            "stopPrice": null,
            "isTriggered": false,
            "quantity": "0.001",
            "amount": "0.0",
            "remainQuantity": "0.001",
            "orderType": "LIMIT",  
            "timeInForce": "GTC", 
            "closedAt": "1629712561919"
        },
        {
            "code": "40035",
            "message": "Open order not found with id",
            "submitted": false,
             "orderId": "204285250571714316",
             "clientOrderId": "1612249737724",
             "marketCode": "BTC-oUSD-SWAP-LIN",
             "closedAt": "1615454881433"
         }
    ]
}

Cancel orders.

Request Parameters Type Required Description
recvWindow LONG NO
timestamp LONG YES
responseType STRING YES FULL or ACK
orders LIST YES
marketCode STRING YES
orderId STRING Either one of orderId or clientOrderId is required
clientOrderId ULONG Either one of orderId or clientOrderId is required Client assigned ID to help manage and identify orders with max value 9223372036854775807
Response Fields Type Description
submitted BOOL Denotes if the cancel request was submitted to the matching engine or not
notice STRING OrderClosed
accountId STRING Account ID
code STRING Error code
message STRING Error message
orderId STRING
clientOrderId STRING Client assigned ID to help manage and identify orders with max value 9223372036854775807
marketCode STRING
side STRING SELL or BUY
price STRING
stopPrice STRING
isTriggered BOOL false (can be true for STOP order types)
quantity STRING
amount STRING
displayQuantity STRING
remainQuantity STRING Remaining quantity
orderType STRING MARKET or LIMIT or STOP or STOP_MARKET
triggerType STRING
timeInForce STRING
closedAt STRING Millisecond timestamp of the order close time

DELETE /v3/orders/cancel-all

Request

DELETE  /v3/orders/cancel-all
{
    "marketCode": "BTC-USD-SWAP-LIN"
}

Successful response format

{
    "success": true,
    "data":  
        {
            "notice": "Orders queued for cancelation"
        }
}

Cancel orders.

Request Parameters Type Required Description
marketCode STRING NO
Response Fields Type Description
notice STRING Orders queued for cancelation or No working orders found”

Trades - Private

GET /v3/trades

Returns your most recent trades.

Request

GET /v3/trades?marketCode={marketCode}&limit={limit}&startTime={startTime}&endTime={endTime}

Successful response format

{
    "success": true,
    "data": [
        {
            "orderId": "160067484555913076",
            "clientOrderId": "123",
            "matchId": "160067484555913077",
            "marketCode": "OX-USDT",
            "side": "SELL",
            "matchedQuantity": "0.1",
            "matchPrice": "0.065",
            "total": "0.0065",      
            "orderMatchType": "TAKER",
            "feeAsset": "OX",
            "fee":"0.0196",
            "source": "10",
            "matchedAt": "1595514663626"

       }
    ]
}
Request Parameter Type Required Description
marketCode String default most recent trades first
limit LONG NO max 500, default 200
startTime LONG NO Millisecond timestamp. Default 24 hours ago. startTime and endTime must be within 7 days of each other. startTime is INCLUSIVE
endTime LONG NO Millisecond timestamp. Default time now. startTime and endTime must be within 7 days of each other. endTime is INCLUSIVE
Response Field Type Description
orderId STRING Order ID
clientOrderId STRING Client assigned ID to help manage and identify orders with max value 9223372036854775807
matchId STRING Match ID
marketCode STRING Market code
side STRING Side of the order, BUY or SELL
matchedQuantity STRING Match quantity
matchPrice STRING Match price
total STRING Total price
orderMatchType STRING TAKER,MAKER
feeAsset STRING Instrument ID of the fees
fee STRING Fees
source STRING Source of the request, available values: 0, 2, 10, 11, 13, 22, 101, 102, 103, 104, 111.

Enumeration: 0: GUI, 2: Borrow, 11: REST, 13: Websocket, 22: Delivery, 101: Automatic borrow, 102: Borrow position liquidation, 103: Contract liquidation, 104: Liquidation revert, 111: Automatic repayment

matchedAt STRING Millisecond timestamp of the order matched time

Market Data - Public

GET /v3/markets

Get a list of markets by OX.FUN.

Request

GET /v3/markets?marketCode={marketCode}

Successful response format

{
    "success": true,
    "data": [
        {
            "marketCode": "BTC-USDT",
            "name": "BTC/USDT",
            "referencePair": "BTC/USDT",
            "base": "BTC",
            "counter": "USDT",
            "type": "SPOT",
            "tickSize": "0.1",
            "minSize": "0.001",
            "listedAt": "1593345600000",
            "upperPriceBound": "65950.5",
            "lowerPriceBound": "60877.3",
            "markPrice": "63413.9",
            "lastUpdatedAt": "1635848576163"
        }
    ]
}
Request Parameter Type Required Description
marketCode STRING NO
Response Field Type Description
marketCode STRING Market Code
name STRING Name of the contract
referencePair STRING Reference pair
base STRING Base asset
counter STRING Counter asset
type STRING Type of the contract
tickSize STRING Tick size of the contract
minSize STRING Minimum tradable quantity and quantity increment
listedAt STRING Listing date of the contract
settlementAt STRING Timestamp of settlement if applicable i.e. Quarterlies and Spreads
upperPriceBound STRING Sanity bound
lowerPriceBound STRING Sanity bound
markPrice STRING Mark price
indexPrice STRING index price
lastUpdatedAt STRING

GET /v3/assets

Get a list of assets supported by OX.FUN

Request

GET /v3/assets?asset={asset}

Successful response format

{
    "success": true,
    "data": [
        {
            "asset": "OX",
            "isCollateral": true,
            "loanToValue": "1.000000000",
            "loanToValueFactor": "0",
            "networkList": [
                {
                    "network": "ERC20",
                    "transactionPrecision": "6",
                    "isWithdrawalFeeChargedToUser": true,
                    "canDeposit": true,
                    "canWithdraw": true,
                    "minDeposit": "0.0001",
                    "minWithdrawal": "1"
                }
            ]
        },
        {
            "asset": "LINK",
            "isCollateral": false,
            "networkList": [
                {
                    "network": "ERC20",
                    "tokenId": "0x514910771af9ca656af840dff83e8264ecf986ca",
                    "transactionPrecision": "18",
                    "isWithdrawalFeeChargedToUser": true,
                    "canDeposit": true,
                    "canWithdraw": true,
                    "minDeposit": "0.0001",
                    "minWithdrawal": "0.0001"
                }
            ]
        }
    ]
}
Request Parameter Type Required Description
asset STRING NO Asset name
Response Field Type Description
asset STRING Asset name
isCollateral BOOL Indicates if the asset can be used as collateral to trade perps
loanToValue STRING Ignore
loanToValueFactor STRING Ignore
networkList LIST List of dictionaries
network STRING Network for deposit and withdrawal
tokenId STRING Token ID
transactionPrecision STRING Precision for the transaction
isWithdrawalFeeChargedToUser BOOL Indicates if there is a withdrawal fee
canDeposit BOOL Indicates can deposit or not
canWithdraw BOOL Indicates can withdraw or not
minDeposit STRING Minimum deposit amount
minWithdrawal STRING Minimum withdrawal amount

GET /v3/tickers

Get tickers.

Request

GET /v3/tickers?marketCode={marketCode}

Successful response format

{
    "success": true,
    "data": [
        {
            "marketCode": "BTC-USD-SWAP-LIN",
            "markPrice": "41512.4",
            "open24h": "41915.3",
            "high24h": "42662.2",
            "low24h": "41167.0",
            "volume24h": "22206.50440",
            "currencyVolume24h": "0.004780",
            "openInterest": "0.001300",
            "lastTradedPrice": "41802.5",
            "lastTradedQuantity": "0.001",
            "lastUpdatedAt": "1642585256002"
        }
    ]
}
Request Parameter Type Required Description
marketCode STRING NO Market code
Response Field Type Description
marketCode STRING Market code
markPrice STRING Mark price
open24h STRING Rolling 24 hour opening price
high24h STRING Rolling 24 hour highest price
low24h STRING Rolling 24 hour lowest price
volume24h STRING Rolling 24 hour notional trading volume in OX terms
currencyVolume24h STRING Rolling 24 hour trading volume in Contracts
openInterest STRING Open interest in Contracts
lastTradedPrice STRING Last traded price
lastTradedQuantity STRIN Last traded quantity
lastUpdatedAt STRING Millisecond timestamp of lastest update

GET /v3/funding/estimates

Request

GET /v3/funding/estimates?marketCode={marketCode}

Successful response format

{
    "success": true,
    "data": [
        {
            "marketCode": "ETH-USD-SWAP-LIN",
            "fundingAt": "1667012400000",
            "estFundingRate": "0"
        },
        {
            "marketCode": "BTC-USD-SWAP-LIN",
            "fundingAt": "1667012400000",
            "estFundingRate": "0"
        }
    ]
}
Request Parameter Type Required Description
marketCode STRING NO Market code
Response Field Type Description
marketCode STRING Market code
estFundingRate STRING Estimates funding rate
fundingAt STRING Millisecond timestamp

GET /v3/candles

Get candles.

Request

GET /v3/candles?marketCode={marketCode}&timeframe={timeframe}&limit={limit}
&startTime={startTime}&endTime={endTime}

Successful response format

{
    "success": true, 
    "timeframe": "3600s", 
    "data": [
        {
            "open": "35888.80000000", 
            "high": "35925.30000000", 
            "low": "35717.00000000", 
            "close": "35923.40000000", 
            "volume": "0",
            "currencyVolume": "0",
            "openedAt": "1642932000000"
        },
        {
            "open": "35805.50000000", 
            "high": "36141.50000000", 
            "low": "35784.90000000", 
            "close": "35886.60000000", 
            "volume": "0",
            "currencyVolume": "0",
            "openedAt": "1642928400000"
        }
    ]
}
Request Parameter Type Required Description
marketCode STRING YES Market code
timeframe STRING NO Available values: 60s,300s,900s,1800s,3600s,7200s,14400s,86400s, default is 3600s
limit LONG NO Default 200, max 500
startTime LONG NO Millisecond timestamp. Default 24 hours ago. startTime and endTime must be within 7 days of each other. startTime is INCLUSIVE
endTime LONG NO Millisecond timestamp. Default time now. startTime and endTime must be within 7 days of each other. endTime is INCLUSIVE
Response Field Type Description
timeframe STRING Available values: 60s,300s,900s,1800s,3600s,7200s,14400s,86400s
open STRING Opening price
high STRING Highest price
low STRING Lowest price
close STRING Closing price
volume STRING Trading volume in OX terms
currencyVolume STRING Trading volume in Contract terms
openedAt STRING Millisecond timestamp of the candle open

GET /v3/depth

Get depth.

Request

GET /v3/depth?marketCode={marketCode}&level={level}

Successful response format

{
    "success": true, 
    "level": "5", 
    "data": {
        "marketCode": "BTC-USD-SWAP-LIN", 
        "lastUpdatedAt": "1643016065958", 
        "asks": [
            [
                39400, 
                0.261
            ], 
            [
                41050.5, 
                0.002
            ], 
            [
                41051, 
                0.094
            ], 
            [
                41052.5, 
                0.002
            ], 
            [
                41054.5, 
                0.002
            ]
        ], 
        "bids": [
            [
                39382.5, 
                0.593
            ], 
            [
                39380.5, 
                0.009
            ], 
            [
                39378, 
                0.009
            ], 
            [
                39375.5, 
                0.009
            ], 
            [
                39373, 
                0.009
            ]
        ]
    }
}
Request Parameter Type Required Description
marketCode STRING YES Market code
level LONG NO Default 5, max 100
Response Field Type Description
level LONG Level
marketCode STRING Market code
lastUpdatedAt STRING Millisecond timestamp of the lastest depth update
asks LIST of floats Sell side depth: [price, quantity]
bids LIST of floats Buy side depth: [price, quantity]

GET /v3/markets/operational

Get markets operational.

Request

GET /v3/markets/operational?marketCode={marketCode}

Successful response format

{
    "success": true,
    "data": {
        "marketCode": "BTC-USD-SWAP-LIN",
        "operational": true
    }
}
Request Parameter Type Required Description
marketCode STRING YES Market code
Response Field Type Description
marketCode STRING Market code
operational BOOL whether the market of the marketCode is operational

GET /v3/exchange-trades

Request

GET /v3/exchange-trades?marketCode={marketCode}&limit={limit}&startTime={startTime}&endTime={endTime}

Successful response format

{
    "success": true,
    "data": [
        {
            "marketCode": "BTC-USD-SWAP-LIN",
            "matchPrice": "9600.00000" ,
            "matchQuantity": "0.100000" ,
            "side": "BUY" ,
            "matchType": "TAKER" ,
            "matchedAt": "1662207330439" 
        }
    ]
}
Request Parameter Type Required Description
marketCode STRING NO Market code
limit LONG NO Default 200, max 500
startTime LONG NO Millisecond timestamp. Default 24 hours ago. startTime and endTime must be within 7 days of each other. startTime is INCLUSIVE
endTime LONG NO Millisecond timestamp. Default time now. startTime and endTime must be within 7 days of each other. endTime is INCLUSIVE
Response Field Type Description
marketCode STRING
matchPrice STRING
matchQuantity STRING
side STRING
matchType STRING
matchedAt STRING

GET /v3/funding/rates

Get all historical funding rates

Request

GET /v3/funding/rates?marketCode={marketCode}&limit={limit}&startTime={startTime}&endTime={endTime}

Successful response format

{
    "success": true,
    "data": [
        {
            "marketCode": "BTC-USD-SWAP-LIN",
            "fundingRate": "0.0",
            "createdAt": "1628362803134"
        }
    ]
}
Request Parameter Type Required Description
marketCode STRING NO Market code
limit LONG NO Default 200, max 500
startTime LONG NO Millisecond timestamp. Default 24 hours ago. startTime and endTime must be within 7 days of each other. startTime is INCLUSIVE
endTime LONG NO Millisecond timestamp. Default time now. startTime and endTime must be within 7 days of each other. endTime is INCLUSIVE
Response Field Type Description
marketCode STRING Market code
fundingRate STRING Funding rate
createdAt STRING Millisecond timestamp

GET /v3/leverage/tiers

Get markets leverage tiers

Request

GET  /v3/leverage/tiers?marketCode={marketCode}

Successful response format

{
    "success": true,
    "data": [
        {
            "marketCode": "BTC-USD-SWAP-LIN",
            "tiers": [
                {
                    "tier": 1,
                    "leverage": "10",
                    "positionFloor": "0",
                    "positionCap": "100",
                    "initialMargin": "0.1",
                    "maintenanceMargin": "0.05"
                },
                {
                    "tier": 2,
                    "leverage": "5",
                    "positionFloor": "100",
                    "positionCap": "250",
                    "initialMargin": "0.2",
                    "maintenanceMargin": "0.1"
                },
                {
                    "tier": 3,
                    "leverage": "4",
                    "positionFloor": "250",
                    "positionCap": "1000",
                    "initialMargin": "0.25",
                    "maintenanceMargin": "0.125"
                }
            ]
        }
    ]
}
Request Parameter Type Required Description
marketCode STRING NO Market code
Response Field Type Description
marketCode STRING Market code
tier STRING Leverage tier
leverage STRING Market leverage
positionFloor STRING The lower position limit
positionCap STRING The upper position limit
initialMargin STRING Initial margin
maintenanceMargin STRING Maintenance margin