From 9973523f1e79d35b110af71a15de8f4e2966c3ac Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Fri, 23 Apr 2021 11:28:41 +1000 Subject: [PATCH] Coinbene: Fix ticker pair parsing (#663) * Fix coinbene ticker pair parsing * Update swap path plus add get instruments method * Fix tests * Add additional swap ticker fields * Add endpoint to auth payload request --- exchanges/coinbene/coinbene.go | 15 +++++++++-- exchanges/coinbene/coinbene_test.go | 34 ++++++++++++++++++++----- exchanges/coinbene/coinbene_types.go | 35 ++++++++++++++++++-------- exchanges/coinbene/coinbene_wrapper.go | 15 ++++------- exchanges/coinbene/ratelimit.go | 6 +++++ testdata/configtest.json | 7 +++--- 6 files changed, 80 insertions(+), 32 deletions(-) diff --git a/exchanges/coinbene/coinbene.go b/exchanges/coinbene/coinbene.go index a8a4d4a8..b2985be4 100644 --- a/exchanges/coinbene/coinbene.go +++ b/exchanges/coinbene/coinbene.go @@ -27,9 +27,9 @@ type Coinbene struct { const ( coinbeneAPIURL = "https://openapi-exchange.coinbene.com/api/exchange/" - coinbeneSwapAPIURL = "https://openapi-contract.coinbene.com/api/swap/" + coinbeneSwapAPIURL = "https://openapi-contract.coinbene.com/api/usdt/" coinbeneAuthPath = "/api/exchange/v2" - coinbeneSwapAuthPath = "/api/swap/v2" + coinbeneSwapAuthPath = "/api/usdt/v2" coinbeneAPIVersion = "v2" // Public endpoints @@ -38,6 +38,7 @@ const ( coinbeneGetTickers = "/market/tickers" coinbeneGetOrderBook = "/market/orderBook" coinbeneGetKlines = "/market/klines" + coinbeneGetInstruments = "/market/instruments" // TODO: Implement function --- coinbeneSpotKlines = "/market/instruments/candles" coinbeneSpotExchangeRate = "/market/rate/list" @@ -555,6 +556,15 @@ func (c *Coinbene) GetSwapTicker(symbol string) (SwapTicker, error) { return t, nil } +// GetSwapInstruments returns a list of tradable instruments +func (c *Coinbene) GetSwapInstruments() ([]Instrument, error) { + resp := struct { + Data []Instrument `json:"data"` + }{} + return resp.Data, c.SendHTTPRequest(exchange.RestSwap, + coinbeneAPIVersion+coinbeneGetInstruments, contractInstruments, &resp) +} + // GetSwapOrderbook returns an orderbook for the specified currency func (c *Coinbene) GetSwapOrderbook(symbol string, size int64) (Orderbook, error) { var s Orderbook @@ -1180,6 +1190,7 @@ func (c *Coinbene) SendAuthHTTPRequest(ep exchange.URL, method, path, epPath str Verbose: c.Verbose, HTTPDebugging: c.HTTPDebugging, HTTPRecording: c.HTTPRecording, + Endpoint: f, }); err != nil { return err } diff --git a/exchanges/coinbene/coinbene_test.go b/exchanges/coinbene/coinbene_test.go index 81c5a9b3..12004027 100644 --- a/exchanges/coinbene/coinbene_test.go +++ b/exchanges/coinbene/coinbene_test.go @@ -21,7 +21,7 @@ const ( testAPISecret = "" canManipulateRealOrders = false spotTestPair = "BTC/USDT" - swapTestPair = "BTCUSDT" + swapTestPair = "BTC-SWAP" ) var c Coinbene @@ -226,11 +226,18 @@ func TestCancelSpotOrders(t *testing.T) { func TestUpdateTicker(t *testing.T) { t.Parallel() - cp := currency.NewPairWithDelimiter("BTC", "USDT", "/") - _, err := c.UpdateTicker(cp, asset.Spot) + cp, err := currency.NewPairFromString(spotTestPair) + if err != nil { + t.Fatal(err) + } + _, err = c.UpdateTicker(cp, asset.Spot) if err != nil { t.Error(err) } + cp, err = currency.NewPairFromString(swapTestPair) + if err != nil { + t.Fatal(err) + } _, err = c.UpdateTicker(cp, asset.PerpetualSwap) if err != nil { t.Error(err) @@ -250,11 +257,18 @@ func TestGetAccountInfo(t *testing.T) { func TestUpdateOrderbook(t *testing.T) { t.Parallel() - cp := currency.NewPairWithDelimiter("BTC", "USDT", "/") - _, err := c.UpdateOrderbook(cp, asset.Spot) + cp, err := currency.NewPairFromString(spotTestPair) + if err != nil { + t.Fatal(err) + } + _, err = c.UpdateOrderbook(cp, asset.Spot) if err != nil { t.Error(err) } + cp, err = currency.NewPairFromString(swapTestPair) + if err != nil { + t.Fatal(err) + } _, err = c.UpdateOrderbook(cp, asset.PerpetualSwap) if err != nil { t.Error(err) @@ -319,6 +333,14 @@ func TestGetSwapTrades(t *testing.T) { } } +func TestGetSwapInstruments(t *testing.T) { + t.Parallel() + _, err := c.GetSwapInstruments() + if err != nil { + t.Error(err) + } +} + func TestGetSwapAccountInfo(t *testing.T) { t.Parallel() if !areTestAPIKeysSet() { @@ -616,7 +638,7 @@ func TestGetHistoricCandles(t *testing.T) { t.Fatal(err) } - currencyPairSwap, err := currency.NewPairFromString(spotTestPair) + currencyPairSwap, err := currency.NewPairFromString(swapTestPair) if err != nil { t.Fatal(err) } diff --git a/exchanges/coinbene/coinbene_types.go b/exchanges/coinbene/coinbene_types.go index 1595549f..d63ae525 100644 --- a/exchanges/coinbene/coinbene_types.go +++ b/exchanges/coinbene/coinbene_types.go @@ -3,6 +3,7 @@ package coinbene import ( "time" + "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/order" ) @@ -255,17 +256,19 @@ type WsUserOrders struct { // SwapTicker stores the swap ticker info type SwapTicker struct { - LastPrice float64 `json:"lastPrice,string"` - MarkPrice float64 `json:"markPrice,string"` - BestAskPrice float64 `json:"bestAskPrice,string"` - BestBidPrice float64 `json:"bestBidPrice,string"` - High24Hour float64 `json:"high24h,string"` - Low24Hour float64 `json:"low24h,string"` - Volume24Hour float64 `json:"volume24h,string"` - BestAskVolume float64 `json:"bestAskVolume,string"` - BestBidVolume float64 `json:"bestBidVolume,string"` - Turnover float64 `json:"turnover,string"` - Timestamp time.Time `json:"timeStamp"` + LastPrice float64 `json:"lastPrice,string"` + MarkPrice float64 `json:"markPrice,string"` + BestAskPrice float64 `json:"bestAskPrice,string"` + BestBidPrice float64 `json:"bestBidPrice,string"` + High24Hour float64 `json:"high24h,string"` + Low24Hour float64 `json:"low24h,string"` + Volume24Hour float64 `json:"volume24h,string"` + BestAskVolume float64 `json:"bestAskVolume,string"` + BestBidVolume float64 `json:"bestBidVolume,string"` + Turnover float64 `json:"turnover,string"` + Timestamp time.Time `json:"timeStamp"` + Change24Hour float64 `json:"chg24h,string"` + ChangeZeroHour float64 `json:"chg0h,string"` } // SwapTickers stores a map of swap tickers @@ -287,6 +290,16 @@ type SwapKlineItem struct { // SwapKlines stores an array of kline data type SwapKlines []SwapKlineItem +// Instrument stores an individual tradable instrument +type Instrument struct { + InstrumentID currency.Pair `json:"instrumentId"` + Multiplier float64 `json:"multiplier,string"` + MinimumAmount float64 `json:"minAmount,string"` + MaximumAmount float64 `json:"maxAmount,string"` + MinimumPriceChange float64 `json:"minPriceChange,string"` + PricePrecision int64 `json:"pricePrecision,string"` +} + // SwapTrade stores an individual trade type SwapTrade struct { Price float64 diff --git a/exchanges/coinbene/coinbene_wrapper.go b/exchanges/coinbene/coinbene_wrapper.go index 2c0733d1..87262d68 100644 --- a/exchanges/coinbene/coinbene_wrapper.go +++ b/exchanges/coinbene/coinbene_wrapper.go @@ -75,6 +75,7 @@ func (c *Coinbene) SetDefaults() { err = c.StoreAssetPairFormat(asset.PerpetualSwap, currency.PairStore{ RequestFormat: ¤cy.PairFormat{ Uppercase: true, + Delimiter: currency.DashDelimiter, }, ConfigFormat: ¤cy.PairFormat{ Uppercase: true, @@ -257,23 +258,17 @@ func (c *Coinbene) FetchTradablePairs(a asset.Item) ([]string, error) { currencies = append(currencies, pairs[x].Symbol) } case asset.PerpetualSwap: - format, err := c.GetPairFormat(a, false) + instruments, err := c.GetSwapInstruments() if err != nil { return nil, err } - - tickers, err := c.GetSwapTickers() + pFmt, err := c.GetPairFormat(asset.PerpetualSwap, false) if err != nil { return nil, err } - for t := range tickers { - idx := strings.Index(t, currency.USDT.String()) - if idx == 0 { - return nil, - fmt.Errorf("%s SWAP currency does not contain USDT", c.Name) - } + for x := range instruments { currencies = append(currencies, - t[0:idx]+format.Delimiter+t[idx:]) + instruments[x].InstrumentID.Format(pFmt.Delimiter, pFmt.Uppercase).String()) } } return currencies, nil diff --git a/exchanges/coinbene/ratelimit.go b/exchanges/coinbene/ratelimit.go index a8415787..2ddc09ba 100644 --- a/exchanges/coinbene/ratelimit.go +++ b/exchanges/coinbene/ratelimit.go @@ -15,6 +15,7 @@ const ( tickersContractReqRate = 20 klineContractReqRate = 20 tradesContractReqRate = 20 + contractInstrumentsReqRate = 20 contractAccountInfoContractReqRate = 10 positionInfoContractReqRate = 10 placeOrderContractReqRate = 20 @@ -54,6 +55,7 @@ const ( contractTickers contractKline contractTrades + contractInstruments contractAccountInfo contractPositionInfo contractPlaceOrder @@ -93,6 +95,7 @@ type RateLimit struct { ContractTickers *rate.Limiter ContractKline *rate.Limiter ContractTrades *rate.Limiter + ContractInstruments *rate.Limiter ContractAccountInfo *rate.Limiter ContractPositionInfo *rate.Limiter ContractPlaceOrder *rate.Limiter @@ -136,6 +139,8 @@ func (r *RateLimit) Limit(f request.EndpointLimit) error { time.Sleep(r.ContractKline.Reserve().Delay()) case contractTrades: time.Sleep(r.ContractTrades.Reserve().Delay()) + case contractInstruments: + time.Sleep(r.ContractInstruments.Reserve().Delay()) case contractAccountInfo: time.Sleep(r.ContractAccountInfo.Reserve().Delay()) case contractPositionInfo: @@ -209,6 +214,7 @@ func SetRateLimit() *RateLimit { ContractTickers: request.NewRateLimit(contractRateInterval, tickersContractReqRate), ContractKline: request.NewRateLimit(contractRateInterval, klineContractReqRate), ContractTrades: request.NewRateLimit(contractRateInterval, tradesContractReqRate), + ContractInstruments: request.NewRateLimit(contractRateInterval, contractInstrumentsReqRate), ContractAccountInfo: request.NewRateLimit(contractRateInterval, contractAccountInfoContractReqRate), ContractPositionInfo: request.NewRateLimit(contractRateInterval, positionInfoContractReqRate), ContractPlaceOrder: request.NewRateLimit(contractRateInterval, placeOrderContractReqRate), diff --git a/testdata/configtest.json b/testdata/configtest.json index ef48c527..e612ee1e 100644 --- a/testdata/configtest.json +++ b/testdata/configtest.json @@ -2360,10 +2360,11 @@ ], "pairs": { "perpetualswap": { - "enabled": "BTC/USDT", - "available": "EOS/USDT,LTC/USDT,ETH/USDT,BTC/USDT", + "enabled": "BTC/SWAP", + "available": "YFI/SWAP,SUSHI/SWAP,TRX/SWAP,ETC/SWAP,XRP/SWAP,LTC/SWAP,EOS/SWAP,FIL/SWAP,UNI/SWAP,DOT/SWAP,LINK/SWAP,BSV/SWAP,BCH/SWAP,ETH/SWAP,BTC/SWAP", "requestFormat": { - "uppercase": true + "uppercase": true, + "delimiter": "-" }, "configFormat": { "uppercase": true,