mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-08 23:16:54 +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:
@@ -16,7 +16,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"
|
||||
)
|
||||
|
||||
@@ -100,7 +100,7 @@ func (k *Kraken) SetDefaults() {
|
||||
wshandler.WebsocketMessageCorrelationSupported
|
||||
k.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
|
||||
k.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
|
||||
|
||||
k.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
|
||||
}
|
||||
|
||||
// Setup sets current exchange configuration
|
||||
@@ -161,6 +161,13 @@ func (k *Kraken) Setup(exch *config.ExchangeConfig) {
|
||||
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
|
||||
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
|
||||
}
|
||||
k.Websocket.Orderbook.Setup(
|
||||
exch.WebsocketOrderbookBufferLimit,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
exch.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
package kraken
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"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"
|
||||
)
|
||||
|
||||
var k Kraken
|
||||
@@ -647,107 +644,6 @@ func TestWithdrawCancel(t *testing.T) {
|
||||
|
||||
// ---------------------------- Websocket tests -----------------------------------------
|
||||
|
||||
// TestOrderbookBufferReset websocket test
|
||||
func TestOrderbookBufferReset(t *testing.T) {
|
||||
if k.Name == "" {
|
||||
k.SetDefaults()
|
||||
TestSetup(t)
|
||||
}
|
||||
if !k.Websocket.IsEnabled() {
|
||||
t.Skip("Websocket not enabled, skipping")
|
||||
}
|
||||
var obUpdates []string
|
||||
obpartial := `[0,{"as":[["5541.30000","2.50700000","0"]],"bs":[["5541.20000","1.52900000","0"]]}]`
|
||||
for i := 1; i < orderbookBufferLimit+2; i++ {
|
||||
obUpdates = append(obUpdates, fmt.Sprintf(`[0,{"a":[["5541.30000","2.50700000","%v"]],"b":[["5541.30000","1.00000000","%v"]]}]`, i, i))
|
||||
}
|
||||
k.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
|
||||
var dataResponse WebsocketDataResponse
|
||||
err := common.JSONDecode([]byte(obpartial), &dataResponse)
|
||||
if err != nil {
|
||||
t.Errorf("Could not parse, %v", err)
|
||||
}
|
||||
obData := dataResponse[1].(map[string]interface{})
|
||||
channelData := WebsocketChannelData{
|
||||
ChannelID: 0,
|
||||
Subscription: "orderbook",
|
||||
Pair: currency.NewPairWithDelimiter("XBT", "USD", "/"),
|
||||
}
|
||||
|
||||
k.wsProcessOrderBookPartial(
|
||||
&channelData,
|
||||
obData,
|
||||
)
|
||||
|
||||
for i := 0; i < len(obUpdates); i++ {
|
||||
err = common.JSONDecode([]byte(obUpdates[i]), &dataResponse)
|
||||
if err != nil {
|
||||
t.Errorf("Could not parse, %v", err)
|
||||
}
|
||||
obData = dataResponse[1].(map[string]interface{})
|
||||
if i < len(obUpdates)-1 {
|
||||
k.wsProcessOrderBookBuffer(&channelData, obData)
|
||||
} else if i == len(obUpdates)-1 {
|
||||
k.wsProcessOrderBookUpdate(&channelData)
|
||||
k.wsProcessOrderBookBuffer(&channelData, obData)
|
||||
if len(orderbookBuffer[channelData.ChannelID]) != 1 {
|
||||
t.Error("Buffer should have 1 entry after being reset")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestOrderbookBufferReset websocket test
|
||||
func TestOrderBookOutOfOrder(t *testing.T) {
|
||||
if k.Name == "" {
|
||||
k.SetDefaults()
|
||||
TestSetup(t)
|
||||
}
|
||||
if !k.Websocket.IsEnabled() {
|
||||
t.Skip("Websocket not enabled, skipping")
|
||||
}
|
||||
obpartial := `[0,{"as":[["5541.30000","2.50700000","0"]],"bs":[["5541.20000","1.52900000","5"]]}]`
|
||||
obupdate1 := `[0,{"a":[["5541.30000","0.00000000","1"]],"b":[["5541.30000","0.00000000","3"]]}]`
|
||||
obupdate2 := `[0,{"a":[["5541.30000","2.50700000","2"]],"b":[["5541.30000","0.00000000","1"]]}]`
|
||||
|
||||
k.Websocket.DataHandler = sharedtestvalues.GetWebsocketInterfaceChannelOverride()
|
||||
var dataResponse WebsocketDataResponse
|
||||
err := common.JSONDecode([]byte(obpartial), &dataResponse)
|
||||
if err != nil {
|
||||
t.Errorf("Could not parse, %v", err)
|
||||
}
|
||||
obData := dataResponse[1].(map[string]interface{})
|
||||
channelData := WebsocketChannelData{
|
||||
ChannelID: 0,
|
||||
Subscription: "orderbook",
|
||||
Pair: currency.NewPairWithDelimiter("XBT", "USD", "/"),
|
||||
}
|
||||
|
||||
k.wsProcessOrderBookPartial(
|
||||
&channelData,
|
||||
obData,
|
||||
)
|
||||
|
||||
err = common.JSONDecode([]byte(obupdate1), &dataResponse)
|
||||
if err != nil {
|
||||
t.Errorf("Could not parse, %v", err)
|
||||
}
|
||||
obData = dataResponse[1].(map[string]interface{})
|
||||
k.wsProcessOrderBookBuffer(&channelData, obData)
|
||||
|
||||
err = common.JSONDecode([]byte(obupdate2), &dataResponse)
|
||||
if err != nil {
|
||||
t.Errorf("Could not parse, %v", err)
|
||||
}
|
||||
obData = dataResponse[1].(map[string]interface{})
|
||||
k.wsProcessOrderBookBuffer(&channelData, obData)
|
||||
|
||||
err = k.wsProcessOrderBookUpdate(&channelData)
|
||||
if !strings.Contains(err.Error(), "orderbook update out of order") {
|
||||
t.Error("Expected out of order orderbook error")
|
||||
}
|
||||
}
|
||||
|
||||
func setupWsTests(t *testing.T) {
|
||||
if wsSetupRan {
|
||||
return
|
||||
|
||||
@@ -5,16 +5,15 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -40,21 +39,13 @@ const (
|
||||
krakenWsSpread = "spread"
|
||||
krakenWsOrderbook = "book"
|
||||
// Only supported asset type
|
||||
krakenWsAssetType = orderbook.Spot
|
||||
orderbookBufferLimit = 3
|
||||
krakenWsRateLimit = 50
|
||||
)
|
||||
|
||||
// orderbookMutex Ensures if two entries arrive at once, only one can be processed at a time
|
||||
var orderbookMutex sync.Mutex
|
||||
var subscriptionChannelPair []WebsocketChannelData
|
||||
|
||||
// krakenOrderBooks TODO THIS IS A TEMPORARY SOLUTION UNTIL ENGINE BRANCH IS MERGED
|
||||
// WS orderbook data can only rely on WS orderbook data
|
||||
// Currently REST and WS runs simultaneously, dirtying the data
|
||||
var krakenOrderBooks map[int64]orderbook.Base
|
||||
|
||||
// orderbookBuffer Stores orderbook updates per channel
|
||||
var orderbookBuffer map[int64][]orderbook.Base
|
||||
var subscribeToDefaultChannels = true
|
||||
|
||||
// Channels require a topic and a currency
|
||||
@@ -227,22 +218,6 @@ func (k *Kraken) WsHandleEventResponse(response *WebsocketEventResponse, rawResp
|
||||
// addNewSubscriptionChannelData stores channel ids, pairs and subscription types to an array
|
||||
// allowing correlation between subscriptions and returned data
|
||||
func addNewSubscriptionChannelData(response *WebsocketEventResponse) {
|
||||
for i := range subscriptionChannelPair {
|
||||
if response.ChannelID != subscriptionChannelPair[i].ChannelID {
|
||||
continue
|
||||
}
|
||||
// kill the stale orderbooks due to resubscribing
|
||||
if orderbookBuffer == nil {
|
||||
orderbookBuffer = make(map[int64][]orderbook.Base)
|
||||
}
|
||||
orderbookBuffer[response.ChannelID] = []orderbook.Base{}
|
||||
if krakenOrderBooks == nil {
|
||||
krakenOrderBooks = make(map[int64]orderbook.Base)
|
||||
}
|
||||
krakenOrderBooks[response.ChannelID] = orderbook.Base{}
|
||||
return
|
||||
}
|
||||
|
||||
// We change the / to - to maintain compatibility with REST/config
|
||||
pair := currency.NewPairWithDelimiter(response.Pair.Base.String(), response.Pair.Quote.String(), "-")
|
||||
subscriptionChannelPair = append(subscriptionChannelPair, WebsocketChannelData{
|
||||
@@ -346,16 +321,13 @@ func (k *Kraken) wsProcessOrderBook(channelData *WebsocketChannelData, data inte
|
||||
if asksExist || bidsExist {
|
||||
k.wsRequestMtx.Lock()
|
||||
defer k.wsRequestMtx.Unlock()
|
||||
k.wsProcessOrderBookBuffer(channelData, obData)
|
||||
if len(orderbookBuffer[channelData.ChannelID]) >= orderbookBufferLimit {
|
||||
err := k.wsProcessOrderBookUpdate(channelData)
|
||||
if err != nil {
|
||||
subscriptionToRemove := wshandler.WebsocketChannelSubscription{
|
||||
Channel: krakenWsOrderbook,
|
||||
Currency: channelData.Pair,
|
||||
}
|
||||
k.Websocket.ResubscribeToChannel(subscriptionToRemove)
|
||||
err := k.wsProcessOrderBookUpdate(channelData, obData)
|
||||
if err != nil {
|
||||
subscriptionToRemove := wshandler.WebsocketChannelSubscription{
|
||||
Channel: krakenWsOrderbook,
|
||||
Currency: channelData.Pair,
|
||||
}
|
||||
k.Websocket.ResubscribeToChannel(subscriptionToRemove)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,7 +335,7 @@ func (k *Kraken) wsProcessOrderBook(channelData *WebsocketChannelData, data inte
|
||||
|
||||
// wsProcessOrderBookPartial creates a new orderbook entry for a given currency pair
|
||||
func (k *Kraken) wsProcessOrderBookPartial(channelData *WebsocketChannelData, obData map[string]interface{}) {
|
||||
ob := orderbook.Base{
|
||||
base := orderbook.Base{
|
||||
Pair: channelData.Pair,
|
||||
AssetType: orderbook.Spot,
|
||||
}
|
||||
@@ -375,11 +347,10 @@ func (k *Kraken) wsProcessOrderBookPartial(channelData *WebsocketChannelData, ob
|
||||
asks := askData[i].([]interface{})
|
||||
price, _ := strconv.ParseFloat(asks[0].(string), 64)
|
||||
amount, _ := strconv.ParseFloat(asks[1].(string), 64)
|
||||
ob.Asks = append(ob.Asks, orderbook.Item{
|
||||
base.Asks = append(base.Asks, orderbook.Item{
|
||||
Amount: amount,
|
||||
Price: price,
|
||||
})
|
||||
|
||||
timeData, _ := strconv.ParseFloat(asks[2].(string), 64)
|
||||
sec, dec := math.Modf(timeData)
|
||||
askUpdatedTime := time.Unix(int64(sec), int64(dec*(1e9)))
|
||||
@@ -387,17 +358,15 @@ func (k *Kraken) wsProcessOrderBookPartial(channelData *WebsocketChannelData, ob
|
||||
highestLastUpdate = askUpdatedTime
|
||||
}
|
||||
}
|
||||
|
||||
bidData := obData["bs"].([]interface{})
|
||||
for i := range bidData {
|
||||
bids := bidData[i].([]interface{})
|
||||
price, _ := strconv.ParseFloat(bids[0].(string), 64)
|
||||
amount, _ := strconv.ParseFloat(bids[1].(string), 64)
|
||||
ob.Bids = append(ob.Bids, orderbook.Item{
|
||||
base.Bids = append(base.Bids, orderbook.Item{
|
||||
Amount: amount,
|
||||
Price: price,
|
||||
})
|
||||
|
||||
timeData, _ := strconv.ParseFloat(bids[2].(string), 64)
|
||||
sec, dec := math.Modf(timeData)
|
||||
bidUpdateTime := time.Unix(int64(sec), int64(dec*(1e9)))
|
||||
@@ -405,33 +374,25 @@ func (k *Kraken) wsProcessOrderBookPartial(channelData *WebsocketChannelData, ob
|
||||
highestLastUpdate = bidUpdateTime
|
||||
}
|
||||
}
|
||||
|
||||
ob.LastUpdated = highestLastUpdate
|
||||
err := k.Websocket.Orderbook.LoadSnapshot(&ob, k.Name, true)
|
||||
base.LastUpdated = highestLastUpdate
|
||||
err := k.Websocket.Orderbook.LoadSnapshot(&base, true)
|
||||
if err != nil {
|
||||
k.Websocket.DataHandler <- err
|
||||
return
|
||||
}
|
||||
|
||||
k.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
|
||||
Exchange: k.Name,
|
||||
Asset: orderbook.Spot,
|
||||
Pair: channelData.Pair,
|
||||
}
|
||||
|
||||
if krakenOrderBooks == nil {
|
||||
krakenOrderBooks = make(map[int64]orderbook.Base)
|
||||
}
|
||||
krakenOrderBooks[channelData.ChannelID] = ob
|
||||
}
|
||||
|
||||
func (k *Kraken) wsProcessOrderBookBuffer(channelData *WebsocketChannelData, obData map[string]interface{}) {
|
||||
ob := orderbook.Base{
|
||||
AssetType: orderbook.Spot,
|
||||
ExchangeName: k.Name,
|
||||
Pair: channelData.Pair,
|
||||
// wsProcessOrderBookUpdate updates an orderbook entry for a given currency pair
|
||||
func (k *Kraken) wsProcessOrderBookUpdate(channelData *WebsocketChannelData, obData map[string]interface{}) error {
|
||||
update := wsorderbook.WebsocketOrderbookUpdate{
|
||||
AssetType: krakenWsAssetType,
|
||||
CurrencyPair: channelData.Pair,
|
||||
}
|
||||
|
||||
var highestLastUpdate time.Time
|
||||
// Ask data is not always sent
|
||||
if _, ok := obData["a"]; ok {
|
||||
@@ -440,11 +401,10 @@ func (k *Kraken) wsProcessOrderBookBuffer(channelData *WebsocketChannelData, obD
|
||||
asks := askData[i].([]interface{})
|
||||
price, _ := strconv.ParseFloat(asks[0].(string), 64)
|
||||
amount, _ := strconv.ParseFloat(asks[1].(string), 64)
|
||||
ob.Asks = append(ob.Asks, orderbook.Item{
|
||||
update.Asks = append(update.Asks, orderbook.Item{
|
||||
Amount: amount,
|
||||
Price: price,
|
||||
})
|
||||
|
||||
timeData, _ := strconv.ParseFloat(asks[2].(string), 64)
|
||||
sec, dec := math.Modf(timeData)
|
||||
askUpdatedTime := time.Unix(int64(sec), int64(dec*(1e9)))
|
||||
@@ -460,7 +420,7 @@ func (k *Kraken) wsProcessOrderBookBuffer(channelData *WebsocketChannelData, obD
|
||||
bids := bidData[i].([]interface{})
|
||||
price, _ := strconv.ParseFloat(bids[0].(string), 64)
|
||||
amount, _ := strconv.ParseFloat(bids[1].(string), 64)
|
||||
ob.Bids = append(ob.Bids, orderbook.Item{
|
||||
update.Bids = append(update.Bids, orderbook.Item{
|
||||
Amount: amount,
|
||||
Price: price,
|
||||
})
|
||||
@@ -472,193 +432,20 @@ func (k *Kraken) wsProcessOrderBookBuffer(channelData *WebsocketChannelData, obD
|
||||
}
|
||||
}
|
||||
}
|
||||
ob.LastUpdated = highestLastUpdate
|
||||
if orderbookBuffer == nil {
|
||||
orderbookBuffer = make(map[int64][]orderbook.Base)
|
||||
}
|
||||
orderbookBuffer[channelData.ChannelID] = append(orderbookBuffer[channelData.ChannelID], ob)
|
||||
if k.Verbose {
|
||||
log.Debugf("%v Adding orderbook to buffer for channel %v. Lastupdated: %v. %v / %v",
|
||||
k.Name,
|
||||
channelData.ChannelID,
|
||||
ob.LastUpdated,
|
||||
len(orderbookBuffer[channelData.ChannelID]),
|
||||
orderbookBufferLimit)
|
||||
}
|
||||
}
|
||||
|
||||
// wsProcessOrderBookUpdate updates an orderbook entry for a given currency pair
|
||||
func (k *Kraken) wsProcessOrderBookUpdate(channelData *WebsocketChannelData) error {
|
||||
if k.Verbose {
|
||||
log.Debugf("%v Current orderbook 'LastUpdated': %v",
|
||||
k.Name,
|
||||
krakenOrderBooks[channelData.ChannelID].LastUpdated)
|
||||
}
|
||||
lowestLastUpdated := orderbookBuffer[channelData.ChannelID][0].LastUpdated
|
||||
if k.Verbose {
|
||||
log.Debugf("%v Sorting orderbook. Earliest 'LastUpdated' entry: %v",
|
||||
k.Name,
|
||||
lowestLastUpdated)
|
||||
}
|
||||
sort.Slice(orderbookBuffer[channelData.ChannelID], func(i, j int) bool {
|
||||
return orderbookBuffer[channelData.ChannelID][i].LastUpdated.Before(orderbookBuffer[channelData.ChannelID][j].LastUpdated)
|
||||
})
|
||||
|
||||
lowestLastUpdated = orderbookBuffer[channelData.ChannelID][0].LastUpdated
|
||||
if k.Verbose {
|
||||
log.Debugf("%v Sorted orderbook. Earliest 'LastUpdated' entry: %v",
|
||||
k.Name,
|
||||
lowestLastUpdated)
|
||||
}
|
||||
// The earliest update has to be after the previously stored orderbook
|
||||
if krakenOrderBooks[channelData.ChannelID].LastUpdated.After(lowestLastUpdated) {
|
||||
err := fmt.Errorf("%v orderbook update out of order. Existing: %v, Attempted: %v",
|
||||
k.Name,
|
||||
krakenOrderBooks[channelData.ChannelID].LastUpdated,
|
||||
lowestLastUpdated)
|
||||
k.Websocket.DataHandler <- err
|
||||
return err
|
||||
}
|
||||
|
||||
k.updateChannelOrderbookEntries(channelData)
|
||||
highestLastUpdate := orderbookBuffer[channelData.ChannelID][len(orderbookBuffer[channelData.ChannelID])-1].LastUpdated
|
||||
if k.Verbose {
|
||||
log.Debugf("%v Saving orderbook. Lastupdated: %v",
|
||||
k.Name,
|
||||
highestLastUpdate)
|
||||
}
|
||||
|
||||
ob := krakenOrderBooks[channelData.ChannelID]
|
||||
ob.LastUpdated = highestLastUpdate
|
||||
err := k.Websocket.Orderbook.LoadSnapshot(&ob, k.Name, true)
|
||||
update.UpdateTime = highestLastUpdate
|
||||
err := k.Websocket.Orderbook.Update(&update)
|
||||
if err != nil {
|
||||
k.Websocket.DataHandler <- err
|
||||
return err
|
||||
}
|
||||
|
||||
k.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
|
||||
Exchange: k.Name,
|
||||
Asset: orderbook.Spot,
|
||||
Pair: channelData.Pair,
|
||||
}
|
||||
// Reset the buffer
|
||||
orderbookBuffer[channelData.ChannelID] = []orderbook.Base{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *Kraken) updateChannelOrderbookEntries(channelData *WebsocketChannelData) {
|
||||
for i := 0; i < len(orderbookBuffer[channelData.ChannelID]); i++ {
|
||||
for j := 0; j < len(orderbookBuffer[channelData.ChannelID][i].Asks); j++ {
|
||||
k.updateChannelOrderbookAsks(i, j, channelData)
|
||||
}
|
||||
for j := 0; j < len(orderbookBuffer[channelData.ChannelID][i].Bids); j++ {
|
||||
k.updateChannelOrderbookBids(i, j, channelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (k *Kraken) updateChannelOrderbookAsks(i, j int, channelData *WebsocketChannelData) {
|
||||
askFound := k.updateChannelOrderbookAsk(i, j, channelData)
|
||||
if !askFound {
|
||||
if k.Verbose {
|
||||
log.Debugf("%v Adding Ask for channel %v. Price %v. Amount %v",
|
||||
k.Name,
|
||||
channelData.ChannelID,
|
||||
orderbookBuffer[channelData.ChannelID][i].Asks[j].Price,
|
||||
orderbookBuffer[channelData.ChannelID][i].Asks[j].Amount)
|
||||
}
|
||||
ob := krakenOrderBooks[channelData.ChannelID]
|
||||
ob.Asks = append(ob.Asks, orderbookBuffer[channelData.ChannelID][i].Asks[j])
|
||||
krakenOrderBooks[channelData.ChannelID] = ob
|
||||
}
|
||||
}
|
||||
|
||||
func (k *Kraken) updateChannelOrderbookAsk(i, j int, channelData *WebsocketChannelData) bool {
|
||||
askFound := false
|
||||
for l := 0; l < len(krakenOrderBooks[channelData.ChannelID].Asks); l++ {
|
||||
if krakenOrderBooks[channelData.ChannelID].Asks[l].Price == orderbookBuffer[channelData.ChannelID][i].Asks[j].Price {
|
||||
askFound = true
|
||||
if orderbookBuffer[channelData.ChannelID][i].Asks[j].Amount == 0 {
|
||||
// Remove existing entry
|
||||
if k.Verbose {
|
||||
log.Debugf("%v Removing Ask for channel %v. Price %v. Old amount %v. Buffer %v",
|
||||
k.Name,
|
||||
channelData.ChannelID,
|
||||
orderbookBuffer[channelData.ChannelID][i].Asks[j].Price,
|
||||
krakenOrderBooks[channelData.ChannelID].Asks[l].Amount, i)
|
||||
}
|
||||
ob := krakenOrderBooks[channelData.ChannelID]
|
||||
ob.Asks = append(ob.Asks[:l], ob.Asks[l+1:]...)
|
||||
krakenOrderBooks[channelData.ChannelID] = ob
|
||||
l--
|
||||
} else if krakenOrderBooks[channelData.ChannelID].Asks[l].Amount != orderbookBuffer[channelData.ChannelID][i].Asks[j].Amount {
|
||||
if k.Verbose {
|
||||
log.Debugf("%v Updating Ask for channel %v. Price %v. Old amount %v, New Amount %v",
|
||||
k.Name,
|
||||
channelData.ChannelID,
|
||||
orderbookBuffer[channelData.ChannelID][i].Asks[j].Price,
|
||||
krakenOrderBooks[channelData.ChannelID].Asks[l].Amount,
|
||||
orderbookBuffer[channelData.ChannelID][i].Asks[j].Amount)
|
||||
}
|
||||
krakenOrderBooks[channelData.ChannelID].Asks[l].Amount = orderbookBuffer[channelData.ChannelID][i].Asks[j].Amount
|
||||
}
|
||||
return askFound
|
||||
}
|
||||
}
|
||||
return askFound
|
||||
}
|
||||
|
||||
func (k *Kraken) updateChannelOrderbookBids(i, j int, channelData *WebsocketChannelData) {
|
||||
bidFound := k.updateChannelOrderbookBid(i, j, channelData)
|
||||
if !bidFound {
|
||||
if k.Verbose {
|
||||
log.Debugf("%v Adding Bid for channel %v. Price %v. Amount %v",
|
||||
k.Name,
|
||||
channelData.ChannelID,
|
||||
orderbookBuffer[channelData.ChannelID][i].Bids[j].Price,
|
||||
orderbookBuffer[channelData.ChannelID][i].Bids[j].Amount)
|
||||
}
|
||||
ob := krakenOrderBooks[channelData.ChannelID]
|
||||
ob.Bids = append(ob.Bids, orderbookBuffer[channelData.ChannelID][i].Bids[j])
|
||||
krakenOrderBooks[channelData.ChannelID] = ob
|
||||
}
|
||||
}
|
||||
|
||||
func (k *Kraken) updateChannelOrderbookBid(i, j int, channelData *WebsocketChannelData) bool {
|
||||
bidFound := false
|
||||
for l := 0; l < len(krakenOrderBooks[channelData.ChannelID].Bids); l++ {
|
||||
if krakenOrderBooks[channelData.ChannelID].Bids[l].Price == orderbookBuffer[channelData.ChannelID][i].Bids[j].Price {
|
||||
bidFound = true
|
||||
if orderbookBuffer[channelData.ChannelID][i].Bids[j].Amount == 0 {
|
||||
// Remove existing entry
|
||||
if k.Verbose {
|
||||
log.Debugf("%v Removing Bid for channel %v. Price %v. Old amount %v. Buffer %v",
|
||||
k.Name,
|
||||
channelData.ChannelID,
|
||||
orderbookBuffer[channelData.ChannelID][i].Bids[j].Price,
|
||||
krakenOrderBooks[channelData.ChannelID].Bids[l].Amount, i)
|
||||
}
|
||||
ob := krakenOrderBooks[channelData.ChannelID]
|
||||
ob.Bids = append(ob.Bids[:l], ob.Bids[l+1:]...)
|
||||
krakenOrderBooks[channelData.ChannelID] = ob
|
||||
l--
|
||||
} else if krakenOrderBooks[channelData.ChannelID].Bids[l].Amount != orderbookBuffer[channelData.ChannelID][i].Bids[j].Amount {
|
||||
if k.Verbose {
|
||||
log.Debugf("%v Updating Bid for channel %v. Price %v. Old amount %v, New Amount %v",
|
||||
k.Name,
|
||||
channelData.ChannelID,
|
||||
orderbookBuffer[channelData.ChannelID][i].Bids[j].Price,
|
||||
krakenOrderBooks[channelData.ChannelID].Bids[l].Amount,
|
||||
orderbookBuffer[channelData.ChannelID][i].Bids[j].Amount)
|
||||
}
|
||||
krakenOrderBooks[channelData.ChannelID].Bids[l].Amount = orderbookBuffer[channelData.ChannelID][i].Bids[j].Amount
|
||||
}
|
||||
return bidFound
|
||||
}
|
||||
}
|
||||
return bidFound
|
||||
}
|
||||
|
||||
// wsProcessCandles converts candle data and sends it to the data handler
|
||||
func (k *Kraken) wsProcessCandles(channelData *WebsocketChannelData, data interface{}) {
|
||||
candleData := data.([]interface{})
|
||||
|
||||
@@ -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