# Flow 8.4 — List Trading (Dealer Response) ## Overview Flow 8.4 defines how a **dealer** responds to a client's Request for Quote (RFQ) through Tradeweb (TW) in a List Trading context. The key rule is: > **The dealer NEVER sends an ExecutionReport (35=8). Only Tradeweb does.** The dealer's role is limited to: - Acknowledging messages (35=AI QuoteStatusReport, 35=BN ExecutionAck) - Sending a price quote (35=S) This document covers the **happy path** (client accepts) and two **alternative flows**: - **Flow 8.6 — Trade Ended:** Client cancels before or after receiving the quote - **QuoteAck Rejected:** TW rejects the dealer's quote ## Participants | Abbreviation | Role | |---|---| | **TW** | Tradeweb — the platform that orchestrates the trade | | **Dealer** | Us — responds to RFQs with quotes and acknowledges TW messages | | **Client** | The counterparty requesting a quote (we never communicate with them directly) | ## Message Flow ``` TW Dealer │ │ │ 1. QuoteRequest (35=R) │ │ ─────────────────────────────────────────> │ │ │ │ 2. QuoteStatusReport (35=AI) [ACK] │ │ <───────────────────────────────────────── │ │ │ │ 3. Quote (35=S) [price] │ │ <───────────────────────────────────────── │ │ │ │ 4. QuoteAck (35=CW) [ACCEPTED] │ │ ─────────────────────────────────────────> │ │ │ │ 5. QuoteResponse (35=AJ) [Hit/Lift] │ │ ─────────────────────────────────────────> │ │ │ │ 6. QuoteStatusReport (35=AI) [TRDREQACK] │ │ <───────────────────────────────────────── │ │ │ │ 7. ExecutionReport (35=8) [_LISTEND] │ │ ─────────────────────────────────────────> │ │ │ │ 8. ExecutionAck (35=BN) │ │ <───────────────────────────────────────── │ │ │ │ 9. ExecutionReport (35=8) [_TRDEND] │ │ ─────────────────────────────────────────> │ │ │ │ 10. ExecutionAck (35=BN) │ │ <───────────────────────────────────────── │ │ │ │ 11. ExecutionReport (35=8) [_TRDSUMM] │ │ ─────────────────────────────────────────> │ │ │ │ 12. ExecutionAck (35=BN) │ │ <───────────────────────────────────────── │ │ │ ``` ## Step-by-Step Detail ### Step 1 — QuoteRequest (35=R) — TW → Dealer TW sends an RFQ on behalf of the client. Key fields: | Tag | Field | Example | Notes | |-----|-------|---------|-------| | 131 | QuoteReqID | `LST_20260316_BYMA_CORI_NY1567246.1_1` | Always starts with `LST_` for List Trading | | 66 | ListID | `NY1567246.1` | Identifies the list/inquiry | | 48 | SecurityID | `040114HT0` | Bond identifier | | 22 | SecurityIDSource | `1` (CUSIP) | Could also be `4` (ISIN) | | 54 | Side | `2` (SELL) | The client's side — if client sells, dealer buys | | 38 | OrderQty | `10000` | Quantity requested | | 15 | Currency | `USD` | Settlement currency | | 64 | SettlDate | `20260317` | Settlement date | | 20073 | NegotiationType | `RFQ` | Must be RFQ for this flow | **Validation:** The dealer must verify `LST_` prefix, non-empty `ListID`, and `NegotiationType=RFQ` before proceeding. ### Step 2 — QuoteStatusReport (35=AI) — Dealer → TW The dealer acknowledges receipt of the QuoteRequest. | Tag | Field | Value | |-----|-------|-------| | 131 | QuoteReqID | Same as received | | 117 | QuoteID | Same as QuoteReqID | | 297 | QuoteStatus | `0` (ACCEPTED) | ### Step 3 — Quote (35=S) — Dealer → TW The dealer sends a price quote. | Tag | Field | Example | Notes | |-----|-------|---------|-------| | 117 | QuoteID | Same as QuoteReqID | | | 131 | QuoteReqID | Same as received | | | 132 | BidPx | `99.60000000` | Set when client side is SELL (dealer bids) | | 133 | OfferPx | `99.60000000` | Set when client side is BUY (dealer offers) | | 44 | Price | `99.60000000` | The quote price | | 423 | PriceType | `1` (Percentage) | | | 537 | QuoteType | `211` (SEND_QUOTE) | | ### Step 4 — QuoteAck (35=CW) — TW → Dealer TW confirms the quote was accepted. | Tag | Field | Value | |-----|-------|-------| | 1865 | QuoteAckStatus | `1` (ACCEPTED) | **Note:** If status is not ACCEPTED, the dealer logs the rejection (including the `Text` field) and cleans up the trade from memory. See [QuoteAck Rejected](#quoteack-rejected-quote-not-accepted) below. ### Step 5 — QuoteResponse (35=AJ) — TW → Dealer The client has decided to trade (Hit/Lift the quote). | Tag | Field | Value | Notes | |-----|-------|-------|-------| | 694 | QuoteRespType | `1` (Hit/Lift) | `2` would be Counter — that's flow 8.5, not handled here | | 693 | QuoteRespID | `..._TRDREQ` | Always ends with `_TRDREQ` | ### Step 6 — QuoteStatusReport (35=AI) — Dealer → TW The dealer acknowledges the trade request (TRDREQACK). | Tag | Field | Value | |-----|-------|-------| | 131 | QuoteReqID | Same as received | | 693 | QuoteRespID | Same as received | | 297 | QuoteStatus | `0` (ACCEPTED) | ### Step 7 — ExecutionReport (35=8) `_LISTEND` — TW → Dealer TW signals that the due-in window for the list has closed. | Tag | Field | Value | Notes | |-----|-------|-------|-------| | 17 | ExecID | `..._LISTEND-{timestamp}` | Contains `_LISTEND` | | 150 | ExecType | `A` (PendingNew) | | | 39 | OrdStatus | `A` (PendingNew) | | **Dealer action:** Send ExecutionAck only. **Do NOT send an ExecutionReport back.** ### Step 8 — ExecutionAck (35=BN) — Dealer → TW | Tag | Field | Value | |-----|-------|-------| | 37 | OrderID | Same as received | | 17 | ExecID | Same as received | | 1036 | ExecAckStatus | `1` (ACCEPTED) | ### Step 9 — ExecutionReport (35=8) `_TRDEND` — TW → Dealer TW sends the trade result. | Tag | Field | Value | Notes | |-----|-------|-------|-------| | 17 | ExecID | `..._TRDEND-{timestamp}` | Contains `_TRDEND` | | 150 | ExecType | `F` (Trade) | The trade was executed | | 39 | OrdStatus | `2` (Filled) | | | 44 | Price | `99.6` | Final execution price | **Dealer action:** Send ExecutionAck. Clean up internal trade tracking state. ### Step 10 — ExecutionAck (35=BN) — Dealer → TW Same format as Step 8. ### Step 11 — ExecutionReport (35=8) `_TRDSUMM` — TW → Dealer TW sends the full trade summary with additional details (parties, settlement info, trade IDs). | Tag | Field | Value | Notes | |-----|-------|-------|-------| | 17 | ExecID | `..._TRDSUMM-{timestamp}` | Contains `_TRDSUMM` | | 150 | ExecType | `F` (Trade) | | | 39 | OrdStatus | `2` (Filled) | | | 453 | NoPartyIDs | Party information | Counterparty details | | 526 | SecondaryClOrdID | `TRD_...` | Tradeweb trade reference | | 1003 | TradeID | `20260316.BYMA.CORI.230` | Unique trade identifier | **Dealer action:** Send ExecutionAck. Log the summary for audit/reconciliation. ### Step 12 — ExecutionAck (35=BN) — Dealer → TW Same format as Step 8. --- ## Alternative Flows ### Flow 8.6 — Trade Ended (Client Cancels) The client changes their mind and ends the trade. This can happen at any point after the QuoteRequest — even before the dealer's quote arrives. TW informs the dealer via QuoteResponse (35=AJ) messages with `_TRDEND` and `_TRDSUMM` suffixes instead of the ExecutionReport chain. > **Critical:** The dealer MUST send a QuoteStatusReport (35=AI) ACK for every QuoteResponse. If no ACK is sent, TW will retry the message indefinitely (~every 11 seconds). ``` TW Dealer │ │ │ 1. QuoteRequest (35=R) │ │ ─────────────────────────────────────────> │ │ │ │ 2. QuoteStatusReport (35=AI) [ACK] │ │ <───────────────────────────────────────── │ │ │ │ ┌─── Client ends trade ───┐ │ │ │ Meanwhile, dealer may │ │ │ │ still send Quote (S) │ │ │ └─────────────────────────┘ │ │ │ │ 3. QuoteResponse (35=AJ) [_TRDEND] │ │ QuoteRespType=7 (End Trade) │ │ ─────────────────────────────────────────> │ │ │ │ 4. QuoteStatusReport (35=AI) [ACK] │ │ <───────────────────────────────────────── │ │ │ │ 5. QuoteAck (35=CW) [REJECTED] │ │ (if quote was sent, TW rejects it) │ │ ─────────────────────────────────────────> │ │ │ │ 6. QuoteResponse (35=AJ) [_TRDSUMM] │ │ QuoteRespType=7, TradeSummary=Y │ │ ─────────────────────────────────────────> │ │ │ │ 7. QuoteStatusReport (35=AI) [ACK] │ │ <───────────────────────────────────────── │ │ │ ``` #### Step 3 — QuoteResponse (35=AJ) `_TRDEND` — TW → Dealer TW notifies that the client ended the trade. | Tag | Field | Value | Notes | |-----|-------|-------|-------| | 693 | QuoteRespID | `..._TRDEND` | Suffix identifies this as trade end | | 694 | QuoteRespType | `7` (End Trade) | | | 131 | QuoteReqID | Same as original | | **Dealer action:** Send QuoteStatusReport (35=AI) with `693=QuoteRespID` and `297=0` (ACCEPTED). #### Step 5 — QuoteAck (35=CW) `REJECTED` — TW → Dealer If the dealer's Quote (35=S) crossed with the TRDEND, TW rejects it. The QuoteAckStatus will be `2` (REJECTED) with a text like "DPL DLRQUOTE received in an invalid state." **Dealer action:** Log the rejection and clean up the trade from memory. #### Step 6 — QuoteResponse (35=AJ) `_TRDSUMM` — TW → Dealer TW sends the final trade summary confirming the outcome. | Tag | Field | Value | Notes | |-----|-------|-------|-------| | 693 | QuoteRespID | `..._TRDSUMM` | Final summary message | | 694 | QuoteRespType | `7` (End Trade) | | | 22636 | TradeSummary | `Y` | Confirms this is the summary | **Dealer action:** Send QuoteStatusReport (35=AI) ACK. Clean up the trade from memory. This is the **terminal message** — no more messages will follow for this QuoteReqID. --- ### QuoteAck Rejected (Quote Not Accepted) If TW rejects the dealer's Quote (35=CW with status != ACCEPTED), the trade is dead from the dealer's perspective. ``` TW Dealer │ │ │ 1-3. (same as happy path) │ │ │ │ 4. QuoteAck (35=CW) [REJECTED] │ │ QuoteAckStatus != 1 │ │ ─────────────────────────────────────────> │ │ │ │ Trade is terminated. │ │ │ ``` **Dealer action:** Log the rejection (including the `Text` field with the reason) and remove the trade from memory. No further action needed — TW may or may not send subsequent messages for this QuoteReqID. --- ## QuoteRespID Suffix Routing in `handleQuoteResponse` All QuoteResponse (35=AJ) messages are routed by the suffix of the `QuoteRespID` (tag 693): ``` QuoteRespID ends with "_TRDREQ" → Trade request (flow 8.4 happy path) — ACK QuoteRespID ends with "_TRDEND" → Trade ended by client (flow 8.6) — ACK QuoteRespID ends with "_TRDSUMM" → Trade summary (flow 8.6 final) — ACK + cleanup QuoteRespID ends with "_LISTEND" → List ended — ACK Other suffix → Ignored (logged) ``` ## Code Reference The implementation lives in `src/client/fix/manager.go`: | Handler | Triggers on | Action | |---------|------------|--------| | `handleQuoteRequest` | 35=R | Sends 35=AI (ack) + 35=S (quote) | | `handleQuoteAck` | 35=CW | If rejected: logs + cleans up trade. If accepted: logs | | `handleQuoteResponse` | 35=AJ | Sends 35=AI (ACK). Routes by QuoteRespID suffix. Cleans up on `_TRDSUMM` | | `handleExecutionReport` | 35=8 | Sends 35=BN (ack) + routes by ExecID suffix | | `sendQuoteStatusReport` | — | Builds 35=AI for QuoteRequest ack | | `sendTradeRequestAck` | — | Builds 35=AI for QuoteResponse ack (all suffixes) | | `sendExecutionAck` | — | Builds 35=BN for ExecutionReport ack | ### ExecID Routing in `handleExecutionReport` ``` ExecID contains "_LISTEND" → Log only, await trade result ExecID contains "_TRDEND" → Log trade end ExecID contains "_TRDSUMM" → Log trade summary + cleanup trade from memory ExecType = F (fallback) → Log generic trade result ``` The order matters: ExecID suffix checks run before ExecType checks, because `_TRDEND` and `_TRDSUMM` both have `ExecType=F`. ### Trade Cleanup Paths A trade is removed from memory in any of these scenarios: | Trigger | Message | Condition | |---------|---------|-----------| | QuoteAck rejected | 35=CW | `QuoteAckStatus != ACCEPTED` | | QuoteResponse summary | 35=AJ | `QuoteRespID` ends with `_TRDSUMM` (flow 8.6) | | ExecutionReport summary | 35=8 | `ExecID` contains `_TRDSUMM` (flow 8.4) | The `loadActiveTrades` recovery function replays today's messages and applies the same cleanup rules to reconstruct accurate state on restart.