Custom Exchange #
The custom
exchange is an interface that connects the application to a
custom exchange implementation through the HTTP and
WebSocket APIs.
Configuration #
{
"type": "custom",
"url": {
"http": "http://custom-exchange.com/api",
"ws": "ws://custom-exchange.com/api/ws"
},
"key": "cQoRSm21nyovtTcufM8Si2IBHtN",
"secret": "qXN4yR2KXQzs7c0DcXWVEOSNIhTrvG"
}
-
type
- string
The type of the exchange configuration.Exchange configuration types are described in detail here.
-
url
- object
The URL information of the custom exchange.-
http
- string
The URL to which HTTP requests are sent. It must be a valid URL as described here. -
ws
- string (optional; default: unused)
The URL that is used to open a WebSocket connection. It must be a valid URL as described here.If this property is omitted or left empty, a WebSocket connection is not opened, which means only the HTTP API is used for data retrieval.
-
-
key
- string (optional; default: unused)
The authentication key that is sent in theCUSTOM-KEY
header. It may be used to authenticate requests in the custom exchange’s system. -
secret
- string (optional; default: unused)
The secret key that is used to sign requests before sending them to the custom exchange. The generated signature is sent in theCUSTOM-SIGNATURE
header.The signature generation process is described in detail here.
HTTP API #
For the application to function properly, all the required HTTP endpoints
should be implemented as described below. If a resource (e.g., account
information) is not available, the 404
status code should be returned.
The CUSTOM-TIMESTAMP
header is used to send a timestamp of each request’s
creation. The custom exchange may use it to reject a request if it arrives
later than expected.
A timestamp is a number of seconds since the Unix Epoch in UTC.
Errors #
In case of an error, the server should respond with an appropriate HTTP
status code (4XX
or 5XX
) and a body containing the following JSON object:
{
"message": "internal error"
}
message
- string
The message of the error.
Cooldown #
The 429
status code can be sent to force the application to enter
a cooldown mode. A Retry-After
header should be sent in the same
response to indicate how long the application should remain in this state.
The Retry-After
value should be a positive integer representing seconds
as described here.
Signature #
If the secret
configuration option is provided, a request’s signature is
generated and included in the CUSTOM-SIGNATURE
header. It may
be used to verify the integrity and authenticity of the request’s data.
The following steps are taken to generate a signature for each request:
var crypto = require('crypto');
var timestamp = Date.now() / 1000; // this value is sent in the CUSTOM-TIMESTAMP header
var method = 'POST';
var requestPath = '/orders'
var body = JSON.stringify({
price: '1.0'
});
var secret = "<value from the config>"
// the order of the elements here is important
// the body is omitted if it is empty
var message = timestamp + method + requestPath + body;
var hmac = crypto.createHmac('sha256', secret);
var signature = hmac.update(message).digest('base64');
// the signature is sent in the CUSTOM-SIGNATURE header
The signature is generated for all requests, including the initial WebSocket connection request.
Exchange Traits Retrieval #
GET /traits
Retrieves the meta information (also known as the traits) of the exchange.
Query parameters: none
Request body: none
Response body:
{
"http_max_candles": 500,
"ws_trades_instead_of_candles": false,
"candle_ttl": "10s",
"ticker_ttl": "30s",
"position_ttl": "5s",
"account_info_ttl": "5s"
}
-
http_max_candles
- unsigned int
The maximum number of candles that can be returned when making a single HTTP request. -
ws_trades_instead_of_candles
- boolean (optional; default: false)
Specifies whether public trade data instead of candle data should be expected to be sent via an open WebSocket connection. -
candle_ttl
- string (duration)
The amount of time that has to pass since a candle’s retrieval before it is deleted from the cache. When a candle is not found in the cache, an HTTP request is performed to retrieve it. If a non-positive value is returned, the data is kept indefinitely.Both HTTP and WebSocket candle data is cached, so it is best to send as much data via a WebSocket connection as possible.
-
ticker_ttl
- string (duration)
The amount of time that has to pass since a ticker’s retrieval before it is deleted from the cache. When a ticker is not found in the cache, an HTTP request is performed to retrieve it. If a non-positive value is returned, the data is kept indefinitely.Both HTTP and WebSocket ticker data is cached, so it is best to send as much data via a WebSocket connection as possible.
-
position_ttl
- string (duration)
The amount of time that has to pass since a position’s retrieval before it is deleted from the cache. When a position is not found in the cache, an HTTP request is performed to retrieve it. If a non-positive value is returned, the data is kept indefinitely.Both HTTP and WebSocket position data is cached, so it is best to send as much data via a WebSocket connection as possible.
-
account_info_ttl
- string (duration)
The amount of time that has to pass since account information’s retrieval before it is deleted from the cache. When account information is not found in the cache, an HTTP request is performed to retrieve it. If a non-positive value is returned, the data is kept indefinitely.Both HTTP and WebSocket account information is cached, so it is best to send as much data via a WebSocket connection as possible.
Multiple Intervals Retrieval #
GET /intervals
Retrieves all available exchange intervals.
Query parameters: none
Request body: none
Response body:
[
"1hour",
"4hours",
"1day",
"1week"
]
The returned array should contain interval strings. Possible values:
1min
3mins
5mins
15mins
30mins
1hour
2hours
4hours
6hours
8hours
12hours
1day
3days
1week
1month
1year
Multiple Symbols Retrieval #
GET /symbols
Retrieves all available exchange symbols.
Query parameters: none
Request body: none
Response body:
[
"BTC_USDT",
"ETH_USDT",
"LTC_USDT"
]
The returned array should contain symbol strings. The allowed symbol formats are:
BASE_QUOTE
BASE/QUOTE
ASSET
Multiple Candles Retrieval #
GET /candles
Retrieves multiple exchange candles.
Query parameters:
-
symbol
- string
The symbol of candles. -
interval
- string
The interval of candles. -
limit
- unsigned int
The number of candles. -
end-time
- string (RFC 3339; optional; default: current time)
If specified, only candles whose timestamps are not greater than this value should be returned.
Request body: none
Response body:
[
{
"opened_at": "2020-09-10T21:00:20Z",
"open": "2",
"high": "3",
"low": "4",
"close": "5",
"volume": "6"
},
{
"opened_at": "2020-09-10T21:05:20Z",
"open": "3",
"high": "4",
"low": "5",
"close": "6",
"volume": "7"
},
{
"opened_at": "2020-09-10T21:10:20Z",
"open": "4",
"high": "5",
"low": "6",
"close": "7",
"volume": "8"
}
]
The candles should be ordered in ascending order, which means that the latest candle should always be the last one.
The returned array should contain candle objects belonging to a specific symbol and interval. Candle object properties are:
-
opened_at
- string (RFC 3339)
The exact time when the candle was opened. It may be used to uniquely identify the candle. -
open
- string (decimal)
The open price of the candle. -
high
- string (decimal)
The high price of the candle. -
low
- string (decimal)
The low price of the candle. -
close
- string (decimal)
The close price of the candle. -
volume
- string (decimal)
The transaction volume of the candle.
Multiple Tickers Retrieval #
GET /tickers
Retrieves multiple exchange tickers.
Query parameters:
symbols
- array of strings
The array of ticker symbols.
Request body: none
Response body:
{
"BTC_USDT": {
"last": "31211.45",
"ask": "32211.45",
"bid": "30211.45",
"change": "1201.12",
"percent_change": "7",
"volume": "31234512.1"
},
"ETH_USDT": {
"last": "3111.45",
"ask": "3221.45",
"bid": "3021.45",
"change": "1201.12",
"percent_change": "12",
"volume": "3124512.1"
}
}
The returned object is a map that should contain symbols along with their ticker data. Ticker object properties are:
-
last
- string (decimal)
The last (most recent) price of the asset. -
ask
- string (decimal)
The ask price of the asset. -
bid
- string (decimal)
The bid price of the asset. -
change
- string (decimal)
The 24-hour price change. -
percent_change
- string (decimal)
The 24-hour price change percentage. -
volume
- string (decimal)
The 24-hour transaction volume.
Account Information Retrieval #
GET /account
Retrieves exchange account information.
Query parameters: none
Request body: none
Response body:
{
"cash": {
"asset": "USD",
"quantity": "31.0"
}
}
cash
- object
The information about cash resources.-
asset
- string
The asset that is being used as cash. If the cash resources are not being used, the asset should be returned as an empty string. -
quantity
- string (decimal)
The available quantity of the cash asset.
-
Exchange Positions Retrieval #
GET /positions
Retrieves all exchange positions.
Query parameters: none
Request body: none
Response body:
{
"BTC": {
"quantity": "0.31"
},
"ETH": {
"quantity": "1203.12"
}
}
The returned object is a map that should contain assets along with their position data. Position object properties are:
quantity
- string (decimal)
The number of asset units that are owned.
Multiple Orders Retrieval #
GET /orders
Retrieves multiple orders.
Query parameters:
-
symbol
- string
The symbol of orders. -
side
- string (enum; optional; default: both)
The side of orders. Possible values:buy
sell
-
status
- string (enum)
The status of orders. Possible values:open
Request body: none
Response body:
[
{
"id": "9bsv0s78ajk0036f3m60",
"side": "buy",
"price": "38012.123",
"total_quantity": "0.0541",
"filled_quantity": "0.0241"
},
{
"id": "1bsv0s78ajk0036f3m60",
"side": "buy",
"price": "18012.123",
"total_quantity": "1.0541",
"filled_quantity": "1.0241"
}
]
The returned array should contain order objects belonging to a specific symbol. Order object properties are:
-
id
- string
The ID of the order that is used by the exchange. -
side
- string (enum)
The side of the order. Possible values:buy
sell
-
price
- string (decimal)
The price of the asset. -
total_quantity
- string (decimal)
The total quantity of the asset. -
filled_quantity
- string (decimal)
The quantity of the asset that is already filled/traded.
Order Placement #
POST /orders
Places a new order.
Query parameters: none
Request body:
{
"symbol": "BTC_USDT",
"side": "buy",
"price": "39131.012",
"quantity": "0.134"
}
-
symbol
- string
The symbol of the order. -
side
- string (enum)
The side of the order. Possible values:buy
sell
-
price
- string (decimal)
The price of the asset. -
quantity
- string (decimal)
The quantity of the asset.
Response body:
{
"id": "9bsv0s78ajk0036f3m60",
"price": "38012.123",
"total_quantity": "0.0541",
"filled_quantity": "0.0241"
}
-
id
- string
The ID of the order that is used by the exchange. -
price
- string (decimal)
The price of the asset. -
total_quantity
- string (decimal)
The total quantity of the asset. -
filled_quantity
- string (decimal)
The quantity of the asset that is already filled/traded.
Order Cancellation #
DELETE /orders
Cancels open orders.
Query parameters:
-
symbol
- string
The symbol of an order. -
ids
- array of strings
The array of order IDs.
Request body: none
Response body: none
WebSocket API #
For the application to function properly, all the required WebSocket topics should be handled as described below. If a resource (e.g., account information) is not available, it should not be published.
Event Payloads and Subscriptions #
All events should be sent in the following format:
{
"topic": "[email protected]",
"payload": {
// <event payload>
}
}
To receive events of a certain topic, a subscription WebSocket request with a request ID of an unsigned int type is sent:
{
"topic": "[email protected]",
"id": 1
}
Likewise, to unsubscribe from a certain topic, an unsubscription WebSocket request is sent:
{
"topic": "[email protected]",
"id": 1
}
On successful subscription or unsubscription, a response with the same request ID should be given:
{
"success": true,
"id": 1
}
Duplicate subscriptions and unsubscriptions targeting nonexistent subscriptions should be permitted without producing any results afterwards.
Candle Update #
update@candles.{symbol}.{interval}
Sends a notification when a specific symbol and interval’s candle data is updated.
Payload:
{
"opened_at": "2020-09-10T21:01:20Z",
"open": "2",
"high": "3",
"low": "4",
"close": "5",
"volume": "6"
}
-
opened_at
- string (RFC 3339)
The exact time when the candle was opened. It may be used to uniquely identify the candle. -
open
- string (decimal)
The open price of the candle. -
high
- string (decimal)
The high price of the candle. -
low
- string (decimal)
The low price of the candle. -
close
- string (decimal)
The close price of the candle. -
volume
- string (decimal)
The transaction volume of the candle.
Public Trade Update #
update@trades.{symbol}
Sends a notification when a specific symbol’s public trade data is updated.
Payload:
{
"timestamp": "2020-09-10T21:01:20Z",
"side": "buy",
"price": "40.1",
"quantity": "3.45"
}
-
timestamp
- string (RFC 3339)
The exact time when the trade was made. -
side
- string (enum)
The side of the trade. Possible values:buy
sell
-
price
- string (decimal)
The price of the asset. -
quantity
- string (decimal)
The quantity of the asset.
Ticker Update #
update@tickers.{symbol}
Sends a notification when a specific symbol’s ticker data is updated.
Ifsymbol
is not provided (update@tickers
) when a subscription is being made, tickers of all symbols should be published. However, when publishing, the topic should still include the symbol (update@tickers.{symbol}
).
Payload:
{
"last": "31211.45",
"ask": "32211.45",
"bid": "30211.45",
"change": "1201.12",
"percent_change": "7",
"volume": "31234512.1"
}
-
last
- string (decimal)
The last (most recent) price of the asset. -
ask
- string (decimal)
The ask price of the asset. -
bid
- string (decimal)
The bid price of the asset. -
change
- string (decimal)
The 24-hour price change. -
percent_change
- string (decimal)
The 24-hour price change percentage. -
volume
- string (decimal)
The 24-hour transaction volume.
Account Information Update #
update@account
Sends a notification when account information is updated.
Payload:
{
"cash": {
"asset": "USD",
"quantity": "31.0"
}
}
cash
- object
The information about cash resources.-
asset
- string
The asset that is being used as cash. If the cash resources are not being used, the asset should be returned as an empty string. -
quantity
- string (decimal)
The available quantity of the cash asset.
-
Positions Update #
update@positions
Sends a notification when account positions are updated.
Payload:
{
"BTC": {
"quantity": "0.31"
},
"ETH": {
"quantity": "1203.12"
}
}
quantity
- string (decimal)
The number of asset units that are owned.
Order Update #
update@orders.{status}.{symbol}
Sends a notification when a specific symbol’s order is updated. Possible status values:
open
cancelled
partially_filled
filled
Ifstatus
andsymbol
are not provided (update@orders
) when a subscription is being made, orders of all statuses and symbols should be published. However, when publishing, the topic should still include the status and symbol (update@orders.{status}.{symbol}
).
Payload:
{
"id": "9bsv0s78ajk0036f3m60",
"side": "buy",
"price": "38012.123",
"total_quantity": "0.0541",
"filled_quantity": "0.0241"
}
-
id
- string
The ID of the order that is used by the exchange. -
side
- string (enum)
The side of the order. Possible values:buy
sell
-
price
- string (decimal)
The price of the asset. -
total_quantity
- string (decimal)
The total quantity of the asset. -
filled_quantity
- string (decimal)
The quantity of the asset that is already filled/traded.