From 0522a54681c2a2b0b26074107980c71e9a0596b4 Mon Sep 17 00:00:00 2001 From: Bea <103050835+Beadko@users.noreply.github.com> Date: Mon, 24 Jul 2023 10:42:49 +0700 Subject: [PATCH] Kraken: Load order execution limits for tradable pairs (#1255) * Kraken: Load asset limits for pairs * Kraken: Add test for spot order limit loading * Kraken: move Limits to UpdateExecutionLimits * Kraken: Whitespace fixed * Kraken: added ErrNotYetImplemented for the futures * Kraken: Fix FetchTradablePairs Loading Limits * Kraken: changed the error to match the Go 1.13 error package --------- Co-authored-by: Gareth Kirwan --- exchanges/kraken/kraken_test.go | 38 +++++++++ exchanges/kraken/kraken_types.go | 3 +- exchanges/kraken/kraken_wrapper.go | 124 +++++++++++++++++++---------- 3 files changed, 124 insertions(+), 41 deletions(-) diff --git a/exchanges/kraken/kraken_test.go b/exchanges/kraken/kraken_test.go index c0d9510a..82f74cdc 100644 --- a/exchanges/kraken/kraken_test.go +++ b/exchanges/kraken/kraken_test.go @@ -99,6 +99,44 @@ func TestWrapperGetServerTime(t *testing.T) { } } +func TestUpdateOrderExecutionLimits(t *testing.T) { + t.Parallel() + + type limitTest struct { + pair currency.Pair + step float64 + min float64 + } + + tests := map[asset.Item][]limitTest{ + asset.Spot: { + {currency.NewPair(currency.ETH, currency.USDT), 0.01, 0.01}, + {currency.NewPair(currency.XBT, currency.USDT), 0.1, 0.0001}, + }, + } + + for assetItem, limitTests := range tests { + if err := k.UpdateOrderExecutionLimits(context.Background(), assetItem); err != nil { + t.Errorf("Error fetching %s pairs for test: %v", assetItem, err) + } + + for _, limitTest := range limitTests { + limits, err := k.GetOrderExecutionLimits(assetItem, limitTest.pair) + if err != nil { + t.Errorf("Kraken GetOrderExecutionLimits() error during TestExecutionLimits; Asset: %s Pair: %s Err: %v", assetItem, limitTest.pair, err) + continue + } + if got := limits.PriceStepIncrementSize; got != limitTest.step { + t.Errorf("Kraken UpdateOrderExecutionLimits wrong PriceStepIncrementSize; Asset: %s Pair: %s Expected: %v Got: %v", assetItem, limitTest.pair, limitTest.step, got) + } + + if got := limits.MinimumBaseAmount; got != limitTest.min { + t.Errorf("Kraken UpdateOrderExecutionLimits wrong MinAmount; Pair: %s Expected: %v Got: %v", limitTest.pair, limitTest.min, got) + } + } + } +} + func TestFetchTradablePairs(t *testing.T) { t.Parallel() _, err := k.FetchTradablePairs(context.Background(), asset.Futures) diff --git a/exchanges/kraken/kraken_types.go b/exchanges/kraken/kraken_types.go index e39e0672..49ff3d5e 100644 --- a/exchanges/kraken/kraken_types.go +++ b/exchanges/kraken/kraken_types.go @@ -122,7 +122,8 @@ type AssetPairs struct { FeeVolumeCurrency string `json:"fee_volume_currency"` MarginCall int `json:"margin_call"` MarginStop int `json:"margin_stop"` - Ordermin string `json:"ordermin"` + OrderMinimum float64 `json:"ordermin,string"` + TickSize float64 `json:"tick_size,string"` Status string `json:"status"` } diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index 1d040ee5..50761937 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -328,23 +328,92 @@ func (k *Kraken) Run(ctx context.Context) { } } - if !k.GetEnabledFeatures().AutoPairUpdates && !forceUpdate { - return + if k.GetEnabledFeatures().AutoPairUpdates || forceUpdate { + if err := k.UpdateTradablePairs(ctx, forceUpdate); err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + k.Name, + err) + } } - err := k.UpdateTradablePairs(ctx, forceUpdate) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update tradable pairs. Err: %s", - k.Name, - err) + for _, a := range k.GetAssetTypes(true) { + if err := k.UpdateOrderExecutionLimits(ctx, a); err != nil && err != common.ErrNotYetImplemented { + log.Errorln(log.ExchangeSys, err.Error()) + } } } +// UpdateOrderExecutionLimits sets exchange execution order limits for an asset type +func (k *Kraken) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) error { + if a != asset.Spot { + return common.ErrNotYetImplemented + } + + pairInfo, err := k.fetchSpotPairInfo(ctx) + if err != nil { + return fmt.Errorf("%s failed to load %s pair execution limits. Err: %s", k.Name, a, err) + } + + limits := make([]order.MinMaxLevel, 0, len(pairInfo)) + + for pair, info := range pairInfo { + limits = append(limits, order.MinMaxLevel{ + Asset: a, + Pair: pair, + PriceStepIncrementSize: info.TickSize, + MinimumBaseAmount: info.OrderMinimum, + }) + } + + if err := k.LoadLimits(limits); err != nil { + return fmt.Errorf("%s Error loading %s exchange limits: %w", k.Name, a, err) + } + + return nil +} + +func (k *Kraken) fetchSpotPairInfo(ctx context.Context) (map[currency.Pair]*AssetPairs, error) { + pairs := make(map[currency.Pair]*AssetPairs) + + pairInfo, err := k.GetAssetPairs(ctx, nil, "") + if err != nil { + return pairs, err + } + + for _, info := range pairInfo { + if info.Status != "online" { + continue + } + base := assetTranslator.LookupAltname(info.Base) + if base == "" { + log.Warnf(log.ExchangeSys, + "%s unable to lookup altname for base currency %s", + k.Name, + info.Base) + continue + } + quote := assetTranslator.LookupAltname(info.Quote) + if quote == "" { + log.Warnf(log.ExchangeSys, + "%s unable to lookup altname for quote currency %s", + k.Name, + info.Quote) + continue + } + pair, err := currency.NewPairFromStrings(base, quote) + if err != nil { + return pairs, err + } + pairs[pair] = info + } + + return pairs, nil +} + // FetchTradablePairs returns a list of the exchanges tradable pairs func (k *Kraken) FetchTradablePairs(ctx context.Context, a asset.Item) (currency.Pairs, error) { - var pairs []currency.Pair - var pair currency.Pair + pairs := currency.Pairs{} switch a { case asset.Spot: if !assetTranslator.Seeded() { @@ -352,36 +421,12 @@ func (k *Kraken) FetchTradablePairs(ctx context.Context, a asset.Item) (currency return nil, err } } - symbols, err := k.GetAssetPairs(ctx, nil, "") + pairInfo, err := k.fetchSpotPairInfo(ctx) if err != nil { - return nil, err + return pairs, err } - - pairs = make([]currency.Pair, 0, len(symbols)) - for _, info := range symbols { - if info.Status != "online" { - continue - } - base := assetTranslator.LookupAltname(info.Base) - if base == "" { - log.Warnf(log.ExchangeSys, - "%s unable to lookup altname for base currency %s", - k.Name, - info.Base) - continue - } - quote := assetTranslator.LookupAltname(info.Quote) - if quote == "" { - log.Warnf(log.ExchangeSys, - "%s unable to lookup altname for quote currency %s", - k.Name, - info.Quote) - continue - } - pair, err = currency.NewPairFromStrings(base, quote) - if err != nil { - return nil, err - } + pairs = make(currency.Pairs, 0, len(pairInfo)) + for pair := range pairInfo { pairs = append(pairs, pair) } case asset.Futures: @@ -404,8 +449,7 @@ func (k *Kraken) FetchTradablePairs(ctx context.Context, a asset.Item) (currency return pairs, nil } -// UpdateTradablePairs updates the exchanges available pairs and stores -// them in the exchanges config +// UpdateTradablePairs updates the exchanges available pairs and stores them in the exchanges config func (k *Kraken) UpdateTradablePairs(ctx context.Context, forceUpdate bool) error { assets := k.GetAssetTypes(false) for x := range assets {