diff --git a/exchanges/gateio/gateio_test.go b/exchanges/gateio/gateio_test.go index c2e2dcd8acd..82d4f91c9eb 100644 --- a/exchanges/gateio/gateio_test.go +++ b/exchanges/gateio/gateio_test.go @@ -3344,8 +3344,8 @@ func BenchmarkGetOpenInterestFutures(b *testing.B) { require.Greater(b, len(availPairs), 5) err = g.CurrencyPairs.StorePairs(a, availPairs[:5], true) require.NoError(b, err) - keys := make([]key.PairAsset, 0, 5) - for i := range 5 { + keys := make([]key.PairAsset, 0, 2) + for i := range 1 { keys = append(keys, key.PairAsset{ Asset: a, Base: availPairs[i].Base.Item, diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index f588283b4de..3ccf1ceeb4e 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -2201,73 +2201,113 @@ func contractToFundingRate(name string, item asset.Item, fPair currency.Pair, co // If no pairs are provided, all enabled assets and pairs will be used func (g *Gateio) GetOpenInterest(ctx context.Context, keys ...key.PairAsset) ([]futures.OpenInterest, error) { var errs error - for _, a := range []asset.Item{asset.DeliveryFutures, asset.CoinMarginedFutures, asset.USDTMarginedFutures} { - pairs, err := g.GetEnabledPairs(a) - if err != nil { - continue - } - if len(keys) == 0 { - for _, p := range pairs { - keys = append(keys, key.PairAsset{ - Asset: a, - Base: p.Base.Item, - Quote: p.Quote.Item, - }) - } - } else { - for _, k := range keys { - if k.Asset == a { - if p := k.Pair(); !pairs.Contains(p, true) { - return nil, fmt.Errorf("%w: %s %s", currency.ErrPairNotEnabled, a, p) - } - } + resp := make([]futures.OpenInterest, 0, len(keys)) + assets := asset.Items{} + if len(keys) == 0 { + assets = g.GetAssetTypes(true) + } else { + for _, k := range keys { + if !slices.Contains(assets, k.Asset) { + assets = append(assets, k.Asset) } } } - resp := make([]futures.OpenInterest, 0, len(keys)) - for _, k := range keys { - p, isEnabled, err := g.MatchSymbolCheckEnabled(k.Pair().String(), k.Asset, false) - if !isEnabled { - err = currency.ErrPairNotEnabled + for _, a := range assets { + var p currency.Pair + if len(keys) == 1 && a == keys[0].Asset { + if p, _, errs = g.MatchSymbolCheckEnabled(keys[0].Pair().String(), a, false); errs != nil { + return nil, errs + } } + contracts, err := g.getOpenInterestContracts(ctx, a, p) if err != nil { - errs = common.AppendError(errs, err) - err = fmt.Errorf("%w: %s", asset.ErrNotEnabled, k.Pair()) + errs = common.AppendError(errs, fmt.Errorf("%w fetching %s", err, a)) continue } - settlementCurrency := currency.USDT - var openInterest float64 - switch k.Asset { - case asset.DeliveryFutures: - contractResp, err2 := g.GetDeliveryContract(ctx, currency.USDT, p) - err = err2 - openInterest = contractResp.QuantoMultiplier.Float64() * float64(contractResp.PositionSize) * contractResp.IndexPrice.Float64() - case asset.CoinMarginedFutures: - settlementCurrency = currency.BTC - fallthrough - case asset.USDTMarginedFutures: - contractResp, err2 := g.GetFuturesContract(ctx, settlementCurrency, p.String()) - err = err2 - openInterest = contractResp.QuantoMultiplier.Float64() * float64(contractResp.PositionSize) * contractResp.IndexPrice.Float64() - default: - err = fmt.Errorf("%w %s %s", asset.ErrNotSupported, k.Asset, k.Pair()) - } - if err != nil { - return nil, err + for _, c := range contracts { + if p == currency.EMPTYPAIR { + pN, isEnabled, err := g.MatchSymbolCheckEnabled(c.contractName(), a, true) + if err != nil && !errors.Is(err, currency.ErrPairNotFound) { + errs = common.AppendError(errs, fmt.Errorf("%w from %s contract %s", err, a, c.contractName())) + continue + } + if !isEnabled { + continue + } + if len(keys) > 1 { + wanted := slices.ContainsFunc(keys, func(k key.PairAsset) bool { + return a == k.Asset && k.Pair().Equal(p) + }) + if !wanted { + continue + } + } + p = pN + } + resp = append(resp, futures.OpenInterest{ + Key: key.ExchangePairAsset{ + Exchange: g.Name, + Base: p.Base.Item, + Quote: p.Quote.Item, + Asset: a, + }, + OpenInterest: c.openInterest(), + }) } - resp = append(resp, futures.OpenInterest{ - Key: key.ExchangePairAsset{ - Exchange: g.Name, - Base: k.Base, - Quote: k.Quote, - Asset: k.Asset, - }, - OpenInterest: openInterest, - }) } return slices.Clip(resp), errs } +type openInterestContract interface { + openInterest() float64 + contractName() string +} + +func (c *FuturesContract) openInterest() float64 { + return c.QuantoMultiplier.Float64() * float64(c.PositionSize) * c.IndexPrice.Float64() +} +func (c *FuturesContract) contractName() string { + return c.Name +} +func (c *DeliveryContract) openInterest() float64 { + return c.QuantoMultiplier.Float64() * float64(c.PositionSize) * c.IndexPrice.Float64() +} +func (c *DeliveryContract) contractName() string { + return c.Name +} + +func (g *Gateio) getOpenInterestContracts(ctx context.Context, a asset.Item, p currency.Pair) ([]openInterestContract, error) { + contracts := []openInterestContract{} + settlementCurrency := currency.USDT + switch a { + case asset.DeliveryFutures: + if p != currency.EMPTYPAIR { + d, err := g.GetDeliveryContract(ctx, currency.USDT, p) + return []openInterestContract{d}, err + } + d, err := g.GetAllDeliveryContracts(ctx, currency.USDT) + for _, c := range d { + contracts = append(contracts, &c) + } + return contracts, err + case asset.CoinMarginedFutures: + settlementCurrency = currency.BTC + case asset.USDTMarginedFutures: + // Continue + default: + return nil, fmt.Errorf("%w: %s", asset.ErrNotSupported, a) + } + if p != currency.EMPTYPAIR { + contract, err := g.GetFuturesContract(ctx, settlementCurrency, p.String()) + return []openInterestContract{contract}, err + } + futures, err := g.GetAllFutureContracts(ctx, settlementCurrency) + for _, c := range futures { + contracts = append(contracts, &c) + } + return contracts, err +} + // getClientOrderIDFromText returns the client order ID from the text response func getClientOrderIDFromText(text string) string { if strings.HasPrefix(text, "t-") {