fixes in quotes

This commit is contained in:
Ramiro Paz
2026-03-13 11:35:37 -03:00
parent 3998726100
commit fbcaac95f5
4 changed files with 69 additions and 39 deletions

View File

@ -325,14 +325,15 @@ func (cont *Controller) SendQuote(ctx *gin.Context) {
return return
} }
bidPx, offerPx, bidSize, offerSize, err := req.toDecimals() bidPx, offerPx, _, _, err := req.toDecimals()
if err != nil { if err != nil {
ctx.JSON(http.StatusBadRequest, HTTPError{Error: err.Error()}) ctx.JSON(http.StatusBadRequest, HTTPError{Error: err.Error()})
return return
} }
if err = cont.fixSender.SendQuote(req.ClOrdID, req.QuoteID, req.Symbol, req.Currency, bidPx, offerPx, bidSize, offerSize); err != nil { if err = cont.fixSender.SendQuote(
req.ClOrdID, req.QuoteID, req.Symbol, req.SecurityIDSource, req.Currency, bidPx, offerPx); err != nil {
ctx.JSON(http.StatusInternalServerError, HTTPError{Error: err.Error()}) ctx.JSON(http.StatusInternalServerError, HTTPError{Error: err.Error()})
return return

View File

@ -30,8 +30,7 @@ type QuoteRequest struct {
Currency string `json:"currency"` Currency string `json:"currency"`
BidPx string `json:"bid_px" binding:"required"` BidPx string `json:"bid_px" binding:"required"`
OfferPx string `json:"offer_px" binding:"required"` OfferPx string `json:"offer_px" binding:"required"`
BidSize string `json:"bid_size"` SecurityIDSource string `json:"security_id_source" binding:"required"`
OfferSize string `json:"offer_size"`
} }
func (r QuoteRequest) toDecimals() (bidPx, offerPx, bidSize, offerSize decimal.Decimal, err error) { func (r QuoteRequest) toDecimals() (bidPx, offerPx, bidSize, offerSize decimal.Decimal, err error) {
@ -45,19 +44,5 @@ func (r QuoteRequest) toDecimals() (bidPx, offerPx, bidSize, offerSize decimal.D
return bidPx, offerPx, bidSize, offerSize, fmt.Errorf("invalid offer_px: %w", err) return bidPx, offerPx, bidSize, offerSize, fmt.Errorf("invalid offer_px: %w", err)
} }
if r.BidSize != "" {
bidSize, err = decimal.NewFromString(r.BidSize)
if err != nil {
return bidPx, offerPx, bidSize, offerSize, fmt.Errorf("invalid bid_size: %w", err)
}
}
if r.OfferSize != "" {
offerSize, err = decimal.NewFromString(r.OfferSize)
if err != nil {
return bidPx, offerPx, bidSize, offerSize, fmt.Errorf("invalid offer_size: %w", err)
}
}
return bidPx, offerPx, bidSize, offerSize, nil return bidPx, offerPx, bidSize, offerSize, nil
} }

View File

