From 68238d309a52378a40fca4a091d7a10311fd811f Mon Sep 17 00:00:00 2001 From: Ramiro Paz Date: Wed, 6 May 2026 11:56:12 -0300 Subject: [PATCH] adding endpoints --- src/client/api/rest/controller.go | 59 +++++++- src/client/api/rest/model.go | 5 + src/client/api/rest/routes.go | 2 + src/client/api/rest/server.go | 3 + src/client/fix/manager.go | 240 ++++++++++++++++++++---------- src/client/fix/protocol.txt | 104 +++++++++++++ 6 files changed, 334 insertions(+), 79 deletions(-) create mode 100644 src/client/fix/protocol.txt diff --git a/src/client/api/rest/controller.go b/src/client/api/rest/controller.go index 913ebd0..fcd359c 100644 --- a/src/client/api/rest/controller.go +++ b/src/client/api/rest/controller.go @@ -4,12 +4,14 @@ import ( "fmt" "log/slog" "net/http" + "strings" "time" "github.com/gin-gonic/gin" "github.com/gomodule/redigo/redis" "github.com/sasha-s/go-deadlock" uuid "github.com/satori/go.uuid" + "github.com/shopspring/decimal" "quantex.com/qfixdpl/src/app" "quantex.com/qfixdpl/src/app/version" @@ -316,7 +318,8 @@ func (cont *Controller) GetLogs(ctx *gin.Context) { logs, err := cont.store.GetLogsByQuoteReqID(quoteReqID) if err != nil { - slog.Error("GetLogs: error fetching logs", "quoteReqID", quoteReqID, "error", err) + err = tracerr.Errorf("GetLogs: error fetching logs (quoteReqID=%s): %w", quoteReqID, err) + slog.Error(err.Error()) ctx.JSON(http.StatusInternalServerError, HTTPError{Error: "error fetching logs"}) return @@ -325,3 +328,57 @@ func (cont *Controller) GetLogs(ctx *gin.Context) { ctx.JSON(http.StatusOK, logs) } +// GetPendingQuoteRequests godoc +// @Summary List pending QuoteRequests +// @Description Returns all QuoteRequests received from TW that have not been quoted yet by the dealer +// @Tags fix +// @Produce json +// @Success 200 {array} domain.ListTrade +// @Router /qfixdpl/v1/quote-requests [get] +func (cont *Controller) GetPendingQuoteRequests(ctx *gin.Context) { + pending := cont.tradeProvider.GetPendingQuoteRequests() + ctx.JSON(http.StatusOK, pending) +} + +// SendQuote godoc +// @Summary Send a Quote for a pending QuoteRequest +// @Description Builds and sends a Quote (35=S) to TW for an existing QuoteRequest at the given price +// @Tags fix +// @Accept json +// @Produce json +// @Param body body SendQuoteRequest true "Quote to send" +// @Success 200 {object} Msg +// @Failure 400 {object} HTTPError +// @Failure 404 {object} HTTPError +// @Failure 409 {object} HTTPError +// @Failure 500 {object} HTTPError +// @Router /qfixdpl/v1/quotes [post] +func (cont *Controller) SendQuote(ctx *gin.Context) { + var req SendQuoteRequest + if err := ctx.ShouldBindJSON(&req); err != nil { + ctx.JSON(http.StatusBadRequest, HTTPError{Error: err.Error()}) + return + } + + price, err := decimal.NewFromString(req.Price) + if err != nil { + ctx.JSON(http.StatusBadRequest, HTTPError{Error: "invalid price: " + err.Error()}) + return + } + + if err := cont.tradeProvider.SendQuote(req.QuoteReqID, price); err != nil { + msg := err.Error() + switch { + case strings.Contains(msg, "not found"): + ctx.JSON(http.StatusNotFound, HTTPError{Error: "quoteReqID not found"}) + case strings.Contains(msg, "already sent"): + ctx.JSON(http.StatusConflict, HTTPError{Error: "quote already sent for this quoteReqID"}) + default: + ctx.JSON(http.StatusInternalServerError, HTTPError{Error: "failed to send quote"}) + } + return + } + + ctx.JSON(http.StatusOK, Msg{Text: "Quote sent"}) +} + diff --git a/src/client/api/rest/model.go b/src/client/api/rest/model.go index 6db83a4..3cc0891 100644 --- a/src/client/api/rest/model.go +++ b/src/client/api/rest/model.go @@ -18,3 +18,8 @@ type Session struct { Email string } +type SendQuoteRequest struct { + QuoteReqID string `json:"quote_req_id" binding:"required"` + Price string `json:"price" binding:"required" example:"99.6"` +} + diff --git a/src/client/api/rest/routes.go b/src/client/api/rest/routes.go index 3980d3a..2e8c7c6 100644 --- a/src/client/api/rest/routes.go +++ b/src/client/api/rest/routes.go @@ -23,6 +23,8 @@ func SetRoutes(api *API) { qfixdpl.GET("/health", cont.HealthCheck) qfixdpl.GET("/trades", cont.GetTrades) qfixdpl.GET("/trades/:quoteReqID/logs", cont.GetLogs) + qfixdpl.GET("/quote-requests", cont.GetPendingQuoteRequests) + qfixdpl.POST("/quotes", cont.SendQuote) backoffice := qfixdpl.Group("/backoffice") backoffice.Use(cont.BackOfficeUser) diff --git a/src/client/api/rest/server.go b/src/client/api/rest/server.go index db29f0f..8d744da 100644 --- a/src/client/api/rest/server.go +++ b/src/client/api/rest/server.go @@ -7,6 +7,7 @@ import ( "github.com/gin-gonic/gin" "github.com/gomodule/redigo/redis" + "github.com/shopspring/decimal" "quantex.com/qfixdpl/src/app" "quantex.com/qfixdpl/src/app/version" @@ -19,6 +20,8 @@ import ( // TradeProvider exposes trade data from the FIX manager. type TradeProvider interface { GetTrades() []domain.ListTrade + GetPendingQuoteRequests() []domain.ListTrade + SendQuote(quoteReqID string, price decimal.Decimal) error } const RedisMaxIdle = 3000 // In ms diff --git a/src/client/fix/manager.go b/src/client/fix/manager.go index d2eb6ad..d5a914f 100644 --- a/src/client/fix/manager.go +++ b/src/client/fix/manager.go @@ -38,6 +38,7 @@ type listTrade struct { Price decimal.Decimal OwnerTraderID string SessionID quickfix.SessionID + Quoted bool } // Manager wraps the QuickFIX initiator and implements domain.FIXSender. @@ -237,7 +238,7 @@ func (m *Manager) handleQuoteRequest(msg quoterequest.QuoteRequest, sessionID qu return } - // Step 1: Send QuoteStatusReport (35=AI) to acknowledge the inquiry. + // Send QuoteStatusReport (35=AI) to acknowledge the inquiry. if ackErr := m.sendQuoteStatusReport(quoteReqID, ownerTraderID, sessionID); ackErr != nil { ackErr = tracerr.Errorf("handleQuoteRequest: failed to send QuoteStatusReport (quoteReqID=%s): %w", quoteReqID, ackErr) slog.Error(ackErr.Error()) @@ -245,63 +246,12 @@ func (m *Manager) handleQuoteRequest(msg quoterequest.QuoteRequest, sessionID qu } slog.Info("QuoteStatusReport sent", "quoteReqID", quoteReqID) - // Step 2: Build and send Quote (35=S) with price. - price := decimal.NewFromFloat(99.6) - sIDSource := enum.SecurityIDSource_ISIN_NUMBER if secIDSource == enum.SecurityIDSource_CUSIP { sIDSource = enum.SecurityIDSource_CUSIP } - quoteID := quoteReqID - q := quote.New( - field.NewQuoteID(quoteID), - field.NewQuoteType(enum.QuoteType_SEND_QUOTE), - field.NewTransactTime(time.Now()), - ) - - q.SetSymbol("[N/A]") - q.SetSecurityID(symbol) - q.SetSecurityIDSource(sIDSource) - q.SetQuoteReqID(quoteReqID) - - if currency != "" { - q.SetCurrency(currency) - } - - if !orderQty.IsZero() { - q.SetOrderQty(orderQty, 0) - } - - if settlDate != "" { - q.SetSettlDate(settlDate) - } - - q.SetPrice(price, 8) - - if side == enum.Side_BUY { - q.SetOfferPx(price, 8) - q.SetSide(enum.Side_BUY) - } else { - q.SetBidPx(price, 8) - q.SetSide(enum.Side_SELL) - } - - q.SetPriceType(enum.PriceType_PERCENTAGE) - - if ownerTraderID != "" { - q.SetOwnerTraderID(ownerTraderID) - } - - if sendErr := quickfix.SendToTarget(q, sessionID); sendErr != nil { - sendErr = tracerr.Errorf("handleQuoteRequest: failed to send quote (quoteReqID=%s): %w", quoteReqID, sendErr) - slog.Error(sendErr.Error()) - return - } - - slog.Info("Quote sent", "quoteReqID", quoteReqID, "quoteID", quoteID, "symbol", symbol) - - // Store trade state for subsequent steps. + // Store trade state as pending; Quote (35=S) is sent later via REST endpoint. m.tradesMu.Lock() m.trades[quoteReqID] = &listTrade{ QuoteReqID: quoteReqID, @@ -312,9 +262,9 @@ func (m *Manager) handleQuoteRequest(msg quoterequest.QuoteRequest, sessionID qu Side: side, OrderQty: orderQty, SettlDate: settlDate, - Price: price, OwnerTraderID: ownerTraderID, SessionID: sessionID, + Quoted: false, } m.tradesMu.Unlock() @@ -327,18 +277,6 @@ func (m *Manager) handleQuoteRequest(msg quoterequest.QuoteRequest, sessionID qu "QuoteStatus": string(enum.QuoteStatus_ACCEPTED), "OwnerTraderID": ownerTraderID, })) - - // Persist outgoing Quote. - m.persistMessage(quoteReqID, buildOutgoingMessageJSON("S", quoteReqID, map[string]interface{}{ - "QuoteReqID": quoteReqID, - "QuoteID": quoteID, - "Symbol": symbol, - "Side": string(side), - "Price": price.String(), - "OrderQty": orderQty.String(), - "Currency": currency, - "SettlDate": settlDate, - })) } // handleQuoteAck handles an incoming QuoteAck (35=CW). @@ -479,23 +417,157 @@ func (m *Manager) GetTrades() []domain.ListTrade { trades := make([]domain.ListTrade, 0, len(m.trades)) for _, t := range m.trades { - trades = append(trades, domain.ListTrade{ - QuoteReqID: t.QuoteReqID, - ListID: t.ListID, - Symbol: t.Symbol, - SecurityIDSrc: string(t.SecurityIDSrc), - Currency: t.Currency, - Side: string(t.Side), - OrderQty: t.OrderQty.String(), - SettlDate: t.SettlDate, - Price: t.Price.String(), - OwnerTraderID: t.OwnerTraderID, - }) + trades = append(trades, toDomainListTrade(t)) } return trades } +// GetPendingQuoteRequests returns trades that have received a QuoteRequest but not yet been quoted by the dealer. +func (m *Manager) GetPendingQuoteRequests() []domain.ListTrade { + m.tradesMu.RLock() + defer m.tradesMu.RUnlock() + + pending := make([]domain.ListTrade, 0) + for _, t := range m.trades { + if !t.Quoted { + pending = append(pending, toDomainListTrade(t)) + } + } + + return pending +} + +func toDomainListTrade(t *listTrade) domain.ListTrade { + return domain.ListTrade{ + QuoteReqID: t.QuoteReqID, + ListID: t.ListID, + Symbol: t.Symbol, + SecurityIDSrc: string(t.SecurityIDSrc), + Currency: t.Currency, + Side: string(t.Side), + OrderQty: t.OrderQty.String(), + SettlDate: t.SettlDate, + Price: t.Price.String(), + OwnerTraderID: t.OwnerTraderID, + } +} + +// SendQuote builds and sends a Quote (35=S) for an existing pending QuoteRequest at the given price. +func (m *Manager) SendQuote(quoteReqID string, price decimal.Decimal) error { + m.tradesMu.Lock() + t, ok := m.trades[quoteReqID] + if !ok { + m.tradesMu.Unlock() + err := tracerr.Errorf("SendQuote: quoteReqID %s not found", quoteReqID) + slog.Error(err.Error()) + return err + } + if t.Quoted { + m.tradesMu.Unlock() + err := tracerr.Errorf("SendQuote: quote already sent for quoteReqID %s", quoteReqID) + slog.Error(err.Error()) + return err + } + + sessionID := t.SessionID + if sessionID == (quickfix.SessionID{}) { + sessionID = m.anyActiveSessionID() + if sessionID == (quickfix.SessionID{}) { + m.tradesMu.Unlock() + err := tracerr.Errorf("SendQuote: no active FIX session for quoteReqID %s", quoteReqID) + slog.Error(err.Error()) + return err + } + } + + symbol := t.Symbol + sIDSource := t.SecurityIDSrc + currency := t.Currency + side := t.Side + orderQty := t.OrderQty + settlDate := t.SettlDate + ownerTraderID := t.OwnerTraderID + m.tradesMu.Unlock() + + quoteID := quoteReqID + q := quote.New( + field.NewQuoteID(quoteID), + field.NewQuoteType(enum.QuoteType_SEND_QUOTE), + field.NewTransactTime(time.Now()), + ) + + q.SetSymbol("[N/A]") + q.SetSecurityID(symbol) + q.SetSecurityIDSource(sIDSource) + q.SetQuoteReqID(quoteReqID) + + if currency != "" { + q.SetCurrency(currency) + } + + if !orderQty.IsZero() { + q.SetOrderQty(orderQty, 0) + } + + if settlDate != "" { + q.SetSettlDate(settlDate) + } + + q.SetPrice(price, 8) + + if side == enum.Side_BUY { + q.SetOfferPx(price, 8) + q.SetSide(enum.Side_BUY) + } else { + q.SetBidPx(price, 8) + q.SetSide(enum.Side_SELL) + } + + q.SetPriceType(enum.PriceType_PERCENTAGE) + + if ownerTraderID != "" { + q.SetOwnerTraderID(ownerTraderID) + } + + if sendErr := quickfix.SendToTarget(q, sessionID); sendErr != nil { + sendErr = tracerr.Errorf("SendQuote: failed to send quote (quoteReqID=%s): %w", quoteReqID, sendErr) + slog.Error(sendErr.Error()) + return sendErr + } + + m.tradesMu.Lock() + if t, ok := m.trades[quoteReqID]; ok { + t.Price = price + t.Quoted = true + } + m.tradesMu.Unlock() + + slog.Info("Quote sent", "quoteReqID", quoteReqID, "quoteID", quoteID, "symbol", symbol, "price", price.String()) + + m.persistMessage(quoteReqID, buildOutgoingMessageJSON("S", quoteReqID, map[string]interface{}{ + "QuoteReqID": quoteReqID, + "QuoteID": quoteID, + "Symbol": symbol, + "Side": string(side), + "Price": price.String(), + "OrderQty": orderQty.String(), + "Currency": currency, + "SettlDate": settlDate, + })) + + return nil +} + +func (m *Manager) anyActiveSessionID() quickfix.SessionID { + m.sessionsMu.RLock() + defer m.sessionsMu.RUnlock() + for _, s := range m.sessions { + return s + } + return quickfix.SessionID{} +} + // handleRawMessage persists raw FIX message strings to the logs table. func (m *Manager) handleRawMessage(direction string, msg *quickfix.Message) { quoteReqID := extractIdentifier(msg) @@ -557,6 +629,10 @@ func (m *Manager) loadActiveTrades() error { trade.Symbol = v } + if v, ok := body["SecurityIDSource"].(string); ok { + trade.SecurityIDSrc = enum.SecurityIDSource(v) + } + if v, ok := body["Currency"].(string); ok { trade.Currency = v } @@ -579,6 +655,14 @@ func (m *Manager) loadActiveTrades() error { activeTrades[msg.QuoteReqID] = trade + case "S": // Outgoing Quote — dealer has already quoted this trade + if t, ok := activeTrades[msg.QuoteReqID]; ok { + t.Quoted = true + if v, ok := msg.JMessage.Body["Price"].(string); ok { + t.Price, _ = decimal.NewFromString(v) + } + } + case "CW": // QuoteAck — if rejected, trade is dead body := msg.JMessage.Body quoteAckStatus, _ := body["QuoteAckStatus"].(string) diff --git a/src/client/fix/protocol.txt b/src/client/fix/protocol.txt new file mode 100644 index 0000000..5be158d --- /dev/null +++ b/src/client/fix/protocol.txt @@ -0,0 +1,104 @@ +Step 1 +Direction: ← QuoteRequest (R) +Comentary: Tradeweb sends trade information to dealer. +FIX Message: 8=FIXT.1.1 9=869 35=R 34=2 49=TRADEWEB 52=20160401-16:53:19.992 56=TW1_CORI_TEST_12345_DLRDPL 131=LST_20160401_TW1_CORI_NY302485.18_1 146=1 55=AMXLMM 2.375 09/08/16 48=02364WBC8 22=1 460=12 167=CORP 762=REGCORIPRC 541=20160908 225=20110908 470=MX 223=2.375 106=AMERICA MOVIL SAB DE CV 54=2 38=1000000 64=20160406 15=USD 6110=Comms 60=20160401-16:53:19 662=99.98046875 22570=0.76 663=1 699=912828UR9 761=1 423=6 44=-999999 5023=0.001000 66=NY302485.18 6847=1 75=20160401 464=Y 20086=1 20074=Y 20075=Y 20077=LatAm Comms 20078=pddealer 20079=60 20081=60 20090=60 20072=60 20098=60 5745=1 20073=RFQ 20076=Y 20156=N 20130=2000000000 20138=JPM,MER 20175=20120308 2115=0 22630=0 20265=LatAm 453=3 448=emack 447=C 452=3 802=3 523=Dev Test 803=2 523=NY 803=25 523=USA 803=4000 448=Tradeweb 447=C 452=1 802=1 523=Tradeweb0001 803=4002 448=DTCC 447=C 452=4 5114=2 5113=1 20169=A2 5113=0 20169=A- 10=121 + +Step 2 +Direction: QuoteStatusReport (AI) → +Comentary: Dealer acknowledges trade. +FIX Message: 8=FIXT.1.1 9=198 35=AI 49=TW1_CORI_TEST_12345_DLRDPL 56=TRADEWEB 34=2 52=20160401-16:53:19.988 131=LST_20160401_TW1_CORI_NY302485.18_1 117=LST_20160401_TW1_CORI_NY302485.18_1 55=[N/A] 60=20160401-16:53:19.988 297=0 10=106 + +Step 3 +Direction: Quote (S) → +Comentary: Dealer sends quote. +FIX Message: 8=FIXT.1.1 9=231 35=S 49=TW1_CORI_TEST_12345_DLRDPL 56=TRADEWEB 34=3 52=20160401-16:53:19.990 117=LST_20160401_TW1_CORI_NY302485.18_1 55=[N/A] 54=2 131=LST_20160401_TW1_CORI_NY302485.18_1 537=211 6153=emackdlr 44=.99 423=6 60=20160401-16:53:19.990 10=247 + +Step 4 +Direction: ← QuoteAck (CW) +Comentary: Tradeweb acknowledges quote. +FIX Message: 8=FIXT.1.1 9=186 35=CW 34=3 49=TRADEWEB 52=20160401-16:53:25.102 56=TW1_CORI_TEST_12345_DLRDPL 60=20160401-16:53:25 117=LST_20160401_TW1_CORI_NY302485.18_1 131=LST_20160401_TW1_CORI_NY302485.18_1 1865=1 10=154 + +Step 5 +Direction: Quote (S) → +Comentary: Dealer sends quote. +FIX Message: 8=FIXT.1.1 9=239 35=S 49=TW1_CORI_TEST_12345_DLRDPL 56=TRADEWEB 34=4 52=20160401-16:53:19.990 117=LST_20160401_TW1_CORI_NY302485.18_1 55=[N/A] 54=2 131=LST_20160401_TW1_CORI_NY302485.18_1 537=211 20087=5 6153=emackdlr 44=.97 423=6 60=20160401-16:53:19.990 10=114 + +Step 6 +Direction: ← QuoteAck (CW) +Comentary: Tradeweb acknowledges quote. +FIX Message: 8=FIXT.1.1 9=186 35=CW 34=4 49=TRADEWEB 52=20160401-16:53:30.055 56=TW1_CORI_TEST_12345_DLRDPL 60=20160401-16:53:30 117=LST_20160401_TW1_CORI_NY302485.18_1 131=LST_20160401_TW1_CORI_NY302485.18_1 1865=1 10=154 + +Step 7 +Direction: Quote (S) → +Comentary: Dealer sends quote. +FIX Message: 8=FIXT.1.1 9=239 35=S 49=TW1_CORI_TEST_12345_DLRDPL 56=TRADEWEB 34=6 52=20160401-16:53:19.990 117=LST_20160401_TW1_CORI_NY302485.18_1 55=[N/A] 54=2 131=LST_20160401_TW1_CORI_NY302485.18_1 537=211 20087=5 6153=emackdlr 44=.98 423=6 60=20160401-16:53:19.990 10=117 + +Step 8 +Direction: ← QuoteAck (CW) +Commentary: Tradeweb acknowledges quote. +FIX Message: 8=FIXT.1.1 9=186 35=CW 34=5 49=TRADEWEB 52=20160401-16:53:35.071 56=TW1_CORI_TEST_12345_DLRDPL 60=20160401-16:53:35 117=LST_20160401_TW1_CORI_NY302485.18_1 131=LST_20160401_TW1_CORI_NY302485.18_1 1865=1 10=163 + +Step 9 +Direction: ← ExecutionReport (8) +Commentary: Tradeweb notifies dealer that the list trading (Due In time) has ended. ExecType=A +FIX Message: 8=FIXT.1.1 9=289 35=8 34=7 49=TRADEWEB 52=20160401-16:54:19.463 56=TW1_CORI_TEST_12345_DLRDPL 6=0 11=LST_20160401_TW1_CORI_NY302485.18_1 14=0 17=LST_20160401_TW1_CORI_NY302485.18_1_LISTEND-125419.463 37=LST_20160401_TW1_CORI_NY302485.18_1 39=D 55=[N/A] 60=20160401-16:54:19 75=20160401 150=I 151=0 20086=1 10=073 + +Step 10 +Direction: ExecutionAck (BN) → +Commentary: Dealer acknowledges ExecutionReport message. +FIX Message: 8=FIXT.1.1 9=255 35=BN 49=TW1_CORI_TEST_12345_DLRDPL 56=TRADEWEB 34=8 52=20160401-16:54:19.448 1036=1 37=LST_20160401_TW1_CORI_NY302485.18_1 11=LST_20160401_TW1_CORI_NY302485.18_1 17=LST_20160401_TW1_CORI_NY302485.18_1_LISTEND-125419.448 55=[N/A] 60=20160401-16:54:19.448 10=119 + +Step 11 +Direction: ← QuoteResponse (AJ) +Commentary: Customer lifts after good-for time expires. Tradeweb informs dealer customer lift. +FIX Message: 8=FIXT.1.1 9=431 35=AJ 34=10 49=TRADEWEB 52=20160401-16:55:32.855 56=TW1_CORI_TEST_12345_DLRDPL 11=LST_20160401_TW1_CORI_NY302485.18_1 22=1 38=1000000 44=0.98 48=02364WBC8 54=2 55=[N/A] 60=20160401-16:55:32 117=LST_20160401_TW1_CORI_NY302485.18_1 131=LST_20160401_TW1_CORI_NY302485.18_1 423=6 662=99.98046875 693=LST_20160401_TW1_CORI_NY302485.18_1_TRDREQ 694=1 2115=1 20074=Y 20075=N 20076=N 20079=60 20082=60 20156=N 22570=0.760289080299 22630=0 10=183 + +Step 12 +Direction: QuoteStatusReport (AI) → +Commentary: Dealer acknowledges customer’s trade acceptance. +FIX Message: 8=FIXT.1.1 9=206 35=AI 49=TW1_CORI_TEST_12345_DLRDPL 56=TRADEWEB 34=12 52=20160401-16:55:32.849 693=LST_20160401_TW1_CORI_NY302485.18_1_TRDREQ 131=LST_20160401_TW1_CORI_NY302485.18_1 55=[N/A] 60=20160401-16:55:32.849 297=0 10=189 + +Step 13 +Direction: ← ExecutionReport (8) +Commentary: Tradeweb notifies the dealer that list has ended +FIX Message: incoming8=FIXT.1.1 9=290 35=8 34=11 49=TRADEWEB 52=20160401-16:55:32.855 56=TW1_CORI_TEST_12345_DLRDPL 6=0 11=LST_20160401_TW1_CORI_NY302485.18_1 14=0 17=LST_20160401_TW1_CORI_NY302485.18_1_LISTEND-125532.855 37=LST_20160401_TW1_CORI_NY302485.18_1 39=A 55=[N/A] 60=20160401-16:55:32 75=20160401 150=A 151=0 20086=1 10=095 + +Step 14 +Direction: ExecutionAck (BN) → +Commentary: Dealer acknowledges ExecutionReport message. +FIX Message: 8=FIXT.1.1 9=256 35=BN 49=TW1_CORI_TEST_12345_DLRDPL 56=TRADEWEB 34=13 52=20160401-16:55:32.852 1036=1 37=LST_20160401_TW1_CORI_NY302485.18_1 11=LST_20160401_TW1_CORI_NY302485.18_1 17=LST_20160401_TW1_CORI_NY302485.18_1_LISTEND-125532.852 55=[N/A] 60=20160401-16:55:32.852 10=155 + +Step 15 +Direction: ExecutionReport (8) → +Commentary: Dealer accepts – re-quote is not allowed. ExecType=F, OrdStatus=Filled +FIX Message: 8=FIXT.1.1 9=283 35=8 34=14 49=TW1_CORI_TEST_12345_DLRDPL 52=20160401-16:55:32.850 56=TRADEWEB 6=0 14=0 17=LST_20160401_TW1_CORI_NY302485.18_1 37=LST_20160401_TW1_CORI_NY302485.18_1 39=2 54=2 55=[N/A] 150=F 151=0 6153=LST_20160401_TW1_CORI_NY302485.18_1 22631=POSTTRADE_STRING 22632=POSTTRADE_STRING 10=155 + +Step 16 +Direction: ← ExecutionAck (BN) +Commentary: Tradeweb acknowledges ExecutionReport +FIX Message: 8=FIXT.1.1 9=233 35=BN 34=12 49=TRADEWEB 52=20160401-16:55:37.917 56=TW1_CORI_TEST_12345_DLRDPL 11=LST_20160401_TW1_CORI_NY302485.18_1 17=LST_20160401_TW1_CORI_NY302485.18_1 37=LST_20160401_TW1_CORI_NY302485.18_1 55=[N/A] 60=20160401-16:55:37 1036=1 10=051 + +Step 17 +Direction: ← ExecutionReport (8) +Commentary: Tradeweb informs Dealer of outcome of a list trade item. OrdStatus=Filled, ExecType=Trade +FIX Message: 8=FIXT.1.1 9=337 35=8 34=13 49=TRADEWEB 52=20160401-16:55:37.917 56=TW1_CORI_TEST_12345_DLRDPL 6=0 11=LST_20160401_TW1_CORI_NY302485.18_1 14=0 17=LST_20160401_TW1_CORI_NY302485.18_1_TRDEND-125537.917 37=LST_20160401_TW1_CORI_NY302485.18_1 39=2 44=0.98 54=2 55=[N/A] 60=20160401-16:55:37 75=20160401 150=F 151=0 423=6 662=99.98046875 22570=0.760289080299 10=067 + +Step 18 +Direction: ExecutionAck (BN) → +Comentary: Dealer acknowledges ExecutionReport message. +FIX Message: 8=FIXT.1.1 9=255 35=BN 49=TW1_CORI_TEST_12345_DLRDPL 56=TRADEWEB 34=15 52=20160401-16:55:37.910 1036=1 37=LST_20160401_TW1_CORI_NY302485.18_1 11=LST_20160401_TW1_CORI_NY302485.18_1 17=LST_20160401_TW1_CORI_NY302485.18_1_TRDEND-125537.917 55=[N/A] 60=20160401-16:55:37.910 10=078 + +Step 19 +Direction: +Comentary: Autospot at Tradeweb Treasury composite. +FIX Message: + +Step 20 +Direction: ← ExecutionReport (8) +Comentary: Tradeweb sends ExecutionReport message to confirm final outcome of list trade item including applicable cover quote, spot, settlement money information, etc. OrdStatus=Filled, ExecType=Order Status TradeSummary = Y +FIX Message: 8=FIXT.1.1 9=733 35=8 34=14 49=TRADEWEB 52=20160401-16:55:40.292 56=TW1_CORI_TEST_12345_DLRDPL 6=0 11=LST_20160401_TW1_CORI_NY302485.18_1 14=0 17=LST_20160401_TW1_CORI_NY302485.18_1_TRDSUMM-125540.277 22=1 37=LST_20160401_TW1_CORI_NY302485.18_1 38=1000000 39=2 44=0.98 48=02364WBC8 54=2 55=[N/A] 60=20160401-16:55:40 64=20160406 75=20160401 150=F 151=0 167=CORP 236=1.74 423=6 453=2 448=emack 447=C 452=3 802=3 523=Dev Test 803=2 523=NY 803=25 523=USA 803=4000 448=Tradeweb 447=C 452=1 802=2 523=Tradeweb0001 803=4002 523=YES 803=4003 526=TRD_20160401_TW1_CORI_23 662=99.98046875 1003=20160401.TW1.CORI.23 6153=emackdlr 6731=20160401.TW1.CORI.23 20115=100.265 20250=225000 22570=0.76 22630=0 22631=POSTTRADE_STRING 22634=160401.DLRX.TRSY.120 22636=Y 10=239 + +Step 21 +Direction: ExecutionAck (BN) → +Comentary: Dealer acknowledges ExecutionReport message. +FIX Message: 8=FIXT.1.1 9=256 35=BN 49=TW1_CORI_TEST_12345_DLRDPL 56=TRADEWEB 34=16 52=20160401-16:55:40.287 1036=1 37=LST_20160401_TW1_CORI_NY302485.18_1 11=LST_20160401_TW1_CORI_NY302485.18_1 17=LST_20160401_TW1_CORI_NY302485.18_1_TRDSUMM-125540.277 55=[N/A] 60=20160401-16:55:40.287 10=182 \ No newline at end of file