pkg/exchange: add time to SliceOrderBook

This commit is contained in:
Edwin 2023-09-01 17:02:08 +08:00
parent 6f4f1ad558
commit 50bfd8ee0e
14 changed files with 51 additions and 24 deletions

View File

@ -71,9 +71,10 @@ var orderbookCmd = &cobra.Command{
} }
if bid, ask, ok := orderBook.BestBidAndAsk(); ok { if bid, ask, ok := orderBook.BestBidAndAsk(); ok {
log.Infof("ASK | %f x %f / %f x %f | BID", log.Infof("ASK | %f x %f / %f x %f | BID | %s",
ask.Volume.Float64(), ask.Price.Float64(), ask.Volume.Float64(), ask.Price.Float64(),
bid.Price.Float64(), bid.Volume.Float64()) bid.Price.Float64(), bid.Volume.Float64(),
book.Time.String())
} }
}) })
@ -84,9 +85,10 @@ var orderbookCmd = &cobra.Command{
orderBook.Update(book) orderBook.Update(book)
if bid, ask, ok := orderBook.BestBidAndAsk(); ok { if bid, ask, ok := orderBook.BestBidAndAsk(); ok {
log.Infof("ASK | %f x %f / %f x %f | BID", log.Infof("ASK | %f x %f / %f x %f | BID | %s",
ask.Volume.Float64(), ask.Price.Float64(), ask.Volume.Float64(), ask.Price.Float64(),
bid.Price.Float64(), bid.Volume.Float64()) bid.Price.Float64(), bid.Volume.Float64(),
book.Time.String())
} }
}) })

View File

