mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-06 15:10:59 +00:00
* [FIX] Enable ws orderbook sync recovery by: - Testing if books have been cleared - Assigning options when loading snapshot * orderbooks: remove setlastupdate method and on select depth method that updates linked list, this reduced lock contention across code base and fixes buffer bug on applying buffered updates * WS - Introduce signaling for the need to fetch the orderbook * Address nits * Update error messages to include exchange name Co-authored-by: shazbert <oharareid.ryan@gmail.com>
This commit is contained in:
@@ -94,8 +94,11 @@ func (d *Depth) TotalAskAmounts() (liquidity, value float64) {
|
||||
}
|
||||
|
||||
// LoadSnapshot flushes the bids and asks with a snapshot
|
||||
func (d *Depth) LoadSnapshot(bids, asks []Item) {
|
||||
func (d *Depth) LoadSnapshot(bids, asks []Item, lastUpdateID int64, lastUpdated time.Time, updateByREST bool) {
|
||||
d.m.Lock()
|
||||
d.lastUpdateID = lastUpdateID
|
||||
d.lastUpdated = lastUpdated
|
||||
d.restSnapshot = updateByREST
|
||||
d.bids.load(bids, d.stack)
|
||||
d.asks.load(asks, d.stack)
|
||||
d.alert()
|
||||
@@ -105,6 +108,8 @@ func (d *Depth) LoadSnapshot(bids, asks []Item) {
|
||||
// Flush flushes the bid and ask depths
|
||||
func (d *Depth) Flush() {
|
||||
d.m.Lock()
|
||||
d.lastUpdateID = 0
|
||||
d.lastUpdated = time.Time{}
|
||||
d.bids.load(nil, d.stack)
|
||||
d.asks.load(nil, d.stack)
|
||||
d.alert()
|
||||
@@ -113,11 +118,13 @@ func (d *Depth) Flush() {
|
||||
|
||||
// UpdateBidAskByPrice updates the bid and ask spread by supplied updates, this
|
||||
// will trim total length of depth level to a specified supplied number
|
||||
func (d *Depth) UpdateBidAskByPrice(bidUpdts, askUpdts Items, maxDepth int) {
|
||||
func (d *Depth) UpdateBidAskByPrice(bidUpdts, askUpdts Items, maxDepth int, lastUpdateID int64, lastUpdated time.Time) {
|
||||
if len(bidUpdts) == 0 && len(askUpdts) == 0 {
|
||||
return
|
||||
}
|
||||
d.m.Lock()
|
||||
d.lastUpdateID = lastUpdateID
|
||||
d.lastUpdated = lastUpdated
|
||||
tn := getNow()
|
||||
if len(bidUpdts) != 0 {
|
||||
d.bids.updateInsertByPrice(bidUpdts, d.stack, maxDepth, tn)
|
||||
@@ -130,7 +137,7 @@ func (d *Depth) UpdateBidAskByPrice(bidUpdts, askUpdts Items, maxDepth int) {
|
||||
}
|
||||
|
||||
// UpdateBidAskByID amends details by ID
|
||||
func (d *Depth) UpdateBidAskByID(bidUpdts, askUpdts Items) error {
|
||||
func (d *Depth) UpdateBidAskByID(bidUpdts, askUpdts Items, lastUpdateID int64, lastUpdated time.Time) error {
|
||||
if len(bidUpdts) == 0 && len(askUpdts) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -148,12 +155,14 @@ func (d *Depth) UpdateBidAskByID(bidUpdts, askUpdts Items) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
d.lastUpdateID = lastUpdateID
|
||||
d.lastUpdated = lastUpdated
|
||||
d.alert()
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteBidAskByID deletes a price level by ID
|
||||
func (d *Depth) DeleteBidAskByID(bidUpdts, askUpdts Items, bypassErr bool) error {
|
||||
func (d *Depth) DeleteBidAskByID(bidUpdts, askUpdts Items, bypassErr bool, lastUpdateID int64, lastUpdated time.Time) error {
|
||||
if len(bidUpdts) == 0 && len(askUpdts) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -171,12 +180,14 @@ func (d *Depth) DeleteBidAskByID(bidUpdts, askUpdts Items, bypassErr bool) error
|
||||
return err
|
||||
}
|
||||
}
|
||||
d.lastUpdateID = lastUpdateID
|
||||
d.lastUpdated = lastUpdated
|
||||
d.alert()
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertBidAskByID inserts new updates
|
||||
func (d *Depth) InsertBidAskByID(bidUpdts, askUpdts Items) error {
|
||||
func (d *Depth) InsertBidAskByID(bidUpdts, askUpdts Items, lastUpdateID int64, lastUpdated time.Time) error {
|
||||
if len(bidUpdts) == 0 && len(askUpdts) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -194,12 +205,14 @@ func (d *Depth) InsertBidAskByID(bidUpdts, askUpdts Items) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
d.lastUpdateID = lastUpdateID
|
||||
d.lastUpdated = lastUpdated
|
||||
d.alert()
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateInsertByID updates or inserts by ID at current price level.
|
||||
func (d *Depth) UpdateInsertByID(bidUpdts, askUpdts Items) error {
|
||||
func (d *Depth) UpdateInsertByID(bidUpdts, askUpdts Items, lastUpdateID int64, lastUpdated time.Time) error {
|
||||
if len(bidUpdts) == 0 && len(askUpdts) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -218,6 +231,8 @@ func (d *Depth) UpdateInsertByID(bidUpdts, askUpdts Items) error {
|
||||
}
|
||||
}
|
||||
d.alert()
|
||||
d.lastUpdateID = lastUpdateID
|
||||
d.lastUpdated = lastUpdated
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -239,15 +254,6 @@ func (d *Depth) AssignOptions(b *Base) {
|
||||
d.m.Unlock()
|
||||
}
|
||||
|
||||
// SetLastUpdate sets details of last update information
|
||||
func (d *Depth) SetLastUpdate(lastUpdate time.Time, lastUpdateID int64, updateByREST bool) {
|
||||
d.m.Lock()
|
||||
d.lastUpdated = lastUpdate
|
||||
d.lastUpdateID = lastUpdateID
|
||||
d.restSnapshot = updateByREST
|
||||
d.m.Unlock()
|
||||
}
|
||||
|
||||
// GetName returns name of exchange
|
||||
func (d *Depth) GetName() string {
|
||||
d.m.Lock()
|
||||
|
||||
@@ -121,7 +121,7 @@ func TestTotalAmounts(t *testing.T) {
|
||||
|
||||
func TestLoadSnapshot(t *testing.T) {
|
||||
d := newDepth(id)
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1}}, Items{{Price: 1337, Amount: 10}})
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1}}, Items{{Price: 1337, Amount: 10}}, 0, time.Time{}, false)
|
||||
if d.Retrieve().Asks[0].Price != 1337 || d.Retrieve().Bids[0].Price != 1337 {
|
||||
t.Fatal("not set")
|
||||
}
|
||||
@@ -129,12 +129,12 @@ func TestLoadSnapshot(t *testing.T) {
|
||||
|
||||
func TestFlush(t *testing.T) {
|
||||
d := newDepth(id)
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1}}, Items{{Price: 1337, Amount: 10}})
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1}}, Items{{Price: 1337, Amount: 10}}, 0, time.Time{}, false)
|
||||
d.Flush()
|
||||
if len(d.Retrieve().Asks) != 0 || len(d.Retrieve().Bids) != 0 {
|
||||
t.Fatal("not flushed")
|
||||
}
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1}}, Items{{Price: 1337, Amount: 10}})
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1}}, Items{{Price: 1337, Amount: 10}}, 0, time.Time{}, false)
|
||||
d.Flush()
|
||||
if len(d.Retrieve().Asks) != 0 || len(d.Retrieve().Bids) != 0 {
|
||||
t.Fatal("not flushed")
|
||||
@@ -143,12 +143,12 @@ func TestFlush(t *testing.T) {
|
||||
|
||||
func TestUpdateBidAskByPrice(t *testing.T) {
|
||||
d := newDepth(id)
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1, ID: 1}}, Items{{Price: 1337, Amount: 10, ID: 2}})
|
||||
d.UpdateBidAskByPrice(Items{{Price: 1337, Amount: 2, ID: 1}}, Items{{Price: 1337, Amount: 2, ID: 2}}, 0)
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1, ID: 1}}, Items{{Price: 1337, Amount: 10, ID: 2}}, 0, time.Time{}, false)
|
||||
d.UpdateBidAskByPrice(Items{{Price: 1337, Amount: 2, ID: 1}}, Items{{Price: 1337, Amount: 2, ID: 2}}, 0, 0, time.Time{})
|
||||
if d.Retrieve().Asks[0].Amount != 2 || d.Retrieve().Bids[0].Amount != 2 {
|
||||
t.Fatal("orderbook amounts not updated correctly")
|
||||
}
|
||||
d.UpdateBidAskByPrice(Items{{Price: 1337, Amount: 0, ID: 1}}, Items{{Price: 1337, Amount: 0, ID: 2}}, 0)
|
||||
d.UpdateBidAskByPrice(Items{{Price: 1337, Amount: 0, ID: 1}}, Items{{Price: 1337, Amount: 0, ID: 2}}, 0, 0, time.Time{})
|
||||
if d.GetAskLength() != 0 || d.GetBidLength() != 0 {
|
||||
t.Fatal("orderbook amounts not updated correctly")
|
||||
}
|
||||
@@ -156,8 +156,8 @@ func TestUpdateBidAskByPrice(t *testing.T) {
|
||||
|
||||
func TestDeleteBidAskByID(t *testing.T) {
|
||||
d := newDepth(id)
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1, ID: 1}}, Items{{Price: 1337, Amount: 10, ID: 2}})
|
||||
err := d.DeleteBidAskByID(Items{{Price: 1337, Amount: 2, ID: 1}}, Items{{Price: 1337, Amount: 2, ID: 2}}, false)
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1, ID: 1}}, Items{{Price: 1337, Amount: 10, ID: 2}}, 0, time.Time{}, false)
|
||||
err := d.DeleteBidAskByID(Items{{Price: 1337, Amount: 2, ID: 1}}, Items{{Price: 1337, Amount: 2, ID: 2}}, false, 0, time.Time{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -165,17 +165,17 @@ func TestDeleteBidAskByID(t *testing.T) {
|
||||
t.Fatal("items not deleted")
|
||||
}
|
||||
|
||||
err = d.DeleteBidAskByID(Items{{Price: 1337, Amount: 2, ID: 1}}, nil, false)
|
||||
err = d.DeleteBidAskByID(Items{{Price: 1337, Amount: 2, ID: 1}}, nil, false, 0, time.Time{})
|
||||
if !errors.Is(err, errIDCannotBeMatched) {
|
||||
t.Fatalf("error expected %v received %v", errIDCannotBeMatched, err)
|
||||
}
|
||||
|
||||
err = d.DeleteBidAskByID(nil, Items{{Price: 1337, Amount: 2, ID: 2}}, false)
|
||||
err = d.DeleteBidAskByID(nil, Items{{Price: 1337, Amount: 2, ID: 2}}, false, 0, time.Time{})
|
||||
if !errors.Is(err, errIDCannotBeMatched) {
|
||||
t.Fatalf("error expected %v received %v", errIDCannotBeMatched, err)
|
||||
}
|
||||
|
||||
err = d.DeleteBidAskByID(nil, Items{{Price: 1337, Amount: 2, ID: 2}}, true)
|
||||
err = d.DeleteBidAskByID(nil, Items{{Price: 1337, Amount: 2, ID: 2}}, true, 0, time.Time{})
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("error expected %v received %v", nil, err)
|
||||
}
|
||||
@@ -183,8 +183,8 @@ func TestDeleteBidAskByID(t *testing.T) {
|
||||
|
||||
func TestUpdateBidAskByID(t *testing.T) {
|
||||
d := newDepth(id)
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1, ID: 1}}, Items{{Price: 1337, Amount: 10, ID: 2}})
|
||||
err := d.UpdateBidAskByID(Items{{Price: 1337, Amount: 2, ID: 1}}, Items{{Price: 1337, Amount: 2, ID: 2}})
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1, ID: 1}}, Items{{Price: 1337, Amount: 10, ID: 2}}, 0, time.Time{}, false)
|
||||
err := d.UpdateBidAskByID(Items{{Price: 1337, Amount: 2, ID: 1}}, Items{{Price: 1337, Amount: 2, ID: 2}}, 0, time.Time{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -193,12 +193,12 @@ func TestUpdateBidAskByID(t *testing.T) {
|
||||
}
|
||||
|
||||
// random unmatching IDs
|
||||
err = d.UpdateBidAskByID(Items{{Price: 1337, Amount: 2, ID: 666}}, nil)
|
||||
err = d.UpdateBidAskByID(Items{{Price: 1337, Amount: 2, ID: 666}}, nil, 0, time.Time{})
|
||||
if !errors.Is(err, errIDCannotBeMatched) {
|
||||
t.Fatalf("error expected %v received %v", errIDCannotBeMatched, err)
|
||||
}
|
||||
|
||||
err = d.UpdateBidAskByID(nil, Items{{Price: 1337, Amount: 2, ID: 69}})
|
||||
err = d.UpdateBidAskByID(nil, Items{{Price: 1337, Amount: 2, ID: 69}}, 0, time.Time{})
|
||||
if !errors.Is(err, errIDCannotBeMatched) {
|
||||
t.Fatalf("error expected %v received %v", errIDCannotBeMatched, err)
|
||||
}
|
||||
@@ -206,8 +206,8 @@ func TestUpdateBidAskByID(t *testing.T) {
|
||||
|
||||
func TestInsertBidAskByID(t *testing.T) {
|
||||
d := newDepth(id)
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1, ID: 1}}, Items{{Price: 1337, Amount: 10, ID: 2}})
|
||||
err := d.InsertBidAskByID(Items{{Price: 1338, Amount: 2, ID: 3}}, Items{{Price: 1336, Amount: 2, ID: 4}})
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1, ID: 1}}, Items{{Price: 1337, Amount: 10, ID: 2}}, 0, time.Time{}, false)
|
||||
err := d.InsertBidAskByID(Items{{Price: 1338, Amount: 2, ID: 3}}, Items{{Price: 1336, Amount: 2, ID: 4}}, 0, time.Time{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -218,19 +218,19 @@ func TestInsertBidAskByID(t *testing.T) {
|
||||
|
||||
func TestUpdateInsertByID(t *testing.T) {
|
||||
d := newDepth(id)
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1, ID: 1}}, Items{{Price: 1337, Amount: 10, ID: 2}})
|
||||
d.LoadSnapshot(Items{{Price: 1337, Amount: 1, ID: 1}}, Items{{Price: 1337, Amount: 10, ID: 2}}, 0, time.Time{}, false)
|
||||
|
||||
err := d.UpdateInsertByID(Items{{Price: 1338, Amount: 0, ID: 3}}, Items{{Price: 1336, Amount: 2, ID: 4}})
|
||||
err := d.UpdateInsertByID(Items{{Price: 1338, Amount: 0, ID: 3}}, Items{{Price: 1336, Amount: 2, ID: 4}}, 0, time.Time{})
|
||||
if !errors.Is(err, errAmountCannotBeLessOrEqualToZero) {
|
||||
t.Fatalf("expected: %v but received: %v", errAmountCannotBeLessOrEqualToZero, err)
|
||||
}
|
||||
|
||||
err = d.UpdateInsertByID(Items{{Price: 1338, Amount: 2, ID: 3}}, Items{{Price: 1336, Amount: 0, ID: 4}})
|
||||
err = d.UpdateInsertByID(Items{{Price: 1338, Amount: 2, ID: 3}}, Items{{Price: 1336, Amount: 0, ID: 4}}, 0, time.Time{})
|
||||
if !errors.Is(err, errAmountCannotBeLessOrEqualToZero) {
|
||||
t.Fatalf("expected: %v but received: %v", errAmountCannotBeLessOrEqualToZero, err)
|
||||
}
|
||||
|
||||
err = d.UpdateInsertByID(Items{{Price: 1338, Amount: 2, ID: 3}}, Items{{Price: 1336, Amount: 2, ID: 4}})
|
||||
err = d.UpdateInsertByID(Items{{Price: 1338, Amount: 2, ID: 3}}, Items{{Price: 1336, Amount: 2, ID: 4}}, 0, time.Time{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -271,17 +271,6 @@ func TestAssignOptions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetLastUpdate(t *testing.T) {
|
||||
d := Depth{}
|
||||
tn := time.Now()
|
||||
d.SetLastUpdate(tn, 1337, true)
|
||||
if d.lastUpdated != tn ||
|
||||
d.lastUpdateID != 1337 ||
|
||||
!d.restSnapshot {
|
||||
t.Fatal("failed to set correctly")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetName(t *testing.T) {
|
||||
d := Depth{}
|
||||
d.exchange = "test"
|
||||
|
||||
@@ -79,8 +79,7 @@ func (s *Service) Update(b *Base) error {
|
||||
book.AssignOptions(b)
|
||||
m3[b.Pair.Quote.Item] = book
|
||||
}
|
||||
book.SetLastUpdate(b.LastUpdated, b.LastUpdateID, true)
|
||||
book.LoadSnapshot(b.Bids, b.Asks)
|
||||
book.LoadSnapshot(b.Bids, b.Asks, b.LastUpdateID, b.LastUpdated, true)
|
||||
s.Unlock()
|
||||
return s.Mux.Publish([]uuid.UUID{m1.ID}, book.Retrieve())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user