Introduction
Open Trade provides Quotes and Limit Order book for SPOT Crypto. This document describes Open Trade WebSocket communication with Open Trade trading services.
Open Trade Taker WEBSOCKET API provides the ability to connect to the venue server, subscribe to market data, place and control orders, receive executions and updates. The API is based on WEBSOCKET specification.
WebSocket Server URLs
Available Channels
- ASSET_LIST
- INSTRUMENT_LIST
- ORDER_BOOK_PUBLIC
- TRADE_PUBLIC
- ACTIVE_SUBSCRIPTIONS
- BALANCE
- TRADE_PRIVATE
Precision & Symbols
Price Precision
The precision level of all trading prices is based on significant figures. All pairs on Bitfinex use up to 7 significant digits and up to 8 decimals (e.g., 1.234567, 123.4567, 1234.567, 0.0001234567). Prices submitted with a precision larger than 7 will be cut by the API.
Amount Precision
The amount field allows up to 8 decimals. Anything exceeding this will be rounded to the 8th decimal.
Symbols
A symbol can be a trading pair or a margin currency.
Connection
Establishing Connection
Connect to the WebSocket endpoint using any standard WebSocket client. Upon connection, you'll receive a confirmation message.
const ws = new WebSocket('wss://opentrade.exchange/exchange/ws');
ws.onopen = () => {
console.log('Connected to Open Trade');
// Send ping every 30 seconds to keep connection alive
setInterval(() => {
ws.send(JSON.stringify({
command: "PING",
channel: "PING"
}));
}, 30000);
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
ws.onclose = () => {
console.log('Connection closed');
};
Subscription
Subscription is required to establish a data channel the server will push updates to. If private channels are required, the valid API key must be provided in every Subscribe request.
{
"requestId": "123",
"command": "SUBSCRIBE",
"signature": "put your token here for private channels",
"channel": "CHANNEL_NAME",
"channelArgs": [
{
"name": "parameter_name",
"value": "parameter_value"
}
]
}
Example: Subscribe to Balance (Private Channel)
{
"command": "SUBSCRIBE",
"signature": "eyJ...",
"channelArgs": [
{
"name": "currency",
"value": "[BTC, USDT]"
}
],
"channel": "BALANCE"
}
{
"command": "SUBSCRIBE",
"event": "SNAPSHOT",
"channel": "BALANCE",
"channelArgs": [
{
"name": "currency",
"value": "[BTC, USDT]"
}
],
"data": [
{
"class": "Balance",
"currencyCode": "USDT",
"balance": 313037.3739066097,
"available": 313037.3739066097,
"locked": 0.0
},
{
"class": "Balance",
"currencyCode": "BTC",
"balance": 3.28944117,
"available": 2.7886285699999998,
"locked": 0.5008126
}
]
}
Rate Limits
To ensure fair usage and system stability, rate limits are enforced on WebSocket connections. Exceeding these limits may result in temporary connection rejection.
Heartbeat / Ping
Ping is a service that users need to upkeep to notify that WebSocket connection is still open.
Maintains the WebSocket connection active. Must be sent at least once every 2 minutes.
{
"command": "PING",
"channel": "PING"
}
{
"event": "PONG",
"channel": "PING"
}
Public Channels
Public channels provide market data and instrument information without requiring authentication. These channels are available to all connected clients.
Provides a list of supported assets and their trading specifications.
{
"command": "GET",
"channel": "ASSET_LIST"
}
{
"event": "GET",
"channel": "ASSET_LIST",
"data": [
{
"class": "Currency",
"code": "USC",
"name": null
},
...
]
}
Provides a list of supported Instruments and their trading specifications. When no symbol is provided, all Instruments are returned, a specific Instrument is provided only selected is returned.
{
"command": "GET",
"channel": "INSTRUMENT_LIST"
}
{
"event": "GET",
"channel": "INSTRUMENT_LIST",
"data": [
{
"class": "Instrument",
"quoteCurrencyCode": "BAT",
"code": "BAT/ETH",
"name": null
},
...
]
}
Provides streaming services an order book for selected symbol, user needs to provide levels of order book to receive. MAX is 20. MIN is 1. Order books are sorted based on NBBO price: BIDs best (Max) first then descending, ASKs best (MIN) first then ascending. The whole Order book is updated every 40ms regardless of changes.
{
"command": "SUBSCRIBE",
"channel": "ORDER_BOOK_PUBLIC",
"channelArgs": [
{
"name": "instrument",
"value": "[USD/ADA,ETH/BTC]"
}
]
}
{
"command": "SUBSCRIBE",
"event": "ACK",
"channel": "ORDER_BOOK_PUBLIC"
}
{
"command": "SUBSCRIBE",
"event": "UPDATE",
"channel": "ORDER_BOOK_PUBLIC",
"data": [
{
"class": "OrderBook",
"exchange": "CROSSTOWER",
"symbol": "BTC/USD",
"bids": [
[19292.21, 0.0124],
[19242.45, 3.0516],
[10000.0, 0.0002]
],
"asks": [
[85100.0, 0.01009],
[19397.85, 21.6067],
[84300.0, 0.00854]
],
"eventTime": 1664651366635
},
...
]
}
Provides streaming services a trading book (public trades) for selected symbol. Once subscribed updates will be pushed to user as they appear at Open Trade.
{
"command": "SUBSCRIBE",
"channel": "TRADE_PUBLIC",
"channelArgs": [
{
"name": "instrument",
"value": "[USD/ADA,ETH/BTC]"
}
]
}
{
"command": "SUBSCRIBE",
"event": "ACK",
"channel": "TRADE_PUBLIC"
}
{
"command": "SUBSCRIBE",
"event": "UPDATE",
"channel": "TRADE_PUBLIC",
"data": [
{
"class": "PublicTrade",
"exchange": "OKCOIN",
"symbol": "BTC/USD",
"price": 19308.6,
"quantity": 0.0103,
"tradeSide": "BUY",
"eventTime": 1664651920827
},
...
]
}
Authentication
To access private channels (Balance, Private Trades, Order Management), you need to obtain an authentication token through a two-step process.
- Login with your API key’s API username and password to obtain a session cookie
- Use the session cookie to obtain a JWT token
- Use the JWT token as the "signature" parameter in private channel requests
Authentication Flow
Before You Start: Create an API Key
Create a key under Account → API Keys: name it, choose its permissions and confirm with your second factor. The platform generates the key's API username, password and secret and shows them once, so store all three straight away.
| Credential | Example | Where it goes |
|---|---|---|
| API username | 100123_mybot | The username field in Step 1, and the {api-username} in the Step 2 URL — the same value both times |
| Password | — | The password field in Step 1 |
| Secret | — | The clientSecret header in Step 2 |
The password expires one year after the key is created, and login is refused from then on — create a new key before that date.
Step 1: Login with the API Username and Password
Using POST method, send the API username and password to the login endpoint. Note that the credentials must be sent as form-data with keys in lowercase. This returns a SESSION cookie (not JSESSIONID).
POST https://auth.opentrade.exchange/login
// Form Data (lowercase keys!)
username: {api-username}
password: {password}
/login. Only the SESSION cookie of a successful login can obtain a token in the next step.
Step 2: Obtain JWT Token
Use the SESSION cookie from Step 1 to obtain your JWT token. This is a POST with the client secret sent as a header, not a query parameter, and an empty body.
POST https://auth.opentrade.exchange/auth/jwt/clients/{api-username}/token
// Headers
Cookie: SESSION={session_cookie_from_step_1}
clientSecret: {secret}
// Body: empty
/auth/jwt/clients/ (plural) — not /client/. And note this is a single /auth/, not the doubled /auth/auth/ some older references show.
Complete Authentication Flow with cURL
curl -s --cookie-jar cookies.txt -X POST \
"https://auth.opentrade.exchange/login" \
-d "username=[API_USERNAME]&password=[PASSWORD]" \
-o /dev/null -w "login redirected to %{redirect_url}\n" \
--next -s -b cookies.txt -X POST \
"https://auth.opentrade.exchange/auth/jwt/clients/[API_USERNAME]/token" \
-H "accept: */*" \
-H "clientSecret: [SECRET]" \
-d ""
Bearer : Bearer eyJ.... Use it as the "signature" parameter in all private channel requests, either exactly as returned or without the prefix — both are accepted. The response also includes a jwt-expire-at header (when the token expires; run both steps again before then) and a jwt-scope header.
Using Your Token
Once you have obtained your JWT token from the authentication process, include it in every private channel request as the "signature" field.
{
"command": "SUBSCRIBE",
"signature": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"channel": "BALANCE",
"channelArgs": [
{
"name": "currency",
"value": "[BTC,USD]"
}
]
}
- Store your token securely and never expose it in client-side code
- Read
jwt-expire-atfrom the token response and run the login + token exchange again before that time, rather than waiting for authentication errors - Use environment variables to manage tokens in different environments