mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-14 07:26:47 +00:00
Kucoin: Add order execution limits (#1369)
* cmd/exchange_template: Add wrapper function * exchanges: force UpdateOrderExecutionLimits to wrappers as this is important for order deployment * kucoin: Add spot order execution limits * linter: fix * glorious: nits * kucoin: change from name to symbol for correct fee fetching and order deployment * WHAAAAAAAT says Vitalik * kucoin: Add futures limit support, fix insertion of non margin tradable pairs, update naming and add comments. * kucoin: implement master branch changes * Update exchanges/kucoin/kucoin_test.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * kucoin/test: update commentary --------- Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io> Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
This commit is contained in:
@@ -553,5 +553,9 @@ func ({{.Variable}} *{{.CapitalName}}) GetLatestFundingRates(_ context.Context,
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func ({{.Variable}} *{{.CapitalName}}) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
{{end}}
|
||||
|
||||
@@ -1817,7 +1817,6 @@ func (c *Config) LoadConfig(configPath string, dryrun bool) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf(ErrFailureOpeningConfig, configPath, err)
|
||||
}
|
||||
|
||||
return c.CheckConfig()
|
||||
}
|
||||
|
||||
|
||||
@@ -488,3 +488,8 @@ func (a *Alphapoint) GetFuturesContractDetails(context.Context, asset.Item) ([]f
|
||||
func (a *Alphapoint) GetLatestFundingRates(context.Context, *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (a *Alphapoint) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -998,3 +998,8 @@ func (bi *Binanceus) GetFuturesContractDetails(context.Context, asset.Item) ([]f
|
||||
func (bi *Binanceus) GetLatestFundingRates(context.Context, *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (bi *Binanceus) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -510,3 +510,8 @@ func (b *Bitflyer) GetFuturesContractDetails(context.Context, asset.Item) ([]fut
|
||||
func (b *Bitflyer) GetLatestFundingRates(context.Context, *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (b *Bitflyer) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -1332,3 +1332,8 @@ func (b *Bitmex) GetLatestFundingRates(ctx context.Context, r *fundingrate.Lates
|
||||
func (b *Bitmex) IsPerpetualFutureCurrency(a asset.Item, _ currency.Pair) (bool, error) {
|
||||
return a == asset.PerpetualContract, nil
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (b *Bitmex) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -1101,3 +1101,8 @@ func (b *Bittrex) GetFuturesContractDetails(context.Context, asset.Item) ([]futu
|
||||
func (b *Bittrex) GetLatestFundingRates(context.Context, *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (b *Bittrex) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -1291,3 +1291,8 @@ func (b *BTSE) GetLatestFundingRates(ctx context.Context, r *fundingrate.LatestR
|
||||
func (b *BTSE) IsPerpetualFutureCurrency(a asset.Item, p currency.Pair) (bool, error) {
|
||||
return a == asset.Futures && p.Quote.Equal(currency.PFC), nil
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (b *BTSE) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -991,3 +991,8 @@ func (c *CoinbasePro) GetLatestFundingRates(context.Context, *fundingrate.Latest
|
||||
func (c *CoinbasePro) GetFuturesContractDetails(context.Context, asset.Item) ([]futures.Contract, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (c *CoinbasePro) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -1205,3 +1205,8 @@ func (c *COINUT) GetFuturesContractDetails(context.Context, asset.Item) ([]futur
|
||||
func (c *COINUT) GetLatestFundingRates(context.Context, *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (c *COINUT) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -1417,11 +1417,6 @@ func getURLTypeFromString(ep string) (URL, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits this is overridable
|
||||
func (b *Base) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// DisableAssetWebsocketSupport disables websocket functionality for the
|
||||
// supplied asset item. In the case that websocket functionality has not yet
|
||||
// been implemented for that specific asset type. This is a base method to
|
||||
|
||||
@@ -2453,14 +2453,6 @@ func TestUpdateCurrencyStates(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateOrderExecutionLimits(t *testing.T) {
|
||||
t.Parallel()
|
||||
var b Base
|
||||
if err := b.UpdateOrderExecutionLimits(context.Background(), asset.Spot); !errors.Is(err, common.ErrNotYetImplemented) {
|
||||
t.Errorf("received: %v, expected: %v", err, common.ErrNotYetImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetTradeFeedStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := Base{
|
||||
|
||||
@@ -840,3 +840,8 @@ func (e *EXMO) GetFuturesContractDetails(context.Context, asset.Item) ([]futures
|
||||
func (e *EXMO) GetLatestFundingRates(context.Context, *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (e *EXMO) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -999,3 +999,8 @@ func (h *HitBTC) GetFuturesContractDetails(context.Context, asset.Item) ([]futur
|
||||
func (h *HitBTC) GetLatestFundingRates(context.Context, *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (h *HitBTC) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -2299,3 +2299,8 @@ func (h *HUOBI) GetLatestFundingRates(ctx context.Context, r *fundingrate.Latest
|
||||
func (h *HUOBI) IsPerpetualFutureCurrency(a asset.Item, _ currency.Pair) (bool, error) {
|
||||
return a == asset.CoinMarginedFutures, nil
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (h *HUOBI) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -704,3 +704,8 @@ func (i *ItBit) GetFuturesContractDetails(context.Context, asset.Item) ([]future
|
||||
func (i *ItBit) GetLatestFundingRates(context.Context, *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (i *ItBit) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -138,10 +138,11 @@ const (
|
||||
)
|
||||
|
||||
// GetSymbols gets pairs details on the exchange
|
||||
func (ku *Kucoin) GetSymbols(ctx context.Context, ccy string) ([]SymbolInfo, error) {
|
||||
// For market details see endpoint: https://www.kucoin.com/docs/rest/spot-trading/market-data/get-market-list
|
||||
func (ku *Kucoin) GetSymbols(ctx context.Context, market string) ([]SymbolInfo, error) {
|
||||
params := url.Values{}
|
||||
if ccy != "" {
|
||||
params.Set("market", ccy)
|
||||
if market != "" {
|
||||
params.Set("market", market)
|
||||
}
|
||||
var resp []SymbolInfo
|
||||
return resp, ku.SendHTTPRequest(ctx, exchange.RestSpot, defaultSpotEPL, common.EncodeURLValues(kucoinGetSymbols, params), &resp)
|
||||
|
||||
@@ -81,14 +81,17 @@ func TestMain(m *testing.M) {
|
||||
// Spot asset test cases starts from here
|
||||
func TestGetSymbols(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := ku.GetSymbols(context.Background(), "")
|
||||
symbols, err := ku.GetSymbols(context.Background(), "")
|
||||
if err != nil {
|
||||
t.Error("GetSymbols() error", err)
|
||||
}
|
||||
_, err = ku.GetSymbols(context.Background(), currency.BTC.String())
|
||||
assert.NotEmpty(t, symbols, "should return all available spot/margin symbols")
|
||||
// Using market string reduces the scope of what is returned.
|
||||
symbols, err = ku.GetSymbols(context.Background(), "ETF")
|
||||
if err != nil {
|
||||
t.Error("GetSymbols() error", err)
|
||||
}
|
||||
assert.NotEmpty(t, symbols, "should return all available ETF symbols")
|
||||
}
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
@@ -2558,3 +2561,33 @@ func TestGetFuturesPositionOrders(t *testing.T) {
|
||||
_, err = ku.GetFuturesPositionOrders(context.Background(), req)
|
||||
assert.ErrorIs(t, err, nil)
|
||||
}
|
||||
|
||||
func TestUpdateOrderExecutionLimits(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
err := ku.UpdateOrderExecutionLimits(context.Background(), asset.Binary)
|
||||
if !errors.Is(err, asset.ErrNotSupported) {
|
||||
t.Fatalf("Received %v, expected %v", err, asset.ErrNotSupported)
|
||||
}
|
||||
|
||||
assets := []asset.Item{asset.Spot, asset.Futures, asset.Margin}
|
||||
for x := range assets {
|
||||
err = ku.UpdateOrderExecutionLimits(context.Background(), assets[x])
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received %v, expected %v", err, nil)
|
||||
}
|
||||
|
||||
enabled, err := ku.GetEnabledPairs(assets[x])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for y := range enabled {
|
||||
lim, err := ku.GetOrderExecutionLimits(assets[x], enabled[y])
|
||||
if err != nil {
|
||||
t.Fatalf("%v %s %v", err, enabled[y], assets[x])
|
||||
}
|
||||
assert.NotEmpty(t, lim, "limit cannot be empty")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ var (
|
||||
errSizeOrFundIsRequired = errors.New("at least one required among size and funds")
|
||||
errInvalidLeverage = errors.New("invalid leverage value")
|
||||
errInvalidClientOrderID = errors.New("invalid client order ID")
|
||||
errCurrencyPairNotEnabled = errors.New("currency pair not enabled")
|
||||
|
||||
subAccountRegExp = regexp.MustCompile("^[a-zA-Z0-9]{7-32}$")
|
||||
subAccountPassphraseRegExp = regexp.MustCompile("^[a-zA-Z0-9]{7-24}$")
|
||||
|
||||
@@ -249,6 +249,16 @@ func (ku *Kucoin) Run(ctx context.Context) {
|
||||
ku.PrintEnabledPairs()
|
||||
}
|
||||
|
||||
assetTypes := ku.GetAssetTypes(false)
|
||||
for i := range assetTypes {
|
||||
if err := ku.UpdateOrderExecutionLimits(ctx, assetTypes[i]); err != nil && !errors.Is(err, common.ErrNotYetImplemented) {
|
||||
log.Errorf(log.ExchangeSys,
|
||||
"%s failed to set exchange order execution limits. Err: %v",
|
||||
ku.Name,
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
if !ku.GetEnabledFeatures().AutoPairUpdates {
|
||||
return
|
||||
}
|
||||
@@ -294,10 +304,12 @@ func (ku *Kucoin) FetchTradablePairs(ctx context.Context, assetType asset.Item)
|
||||
}
|
||||
pairs := make(currency.Pairs, 0, len(myPairs))
|
||||
for x := range myPairs {
|
||||
if !myPairs[x].EnableTrading {
|
||||
if !myPairs[x].EnableTrading || (assetType == asset.Margin && !myPairs[x].IsMarginEnabled) {
|
||||
continue
|
||||
}
|
||||
cp, err = currency.NewPairFromString(strings.ToUpper(myPairs[x].Name))
|
||||
// Symbol field must be used to generate pair as this is the symbol
|
||||
// to fetch data from the API. e.g. BSV-USDT name is BCHSV-USDT as symbol.
|
||||
cp, err = currency.NewPairFromString(strings.ToUpper(myPairs[x].Symbol))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1904,3 +1916,68 @@ func (ku *Kucoin) GetFuturesPositionOrders(ctx context.Context, r *futures.Posit
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (ku *Kucoin) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) error {
|
||||
if !ku.SupportsAsset(a) {
|
||||
return fmt.Errorf("%w %v", asset.ErrNotSupported, a)
|
||||
}
|
||||
|
||||
var limits []order.MinMaxLevel
|
||||
switch a {
|
||||
case asset.Spot, asset.Margin:
|
||||
symbols, err := ku.GetSymbols(ctx, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
limits = make([]order.MinMaxLevel, 0, len(symbols))
|
||||
for x := range symbols {
|
||||
if a == asset.Margin && !symbols[x].IsMarginEnabled {
|
||||
continue
|
||||
}
|
||||
pair, enabled, err := ku.MatchSymbolCheckEnabled(symbols[x].Symbol, a, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !enabled {
|
||||
continue
|
||||
}
|
||||
limits = append(limits, order.MinMaxLevel{
|
||||
Pair: pair,
|
||||
Asset: a,
|
||||
AmountStepIncrementSize: symbols[x].BaseIncrement,
|
||||
QuoteStepIncrementSize: symbols[x].QuoteIncrement,
|
||||
PriceStepIncrementSize: symbols[x].PriceIncrement,
|
||||
MinimumBaseAmount: symbols[x].BaseMinSize,
|
||||
MaximumBaseAmount: symbols[x].BaseMaxSize,
|
||||
MinimumQuoteAmount: symbols[x].QuoteMinSize,
|
||||
MaximumQuoteAmount: symbols[x].QuoteMaxSize,
|
||||
})
|
||||
}
|
||||
case asset.Futures:
|
||||
contract, err := ku.GetFuturesOpenContracts(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
limits = make([]order.MinMaxLevel, 0, len(contract))
|
||||
for x := range contract {
|
||||
pair, enabled, err := ku.MatchSymbolCheckEnabled(contract[x].Symbol, a, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !enabled {
|
||||
continue
|
||||
}
|
||||
limits = append(limits, order.MinMaxLevel{
|
||||
Pair: pair,
|
||||
Asset: a,
|
||||
AmountStepIncrementSize: contract[x].LotSize,
|
||||
QuoteStepIncrementSize: contract[x].TickSize,
|
||||
MaximumBaseAmount: contract[x].MaxOrderQty,
|
||||
MaximumQuoteAmount: contract[x].MaxPrice,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return ku.LoadLimits(limits)
|
||||
}
|
||||
|
||||
@@ -1031,3 +1031,8 @@ func (l *Lbank) GetFuturesContractDetails(context.Context, asset.Item) ([]future
|
||||
func (l *Lbank) GetLatestFundingRates(context.Context, *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (l *Lbank) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -1143,3 +1143,8 @@ func (p *Poloniex) GetLatestFundingRates(context.Context, *fundingrate.LatestRat
|
||||
// TODO: implement with API upgrade
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (p *Poloniex) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -772,3 +772,8 @@ func (y *Yobit) GetFuturesContractDetails(context.Context, asset.Item) ([]future
|
||||
func (y *Yobit) GetLatestFundingRates(context.Context, *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (y *Yobit) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
@@ -1158,3 +1158,8 @@ func (z *ZB) GetFuturesContractDetails(context.Context, asset.Item) ([]futures.C
|
||||
func (z *ZB) GetLatestFundingRates(context.Context, *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// UpdateOrderExecutionLimits updates order execution limits
|
||||
func (z *ZB) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user