mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-04 15:10:54 +00:00
exchanges/futures: Implement open interest (#1417)
* adds open interest to exchanges * ADDS TESTING YEAH * New endpoints, BTSE, RPCS, cached * slight design change, begin gateio You will need to get cached for each exchange that supports it * gateio, huobi, rpc * fix up kraken, cache retrieval * okx, gateio * finalising all implementations and tests * definitely my final ever commit on this * Well, well, well * final v2 * quick fix of bug * test coverage, assert notempty, test helper Added a new testhelper for currency management because its very annoying in a parallel test setting which wastes so much space otherwise * minimises REST requests for Open Interest * types.Number merge misses * Minimises Kraken REST calls * len change, value -> pointer receiver * further fixup * fixes gateio, batch calculates open interest * single gateio, lint const fixes * rejig and more thorough oi for huobi * formatting expansion * minor fix for handling expiring contracts * rm unused Binance strings * add bybit support, fix bybit issues * oopsie doopsie, dont look at my whoopsie * Fix issue, remove feature * move an irrelevant function for the pr * mini bybit upgrades * fixes cli request bug
This commit is contained in:
@@ -39,14 +39,23 @@ type FContractPriceLimits struct {
|
||||
|
||||
// FContractOIData stores open interest data for futures contracts
|
||||
type FContractOIData struct {
|
||||
Data []struct {
|
||||
Symbol string `json:"symbol"`
|
||||
ContractType string `json:"contract_type"`
|
||||
Volume float64 `json:"volume"`
|
||||
Amount float64 `json:"amount"`
|
||||
ContractCode string `json:"contract_code"`
|
||||
} `json:"data"`
|
||||
Timestamp int64 `json:"ts"`
|
||||
Data []UContractOpenInterest `json:"data"`
|
||||
Timestamp int64 `json:"ts"`
|
||||
}
|
||||
|
||||
// UContractOpenInterest stores open interest data for futures contracts
|
||||
type UContractOpenInterest struct {
|
||||
Volume float64 `json:"volume"`
|
||||
Amount float64 `json:"amount"`
|
||||
Symbol string `json:"symbol"`
|
||||
Value float64 `json:"value"`
|
||||
ContractCode string `json:"contract_code"`
|
||||
TradeAmount float64 `json:"trade_amount"`
|
||||
TradeVolume float64 `json:"trade_volume"`
|
||||
TradeTurnover float64 `json:"trade_turnover"`
|
||||
BusinessType string `json:"business_type"`
|
||||
Pair string `json:"pair"`
|
||||
ContractType string `json:"contract_type"`
|
||||
}
|
||||
|
||||
// FEstimatedDeliveryPriceInfo stores estimated delivery price data for futures
|
||||
|
||||
@@ -109,7 +109,9 @@ func (h *HUOBI) SwapOpenInterestInformation(ctx context.Context, code currency.P
|
||||
return resp, err
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("contract_code", codeValue)
|
||||
if !code.IsEmpty() {
|
||||
params.Set("contract_code", codeValue)
|
||||
}
|
||||
path := common.EncodeURLValues(huobiSwapOpenInterestInfo, params)
|
||||
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
||||
}
|
||||
|
||||
@@ -75,6 +75,10 @@ const (
|
||||
fCancelAllTriggerOrders = "/api/v1/contract_trigger_cancelall"
|
||||
fTriggerOpenOrders = "/api/v1/contract_trigger_openorders"
|
||||
fTriggerOrderHistory = "/api/v1/contract_trigger_hisorders"
|
||||
|
||||
uContractOpenInterest = "/linear-swap-api/v1/swap_open_interest"
|
||||
|
||||
fContractDateFormat = "060102"
|
||||
)
|
||||
|
||||
// FGetContractInfo gets contract info for futures
|
||||
@@ -140,6 +144,39 @@ func (h *HUOBI) FContractPriceLimitations(ctx context.Context, symbol, contractT
|
||||
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
||||
}
|
||||
|
||||
// ContractOpenInterestUSDT gets open interest data for futures contracts
|
||||
func (h *HUOBI) ContractOpenInterestUSDT(ctx context.Context, contractCode, pair currency.Pair, contractType, businessType string) ([]UContractOpenInterest, error) {
|
||||
params := url.Values{}
|
||||
if !contractCode.IsEmpty() {
|
||||
cc, err := h.formatFuturesPair(contractCode, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.Set("contract_code", cc)
|
||||
}
|
||||
if !pair.IsEmpty() {
|
||||
p, err := h.formatFuturesPair(pair, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.Set("pair", p)
|
||||
}
|
||||
if contractType != "" {
|
||||
if !common.StringDataCompareInsensitive(validContractTypes, contractType) {
|
||||
return nil, fmt.Errorf("invalid contractType")
|
||||
}
|
||||
params.Set("contract_type", contractType)
|
||||
}
|
||||
if businessType != "" {
|
||||
params.Set("business_type", businessType)
|
||||
}
|
||||
path := common.EncodeURLValues(uContractOpenInterest, params)
|
||||
var resp struct {
|
||||
Data []UContractOpenInterest `json:"data"`
|
||||
}
|
||||
return resp.Data, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
||||
}
|
||||
|
||||
// FContractOpenInterest gets open interest data for futures contracts
|
||||
func (h *HUOBI) FContractOpenInterest(ctx context.Context, symbol, contractType string, code currency.Pair) (FContractOIData, error) {
|
||||
var resp FContractOIData
|
||||
@@ -154,11 +191,11 @@ func (h *HUOBI) FContractOpenInterest(ctx context.Context, symbol, contractType
|
||||
params.Set("contract_type", contractType)
|
||||
}
|
||||
if !code.IsEmpty() {
|
||||
codeValue, err := h.FormatSymbol(code, asset.Futures)
|
||||
codeValue, err := h.convertContractShortHandToExpiry(code)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
params.Set("contract_code", codeValue)
|
||||
params.Set("contract_code", codeValue.String())
|
||||
}
|
||||
path := common.EncodeURLValues(fContractOpenInterest, params)
|
||||
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
||||
@@ -179,7 +216,7 @@ func (h *HUOBI) FGetEstimatedDeliveryPrice(ctx context.Context, symbol currency.
|
||||
|
||||
// FGetMarketDepth gets market depth data for futures contracts
|
||||
func (h *HUOBI) FGetMarketDepth(ctx context.Context, symbol currency.Pair, dataType string) (*OBData, error) {
|
||||
symbolValue, err := h.formatFuturesPair(symbol)
|
||||
symbolValue, err := h.formatFuturesPair(symbol, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -219,7 +256,7 @@ func (h *HUOBI) FGetMarketDepth(ctx context.Context, symbol currency.Pair, dataT
|
||||
func (h *HUOBI) FGetKlineData(ctx context.Context, symbol currency.Pair, period string, size int64, startTime, endTime time.Time) (FKlineData, error) {
|
||||
var resp FKlineData
|
||||
params := url.Values{}
|
||||
symbolValue, err := h.formatFuturesPair(symbol)
|
||||
symbolValue, err := h.formatFuturesPair(symbol, false)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
@@ -246,7 +283,7 @@ func (h *HUOBI) FGetKlineData(ctx context.Context, symbol currency.Pair, period
|
||||
func (h *HUOBI) FGetMarketOverviewData(ctx context.Context, symbol currency.Pair) (FMarketOverviewData, error) {
|
||||
var resp FMarketOverviewData
|
||||
params := url.Values{}
|
||||
symbolValue, err := h.formatFuturesPair(symbol)
|
||||
symbolValue, err := h.formatFuturesPair(symbol, false)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
@@ -259,7 +296,7 @@ func (h *HUOBI) FGetMarketOverviewData(ctx context.Context, symbol currency.Pair
|
||||
func (h *HUOBI) FLastTradeData(ctx context.Context, symbol currency.Pair) (FLastTradeData, error) {
|
||||
var resp FLastTradeData
|
||||
params := url.Values{}
|
||||
symbolValue, err := h.formatFuturesPair(symbol)
|
||||
symbolValue, err := h.formatFuturesPair(symbol, false)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
@@ -271,7 +308,7 @@ func (h *HUOBI) FLastTradeData(ctx context.Context, symbol currency.Pair) (FLast
|
||||
// FRequestPublicBatchTrades gets public batch trades for a futures contract
|
||||
func (h *HUOBI) FRequestPublicBatchTrades(ctx context.Context, symbol currency.Pair, size int64) (FBatchTradesForContractData, error) {
|
||||
params := url.Values{}
|
||||
symbolValue, err := h.formatFuturesPair(symbol)
|
||||
symbolValue, err := h.formatFuturesPair(symbol, false)
|
||||
if err != nil {
|
||||
return FBatchTradesForContractData{}, err
|
||||
}
|
||||
@@ -432,7 +469,7 @@ func (h *HUOBI) FLiquidationOrders(ctx context.Context, symbol currency.Code, tr
|
||||
func (h *HUOBI) FIndexKline(ctx context.Context, symbol currency.Pair, period string, size int64) (FIndexKlineData, error) {
|
||||
var resp FIndexKlineData
|
||||
params := url.Values{}
|
||||
symbolValue, err := h.formatFuturesPair(symbol)
|
||||
symbolValue, err := h.formatFuturesPair(symbol, false)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
@@ -453,7 +490,7 @@ func (h *HUOBI) FIndexKline(ctx context.Context, symbol currency.Pair, period st
|
||||
func (h *HUOBI) FGetBasisData(ctx context.Context, symbol currency.Pair, period, basisPriceType string, size int64) (FBasisData, error) {
|
||||
var resp FBasisData
|
||||
params := url.Values{}
|
||||
symbolValue, err := h.formatFuturesPair(symbol)
|
||||
symbolValue, err := h.formatFuturesPair(symbol, false)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
@@ -1216,9 +1253,58 @@ func (h *HUOBI) formatFuturesCode(p currency.Code) (string, error) {
|
||||
}
|
||||
|
||||
// formatFuturesPair handles pairs in the format as "BTC-NW" and "BTC210827"
|
||||
func (h *HUOBI) formatFuturesPair(p currency.Pair) (string, error) {
|
||||
func (h *HUOBI) formatFuturesPair(p currency.Pair, convertQuoteToExpiry bool) (string, error) {
|
||||
if common.StringDataCompareInsensitive(validContractShortTypes, p.Quote.String()) {
|
||||
if convertQuoteToExpiry {
|
||||
cp, err := h.convertContractShortHandToExpiry(p)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return cp.String(), nil
|
||||
}
|
||||
return p.Format(currency.PairFormat{Delimiter: "_", Uppercase: true}).String(), nil
|
||||
}
|
||||
if p.Quote.IsStableCurrency() {
|
||||
return p.Format(currency.PairFormat{Delimiter: "-", Uppercase: true}).String(), nil
|
||||
}
|
||||
|
||||
return h.FormatSymbol(p, asset.Futures)
|
||||
}
|
||||
|
||||
// convertContractShortHandToExpiry converts a contract shorthand eg BTC-CW into a full expiry date
|
||||
// eg BTC240329 to associate with tradable pair formatting
|
||||
func (h *HUOBI) convertContractShortHandToExpiry(pair currency.Pair) (currency.Pair, error) {
|
||||
if !common.StringDataCompareInsensitive(validContractShortTypes, pair.Quote.String()) {
|
||||
return currency.EMPTYPAIR, fmt.Errorf("%s invalid contract type", pair)
|
||||
}
|
||||
|
||||
tt := time.Now()
|
||||
switch pair.Quote.Item.Symbol {
|
||||
case "NW":
|
||||
tt = tt.AddDate(0, 0, 7)
|
||||
fallthrough
|
||||
case "CW":
|
||||
for {
|
||||
if tt.Weekday() == time.Friday {
|
||||
break
|
||||
}
|
||||
tt = tt.AddDate(0, 0, 1)
|
||||
}
|
||||
case "NQ":
|
||||
tt = tt.AddDate(0, 3, 0)
|
||||
fallthrough
|
||||
case "CQ":
|
||||
// Find the next quarter end
|
||||
for !(tt.Month() == time.March || tt.Month() == time.June || tt.Month() == time.September || tt.Month() == time.December) {
|
||||
tt = tt.AddDate(0, 1, 0)
|
||||
}
|
||||
// Find the last day of the quarter
|
||||
tt = time.Date(tt.Year(), tt.Month()+1, 0, 0, 0, 0, 0, time.UTC)
|
||||
// Find the last Friday of the quarter
|
||||
for tt.Weekday() != time.Friday {
|
||||
tt = tt.AddDate(0, 0, -1)
|
||||
}
|
||||
}
|
||||
pair.Quote = currency.NewCode(tt.Format(fContractDateFormat))
|
||||
return pair, nil
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/key"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/core"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
@@ -2688,7 +2690,7 @@ func TestGetAvailableTransferChains(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFormatFuturesPair(t *testing.T) {
|
||||
r, err := h.formatFuturesPair(futuresTestPair)
|
||||
r, err := h.formatFuturesPair(futuresTestPair, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -2704,7 +2706,7 @@ func TestFormatFuturesPair(t *testing.T) {
|
||||
}
|
||||
// test getting a tradable pair in the format of BTC210827 but make it lower
|
||||
// case to test correct formatting
|
||||
r, err = h.formatFuturesPair(availInstruments[0])
|
||||
r, err = h.formatFuturesPair(availInstruments[0], false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -2714,6 +2716,22 @@ func TestFormatFuturesPair(t *testing.T) {
|
||||
if !strings.Contains(r, "BTC") {
|
||||
t.Errorf("expected %s, got %s", "BTC220708", r)
|
||||
}
|
||||
|
||||
r, err = h.formatFuturesPair(futuresTestPair, true)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if r == "BTC_CW" {
|
||||
t.Errorf("expected BTC{{date}}, got %s", r)
|
||||
}
|
||||
|
||||
r, err = h.formatFuturesPair(currency.NewPair(currency.BTC, currency.USDT), false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if r != "BTC-USDT" {
|
||||
t.Errorf("expected BTC-USDT, got %s", r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchForExistedWithdrawsAndDeposits(t *testing.T) {
|
||||
@@ -2841,3 +2859,95 @@ func TestGetSwapFundingRates(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOpenInterest(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := h.GetOpenInterest(context.Background(), key.PairAsset{
|
||||
Base: currency.ETH.Item,
|
||||
Quote: currency.USDT.Item,
|
||||
Asset: asset.USDTMarginedFutures,
|
||||
})
|
||||
assert.ErrorIs(t, err, asset.ErrNotSupported)
|
||||
|
||||
resp, err := h.GetOpenInterest(context.Background(), key.PairAsset{
|
||||
Base: currency.BTC.Item,
|
||||
Quote: currency.USD.Item,
|
||||
Asset: asset.CoinMarginedFutures,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, resp)
|
||||
|
||||
resp, err = h.GetOpenInterest(context.Background(), key.PairAsset{
|
||||
Base: futuresTestPair.Base.Item,
|
||||
Quote: futuresTestPair.Quote.Item,
|
||||
Asset: asset.Futures,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, resp)
|
||||
|
||||
resp, err = h.GetOpenInterest(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, resp)
|
||||
}
|
||||
|
||||
func TestContractOpenInterestUSDT(t *testing.T) {
|
||||
t.Parallel()
|
||||
resp, err := h.ContractOpenInterestUSDT(context.Background(), currency.EMPTYPAIR, currency.EMPTYPAIR, "", "")
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, resp)
|
||||
|
||||
cp := currency.NewPair(currency.BTC, currency.USDT)
|
||||
resp, err = h.ContractOpenInterestUSDT(context.Background(), cp, currency.EMPTYPAIR, "", "")
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, resp)
|
||||
|
||||
resp, err = h.ContractOpenInterestUSDT(context.Background(), currency.EMPTYPAIR, cp, "", "")
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, resp)
|
||||
|
||||
resp, err = h.ContractOpenInterestUSDT(context.Background(), cp, currency.EMPTYPAIR, "this_week", "")
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, resp)
|
||||
|
||||
resp, err = h.ContractOpenInterestUSDT(context.Background(), currency.EMPTYPAIR, currency.EMPTYPAIR, "", "swap")
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, resp)
|
||||
}
|
||||
|
||||
func TestConvertContractShortHandToExpiry(t *testing.T) {
|
||||
t.Parallel()
|
||||
cp := currency.NewPair(currency.BTC, currency.NewCode("CW"))
|
||||
cp, err := h.convertContractShortHandToExpiry(cp)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, cp.Quote.String(), "CW")
|
||||
tick, err := h.FetchTicker(context.Background(), cp, asset.Futures)
|
||||
assert.NoError(t, err)
|
||||
assert.NotZero(t, tick.Close)
|
||||
|
||||
cp = currency.NewPair(currency.BTC, currency.NewCode("NW"))
|
||||
cp, err = h.convertContractShortHandToExpiry(cp)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, cp.Quote.String(), "NW")
|
||||
tick, err = h.FetchTicker(context.Background(), cp, asset.Futures)
|
||||
assert.NoError(t, err)
|
||||
assert.NotZero(t, tick.Close)
|
||||
|
||||
cp = currency.NewPair(currency.BTC, currency.NewCode("CQ"))
|
||||
cp, err = h.convertContractShortHandToExpiry(cp)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, cp.Quote.String(), "CQ")
|
||||
tick, err = h.FetchTicker(context.Background(), cp, asset.Futures)
|
||||
assert.NoError(t, err)
|
||||
assert.NotZero(t, tick.Close)
|
||||
|
||||
cp = currency.NewPair(currency.BTC, currency.NewCode("NQ"))
|
||||
cp, err = h.convertContractShortHandToExpiry(cp)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEqual(t, cp.Quote.String(), "NQ")
|
||||
tick, err = h.FetchTicker(context.Background(), cp, asset.Futures)
|
||||
if err != nil {
|
||||
// Huobi doesn't always have a next-quarter contract
|
||||
return
|
||||
}
|
||||
assert.NotZero(t, tick.Close)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/key"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
@@ -80,7 +81,7 @@ func (h *HUOBI) SetDefaults() {
|
||||
Delimiter: currency.DashDelimiter,
|
||||
},
|
||||
}
|
||||
futures := currency.PairStore{
|
||||
futuresFormatting := currency.PairStore{
|
||||
RequestFormat: ¤cy.PairFormat{
|
||||
Uppercase: true,
|
||||
},
|
||||
@@ -97,7 +98,7 @@ func (h *HUOBI) SetDefaults() {
|
||||
if err != nil {
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
}
|
||||
err = h.StoreAssetPairFormat(asset.Futures, futures)
|
||||
err = h.StoreAssetPairFormat(asset.Futures, futuresFormatting)
|
||||
if err != nil {
|
||||
log.Errorln(log.ExchangeSys, err)
|
||||
}
|
||||
@@ -155,6 +156,10 @@ func (h *HUOBI) SetDefaults() {
|
||||
FundingRateBatching: map[asset.Item]bool{
|
||||
asset.CoinMarginedFutures: true,
|
||||
},
|
||||
OpenInterest: exchange.OpenInterestSupport{
|
||||
Supported: true,
|
||||
SupportsRestBatch: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Enabled: exchange.FeaturesEnabled{
|
||||
@@ -2304,3 +2309,159 @@ func (h *HUOBI) IsPerpetualFutureCurrency(a asset.Item, _ currency.Pair) (bool,
|
||||
func (h *HUOBI) UpdateOrderExecutionLimits(_ context.Context, _ asset.Item) error {
|
||||
return common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// GetOpenInterest returns the open interest rate for a given asset pair
|
||||
func (h *HUOBI) GetOpenInterest(ctx context.Context, k ...key.PairAsset) ([]futures.OpenInterest, error) {
|
||||
for i := range k {
|
||||
if k[i].Asset != asset.Futures && k[i].Asset != asset.CoinMarginedFutures {
|
||||
// avoid API calls or returning errors after a successful retrieval
|
||||
return nil, fmt.Errorf("%w %v %v", asset.ErrNotSupported, k[i].Asset, k[i].Pair())
|
||||
}
|
||||
}
|
||||
if len(k) == 1 {
|
||||
switch k[0].Asset {
|
||||
case asset.Futures:
|
||||
_, err := strconv.ParseInt(k[0].Quote.Symbol, 10, 64)
|
||||
if err == nil {
|
||||
// Huobi does not like requests being made with contract expiry in them (eg BTC240109)
|
||||
return nil, fmt.Errorf("%w %v, must use shorthand such as CW (current week)", currency.ErrCurrencyNotSupported, k[0].Pair())
|
||||
}
|
||||
data, err := h.FContractOpenInterest(ctx, "", "", k[0].Pair())
|
||||
if err != nil {
|
||||
data2, err2 := h.ContractOpenInterestUSDT(ctx, k[0].Pair(), currency.EMPTYPAIR, "", "")
|
||||
if err2 != nil {
|
||||
return nil, fmt.Errorf("%w %w", err, err2)
|
||||
}
|
||||
data.Data = data2
|
||||
}
|
||||
|
||||
for i := range data.Data {
|
||||
var p currency.Pair
|
||||
p, err = h.MatchSymbolWithAvailablePairs(data.Data[i].ContractCode, k[0].Asset, true)
|
||||
if err != nil {
|
||||
if errors.Is(err, currency.ErrPairNotFound) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return []futures.OpenInterest{
|
||||
{
|
||||
Key: key.ExchangePairAsset{
|
||||
Exchange: h.Name,
|
||||
Base: p.Base.Item,
|
||||
Quote: p.Quote.Item,
|
||||
Asset: k[0].Asset,
|
||||
},
|
||||
OpenInterest: data.Data[i].Amount,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
case asset.CoinMarginedFutures:
|
||||
data, err := h.SwapOpenInterestInformation(ctx, k[0].Pair())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range data.Data {
|
||||
var p currency.Pair
|
||||
p, err = h.MatchSymbolWithAvailablePairs(data.Data[i].ContractCode, k[0].Asset, true)
|
||||
if err != nil {
|
||||
if errors.Is(err, currency.ErrPairNotFound) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return []futures.OpenInterest{
|
||||
{
|
||||
Key: key.ExchangePairAsset{
|
||||
Exchange: h.Name,
|
||||
Base: p.Base.Item,
|
||||
Quote: p.Quote.Item,
|
||||
Asset: k[0].Asset,
|
||||
},
|
||||
OpenInterest: data.Data[i].Amount,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
var resp []futures.OpenInterest
|
||||
for _, a := range h.GetAssetTypes(true) {
|
||||
switch a {
|
||||
case asset.Futures:
|
||||
data, err := h.FContractOpenInterest(ctx, "", "", currency.EMPTYPAIR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uData, err := h.ContractOpenInterestUSDT(ctx, currency.EMPTYPAIR, currency.EMPTYPAIR, "", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allData := make([]UContractOpenInterest, 0, len(data.Data)+len(uData))
|
||||
allData = append(allData, data.Data...)
|
||||
allData = append(allData, uData...)
|
||||
for i := range allData {
|
||||
var p currency.Pair
|
||||
var isEnabled, appendData bool
|
||||
p, isEnabled, err = h.MatchSymbolCheckEnabled(allData[i].ContractCode, a, true)
|
||||
if err != nil && !errors.Is(err, currency.ErrPairNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
if !isEnabled {
|
||||
continue
|
||||
}
|
||||
for j := range k {
|
||||
if k[j].Pair().Equal(p) {
|
||||
appendData = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(k) > 0 && !appendData {
|
||||
continue
|
||||
}
|
||||
resp = append(resp, futures.OpenInterest{
|
||||
Key: key.ExchangePairAsset{
|
||||
Exchange: h.Name,
|
||||
Base: p.Base.Item,
|
||||
Quote: p.Quote.Item,
|
||||
Asset: a,
|
||||
},
|
||||
OpenInterest: allData[i].Amount,
|
||||
})
|
||||
}
|
||||
case asset.CoinMarginedFutures:
|
||||
data, err := h.SwapOpenInterestInformation(ctx, currency.EMPTYPAIR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range data.Data {
|
||||
p, isEnabled, err := h.MatchSymbolCheckEnabled(data.Data[i].ContractCode, a, true)
|
||||
if err != nil && !errors.Is(err, currency.ErrPairNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
if !isEnabled {
|
||||
continue
|
||||
}
|
||||
var appendData bool
|
||||
for j := range k {
|
||||
if k[j].Pair().Equal(p) {
|
||||
appendData = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(k) > 0 && !appendData {
|
||||
continue
|
||||
}
|
||||
resp = append(resp, futures.OpenInterest{
|
||||
Key: key.ExchangePairAsset{
|
||||
Exchange: h.Name,
|
||||
Base: p.Base.Item,
|
||||
Quote: p.Quote.Item,
|
||||
Asset: a,
|
||||
},
|
||||
OpenInterest: data.Data[i].Amount,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user