A "Request for Quote" (RFQ) in bond trading is a process used by investors and traders to obtain price quotes from multiple dealers or market makers for a specific bond. This process is crucial in the fixed-income market, where liquidity can vary significantly, and pricing transparency is not as clear-cut as in more liquid markets like equities. This is an introductory guide to understanding RFQs in the fixed-income market.
Nomenclature
In the context of bond trading, RFQs utilize the concept of "bids-wanted" and "offers-wanted" as mechanisms for soliciting price quotes, which differ from traditional buy/sell orders.
- Bids-Wanted: This term is used when a bond holder wants to sell a bond and is seeking potential buyers to submit their bid prices. The seller is essentially asking, "What are you willing to pay for this bond?" This process allows the seller to gauge market interest and obtain competitive pricing from multiple buyers.
- Offers-Wanted: Conversely, this term is used when a buyer is interested in purchasing a bond and requests sellers to provide their offer prices. Here, the buyer is inquiring, "At what price are you willing to sell this bond?" This allows the buyer to compare offers and select the most favorable terms.
These mechanisms differ from direct buy/sell orders, where a trader specifies a fixed price and quantity for a transaction, expecting immediate execution if the price conditions are met. RFQs, through bids-wanted and offers-wanted, provide a more flexible and potentially strategic approach to trading, allowing participants to explore market conditions and negotiate better terms before committing to a trade.
Note: For simplicity, Moment's RFQ APIs use the same "side" terminology as our order APIs. Namely, you will submit an RFQ with side = sell
for a bids-wanted RFQ, and side = buy
for an offers-wanted RFQ.
The RFQ Lifecycle
The general lifecycle of an RFQ can be broken down into the following stages.
- Initiation: An investor or trader identifies a bond they are interested in buying or selling. They prepare a request specifying the desired trade details such as the security identifier and the desired quantity to buy/sell.
- Distribution: The RFQ is sent to a market participant (either a venue, or single platform dealer).
- Response: Brokers and dealers respond with their quotes, indicating the price at which they are willing to buy or sell the bond. These quotes are typically valid for a short period, reflecting the current market conditions.
- Evaluation: The investor or trader evaluates the quotes received, considering factors such as price, transaction size, and the reputation or reliability of the dealer.
- Execution: Once a suitable quote is selected, the investor or trader executes the trade with the chosen dealer at the agreed-upon price.
Below we will walk through this lifecycle using Moment's RFQ APIs.
Initiation
To initiate an RFQ, a user can submit an RFQ Request using the POST /v1/trading/rfq/ endpoint.
// POST /v1/trading/rfq/
{
"client_rfq_id": "e3f2c1b0-4d5f-4a8b-9f2d-8a6f8b5c9b1e",
"ib_id": "01FJ5C9D4D5K3F5E6K8G6E5D5D",
"risk_group_id": "01FJ2Z4G8D6M8K1J8G9T5Y0A8B",
"instrument_id": "US0378331005",
"type": "bin",
"side": "sell",
"amount": {
"type": "par",
"value": 1000
},
"venue_mpid": "TMCC",
"disclosure": "disclosed",
"due_time": "2024-12-31T10:00:00-05:00",
"firm_until_time": "2024-12-31T10:30:00-05:00",
"expire_time": "2025-01-01T00:00:00-05:00"
}
After submitting the RFQ to Moment, validation checks will be run on the RFQ to ensure it is able to be routed to the venue in question. If the RFQ does not pass these checks, it will be rejected.
The RFQ creation endpoint returns the ULID identifier of the RFQ. Users can use this identifier to retrieve the latest status for the RFQ via the Get RFQ endpoint. Users can also retrieve all events for the RFQ via the Get RFQ Events endpoint.
// GET /v1/trading/rfq/01FJ8Y6R8B7F7ZC3M4V6QZ2D4C/
{
"id": "01FJ8Y6R8B7F7ZC3M4V6QZ2D4C",
"client_rfq_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"ib_id": "01FJ5C9D4D5K3F5E6K8G6E5D5D",
"risk_group_id": "01FJ2Z4G8D6M8K1J8G9T5Y0A8B",
"isin": "US0378331005",
"type": "bin",
"side": "sell",
"amount": {
"type": "par",
"value": 1000
},
"venue_mpid": "TMCC",
"disclosure": "disclosed",
"due_time": "2024-12-31T10:00:00-05:00",
"firm_until_time": "2024-12-31T10:30:00-05:00",
"expire_time": "2025-01-01T00:00:00-05:00",
"settlement_date": "2025-01-02",
"created_at": "2024-12-31T09:30:00-05:00",
"status": "open",
"contra_quote_id": null,
"quotes": [],
"quote_responses": [],
"latest_msg_num": "202412310000000001"
}
At any point of the RFQ lifecycle - before the RFQ hits a terminal state - you can cancel and RFQ via a request to the Cancel RFQ endpoint.
DELETE /v1/trading/rfq/01FJ6F1Y5GQZ3W6J4K8D1D7G2R/
Distribution and response
After validating the RFQ and making any adjustments outlined in our Venue Specific Behavior guide, Moment will send the RFQ to the corresponding market participant. As venues respond, quotes will become available on the RFQ object. This is visible on the summary and events for the RFQ under the quotes
field. Event messages from the Get RFQ Events endpoint and the realtime RFQ API will contain the latest quote state as well.
{
"...": "...",
"quotes": [
{
"venue_quote_id": "01FZ3Z4GQY5X8YV0TB1J4H6K8N",
"side": "sell",
"quantity": {
"type": "par",
"value": 100000,
},
"price": 98.320,
"ytw": 4.521,
"ytm": 4.521,
"firm_until_time": "2024-12-31T12:00:00-05:00",
"expire_time": "2024-12-31T17:00:00-05:00",
"venue_mpid": "TMCC",
"venue": "ice_tmc",
"created_at": "2024-12-31T11:55:00-05:00",
"updated_at": "2024-12-31T11:57:00-05:00"
},
{
"...": "..."
}
],
"...": "..."
}
Evaluation
Once the RFQ has reached it's due_time
traders can evaluate which quote they would like to trade against. Usually a user will either pick the best quote and trade against it, or pass on the RFQ by sending a Cancel RFQ request.
Execution
When a trader decides to execute against an RFQ they will create an order using the Place an Order endpoint. Creating an order for an RFQ is similar to creating a standard order against a limit order book, however, when creating an order against an RFQ the user must specify that the order is being placed in response to an RFQ, along with information on that RFQ and the quote being traded against.
// POST /v1/trading/order/
{
"...": "...",
"type": "rfq_response",
"rfq": {
"id": "01FJZ7C2H5D2K8J1G1S2B3E5J8",
"contra_quote_id": "7f3a9d2b-c5e1-47b8-a9c6-4d8b2f5e7a1d"
},
"...": "..."
}
The lifecycle of an rfq_response
order will match that of other orders as described in the Understanding Orders Guide.
Trades against an order will also be reflected in the RFQ object and event streams. Namely, the quote currently being traded against will be tagged under the contra_quote_id
field, and the order_responses
field will contain an array of all orders submitted against the RFQ.
// GET /v1/trading/rfq/:id/
{
"...": "...",
"contra_quote_id": "7f3a9d2b-c5e1-47b8-a9c6-4d8b2f5e7a1d",
"quote_responses": [
{
"id": "01FJ8M0Z8FJ3JH6F5Y6H5B8N6C",
"client_order_id": "e7f3d1a2-9c6a-4b2f-8c3b-8e8e9c0e5b45",
"contra_quote_id": "7f3a9d2b-c5e1-47b8-a9c6-4d8b2f5e7a1d",
"status": "filled",
"created_at": "2024-12-31T12:00:00-05:00"
}
]
}
Once the order has executed the RFQ will also enter the terminal "filled" state.
Events and States
RFQs
Event | State | Description | Possible Next Event/State |
---|---|---|---|
ack | validating | The RFQ has been received by Moment's systems and is pending validation. | (approve, open) , (reject, rejected) , (cancel, canceled) |
approve | open | The RFQ has passed validation and will be routed to the relevant venue/dealer. | (quote_update, open) , (cancel, canceled) , (expire, expired) |
reject | rejected | The RFQ did not pass validation. | N/A - The RFQ has reached a terminal state. |
expire | expired | The RFQ has reached its expire time without being traded against. | N/A - The RFQ has reached a terminal state. |
cancel | canceled | The RFQ has been canceled. | N/A - The RFQ has reached a terminal state. |
quote_update | open | A new or updated quote from a counterparty has been received. | (quote_update, open) , (cancel, canceled) , (expire, expired) , (quote_response_recieve, order_placed) |
quote_response_receive | order_placed | An order has been submitted for the RFQ. | (quote_response_approve, order_placed) , (quote_response_reject, open) , (quote_response_cancel, open) |
quote_response_approve | order_placed | The order placed for the RFQ has been approved. | (fill, filled) , (quote_response_cancel, open) |
quote_response_reject | open | The order submitted for the RFQ was rejected. Risk controls or other parameters will need to be updated to allow trading against the RFQ. | (cancel, canceled) , (expire, expired) , (quote_response_recieve, order_placed) |
quote_response_cancel | open | The order submitted for the RFQ was cancelled. A new order must now be submitted to trade against the RFQ. | (cancel, canceled) , (expire, expired) , (quote_response_recieve, order_placed) |
fill | filled | The order submitted against the RFQ filled. The RFQ has been successfully traded. | N/A - The RFQ has reached a terminal state. |
System
Event | State | Description | Possible Next Event/State |
---|---|---|---|
cancel_reject | N/A | A cancel request for an RFQ was rejected. Often this is due to the RFQ already being in a terminal state. | N/A |