roq-json-bridge#
Purpose#
Gateway bridge using a JSON based message protocol
Integration with third-party vendors
Description#
The JSON bridge allows clients to connect with gateways receive market data, and manage orders.
Conda#
$ mamba install \
--channel https://roq-trading.com/conda/stable \
roq-json-bridge
$ cp $CONDA_PREFIX/share/roq-json-bridge/config.toml $CONFIG_FILE_PATH
# Then modify $CONFIG_FILE_PATH to match your specific configuration
$ roq-json-bridge \
--name "json-bridge" \
--config_file "$CONFIG_FILE_PATH" \
--service_listen_address "$TCP_LISTEN_PORT_FOR_METRICS" \
--listen_address "$TCP_LISTEN_PORT_FOR_WS_CLIENTS" \
--flagfile "$FLAG_FILE" \
[<gateway unix domain sockets>]
Config#
A list of exchanges and corresponding symbols.
[symbols]
[symbols.bitmex]
regex=["XBTUSD", "XBT.*"]
[symbols.deribit]
regex=".*"
A list of JSON clients allowed to connect to the JSON bridge.
[users]
[users.MD1]
username="tbmd1"
Flags#
$ roq-json-bridge --help
|
Config file (path) |
|
Listen address (network address) |
|
Instruct gateway to cancel all open orders on (client or bridge) disconnect? |
|
Terminate process when detecting order-ack timeout? |
|
Initialize missing MDType (269) to a zero value when not exists? |
|
Heartbeat frequency (server to client) |
|
Logon timeout. Must be less than or equal to heartbeat frequency. Heartbeat frequency is chosen if this flag is unspecified |
Protocol#
Features
JSON
Optimised for single-pass decoding
Connection state pushed to client (to avoid synchronisation issues)
Request / response
Response (logon, subscribe, …) may include cached object
Multiple response messages may be sent for cached objects (funds, position, order, trade)
Publish / subscribe
Client must discover markets (exchange-symbol) before subscribing
Objects are keyed by source and the exchange-symbol pair
Logon#
# |
Name |
Type |
Comments |
---|---|---|---|
0 |
type |
Enum |
|
1 |
obj |
Dictionary |
|
Logon#
# |
Name |
Type |
Comments |
---|---|---|---|
0 |
type |
Enum |
|
1 |
obj |
Dictionary |
|
Request#
# |
Name |
Type |
Comments |
---|---|---|---|
0 |
type |
Enum |
|
1 |
obj |
Dictionary |
Request object (depends on type) |
2 |
source |
String |
Source name (used for routing) |
3 |
id |
Integer |
Optional opaque value to be mirrored to response |
Response#
# |
Name |
Type |
Comments |
---|---|---|---|
0 |
type |
Enum |
|
1 |
obj |
Dictionary |
Update object (depends on type) |
2 |
source |
String |
Source name |
3 |
timestamp |
DateTime |
Timestamp (microseconds) when the update was received |
4 |
id |
Integer |
The opaque value used for the request |
Update#
# |
Name |
Type |
Comments |
---|---|---|---|
0 |
type |
Enum |
|
1 |
obj |
Dictionary |
Update object (depends on type) |
2 |
source |
String |
Source name |
3 |
timestamp |
DateTime |
Timestamp (microseconds) when the update was received |
Logon#
Request
[
"logon",
{
"username": "test",
"password": "secret"
}
]
Response (failure)
[
"logon",
{
"success": false,
"reason": "something_was_wrong"
}
]
Response (success, no sources)
[
"logon",
{
"success": true,
"account": "A1",
"sources": {}
}
]
Response (success, sources)
[
"logon",
{
"success": true,
"account": "A1",
"sources": {
"deribit": {
"connected": true,
"max_order_id": 1000,
"markets": [
["", "USDT"],
["deribit", "BTC-PERPETUAL"]
]
}
}
}
]
State#
Connectivity
[
"state",
{
"connected": true
}
"deribit"
]
Order ID
[
"state",
{
"max_order_id": 1000
}
"deribit"
]
Markets
[
"state",
{
"markets": [
["deribit", "BTC-PERPETUAL"]
]
},
"deribit"
]
Update#
ReferenceData
[
"reference_data",
{
"exchange": "deribit",
"symbol": "BTC-PERPETUAL",
"description": "future",
"security_type": "FUTURES",
"base_currency": "USD",
"quote_currency": "USD",
"commission_currency": "BTC",
"tick_size": 0.5,
"multiplier": 10,
"min_trade_vol": 1,
"max_trade_vol": null,
"trade_vol_step_size": 1,
"option_type": null,
"strike_currency": null,
"strike_price": null,
"underlying": null,
"time_zone": null,
"issue_date": 1534118400000000,
"settlement_date": null,
"expiry_datetime": 32503708800000000,
"expiry_datetime_utc": 32503708800000000
},
"deribit",
1637746789945131
]
TopOfBook
[
"top_of_book",
{
"exchange": "deribit",
"symbol": "BTC-PERPETUAL",
"layer": [56872, 17, 56878, 4590]
},
"deribit",
1637746789945131
]
MarketByPrice
[
"market_by_price",
{
"exchange": "deribit",
"symbol": "BTC-PERPETUAL",
"bids": [[56834, 5]],
"asks": [],
"exchange_time_utc": 1637746790018000
},
"deribit",
1637746790034639
]
Statistics
[
"statistics",
{
"exchange": "deribit",
"symbol": "BTC-PERPETUAL",
"statistics": {
"PRE_OPEN_INTEREST": {
"value": 392206482,
"begin_time_utc": null,
"end_time_utc": null
},
"PRE_SETTLEMENT_PRICE": {
"value": 56860.75,
"begin_time_utc": null,
"end_time_utc": null
}
},
"exchange_time_utc": null
},
"deribit",
1637746790034639
]
CreateOrder#
Request
[
"create_order",
{
"order_id": 1001,
"exchange": "deribit",
"symbol": "BTC-PERPETUAL",
"side": "BUY",
"position_effect": "",
"max_show_quantity": null,
"order_type": "LIMIT",
"time_in_force": "GTC",
"execution_instruction": "",
"order_template": "",
"quantity": 1.0,
"price": 57348.5,
"stop_price": null,
"routing_id": ""
},
"deribit",
123
]
Response (failure)
[
"create_order",
{
"success": false,
"reasons": "NOT_CONNECTED"
},
"deribit",
null,
123
]
Response (success)
[
"create_order",
{
"success": true
},
"deribit",
null,
123
]
ModifyOrder#
Request
[
"modify_order",
{
"order_id": 1001,
"quantity": 1.0,
"price": 57348.5,
"routing_id": "",
"version": 2,
"conditional_on_version": 1
},
"deribit",
123
]
Response (failure)
[
"modify_order",
{
"success": false,
"reasons": "NOT_CONNECTED"
},
"deribit",
null,
123
]
Response (success)
[
"modify_order",
{
"success": true
},
"deribit",
null,
123
]
CancelOrder#
Request
[
"cancel_order",
{
"order_id": 1001,
"routing_id": "",
"version": 3,
"conditional_on_version": 2
},
"deribit",
123
]
Response (failure)
[
"cancel_order",
{
"success": false,
"reasons": "NOT_CONNECTED"
},
"deribit",
null,
123
]
Response (success)
[
"cancel_order",
{
"success": true
},
"deribit",
null,
123
]
Constraints#
The bridge must register as a regular user to the gateways. The implication is that the bridge only has visibility to that user, i.e. there is no option to see the orders managed by other users. Thus, there is currently no option to implement a true drop-copy solution.
The bridge requires each downstream client to be mapped 1:1 with an account. This is currently the only option to route order acks, order updates and trades back to the correct client.
Note
The C++ API fully supports account multiplexing.
The subscription model is designed around {exchange, symbol} pairs by source. The only exception is order acks which will always be communicated to the client.
Note
The client should allow for the loss of order acks and implement logic to also subscribe and monitor order updates.
The bridge will respond to subscription requests with last updated object state or null, if no such object exists. The bridge may also respond with multiple messages for channels where further keys are used, e.g. order_id’s. The client must support all these scenarios.
Clients are not allowed to create subscriptions prior to discovering new markets. This is done to avoid clients inadverently creating state that can be expensive in terms of memory consumption.
The bridge will push state to the clients. In particular, state is by source and includes
Connection state
Last known
max_order_id
Markets (exchange-symbol pairs)
Note
The push model is designed to avoid a situation where possibly complex request/response logic has to be implemented by the client. The risk is that errors are made.