github.com/bitfinexcom/bitfinex-api-go@v0.0.0-20210608095005-9e0b26f200fb/v2/websocket/orderbook.go (about) 1 package websocket 2 3 import ( 4 "hash/crc32" 5 "sort" 6 "strings" 7 "sync" 8 9 "github.com/bitfinexcom/bitfinex-api-go/pkg/models/book" 10 "github.com/bitfinexcom/bitfinex-api-go/pkg/models/common" 11 ) 12 13 type Orderbook struct { 14 lock sync.RWMutex 15 16 symbol string 17 bids []*book.Book 18 asks []*book.Book 19 } 20 21 // return a dereferenced copy of an orderbook side. This is so consumers can access 22 // the book but not change the values that are used to generate the crc32 checksum 23 func (ob *Orderbook) copySide(side []*book.Book) []book.Book { 24 var cpy []book.Book 25 for i := 0; i < len(side); i++ { 26 cpy = append(cpy, *side[i]) 27 } 28 return cpy 29 } 30 31 func (ob *Orderbook) Symbol() string { 32 return ob.symbol 33 } 34 35 func (ob *Orderbook) Asks() []book.Book { 36 ob.lock.RLock() 37 defer ob.lock.RUnlock() 38 return ob.copySide(ob.asks) 39 } 40 41 func (ob *Orderbook) Bids() []book.Book { 42 ob.lock.RLock() 43 defer ob.lock.RUnlock() 44 return ob.copySide(ob.bids) 45 } 46 47 func (ob *Orderbook) SetWithSnapshot(bs *book.Snapshot) { 48 ob.lock.Lock() 49 defer ob.lock.Unlock() 50 51 ob.bids = make([]*book.Book, 0) 52 ob.asks = make([]*book.Book, 0) 53 for _, order := range bs.Snapshot { 54 if order.Side == common.Bid { 55 ob.bids = append(ob.bids, order) 56 } else { 57 ob.asks = append(ob.asks, order) 58 } 59 } 60 } 61 62 func (ob *Orderbook) UpdateWith(b *book.Book) { 63 ob.lock.Lock() 64 defer ob.lock.Unlock() 65 66 side := &ob.asks 67 if b.Side == common.Bid { 68 side = &ob.bids 69 } 70 71 // check if first in book 72 if len(*side) == 0 { 73 *side = append(*side, b) 74 return 75 } 76 77 // match price level 78 for index, sOrder := range *side { 79 if sOrder.Price == b.Price { 80 if index+1 > len(*(side)) { 81 return 82 } 83 if b.Count <= 0 { 84 // delete if count is equal to zero 85 *side = append((*side)[:index], (*side)[index+1:]...) 86 return 87 } 88 // remove now and we will add in the code below 89 *side = append((*side)[:index], (*side)[index+1:]...) 90 } 91 } 92 *side = append(*side, b) 93 // add to the orderbook and sort lowest to highest 94 sort.Slice(*side, func(i, j int) bool { 95 if i >= len(*(side)) || j >= len(*(side)) { 96 return false 97 } 98 if b.Side == common.Ask { 99 return (*side)[i].Price < (*side)[j].Price 100 } 101 return (*side)[i].Price > (*side)[j].Price 102 }) 103 } 104 105 func (ob *Orderbook) Checksum() uint32 { 106 ob.lock.Lock() 107 defer ob.lock.Unlock() 108 var checksumItems []string 109 for i := 0; i < 25; i++ { 110 if len(ob.bids) > i { 111 // append bid 112 checksumItems = append(checksumItems, (ob.bids)[i].PriceJsNum.String()) 113 checksumItems = append(checksumItems, (ob.bids)[i].AmountJsNum.String()) 114 } 115 if len(ob.asks) > i { 116 // append ask 117 checksumItems = append(checksumItems, (ob.asks)[i].PriceJsNum.String()) 118 checksumItems = append(checksumItems, (ob.asks)[i].AmountJsNum.String()) 119 } 120 } 121 checksumStrings := strings.Join(checksumItems, ":") 122 return crc32.ChecksumIEEE([]byte(checksumStrings)) 123 }