mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-23 15:10:15 +00:00
orderbook/buffer: data integrity and resubscription pass (#910)
* orderbook/buffer: data integrity and resubscription pass * btcmarkets: REMOVE THAT LIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIINE!!!!!!!!!!!!!!!!! * buffer: reinstate publish, refaactor, invalidate more and comments * buffer/orderbook: improve update and snapshot performance. Move Update type to orderbook package to util. pointer through entire function calls. (cleanup). Change action string to uint8 for easier comparison. Add parsing helper. Update current test benchmark comments. * dispatch: change publish func to variadic id param * dispatch: remove sender receiver wait time as this adds overhead and complexity. update tests. * dispatch: don't create pointers for every job container * rpcserver: fix assertion issues with data publishing change * linter: fixes * glorious: nits addr * depth: change validation handling to incorporate and store err * linter: fix more issues * dispatch: fix race * travis: update before fetching * depth: wrap and return wrapped error in invalidate call and fix tests * btcmarkets: fix commenting * workflow: check * workflow: check * orderbook: check error * buffer/depth: return invalidation error and fix tests * gctcli: display errors on orderbook streams * buffer: remove unused types * orderbook/bitmex: shift function to bitmex * orderbook: Add specific comments to unexported functions that don't have locking require locking. * orderbook: restrict published data functionality to orderbook.Outbound interface * common: add assertion failure helper for error * dispatch: remove atomics, add mutex protection, remove add/remove worker, redo main tests * dispatch: export function * engine: revert and change sub logger to manager * engine: remove old test * dispatch: add common variable ;) * btcmarket: don't overflow int in tests on 32bit systems * ci: force 1.17.7 usage for go * Revert "ci: force 1.17.7 usage for go" This reverts commit af2f95563bf218cf2b9f36a9fcf3258e2c6a2d91. * golangci: bump version add and remove linter items * Revert "golangci: bump version add and remove linter items" This reverts commit 3c98bffc9d030e39faca0387ea40c151df2ab06b. * dispatch: remove unsused mutex from mux * order: slight optimizations * nits: glorious * dispatch: fix regression on uuid generation and input inline with master * linter: fix * linter: fix * glorious: nit - rm slice segration * account: fix test after merge * coinbasepro: revert change * account: close channel instead of needing a receiver, push alert in routine to prepare for waiter. Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io>
This commit is contained in:
@@ -24,6 +24,13 @@ var (
|
||||
errUpdateNoTargets = errors.New("update bid/ask targets cannot be nil")
|
||||
errDepthNotFound = errors.New("orderbook depth not found")
|
||||
errRESTOverwrite = errors.New("orderbook has been overwritten by REST protocol")
|
||||
errInvalidAction = errors.New("invalid action")
|
||||
errAmendFailure = errors.New("orderbook amend update failure")
|
||||
errDeleteFailure = errors.New("orderbook delete update failure")
|
||||
errInsertFailure = errors.New("orderbook insert update failure")
|
||||
errUpdateInsertFailure = errors.New("orderbook update/insert update failure")
|
||||
errRESTTimerLapse = errors.New("rest sync timer lapse with active websocket connection")
|
||||
errOrderbookFlushed = errors.New("orderbook flushed")
|
||||
)
|
||||
|
||||
// Setup sets private variables
|
||||
@@ -68,7 +75,7 @@ func (w *Orderbook) Setup(exchangeConfig *config.Exchange, c *Config, dataHandle
|
||||
}
|
||||
|
||||
// validate validates update against setup values
|
||||
func (w *Orderbook) validate(u *Update) error {
|
||||
func (w *Orderbook) validate(u *orderbook.Update) error {
|
||||
if u == nil {
|
||||
return fmt.Errorf(packageError, errUpdateIsNil)
|
||||
}
|
||||
@@ -80,7 +87,7 @@ func (w *Orderbook) validate(u *Update) error {
|
||||
|
||||
// Update updates a stored pointer to an orderbook.Depth struct containing a
|
||||
// linked list, this switches between the usage of a buffered update
|
||||
func (w *Orderbook) Update(u *Update) error {
|
||||
func (w *Orderbook) Update(u *orderbook.Update) error {
|
||||
if err := w.validate(u); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -110,8 +117,26 @@ func (w *Orderbook) Update(u *Update) error {
|
||||
// Checks for when the rest protocol overwrites a streaming dominated book
|
||||
// will stop updating book via incremental updates. This occurs because our
|
||||
// sync manager (engine/sync.go) timer has elapsed for streaming. Usually
|
||||
// because the book is highly illiquid. TODO: Book resubscribe on websocket.
|
||||
if book.ob.IsRestSnapshot() {
|
||||
// because the book is highly illiquid.
|
||||
isREST, err := book.ob.IsRESTSnapshot()
|
||||
if err != nil {
|
||||
if !errors.Is(err, orderbook.ErrOrderbookInvalid) {
|
||||
return err
|
||||
}
|
||||
// In the event a checksum or processing error invalidates the book, all
|
||||
// updates that could be stored in the websocket buffer, skip applying
|
||||
// until a new snapshot comes through.
|
||||
if w.verbose {
|
||||
log.Warnf(log.WebsocketMgr,
|
||||
"Exchange %s CurrencyPair: %s AssetType: %s underlying book is invalid, cannot apply update.",
|
||||
w.exchangeName,
|
||||
u.Pair,
|
||||
u.Asset)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if isREST {
|
||||
if w.verbose {
|
||||
log.Warnf(log.WebsocketMgr,
|
||||
"%s for Exchange %s CurrencyPair: %s AssetType: %s consider extending synctimeoutwebsocket",
|
||||
@@ -120,15 +145,16 @@ func (w *Orderbook) Update(u *Update) error {
|
||||
u.Pair,
|
||||
u.Asset)
|
||||
}
|
||||
return fmt.Errorf("%w for Exchange %s CurrencyPair: %s AssetType: %s",
|
||||
errRESTOverwrite,
|
||||
w.exchangeName,
|
||||
u.Pair,
|
||||
u.Asset)
|
||||
// Instance of illiquidity, this signal notifies that there is websocket
|
||||
// activity. We can invalidate the book and request a new snapshot. All
|
||||
// further updates through the websocket should be caught above in the
|
||||
// IsRestSnapshot() call.
|
||||
return book.ob.Invalidate(errRESTTimerLapse)
|
||||
}
|
||||
|
||||
if w.bufferEnabled {
|
||||
processed, err := w.processBufferUpdate(book, u)
|
||||
var processed bool
|
||||
processed, err = w.processBufferUpdate(book, u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -137,55 +163,54 @@ func (w *Orderbook) Update(u *Update) error {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
err := w.processObUpdate(book, u)
|
||||
err = w.processObUpdate(book, u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if book.ob.VerifyOrderbook { // This is used here so as to not retrieve
|
||||
// book if verification is off.
|
||||
// On every update, this will retrieve and verify orderbook depths
|
||||
err := book.ob.Retrieve().Verify()
|
||||
var ret *orderbook.Base
|
||||
if book.ob.VerifyOrderbook {
|
||||
// This is used here so as to not retrieve book if verification is off.
|
||||
// On every update, this will retrieve and verify orderbook depth.
|
||||
ret, err = book.ob.Retrieve()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// a nil ticker means that a zero publish period has been requested,
|
||||
// this means publish now whatever was received with no throttling
|
||||
if book.ticker == nil {
|
||||
go func() {
|
||||
w.dataHandler <- book.ob.Retrieve()
|
||||
book.ob.Publish()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
select {
|
||||
case <-book.ticker.C:
|
||||
// Opted to wait for receiver because we are limiting here and the sync
|
||||
// manager requires update
|
||||
go func() {
|
||||
w.dataHandler <- book.ob.Retrieve()
|
||||
book.ob.Publish()
|
||||
}()
|
||||
default:
|
||||
// We do not need to send an update to the sync manager within this time
|
||||
// window unless verbose is turned on
|
||||
if w.verbose {
|
||||
w.dataHandler <- book.ob.Retrieve()
|
||||
book.ob.Publish()
|
||||
err = ret.Verify()
|
||||
if err != nil {
|
||||
return book.ob.Invalidate(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Publish all state changes, disregarding verbosity or sync requirements.
|
||||
book.ob.Publish()
|
||||
|
||||
if book.ticker != nil {
|
||||
select {
|
||||
case <-book.ticker.C:
|
||||
// Send update to engine websocket manager to update engine
|
||||
// sync manager to reset websocket orderbook sync timeout. This will
|
||||
// stop the fall over to REST protocol fetching of orderbook data.
|
||||
default:
|
||||
if !w.verbose {
|
||||
// We do not need to send an update to the sync manager within
|
||||
// this time window unless verbose is turned on.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A nil ticker means that a zero publish period has been set and the entire
|
||||
// websocket updates will be sent to the engine websocket manager for
|
||||
// display purposes. Same as being verbose.
|
||||
w.dataHandler <- book.ob
|
||||
return nil
|
||||
}
|
||||
|
||||
// processBufferUpdate stores update into buffer, when buffer at capacity as
|
||||
// defined by w.obBufferLimit it well then sort and apply updates.
|
||||
func (w *Orderbook) processBufferUpdate(o *orderbookHolder, u *Update) (bool, error) {
|
||||
func (w *Orderbook) processBufferUpdate(o *orderbookHolder, u *orderbook.Update) (bool, error) {
|
||||
*o.buffer = append(*o.buffer, *u)
|
||||
if len(*o.buffer) < w.obBufferLimit {
|
||||
return false, nil
|
||||
@@ -216,16 +241,20 @@ func (w *Orderbook) processBufferUpdate(o *orderbookHolder, u *Update) (bool, er
|
||||
|
||||
// processObUpdate processes updates either by its corresponding id or by
|
||||
// price level
|
||||
func (w *Orderbook) processObUpdate(o *orderbookHolder, u *Update) error {
|
||||
func (w *Orderbook) processObUpdate(o *orderbookHolder, u *orderbook.Update) error {
|
||||
if w.updateEntriesByID {
|
||||
return o.updateByIDAndAction(u)
|
||||
}
|
||||
o.updateByPrice(u)
|
||||
if w.checksum != nil {
|
||||
err := w.checksum(o.ob.Retrieve(), u.Checksum)
|
||||
compare, err := o.ob.Retrieve()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = w.checksum(compare, u.Checksum)
|
||||
if err != nil {
|
||||
return o.ob.Invalidate(err)
|
||||
}
|
||||
o.updateID = u.UpdateID
|
||||
}
|
||||
return nil
|
||||
@@ -233,48 +262,50 @@ func (w *Orderbook) processObUpdate(o *orderbookHolder, u *Update) error {
|
||||
|
||||
// updateByPrice ammends amount if match occurs by price, deletes if amount is
|
||||
// zero or less and inserts if not found.
|
||||
func (o *orderbookHolder) updateByPrice(updts *Update) {
|
||||
o.ob.UpdateBidAskByPrice(updts.Bids,
|
||||
updts.Asks,
|
||||
updts.MaxDepth,
|
||||
updts.UpdateID,
|
||||
updts.UpdateTime)
|
||||
func (o *orderbookHolder) updateByPrice(updts *orderbook.Update) {
|
||||
o.ob.UpdateBidAskByPrice(updts)
|
||||
}
|
||||
|
||||
// updateByIDAndAction will receive an action to execute against the orderbook
|
||||
// it will then match by IDs instead of price to perform the action
|
||||
func (o *orderbookHolder) updateByIDAndAction(updts *Update) error {
|
||||
func (o *orderbookHolder) updateByIDAndAction(updts *orderbook.Update) error {
|
||||
switch updts.Action {
|
||||
case Amend:
|
||||
return o.ob.UpdateBidAskByID(updts.Bids,
|
||||
updts.Asks,
|
||||
updts.UpdateID,
|
||||
updts.UpdateTime)
|
||||
case Delete:
|
||||
case orderbook.Amend:
|
||||
err := o.ob.UpdateBidAskByID(updts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v %w", errAmendFailure, err)
|
||||
}
|
||||
case orderbook.Delete:
|
||||
// edge case for Bitfinex as their streaming endpoint duplicates deletes
|
||||
bypassErr := o.ob.GetName() == "Bitfinex" && o.ob.IsFundingRate()
|
||||
return o.ob.DeleteBidAskByID(updts.Bids,
|
||||
updts.Asks,
|
||||
bypassErr,
|
||||
updts.UpdateID,
|
||||
updts.UpdateTime)
|
||||
case Insert:
|
||||
return o.ob.InsertBidAskByID(updts.Bids,
|
||||
updts.Asks,
|
||||
updts.UpdateID,
|
||||
updts.UpdateTime)
|
||||
case UpdateInsert:
|
||||
return o.ob.UpdateInsertByID(updts.Bids,
|
||||
updts.Asks,
|
||||
updts.UpdateID,
|
||||
updts.UpdateTime)
|
||||
err := o.ob.DeleteBidAskByID(updts, bypassErr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v %w", errDeleteFailure, err)
|
||||
}
|
||||
case orderbook.Insert:
|
||||
err := o.ob.InsertBidAskByID(updts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v %w", errInsertFailure, err)
|
||||
}
|
||||
case orderbook.UpdateInsert:
|
||||
err := o.ob.UpdateInsertByID(updts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%v %w", errUpdateInsertFailure, err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid action [%s]", updts.Action)
|
||||
return fmt.Errorf("%w [%d]", errInvalidAction, updts.Action)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadSnapshot loads initial snapshot of orderbook data from websocket
|
||||
func (w *Orderbook) LoadSnapshot(book *orderbook.Base) error {
|
||||
// Checks if book can deploy to linked list
|
||||
err := book.Verify()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.m.Lock()
|
||||
defer w.m.Unlock()
|
||||
m1, ok := w.ob[book.Pair.Base]
|
||||
@@ -290,12 +321,13 @@ func (w *Orderbook) LoadSnapshot(book *orderbook.Base) error {
|
||||
holder, ok := m2[book.Asset]
|
||||
if !ok {
|
||||
// Associate orderbook pointer with local exchange depth map
|
||||
depth, err := orderbook.DeployDepth(book.Exchange, book.Pair, book.Asset)
|
||||
var depth *orderbook.Depth
|
||||
depth, err = orderbook.DeployDepth(book.Exchange, book.Pair, book.Asset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
depth.AssignOptions(book)
|
||||
buffer := make([]Update, w.obBufferLimit)
|
||||
buffer := make([]orderbook.Update, w.obBufferLimit)
|
||||
|
||||
var ticker *time.Ticker
|
||||
if w.publishPeriod != 0 {
|
||||
@@ -311,31 +343,28 @@ func (w *Orderbook) LoadSnapshot(book *orderbook.Base) error {
|
||||
|
||||
holder.updateID = book.LastUpdateID
|
||||
|
||||
// Checks if book can deploy to linked list
|
||||
err := book.Verify()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
holder.ob.LoadSnapshot(book.Bids,
|
||||
book.Asks,
|
||||
book.LastUpdateID,
|
||||
book.LastUpdated,
|
||||
false,
|
||||
)
|
||||
false)
|
||||
|
||||
if holder.ob.VerifyOrderbook { // This is used here so as to not retrieve
|
||||
// book if verification is off.
|
||||
if holder.ob.VerifyOrderbook {
|
||||
// This is used here so as to not retrieve book if verification is off.
|
||||
// Checks to see if orderbook snapshot that was deployed has not been
|
||||
// altered in any way
|
||||
err = holder.ob.Retrieve().Verify()
|
||||
book, err = holder.ob.Retrieve()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = book.Verify()
|
||||
if err != nil {
|
||||
return holder.ob.Invalidate(err)
|
||||
}
|
||||
}
|
||||
|
||||
w.dataHandler <- holder.ob.Retrieve()
|
||||
holder.ob.Publish()
|
||||
w.dataHandler <- holder.ob
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -351,7 +380,7 @@ func (w *Orderbook) GetOrderbook(p currency.Pair, a asset.Item) (*orderbook.Base
|
||||
a,
|
||||
errDepthNotFound)
|
||||
}
|
||||
return book.ob.Retrieve(), nil
|
||||
return book.ob.Retrieve()
|
||||
}
|
||||
|
||||
// FlushBuffer flushes w.ob data to be garbage collected and refreshed when a
|
||||
@@ -374,6 +403,7 @@ func (w *Orderbook) FlushOrderbook(p currency.Pair, a asset.Item) error {
|
||||
a,
|
||||
errDepthNotFound)
|
||||
}
|
||||
book.ob.Flush()
|
||||
// error not needed in this return
|
||||
_ = book.ob.Invalidate(errOrderbookFlushed)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package buffer
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -44,9 +45,14 @@ func createSnapshot() (holder *Orderbook, asks, bids orderbook.Items, err error)
|
||||
|
||||
newBook := make(map[currency.Code]map[currency.Code]map[asset.Item]*orderbookHolder)
|
||||
|
||||
ch := make(chan interface{})
|
||||
go func(<-chan interface{}) { // reader
|
||||
for range ch {
|
||||
}
|
||||
}(ch)
|
||||
holder = &Orderbook{
|
||||
exchangeName: exchangeName,
|
||||
dataHandler: make(chan interface{}, 100),
|
||||
dataHandler: ch,
|
||||
ob: newBook,
|
||||
}
|
||||
err = holder.LoadSnapshot(book)
|
||||
@@ -78,7 +84,7 @@ func BenchmarkUpdateBidsByPrice(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bidAsks := bidAskGenerator()
|
||||
update := &Update{
|
||||
update := &orderbook.Update{
|
||||
Bids: bidAsks,
|
||||
Asks: bidAsks,
|
||||
Pair: cp,
|
||||
@@ -98,7 +104,7 @@ func BenchmarkUpdateAsksByPrice(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
bidAsks := bidAskGenerator()
|
||||
update := &Update{
|
||||
update := &orderbook.Update{
|
||||
Bids: bidAsks,
|
||||
Asks: bidAsks,
|
||||
Pair: cp,
|
||||
@@ -112,14 +118,14 @@ func BenchmarkUpdateAsksByPrice(b *testing.B) {
|
||||
|
||||
// BenchmarkBufferPerformance demonstrates buffer more performant than multi
|
||||
// process calls
|
||||
// 4219518 287 ns/op 176 B/op 1 allocs/op
|
||||
// 890016 1688 ns/op 416 B/op 3 allocs/op
|
||||
func BenchmarkBufferPerformance(b *testing.B) {
|
||||
holder, asks, bids, err := createSnapshot()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
holder.bufferEnabled = true
|
||||
update := &Update{
|
||||
update := &orderbook.Update{
|
||||
Bids: bids,
|
||||
Asks: asks,
|
||||
Pair: cp,
|
||||
@@ -139,7 +145,7 @@ func BenchmarkBufferPerformance(b *testing.B) {
|
||||
}
|
||||
|
||||
// BenchmarkBufferSortingPerformance benchmark
|
||||
// 2693391 467 ns/op 208 B/op 2 allocs/op
|
||||
// 613964 2093 ns/op 440 B/op 4 allocs/op
|
||||
func BenchmarkBufferSortingPerformance(b *testing.B) {
|
||||
holder, asks, bids, err := createSnapshot()
|
||||
if err != nil {
|
||||
@@ -147,7 +153,7 @@ func BenchmarkBufferSortingPerformance(b *testing.B) {
|
||||
}
|
||||
holder.bufferEnabled = true
|
||||
holder.sortBuffer = true
|
||||
update := &Update{
|
||||
update := &orderbook.Update{
|
||||
Bids: bids,
|
||||
Asks: asks,
|
||||
Pair: cp,
|
||||
@@ -167,7 +173,7 @@ func BenchmarkBufferSortingPerformance(b *testing.B) {
|
||||
}
|
||||
|
||||
// BenchmarkBufferSortingPerformance benchmark
|
||||
// 1000000 1019 ns/op 208 B/op 2 allocs/op
|
||||
// 914500 1599 ns/op 440 B/op 4 allocs/op
|
||||
func BenchmarkBufferSortingByIDPerformance(b *testing.B) {
|
||||
holder, asks, bids, err := createSnapshot()
|
||||
if err != nil {
|
||||
@@ -176,7 +182,7 @@ func BenchmarkBufferSortingByIDPerformance(b *testing.B) {
|
||||
holder.bufferEnabled = true
|
||||
holder.sortBuffer = true
|
||||
holder.sortBufferByUpdateIDs = true
|
||||
update := &Update{
|
||||
update := &orderbook.Update{
|
||||
Bids: bids,
|
||||
Asks: asks,
|
||||
Pair: cp,
|
||||
@@ -197,13 +203,15 @@ func BenchmarkBufferSortingByIDPerformance(b *testing.B) {
|
||||
|
||||
// BenchmarkNoBufferPerformance demonstrates orderbook process more performant
|
||||
// than buffer
|
||||
// 9516966 141 ns/op 0 B/op 0 allocs/op
|
||||
// 122659 12792 ns/op 972 B/op 7 allocs/op PRIOR
|
||||
// 1225924 1028 ns/op 240 B/op 2 allocs/op CURRENT
|
||||
|
||||
func BenchmarkNoBufferPerformance(b *testing.B) {
|
||||
obl, asks, bids, err := createSnapshot()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
update := &Update{
|
||||
update := &orderbook.Update{
|
||||
Bids: bids,
|
||||
Asks: asks,
|
||||
Pair: cp,
|
||||
@@ -211,6 +219,7 @@ func BenchmarkNoBufferPerformance(b *testing.B) {
|
||||
Asset: asset.Spot,
|
||||
}
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
randomIndex := rand.Intn(4) // nolint:gosec // no need to import crypo/rand for testing
|
||||
update.Asks = itemArray[randomIndex]
|
||||
@@ -229,7 +238,7 @@ func TestUpdates(t *testing.T) {
|
||||
}
|
||||
|
||||
book := holder.ob[cp.Base][cp.Quote][asset.Spot]
|
||||
book.updateByPrice(&Update{
|
||||
book.updateByPrice(&orderbook.Update{
|
||||
Bids: itemArray[5],
|
||||
Asks: itemArray[5],
|
||||
Pair: cp,
|
||||
@@ -240,7 +249,7 @@ func TestUpdates(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
book.updateByPrice(&Update{
|
||||
book.updateByPrice(&orderbook.Update{
|
||||
Bids: itemArray[0],
|
||||
Asks: itemArray[0],
|
||||
Pair: cp,
|
||||
@@ -251,7 +260,12 @@ func TestUpdates(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if book.ob.GetAskLength() != 3 {
|
||||
askLen, err := book.ob.GetAskLength()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if askLen != 3 {
|
||||
t.Error("Did not update")
|
||||
}
|
||||
}
|
||||
@@ -267,7 +281,7 @@ func TestHittingTheBuffer(t *testing.T) {
|
||||
for i := range itemArray {
|
||||
asks := itemArray[i]
|
||||
bids := itemArray[i]
|
||||
err = holder.Update(&Update{
|
||||
err = holder.Update(&orderbook.Update{
|
||||
Bids: bids,
|
||||
Asks: asks,
|
||||
Pair: cp,
|
||||
@@ -280,11 +294,22 @@ func TestHittingTheBuffer(t *testing.T) {
|
||||
}
|
||||
|
||||
book := holder.ob[cp.Base][cp.Quote][asset.Spot]
|
||||
if book.ob.GetAskLength() != 3 {
|
||||
t.Errorf("expected 3 entries, received: %v", book.ob.GetAskLength())
|
||||
askLen, err := book.ob.GetAskLength()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
if book.ob.GetBidLength() != 3 {
|
||||
t.Errorf("expected 3 entries, received: %v", book.ob.GetBidLength())
|
||||
|
||||
if askLen != 3 {
|
||||
t.Errorf("expected 3 entries, received: %v", askLen)
|
||||
}
|
||||
|
||||
bidLen, err := book.ob.GetBidLength()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if bidLen != 3 {
|
||||
t.Errorf("expected 3 entries, received: %v", bidLen)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,13 +328,13 @@ func TestInsertWithIDs(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
bids := itemArray[i]
|
||||
err = holder.Update(&Update{
|
||||
err = holder.Update(&orderbook.Update{
|
||||
Bids: bids,
|
||||
Asks: asks,
|
||||
Pair: cp,
|
||||
UpdateTime: time.Now(),
|
||||
Asset: asset.Spot,
|
||||
Action: UpdateInsert,
|
||||
Action: orderbook.UpdateInsert,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -317,11 +342,20 @@ func TestInsertWithIDs(t *testing.T) {
|
||||
}
|
||||
|
||||
book := holder.ob[cp.Base][cp.Quote][asset.Spot]
|
||||
if book.ob.GetAskLength() != 6 {
|
||||
t.Errorf("expected 5 entries, received: %v", book.ob.GetAskLength())
|
||||
askLen, err := book.ob.GetAskLength()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
if book.ob.GetBidLength() != 6 {
|
||||
t.Errorf("expected 5 entries, received: %v", book.ob.GetBidLength())
|
||||
if askLen != 6 {
|
||||
t.Errorf("expected 6 entries, received: %v", askLen)
|
||||
}
|
||||
|
||||
bidLen, err := book.ob.GetBidLength()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
if bidLen != 6 {
|
||||
t.Errorf("expected 6 entries, received: %v", bidLen)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,7 +372,7 @@ func TestSortIDs(t *testing.T) {
|
||||
for i := range itemArray {
|
||||
asks := itemArray[i]
|
||||
bids := itemArray[i]
|
||||
err = holder.Update(&Update{
|
||||
err = holder.Update(&orderbook.Update{
|
||||
Bids: bids,
|
||||
Asks: asks,
|
||||
Pair: cp,
|
||||
@@ -350,11 +384,20 @@ func TestSortIDs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
book := holder.ob[cp.Base][cp.Quote][asset.Spot]
|
||||
if book.ob.GetAskLength() != 3 {
|
||||
t.Errorf("expected 3 entries, received: %v", book.ob.GetAskLength())
|
||||
askLen, err := book.ob.GetAskLength()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
if book.ob.GetAskLength() != 3 {
|
||||
t.Errorf("expected 3 entries, received: %v", book.ob.GetAskLength())
|
||||
if askLen != 3 {
|
||||
t.Errorf("expected 3 entries, received: %v", askLen)
|
||||
}
|
||||
|
||||
bidLen, err := book.ob.GetBidLength()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
if bidLen != 3 {
|
||||
t.Errorf("expected 3 entries, received: %v", bidLen)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,7 +417,7 @@ func TestOutOfOrderIDs(t *testing.T) {
|
||||
holder.obBufferLimit = 5
|
||||
for i := range itemArray {
|
||||
asks := itemArray[i]
|
||||
err = holder.Update(&Update{
|
||||
err = holder.Update(&orderbook.Update{
|
||||
Asks: asks,
|
||||
Pair: cp,
|
||||
UpdateID: outOFOrderIDs[i],
|
||||
@@ -385,15 +428,16 @@ func TestOutOfOrderIDs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
book := holder.ob[cp.Base][cp.Quote][asset.Spot]
|
||||
cpy := book.ob.Retrieve()
|
||||
cpy, err := book.ob.Retrieve()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
// Index 1 since index 0 is price 7000
|
||||
if cpy.Asks[1].Price != 2000 {
|
||||
t.Errorf("expected sorted price to be 2000, received: %v", cpy.Asks[1].Price)
|
||||
}
|
||||
}
|
||||
|
||||
var errTest = errors.New("test error")
|
||||
|
||||
func TestOrderbookLastUpdateID(t *testing.T) {
|
||||
holder, _, _, err := createSnapshot()
|
||||
if err != nil {
|
||||
@@ -404,16 +448,22 @@ func TestOrderbookLastUpdateID(t *testing.T) {
|
||||
exp, itemArray[1][0].Price)
|
||||
}
|
||||
|
||||
holder.checksum = func(state *orderbook.Base, checksum uint32) error { return errTest }
|
||||
holder.checksum = func(state *orderbook.Base, checksum uint32) error { return errors.New("testerino") }
|
||||
|
||||
err = holder.Update(&Update{
|
||||
// this update invalidates the book
|
||||
err = holder.Update(&orderbook.Update{
|
||||
Asks: []orderbook.Item{{Price: 999999}},
|
||||
Pair: cp,
|
||||
UpdateID: -1,
|
||||
Asset: asset.Spot,
|
||||
})
|
||||
if !errors.Is(err, errTest) {
|
||||
t.Fatalf("received: %v but expected: %v", err, errTest)
|
||||
if !errors.Is(err, orderbook.ErrOrderbookInvalid) {
|
||||
t.Fatalf("received: %v but expected: %v", err, orderbook.ErrOrderbookInvalid)
|
||||
}
|
||||
|
||||
holder, _, _, err = createSnapshot()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
holder.checksum = func(state *orderbook.Base, checksum uint32) error { return nil }
|
||||
@@ -421,7 +471,7 @@ func TestOrderbookLastUpdateID(t *testing.T) {
|
||||
|
||||
for i := range itemArray {
|
||||
asks := itemArray[i]
|
||||
err = holder.Update(&Update{
|
||||
err = holder.Update(&orderbook.Update{
|
||||
Asks: asks,
|
||||
Pair: cp,
|
||||
UpdateID: int64(i) + 1,
|
||||
@@ -434,7 +484,7 @@ func TestOrderbookLastUpdateID(t *testing.T) {
|
||||
|
||||
// out of order
|
||||
holder.verbose = true
|
||||
err = holder.Update(&Update{
|
||||
err = holder.Update(&orderbook.Update{
|
||||
Asks: []orderbook.Item{{Price: 999999}},
|
||||
Pair: cp,
|
||||
UpdateID: 1,
|
||||
@@ -470,7 +520,7 @@ func TestRunUpdateWithoutSnapshot(t *testing.T) {
|
||||
snapShot1.Asset = asset.Spot
|
||||
snapShot1.Pair = cp
|
||||
holder.exchangeName = exchangeName
|
||||
err := holder.Update(&Update{
|
||||
err := holder.Update(&orderbook.Update{
|
||||
Bids: bids,
|
||||
Asks: asks,
|
||||
Pair: cp,
|
||||
@@ -492,7 +542,7 @@ func TestRunUpdateWithoutAnyUpdates(t *testing.T) {
|
||||
snapShot1.Asset = asset.Spot
|
||||
snapShot1.Pair = cp
|
||||
obl.exchangeName = exchangeName
|
||||
err := obl.Update(&Update{
|
||||
err := obl.Update(&orderbook.Update{
|
||||
Bids: snapShot1.Asks,
|
||||
Asks: snapShot1.Bids,
|
||||
Pair: cp,
|
||||
@@ -729,9 +779,21 @@ func TestGetOrderbook(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bufferOb := holder.ob[cp.Base][cp.Quote][asset.Spot]
|
||||
b := bufferOb.ob.Retrieve()
|
||||
if bufferOb.ob.GetAskLength() != len(ob.Asks) ||
|
||||
bufferOb.ob.GetBidLength() != len(ob.Bids) ||
|
||||
b, err := bufferOb.ob.Retrieve()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
askLen, err := bufferOb.ob.GetAskLength()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
bidLen, err := bufferOb.ob.GetBidLength()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
if askLen != len(ob.Asks) ||
|
||||
bidLen != len(ob.Bids) ||
|
||||
b.Asset != ob.Asset ||
|
||||
b.Exchange != ob.Exchange ||
|
||||
b.LastUpdateID != ob.LastUpdateID ||
|
||||
@@ -795,7 +857,7 @@ func TestValidate(t *testing.T) {
|
||||
t.Fatalf("expected error %v but received %v", errUpdateIsNil, err)
|
||||
}
|
||||
|
||||
err = w.validate(&Update{})
|
||||
err = w.validate(&orderbook.Update{})
|
||||
if !errors.Is(err, errUpdateNoTargets) {
|
||||
t.Fatalf("expected error %v but received %v", errUpdateNoTargets, err)
|
||||
}
|
||||
@@ -810,7 +872,7 @@ func TestEnsureMultipleUpdatesViaPrice(t *testing.T) {
|
||||
|
||||
asks := bidAskGenerator()
|
||||
book := holder.ob[cp.Base][cp.Quote][asset.Spot]
|
||||
book.updateByPrice(&Update{
|
||||
book.updateByPrice(&orderbook.Update{
|
||||
Bids: asks,
|
||||
Asks: asks,
|
||||
Pair: cp,
|
||||
@@ -821,7 +883,12 @@ func TestEnsureMultipleUpdatesViaPrice(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if book.ob.GetAskLength() <= 3 {
|
||||
askLen, err := book.ob.GetAskLength()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if askLen <= 3 {
|
||||
t.Errorf("Insufficient updates")
|
||||
}
|
||||
}
|
||||
@@ -851,20 +918,25 @@ func TestUpdateByIDAndAction(t *testing.T) {
|
||||
|
||||
book.LoadSnapshot(append(bids[:0:0], bids...), append(asks[:0:0], asks...), 0, time.Time{}, true)
|
||||
|
||||
err = book.Retrieve().Verify()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
ob, err := book.Retrieve()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
err = ob.Verify()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
holder.ob = book
|
||||
|
||||
err = holder.updateByIDAndAction(&Update{})
|
||||
if err == nil {
|
||||
t.Fatal("error cannot be nil")
|
||||
err = holder.updateByIDAndAction(&orderbook.Update{})
|
||||
if !errors.Is(err, errInvalidAction) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, errInvalidAction)
|
||||
}
|
||||
|
||||
err = holder.updateByIDAndAction(&Update{
|
||||
Action: Amend,
|
||||
err = holder.updateByIDAndAction(&orderbook.Update{
|
||||
Action: orderbook.Amend,
|
||||
Bids: []orderbook.Item{
|
||||
{
|
||||
Price: 100,
|
||||
@@ -872,13 +944,14 @@ func TestUpdateByIDAndAction(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("error cannot be nil")
|
||||
if !strings.Contains(err.Error(), errAmendFailure.Error()) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, errAmendFailure)
|
||||
}
|
||||
|
||||
book.LoadSnapshot(append(bids[:0:0], bids...), append(asks[:0:0], asks...), 0, time.Time{}, true)
|
||||
// append to slice
|
||||
err = holder.updateByIDAndAction(&Update{
|
||||
Action: UpdateInsert,
|
||||
err = holder.updateByIDAndAction(&orderbook.Update{
|
||||
Action: orderbook.UpdateInsert,
|
||||
Bids: []orderbook.Item{
|
||||
{
|
||||
Price: 0,
|
||||
@@ -894,11 +967,14 @@ func TestUpdateByIDAndAction(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
cpy := book.Retrieve()
|
||||
cpy, err := book.Retrieve()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if cpy.Bids[len(cpy.Bids)-1].Price != 0 {
|
||||
t.Fatal("did not append bid item")
|
||||
@@ -908,8 +984,8 @@ func TestUpdateByIDAndAction(t *testing.T) {
|
||||
}
|
||||
|
||||
// Change amount
|
||||
err = holder.updateByIDAndAction(&Update{
|
||||
Action: UpdateInsert,
|
||||
err = holder.updateByIDAndAction(&orderbook.Update{
|
||||
Action: orderbook.UpdateInsert,
|
||||
Bids: []orderbook.Item{
|
||||
{
|
||||
Price: 0,
|
||||
@@ -925,11 +1001,14 @@ func TestUpdateByIDAndAction(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
cpy = book.Retrieve()
|
||||
cpy, err = book.Retrieve()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if cpy.Bids[len(cpy.Bids)-1].Amount != 100 {
|
||||
t.Fatal("did not update bid amount", cpy.Bids[len(cpy.Bids)-1].Amount)
|
||||
@@ -940,8 +1019,8 @@ func TestUpdateByIDAndAction(t *testing.T) {
|
||||
}
|
||||
|
||||
// Change price level
|
||||
err = holder.updateByIDAndAction(&Update{
|
||||
Action: UpdateInsert,
|
||||
err = holder.updateByIDAndAction(&orderbook.Update{
|
||||
Action: orderbook.UpdateInsert,
|
||||
Bids: []orderbook.Item{
|
||||
{
|
||||
Price: 100,
|
||||
@@ -957,11 +1036,14 @@ func TestUpdateByIDAndAction(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
cpy = book.Retrieve()
|
||||
cpy, err = book.Retrieve()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if cpy.Bids[0].Amount != 99 && cpy.Bids[0].Price != 100 {
|
||||
t.Fatal("did not adjust bid item placement and details")
|
||||
@@ -972,10 +1054,9 @@ func TestUpdateByIDAndAction(t *testing.T) {
|
||||
}
|
||||
|
||||
book.LoadSnapshot(append(bids[:0:0], bids...), append(bids[:0:0], bids...), 0, time.Time{}, true) // nolint:gocritic
|
||||
|
||||
// Delete - not found
|
||||
err = holder.updateByIDAndAction(&Update{
|
||||
Action: Delete,
|
||||
err = holder.updateByIDAndAction(&orderbook.Update{
|
||||
Action: orderbook.Delete,
|
||||
Asks: []orderbook.Item{
|
||||
{
|
||||
Price: 0,
|
||||
@@ -984,54 +1065,54 @@ func TestUpdateByIDAndAction(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("error cannot be nil")
|
||||
}
|
||||
err = holder.updateByIDAndAction(&Update{
|
||||
Action: Delete,
|
||||
Bids: []orderbook.Item{
|
||||
{
|
||||
Price: 0,
|
||||
ID: 1337,
|
||||
Amount: 99,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("error cannot be nil")
|
||||
if !strings.Contains(err.Error(), errDeleteFailure.Error()) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, errDeleteFailure)
|
||||
}
|
||||
|
||||
book.LoadSnapshot(append(bids[:0:0], bids...), append(bids[:0:0], bids...), 0, time.Time{}, true) // nolint:gocritic
|
||||
// Delete - found
|
||||
err = holder.updateByIDAndAction(&Update{
|
||||
Action: Delete,
|
||||
err = holder.updateByIDAndAction(&orderbook.Update{
|
||||
Action: orderbook.Delete,
|
||||
Asks: []orderbook.Item{
|
||||
asks[0],
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if book.GetAskLength() != 99 {
|
||||
askLen, err := book.GetAskLength()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if askLen != 99 {
|
||||
t.Fatal("element not deleted")
|
||||
}
|
||||
|
||||
// Apply update
|
||||
err = holder.updateByIDAndAction(&Update{
|
||||
Action: Amend,
|
||||
err = holder.updateByIDAndAction(&orderbook.Update{
|
||||
Action: orderbook.Amend,
|
||||
Asks: []orderbook.Item{
|
||||
{ID: 123456},
|
||||
},
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal("error cannot be nil")
|
||||
if !strings.Contains(err.Error(), errAmendFailure.Error()) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, errAmendFailure)
|
||||
}
|
||||
|
||||
update := book.Retrieve().Asks[0]
|
||||
book.LoadSnapshot(bids, bids, 0, time.Time{}, true)
|
||||
|
||||
ob, err = book.Retrieve()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
update := ob.Asks[0]
|
||||
update.Amount = 1337
|
||||
|
||||
err = holder.updateByIDAndAction(&Update{
|
||||
Action: Amend,
|
||||
err = holder.updateByIDAndAction(&orderbook.Update{
|
||||
Action: orderbook.Amend,
|
||||
Asks: []orderbook.Item{
|
||||
update,
|
||||
},
|
||||
@@ -1040,7 +1121,12 @@ func TestUpdateByIDAndAction(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if book.Retrieve().Asks[0].Amount != 1337 {
|
||||
ob, err = book.Retrieve()
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, nil)
|
||||
}
|
||||
|
||||
if ob.Asks[0].Amount != 1337 {
|
||||
t.Fatal("element not updated")
|
||||
}
|
||||
}
|
||||
@@ -1086,12 +1172,8 @@ func TestFlushOrderbook(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
o, err := w.GetOrderbook(cp, asset.Spot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(o.Bids) != 0 || len(o.Asks) != 0 {
|
||||
t.Fatal("orderbook items not flushed")
|
||||
_, err = w.GetOrderbook(cp, asset.Spot)
|
||||
if !errors.Is(err, orderbook.ErrOrderbookInvalid) {
|
||||
t.Fatalf("received: '%v' but expected: '%v'", err, orderbook.ErrOrderbookInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ type Orderbook struct {
|
||||
// orderbook depth
|
||||
type orderbookHolder struct {
|
||||
ob *orderbook.Depth
|
||||
buffer *[]Update
|
||||
buffer *[]orderbook.Update
|
||||
// Reduces the amount of outbound alerts to the data handler for example
|
||||
// coinbasepro can have up too 100 updates per second introducing overhead.
|
||||
// The sync agent only requires an alert every 15 seconds for a specific
|
||||
@@ -62,36 +62,3 @@ type orderbookHolder struct {
|
||||
ticker *time.Ticker
|
||||
updateID int64
|
||||
}
|
||||
|
||||
// Update stores orderbook updates and dictates what features to use when processing
|
||||
type Update struct {
|
||||
UpdateID int64 // Used when no time is provided
|
||||
UpdateTime time.Time
|
||||
Asset asset.Item
|
||||
Action
|
||||
Bids []orderbook.Item
|
||||
Asks []orderbook.Item
|
||||
Pair currency.Pair
|
||||
// Checksum defines the expected value when the books have been verified
|
||||
Checksum uint32
|
||||
// Determines if there is a max depth of orderbooks and after an append we
|
||||
// should remove any items that are outside of this scope. Kraken is the
|
||||
// only exchange utilising this field.
|
||||
MaxDepth int
|
||||
}
|
||||
|
||||
// Action defines a set of differing states required to implement an incoming
|
||||
// orderbook update used in conjunction with UpdateEntriesByID
|
||||
type Action string
|
||||
|
||||
const (
|
||||
// Amend applies amount adjustment by ID
|
||||
Amend Action = "update"
|
||||
// Delete removes price level from book by ID
|
||||
Delete Action = "delete"
|
||||
// Insert adds price level to book
|
||||
Insert Action = "insert"
|
||||
// UpdateInsert on conflict applies amount adjustment or appends new amount
|
||||
// to book
|
||||
UpdateInsert Action = "update/insert"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user