mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
fix binance depth snapshot updating
This commit is contained in:
parent
d0fc161ae7
commit
6a6dacd595
|
@ -49,15 +49,36 @@ var rootCmd = &cobra.Command{
|
||||||
stream.Subscribe(types.BookChannel, symbol, types.SubscribeOptions{})
|
stream.Subscribe(types.BookChannel, symbol, types.SubscribeOptions{})
|
||||||
|
|
||||||
stream.OnBookSnapshot(func(book types.OrderBook) {
|
stream.OnBookSnapshot(func(book types.OrderBook) {
|
||||||
log.Infof("book snapshot: %+v", book)
|
// log.Infof("book snapshot: %+v", book)
|
||||||
})
|
})
|
||||||
|
|
||||||
stream.OnBookUpdate(func(book types.OrderBook) {
|
stream.OnBookUpdate(func(book types.OrderBook) {
|
||||||
log.Infof("book update: %+v", book)
|
// log.Infof("book update: %+v", book)
|
||||||
})
|
})
|
||||||
|
|
||||||
streambook := types.NewStreamBook(symbol)
|
streambook := types.NewStreamBook(symbol)
|
||||||
streambook.BindStream(stream)
|
streambook.BindStream(stream)
|
||||||
|
streambook.OnUpdate(func(book *types.OrderBook) {
|
||||||
|
bestBid, hasBid := book.BestBid()
|
||||||
|
bestAsk, hasAsk := book.BestAsk()
|
||||||
|
|
||||||
|
if !book.IsValid() {
|
||||||
|
log.Warnf("order book is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasBid && hasAsk {
|
||||||
|
log.Infof("================================")
|
||||||
|
log.Infof("best ask %f % -12f",
|
||||||
|
bestAsk.Price.Float64(),
|
||||||
|
bestAsk.Volume.Float64(),
|
||||||
|
)
|
||||||
|
log.Infof("best bid %f % -12f",
|
||||||
|
bestBid.Price.Float64(),
|
||||||
|
bestBid.Volume.Float64(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
log.Info("connecting websocket...")
|
log.Info("connecting websocket...")
|
||||||
if err := stream.Connect(ctx); err != nil {
|
if err := stream.Connect(ctx); err != nil {
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -16,9 +18,23 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var debugBinanceDepth bool
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// randomize pulling
|
// randomize pulling
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
if s := os.Getenv("BINANCE_DEBUG_DEPTH"); len(s) > 0 {
|
||||||
|
v, err := strconv.ParseBool(s)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
} else {
|
||||||
|
debugBinanceDepth = v
|
||||||
|
if debugBinanceDepth {
|
||||||
|
log.Info("binance depth debugging is enabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type StreamRequest struct {
|
type StreamRequest struct {
|
||||||
|
@ -457,21 +473,27 @@ func (f *DepthFrame) loadDepthSnapshot() {
|
||||||
}
|
}
|
||||||
|
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
|
|
||||||
|
// since we're buffering the update events, ideally the some of the head events
|
||||||
|
// should be older than the received depth snapshot.
|
||||||
|
// if the head event is newer than the depth we got,
|
||||||
|
// then there are something missed, we need to restart the process.
|
||||||
|
if len(f.BufEvents) > 0 {
|
||||||
|
e := f.BufEvents[0]
|
||||||
|
if e.FirstUpdateID > depth.FinalUpdateID+1 {
|
||||||
|
log.Warn("miss matched final update id for order book")
|
||||||
|
f.SnapshotDepth = nil
|
||||||
|
f.BufEvents = nil
|
||||||
|
f.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
f.SnapshotDepth = depth
|
f.SnapshotDepth = depth
|
||||||
|
// filter the events by the event IDs
|
||||||
|
|
||||||
var events []DepthEvent
|
var events []DepthEvent
|
||||||
for _, e := range f.BufEvents {
|
for _, e := range f.BufEvents {
|
||||||
/*
|
|
||||||
if i == 0 {
|
|
||||||
if e.FirstUpdateID > f.SnapshotDepth.FinalUpdateID+1 {
|
|
||||||
// FIXME: we missed some events
|
|
||||||
log.Warn("miss matched final update id for order book")
|
|
||||||
f.SnapshotDepth = nil
|
|
||||||
f.mu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if e.FirstUpdateID <= f.SnapshotDepth.FinalUpdateID || e.FinalUpdateID <= f.SnapshotDepth.FinalUpdateID {
|
if e.FirstUpdateID <= f.SnapshotDepth.FinalUpdateID || e.FinalUpdateID <= f.SnapshotDepth.FinalUpdateID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -490,10 +512,16 @@ func (f *DepthFrame) PushEvent(e DepthEvent) {
|
||||||
f.BufEvents = append(f.BufEvents, e)
|
f.BufEvents = append(f.BufEvents, e)
|
||||||
f.mu.Unlock()
|
f.mu.Unlock()
|
||||||
|
|
||||||
go f.once.Do(f.loadDepthSnapshot)
|
go f.once.Do(func() {
|
||||||
go func() {
|
if debugBinanceDepth {
|
||||||
|
log.Infof("starting depth snapshot updater for %s market", f.Symbol)
|
||||||
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ticker := time.NewTicker(5*time.Minute + time.Duration(rand.Intn(10))*time.Second)
|
|
||||||
|
f.loadDepthSnapshot()
|
||||||
|
|
||||||
|
ticker := time.NewTicker(1*time.Minute + time.Duration(rand.Intn(10))*time.Second)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -503,7 +531,7 @@ func (f *DepthFrame) PushEvent(e DepthEvent) {
|
||||||
f.loadDepthSnapshot()
|
f.loadDepthSnapshot()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
} else {
|
} else {
|
||||||
if e.FirstUpdateID > f.SnapshotDepth.FinalUpdateID || e.FinalUpdateID > f.SnapshotDepth.FinalUpdateID {
|
if e.FirstUpdateID > f.SnapshotDepth.FinalUpdateID || e.FinalUpdateID > f.SnapshotDepth.FinalUpdateID {
|
||||||
if e.FinalUpdateID > f.SnapshotDepth.FinalUpdateID {
|
if e.FinalUpdateID > f.SnapshotDepth.FinalUpdateID {
|
||||||
|
@ -518,6 +546,10 @@ func (f *DepthFrame) PushEvent(e DepthEvent) {
|
||||||
|
|
||||||
// fetch fetches the depth and convert to the depth event so that we can reuse the event structure to convert it to the global orderbook type
|
// fetch fetches the depth and convert to the depth event so that we can reuse the event structure to convert it to the global orderbook type
|
||||||
func (f *DepthFrame) fetch(ctx context.Context) (*DepthEvent, error) {
|
func (f *DepthFrame) fetch(ctx context.Context) (*DepthEvent, error) {
|
||||||
|
if debugBinanceDepth {
|
||||||
|
log.Infof("fetching %s depth snapshot", f.Symbol)
|
||||||
|
}
|
||||||
|
|
||||||
response, err := f.client.NewDepthService().Symbol(f.Symbol).Do(ctx)
|
response, err := f.client.NewDepthService().Symbol(f.Symbol).Do(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -123,6 +123,33 @@ type OrderBook struct {
|
||||||
asksChangeCallbacks []func(pvs PriceVolumeSlice)
|
asksChangeCallbacks []func(pvs PriceVolumeSlice)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *OrderBook) BestBid() (PriceVolume, bool) {
|
||||||
|
if len(b.Bids) == 0 {
|
||||||
|
return PriceVolume{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.Bids[0], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *OrderBook) BestAsk() (PriceVolume, bool) {
|
||||||
|
if len(b.Asks) == 0 {
|
||||||
|
return PriceVolume{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.Asks[0], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *OrderBook) IsValid() bool {
|
||||||
|
bid, hasBid := b.BestBid()
|
||||||
|
ask, hasAsk := b.BestAsk()
|
||||||
|
|
||||||
|
if !hasBid || !hasAsk {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return bid.Price < ask.Price
|
||||||
|
}
|
||||||
|
|
||||||
func (b *OrderBook) PriceVolumesBySide(side SideType) PriceVolumeSlice {
|
func (b *OrderBook) PriceVolumesBySide(side SideType) PriceVolumeSlice {
|
||||||
switch side {
|
switch side {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user