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
This commit is contained in:
Bea
2024-02-02 13:03:48 +07:00
committed by GitHub
parent d7818ea956
commit e16ee53746
3 changed files with 95 additions and 19 deletions

View File

@@ -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 == "" {

View File

@@ -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) {

View File

@@ -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