@ -1346,6 +1346,8 @@ func (e *Exchange) QueryDepth(ctx context.Context, symbol string) (snapshot type
func convertDepth(snapshot types.SliceOrderBook, symbol string, finalUpdateID int64, response *binance.DepthResponse) (types.SliceOrderBook, int64, error) { func convertDepth(snapshot types.SliceOrderBook, symbol string, finalUpdateID int64, response *binance.DepthResponse) (types.SliceOrderBook, int64, error) {
snapshot.Symbol = symbol snapshot.Symbol = symbol
// empty time since the API does not provide time information.
snapshot.Time = time.Time{}
finalUpdateID = response.LastUpdateID finalUpdateID = response.LastUpdateID
for _, entry := range response.Bids { for _, entry := range response.Bids {
// entry.Price, Quantity: entry.Quantity // entry.Price, Quantity: entry.Quantity

View File

@ -461,6 +461,7 @@ func (e *DepthEvent) String() (o string) {
func (e *DepthEvent) OrderBook() (book types.SliceOrderBook, err error) { func (e *DepthEvent) OrderBook() (book types.SliceOrderBook, err error) {
book.Symbol = e.Symbol book.Symbol = e.Symbol
book.Time = types.NewMillisecondTimestampFromInt(e.EventBase.Time).Time()
// already in descending order // already in descending order
book.Bids = e.Bids book.Bids = e.Bids

View File

@ -88,6 +88,7 @@ func NewStream(ex *Exchange, client *binance.Client, futuresClient *futures.Clie
if ok { if ok {
err := f.AddUpdate(types.SliceOrderBook{ err := f.AddUpdate(types.SliceOrderBook{
Symbol: e.Symbol, Symbol: e.Symbol,
Time: types.NewMillisecondTimestampFromInt(e.EventBase.Time).Time(),
Bids: e.Bids, Bids: e.Bids,
Asks: e.Asks, Asks: e.Asks,
}, e.FirstUpdateID, e.FinalUpdateID) }, e.FirstUpdateID, e.FinalUpdateID)

View File

@ -131,6 +131,7 @@ func (s *Stream) parseWebSocketEvent(in []byte) (interface{}, error) {
} }
book.Type = e.WebSocketTopicEvent.Type book.Type = e.WebSocketTopicEvent.Type
book.ServerTime = e.WebSocketTopicEvent.Ts.Time()
return &book, nil return &book, nil
case TopicTypeKLine: case TopicTypeKLine:

View File

@ -35,6 +35,7 @@ func getTestClientOrSkip(t *testing.T) *Stream {
} }
func TestStream(t *testing.T) { func TestStream(t *testing.T) {
t.Skip()
s := getTestClientOrSkip(t) s := getTestClientOrSkip(t)
t.Run("Auth test", func(t *testing.T) { t.Run("Auth test", func(t *testing.T) {
@ -182,6 +183,7 @@ func TestStream_parseWebSocketEvent(t *testing.T) {
UpdateId: fixedpoint.NewFromFloat(1854104), UpdateId: fixedpoint.NewFromFloat(1854104),
SequenceId: fixedpoint.NewFromFloat(10559247733), SequenceId: fixedpoint.NewFromFloat(10559247733),
Type: DataTypeDelta, Type: DataTypeDelta,
ServerTime: types.NewMillisecondTimestampFromInt(1691130685111).Time(),
}, *book) }, *book)
}) })

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/c9s/bbgo/pkg/exchange/bybit/bybitapi" "github.com/c9s/bbgo/pkg/exchange/bybit/bybitapi"
"github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/fixedpoint"
@ -117,14 +118,18 @@ type BookEvent struct {
SequenceId fixedpoint.Value `json:"seq"` SequenceId fixedpoint.Value `json:"seq"`
// internal use // internal use
// Type can be one of snapshot or delta. Copied from WebSocketTopicEvent.Type // Copied from WebSocketTopicEvent.Type, WebSocketTopicEvent.Ts
// Type can be one of snapshot or delta.
Type DataType Type DataType
// ServerTime using the websocket timestamp as server time. Since the event not provide server time information.
ServerTime time.Time
} }
func (e *BookEvent) OrderBook() (snapshot types.SliceOrderBook) { func (e *BookEvent) OrderBook() (snapshot types.SliceOrderBook) {
snapshot.Symbol = e.Symbol snapshot.Symbol = e.Symbol
snapshot.Bids = e.Bids snapshot.Bids = e.Bids
snapshot.Asks = e.Asks snapshot.Asks = e.Asks
snapshot.Time = e.ServerTime
return snapshot return snapshot
} }

View File

@ -438,6 +438,7 @@ func (e *Exchange) QueryDepth(ctx context.Context, symbol string) (types.SliceOr
return types.SliceOrderBook{ return types.SliceOrderBook{
Symbol: toGlobalSymbol(symbol), Symbol: toGlobalSymbol(symbol),
Time: orderBook.Time.Time(),
Bids: orderBook.Bids, Bids: orderBook.Bids,
Asks: orderBook.Asks, Asks: orderBook.Asks,
}, sequence, nil }, sequence, nil

View File

@ -73,6 +73,7 @@ func (s *Stream) handleOrderBookL2Event(e *WebSocketOrderBookL2Event) {
if ok { if ok {
f.AddUpdate(types.SliceOrderBook{ f.AddUpdate(types.SliceOrderBook{
Symbol: toGlobalSymbol(e.Symbol), Symbol: toGlobalSymbol(e.Symbol),
Time: e.Time.Time(),
Bids: e.Changes.Bids, Bids: e.Changes.Bids,
Asks: e.Changes.Asks, Asks: e.Changes.Asks,
}, e.SequenceStart, e.SequenceEnd) }, e.SequenceStart, e.SequenceEnd)

View File

@ -80,6 +80,7 @@ type WebSocketOrderBookL2Event struct {
Asks types.PriceVolumeSlice `json:"asks"` Asks types.PriceVolumeSlice `json:"asks"`
Bids types.PriceVolumeSlice `json:"bids"` Bids types.PriceVolumeSlice `json:"bids"`
} `json:"changes"` } `json:"changes"`
Time types.MillisecondTimestamp `json:"time"`
} }
type WebSocketCandleEvent struct { type WebSocketCandleEvent struct {

View File

@ -81,25 +81,25 @@ type KLineEvent struct {
} }
/* /*
{ {
"c": "kline", "c": "kline",
"M": "btcusdt", "M": "btcusdt",
"e": "update", "e": "update",
"T": 1602999650179, "T": 1602999650179,
"k": { "k": {
"ST": 1602999900000, "ST": 1602999900000,
"ET": 1602999900000, "ET": 1602999900000,
"M": "btcusdt", "M": "btcusdt",
"R": "5m", "R": "5m",
"O": "11417.21", "O": "11417.21",
"H": "11417.21", "H": "11417.21",
"L": "11417.21", "L": "11417.21",
"C": "11417.21", "C": "11417.21",
"v": "0", "v": "0",
"ti": 0, "ti": 0,
"x": false "x": false
} }
} }
*/ */
type KLinePayload struct { type KLinePayload struct {
StartTime int64 `json:"ST"` StartTime int64 `json:"ST"`
@ -175,6 +175,7 @@ func (e *BookEvent) Time() time.Time {
func (e *BookEvent) OrderBook() (snapshot types.SliceOrderBook, err error) { func (e *BookEvent) OrderBook() (snapshot types.SliceOrderBook, err error) {
snapshot.Symbol = strings.ToUpper(e.Market) snapshot.Symbol = strings.ToUpper(e.Market)
snapshot.Time = e.Time()
for _, bid := range e.Bids { for _, bid := range e.Bids {
pv, err := bid.PriceVolumePair() pv, err := bid.PriceVolumePair()

View File

@ -188,6 +188,7 @@ func (s *Stream) handleBookEvent(e max.BookEvent) {
} }
newBook.Symbol = toGlobalSymbol(e.Market) newBook.Symbol = toGlobalSymbol(e.Market)
newBook.Time = e.Time()
switch e.Event { switch e.Event {
case "snapshot": case "snapshot":

View File

@ -81,6 +81,7 @@ func (data *BookEvent) BookTicker() types.BookTicker {
func (data *BookEvent) Book() types.SliceOrderBook { func (data *BookEvent) Book() types.SliceOrderBook {
book := types.SliceOrderBook{ book := types.SliceOrderBook{
Symbol: data.Symbol, Symbol: data.Symbol,
Time: types.NewMillisecondTimestampFromInt(data.MillisecondTimestamp).Time(),
} }
for _, bid := range data.Bids { for _, bid := range data.Bids {

View File

@ -12,11 +12,14 @@ import (
// SliceOrderBook is a general order book structure which could be used // SliceOrderBook is a general order book structure which could be used
// for RESTful responses and websocket stream parsing // for RESTful responses and websocket stream parsing
//
//go:generate callbackgen -type SliceOrderBook //go:generate callbackgen -type SliceOrderBook
type SliceOrderBook struct { type SliceOrderBook struct {
Symbol string Symbol string
Bids PriceVolumeSlice Bids PriceVolumeSlice
Asks PriceVolumeSlice Asks PriceVolumeSlice
// Time represents the server time. If empty, it indicates that the server does not provide this information.
Time time.Time
lastUpdateTime time.Time lastUpdateTime time.Time
@ -162,6 +165,8 @@ func (b *SliceOrderBook) String() string {
sb.WriteString("BOOK ") sb.WriteString("BOOK ")
sb.WriteString(b.Symbol) sb.WriteString(b.Symbol)
sb.WriteString("\n") sb.WriteString("\n")
sb.WriteString(b.Time.Format(time.RFC1123))
sb.WriteString("\n")
if len(b.Asks) > 0 { if len(b.Asks) > 0 {
sb.WriteString("ASKS:\n") sb.WriteString("ASKS:\n")
@ -187,6 +192,7 @@ func (b *SliceOrderBook) String() string {
func (b *SliceOrderBook) CopyDepth(limit int) OrderBook { func (b *SliceOrderBook) CopyDepth(limit int) OrderBook {
var book SliceOrderBook var book SliceOrderBook
book.Symbol = b.Symbol book.Symbol = b.Symbol
book.Time = b.Time
book.Bids = b.Bids.CopyDepth(limit) book.Bids = b.Bids.CopyDepth(limit)
book.Asks = b.Asks.CopyDepth(limit) book.Asks = b.Asks.CopyDepth(limit)
return &book return &book
@ -195,6 +201,7 @@ func (b *SliceOrderBook) CopyDepth(limit int) OrderBook {
func (b *SliceOrderBook) Copy() OrderBook { func (b *SliceOrderBook) Copy() OrderBook {
var book SliceOrderBook var book SliceOrderBook
book.Symbol = b.Symbol book.Symbol = b.Symbol
book.Time = b.Time
book.Bids = b.Bids.Copy() book.Bids = b.Bids.Copy()
book.Asks = b.Asks.Copy() book.Asks = b.Asks.Copy()
return &book return &book