Skip to main content

RFQ Router Algorithm

Summary

RFQ Router Algorithm aggregates quotes from multiple configured RFQ liquidity providers (LPs). It receives quote requests from clients on a TimeBase stream, forwards each request to the relevant LPs, combines their responses into a single composite best bid and best offer, and sends that quote back to the client. Child orders are routed to the LP that supplied the priced side the client trades against.

Unlike the RFQ Algorithm, which derives quotes from a local order book, RFQ Router does not compute prices from market depth; it orchestrates external RFQ sessions.

Description

RFQ Router uses the configured TB streamKey for client-facing traffic: it polls for incoming QuoteRequest and QuoteCancel, and sends Quote, QuoteCancel, and QuoteReject back to the requester. Message types are from the deltix.timebase.api.messages.rfq package of deltix.qsrv:deltix-timebase-api-messages.

Each LP is configured with a quote connector (for RFQ messaging) and optionally a separate order connector (for child orders). For each LP, the algorithm also uses a dedicated TB stream (defaults to the quote connector id unless quoteStreamKey is set) to send QuoteRequest / QuoteCancel to the LP and to receive Quote / QuoteReject / QuoteCancel.

The algorithm subscribes to market data (per-exchange instrument data) and can use it to round padded prices in its quotes to exchange price increments.

Quote request handling

When a QuoteRequest arrives:

  • If the request has no account, the algorithm sets account from partyId.
  • The request timestamp is set to the current time and sourceId to the algorithm id.
  • If expireTime is unset, it defaults to 2 minutes after the current time.
  • The request is forwarded to each eligible LP
  • An LP is skipped if its disconnected, if the instrument is not configured for that LP’s exchange in the instrument snapshot, or if an optional eligible-LP filter excludes it (see below).

The algorithm will then wait for responses (Quote, QuoteReject, or QuoteCancel) from every LP that was actually sent a request. As the quotes start to arrive, algorithm will publish the best composite quote it can generate from the quotes it has received so far. If after receiving all responses, no valid quote can be built, the algorithm sends QuoteReject to the client.

The algorithm will continue publishing the best composite quote until request expiration. If an LP cancels its quote before that, the router will try to obtain another quote again if resubscribeOnCancel is set for that LP; otherwise, it treats cancel as a final response from that LP and excludes it from the composite quote.

When request expires, the router will cancel any outstanding provider requests and then send QuoteCancel to the client.

Composite quote

The composite best bid is the highest bid among active LPs whose quotes are not expired; the best offer is the lowest offer under the same rules. Bid and offer may come from different LPs. One-sided quotes are supported (only bid or only offer).

Prices sent to the client are padded using a per-LP quotePriceMargin (fractional, 0–1) and optional per-symbol overrides in margins. The margin widens the client price away from the LP: bid is reduced and offer is increased. Padded prices are rounded using the instrument’s order price increment for that LP’s exchange when available.

The Quote sent to the client uses the original quoteRequestId as quoteRequestId, and quoteId is set to the original client quote request id (the composite id the client uses when placing an order). Composite validity time is the minimum of the bid and offer validUntilTime values when both sides are present.

After the first composite Quote is published, further LP updates can refresh the client quote when the best bid/offer changes. If every LP becomes inactive (reject, cancel, or expiry), the algorithm sends QuoteCancel to the client.

Eligible LPs (optional)

A QuoteRequest may include a custom attribute with key 7244 (ELIGIBLE_LPS_KEY). The value must be a comma-separated list of LP connector ids (alphanumeric), with no spaces. Only those LPs are considered. If the list is empty, malformed, or no listed LP is available for the symbol, the request may be rejected (for example when no provider is usable).

Previously quoted orders

Only orders with type PREVIOUSLY_QUOTED and a quoteId are accepted. The algorithm will route the order using the best bid or offer side: a BUY uses the best offer LP; a SELL uses the best bid LP.

The parent order’s limit price is checked against the last raw quoted price from that LP for the relevant side; if the market quote has moved to a worse level than the client’s limit allows, the order is rejected (“Order price does not match the last quoted price”). The child order is sent to the LP’s order connector (orderConnectorId, defaulting to the quote connector if omitted) with that LP’s provider quoteId and limit price set to the current best price for that side.

Replace and cancel on the parent order are not supported and are rejected.

Configuration

Sample RFQ Router algorithm configuration from the ember.conf configuration file:

RFQ_ROUTER: ${template.algorithm.default} {
factory = "deltix.ember.algorithm.pack.rfqr.RFQRouterAlgorithmFactory"
subscription {
allInstruments = true
streams = []
}
settings {
streamKey = "rfq"
sources = [
{ quoteConnectorId = "RFQCAB", orderConnectorId = "CAB", quoteStreamKey = "CAB.rfq", quotePriceMargin = 0.00005 },
{ quoteConnectorId = "RFQBOS", orderConnectorId = "RFQBOS", quoteStreamKey = "BOS.rfq", quotePriceMargin = 0.00001 }
]
margins = [
{ symbols = [ EURUSD, GBPUSD, USDCHF ], quotePriceMargin = 0.00002 },
{ symbols = [ USDNGN, USDKES, USDCAD ], quotePriceMargin = 0.000015 }
]
}
}

Settings reference

Parameter NameDescription
streamKeyOptional, string, default "rfq". TB stream for client QuoteRequest / QuoteCancel input and Quote / QuoteCancel / QuoteReject output.
sourcesRequired. Array of LP definitions; see RFQ source below.
marginsOptional. Array of per-symbol quotePriceMargin overrides (0–1). Overrides the margin from sources for listed symbols; a symbol must not appear in more than one entry.

RFQ source (sources[]):

FieldDescription
quoteConnectorIdRequired. Alphanumeric id of the LP’s quote (RFQ) session / destination.
orderConnectorIdOptional. Destination for child orders; defaults to quoteConnectorId if omitted.
quoteStreamKeyOptional. TB stream for that LP’s RFQ traffic; defaults to the alphanumeric string of quoteConnectorId.
quotePriceMarginOptional, double, default 0. Fractional padding applied to that LP’s prices (0 ≤ margin < 1).
resubscribeOnCancelOptional, boolean, default false.

Inherited order cache settings from the algorithm template / AbstractAlgorithmFactory (same meaning as for other algorithms):

Parameter NameDescription
initialActiveOrdersCacheSizeOptional, int. Initial active orders cache size.
initialClientsCapacityOptional, int. Initial client capacity.
maxInactiveOrdersCacheSizeOptional, int. Maximum inactive orders cache size.
recyclingDisabledOptional, boolean. Disables recycling of order instances.
orderCacheCapacityOptional, int. Order cache capacity.
Reject messageReason
Only previously quoted orders are supportedOrder type is not PREVIOUSLY_QUOTED or quote id is missing.
Quote is no longer activeComposite quote request id is unknown or no longer tracked.
Quote is no longer availableSide/LP resolution failed (e.g. invalid compound quoteId).
No active destination to route quote toNo last quote state for the chosen LP.
Order price does not match the last quoted priceClient limit is inconsistent with the LP’s current quote for that side.
Replace is not supported for RFQ ordersReplace request.
Cancel is not supported for RFQ ordersCancel request.