mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-29 23:16:51 +00:00
Websocket orderbook buffering (#333)
* Initial commit setting up a map orderbook system with a buffer. It will write to the buffer, sort apply to main orderbook and then process. * Moves namespaces again * Updates orderbook to use a sweet new WebsocketOrderbookUpdate type to handle all updates whether its using ID or not. So good. Adds many tests * Starting to implement orderbook update handling per exchange. Updates namespaces again. Hopefuylly will find a way to update via ID not timestamp, too many endpoints dont provide update timestamps * Changes orderbookbuffer to use BufferUpdate type instead of orderbook.Base to achieve more functionality and no need for type conversion functions. Updates tests * Updates all instances of ws.orderbook.Update. Simplifies some orderbook logic * Introduces toggleable buffer. Renames orderbooks. Completes implementation for everywhere but OKGroup due to hash calculation * Implements orderbook update for okgroup, but forgets about the orderbook hash checking * Fixes okgroup checksum calculation. Fixes linting issue. Removes redundant Kraken tests. * Introduces sorting toggle and separates from buffer toggle. Uses benchmarks to highlight performance gains * Fixes Gemini rate limit and parsing. Removes comments and fixes typos * Fixes bitfinex orderbook processing * Inbuilt sorting, minor fixes for websocket implementations. Improves test coverage * Adds surprise LakeBTC websocket support * Fixes data race * Fixes rebasing issues due to namespace movements * Addresses PR nits: moves folder namespace from ws to websocket. Removes line spaces in imports. Fixes lakebtc websocket returns and defer fucntions. Fixes comments * Adds poloniex orderook sorting support * Enables bitstamp and hitbtc orderbook sorting. Fixes poloniex's sorting * Renames namespaces and combines monitor and connection into wshandler. Removes unused SPOT const. Changes how orderbook stuff is loaded. It is done in startup with a setup. Removes exchange name from loadsnapshot as well * Removes the connection.go from rebasing issues. Removes error response from functions used in goroutines * Fixes test with exchange name output change * Fixes issues where copy and paste and replace all were used poorly
This commit is contained in:
@@ -15,7 +15,7 @@ import (
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/wshandler"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
|
||||
log "github.com/thrasher-corp/gocryptotrader/logger"
|
||||
)
|
||||
|
||||
@@ -143,6 +143,7 @@ func (b *Bitmex) SetDefaults() {
|
||||
wshandler.WebsocketDeadMansSwitchSupported
|
||||
b.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
|
||||
b.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
|
||||
b.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
|
||||
}
|
||||
|
||||
// Setup takes in the supplied exchange configuration details and sets params
|
||||
@@ -201,6 +202,13 @@ func (b *Bitmex) Setup(exch *config.ExchangeConfig) {
|
||||
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
|
||||
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
|
||||
}
|
||||
b.Websocket.Orderbook.Setup(
|
||||
exch.WebsocketOrderbookBufferLimit,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
exch.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/wshandler"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
|
||||
)
|
||||
|
||||
// Please supply your own keys here for due diligence testing
|
||||
|
||||
@@ -13,7 +13,8 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/wshandler"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook"
|
||||
log "github.com/thrasher-corp/gocryptotrader/logger"
|
||||
)
|
||||
|
||||
@@ -220,23 +221,22 @@ func (b *Bitmex) wsHandleIncomingData() {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, trade := range trades.Data {
|
||||
for i := range trades.Data {
|
||||
var timestamp time.Time
|
||||
timestamp, err = time.Parse(time.RFC3339, trade.Timestamp)
|
||||
timestamp, err = time.Parse(time.RFC3339, trades.Data[i].Timestamp)
|
||||
if err != nil {
|
||||
b.Websocket.DataHandler <- err
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO: update this to support multiple asset types
|
||||
b.Websocket.DataHandler <- wshandler.TradeData{
|
||||
Timestamp: timestamp,
|
||||
Price: trade.Price,
|
||||
Amount: float64(trade.Size),
|
||||
CurrencyPair: currency.NewPairFromString(trade.Symbol),
|
||||
Price: trades.Data[i].Price,
|
||||
Amount: float64(trades.Data[i].Size),
|
||||
CurrencyPair: currency.NewPairFromString(trades.Data[i].Symbol),
|
||||
Exchange: b.GetName(),
|
||||
AssetType: "CONTRACT",
|
||||
Side: trade.Side,
|
||||
Side: trades.Data[i].Side,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,99 +326,80 @@ func (b *Bitmex) wsHandleIncomingData() {
|
||||
}
|
||||
}
|
||||
|
||||
var snapshotloaded = make(map[currency.Pair]map[string]bool)
|
||||
|
||||
// ProcessOrderbook processes orderbook updates
|
||||
func (b *Bitmex) processOrderbook(data []OrderBookL2, action string, currencyPair currency.Pair, assetType string) error { // nolint: unparam
|
||||
if len(data) < 1 {
|
||||
return errors.New("bitmex_websocket.go error - no orderbook data")
|
||||
}
|
||||
|
||||
_, ok := snapshotloaded[currencyPair]
|
||||
if !ok {
|
||||
snapshotloaded[currencyPair] = make(map[string]bool)
|
||||
}
|
||||
|
||||
_, ok = snapshotloaded[currencyPair][assetType]
|
||||
if !ok {
|
||||
snapshotloaded[currencyPair][assetType] = false
|
||||
}
|
||||
|
||||
switch action {
|
||||
case bitmexActionInitialData:
|
||||
if !snapshotloaded[currencyPair][assetType] {
|
||||
var newOrderBook orderbook.Base
|
||||
var bids, asks []orderbook.Item
|
||||
|
||||
for _, orderbookItem := range data {
|
||||
if strings.EqualFold(orderbookItem.Side, exchange.SellOrderSide.ToString()) {
|
||||
asks = append(asks, orderbook.Item{
|
||||
Price: orderbookItem.Price,
|
||||
Amount: float64(orderbookItem.Size),
|
||||
})
|
||||
continue
|
||||
}
|
||||
bids = append(bids, orderbook.Item{
|
||||
Price: orderbookItem.Price,
|
||||
Amount: float64(orderbookItem.Size),
|
||||
var newOrderBook orderbook.Base
|
||||
var bids, asks []orderbook.Item
|
||||
for i := range data {
|
||||
if strings.EqualFold(data[i].Side, exchange.SellOrderSide.ToString()) {
|
||||
asks = append(asks, orderbook.Item{
|
||||
Price: data[i].Price,
|
||||
Amount: float64(data[i].Size),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if len(bids) == 0 || len(asks) == 0 {
|
||||
return errors.New("bitmex_websocket.go error - snapshot not initialised correctly")
|
||||
}
|
||||
|
||||
newOrderBook.Asks = asks
|
||||
newOrderBook.Bids = bids
|
||||
newOrderBook.AssetType = assetType
|
||||
newOrderBook.Pair = currencyPair
|
||||
|
||||
err := b.Websocket.Orderbook.LoadSnapshot(&newOrderBook, b.GetName(), false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bitmex_websocket.go process orderbook error - %s",
|
||||
err)
|
||||
}
|
||||
snapshotloaded[currencyPair][assetType] = true
|
||||
b.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
|
||||
Pair: currencyPair,
|
||||
Asset: assetType,
|
||||
Exchange: b.GetName(),
|
||||
}
|
||||
bids = append(bids, orderbook.Item{
|
||||
Price: data[i].Price,
|
||||
Amount: float64(data[i].Size),
|
||||
})
|
||||
}
|
||||
|
||||
if len(bids) == 0 || len(asks) == 0 {
|
||||
return errors.New("bitmex_websocket.go error - snapshot not initialised correctly")
|
||||
}
|
||||
|
||||
newOrderBook.Asks = asks
|
||||
newOrderBook.Bids = bids
|
||||
newOrderBook.AssetType = assetType
|
||||
newOrderBook.Pair = currencyPair
|
||||
err := b.Websocket.Orderbook.LoadSnapshot(&newOrderBook, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bitmex_websocket.go process orderbook error - %s",
|
||||
err)
|
||||
}
|
||||
b.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
|
||||
Pair: currencyPair,
|
||||
Asset: assetType,
|
||||
Exchange: b.GetName(),
|
||||
}
|
||||
default:
|
||||
if snapshotloaded[currencyPair][assetType] {
|
||||
var asks, bids []orderbook.Item
|
||||
for _, orderbookItem := range data {
|
||||
if orderbookItem.Side == "Sell" {
|
||||
asks = append(asks, orderbook.Item{
|
||||
Price: orderbookItem.Price,
|
||||
Amount: float64(orderbookItem.Size),
|
||||
})
|
||||
continue
|
||||
}
|
||||
bids = append(bids, orderbook.Item{
|
||||
Price: orderbookItem.Price,
|
||||
Amount: float64(orderbookItem.Size),
|
||||
var asks, bids []orderbook.Item
|
||||
for i := range data {
|
||||
if strings.EqualFold(data[i].Side, "Sell") {
|
||||
asks = append(asks, orderbook.Item{
|
||||
Price: data[i].Price,
|
||||
Amount: float64(data[i].Size),
|
||||
})
|
||||
continue
|
||||
}
|
||||
bids = append(bids, orderbook.Item{
|
||||
Price: data[i].Price,
|
||||
Amount: float64(data[i].Size),
|
||||
})
|
||||
}
|
||||
|
||||
err := b.Websocket.Orderbook.UpdateUsingID(bids,
|
||||
asks,
|
||||
currencyPair,
|
||||
b.GetName(),
|
||||
assetType,
|
||||
action)
|
||||
err := b.Websocket.Orderbook.Update(&wsorderbook.WebsocketOrderbookUpdate{
|
||||
Bids: bids,
|
||||
Asks: asks,
|
||||
CurrencyPair: currencyPair,
|
||||
UpdateTime: time.Now(),
|
||||
AssetType: assetType,
|
||||
Action: action,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
|
||||
Pair: currencyPair,
|
||||
Asset: assetType,
|
||||
Exchange: b.GetName(),
|
||||
}
|
||||
b.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
|
||||
Pair: currencyPair,
|
||||
Asset: assetType,
|
||||
Exchange: b.GetName(),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/wshandler"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
|
||||
log "github.com/thrasher-corp/gocryptotrader/logger"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user