roq-fix-bridge#
Links#
Purpose#
Gateway bridge using a FIX 4.4 based message protocol
Integration with third-party vendors
Description#
The FIX bridge allows FIX clients to connect with gateways, receive market data, and manage orders.
The FIX bridge configuration defines a list of exchanges and corresponding
symbols (regex).
This implementation use exchange
/symbol
for all message
routing!
Note
Some FIX clients may rely on TradingSessionID
for state management.
For many Crypto exchanges, this is not a well-defined concept.
The implementation choice is to use the source_name
of the gateway
(as specified using the gateway --name
command-line flag) to mean
TradingSessionID
.
The concept of TradingSessionID
is otherwise not used directly by
the FIX bridge for any message routing.
The FIX bridge will automatically manage gateway connectivity.
Upon connecting to a gateway, the FIX bridge will discover the universe of
exchange
/symbol
as well as order management capabilities, e.g.
whether order rewrite is supported by a gateway.
Furthermore, a “ready”-state is being maintained based on gateway notifications
around account availability.
Note
FIX clients should expect and manager order action rejects if the
exchange
/symbol
has not yet been discovered and/or the
account is not yet in the ready state (e.g. due to disconnect).
Order management has been implemented to conform to FIX Order State Changes.
This effectively means that ClOrdId
/OrigClOrdId
chaining is
supported when modifying and canceling orders.
When possible, gateways will try to pass enough information to the exchange
such that later order download will be able to notify the FIX bridge with the
correct ClOrdId
/OrigClOrdId
information.
This is however only best effort and the feature should not be relied upon!
Note
The FIX bridge and the gateways are all designed for low latency and there is
no database persistence layer causing latency anywhere in the path of order
routing.
Furthermore, not all exchanges support rewriting client order identifiers.
The FIX bridge and the gateways maintain in-memory maps between exchange
order identifiers and outstanding requests on the one side, and
ClOrdId
/OrigClOrdId
on the other side.
Correct updates around ClOrdId
/OrigClOrdId
chaining should
only be expected if both FIX bridge and gateway remain connected.
Reconnection may work, but it’s best effort, only.
To manage uncertainties around disconnects, the FIX bridge may be configured such that gateways are instructed to auto-cancel orders upon disconnect. Similarly, when possible, gateways may be configured such that exchanges are instructed to auto-cancel orders upon disconnect.
Conda#
$ mamba install \
--channel https://roq-trading.com/conda/stable \
roq-fix-bridge
$ cp $CONDA_PREFIX/share/roq-fix-bridge/config.toml $CONFIG_FILE_PATH
# Then modify $CONFIG_FILE_PATH to match your specific configuration
$ roq-fix-bridge \
--name "fix-bridge" \
--config_file "$CONFIG_FILE_PATH" \
--service_listen_address "$TCP_LISTEN_PORT_FOR_METRICS" \
--client_listen_address "$TCP_LISTEN_PORT_FOR_FIX_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 FIX clients allowed to connect to the FIX bridge.
[users]
[users.MD1]
component="roq-fix-client-test"
username="tbmd1"
An optional list of mapping overrides. This may be necessary because FIX 4.4 has a limited set of market data types (MDEntryType).
[statistics]
[statistics.FUNDING_RATE]
fix_md_entry_type="SETTLEMENT_PRICE"
Flags#
$ roq-fix-bridge --help
|
Config file (path) |
|
Listen address (network address) |
|
Instruct gateway to cancel all open orders on (client or bridge) disconnect? |
|
FIX version, e.g. 4.4 |
|
Component identifier |
|
Logon timeout. Must be less than or equal to heartbeat frequency. Heartbeat frequency is chosen if this flag is unspecified |
|
Heartbeat frequency (server to client) |
|
Debug FIX msesages? |
|
Filename for logging FIX messages |
|
Can be used to initialize non-existing statistics types to zero. |
|
Use quote currency for execution report? |
|
Use exchange time for sending time (tag 52)? |
|
Use market by price as the source for top of book? |
Session#
Client → Server#
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
98 |
EncryptMethod |
|
✓ |
108 |
HeartBtInt |
Heartbeat interval in seconds |
141 |
ResetSeqNumFlag |
Reset sequence numbers? |
|
789 |
NextExpectedMsgSeqNum |
||
553 |
Username |
||
554 |
Password |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
58 |
Text |
Informative text |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
112 |
TestReqId |
Pass-through value (will be returned with Heartbeat) |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
112 |
TestReqId |
Pass-through value (copied from TestRequest) |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
45 |
RefSeqNum |
Sequence number causing the reject |
✓ |
58 |
Text |
Informative text |
✓ |
371 |
RefTagID |
Tag ID causing the reject |
✓ |
372 |
RefMsgType |
Message type causing the reject |
373 |
SessionRejectReason |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
7 |
BeginSeqNo |
First sequence number to replay |
✓ |
16 |
EndSeqNo |
Last sequence number to replay |
Server → Client#
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
108 |
HeartBtInt |
Heartbeat interval (seconds) |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
58 |
Text |
Informative text |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
112 |
TestReqId |
Pass-through value (will be returned with Heartbeat) |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
112 |
TestReqId |
Some pass-through value (copied from TestRequest) |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
45 |
RefSeqNum |
Sequence number causing the reject |
✓ |
58 |
Text |
Informative text |
✓ |
371 |
RefTagID |
Tag ID causing the reject |
✓ |
372 |
RefMsgType |
Message type causing the reject |
373 |
SessionRejectReason |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
7 |
BeginSeqNo |
First sequence number to replay |
✓ |
16 |
EndSeqNo |
Last sequence number to replay |
Market Data#
Client → Server#
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
335 |
TradSesReqID |
Request ID |
336 |
TradingSessionID |
Specific name |
|
✓ |
263 |
SubscriptionRequestType |
Snapshot, subscribe, unsubscribe |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
320 |
SecurityReqId |
Request ID |
✓ |
559 |
SecurityListRequestType |
Request type |
✓ |
55 |
Symbol |
|
✓ |
207 |
SecurityExchange |
|
336 |
TradingSessionID |
||
✓ |
263 |
SubscriptionRequestType |
Snapshot, subscribe, unsubscribe |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
320 |
SecurityReqId |
Request ID |
✓ |
321 |
SecurityRequestType |
Request type |
✓ |
55 |
Symbol |
|
✓ |
207 |
SecurityExchange |
|
336 |
TradingSessionID |
||
✓ |
263 |
SubscriptionRequestType |
Snapshot, subscribe, unsubscribe |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
324 |
SecurityStatusReqId |
Request ID |
✓ |
55 |
Symbol |
Symbol |
✓ |
207 |
SecurityExchange |
Exchange |
✓ |
263 |
SubscriptionRequestType |
Snapshot, subscribe, unsubscribe |
336 |
TradingSessionID |
Tag |
Name |
Comments |
||
---|---|---|---|---|
✓ |
262 |
MDReqID |
Request ID |
|
✓ |
263 |
SubscriptionRequestType |
Snapshot, Subscribe, Unsubscribe |
|
264 |
MarketDepth |
Depth? |
||
✓ |
265 |
MDUpdateType |
Update type. Note! we currently only support incremental updates |
|
266 |
AggregateBook |
Using TopOfBook if true |
||
✓ |
267 |
NoMDEntryTypes |
Count |
|
✓ |
⇒ |
269 |
MDEntryType |
|
✓ |
146 |
NoRelatedSym |
Count |
|
✓ |
⇒ |
55 |
Symbol |
|
✓ |
⇒ |
207 |
SecurityExchange |
|
386 |
NoTradingSessions |
Count |
||
⇒ |
336 |
TradingSessionID |
||
20000 |
CustomType |
Enum (string): VWAP |
||
20001 |
CustomValue |
Value (double) depending on CustomType |
Server → Client#
Tag |
Name |
Comments |
|
---|---|---|---|
335 |
TradSesReqID |
Request ID |
|
✓ |
336 |
TradingSessionID |
Specific name |
325 |
UnsolicitedIndicator |
||
567 |
TradSesStatusRejReason |
||
58 |
Text |
Tag |
Name |
Comments |
||
---|---|---|---|---|
✓ |
320 |
SecurityReqId |
Request ID (only snapshot) |
|
✓ |
322 |
SecurityResponseID |
Response ID |
|
✓ |
560 |
SecurityRequestResult |
Result type |
|
✓ |
146 |
NoRelatedSym |
Count |
|
✓ |
⇒ |
55 |
Symbol |
|
231 |
ContractMultiplier |
|||
✓ |
⇒ |
207 |
SecurityExchange |
|
562 |
MinTradeVol |
|||
⇒ |
336 |
TradingSessionID |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
320 |
SecurityReqId |
Request ID |
✓ |
322 |
SecurityResponseID |
Response ID |
✓ |
323 |
SecurityResponseType |
Response type |
✓ |
55 |
Symbol |
|
231 |
ContractMultiplier |
||
✓ |
207 |
SecurityExchange |
|
562 |
MinTradeVol |
||
336 |
TradingSessionID |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
324 |
SecurityStatusReqId |
Request ID (only snapshot) |
✓ |
55 |
Symbol |
|
✓ |
207 |
SecurityExchange |
|
336 |
TradingSessionID |
||
✓ |
325 |
UnsolicitedIndicator |
False if snapshot, True if incremental update |
✓ |
326 |
SecurityTradingStatus |
Market status |
Tag |
Name |
Comments |
||
---|---|---|---|---|
✓ |
262 |
MDReqID |
Request ID |
|
✓ |
55 |
Symbol |
||
✓ |
207 |
SecurityExchange |
||
✓ |
268 |
NoMDEntries |
Count |
|
✓ |
⇒ |
269 |
MDEntryType |
Field |
⇒ |
270 |
MDEntryPx |
Price |
|
⇒ |
271 |
MDEntrySize |
Size |
|
⇒ |
272 |
MDEntryDate |
Begin time (UTC) |
|
⇒ |
273 |
MDEntryTime |
Begin time (UTC) |
|
⇒ |
336 |
TradingSessionID |
||
⇒ |
126 |
ExpireTime |
End time (UTC) |
|
⇒ |
37 |
OrderID |
Order ID (optional) |
|
⇒ |
346 |
NumberOfOrders |
Number of orders (MBP) |
|
⇒ |
290 |
MDEntryPositionNo |
Order priority (MBO) |
Tag |
Name |
Comments |
||
---|---|---|---|---|
✓ |
268 |
NoMDEntries |
Count |
|
✓ |
⇒ |
279 |
MDUpdateAction |
Update action |
✓ |
⇒ |
269 |
MDEntryType |
Field |
✓ |
⇒ |
55 |
Symbol |
|
✓ |
⇒ |
207 |
SecurityExchange |
|
⇒ |
270 |
MDEntryPx |
Price |
|
⇒ |
271 |
MDEntrySize |
Size |
|
⇒ |
272 |
MDEntryDate |
Begin time (UTC) |
|
⇒ |
273 |
MDEntryTime |
Begin time (UTC) |
|
⇒ |
336 |
TradingSessionID |
||
⇒ |
126 |
ExpireTime |
End time (UTC) |
|
⇒ |
37 |
OrderID |
||
⇒ |
346 |
NumberOfOrders |
Number of orders (MBP) |
|
⇒ |
290 |
MDEntryPositionNo |
Order priority (MBO) |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
262 |
MDReqID |
Request ID |
✓ |
281 |
MDReqRejReason |
Reject reason |
✓ |
58 |
Text |
Informative text |
Order Management#
Client → Server#
Tag |
Name |
Comments |
|
---|---|---|---|
37 |
OrderID |
||
✓ |
11 |
ClOrdID |
|
790 |
OrdStatusReqID |
Unique ID (for the request) |
|
55 |
Symbol |
||
207 |
SecurityExchange |
||
54 |
Side |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
584 |
MassStatusReqID |
Unique ID (for the request) |
✓ |
585 |
MassStatusReqType |
Request type |
336 |
TradingSessionID |
||
55 |
Symbol |
||
✓ |
207 |
SecurityExchange |
|
54 |
Side |
Tag |
Name |
Comments |
||
---|---|---|---|---|
✓ |
11 |
ClOrdID |
Unique ID (for the request) |
|
453 |
NoPartyIDs |
|||
⇒ |
448 |
PartyID |
||
⇒ |
447 |
PartyIDSource |
||
⇒ |
452 |
PartyRole |
||
1 |
Account |
|||
21 |
HandlInst |
|||
18 |
ExecInst |
Execution instruction |
||
386 |
NoTradingSessions |
Count |
||
⇒ |
336 |
TradingSessionID |
||
✓ |
55 |
Symbol |
||
✓ |
207 |
SecurityExchange |
||
✓ |
54 |
Side |
||
60 |
TransactTime |
Request timestamp |
||
✓ |
38 |
OrderQty |
Total intended order quantity |
|
✓ |
40 |
OrdType |
Order type |
|
44 |
Price |
|||
99 |
StopPx |
Stop price |
||
59 |
TimeInForce |
|||
58 |
Text |
|||
77 |
PositionEffect |
|||
210 |
MaxShow |
Max quantity to show (if supported by exchange) |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
41 |
OrigClOrdID |
ClOrdID of the previous non-rejected order |
37 |
OrderID |
||
✓ |
11 |
ClOrdID |
Unique ID (for the request) |
55 |
Symbol |
||
207 |
SecurityExchange |
||
54 |
Side |
||
60 |
TransactTime |
Request timestamp |
|
38 |
OrderQty |
||
58 |
Text |
Tag |
Name |
Comments |
|
---|---|---|---|
37 |
OrderID |
Order ID |
|
✓ |
41 |
OrigClOrdID |
ClOrdID of the previous non-rejected order |
✓ |
11 |
ClOrdID |
Unique ID (for the request) |
55 |
Symbol |
||
207 |
SecurityExchange |
||
38 |
OrderQty |
Total intended order quantity |
|
44 |
Price |
||
54 |
Side |
||
60 |
TransactTime |
Request timestamp |
|
40 |
OrdType |
Order type |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
11 |
ClOrdID |
Unique ID (for the request) |
✓ |
530 |
MassCancelRequestType |
Type of request (only supporting 7 = cancel all orders) |
336 |
TradingSessionID |
||
60 |
TransactTime |
Request timestamp |
Server → Client#
Tag |
Name |
Comments |
|
---|---|---|---|
37 |
OrderID |
||
✓ |
11 |
ClOrdID |
Client order ID |
41 |
OrigClOrdID |
Original client order ID |
|
790 |
OrdStatusReqID |
Request ID (if response to OrderStatusRequest) |
|
584 |
MassStatusReqID |
Request ID (if response to OrderMassStatusRequest) |
|
911 |
TotNumReports |
Total number of reports (if response to OrderMassStatusRequest) |
|
912 |
LastRptRequested |
True if last report (if response to OrderMassStatusRequest) |
|
17 |
ExecID |
Execution ID |
|
150 |
ExecType |
Execution type |
|
39 |
OrdStatus |
Order status |
|
636 |
WorkingIndicator |
||
103 |
OrdrejReason |
Order reject reason |
|
1 |
Account |
||
581 |
AccountType |
||
✓ |
55 |
Symbol |
|
✓ |
207 |
SecurityExchange |
|
54 |
Side |
||
40 |
OrdType |
||
38 |
OrderQty |
Order quantity |
|
44 |
Price |
||
99 |
StopPx |
Stop price |
|
15 |
Currency |
Currency (from reference data, if available) |
|
59 |
TimeInForce |
||
18 |
ExecInst |
Execution instruction |
|
32 |
LastQty |
Fill quantity (if trade) |
|
31 |
LastPx |
Fill price (if trade) |
|
336 |
TradingSessionID |
||
151 |
LeavesQty |
Remaining order quantity |
|
14 |
CumQty |
Cummulative traded quantity |
|
6 |
AvgPx |
Average fill price |
|
60 |
TransactTime |
Transaction time |
|
77 |
PositionEffect |
Position effect |
|
210 |
MaxShow |
Max shown quantity |
|
58 |
Text |
||
851 |
LastLiquidityInd |
Liquidity indicator of fill (if trade, maker or taker) |
Tag |
Name |
Comments |
|
---|---|---|---|
37 |
OrderID |
||
✓ |
11 |
ClOrdID |
Client order ID |
✓ |
41 |
OrigClOrdID |
Original client order ID |
39 |
OrdStatus |
Order status |
|
636 |
WorkingIndicator |
||
1 |
Account |
||
✓ |
434 |
CxlRejResponseTo |
Origin of request (OrderCancel or OrderCancelReplace) |
✓ |
102 |
CxlRejReason |
Reason for reject |
58 |
Text |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
11 |
ClOrdID |
Unique ID (for the request) |
✓ |
37 |
OrderID |
Unique ID (from the server) |
✓ |
530 |
MassCancelRequestType |
Request type |
✓ |
531 |
MassCancelResponse |
Response type |
532 |
MassCancelRejectReason |
Reason for reject |
|
58 |
Text |
Trade Reporting#
Client → Server#
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
568 |
TradeRequestID |
|
✓ |
569 |
TradeRequestType |
|
✓ |
263 |
SubscriptionRequestType |
|
37 |
OrderID |
||
11 |
ClOrdID |
||
880 |
TrdMatchID |
Server → Client#
Tag |
Name |
Comments |
||
---|---|---|---|---|
✓ |
571 |
TradeReportID |
||
568 |
TradeRequestID |
|||
150 |
ExecType |
|||
748 |
TotNumTradeReports |
|||
912 |
LastRptRequested |
|||
325 |
UnsolicitedIndicator |
|||
880 |
TrdMatchID |
|||
55 |
Symbol |
|||
207 |
SecurityExchange |
|||
32 |
LastQty |
|||
31 |
LastPx |
|||
60 |
TransactTime |
|||
552 |
NoSides |
|||
⇒ |
54 |
Side |
||
⇒ |
37 |
OrderID |
||
⇒ |
11 |
ClOrdID |
||
⇒ |
1 |
Account |
||
⇒ |
581 |
AccountType |
||
⇒ |
77 |
PositionEffect |
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
568 |
TradeRequestID |
|
✓ |
569 |
TradeRequestType |
|
✓ |
749 |
TradeRequestResult |
|
✓ |
750 |
TradeRequestStatus |
Position Management#
Client → Server#
Tag |
Name |
Comments |
||
---|---|---|---|---|
✓ |
710 |
PosReqID |
||
✓ |
724 |
PosReqtype |
||
✓ |
263 |
SubscriptionRequestType |
||
55 |
Symbol |
|||
207 |
SecurityExchange |
|||
386 |
NoTradingSessions |
Count |
||
⇒ |
336 |
TradingSessionID |
Server → Client#
Tag |
Name |
Comments |
||
---|---|---|---|---|
721 |
PosMaintRptID |
|||
710 |
PosReqID |
|||
724 |
PosReqType |
|||
263 |
SubscriptionRequestType |
|||
727 |
TotalNumPosReports |
|||
325 |
UnsolicitedIndicator |
|||
728 |
PosReqResult |
|||
715 |
ClearingBusinessDate |
|||
1 |
Account |
|||
581 |
AccountType |
|||
55 |
Symbol |
|||
207 |
SecurityExchange |
|||
730 |
SettlPrice |
|||
731 |
SettlPriceType |
|||
734 |
PriorSettlPrice |
|||
702 |
NoPositions |
|||
⇒ |
703 |
PosType |
||
⇒ |
704 |
LongQty |
||
⇒ |
705 |
ShortQty |
||
753 |
NoPosAmt |
|||
⇒ |
707 |
PosAmtType |
||
⇒ |
708 |
PosAmt |
Miscellaneous#
Server → Client#
Tag |
Name |
Comments |
|
---|---|---|---|
✓ |
45 |
RefSeqNum |
|
✓ |
372 |
RefMsgTyp |
|
✓ |
379 |
BusinessRejectRefID |
|
✓ |
380 |
BusinessRejectReason |
|
58 |
Text |
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 does support account multiplexing.
The universe of {exchange, symbol} is dynamic and can not always be known at the time when a FIX client issues a request. The FIX protocol also does not allow for a client to receive notifications when an exchange hase become ready. Thus, a FIX client must expect rejects (when an {exchange, symbol} combination has not yet become available, for example) and implement a retry policy.
Successful FIX client subscriptions will be cached and survive gateway reconnects. This is done because the FIX protocol does not allow for a client to receive notifications when a subscription is broken. Thus, a FIX client must expect stale subscriptions and implement a timeout policy.
The FIX protocol requires subsequent order actions to be rejected if a previous request has been rejected. Full support is only possible if the exchanges support it or if the gateways disallow pipelining (sending multiple requests without first waiting for the previous request’s response). A low-latency configuration should allow pipelining (at the gateway level) and the FIX bridge can therefore not be fully FIX compliant (for the reasons just described).
Custom order book calculations are supported but constrained to one custom calculation per market data request. It is not possible to overlap custom calculations with regular updates (like top of book or market by price) due to the FIX protocol allowing for any other special identifiers with the full/incremental updates.