diff --git a/exchanges/okx/okx_test.go b/exchanges/okx/okx_test.go index 68ca68af..e5e93a34 100644 --- a/exchanges/okx/okx_test.go +++ b/exchanges/okx/okx_test.go @@ -1847,6 +1847,63 @@ func TestUpdateTradablePairs(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.0001}, + {currency.NewPair(currency.BTC, currency.USDT), 0.1, 0.00001}, + }, + asset.Margin: { + {currency.NewPair(currency.ETH, currency.USDT), 0.01, 0.0001}, + {currency.NewPair(currency.ETH, currency.BTC), 0.00001, 0.0001}, + }, + } + + for _, a := range []asset.Item{asset.PerpetualSwap, asset.Futures, asset.Options} { + pairs, err := ok.FetchTradablePairs(context.Background(), a) + if err != nil { + t.Errorf("Error fetching dated %s pairs for test: %v", a, err) + } + stepIncr := 0.1 + if a == asset.Options { + stepIncr = 0.0005 + } + + tests[a] = []limitTest{{pairs[0], stepIncr, 1}} + } + + for _, a := range ok.GetAssetTypes(false) { + if err := ok.UpdateOrderExecutionLimits(context.Background(), a); err != nil { + t.Error("Okx UpdateOrderExecutionLimits() error", err) + continue + } + + for _, tt := range tests[a] { + limits, err := ok.GetOrderExecutionLimits(a, tt.pair) + if err != nil { + t.Errorf("Okx GetOrderExecutionLimits() error during TestUpdateOrderExecutionLimits; Asset: %s Pair: %s Err: %v", a, tt.pair, err) + continue + } + + if got := limits.PriceStepIncrementSize; got != tt.step { + t.Errorf("Okx UpdateOrderExecutionLimits wrong PriceStepIncrementSize; Asset: %s Pair: %s Expected: %v Got: %v", a, tt.pair, tt.step, got) + } + + if got := limits.MinAmount; got != tt.min { + t.Errorf("Okx UpdateOrderExecutionLimits wrong MinAmount; Pair: %s Expected: %v Got: %v", tt.pair, tt.min, got) + } + } + } +} + func TestUpdateTicker(t *testing.T) { t.Parallel() if _, err := ok.UpdateTicker(context.Background(), currency.NewPair(currency.BTC, currency.USDT), asset.Spot); err != nil { diff --git a/exchanges/okx/okx_wrapper.go b/exchanges/okx/okx_wrapper.go index 9d5b0453..a8ad5461 100644 --- a/exchanges/okx/okx_wrapper.go +++ b/exchanges/okx/okx_wrapper.go @@ -262,15 +262,23 @@ func (ok *Okx) Run(ctx context.Context) { ok.PrintEnabledPairs() } - if !ok.GetEnabledFeatures().AutoPairUpdates { - return + assetTypes := ok.GetAssetTypes(false) + for i := range assetTypes { + if err := ok.UpdateOrderExecutionLimits(ctx, assetTypes[i]); err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to set exchange order execution limits. Err: %v", + ok.Name, + err) + } } - err := ok.UpdateTradablePairs(ctx, false) - if err != nil { - log.Errorf(log.ExchangeSys, - "%s failed to update tradable pairs. Err: %s", - ok.Name, - err) + + if ok.GetEnabledFeatures().AutoPairUpdates { + if err := ok.UpdateTradablePairs(ctx, false); err != nil { + log.Errorf(log.ExchangeSys, + "%s failed to update tradable pairs. Err: %s", + ok.Name, + err) + } } } @@ -281,46 +289,7 @@ func (ok *Okx) GetServerTime(ctx context.Context, _ asset.Item) (time.Time, erro // FetchTradablePairs returns a list of the exchanges tradable pairs func (ok *Okx) FetchTradablePairs(ctx context.Context, a asset.Item) (currency.Pairs, error) { - if !ok.SupportsAsset(a) { - return nil, fmt.Errorf("asset type of %s is not supported by %s", a, ok.Name) - } - var insts []Instrument - var err error - switch a { - case asset.Spot: - insts, err = ok.GetInstruments(ctx, &InstrumentsFetchParams{ - InstrumentType: okxInstTypeSpot, - }) - case asset.Futures: - insts, err = ok.GetInstruments(ctx, &InstrumentsFetchParams{ - InstrumentType: okxInstTypeFutures, - }) - case asset.PerpetualSwap: - insts, err = ok.GetInstruments(ctx, &InstrumentsFetchParams{ - InstrumentType: okxInstTypeSwap, - }) - case asset.Options: - var underlyings []string - underlyings, err = ok.GetPublicUnderlyings(context.Background(), okxInstTypeOption) - if err != nil { - return nil, err - } - for x := range underlyings { - var instruments []Instrument - instruments, err = ok.GetInstruments(ctx, &InstrumentsFetchParams{ - InstrumentType: okxInstTypeOption, - Underlying: underlyings[x], - }) - if err != nil { - return nil, err - } - insts = append(insts, instruments...) - } - case asset.Margin: - insts, err = ok.GetInstruments(ctx, &InstrumentsFetchParams{ - InstrumentType: okxInstTypeMargin, - }) - } + insts, err := ok.getInstrumentsForAsset(ctx, a) if err != nil { return nil, err } @@ -353,6 +322,33 @@ func (ok *Okx) UpdateTradablePairs(ctx context.Context, forceUpdate bool) error return nil } +// UpdateOrderExecutionLimits sets exchange execution order limits for an asset type +func (ok *Okx) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) error { + insts, err := ok.getInstrumentsForAsset(ctx, a) + if err != nil { + return err + } + if len(insts) == 0 { + return errNoInstrumentFound + } + limits := make([]order.MinMaxLevel, len(insts)) + for x := range insts { + pair, err := currency.NewPairFromString(insts[x].InstrumentID) + if err != nil { + return err + } + + limits[x] = order.MinMaxLevel{ + Pair: pair, + Asset: a, + PriceStepIncrementSize: insts[x].TickSize.Float64(), + MinAmount: insts[x].MinimumOrderSize.Float64(), + } + } + + return ok.LoadLimits(limits) +} + // UpdateTicker updates and returns the ticker for a currency pair func (ok *Okx) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { format, err := ok.GetPairFormat(a, false) @@ -1466,3 +1462,49 @@ func (ok *Okx) GetAvailableTransferChains(ctx context.Context, cryptocurrency cu } return chains, nil } + +// getInstrumentsForOptions returns the instruments for options asset type +func (ok *Okx) getInstrumentsForOptions(ctx context.Context) ([]Instrument, error) { + underlyings, err := ok.GetPublicUnderlyings(context.Background(), okxInstTypeOption) + if err != nil { + return nil, err + } + var insts []Instrument + for x := range underlyings { + var instruments []Instrument + instruments, err = ok.GetInstruments(ctx, &InstrumentsFetchParams{ + InstrumentType: okxInstTypeOption, + Underlying: underlyings[x], + }) + if err != nil { + return nil, err + } + insts = append(insts, instruments...) + } + return insts, nil +} + +// getInstrumentsForAsset returns the instruments for an asset type +func (ok *Okx) getInstrumentsForAsset(ctx context.Context, a asset.Item) ([]Instrument, error) { + if !ok.SupportsAsset(a) { + return nil, fmt.Errorf("asset type of %s is not supported by %s", a, ok.Name) + } + + var instType string + switch a { + case asset.Options: + return ok.getInstrumentsForOptions(ctx) + case asset.Spot: + instType = okxInstTypeSpot + case asset.Futures: + instType = okxInstTypeFutures + case asset.PerpetualSwap: + instType = okxInstTypeSwap + case asset.Margin: + instType = okxInstTypeMargin + } + + return ok.GetInstruments(ctx, &InstrumentsFetchParams{ + InstrumentType: instType, + }) +}