From d857d704e35e533b2813d5cffd02338954b13c45 Mon Sep 17 00:00:00 2001 From: Bea Date: Thu, 20 Mar 2025 08:33:08 +0700 Subject: [PATCH] BTSE: Fix sending trades to the websocket DataHandler (#1845) * Fix ws trades stream Update the raw trade data to match the API docs Fix extracting the pair from the topic Update the test Amend the configtest to enable the trade feed * Rename the test, exctract pair from symbol * Add check if neither trade feed or save trade data is enabled * Update the channel name to match the API docs * Change side parsing * Change tradeHistory Side type * Fix linter and nit * Fix linter * Fix the channel trades channel name --- exchanges/btse/btse_test.go | 49 +++++++++++++++++++++--- exchanges/btse/btse_types.go | 15 +++++--- exchanges/btse/btse_websocket.go | 36 +++++++++-------- exchanges/btse/testdata/wsAllTrades.json | 2 + testdata/configtest.json | 4 +- 5 files changed, 76 insertions(+), 30 deletions(-) create mode 100644 exchanges/btse/testdata/wsAllTrades.json diff --git a/exchanges/btse/btse_test.go b/exchanges/btse/btse_test.go index e0d3673c..d643caf4 100644 --- a/exchanges/btse/btse_test.go +++ b/exchanges/btse/btse_test.go @@ -24,6 +24,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/subscription" "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" + "github.com/thrasher-corp/gocryptotrader/exchanges/trade" testexch "github.com/thrasher-corp/gocryptotrader/internal/testing/exchange" testsubs "github.com/thrasher-corp/gocryptotrader/internal/testing/subscriptions" ) @@ -485,12 +486,48 @@ func TestWsOrderbook(t *testing.T) { // TODO: Meaningful test of data parsing } -func TestWsTrades(t *testing.T) { +func TestWSTrades(t *testing.T) { t.Parallel() - pressXToJSON := []byte(`{"topic":"tradeHistory:BTC-USD","data":[{"amount":0.09,"gain":1,"newest":0,"price":9273.6,"serialId":0,"transactionUnixtime":1580349090693}]}`) - err := b.wsHandleData(pressXToJSON) - assert.NoError(t, err, "wsHandleData tradeHistory should not error") - // TODO: Meaningful test of data parsing + + b := new(BTSE) //nolint:govet // Intentional shadow to avoid future copy/paste mistakes + require.NoError(t, testexch.Setup(b), "Setup Instance must not error") + testexch.FixtureToDataHandler(t, "testdata/wsAllTrades.json", b.wsHandleData) + close(b.Websocket.DataHandler) + + exp := []trade.Data{ + { + Exchange: b.Name, + CurrencyPair: spotPair, + Timestamp: time.UnixMilli(1741836562893).UTC(), + Price: 83894.01, + Amount: 0.00067, + Side: order.Buy, + TID: "74040596", + AssetType: asset.Spot, + }, + { + Exchange: b.Name, + CurrencyPair: spotPair, + Timestamp: time.UnixMilli(1741836562687).UTC(), + Price: 83894.87, + Amount: 0.0035, + Side: order.Sell, + TID: "74040529", + AssetType: asset.Spot, + }, + } + require.Len(t, b.Websocket.DataHandler, 2, "Must see the correct number of trades") + for resp := range b.Websocket.DataHandler { + switch v := resp.(type) { + case trade.Data: + i := 1 - len(b.Websocket.DataHandler) + require.Equalf(t, exp[i], v, "Trade [%d] must be correct", i) + case error: + t.Error(v) + default: + t.Errorf("Unexpected type in DataHandler: %T(%s)", v, v) + } + } } func TestWsOrderNotification(t *testing.T) { @@ -784,7 +821,7 @@ func TestGenerateSubscriptions(t *testing.T) { require.NoError(t, testexch.Setup(b), "Test instance Setup must not error") exp := subscription.List{ - {Channel: subscription.AllTradesChannel, QualifiedChannel: "tradeHistory:BTC-USD", Asset: asset.Spot, Pairs: currency.Pairs{spotPair}}, + {Channel: subscription.AllTradesChannel, QualifiedChannel: "tradeHistoryApi:BTC-USD", Asset: asset.Spot, Pairs: currency.Pairs{spotPair}}, {Channel: subscription.MyTradesChannel, QualifiedChannel: "notificationApi"}, } diff --git a/exchanges/btse/btse_types.go b/exchanges/btse/btse_types.go index b829e580..8643cc57 100644 --- a/exchanges/btse/btse_types.go +++ b/exchanges/btse/btse_types.go @@ -2,6 +2,9 @@ package btse import ( "time" + + "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/types" ) const ( @@ -313,12 +316,12 @@ type wsOrderBook struct { } type wsTradeData struct { - Amount float64 `json:"amount"` - Gain int64 `json:"gain"` - Newest int64 `json:"newest"` - Price float64 `json:"price"` - ID int64 `json:"serialId"` - TransactionTime int64 `json:"transactionUnixTime"` + Symbol string `json:"symbol"` + Side order.Side `json:"side"` + Size float64 `json:"size"` + Price float64 `json:"price"` + TID int64 `json:"tradeID"` + Timestamp types.Time `json:"timestamp"` } type wsTradeHistory struct { diff --git a/exchanges/btse/btse_websocket.go b/exchanges/btse/btse_websocket.go index df7007d4..5c9c01e4 100644 --- a/exchanges/btse/btse_websocket.go +++ b/exchanges/btse/btse_websocket.go @@ -30,7 +30,7 @@ const ( var subscriptionNames = map[string]string{ subscription.MyTradesChannel: "notificationApi", - subscription.AllTradesChannel: "tradeHistory", + subscription.AllTradesChannel: "tradeHistoryApi", } var defaultSubscriptions = subscription.List{ @@ -240,10 +240,13 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { Pair: p, } } - case strings.Contains(topic, "tradeHistory"): - if !b.IsSaveTradeDataEnabled() { + case strings.Contains(topic, "tradeHistoryApi"): + saveTradeData := b.IsSaveTradeDataEnabled() + tradeFeed := b.IsTradeFeedEnabled() + if !saveTradeData && !tradeFeed { return nil } + var tradeHistory wsTradeHistory err = json.Unmarshal(respRaw, &tradeHistory) if err != nil { @@ -251,16 +254,8 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { } var trades []trade.Data for x := range tradeHistory.Data { - side := order.Buy - if tradeHistory.Data[x].Gain == -1 { - side = order.Sell - } - var p currency.Pair - p, err = currency.NewPairFromString(strings.Replace(tradeHistory.Topic, - "tradeHistory:", - "", - 1)) + p, err = currency.NewPairFromString(tradeHistory.Data[x].Symbol) if err != nil { return err } @@ -270,17 +265,24 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { return err } trades = append(trades, trade.Data{ - Timestamp: time.UnixMilli(tradeHistory.Data[x].TransactionTime), + Timestamp: tradeHistory.Data[x].Timestamp.Time().UTC(), CurrencyPair: p, AssetType: a, Exchange: b.Name, Price: tradeHistory.Data[x].Price, - Amount: tradeHistory.Data[x].Amount, - Side: side, - TID: strconv.FormatInt(tradeHistory.Data[x].ID, 10), + Amount: tradeHistory.Data[x].Size, + Side: tradeHistory.Data[x].Side, + TID: strconv.FormatInt(tradeHistory.Data[x].TID, 10), }) } - return trade.AddTradesToBuffer(trades...) + if tradeFeed { + for i := range trades { + b.Websocket.DataHandler <- trades[i] + } + } + if saveTradeData { + return trade.AddTradesToBuffer(trades...) + } case strings.Contains(topic, "orderBookL2Api"): // TODO: Fix orderbook updates. var t wsOrderBook err = json.Unmarshal(respRaw, &t) diff --git a/exchanges/btse/testdata/wsAllTrades.json b/exchanges/btse/testdata/wsAllTrades.json new file mode 100644 index 00000000..2c2152ef --- /dev/null +++ b/exchanges/btse/testdata/wsAllTrades.json @@ -0,0 +1,2 @@ +{"topic":"tradeHistoryApi:BTC-USD","data":[{"symbol":"BTC-USD","side":"BUY","size":0.00067,"price":83894.01,"tradeId":74040596,"timestamp":1741836562893}]} +{"topic":"tradeHistoryApi:BTC-USD","data":[{"symbol":"BTC-USD","side":"SELL","size":0.0035,"price":83894.87,"tradeId":74040529,"timestamp":1741836562687}]} \ No newline at end of file diff --git a/testdata/configtest.json b/testdata/configtest.json index 686d067b..d3d77a5f 100644 --- a/testdata/configtest.json +++ b/testdata/configtest.json @@ -397,7 +397,9 @@ }, "enabled": { "autoPairUpdates": true, - "websocketAPI": true + "websocketAPI": true, + "saveTradeData": false, + "tradeFeed": true } }, "bankAccounts": [