diff --git a/exchanges/btse/btse_test.go b/exchanges/btse/btse_test.go index 34b86a08..589f6a57 100644 --- a/exchanges/btse/btse_test.go +++ b/exchanges/btse/btse_test.go @@ -525,8 +525,6 @@ func TestParseOrderTime(t *testing.T) { } } -// Any tests below this line have the ability to impact your orders on the exchange. Enable canManipulateRealOrders to run them -// ---------------------------------------------------------------------------------------------------------------------------- func TestSubmitOrder(t *testing.T) { t.Parallel() if !areTestAPIKeysSet() || !canManipulateRealOrders { @@ -624,7 +622,8 @@ func TestCancelAllExchangeOrders(t *testing.T) { } func TestWsOrderbook(t *testing.T) { - pressXToJSON := []byte(`{"topic":"orderBookApi:BTC-USD_0","data":{"buyQuote":[{"price":"9272.0","size":"0.077"},{"price":"9271.0","size":"1.122"},{"price":"9270.0","size":"2.548"},{"price":"9267.5","size":"1.015"},{"price":"9265.5","size":"0.930"},{"price":"9265.0","size":"0.475"},{"price":"9264.5","size":"2.216"},{"price":"9264.0","size":"9.709"},{"price":"9263.5","size":"3.667"},{"price":"9263.0","size":"8.481"},{"price":"9262.5","size":"7.660"},{"price":"9262.0","size":"9.689"},{"price":"9261.5","size":"4.213"},{"price":"9261.0","size":"1.491"},{"price":"9260.5","size":"6.264"},{"price":"9260.0","size":"1.690"},{"price":"9259.5","size":"5.718"},{"price":"9259.0","size":"2.706"},{"price":"9258.5","size":"0.192"},{"price":"9258.0","size":"1.592"},{"price":"9257.5","size":"1.749"},{"price":"9257.0","size":"8.104"},{"price":"9256.0","size":"0.161"},{"price":"9252.0","size":"1.544"},{"price":"9249.5","size":"1.462"},{"price":"9247.5","size":"1.833"},{"price":"9247.0","size":"0.168"},{"price":"9245.5","size":"1.941"},{"price":"9244.0","size":"1.423"},{"price":"9243.5","size":"0.175"}],"currency":"USD","sellQuote":[{"price":"9303.5","size":"1.839"},{"price":"9303.0","size":"2.067"},{"price":"9302.0","size":"0.117"},{"price":"9298.5","size":"1.569"},{"price":"9297.0","size":"1.527"},{"price":"9295.0","size":"0.184"},{"price":"9294.0","size":"1.785"},{"price":"9289.0","size":"1.673"},{"price":"9287.5","size":"4.194"},{"price":"9287.0","size":"6.622"},{"price":"9286.5","size":"2.147"},{"price":"9286.0","size":"3.348"},{"price":"9285.5","size":"5.655"},{"price":"9285.0","size":"10.423"},{"price":"9284.5","size":"6.233"},{"price":"9284.0","size":"8.860"},{"price":"9283.5","size":"9.441"},{"price":"9283.0","size":"3.455"},{"price":"9282.5","size":"11.033"},{"price":"9282.0","size":"11.471"},{"price":"9281.5","size":"4.742"},{"price":"9281.0","size":"14.789"},{"price":"9280.5","size":"11.117"},{"price":"9280.0","size":"0.807"},{"price":"9279.5","size":"1.651"},{"price":"9279.0","size":"0.244"},{"price":"9278.5","size":"0.533"},{"price":"9277.0","size":"1.447"},{"price":"9273.0","size":"1.976"},{"price":"9272.5","size":"0.093"}]}}`) + t.Parallel() + pressXToJSON := []byte(`{"topic":"orderBookL2Api:BTC-USD_0","data":{"buyQuote":[{"price":"9272.0","size":"0.077"},{"price":"9271.0","size":"1.122"},{"price":"9270.0","size":"2.548"},{"price":"9267.5","size":"1.015"},{"price":"9265.5","size":"0.930"},{"price":"9265.0","size":"0.475"},{"price":"9264.5","size":"2.216"},{"price":"9264.0","size":"9.709"},{"price":"9263.5","size":"3.667"},{"price":"9263.0","size":"8.481"},{"price":"9262.5","size":"7.660"},{"price":"9262.0","size":"9.689"},{"price":"9261.5","size":"4.213"},{"price":"9261.0","size":"1.491"},{"price":"9260.5","size":"6.264"},{"price":"9260.0","size":"1.690"},{"price":"9259.5","size":"5.718"},{"price":"9259.0","size":"2.706"},{"price":"9258.5","size":"0.192"},{"price":"9258.0","size":"1.592"},{"price":"9257.5","size":"1.749"},{"price":"9257.0","size":"8.104"},{"price":"9256.0","size":"0.161"},{"price":"9252.0","size":"1.544"},{"price":"9249.5","size":"1.462"},{"price":"9247.5","size":"1.833"},{"price":"9247.0","size":"0.168"},{"price":"9245.5","size":"1.941"},{"price":"9244.0","size":"1.423"},{"price":"9243.5","size":"0.175"}],"currency":"USD","sellQuote":[{"price":"9303.5","size":"1.839"},{"price":"9303.0","size":"2.067"},{"price":"9302.0","size":"0.117"},{"price":"9298.5","size":"1.569"},{"price":"9297.0","size":"1.527"},{"price":"9295.0","size":"0.184"},{"price":"9294.0","size":"1.785"},{"price":"9289.0","size":"1.673"},{"price":"9287.5","size":"4.194"},{"price":"9287.0","size":"6.622"},{"price":"9286.5","size":"2.147"},{"price":"9286.0","size":"3.348"},{"price":"9285.5","size":"5.655"},{"price":"9285.0","size":"10.423"},{"price":"9284.5","size":"6.233"},{"price":"9284.0","size":"8.860"},{"price":"9283.5","size":"9.441"},{"price":"9283.0","size":"3.455"},{"price":"9282.5","size":"11.033"},{"price":"9282.0","size":"11.471"},{"price":"9281.5","size":"4.742"},{"price":"9281.0","size":"14.789"},{"price":"9280.5","size":"11.117"},{"price":"9280.0","size":"0.807"},{"price":"9279.5","size":"1.651"},{"price":"9279.0","size":"0.244"},{"price":"9278.5","size":"0.533"},{"price":"9277.0","size":"1.447"},{"price":"9273.0","size":"1.976"},{"price":"9272.5","size":"0.093"}]}}`) err := b.wsHandleData(pressXToJSON) if err != nil { t.Error(err) @@ -632,6 +631,7 @@ func TestWsOrderbook(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) if err != nil { @@ -640,6 +640,7 @@ func TestWsTrades(t *testing.T) { } func TestWsOrderNotification(t *testing.T) { + t.Parallel() status := []string{"ORDER_INSERTED", "ORDER_CANCELLED", "TRIGGER_INSERTED", "ORDER_FULL_TRANSACTED", "ORDER_PARTIALLY_TRANSACTED", "INSUFFICIENT_BALANCE", "TRIGGER_ACTIVATED", "MARKET_UNAVAILABLE"} for i := range status { pressXToJSON := []byte(`{"topic": "notificationApi","data": [{"symbol": "BTC-USD","orderID": "1234","orderMode": "MODE_BUY","orderType": "TYPE_LIMIT","price": "1","size": "1","status": "` + status[i] + `","timestamp": "1580349090693","type": "STOP","triggerPrice": "1"}]}`) @@ -865,3 +866,45 @@ func TestOrderbookFilter(t *testing.T) { t.Fatal("incorrect filtering") } } + +func TestWsLogin(t *testing.T) { + t.Parallel() + data := []byte(`{"event":"login","success":true}`) + err := b.wsHandleData(data) + if err != nil { + t.Error(err) + } + if !b.Websocket.CanUseAuthenticatedEndpoints() { + t.Error("expected true") + } + + data = []byte(`{"event":"login","success":false}`) + err = b.wsHandleData(data) + if err != nil { + t.Error(err) + } + if b.Websocket.CanUseAuthenticatedEndpoints() { + t.Error("expected false") + } +} + +func TestWsSubscription(t *testing.T) { + t.Parallel() + data := []byte(`{"event":"subscribe","channel":["orderBookL2Api:SFI-ETH_0","tradeHistory:SFI-ETH"]}`) + err := b.wsHandleData(data) + if err != nil { + t.Error(err) + } +} + +func TestWsUnexpectedData(t *testing.T) { + t.Parallel() + data := []byte(`{}`) + err := b.wsHandleData(data) + if err != nil && err.Error() != "BTSE - Unhandled websocket message: {}" { + t.Error(err) + } + if err == nil { + t.Error("expected error response from bad data") + } +} diff --git a/exchanges/btse/btse_types.go b/exchanges/btse/btse_types.go index 63020de7..d26f9670 100644 --- a/exchanges/btse/btse_types.go +++ b/exchanges/btse/btse_types.go @@ -351,3 +351,15 @@ type OrderSizeLimit struct { // orderSizeLimitMap map of OrderSizeLimit per currency var orderSizeLimitMap sync.Map + +// WsSubscriptionAcknowledgement contains successful subscription messages +type WsSubscriptionAcknowledgement struct { + Channel []string `json:"channel"` + Event string `json:"event"` +} + +// WsLoginAcknowledgement contains whether authentication was successful +type WsLoginAcknowledgement struct { + Event string `json:"event"` + Success bool `json:"success"` +} diff --git a/exchanges/btse/btse_websocket.go b/exchanges/btse/btse_websocket.go index ee308f9d..221cd23f 100644 --- a/exchanges/btse/btse_websocket.go +++ b/exchanges/btse/btse_websocket.go @@ -18,6 +18,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" "github.com/thrasher-corp/gocryptotrader/exchanges/stream" "github.com/thrasher-corp/gocryptotrader/exchanges/trade" + "github.com/thrasher-corp/gocryptotrader/log" ) const ( @@ -111,17 +112,48 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { var result Result err := json.Unmarshal(respRaw, &result) if err != nil { - if strings.Contains(string(respRaw), "connect success") || - strings.Contains(string(respRaw), "authenticated successfully") { + if strings.Contains(string(respRaw), "connect success") { return nil - } else if strings.Contains(string(respRaw), "AUTHENTICATE ERROR") { - b.Websocket.SetCanUseAuthenticatedEndpoints(false) - return errors.New("authentication failure") } return err } + if result == nil { + return nil + } + + if result["event"] != nil { + event, ok := result["event"].(string) + if !ok { + return errors.New(b.Name + stream.UnhandledMessage + string(respRaw)) + } + switch event { + case "subscribe": + var subscribe WsSubscriptionAcknowledgement + err = json.Unmarshal(respRaw, &subscribe) + if err != nil { + return err + } + log.Infof(log.WebsocketMgr, "%v subscribed to %v", b.Name, strings.Join(subscribe.Channel, ", ")) + case "login": + var login WsLoginAcknowledgement + err = json.Unmarshal(respRaw, &login) + if err != nil { + return err + } + b.Websocket.SetCanUseAuthenticatedEndpoints(login.Success) + log.Infof(log.WebsocketMgr, "%v websocket authenticated: %v", b.Name, login.Success) + default: + return errors.New(b.Name + stream.UnhandledMessage + string(respRaw)) + } + return nil + } + + topic, ok := result["topic"].(string) + if !ok { + return errors.New(b.Name + stream.UnhandledMessage + string(respRaw)) + } switch { - case result["topic"] == "notificationApi": + case topic == "notificationApi": var notification wsNotification err = json.Unmarshal(respRaw, ¬ification) if err != nil { @@ -182,7 +214,7 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { Pair: p, } } - case strings.Contains(result["topic"].(string), "tradeHistory"): + case strings.Contains(topic, "tradeHistory"): if !b.IsSaveTradeDataEnabled() { return nil } @@ -223,7 +255,7 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { }) } return trade.AddTradesToBuffer(b.Name, trades...) - case strings.Contains(result["topic"].(string), "orderBookL2Api"): + case strings.Contains(topic, "orderBookL2Api"): var t wsOrderBook err = json.Unmarshal(respRaw, &t) if err != nil { @@ -288,9 +320,9 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { return err } default: - b.Websocket.DataHandler <- stream.UnhandledMessageWarning{Message: b.Name + stream.UnhandledMessage + string(respRaw)} - return nil + return errors.New(b.Name + stream.UnhandledMessage + string(respRaw)) } + return nil } diff --git a/exchanges/btse/btse_wrapper.go b/exchanges/btse/btse_wrapper.go index 0563cb11..705c8c8e 100644 --- a/exchanges/btse/btse_wrapper.go +++ b/exchanges/btse/btse_wrapper.go @@ -335,11 +335,17 @@ func (b *BTSE) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderboo } for x := range a.BuyQuote { + if b.orderbookFilter(a.BuyQuote[x].Price, a.BuyQuote[x].Size) { + continue + } book.Bids = append(book.Bids, orderbook.Item{ Price: a.BuyQuote[x].Price, Amount: a.BuyQuote[x].Size}) } for x := range a.SellQuote { + if b.orderbookFilter(a.SellQuote[x].Price, a.SellQuote[x].Size) { + continue + } book.Asks = append(book.Asks, orderbook.Item{ Price: a.SellQuote[x].Price, Amount: a.SellQuote[x].Size}) diff --git a/go.sum b/go.sum index 36ef139e..524318f8 100644 --- a/go.sum +++ b/go.sum @@ -668,6 +668,7 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.34.1/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0 h1:lQ+dE99pFsb8osbJB3oRfE5eW4Hx6a/lZQr8Jh+eoT4= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=