// Package fix implements the QuickFIX initiator application. package fix import ( "log/slog" "quantex.com/qfixdpl/quickfix" "quantex.com/qfixdpl/quickfix/gen/fix50sp2/executionack" "quantex.com/qfixdpl/quickfix/gen/fix50sp2/executionreport" "quantex.com/qfixdpl/quickfix/gen/fix50sp2/quote" "quantex.com/qfixdpl/quickfix/gen/fix50sp2/quoteack" "quantex.com/qfixdpl/quickfix/gen/fix50sp2/quoterequest" "quantex.com/qfixdpl/quickfix/gen/fix50sp2/quoteresponse" "quantex.com/qfixdpl/quickfix/gen/tag" "quantex.com/qfixdpl/src/domain" ) type application struct { router *quickfix.MessageRouter notifier domain.Notifier onLogon func(quickfix.SessionID) onLogout func(quickfix.SessionID) onQuote func(quote.Quote, quickfix.SessionID) onQuoteRequest func(quoterequest.QuoteRequest, quickfix.SessionID) onQuoteAck func(quoteack.QuoteAck, quickfix.SessionID) onQuoteResponse func(quoteresponse.QuoteResponse, quickfix.SessionID) onExecutionReport func(executionreport.ExecutionReport, quickfix.SessionID) onExecutionAck func(executionack.ExecutionAck, quickfix.SessionID) onRawMessage func(direction string, msg *quickfix.Message) } func newApplication(n domain.Notifier) *application { app := &application{ router: quickfix.NewMessageRouter(), notifier: n, } app.router.AddRoute(quote.Route(app.handleQuote)) app.router.AddRoute(quoteack.Route(app.handleQuoteAck)) app.router.AddRoute(quoterequest.Route(app.handleQuoteRequest)) app.router.AddRoute(quoteresponse.Route(app.handleQuoteResponse)) app.router.AddRoute(executionack.Route(app.handleExecutionAck)) app.router.AddRoute(executionreport.Route(app.handleExecutionReport)) return app } func (a *application) OnCreate(sessionID quickfix.SessionID) { slog.Info("FIX session created", "session", sessionID.String()) } func (a *application) OnLogon(sessionID quickfix.SessionID) { slog.Info("FIX session logged on", "session", sessionID.String()) if a.onLogon != nil { a.onLogon(sessionID) } } func (a *application) OnLogout(sessionID quickfix.SessionID) { slog.Info("FIX session logged out", "session", sessionID.String()) go a.notifier.SendMsg(domain.MessageChannelError, "Logout", domain.MessageStatusWarning, nil) if a.onLogout != nil { a.onLogout(sessionID) } } func (a *application) ToAdmin(_ *quickfix.Message, _ quickfix.SessionID) {} func (a *application) ToApp(msg *quickfix.Message, _ quickfix.SessionID) error { if a.onRawMessage != nil { a.onRawMessage("OUT", msg) } return nil } func (a *application) FromAdmin(_ *quickfix.Message, _ quickfix.SessionID) quickfix.MessageRejectError { return nil } func (a *application) FromApp(msg *quickfix.Message, sessionID quickfix.SessionID) quickfix.MessageRejectError { beginString, _ := msg.Header.GetBytes(tag.BeginString) msgType, _ := msg.Header.GetBytes(tag.MsgType) var applVerID quickfix.FIXString msg.Header.GetField(tag.ApplVerID, &applVerID) slog.Info("FIX FromApp received", "beginString", string(beginString), "msgType", string(msgType), "applVerID", string(applVerID), "session", sessionID.String(), "rawMsg", msg.String(), ) if a.onRawMessage != nil { a.onRawMessage("IN", msg) } rejErr := a.router.Route(msg, sessionID) if rejErr != nil { slog.Error("FIX FromApp routing failed", "msgType", string(msgType), "error", rejErr.Error(), "isBusinessReject", rejErr.IsBusinessReject(), ) } return rejErr } func (a *application) handleQuoteRequest(msg quoterequest.QuoteRequest, sessionID quickfix.SessionID) quickfix.MessageRejectError { quoteReqID, err := msg.GetQuoteReqID() if err != nil { slog.Error("QuoteRequest missing QuoteReqID", "error", err.Error()) return err } slog.Info("QuoteRequest received", "quoteReqID", quoteReqID, "session", sessionID.String(), ) if a.onQuoteRequest != nil { a.onQuoteRequest(msg, sessionID) } return nil } func (a *application) handleQuoteAck(msg quoteack.QuoteAck, sessionID quickfix.SessionID) quickfix.MessageRejectError { quoteReqID, _ := msg.GetQuoteReqID() quoteID, _ := msg.GetQuoteID() status, _ := msg.GetQuoteAckStatus() text, _ := msg.GetText() slog.Info("QuoteAck received", "quoteReqID", quoteReqID, "quoteID", quoteID, "quoteAckStatus", status, "text", text, "session", sessionID.String(), ) if a.onQuoteAck != nil { a.onQuoteAck(msg, sessionID) } return nil } func (a *application) handleQuote(msg quote.Quote, sessionID quickfix.SessionID) quickfix.MessageRejectError { quoteID, err := msg.GetQuoteID() if err != nil { return err } symbol, _ := msg.GetSymbol() slog.Info("Quote received", "quoteID", quoteID, "symbol", symbol, "session", sessionID.String()) if a.onQuote != nil { a.onQuote(msg, sessionID) } return nil } func (a *application) handleQuoteResponse(msg quoteresponse.QuoteResponse, sessionID quickfix.SessionID) quickfix.MessageRejectError { quoteRespID, _ := msg.GetQuoteRespID() quoteReqID, _ := msg.GetQuoteReqID() quoteRespType, _ := msg.GetQuoteRespType() slog.Info("QuoteResponse received", "quoteRespID", quoteRespID, "quoteReqID", quoteReqID, "quoteRespType", quoteRespType, "session", sessionID.String(), ) if a.onQuoteResponse != nil { a.onQuoteResponse(msg, sessionID) } return nil } func (a *application) handleExecutionAck(msg executionack.ExecutionAck, sessionID quickfix.SessionID) quickfix.MessageRejectError { execID, _ := msg.GetExecID() orderID, _ := msg.GetOrderID() status, _ := msg.GetExecAckStatus() slog.Info("ExecutionAck received", "execID", execID, "orderID", orderID, "execAckStatus", status, "session", sessionID.String(), ) if a.onExecutionAck != nil { a.onExecutionAck(msg, sessionID) } return nil } func (a *application) handleExecutionReport(msg executionreport.ExecutionReport, sessionID quickfix.SessionID) quickfix.MessageRejectError { execID, _ := msg.GetExecID() orderID, _ := msg.GetOrderID() listID, _ := msg.GetListID() execType, _ := msg.GetExecType() ordStatus, _ := msg.GetOrdStatus() slog.Info("ExecutionReport received", "execID", execID, "orderID", orderID, "listID", listID, "execType", execType, "ordStatus", ordStatus, "session", sessionID.String(), ) if a.onExecutionReport != nil { a.onExecutionReport(msg, sessionID) } return nil }