all: integrate metrics into stream book

This commit is contained in:
c9s 2024-08-24 13:45:01 +08:00
parent b0cc009d67
commit afac81a3e8
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
14 changed files with 97 additions and 29 deletions

View File

@ -48,7 +48,7 @@ var rootCmd = &cobra.Command{
stream.SetPublicOnly() stream.SetPublicOnly()
stream.Subscribe(types.BookChannel, symbol, types.SubscribeOptions{}) stream.Subscribe(types.BookChannel, symbol, types.SubscribeOptions{})
streamBook := types.NewStreamBook(symbol) streamBook := types.NewStreamBook(symbol, exchange.Name())
streamBook.BindStream(stream) streamBook.BindStream(stream)
go func() { go func() {

View File

@ -464,7 +464,7 @@ func (session *ExchangeSession) initSymbol(ctx context.Context, environ *Environ
for _, sub := range session.Subscriptions { for _, sub := range session.Subscriptions {
switch sub.Channel { switch sub.Channel {
case types.BookChannel: case types.BookChannel:
book := types.NewStreamBook(sub.Symbol) book := types.NewStreamBook(sub.Symbol, session.ExchangeName)
book.BindStream(session.MarketDataStream) book.BindStream(session.MarketDataStream)
session.orderBooks[sub.Symbol] = book session.orderBooks[sub.Symbol] = book

View File

@ -54,7 +54,7 @@ var orderbookCmd = &cobra.Command{
return fmt.Errorf("session %s not found", sessionName) return fmt.Errorf("session %s not found", sessionName)
} }
orderBook := types.NewMutexOrderBook(symbol) orderBook := types.NewMutexOrderBook(symbol, session.Exchange.Name())
s := session.Exchange.NewStream() s := session.Exchange.NewStream()
s.SetPublicOnly() s.SetPublicOnly()

View File

@ -2,6 +2,7 @@ package audacitymaker
import ( import (
"context" "context"
"github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/datatype/floats" "github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
@ -38,7 +39,7 @@ func (s *PerTrade) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
position := orderExecutor.Position() position := orderExecutor.Position()
symbol := position.Symbol symbol := position.Symbol
// ger best bid/ask, not used yet // ger best bid/ask, not used yet
s.StreamBook = types.NewStreamBook(symbol) s.StreamBook = types.NewStreamBook(symbol, session.ExchangeName)
s.StreamBook.BindStream(session.MarketDataStream) s.StreamBook.BindStream(session.MarketDataStream)
// use queue to do time-series rolling // use queue to do time-series rolling
@ -138,7 +139,9 @@ func (s *PerTrade) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
} }
} }
func (s *PerTrade) placeOrder(ctx context.Context, side types.SideType, quantity fixedpoint.Value, price fixedpoint.Value, symbol string) error { func (s *PerTrade) placeOrder(
ctx context.Context, side types.SideType, quantity fixedpoint.Value, price fixedpoint.Value, symbol string,
) error {
market, _ := s.session.Market(symbol) market, _ := s.session.Market(symbol)
_, err := s.orderExecutor.SubmitOrders(ctx, types.SubmitOrder{ _, err := s.orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
Symbol: symbol, Symbol: symbol,

View File

@ -98,7 +98,7 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.ExchangeSession) error { func (s *Strategy) Run(ctx context.Context, _ bbgo.OrderExecutor, session *bbgo.ExchangeSession) error {
s.Strategy.Initialize(ctx, s.Environment, session, s.Market, ID, s.InstanceID()) s.Strategy.Initialize(ctx, s.Environment, session, s.Market, ID, s.InstanceID())
s.book = types.NewStreamBook(s.Symbol) s.book = types.NewStreamBook(s.Symbol, session.Exchange.Name())
s.book.BindStream(session.MarketDataStream) s.book.BindStream(session.MarketDataStream)
s.liquidityOrderBook = bbgo.NewActiveOrderBook(s.Symbol) s.liquidityOrderBook = bbgo.NewActiveOrderBook(s.Symbol)

View File

@ -513,7 +513,9 @@ func notifyUsdPnL(profit fixedpoint.Value) {
bbgo.Notify(title) bbgo.Notify(title)
} }
func (s *Strategy) iocOrderExecution(ctx context.Context, session *bbgo.ExchangeSession, orders [3]types.SubmitOrder, ratio float64) (types.OrderSlice, error) { func (s *Strategy) iocOrderExecution(
ctx context.Context, session *bbgo.ExchangeSession, orders [3]types.SubmitOrder, ratio float64,
) (types.OrderSlice, error) {
service, ok := session.Exchange.(types.ExchangeOrderQueryService) service, ok := session.Exchange.(types.ExchangeOrderQueryService)
if !ok { if !ok {
return nil, errors.New("exchange does not support ExchangeOrderQueryService") return nil, errors.New("exchange does not support ExchangeOrderQueryService")
@ -700,7 +702,9 @@ func (s *Strategy) waitWebSocketOrderDone(ctx context.Context, orderID uint64, t
} }
} }
func (s *Strategy) waitOrdersAndCollectTrades(ctx context.Context, service types.ExchangeOrderQueryService, createdOrders types.OrderSlice) (map[uint64][]types.Trade, types.OrderSlice, error) { func (s *Strategy) waitOrdersAndCollectTrades(
ctx context.Context, service types.ExchangeOrderQueryService, createdOrders types.OrderSlice,
) (map[uint64][]types.Trade, types.OrderSlice, error) {
var err error var err error
var orderTrades = make(map[uint64][]types.Trade) var orderTrades = make(map[uint64][]types.Trade)
var updatedOrders types.OrderSlice var updatedOrders types.OrderSlice
@ -763,7 +767,9 @@ func (s *Strategy) analyzeOrders(orders types.OrderSlice) {
} }
} }
func (s *Strategy) buildArbMarkets(session *bbgo.ExchangeSession, symbols []string, separateStream bool, sigC sigchan.Chan) (map[string]*ArbMarket, error) { func (s *Strategy) buildArbMarkets(
session *bbgo.ExchangeSession, symbols []string, separateStream bool, sigC sigchan.Chan,
) (map[string]*ArbMarket, error) {
markets := make(map[string]*ArbMarket) markets := make(map[string]*ArbMarket)
// build market object // build market object
for _, symbol := range symbols { for _, symbol := range symbols {
@ -790,7 +796,7 @@ func (s *Strategy) buildArbMarkets(session *bbgo.ExchangeSession, symbols []stri
Speed: types.SpeedHigh, Speed: types.SpeedHigh,
}) })
book := types.NewStreamBook(symbol) book := types.NewStreamBook(symbol, session.ExchangeName)
priceUpdater := func(_ types.SliceOrderBook) { priceUpdater := func(_ types.SliceOrderBook) {
bestBid, bestAsk, _ := book.BestBidAndAsk() bestBid, bestAsk, _ := book.BestBidAndAsk()
if bestAsk.Equals(m.bestAsk) && bestBid.Equals(m.bestBid) { if bestAsk.Equals(m.bestAsk) && bestBid.Equals(m.bestBid) {

View File

@ -393,7 +393,7 @@ func (s *Strategy) CrossRun(
return err return err
} }
s.pricingBook = types.NewStreamBook(s.HedgeSymbol) s.pricingBook = types.NewStreamBook(s.HedgeSymbol, s.hedgeSession.ExchangeName)
s.pricingBook.BindStream(s.hedgeSession.MarketDataStream) s.pricingBook.BindStream(s.hedgeSession.MarketDataStream)
s.stopC = make(chan struct{}) s.stopC = make(chan struct{})

View File

@ -44,7 +44,7 @@ func TestStrategy_generateMakerOrders(t *testing.T) {
}, },
} }
pricingBook := types.NewStreamBook("BTCUSDT") pricingBook := types.NewStreamBook("BTCUSDT", types.ExchangeBinance)
pricingBook.Load(types.SliceOrderBook{ pricingBook.Load(types.SliceOrderBook{
Symbol: "BTCUSDT", Symbol: "BTCUSDT",
Bids: types.PriceVolumeSlice{ Bids: types.PriceVolumeSlice{

View File

@ -153,11 +153,11 @@ func (s *Strategy) CrossRun(ctx context.Context, _ bbgo.OrderExecutionRouter, se
}) })
if s.SourceExchange != "" { if s.SourceExchange != "" {
s.sourceBook = types.NewStreamBook(s.Symbol) s.sourceBook = types.NewStreamBook(s.Symbol, sourceSession.ExchangeName)
s.sourceBook.BindStream(s.sourceSession.MarketDataStream) s.sourceBook.BindStream(s.sourceSession.MarketDataStream)
} }
s.tradingBook = types.NewStreamBook(s.Symbol) s.tradingBook = types.NewStreamBook(s.Symbol, tradingSession.ExchangeName)
s.tradingBook.BindStream(s.tradingSession.MarketDataStream) s.tradingBook.BindStream(s.tradingSession.MarketDataStream)
s.tradingSession.UserDataStream.OnTradeUpdate(func(trade types.Trade) { s.tradingSession.UserDataStream.OnTradeUpdate(func(trade types.Trade) {

View File

@ -819,7 +819,7 @@ func (s *Strategy) CrossRun(
}) })
} }
s.book = types.NewStreamBook(s.Symbol) s.book = types.NewStreamBook(s.Symbol, s.sourceSession.ExchangeName)
s.book.BindStream(s.sourceSession.MarketDataStream) s.book.BindStream(s.sourceSession.MarketDataStream)
s.activeMakerOrders = bbgo.NewActiveOrderBook(s.Symbol) s.activeMakerOrders = bbgo.NewActiveOrderBook(s.Symbol)

View File

@ -396,7 +396,7 @@ func (e *StreamExecutor) Run(parentCtx context.Context) error {
e.marketDataStream.SetPublicOnly() e.marketDataStream.SetPublicOnly()
e.marketDataStream.Subscribe(types.BookChannel, e.Symbol, types.SubscribeOptions{}) e.marketDataStream.Subscribe(types.BookChannel, e.Symbol, types.SubscribeOptions{})
e.orderBook = types.NewStreamBook(e.Symbol) e.orderBook = types.NewStreamBook(e.Symbol, e.Session.ExchangeName)
e.orderBook.BindStream(e.marketDataStream) e.orderBook.BindStream(e.marketDataStream)
e.userDataStream = e.Session.Exchange.NewStream() e.userDataStream = e.Session.Exchange.NewStream()

View File

@ -88,7 +88,7 @@ func NewFixedQuantityExecutor(
Depth: types.DepthLevelMedium, Depth: types.DepthLevelMedium,
}) })
orderBook := types.NewStreamBook(symbol) orderBook := types.NewStreamBook(symbol, exchange.Name())
orderBook.BindStream(marketDataStream) orderBook.BindStream(marketDataStream)
userDataStream := exchange.NewStream() userDataStream := exchange.NewStream()

View File

@ -143,6 +143,7 @@ func TestNewStreamExecutor(t *testing.T) {
}, },
} }
mockEx.EXPECT().Name().Return(exchangeName)
mockEx.EXPECT().NewStream().Return(mockMarketDataStream) mockEx.EXPECT().NewStream().Return(mockMarketDataStream)
mockEx.EXPECT().NewStream().Return(mockUserDataStream) mockEx.EXPECT().NewStream().Return(mockUserDataStream)
mockEx.EXPECT().QueryAccountBalances(gomock.AssignableToTypeOf(ctx)).Return(initialBalances, nil) mockEx.EXPECT().QueryAccountBalances(gomock.AssignableToTypeOf(ctx)).Return(initialBalances, nil)

View File

@ -6,6 +6,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/prometheus/client_golang/prometheus"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
) )
@ -27,11 +29,12 @@ type MutexOrderBook struct {
sync.Mutex sync.Mutex
Symbol string Symbol string
Exchange ExchangeName
orderBook OrderBook orderBook OrderBook
} }
func NewMutexOrderBook(symbol string) *MutexOrderBook { func NewMutexOrderBook(symbol string, exchangeName ExchangeName) *MutexOrderBook {
var book OrderBook = NewSliceOrderBook(symbol) var book OrderBook = NewSliceOrderBook(symbol)
if v, _ := strconv.ParseBool(os.Getenv("ENABLE_RBT_ORDERBOOK")); v { if v, _ := strconv.ParseBool(os.Getenv("ENABLE_RBT_ORDERBOOK")); v {
@ -40,6 +43,7 @@ func NewMutexOrderBook(symbol string) *MutexOrderBook {
return &MutexOrderBook{ return &MutexOrderBook{
Symbol: symbol, Symbol: symbol,
Exchange: exchangeName,
orderBook: book, orderBook: book,
} }
} }
@ -134,6 +138,46 @@ type BookSignal struct {
Time time.Time Time time.Time
} }
var streamOrderBookBestBidPriceMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_stream_order_book_best_bid_price",
Help: "",
}, []string{"symbol", "exchange"})
var streamOrderBookBestAskPriceMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_stream_order_book_best_ask_price",
Help: "",
}, []string{"symbol", "exchange"})
var streamOrderBookBestBidVolumeMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_stream_order_book_best_bid_volume",
Help: "",
}, []string{"symbol", "exchange"})
var streamOrderBookBestAskVolumeMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_stream_order_book_best_ask_volume",
Help: "",
}, []string{"symbol", "exchange"})
var streamOrderBookUpdateTimeMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_stream_order_book_update_time_milliseconds",
Help: "",
}, []string{"symbol", "exchange"})
func init() {
prometheus.MustRegister(
streamOrderBookBestBidPriceMetrics,
streamOrderBookBestAskPriceMetrics,
streamOrderBookBestBidVolumeMetrics,
streamOrderBookBestAskVolumeMetrics,
streamOrderBookUpdateTimeMetrics,
)
}
// StreamOrderBook receives streaming data from websocket connection and // StreamOrderBook receives streaming data from websocket connection and
// update the order book with mutex lock, so you can safely access it. // update the order book with mutex lock, so you can safely access it.
// //
@ -147,13 +191,25 @@ type StreamOrderBook struct {
snapshotCallbacks []func(snapshot SliceOrderBook) snapshotCallbacks []func(snapshot SliceOrderBook)
} }
func NewStreamBook(symbol string) *StreamOrderBook { func NewStreamBook(symbol string, exchangeName ExchangeName) *StreamOrderBook {
return &StreamOrderBook{ return &StreamOrderBook{
MutexOrderBook: NewMutexOrderBook(symbol), MutexOrderBook: NewMutexOrderBook(symbol, exchangeName),
C: make(chan *BookSignal, 1), C: make(chan *BookSignal, 1),
} }
} }
func (sb *StreamOrderBook) updateMetrics(t time.Time) {
bestBid, bestAsk, ok := sb.BestBidAndAsk()
if ok {
exchangeName := string(sb.Exchange)
streamOrderBookBestAskPriceMetrics.WithLabelValues(sb.Symbol, exchangeName).Set(bestAsk.Price.Float64())
streamOrderBookBestBidPriceMetrics.WithLabelValues(sb.Symbol, exchangeName).Set(bestBid.Price.Float64())
streamOrderBookBestAskVolumeMetrics.WithLabelValues(sb.Symbol, exchangeName).Set(bestAsk.Volume.Float64())
streamOrderBookBestBidVolumeMetrics.WithLabelValues(sb.Symbol, exchangeName).Set(bestBid.Volume.Float64())
streamOrderBookUpdateTimeMetrics.WithLabelValues(sb.Symbol, exchangeName).Set(float64(t.UnixMilli()))
}
}
func (sb *StreamOrderBook) BindStream(stream Stream) { func (sb *StreamOrderBook) BindStream(stream Stream) {
stream.OnBookSnapshot(func(book SliceOrderBook) { stream.OnBookSnapshot(func(book SliceOrderBook) {
if sb.MutexOrderBook.Symbol != book.Symbol { if sb.MutexOrderBook.Symbol != book.Symbol {
@ -163,6 +219,7 @@ func (sb *StreamOrderBook) BindStream(stream Stream) {
sb.Load(book) sb.Load(book)
sb.EmitSnapshot(book) sb.EmitSnapshot(book)
sb.emitChange(BookSignalSnapshot, book.Time) sb.emitChange(BookSignalSnapshot, book.Time)
sb.updateMetrics(book.Time)
}) })
stream.OnBookUpdate(func(book SliceOrderBook) { stream.OnBookUpdate(func(book SliceOrderBook) {
@ -173,6 +230,7 @@ func (sb *StreamOrderBook) BindStream(stream Stream) {
sb.Update(book) sb.Update(book)
sb.EmitUpdate(book) sb.EmitUpdate(book)
sb.emitChange(BookSignalUpdate, book.Time) sb.emitChange(BookSignalUpdate, book.Time)
sb.updateMetrics(book.Time)
}) })
} }