From d907aab576a18fbd79704a9d64b5f0d61a40cb2c Mon Sep 17 00:00:00 2001 From: Scott Date: Mon, 22 Jan 2024 15:19:47 +1100 Subject: [PATCH] Huobi: Implement ticker batching (#1439) * Add huobi ticker batching and tests * sneaky funding rate fix, don't look * better processing, better tests * inline structs per shazbaz * formatting expansion * weird * introduce time param, mini fixes * linter splinter * merge fix, kraken fix * move comment, use require, add len --- exchanges/huobi/huobi.go | 106 ++++++++++++++--------- exchanges/huobi/huobi_futures.go | 22 +++-- exchanges/huobi/huobi_test.go | 136 ++++++++++++++++++++--------- exchanges/huobi/huobi_types.go | 41 +++++++-- exchanges/huobi/huobi_wrapper.go | 141 ++++++++++++++++++++++++++++++- exchanges/kraken/kraken_test.go | 2 +- 6 files changed, 349 insertions(+), 99 deletions(-) diff --git a/exchanges/huobi/huobi.go b/exchanges/huobi/huobi.go index bd1f8b3a..d1511cde 100644 --- a/exchanges/huobi/huobi.go +++ b/exchanges/huobi/huobi.go @@ -28,44 +28,47 @@ const ( huobiAPIVersion2 = "2" // Spot endpoints - huobiMarketHistoryKline = "/market/history/kline" - huobiMarketDetail = "/market/detail" - huobiMarketDetailMerged = "/market/detail/merged" - huobi24HrMarketSummary = "/market/detail?" - huobiMarketDepth = "/market/depth" - huobiMarketTrade = "/market/trade" - huobiMarketTickers = "/market/tickers" - huobiMarketTradeHistory = "/market/history/trade" - huobiSymbols = "/v1/common/symbols" - huobiCurrencies = "/v1/common/currencys" - huobiTimestamp = "/common/timestamp" - huobiAccounts = "/account/accounts" - huobiAccountBalance = "/account/accounts/%s/balance" - huobiAccountDepositAddress = "/account/deposit/address" - huobiAccountWithdrawQuota = "/account/withdraw/quota" - huobiAccountQueryWithdrawAddress = "/account/withdraw/" - huobiAggregatedBalance = "/subuser/aggregate-balance" - huobiOrderPlace = "/order/orders/place" - huobiOrderCancel = "/order/orders/%s/submitcancel" - huobiOrderCancelBatch = "/order/orders/batchcancel" - huobiBatchCancelOpenOrders = "/order/orders/batchCancelOpenOrders" - huobiGetOrder = "/order/orders/getClientOrder" - huobiGetOrderMatch = "/order/orders/%s/matchresults" - huobiGetOrders = "/order/orders" - huobiGetOpenOrders = "/order/openOrders" - huobiGetOrdersMatch = "/orders/matchresults" - huobiMarginTransferIn = "/dw/transfer-in/margin" - huobiMarginTransferOut = "/dw/transfer-out/margin" - huobiMarginOrders = "/margin/orders" - huobiMarginRepay = "/margin/orders/%s/repay" - huobiMarginLoanOrders = "/margin/loan-orders" - huobiMarginAccountBalance = "/margin/accounts/balance" - huobiWithdrawCreate = "/dw/withdraw/api/create" - huobiWithdrawCancel = "/dw/withdraw-virtual/%s/cancel" - huobiStatusError = "error" - huobiMarginRates = "/margin/loan-info" - huobiCurrenciesReference = "/v2/reference/currencies" - huobiWithdrawHistory = "/query/deposit-withdraw" + huobiMarketHistoryKline = "/market/history/kline" + huobiMarketDetail = "/market/detail" + huobiMarketDetailMerged = "/market/detail/merged" + huobi24HrMarketSummary = "/market/detail?" + huobiMarketDepth = "/market/depth" + huobiMarketTrade = "/market/trade" + huobiMarketTickers = "/market/tickers" + huobiMarketTradeHistory = "/market/history/trade" + huobiSymbols = "/v1/common/symbols" + huobiCurrencies = "/v1/common/currencys" + huobiTimestamp = "/common/timestamp" + huobiAccounts = "/account/accounts" + huobiAccountBalance = "/account/accounts/%s/balance" + huobiAccountDepositAddress = "/account/deposit/address" + huobiAccountWithdrawQuota = "/account/withdraw/quota" + huobiAccountQueryWithdrawAddress = "/account/withdraw/" + huobiAggregatedBalance = "/subuser/aggregate-balance" + huobiOrderPlace = "/order/orders/place" + huobiOrderCancel = "/order/orders/%s/submitcancel" + huobiOrderCancelBatch = "/order/orders/batchcancel" + huobiBatchCancelOpenOrders = "/order/orders/batchCancelOpenOrders" + huobiGetOrder = "/order/orders/getClientOrder" + huobiGetOrderMatch = "/order/orders/%s/matchresults" + huobiGetOrders = "/order/orders" + huobiGetOpenOrders = "/order/openOrders" + huobiGetOrdersMatch = "/orders/matchresults" + huobiMarginTransferIn = "/dw/transfer-in/margin" + huobiMarginTransferOut = "/dw/transfer-out/margin" + huobiMarginOrders = "/margin/orders" + huobiMarginRepay = "/margin/orders/%s/repay" + huobiMarginLoanOrders = "/margin/loan-orders" + huobiMarginAccountBalance = "/margin/accounts/balance" + huobiWithdrawCreate = "/dw/withdraw/api/create" + huobiWithdrawCancel = "/dw/withdraw-virtual/%s/cancel" + huobiStatusError = "error" + huobiMarginRates = "/margin/loan-info" + huobiCurrenciesReference = "/v2/reference/currencies" + huobiWithdrawHistory = "/query/deposit-withdraw" + huobiBatchCoinMarginSwapContracts = "/v2/swap-ex/market/detail/batch_merged" + huobiBatchLinearSwapContracts = "/linear-swap-ex/market/detail/batch_merged" + huobiBatchContracts = "/v2/market/detail/batch_merged" ) // HUOBI is the overarching type across this package @@ -129,6 +132,33 @@ func (h *HUOBI) Get24HrMarketSummary(ctx context.Context, symbol currency.Pair) return result, h.SendHTTPRequest(ctx, exchange.RestSpot, huobi24HrMarketSummary+params.Encode(), &result) } +// GetBatchCoinMarginSwapContracts returns the tickers for coin margined swap contracts +func (h *HUOBI) GetBatchCoinMarginSwapContracts(ctx context.Context) ([]FuturesBatchTicker, error) { + var result struct { + Data []FuturesBatchTicker `json:"ticks"` + } + err := h.SendHTTPRequest(ctx, exchange.RestFutures, huobiBatchCoinMarginSwapContracts, &result) + return result.Data, err +} + +// GetBatchLinearSwapContracts returns the tickers for linear swap contracts +func (h *HUOBI) GetBatchLinearSwapContracts(ctx context.Context) ([]FuturesBatchTicker, error) { + var result struct { + Data []FuturesBatchTicker `json:"ticks"` + } + err := h.SendHTTPRequest(ctx, exchange.RestFutures, huobiBatchLinearSwapContracts, &result) + return result.Data, err +} + +// GetBatchFuturesContracts returns the tickers for futures contracts +func (h *HUOBI) GetBatchFuturesContracts(ctx context.Context) ([]FuturesBatchTicker, error) { + var result struct { + Data []FuturesBatchTicker `json:"ticks"` + } + err := h.SendHTTPRequest(ctx, exchange.RestFutures, huobiBatchContracts, &result) + return result.Data, err +} + // GetTickers returns the ticker for the specified symbol func (h *HUOBI) GetTickers(ctx context.Context) (Tickers, error) { var result Tickers diff --git a/exchanges/huobi/huobi_futures.go b/exchanges/huobi/huobi_futures.go index 388336db..a1f1ee18 100644 --- a/exchanges/huobi/huobi_futures.go +++ b/exchanges/huobi/huobi_futures.go @@ -81,6 +81,8 @@ const ( fContractDateFormat = "060102" ) +var errInvalidContractType = errors.New("invalid contract type") + // FGetContractInfo gets contract info for futures func (h *HUOBI) FGetContractInfo(ctx context.Context, symbol, contractType string, code currency.Pair) (FContractInfoData, error) { var resp FContractInfoData @@ -191,11 +193,11 @@ func (h *HUOBI) FContractOpenInterest(ctx context.Context, symbol, contractType params.Set("contract_type", contractType) } if !code.IsEmpty() { - codeValue, err := h.convertContractShortHandToExpiry(code) + codeValue, err := h.formatFuturesPair(code, true) if err != nil { return resp, err } - params.Set("contract_code", codeValue.String()) + params.Set("contract_code", codeValue) } path := common.EncodeURLValues(fContractOpenInterest, params) return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp) @@ -374,7 +376,7 @@ func (h *HUOBI) FQueryHisOpenInterest(ctx context.Context, symbol, contractType, params.Set("symbol", symbol) } if !common.StringDataCompareInsensitive(validContractTypes, contractType) { - return resp, fmt.Errorf("invalid contract type") + return resp, fmt.Errorf("%w %v", errInvalidContractType, contractType) } params.Set("contract_type", contractType) if !common.StringDataCompareInsensitive(validPeriods, period) { @@ -1256,7 +1258,7 @@ func (h *HUOBI) formatFuturesCode(p currency.Code) (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) + cp, err := h.convertContractShortHandToExpiry(p, time.Now()) if err != nil { return "", err } @@ -1273,12 +1275,12 @@ func (h *HUOBI) formatFuturesPair(p currency.Pair, convertQuoteToExpiry bool) (s // 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) +func (h *HUOBI) convertContractShortHandToExpiry(pair currency.Pair, tt time.Time) (currency.Pair, error) { + loc, err := time.LoadLocation("Asia/Singapore") + if err != nil { + return currency.EMPTYPAIR, err } - - tt := time.Now() + tt = tt.In(loc) switch pair.Quote.Item.Symbol { case "NW": tt = tt.AddDate(0, 0, 7) @@ -1304,6 +1306,8 @@ func (h *HUOBI) convertContractShortHandToExpiry(pair currency.Pair) (currency.P for tt.Weekday() != time.Friday { tt = tt.AddDate(0, 0, -1) } + default: + return currency.EMPTYPAIR, fmt.Errorf(" %w %v", errInvalidContractType, pair) } pair.Quote = currency.NewCode(tt.Format(fContractDateFormat)) return pair, nil diff --git a/exchanges/huobi/huobi_test.go b/exchanges/huobi/huobi_test.go index 447e21e2..409e32d2 100644 --- a/exchanges/huobi/huobi_test.go +++ b/exchanges/huobi/huobi_test.go @@ -13,6 +13,7 @@ import ( "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/thrasher-corp/gocryptotrader/common" "github.com/thrasher-corp/gocryptotrader/common/key" "github.com/thrasher-corp/gocryptotrader/config" @@ -26,6 +27,7 @@ import ( "github.com/thrasher-corp/gocryptotrader/exchanges/order" "github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues" "github.com/thrasher-corp/gocryptotrader/exchanges/stream" + "github.com/thrasher-corp/gocryptotrader/exchanges/ticker" "github.com/thrasher-corp/gocryptotrader/portfolio/withdraw" ) @@ -2686,7 +2688,6 @@ func TestGetAvailableTransferChains(t *testing.T) { t.Error("expected more than one result") } } - func TestFormatFuturesPair(t *testing.T) { r, err := h.formatFuturesPair(futuresTestPair, false) if err != nil { @@ -2858,6 +2859,101 @@ func TestGetSwapFundingRates(t *testing.T) { } } +func TestGetBatchCoinMarginSwapContracts(t *testing.T) { + t.Parallel() + resp, err := h.GetBatchCoinMarginSwapContracts(context.Background()) + assert.NoError(t, err) + assert.NotEmpty(t, resp) +} + +func TestGetBatchLinearSwapContracts(t *testing.T) { + t.Parallel() + resp, err := h.GetBatchLinearSwapContracts(context.Background()) + assert.NoError(t, err) + assert.NotEmpty(t, resp) +} + +func TestGetBatchFuturesContracts(t *testing.T) { + t.Parallel() + resp, err := h.GetBatchFuturesContracts(context.Background()) + assert.NoError(t, err) + assert.NotEmpty(t, resp) +} + +func TestUpdateTickers(t *testing.T) { + t.Parallel() + for _, a := range h.GetAssetTypes(false) { + err := h.UpdateTickers(context.Background(), a) + assert.NoErrorf(t, err, "asset %s", a) + + avail, err := h.GetAvailablePairs(a) + require.NoError(t, err) + for x := range avail { + _, err = ticker.GetTicker(h.Name, avail[x], a) + assert.NoError(t, err) + } + } +} + +func TestConvertContractShortHandToExpiry(t *testing.T) { + t.Parallel() + tt := time.Now() + cp := currency.NewPair(currency.BTC, currency.NewCode("CW")) + cp, err := h.convertContractShortHandToExpiry(cp, tt) + assert.NoError(t, err) + assert.NotEqual(t, cp.Quote.String(), "CW") + tick, err := h.FetchTicker(context.Background(), cp, asset.Futures) + if assert.NoError(t, err) { + assert.NotZero(t, tick.Close) + } + + cp = currency.NewPair(currency.BTC, currency.NewCode("NW")) + cp, err = h.convertContractShortHandToExpiry(cp, tt) + assert.NoError(t, err) + assert.NotEqual(t, cp.Quote.String(), "NW") + tick, err = h.FetchTicker(context.Background(), cp, asset.Futures) + if assert.NoError(t, err) { + assert.NotZero(t, tick.Close) + } + + cp = currency.NewPair(currency.BTC, currency.NewCode("CQ")) + cp, err = h.convertContractShortHandToExpiry(cp, tt) + assert.NoError(t, err) + assert.NotEqual(t, cp.Quote.String(), "CQ") + tick, err = h.FetchTicker(context.Background(), cp, asset.Futures) + if assert.NoError(t, err) { + assert.NotZero(t, tick.Close) + } + + // calculate a specific date + cp = currency.NewPair(currency.BTC, currency.NewCode("CQ")) + tt = time.Date(2021, 6, 3, 0, 0, 0, 0, time.UTC) + cp, err = h.convertContractShortHandToExpiry(cp, tt) + assert.NoError(t, err) + assert.Equal(t, cp.Quote.String(), "210625") + + cp = currency.NewPair(currency.BTC, currency.NewCode("CW")) + cp, err = h.convertContractShortHandToExpiry(cp, tt) + assert.NoError(t, err) + assert.Equal(t, cp.Quote.String(), "210604") + + cp = currency.NewPair(currency.BTC, currency.NewCode("CWif hat")) + _, err = h.convertContractShortHandToExpiry(cp, tt) + assert.ErrorIs(t, err, errInvalidContractType) + + tt = time.Now() + cp = currency.NewPair(currency.BTC, currency.NewCode("NQ")) + cp, err = h.convertContractShortHandToExpiry(cp, tt) + 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 if no data found + return + } + assert.NotZero(t, tick.Close) +} + func TestGetOpenInterest(t *testing.T) { t.Parallel() _, err := h.GetOpenInterest(context.Background(), key.PairAsset{ @@ -2911,41 +3007,3 @@ func TestContractOpenInterestUSDT(t *testing.T) { 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) -} diff --git a/exchanges/huobi/huobi_types.go b/exchanges/huobi/huobi_types.go index aa3259c1..e59ff232 100644 --- a/exchanges/huobi/huobi_types.go +++ b/exchanges/huobi/huobi_types.go @@ -3,6 +3,7 @@ package huobi import ( "github.com/thrasher-corp/gocryptotrader/currency" "github.com/thrasher-corp/gocryptotrader/exchanges/order" + "github.com/thrasher-corp/gocryptotrader/types" ) type errorCapture struct { @@ -520,16 +521,40 @@ type Tickers struct { Data []Ticker `json:"data"` } +// FuturesBatchTicker holds ticker data +type FuturesBatchTicker struct { + ID float64 `json:"id"` + Timestamp int64 `json:"ts"` + Ask [2]float64 `json:"ask"` + Bid [2]float64 `json:"bid"` + BusinessType string `json:"business_type"` + ContractCode string `json:"contract_code"` + Open types.Number `json:"open"` + Close types.Number `json:"close"` + Low types.Number `json:"low"` + High types.Number `json:"high"` + Amount types.Number `json:"amount"` + Count float64 `json:"count"` + Volume types.Number `json:"vol"` + TradeTurnover types.Number `json:"trade_turnover"` + TradePartition string `json:"trade_partition"` + Symbol string `json:"symbol"` // If ContractCode is empty, Symbol is populated +} + // Ticker latest ticker data type Ticker struct { - Amount float64 `json:"amount"` - Close float64 `json:"close"` - Count int64 `json:"count"` - High float64 `json:"high"` - Low float64 `json:"low"` - Open float64 `json:"open"` - Symbol string `json:"symbol"` - Volume float64 `json:"vol"` + Symbol string `json:"symbol"` + Open float64 `json:"open"` + High float64 `json:"high"` + Low float64 `json:"low"` + Close float64 `json:"close"` + Amount float64 `json:"amount"` + Volume float64 `json:"vol"` + Count float64 `json:"count"` + Bid float64 `json:"bid"` + BidSize float64 `json:"bidSize"` + Ask float64 `json:"ask"` + AskSize float64 `json:"askSize"` } // OrderBookDataRequestParamsType var for request param types diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index 58368beb..283e8b23 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -109,6 +109,7 @@ func (h *HUOBI) SetDefaults() { Websocket: true, RESTCapabilities: protocol.Features{ TickerFetching: true, + TickerBatching: true, KlineFetching: true, TradeFetching: true, OrderbookFetching: true, @@ -141,7 +142,6 @@ func (h *HUOBI) SetDefaults() { GetOrders: true, TickerFetching: true, FundingRateFetching: false, // supported but not implemented // TODO when multi-websocket support added - }, WithdrawPermissions: exchange.AutoWithdrawCryptoWithSetup | exchange.NoFiatWithdrawals, @@ -445,8 +445,141 @@ func (h *HUOBI) UpdateTradablePairs(ctx context.Context, forceUpdate bool) error } // UpdateTickers updates the ticker for all currency pairs of a given asset type -func (h *HUOBI) UpdateTickers(_ context.Context, _ asset.Item) error { - return common.ErrFunctionNotSupported +func (h *HUOBI) UpdateTickers(ctx context.Context, a asset.Item) error { + switch a { + case asset.Spot: + ticks, err := h.GetTickers(ctx) + if err != nil { + return err + } + for i := range ticks.Data { + var cp currency.Pair + cp, _, err = h.MatchSymbolCheckEnabled(ticks.Data[i].Symbol, a, false) + if err != nil { + if errors.Is(err, currency.ErrPairNotFound) { + continue + } + return err + } + err = ticker.ProcessTicker(&ticker.Price{ + High: ticks.Data[i].High, + Low: ticks.Data[i].Low, + Bid: ticks.Data[i].Bid, + Ask: ticks.Data[i].Ask, + Volume: ticks.Data[i].Volume, + QuoteVolume: ticks.Data[i].Amount, + Open: ticks.Data[i].Open, + Close: ticks.Data[i].Close, + BidSize: ticks.Data[i].BidSize, + AskSize: ticks.Data[i].AskSize, + Pair: cp, + ExchangeName: h.Name, + AssetType: a, + LastUpdated: time.Now(), + }) + if err != nil { + return err + } + } + case asset.CoinMarginedFutures: + ticks, err := h.GetBatchCoinMarginSwapContracts(ctx) + if err != nil { + return err + } + for i := range ticks { + var cp currency.Pair + cp, _, err = h.MatchSymbolCheckEnabled(ticks[i].ContractCode, a, true) + if err != nil { + if errors.Is(err, currency.ErrPairNotFound) { + continue + } + return err + } + tt := time.UnixMilli(ticks[i].Timestamp) + err = ticker.ProcessTicker(&ticker.Price{ + High: ticks[i].High.Float64(), + Low: ticks[i].Low.Float64(), + Volume: ticks[i].Volume.Float64(), + QuoteVolume: ticks[i].Amount.Float64(), + Open: ticks[i].Open.Float64(), + Close: ticks[i].Close.Float64(), + Bid: ticks[i].Bid[0], + BidSize: ticks[i].Bid[1], + Ask: ticks[i].Ask[0], + AskSize: ticks[i].Ask[1], + Pair: cp, + ExchangeName: h.Name, + AssetType: a, + LastUpdated: tt, + }) + if err != nil { + return err + } + } + case asset.Futures: + linearTicks, err := h.GetBatchLinearSwapContracts(ctx) + if err != nil { + return err + } + ticks, err := h.GetBatchFuturesContracts(ctx) + if err != nil { + return err + } + allTicks := make([]FuturesBatchTicker, 0, len(linearTicks)+len(ticks)) + allTicks = append(allTicks, linearTicks...) + allTicks = append(allTicks, ticks...) + for i := range allTicks { + var cp currency.Pair + if allTicks[i].Symbol != "" { + cp, err = currency.NewPairFromString(allTicks[i].Symbol) + if err != nil { + return err + } + cp, err = h.convertContractShortHandToExpiry(cp, time.Now()) + if err != nil { + return err + } + cp, _, err = h.MatchSymbolCheckEnabled(cp.String(), a, true) + if err != nil { + if errors.Is(err, currency.ErrPairNotFound) { + continue + } + return err + } + } else { + cp, _, err = h.MatchSymbolCheckEnabled(allTicks[i].ContractCode, a, true) + if err != nil { + if errors.Is(err, currency.ErrPairNotFound) { + continue + } + return err + } + } + tt := time.UnixMilli(allTicks[i].Timestamp) + err = ticker.ProcessTicker(&ticker.Price{ + High: allTicks[i].High.Float64(), + Low: allTicks[i].Low.Float64(), + Volume: allTicks[i].Volume.Float64(), + QuoteVolume: allTicks[i].Amount.Float64(), + Open: allTicks[i].Open.Float64(), + Close: allTicks[i].Close.Float64(), + Bid: allTicks[i].Bid[0], + BidSize: allTicks[i].Bid[1], + Ask: allTicks[i].Ask[0], + AskSize: allTicks[i].Ask[1], + Pair: cp, + ExchangeName: h.Name, + AssetType: a, + LastUpdated: tt, + }) + if err != nil { + return err + } + } + default: + return fmt.Errorf("%w %v", asset.ErrNotSupported, a) + } + return nil } // UpdateTicker updates and returns the ticker for a currency pair @@ -2281,7 +2414,7 @@ func (h *HUOBI) GetLatestFundingRates(ctx context.Context, r *fundingrate.Latest rate := fundingrate.LatestRateResponse{ Exchange: h.Name, Asset: r.Asset, - Pair: r.Pair, + Pair: cp, LatestRate: fundingrate.Rate{ Time: ft, Rate: decimal.NewFromFloat(rates[i].FundingRate), diff --git a/exchanges/kraken/kraken_test.go b/exchanges/kraken/kraken_test.go index 0847413f..61a99259 100644 --- a/exchanges/kraken/kraken_test.go +++ b/exchanges/kraken/kraken_test.go @@ -2298,7 +2298,7 @@ func TestGetOpenInterest(t *testing.T) { assert.NoError(t, err) assert.NotEmpty(t, resp) - _, err = k.GetOpenInterest(context.Background()) + resp, err = k.GetOpenInterest(context.Background()) assert.NoError(t, err) assert.NotEmpty(t, resp) }