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

Config file (path)

--listen_address

Listen address (network address)

--cancel_on_disconnect

Instruct gateway to cancel all open orders on (client or bridge) disconnect?

--terminate_on_timeout

Terminate process when detecting order-ack timeout?

--init_missing_md_entry_type_to_zero

Initialize missing MDType (269) to a zero value when not exists?

--ws_heartbeat_freq

Heartbeat frequency (server to client)

--ws_logon_timeout

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

  • logon

1

obj

Dictionary

  • username

  • password

Logon#

#

Name

Type

Comments

0

type

Enum

  • logon

1

obj

Dictionary

  • success

  • reason

  • account

  • sources: name ⇒ { connected, max_order_id, markets }

Request#

#

Name

Type

Comments

0

type

Enum

  • subscribe

  • unsubscribe

  • create_order

  • modify_order

  • cancel_order

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

  • unsubscribe

  • subscribe

  • reference_data

  • market_status

  • top_of_book

  • market_by_price

  • market_by_order

  • statistics

  • funds

  • position

  • order

  • trade

  • create_order

  • modify_order

  • cancel_order

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

  • state

  • reference_data

  • market_status

  • top_of_book

  • market_by_price

  • market_by_order

  • statistics

  • funds

  • position

  • order_ack

  • order

  • trade

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.