diff --git a/docs/FLOW_8_4_LIST_TRADING.md b/docs/FLOW_8_4_LIST_TRADING.md new file mode 100644 index 0000000..eb0731f --- /dev/null +++ b/docs/FLOW_8_4_LIST_TRADING.md @@ -0,0 +1,216 @@ +# 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) + +## 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 should log a warning — the quote may have been rejected. + +### 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. + +## 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 | Logs status | +| `handleQuoteResponse` | 35=AJ | Sends 35=AI (TRDREQACK) | +| `handleExecutionReport` | 35=8 | Sends 35=BN (ack) + routes by ExecID suffix | +| `sendQuoteStatusReport` | — | Builds 35=AI for QuoteRequest ack | +| `sendTradeRequestAck` | — | Builds 35=AI for TRDREQACK | +| `sendExecutionAck` | — | Builds 35=BN for ExecutionReport ack | + +### ExecID Routing in `handleExecutionReport` + +``` +ExecID contains "_LISTEND" → Log only, await trade result +ExecID contains "_TRDEND" → Log + cleanup trade from memory +ExecID contains "_TRDSUMM" → Log trade summary +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`.