@ -115,7 +115,12 @@ func (m *Manager) onLogout(sessionID quickfix.SessionID) {
} }
// SendQuote implements domain.FIXSender. // SendQuote implements domain.FIXSender.
func (m *Manager) SendQuote(clOrdID, quoteID, symbol, currency string, bidPx, offerPx, bidSize, offerSize decimal.Decimal) error { func (m *Manager) SendQuote(
clOrdID, quoteID, symbol string,
secIDSource string,
currency string,
bidPx, offerPx decimal.Decimal,
) error {
m.sessionsMu.RLock() m.sessionsMu.RLock()
var sessionID quickfix.SessionID var sessionID quickfix.SessionID
var ok bool var ok bool
@ -134,28 +139,34 @@ func (m *Manager) SendQuote(clOrdID, quoteID, symbol, currency string, bidPx, of
} }
q := quote.New( q := quote.New(
field.NewQuoteID(quoteID), field.NewQuoteID("NONREF"),
field.NewQuoteType(enum.QuoteType_INDICATIVE), field.NewQuoteType(enum.QuoteType_INDICATIVE),
field.NewTransactTime(time.Now()), field.NewTransactTime(time.Now()),
) )
q.SetSymbol(symbol) sIDSource := enum.SecurityIDSource_ISIN_NUMBER
if secIDSource == "1" {
sIDSource = enum.SecurityIDSource_CUSIP
}
q.SetSymbol("[N/A]")
q.SetSecurityID(symbol)
q.SetSecurityIDSource(sIDSource)
q.SetQuoteID(quoteID) q.SetQuoteID(quoteID)
if currency != "" { if currency != "" {
q.SetCurrency(currency) q.SetCurrency(currency)
} }
if !bidPx.IsZero() {
q.SetBidPx(bidPx, 8) q.SetBidPx(bidPx, 8)
q.SetSide(enum.Side_SELL)
} else {
q.SetOfferPx(offerPx, 8) q.SetOfferPx(offerPx, 8)
q.SetSide(enum.Side_BUY)
if !bidSize.IsZero() {
q.SetBidSize(bidSize, 8)
} }
if !offerSize.IsZero() { q.SetPriceType(enum.PriceType_PERCENTAGE)
q.SetOfferSize(offerSize, 8)
}
if err := quickfix.SendToTarget(q, sessionID); err != nil { if err := quickfix.SendToTarget(q, sessionID); err != nil {
err = tracerr.Errorf("error sending FIX quote: %s", err) err = tracerr.Errorf("error sending FIX quote: %s", err)
@ -177,18 +188,46 @@ func (m *Manager) handleQuoteRequest(msg quoterequest.QuoteRequest, sessionID qu
return return
} }
var symbol, currency string var (
symbol, currency string
side enum.Side
secIDSource enum.SecurityIDSource
)
relatedSyms, relErr := msg.GetNoRelatedSym() relatedSyms, relErr := msg.GetNoRelatedSym()
if relErr == nil && relatedSyms.Len() > 0 { if relErr == nil && relatedSyms.Len() > 0 {
sym := relatedSyms.Get(0) sym := relatedSyms.Get(0)
symbol, _ = sym.GetSymbol() symbol, _ = sym.GetSecurityID()
secIDSource, _ = sym.GetSecurityIDSource()
currency, _ = sym.GetCurrency() currency, _ = sym.GetCurrency()
side, _ = sym.GetSide()
} }
price := decimal.NewFromFloat(99.6) price := decimal.NewFromFloat(99.6)
bidPx, offerPx := decimal.Zero, decimal.Zero
if sendErr := m.SendQuote(quoteReqID, quoteReqID, symbol, currency, price, price, decimal.Zero, decimal.Zero); sendErr != nil { if side == enum.Side_BUY {
offerPx = price
} else {
bidPx = price
}
var sIDSource string
if secIDSource == enum.SecurityIDSource_ISIN_NUMBER {
sIDSource = "4"
} else {
sIDSource = "1"
}
if sendErr := m.SendQuote(
quoteReqID,
quoteReqID,
symbol,
sIDSource,
currency,
bidPx,
offerPx,
); sendErr != nil {
slog.Error("handleQuoteRequest: failed to send quote", "quoteReqID", quoteReqID, "error", sendErr.Error()) slog.Error("handleQuoteRequest: failed to send quote", "quoteReqID", quoteReqID, "error", sendErr.Error())
} }
} }

View File

@ -28,5 +28,10 @@ type OrderStore interface {
// FIXSender is the port for sending FIX messages back to clients. // FIXSender is the port for sending FIX messages back to clients.
type FIXSender interface { type FIXSender interface {
SendQuote(clOrdID, quoteID, symbol, currency string, bidPx, offerPx, bidSize, offerSize decimal.Decimal) error SendQuote(
clOrdID, quoteID, symbol string,
secIDSource string,
currency string,
bidPx, offerPx decimal.Decimal,
) error
} }