adding project logic
This commit is contained in:
141
src/client/fix/manager.go
Normal file
141
src/client/fix/manager.go
Normal file
@ -0,0 +1,141 @@
|
||||
package fix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"quantex.com/qfixdpl/quickfix"
|
||||
"quantex.com/qfixdpl/quickfix/gen/enum"
|
||||
"quantex.com/qfixdpl/quickfix/gen/field"
|
||||
"quantex.com/qfixdpl/quickfix/gen/fix50sp2/quote"
|
||||
"quantex.com/qfixdpl/src/app"
|
||||
"quantex.com/qfixdpl/src/domain"
|
||||
)
|
||||
|
||||
// Manager wraps the QuickFIX acceptor and implements domain.FIXSender.
|
||||
type Manager struct {
|
||||
acceptor *quickfix.Acceptor
|
||||
app *application
|
||||
sessionsMu sync.RWMutex
|
||||
sessions map[string]quickfix.SessionID
|
||||
orderStore domain.OrderStore
|
||||
notify domain.Notifier
|
||||
cfg app.FIXConfig
|
||||
}
|
||||
|
||||
func NewManager(cfg app.FIXConfig, orderStore domain.OrderStore, notify domain.Notifier) *Manager {
|
||||
return &Manager{
|
||||
sessions: make(map[string]quickfix.SessionID),
|
||||
orderStore: orderStore,
|
||||
notify: notify,
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) Start() error {
|
||||
fixApp := newApplication(m.orderStore)
|
||||
fixApp.onLogon = m.onLogon
|
||||
fixApp.onLogout = m.onLogout
|
||||
m.app = fixApp
|
||||
|
||||
f, err := os.Open(m.cfg.SettingsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening FIX settings file %q: %w", m.cfg.SettingsFile, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
settings, err := quickfix.ParseSettings(f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing FIX settings: %w", err)
|
||||
}
|
||||
|
||||
storeFactory := quickfix.NewMemoryStoreFactory()
|
||||
logFactory := quickfix.NewNullLogFactory()
|
||||
|
||||
acceptor, err := quickfix.NewAcceptor(fixApp, storeFactory, settings, logFactory)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating FIX acceptor: %w", err)
|
||||
}
|
||||
|
||||
m.acceptor = acceptor
|
||||
|
||||
if err = m.acceptor.Start(); err != nil {
|
||||
return fmt.Errorf("starting FIX acceptor: %w", err)
|
||||
}
|
||||
|
||||
slog.Info("FIX acceptor started", "settings", m.cfg.SettingsFile)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Stop() {
|
||||
if m.acceptor != nil {
|
||||
m.acceptor.Stop()
|
||||
slog.Info("FIX acceptor stopped")
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) onLogon(sessionID quickfix.SessionID) {
|
||||
m.sessionsMu.Lock()
|
||||
m.sessions[sessionID.String()] = sessionID
|
||||
m.sessionsMu.Unlock()
|
||||
}
|
||||
|
||||
func (m *Manager) onLogout(sessionID quickfix.SessionID) {
|
||||
m.sessionsMu.Lock()
|
||||
delete(m.sessions, sessionID.String())
|
||||
m.sessionsMu.Unlock()
|
||||
}
|
||||
|
||||
// SendQuote implements domain.FIXSender.
|
||||
func (m *Manager) SendQuote(clOrdID, quoteID, symbol, currency string, bidPx, offerPx, bidSize, offerSize decimal.Decimal) error {
|
||||
order, ok := m.orderStore.GetOrderByClOrdID(clOrdID)
|
||||
if !ok {
|
||||
return fmt.Errorf("order not found: %s", clOrdID)
|
||||
}
|
||||
|
||||
m.sessionsMu.RLock()
|
||||
sessionID, ok := m.sessions[order.SessionID]
|
||||
m.sessionsMu.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("session not active for order %s (session %s)", clOrdID, order.SessionID)
|
||||
}
|
||||
|
||||
q := quote.New(
|
||||
field.NewQuoteID(quoteID),
|
||||
field.NewQuoteType(enum.QuoteType_INDICATIVE),
|
||||
field.NewTransactTime(time.Now()),
|
||||
)
|
||||
|
||||
q.SetSymbol(symbol)
|
||||
q.SetQuoteID(quoteID)
|
||||
|
||||
if currency != "" {
|
||||
q.SetCurrency(currency)
|
||||
}
|
||||
|
||||
q.SetBidPx(bidPx, 8)
|
||||
q.SetOfferPx(offerPx, 8)
|
||||
|
||||
if !bidSize.IsZero() {
|
||||
q.SetBidSize(bidSize, 8)
|
||||
}
|
||||
|
||||
if !offerSize.IsZero() {
|
||||
q.SetOfferSize(offerSize, 8)
|
||||
}
|
||||
|
||||
if err := quickfix.SendToTarget(q, sessionID); err != nil {
|
||||
return fmt.Errorf("sending FIX quote: %w", err)
|
||||
}
|
||||
|
||||
slog.Info("Quote sent", "clOrdID", clOrdID, "quoteID", quoteID, "symbol", symbol)
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user