From e16ee537464cb2aae5264e4f3e07b9a0f2f10db1 Mon Sep 17 00:00:00 2001 From: Bea <103050835+Beadko@users.noreply.github.com> Date: Fri, 2 Feb 2024 13:03:48 +0700 Subject: [PATCH] Kucoin: Implement GetFuturesTickers, update UpdateTickers and enhance test coverage (#1431) * Kucoin: update TestUpdateTickers, create GetFuturesTickers * Kucoin: Update UpdateTickers, getFuturesTickers and testing * Kucoin: Amend the GetFuturesTickers description, remove currency.ErrPairNotFound, return err * Kucoin: Parallelilise GetFuturesTickers * Kucoin: Revert to GetFuturesOpenContracts in GetTickers * Kucoin: Append the errors, fixup linter * Kucoin: Fix the race warning * Kucoin: Fixup * Kucoin: Move GetFuturesTickers to kucoin_futures --- exchanges/kucoin/kucoin_futures.go | 61 ++++++++++++++++++++++++++++++ exchanges/kucoin/kucoin_test.go | 45 ++++++++++++++-------- exchanges/kucoin/kucoin_wrapper.go | 8 ++-- 3 files changed, 95 insertions(+), 19 deletions(-) diff --git a/exchanges/kucoin/kucoin_futures.go b/exchanges/kucoin/kucoin_futures.go index 2778143b..d1883d4d 100644 --- a/exchanges/kucoin/kucoin_futures.go +++ b/exchanges/kucoin/kucoin_futures.go @@ -7,14 +7,17 @@ import ( "net/http" "net/url" "strconv" + "sync" "time" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" + "github.com/thrasher-corp/gocryptotrader/exchanges/asset" "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/orderbook" + "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" ) const ( @@ -97,6 +100,64 @@ func (ku *Kucoin) GetFuturesTicker(ctx context.Context, symbol string) (*Futures return resp, ku.SendHTTPRequest(ctx, exchange.RestFutures, defaultFuturesEPL, common.EncodeURLValues(kucoinFuturesTicker, params), &resp) } +// GetFuturesTickers does n * REST requests based on enabled pairs of the futures asset type +func (ku *Kucoin) GetFuturesTickers(ctx context.Context) ([]*ticker.Price, error) { + pairs, err := ku.GetEnabledPairs(asset.Futures) + if err != nil { + return nil, err + } + + var wg sync.WaitGroup + tickersC := make(chan *ticker.Price, len(pairs)) + errC := make(chan error, len(pairs)) + + for i := range pairs { + var p currency.Pair + if p, err = ku.FormatExchangeCurrency(pairs[i], asset.Futures); err != nil { + errC <- err + break + } + wg.Add(1) + go func() { + defer wg.Done() + + if tick, err2 := ku.GetFuturesTicker(ctx, p.String()); err2 != nil { + errC <- err2 + } else { + tickersC <- &ticker.Price{ + Last: tick.Price.Float64(), + Bid: tick.BestBidPrice.Float64(), + Ask: tick.BestAskPrice.Float64(), + BidSize: tick.BestBidSize, + AskSize: tick.BestAskSize, + Volume: tick.Size, + Pair: p, + LastUpdated: tick.FilledTime.Time(), + ExchangeName: ku.Name, + AssetType: asset.Futures, + } + } + }() + } + + wg.Wait() + close(tickersC) + close(errC) + var errs error + for err := range errC { + errs = common.AppendError(errs, err) + } + if errs != nil { + return nil, errs + } + + tickers := make([]*ticker.Price, 0, len(pairs)) + for tick := range tickersC { + tickers = append(tickers, tick) + } + return tickers, nil +} + // GetFuturesOrderbook gets full orderbook for a specified symbol func (ku *Kucoin) GetFuturesOrderbook(ctx context.Context, symbol string) (*Orderbook, error) { if symbol == "" { diff --git a/exchanges/kucoin/kucoin_test.go b/exchanges/kucoin/kucoin_test.go index 0be84033..5518c5a3 100644 --- a/exchanges/kucoin/kucoin_test.go +++ b/exchanges/kucoin/kucoin_test.go @@ -115,6 +115,21 @@ func TestGetAllTickers(t *testing.T) { } } +func TestGetFuturesTickers(t *testing.T) { + t.Parallel() + tickers, err := ku.GetFuturesTickers(context.Background()) + assert.NoError(t, err, "GetFuturesTickers should not error") + for i := range tickers { + assert.Positive(t, tickers[i].Last, "Last should be positive") + assert.Positive(t, tickers[i].Bid, "Bid should be positive") + assert.Positive(t, tickers[i].Ask, "Ask should be positive") + assert.NotEmpty(t, tickers[i].Pair, "Pair should not be empty") + assert.NotEmpty(t, tickers[i].LastUpdated, "LastUpdated should not be empty") + assert.Equal(t, ku.Name, tickers[i].ExchangeName, "Exchange name should be correct") + assert.Equal(t, asset.Futures, tickers[i].AssetType, "Asset type should be correct") + } +} + func TestGet24hrStats(t *testing.T) { t.Parallel() _, err := ku.Get24hrStats(context.Background(), "BTC-USDT") @@ -1666,21 +1681,21 @@ func TestUpdateOrderbook(t *testing.T) { } func TestUpdateTickers(t *testing.T) { t.Parallel() - err := ku.UpdateTickers(context.Background(), asset.Spot) - if err != nil { - t.Fatal(err) - } - err = ku.UpdateTickers(context.Background(), asset.Margin) - if err != nil { - t.Fatal(err) - } - err = ku.UpdateTickers(context.Background(), asset.Futures) - if err != nil { - t.Fatal(err) - } - err = ku.UpdateTickers(context.Background(), asset.Empty) - if !errors.Is(err, asset.ErrNotSupported) { - t.Fatal(err) + for _, a := range ku.GetAssetTypes(true) { + err := ku.UpdateTickers(context.Background(), a) + assert.NoError(t, err, "UpdateTickers should not error") + pairs, err := ku.GetEnabledPairs(a) + assert.NoError(t, err, "GetEnabledPairs should not error") + for _, p := range pairs { + tick, err := ticker.GetTicker(ku.Name, p, a) + if assert.NoError(t, err, "GetTicker %s %s should not error", a, p) { + assert.Positive(t, tick.Last, "%s %s Tick Last should be positive", a, p) + assert.NotEmpty(t, tick.Pair, "%s %s Tick Pair should not be empty", a, p) + assert.Equal(t, ku.Name, tick.ExchangeName, "ExchangeName should be correct") + assert.Equal(t, a, tick.AssetType, "AssetType should be correct") + assert.NotEmpty(t, tick.LastUpdated, "%s %s Tick LastUpdated should not be empty", a, p) + } + } } } func TestUpdateTicker(t *testing.T) { diff --git a/exchanges/kucoin/kucoin_wrapper.go b/exchanges/kucoin/kucoin_wrapper.go index f5155788..e00abb75 100644 --- a/exchanges/kucoin/kucoin_wrapper.go +++ b/exchanges/kucoin/kucoin_wrapper.go @@ -330,6 +330,7 @@ func (ku *Kucoin) UpdateTicker(ctx context.Context, p currency.Pair, assetType a // UpdateTickers updates all currency pairs of a given asset type func (ku *Kucoin) UpdateTickers(ctx context.Context, assetType asset.Item) error { + var errs error switch assetType { case asset.Futures: ticks, err := ku.GetFuturesOpenContracts(ctx) @@ -360,10 +361,9 @@ func (ku *Kucoin) UpdateTickers(ctx context.Context, assetType asset.Item) error AssetType: assetType, }) if err != nil { - return err + errs = common.AppendError(errs, err) } } - return nil case asset.Spot, asset.Margin: ticks, err := ku.GetTickers(ctx) if err != nil { @@ -395,14 +395,14 @@ func (ku *Kucoin) UpdateTickers(ctx context.Context, assetType asset.Item) error LastUpdated: ticks.Time.Time(), }) if err != nil { - return err + errs = common.AppendError(errs, err) } } } default: return fmt.Errorf("%w %v", asset.ErrNotSupported, assetType) } - return nil + return errs } // FetchTicker returns the ticker for a currency pair