Files
gocryptotrader/exchanges/huobi/huobi_futures.go
Adam 504c2fad6d Feature: Implement funding rates, futures and coin margin (exchange API coverage) (#530)
* ALMOST THERE

* more api wips

* more api thingz

* testing n more api wipz

* more apiz

* more wips

* what is goin on

* more wips

* whip n testing

* testing

* testing

no keys

* remove log

* kraken is broken

ugh

* still broken

* fixing auth funcs + usdtm api docs

* wip

* api stuffs

* whip

* more wips

* whip

* more wip

* api wip n testing

* wip

* wip

* unsaved

* wip n testing

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* whip

* wrapper authenticated functions

* adding asset type and fixing dependencies

* wip

* binance auth wrapper start

* wrapper functionality

* wip

* wip

* wip

* wrapper cancel functions

* order submission for wrappers

* wip

* more error fixing and nits

* websocket beginning n error fix

* wip

* WOW

* glorious n shazzy nits

* useless nits

* wip

* fixing things

* merge stuffs

* crapveyor

* crapveyor rebuild

* probably broke more things than he fixed

* rm lns n other thangs

* hope

* please

* stop it

* done

* ofcourse

* rm vb

* fix lbank

* appveyor please

* float lev

* DONT ASK RYAN FOR HELP EVER

* wip

* wip

* endpoint upgrades continued

* path upgrade

* NeeeNeeeNeeeNeeeNING

* fix stuffs

* fixing time issue

* fixing broken funcs

* glorious nits

* shaz changes

* fixing errors for fundmon

* more error fixing for fundmon

* test running past 30s

* basic changes

* THX AGAIN SHAZBERT

* path system upgrade

* config upgrade

* unsaved stuffs

* broken wip config upgrade

* path system upgrade contd.

* path system upgrade contd

* path upgrade ready for review

* testing verbose removed

* linter stuffs

* appveyor stuffs

* appveyor stuff

* fixed?

* bugfix

* wip

* broken stuff

* fix test

* wierd hack fix

* appveyor pls stop

* error found

* more useless nits

* bitmex err

* broken wip

* broken wip path upgrade change to uint32

* changed url lookups to uint

* WOW

* ready4review

* config fixed HOPEFULLY

* config fix and glorious changes

* efficient way of getting orders and open orders

* binance wrapper logic fixing

* testing, adding tests and fixing lot of errrrrs

* merge master

* appveyor stuffs

* appveyor stuffs

* fmt

* test

* octalLiteral issue fix?

* octalLiteral fix?

* rm vb

* prnt ln to restart

* adding testz

* test fixzzz

* READY FOR REVIEW

* Actually ready now

* FORMATTING

* addressing shazzy n glorious nits

* crapveyor

* rm vb

* small change

* fixing err

* shazbert nits

* review changes

* requested changes

* more requested changes

* noo

* last nit fixes

* restart appveyor

* improving test cov

* Update .golangci.yml

* shazbert changes

* moving pair formatting

* format pair update wip

* path upgrade complete

* error fix

* appveyor linters

* more linters

* remove testexch

* more formatting changes

* changes

* shazbert changes

* checking older requested changes to ensure completion

* wip

* fixing broken code

* error fix

* all fixed

* additional changes

* more changes

* remove commented code

* ftx margin api

* appveyor fixes

* more appveyor issues + test addition

* more appveyor issues + test addition

* remove unnecessary

* testing

* testing, fixing okex api, error fix

* git merge fix

* go sum

* glorious changes and error fix

* rm vb

* more glorious changes and go mod tidy

* fixed now

* okex testing upgrade

* old config migration and batch fetching fix

* added test

* glorious requested changes WIP

* tested and fixed

* go fmted

* go fmt and test fix

* additional funcs and tests for fundingRates

* OKEX tested and fixed

* appveyor fixes

* ineff assign

* 1 glorious change

* error fix

* typo

* shazbert changes

* glorious code changes and path fixing huobi WIP

* adding assetType to accountinfo functions

* fixing panic

* panic fix and updating account info wrappers WIP

* updateaccountinfo updated

* testing WIP binance USDT n Coin Margined and Kraken Futures

* auth functions tested and fixed

* added test

* config reverted

* shazbert and glorious changes

* shazbert and glorious changes

* latest changes and portfolio update

* go fmt change:

* remove commented codes

* improved error checking

* index out of range fix

* rm ln

* critical nit

* glorious latest changes

* appveyor changes

* shazbert change

* easier readability

* latest glorious changes

* shadow dec

* assetstore updated

* last change

* another last change

* merge changes

* go mod tidy

* thrasher requested changes wip

* improving struct layouts

* appveyor go fmt

* remove unnecessary code

* shazbert changes

* small change

* oopsie

* tidy

* configtest reverted

* error fix

* oopsie

* for what

* test patch fix

* insecurities

* fixing tests

* fix config
2021-02-12 16:19:18 +11:00

1183 lines
41 KiB
Go

package huobi
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"time"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/common/crypto"
"github.com/thrasher-corp/gocryptotrader/currency"
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
)
const (
// Unauth
fContractInfo = "api/v1/contract_contract_info?"
fContractIndexPrice = "api/v1/contract_index?"
fContractPriceLimitation = "api/v1/contract_price_limit?"
fContractOpenInterest = "api/v1/contract_open_interest?"
fEstimatedDeliveryPrice = "api/v1/contract_delivery_price?"
fContractMarketDepth = "/market/depth?"
fContractKline = "/market/history/kline?"
fMarketOverview = "/market/detail/merged?"
fLastTradeContract = "/market/trade?"
fContractBatchTradeRecords = "/market/history/trade?"
fInsuranceAndClawback = "api/v1/contract_risk_info?"
fInsuranceBalanceHistory = "api/v1/contract_insurance_fund?"
fTieredAdjustmentFactor = "api/v1/contract_adjustfactor?"
fHisContractOpenInterest = "api/v1/contract_his_open_interest?"
fSystemStatus = "api/v1/contract_api_state?"
fTopAccountsSentiment = "api/v1/contract_elite_account_ratio?"
fTopPositionsSentiment = "api/v1/contract_elite_position_ratio?"
fLiquidationOrders = "api/v1/contract_liquidation_orders?"
fIndexKline = "/index/market/history/index?"
fBasisData = "/index/market/history/basis?"
// Auth
fAccountData = "api/v1/contract_account_info"
fPositionInformation = "api/v1/contract_position_info"
fAllSubAccountAssets = "api/v1/contract_sub_account_list"
fSingleSubAccountAssets = "api/v1/contract_sub_account_info"
fSingleSubAccountPositions = "api/v1/contract_sub_position_info"
fFinancialRecords = "api/v1/contract_financial_record"
fSettlementRecords = "api/v1/contract_user_settlement_records"
fOrderLimitInfo = "api/v1/contract_order_limit"
fContractTradingFee = "api/v1/contract_fee"
fTransferLimitInfo = "api/v1/contract_transfer_limit"
fPositionLimitInfo = "api/v1/contract_position_limit"
fQueryAssetsAndPositions = "api/v1/contract_account_position_info"
fTransfer = "api/v1/contract_master_sub_transfer"
fTransferRecords = "api/v1/contract_master_sub_transfer_record"
fAvailableLeverage = "api/v1/contract_available_level_rate"
fOrder = "api/v1/contract_order"
fBatchOrder = "api/v1/contract_batchorder"
fCancelOrder = "api/v1/contract_cancel"
fCancelAllOrders = "api/v1/contract_cancelall"
fFlashCloseOrder = "api/v1/lightning_close_position"
fOrderInfo = "api/v1/contract_order_info"
fOrderDetails = "api/v1/contract_order_detail"
fQueryOpenOrders = "api/v1/contract_openorders"
fOrderHistory = "api/v1/contract_hisorders"
fMatchResult = "api/v1/contract_matchresults"
fTriggerOrder = "api/v1/contract_trigger_order"
fCancelTriggerOrder = "api/v1/contract_trigger_cancel"
fCancelAllTriggerOrders = "api/v1/contract_trigger_cancelall"
fTriggerOpenOrders = "api/v1/contract_trigger_openorders"
fTriggerOrderHistory = "api/v1/contract_trigger_hisorders"
)
// FGetContractInfo gets contract info for futures
func (h *HUOBI) FGetContractInfo(symbol, contractType string, code currency.Pair) (FContractInfoData, error) {
var resp FContractInfoData
params := url.Values{}
if symbol != "" {
params.Set("symbol", symbol)
}
if contractType != "" {
if !common.StringDataCompare(validContractTypes, contractType) {
return resp, fmt.Errorf("invalid contractType")
}
params.Set("contract_type", contractType)
}
if code != (currency.Pair{}) {
codeValue, err := h.FormatSymbol(code, asset.Futures)
if err != nil {
return resp, err
}
params.Set("contract_code", codeValue)
}
path := fContractInfo + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FIndexPriceInfo gets index price info for a futures contract
func (h *HUOBI) FIndexPriceInfo(symbol currency.Code) (FContractIndexPriceInfo, error) {
var resp FContractIndexPriceInfo
params := url.Values{}
if symbol != (currency.Code{}) {
codeValue, err := h.formatFuturesCode(symbol)
if err != nil {
return resp, err
}
params.Set("symbol", codeValue)
}
path := fContractIndexPrice + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FContractPriceLimitations gets price limits for a futures contract
func (h *HUOBI) FContractPriceLimitations(symbol, contractType string, code currency.Pair) (FContractIndexPriceInfo, error) {
var resp FContractIndexPriceInfo
params := url.Values{}
if symbol != "" {
params.Set("symbol", symbol)
}
if contractType != "" {
if !common.StringDataCompare(validContractTypes, contractType) {
return resp, fmt.Errorf("invalid contractType: %s", contractType)
}
params.Set("contract_type", contractType)
}
if code != (currency.Pair{}) {
codeValue, err := h.FormatSymbol(code, asset.Futures)
if err != nil {
return resp, err
}
params.Set("contract_code", codeValue)
}
path := fContractPriceLimitation + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FContractOpenInterest gets open interest data for futures contracts
func (h *HUOBI) FContractOpenInterest(symbol, contractType string, code currency.Pair) (FContractOIData, error) {
var resp FContractOIData
params := url.Values{}
if symbol != "" {
params.Set("symbol", symbol)
}
if contractType != "" {
if !common.StringDataCompare(validContractTypes, contractType) {
return resp, fmt.Errorf("invalid contractType")
}
params.Set("contract_type", contractType)
}
if code != (currency.Pair{}) {
codeValue, err := h.FormatSymbol(code, asset.Futures)
if err != nil {
return resp, err
}
params.Set("contract_code", codeValue)
}
path := fContractOpenInterest + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FGetEstimatedDeliveryPrice gets estimated delivery price info for futures
func (h *HUOBI) FGetEstimatedDeliveryPrice(symbol currency.Code) (FEstimatedDeliveryPriceInfo, error) {
var resp FEstimatedDeliveryPriceInfo
params := url.Values{}
codeValue, err := h.formatFuturesCode(symbol)
if err != nil {
return resp, err
}
params.Set("symbol", codeValue)
path := fEstimatedDeliveryPrice + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FGetMarketDepth gets market depth data for futures contracts
func (h *HUOBI) FGetMarketDepth(symbol currency.Pair, dataType string) (OBData, error) {
var resp OBData
var tempData FMarketDepth
params := url.Values{}
symbolValue, err := h.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
params.Set("type", dataType)
path := fContractMarketDepth + params.Encode()
err = h.SendHTTPRequest(exchange.RestFutures, path, &tempData)
if err != nil {
return resp, err
}
resp.Symbol = symbolValue
for x := range tempData.Tick.Asks {
resp.Asks = append(resp.Asks, obItem{
Price: tempData.Tick.Asks[x][0],
Quantity: tempData.Tick.Asks[x][1],
})
}
for y := range tempData.Tick.Bids {
resp.Bids = append(resp.Bids, obItem{
Price: tempData.Tick.Bids[y][0],
Quantity: tempData.Tick.Bids[y][1],
})
}
return resp, nil
}
// FGetKlineData gets kline data for futures
func (h *HUOBI) FGetKlineData(symbol currency.Pair, period string, size int64, startTime, endTime time.Time) (FKlineData, error) {
var resp FKlineData
params := url.Values{}
symbolValue, err := h.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
if !common.StringDataCompare(validFuturesPeriods, period) {
return resp, fmt.Errorf("invalid period value received")
}
params.Set("period", period)
if size <= 0 || size > 1200 {
return resp, fmt.Errorf("invalid size provided values from 1-1200 supported")
}
params.Set("size", strconv.FormatInt(size, 10))
if !startTime.IsZero() && !endTime.IsZero() {
if startTime.After(endTime) {
return resp, errors.New("startTime cannot be after endTime")
}
params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
}
path := fContractKline + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FGetMarketOverviewData gets market overview data for futures
func (h *HUOBI) FGetMarketOverviewData(symbol currency.Pair) (FMarketOverviewData, error) {
var resp FMarketOverviewData
params := url.Values{}
symbolValue, err := h.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
path := fMarketOverview + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FLastTradeData gets last trade data for a futures contract
func (h *HUOBI) FLastTradeData(symbol currency.Pair) (FLastTradeData, error) {
var resp FLastTradeData
params := url.Values{}
symbolValue, err := h.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
path := fLastTradeContract + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FRequestPublicBatchTrades gets public batch trades for a futures contract
func (h *HUOBI) FRequestPublicBatchTrades(symbol currency.Pair, size int64) (FBatchTradesForContractData, error) {
var resp FBatchTradesForContractData
params := url.Values{}
symbolValue, err := h.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
if size > 1 && size < 2000 {
params.Set("size", strconv.FormatInt(size, 10))
}
path := fContractBatchTradeRecords + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FQueryInsuranceAndClawbackData gets insurance and clawback data for a futures contract
func (h *HUOBI) FQueryInsuranceAndClawbackData(symbol currency.Code) (FClawbackRateAndInsuranceData, error) {
var resp FClawbackRateAndInsuranceData
params := url.Values{}
if symbol != (currency.Code{}) {
codeValue, err := h.formatFuturesCode(symbol)
if err != nil {
return resp, err
}
params.Set("symbol", codeValue)
}
path := fInsuranceAndClawback + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FQueryHistoricalInsuranceData gets insurance data
func (h *HUOBI) FQueryHistoricalInsuranceData(symbol currency.Code) (FHistoricalInsuranceRecordsData, error) {
var resp FHistoricalInsuranceRecordsData
params := url.Values{}
if symbol != (currency.Code{}) {
codeValue, err := h.formatFuturesCode(symbol)
if err != nil {
return resp, err
}
params.Set("symbol", codeValue)
}
path := fInsuranceBalanceHistory + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FQueryTieredAdjustmentFactor gets tiered adjustment factor for futures contracts
func (h *HUOBI) FQueryTieredAdjustmentFactor(symbol currency.Code) (FTieredAdjustmentFactorInfo, error) {
var resp FTieredAdjustmentFactorInfo
params := url.Values{}
if symbol != (currency.Code{}) {
codeValue, err := h.formatFuturesCode(symbol)
if err != nil {
return resp, err
}
params.Set("symbol", codeValue)
}
path := fTieredAdjustmentFactor + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FQueryHisOpenInterest gets open interest for futures contract
func (h *HUOBI) FQueryHisOpenInterest(symbol, contractType, period, amountType string, size int64) (FOIData, error) {
var resp FOIData
params := url.Values{}
if symbol != "" {
params.Set("symbol", symbol)
}
if !common.StringDataCompare(validContractTypes, contractType) {
return resp, fmt.Errorf("invalid contract type")
}
params.Set("contract_type", contractType)
if !common.StringDataCompare(validPeriods, period) {
return resp, fmt.Errorf("invalid period")
}
params.Set("period", period)
if size > 0 || size <= 200 {
params.Set("size", strconv.FormatInt(size, 10))
}
validAmount, ok := validAmountType[amountType]
if !ok {
return resp, fmt.Errorf("invalid amountType")
}
params.Set("amount_type", strconv.FormatInt(validAmount, 10))
path := fHisContractOpenInterest + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FQuerySystemStatus gets system status data
func (h *HUOBI) FQuerySystemStatus(symbol currency.Code) (FContractOIData, error) {
var resp FContractOIData
params := url.Values{}
if symbol != (currency.Code{}) {
codeValue, err := h.formatFuturesCode(symbol)
if err != nil {
return resp, err
}
params.Set("symbol", codeValue)
}
path := fSystemStatus + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FQueryTopAccountsRatio gets top accounts' ratio
func (h *HUOBI) FQueryTopAccountsRatio(symbol, period string) (FTopAccountsLongShortRatio, error) {
var resp FTopAccountsLongShortRatio
params := url.Values{}
if symbol != "" {
params.Set("symbol", symbol)
}
if !common.StringDataCompare(validPeriods, period) {
return resp, fmt.Errorf("invalid period")
}
params.Set("period", period)
path := fTopAccountsSentiment + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FQueryTopPositionsRatio gets top positions' long/short ratio for futures
func (h *HUOBI) FQueryTopPositionsRatio(symbol, period string) (FTopPositionsLongShortRatio, error) {
var resp FTopPositionsLongShortRatio
params := url.Values{}
if symbol != "" {
params.Set("symbol", symbol)
}
if !common.StringDataCompare(validPeriods, period) {
return resp, fmt.Errorf("invalid period")
}
params.Set("period", period)
path := fTopPositionsSentiment + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FLiquidationOrders gets liquidation orders for futures contracts
func (h *HUOBI) FLiquidationOrders(symbol, tradeType string, pageIndex, pageSize, createDate int64) (FLiquidationOrdersInfo, error) {
var resp FLiquidationOrdersInfo
params := url.Values{}
params.Set("symbol", symbol)
if createDate != 7 && createDate != 90 {
return resp, fmt.Errorf("invalid createDate. 7 and 90 are the only supported values")
}
params.Set("create_date", strconv.FormatInt(createDate, 10))
tType, ok := validTradeTypes[tradeType]
if !ok {
return resp, fmt.Errorf("invalid trade type")
}
params.Set("trade_type", strconv.FormatInt(tType, 10))
if pageIndex != 0 {
params.Set("page_index", strconv.FormatInt(pageIndex, 10))
}
if pageSize != 0 {
params.Set("page_size", strconv.FormatInt(pageIndex, 10))
}
path := fLiquidationOrders + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FIndexKline gets index kline data for futures contracts
func (h *HUOBI) FIndexKline(symbol currency.Pair, period string, size int64) (FIndexKlineData, error) {
var resp FIndexKlineData
params := url.Values{}
symbolValue, err := h.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
if !common.StringDataCompare(validFuturesPeriods, period) {
return resp, fmt.Errorf("invalid period value received")
}
params.Set("period", period)
if size <= 0 || size > 2000 {
return resp, fmt.Errorf("invalid size")
}
params.Set("size", strconv.FormatInt(size, 10))
path := fIndexKline + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FGetBasisData gets basis data futures contracts
func (h *HUOBI) FGetBasisData(symbol currency.Pair, period, basisPriceType string, size int64) (FBasisData, error) {
var resp FBasisData
params := url.Values{}
symbolValue, err := h.FormatSymbol(symbol, asset.Futures)
if err != nil {
return resp, err
}
params.Set("symbol", symbolValue)
if !common.StringDataCompare(validFuturesPeriods, period) {
return resp, fmt.Errorf("invalid period value received")
}
params.Set("period", period)
if basisPriceType != "" {
if common.StringDataCompare(validBasisPriceTypes, basisPriceType) {
params.Set("basis_price_type", basisPriceType)
}
}
if size > 0 && size <= 2000 {
params.Set("size", strconv.FormatInt(size, 10))
}
path := fBasisData + params.Encode()
return resp, h.SendHTTPRequest(exchange.RestFutures, path, &resp)
}
// FGetAccountInfo gets user info for futures account
func (h *HUOBI) FGetAccountInfo(symbol currency.Code) (FUserAccountData, error) {
var resp FUserAccountData
req := make(map[string]interface{})
if symbol != (currency.Code{}) {
codeValue, err := h.formatFuturesCode(symbol)
if err != nil {
return resp, err
}
req["symbol"] = codeValue
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fAccountData, nil, req, &resp)
}
// FGetPositionsInfo gets positions info for futures account
func (h *HUOBI) FGetPositionsInfo(symbol currency.Code) (FUserAccountData, error) {
var resp FUserAccountData
req := make(map[string]interface{})
if symbol != (currency.Code{}) {
codeValue, err := h.formatFuturesCode(symbol)
if err != nil {
return resp, err
}
req["symbol"] = codeValue
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fPositionInformation, nil, req, &resp)
}
// FGetAllSubAccountAssets gets assets info for all futures subaccounts
func (h *HUOBI) FGetAllSubAccountAssets(symbol currency.Code) (FSubAccountAssetsInfo, error) {
var resp FSubAccountAssetsInfo
req := make(map[string]interface{})
if symbol != (currency.Code{}) {
codeValue, err := h.formatFuturesCode(symbol)
if err != nil {
return resp, err
}
req["symbol"] = codeValue
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fAllSubAccountAssets, nil, req, &resp)
}
// FGetSingleSubAccountInfo gets assets info for a futures subaccount
func (h *HUOBI) FGetSingleSubAccountInfo(symbol, subUID string) (FSingleSubAccountAssetsInfo, error) {
var resp FSingleSubAccountAssetsInfo
req := make(map[string]interface{})
if symbol != "" {
req["symbol"] = symbol
}
req["sub_uid"] = subUID
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fSingleSubAccountAssets, nil, req, &resp)
}
// FGetSingleSubPositions gets positions info for a single sub account
func (h *HUOBI) FGetSingleSubPositions(symbol, subUID string) (FSingleSubAccountPositionsInfo, error) {
var resp FSingleSubAccountPositionsInfo
req := make(map[string]interface{})
if symbol != "" {
req["symbol"] = symbol
}
req["sub_uid"] = subUID
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fSingleSubAccountPositions, nil, req, &resp)
}
// FGetFinancialRecords gets financial records for futures
func (h *HUOBI) FGetFinancialRecords(symbol, recordType string, createDate, pageIndex, pageSize int64) (FFinancialRecords, error) {
var resp FFinancialRecords
req := make(map[string]interface{})
if symbol != "" {
req["symbol"] = symbol
}
if recordType != "" {
rType, ok := validFuturesRecordTypes[recordType]
if !ok {
return resp, fmt.Errorf("invalid recordType")
}
req["type"] = rType
}
if createDate > 0 && createDate < 90 {
req["create_date"] = createDate
}
if pageIndex != 0 {
req["page_index"] = pageIndex
}
if pageSize != 0 {
req["page_size"] = pageSize
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fFinancialRecords, nil, req, &resp)
}
// FGetSettlementRecords gets settlement records for futures
func (h *HUOBI) FGetSettlementRecords(symbol currency.Code, pageIndex, pageSize int64, startTime, endTime time.Time) (FSettlementRecords, error) {
var resp FSettlementRecords
req := make(map[string]interface{})
req["symbol"] = symbol
if pageIndex != 0 {
req["page_index"] = pageIndex
}
if pageSize != 0 {
req["page_size"] = pageSize
}
if !startTime.IsZero() && !endTime.IsZero() {
if startTime.After(endTime) {
return resp, errors.New("startTime cannot be after endTime")
}
req["start_time"] = strconv.FormatInt(startTime.Unix()*1000, 10)
req["end_time"] = strconv.FormatInt(endTime.Unix()*1000, 10)
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fSettlementRecords, nil, req, &resp)
}
// FGetOrderLimits gets order limits for futures contracts
func (h *HUOBI) FGetOrderLimits(symbol, orderPriceType string) (FContractInfoOnOrderLimit, error) {
var resp FContractInfoOnOrderLimit
req := make(map[string]interface{})
if symbol != "" {
req["symbol"] = symbol
}
if orderPriceType != "" {
if !common.StringDataCompare(validFuturesOrderPriceTypes, orderPriceType) {
return resp, fmt.Errorf("invalid orderPriceType")
}
req["order_price_type"] = orderPriceType
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fOrderLimitInfo, nil, req, &resp)
}
// FContractTradingFee gets futures contract trading fees
func (h *HUOBI) FContractTradingFee(symbol currency.Code) (FContractTradingFeeData, error) {
var resp FContractTradingFeeData
req := make(map[string]interface{})
if symbol != (currency.Code{}) {
codeValue, err := h.formatFuturesCode(symbol)
if err != nil {
return resp, err
}
req["symbol"] = codeValue
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fContractTradingFee, nil, req, &resp)
}
// FGetTransferLimits gets transfer limits for futures
func (h *HUOBI) FGetTransferLimits(symbol currency.Code) (FTransferLimitData, error) {
var resp FTransferLimitData
req := make(map[string]interface{})
if symbol != (currency.Code{}) {
codeValue, err := h.formatFuturesCode(symbol)
if err != nil {
return resp, err
}
req["symbol"] = codeValue
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fTransferLimitInfo, nil, req, &resp)
}
// FGetPositionLimits gets position limits for futures
func (h *HUOBI) FGetPositionLimits(symbol currency.Code) (FPositionLimitData, error) {
var resp FPositionLimitData
req := make(map[string]interface{})
if symbol != (currency.Code{}) {
codeValue, err := h.formatFuturesCode(symbol)
if err != nil {
return resp, err
}
req["symbol"] = codeValue
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fPositionLimitInfo, nil, req, &resp)
}
// FGetAssetsAndPositions gets assets and positions for futures
func (h *HUOBI) FGetAssetsAndPositions(symbol currency.Code) (FAssetsAndPositionsData, error) {
var resp FAssetsAndPositionsData
req := make(map[string]interface{})
req["symbol"] = symbol
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fQueryAssetsAndPositions, nil, req, &resp)
}
// FTransfer transfers assets between master and subaccounts
func (h *HUOBI) FTransfer(subUID, symbol, transferType string, amount float64) (FAccountTransferData, error) {
var resp FAccountTransferData
req := make(map[string]interface{})
req["symbol"] = symbol
req["subUid"] = subUID
req["amount"] = amount
if !common.StringDataCompare(validTransferType, transferType) {
return resp, fmt.Errorf("inavlid transferType received")
}
req["type"] = transferType
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fTransfer, nil, req, &resp)
}
// FGetTransferRecords gets transfer records data for futures
func (h *HUOBI) FGetTransferRecords(symbol, transferType string, createDate, pageIndex, pageSize int64) (FTransferRecords, error) {
var resp FTransferRecords
req := make(map[string]interface{})
if symbol != "" {
req["symbol"] = symbol
}
if !common.StringDataCompare(validTransferType, transferType) {
return resp, fmt.Errorf("inavlid transferType received")
}
req["type"] = transferType
if createDate < 0 || createDate > 90 {
return resp, fmt.Errorf("invalid create date value: only supports up to 90 days")
}
req["create_date"] = strconv.FormatInt(createDate, 10)
if pageIndex != 0 {
req["page_index"] = pageIndex
}
if pageSize > 0 && pageSize <= 50 {
req["page_size"] = pageSize
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fTransferRecords, nil, req, &resp)
}
// FGetAvailableLeverage gets available leverage data for futures
func (h *HUOBI) FGetAvailableLeverage(symbol currency.Code) (FAvailableLeverageData, error) {
var resp FAvailableLeverageData
req := make(map[string]interface{})
if symbol != (currency.Code{}) {
codeValue, err := h.formatFuturesCode(symbol)
if err != nil {
return resp, err
}
req["symbol"] = codeValue
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fAvailableLeverage, nil, req, &resp)
}
// FOrder places an order for futures
func (h *HUOBI) FOrder(contractCode currency.Pair, symbol, contractType, clientOrderID, direction, offset, orderPriceType string, price, volume, leverageRate float64) (FOrderData, error) {
var resp FOrderData
req := make(map[string]interface{})
if symbol != "" {
req["symbol"] = symbol
}
if contractType != "" {
if !common.StringDataCompare(validContractTypes, contractType) {
return resp, fmt.Errorf("invalid contractType")
}
req["contract_type"] = contractType
}
if contractCode != (currency.Pair{}) {
codeValue, err := h.FormatSymbol(contractCode, asset.Futures)
if err != nil {
return resp, err
}
req["contract_code"] = codeValue
}
if clientOrderID != "" {
req["client_order_id"] = clientOrderID
}
req["direction"] = direction
if !common.StringDataCompare(validOffsetTypes, offset) {
return resp, fmt.Errorf("invalid offset amounts")
}
if !common.StringDataCompare(validFuturesOrderPriceTypes, orderPriceType) {
return resp, fmt.Errorf("invalid orderPriceType")
}
req["order_price_type"] = orderPriceType
req["lever_rate"] = leverageRate
req["volume"] = volume
req["price"] = price
req["offset"] = offset
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fOrder, nil, req, &resp)
}
// FPlaceBatchOrder places a batch of orders for futures
func (h *HUOBI) FPlaceBatchOrder(data []fBatchOrderData) (FBatchOrderResponse, error) {
var resp FBatchOrderResponse
req := make(map[string]interface{})
if len(data) > 10 || len(data) == 0 {
return resp, fmt.Errorf("invalid data provided: maximum of 10 batch orders supported")
}
for x := range data {
if data[x].ContractCode != "" {
unformattedPair, err := currency.NewPairFromString(data[x].ContractCode)
if err != nil {
return resp, err
}
formattedPair, err := h.FormatExchangeCurrency(unformattedPair, asset.Futures)
if err != nil {
return resp, err
}
data[x].ContractCode = formattedPair.String()
}
if data[x].ContractType != "" {
if !common.StringDataCompare(validContractTypes, data[x].ContractType) {
return resp, fmt.Errorf("invalid contractType")
}
}
if !common.StringDataCompare(validOffsetTypes, data[x].Offset) {
return resp, fmt.Errorf("invalid offset amounts")
}
if !common.StringDataCompare(validFuturesOrderPriceTypes, data[x].OrderPriceType) {
return resp, fmt.Errorf("invalid orderPriceType")
}
}
req["orders_data"] = data
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fBatchOrder, nil, req, &resp)
}
// FCancelOrder cancels a futures order
func (h *HUOBI) FCancelOrder(symbol, orderID, clientOrderID string) (FCancelOrderData, error) {
var resp FCancelOrderData
req := make(map[string]interface{})
if symbol != "" {
req["symbol"] = symbol
}
if orderID != "" {
req["order_id"] = orderID
}
if clientOrderID != "" {
req["client_order_id"] = clientOrderID
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fCancelOrder, nil, req, &resp)
}
// FCancelAllOrders cancels all futures order for a given symbol
func (h *HUOBI) FCancelAllOrders(contractCode currency.Pair, symbol, contractType string) (FCancelOrderData, error) {
var resp FCancelOrderData
req := make(map[string]interface{})
if symbol != "" {
req["symbol"] = symbol
}
if contractType != "" {
if !common.StringDataCompare(validContractTypes, contractType) {
return resp, fmt.Errorf("invalid contractType")
}
req["contract_type"] = contractType
}
if contractCode != (currency.Pair{}) {
codeValue, err := h.FormatSymbol(contractCode, asset.Futures)
if err != nil {
return resp, err
}
req["contract_code"] = codeValue
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fCancelAllOrders, nil, req, &resp)
}
// FFlashCloseOrder flash closes a futures order
func (h *HUOBI) FFlashCloseOrder(contractCode currency.Pair, symbol, contractType, direction, orderPriceType, clientOrderID string, volume float64) (FOrderData, error) {
var resp FOrderData
req := make(map[string]interface{})
req["symbol"] = symbol
if contractType != "" {
if !common.StringDataCompare(validContractTypes, contractType) {
return resp, fmt.Errorf("invalid contractType")
}
req["contract_type"] = contractType
}
if contractCode != (currency.Pair{}) {
codeValue, err := h.FormatSymbol(contractCode, asset.Futures)
if err != nil {
return resp, err
}
req["contract_code"] = codeValue
}
req["direction"] = direction
req["volume"] = volume
if clientOrderID != "" {
req["client_order_id"] = clientOrderID
}
if orderPriceType != "" {
if !common.StringDataCompare(validOPTypes, orderPriceType) {
return resp, fmt.Errorf("invalid orderPriceType")
}
req["orderPriceType"] = orderPriceType
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fFlashCloseOrder, nil, req, &resp)
}
// FGetOrderInfo gets order info for futures
func (h *HUOBI) FGetOrderInfo(symbol, clientOrderID, orderID string) (FOrderInfo, error) {
var resp FOrderInfo
req := make(map[string]interface{})
req["symbol"] = symbol
if orderID != "" {
req["order_id"] = orderID
}
if clientOrderID != "" {
req["client_order_id"] = clientOrderID
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fOrderInfo, nil, req, &resp)
}
// FOrderDetails gets order details for futures orders
func (h *HUOBI) FOrderDetails(symbol, orderID, orderType string, createdAt time.Time, pageIndex, pageSize int64) (FOrderDetailsData, error) {
var resp FOrderDetailsData
req := make(map[string]interface{})
req["symbol"] = symbol
req["order_id"] = orderID
req["created_at"] = strconv.FormatInt(createdAt.Unix(), 10)
oType, ok := validOrderType[orderType]
if !ok {
return resp, fmt.Errorf("invalid orderType")
}
req["order_type"] = oType
if pageIndex != 0 {
req["page_index"] = pageIndex
}
if pageSize != 0 {
req["page_size"] = pageSize
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fOrderDetails, nil, req, &resp)
}
// FGetOpenOrders gets order details for futures orders
func (h *HUOBI) FGetOpenOrders(symbol currency.Code, pageIndex, pageSize int64) (FOpenOrdersData, error) {
var resp FOpenOrdersData
req := make(map[string]interface{})
req["symbol"] = symbol
if pageIndex != 0 {
req["page_index"] = pageIndex
}
if pageSize != 0 {
req["page_size"] = pageSize
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fQueryOpenOrders, nil, req, &resp)
}
// FGetOrderHistory gets order order history for futures
func (h *HUOBI) FGetOrderHistory(contractCode currency.Pair, symbol, tradeType, reqType, orderType string, status []order.Status, createDate, pageIndex, pageSize int64) (FOrderHistoryData, error) {
var resp FOrderHistoryData
req := make(map[string]interface{})
req["symbol"] = symbol
tType, ok := validFuturesTradeType[tradeType]
if !ok {
return resp, fmt.Errorf("invalid tradeType")
}
req["trade_type"] = tType
rType, ok := validFuturesReqType[reqType]
if !ok {
return resp, fmt.Errorf("invalid reqType")
}
req["type"] = rType
var reqStatus string = "0"
if len(status) > 0 {
var firstTime bool = true
for x := range status {
sType, ok := validOrderStatus[status[x]]
if !ok {
return resp, fmt.Errorf("invalid status")
}
if firstTime {
firstTime = false
reqStatus = strconv.FormatInt(sType, 10)
continue
}
reqStatus = reqStatus + "," + strconv.FormatInt(sType, 10)
}
}
req["status"] = reqStatus
if createDate < 0 || createDate > 90 {
return resp, fmt.Errorf("invalid createDate")
}
req["create_date"] = createDate
if contractCode != (currency.Pair{}) {
codeValue, err := h.FormatSymbol(contractCode, asset.Futures)
if err != nil {
return resp, err
}
req["contract_code"] = codeValue
}
if orderType != "" {
oType, ok := validFuturesOrderTypes[orderType]
if !ok {
return resp, fmt.Errorf("invalid orderType")
}
req["order_type"] = oType
}
if pageIndex != 0 {
req["page_index"] = pageIndex
}
if pageSize != 0 {
req["page_size"] = pageSize
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fOrderHistory, nil, req, &resp)
}
// FTradeHistory gets trade history data for futures
func (h *HUOBI) FTradeHistory(contractCode currency.Pair, symbol, tradeType string, createDate, pageIndex, pageSize int64) (FOrderHistoryData, error) {
var resp FOrderHistoryData
req := make(map[string]interface{})
req["symbol"] = symbol
tType, ok := validTradeType[tradeType]
if !ok {
return resp, fmt.Errorf("invalid tradeType")
}
req["trade_type"] = tType
if contractCode != (currency.Pair{}) {
codeValue, err := h.FormatSymbol(contractCode, asset.Futures)
if err != nil {
return resp, err
}
req["contract_code"] = codeValue
}
if createDate <= 0 || createDate > 90 {
return resp, fmt.Errorf("invalid createDate")
}
req["create_date"] = createDate
if pageIndex != 0 {
req["page_index"] = pageIndex
}
if pageSize != 0 {
req["page_size"] = pageSize
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fMatchResult, nil, req, &resp)
}
// FPlaceTriggerOrder places a trigger order for futures
func (h *HUOBI) FPlaceTriggerOrder(contractCode currency.Pair, symbol, contractType, triggerType, orderPriceType, direction, offset string, triggerPrice, orderPrice, volume, leverageRate float64) (FTriggerOrderData, error) {
var resp FTriggerOrderData
req := make(map[string]interface{})
if symbol != "" {
req["symbol"] = symbol
}
if contractType != "" {
if !common.StringDataCompare(validContractTypes, contractType) {
return resp, fmt.Errorf("invalid contractType: %s", contractType)
}
req["contract_type"] = contractType
}
if contractCode != (currency.Pair{}) {
codeValue, err := h.FormatSymbol(contractCode, asset.Futures)
if err != nil {
return resp, err
}
req["contract_code"] = codeValue
}
tType, ok := validTriggerType[triggerType]
if !ok {
return resp, fmt.Errorf("invalid trigger type")
}
req["trigger_type"] = tType
req["direction"] = direction
if !common.StringDataCompare(validOffsetTypes, offset) {
return resp, fmt.Errorf("invalid offset")
}
req["offset"] = offset
req["trigger_price"] = triggerPrice
req["volume"] = volume
req["lever_rate"] = leverageRate
req["order_price"] = orderPrice
if !common.StringDataCompare(validOrderPriceType, orderPriceType) {
return resp, fmt.Errorf("invalid order price type")
}
req["order_price_type"] = orderPriceType
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fTriggerOrder, nil, req, &resp)
}
// FCancelTriggerOrder cancels trigger order for futures
func (h *HUOBI) FCancelTriggerOrder(symbol, orderID string) (FCancelOrderData, error) {
var resp FCancelOrderData
req := make(map[string]interface{})
req["symbol"] = symbol
req["order_id"] = orderID
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fCancelTriggerOrder, nil, req, &resp)
}
// FCancelAllTriggerOrders cancels all trigger order for futures
func (h *HUOBI) FCancelAllTriggerOrders(contractCode currency.Pair, symbol, contractType string) (FCancelOrderData, error) {
var resp FCancelOrderData
req := make(map[string]interface{})
req["symbol"] = symbol
if contractCode != (currency.Pair{}) {
codeValue, err := h.FormatSymbol(contractCode, asset.Futures)
if err != nil {
return resp, err
}
req["contract_code"] = codeValue
}
if contractType != "" {
if !common.StringDataCompare(validContractTypes, contractType) {
return resp, nil
}
req["contract_type"] = contractType
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fCancelAllTriggerOrders, nil, req, &resp)
}
// FQueryTriggerOpenOrders queries open trigger orders for futures
func (h *HUOBI) FQueryTriggerOpenOrders(contractCode currency.Pair, symbol string, pageIndex, pageSize int64) (FTriggerOpenOrders, error) {
var resp FTriggerOpenOrders
req := make(map[string]interface{})
req["symbol"] = symbol
if contractCode != (currency.Pair{}) {
codeValue, err := h.FormatSymbol(contractCode, asset.Futures)
if err != nil {
return resp, err
}
req["contract_code"] = codeValue
}
if pageIndex != 0 {
req["page_index"] = pageIndex
}
if pageSize != 0 {
req["page_size"] = pageSize
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fTriggerOpenOrders, nil, req, &resp)
}
// FQueryTriggerOrderHistory queries trigger order history for futures
func (h *HUOBI) FQueryTriggerOrderHistory(contractCode currency.Pair, symbol, tradeType, status string, createDate, pageIndex, pageSize int64) (FTriggerOrderHistoryData, error) {
var resp FTriggerOrderHistoryData
req := make(map[string]interface{})
req["symbol"] = symbol
if contractCode != (currency.Pair{}) {
codeValue, err := h.FormatSymbol(contractCode, asset.Futures)
if err != nil {
return resp, err
}
req["contract_code"] = codeValue
}
if tradeType != "" {
tType, ok := validTradeType[tradeType]
if !ok {
return resp, fmt.Errorf("invalid tradeType")
}
req["trade_type"] = tType
}
validStatus, ok := validStatusTypes[status]
if !ok {
return resp, fmt.Errorf("invalid status")
}
req["status"] = validStatus
if createDate <= 0 || createDate > 90 {
return resp, fmt.Errorf("invalid createDate")
}
req["create_date"] = createDate
if pageIndex != 0 {
req["page_index"] = pageIndex
}
if pageSize != 0 {
req["page_size"] = pageSize
}
return resp, h.FuturesAuthenticatedHTTPRequest(exchange.RestFutures, http.MethodPost, fTriggerOrderHistory, nil, req, &resp)
}
// FuturesAuthenticatedHTTPRequest sends authenticated requests to the HUOBI API
func (h *HUOBI) FuturesAuthenticatedHTTPRequest(ep exchange.URL, method, endpoint string, values url.Values, data, result interface{}) error {
if !h.AllowAuthenticatedRequest() {
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, h.Name)
}
ePoint, err := h.API.Endpoints.GetURL(ep)
if err != nil {
return err
}
if values == nil {
values = url.Values{}
}
now := time.Now()
values.Set("AccessKeyId", h.API.Credentials.Key)
values.Set("SignatureMethod", "HmacSHA256")
values.Set("SignatureVersion", "2")
values.Set("Timestamp", now.UTC().Format("2006-01-02T15:04:05"))
sigPath := fmt.Sprintf("%s\napi.hbdm.com\n/%s\n%s",
method, endpoint, values.Encode())
headers := make(map[string]string)
if method == http.MethodGet {
headers["Content-Type"] = "application/x-www-form-urlencoded"
} else {
headers["Content-Type"] = "application/json"
}
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(sigPath), []byte(h.API.Credentials.Secret))
sigValues := url.Values{}
sigValues.Add("Signature", crypto.Base64Encode(hmac))
urlPath :=
common.EncodeURLValues(ePoint+endpoint, values) + "&" + sigValues.Encode()
var body io.Reader
var payload []byte
if data != nil {
payload, err = json.Marshal(data)
if err != nil {
return err
}
body = bytes.NewBuffer(payload)
}
var tempResp json.RawMessage
var errCap errorCapture
ctx, cancel := context.WithDeadline(context.Background(), now.Add(15*time.Second))
defer cancel()
if err := h.SendPayload(ctx, &request.Item{
Method: method,
Path: urlPath,
Headers: headers,
Body: body,
Result: &tempResp,
AuthRequest: true,
Verbose: h.Verbose,
HTTPDebugging: h.HTTPDebugging,
HTTPRecording: h.HTTPRecording,
}); err != nil {
return err
}
if err := json.Unmarshal(tempResp, &errCap); err == nil {
if errCap.Code != 200 && errCap.ErrMsg != "" {
return errors.New(errCap.ErrMsg)
}
}
return json.Unmarshal(tempResp, result)
}
func (h *HUOBI) formatFuturesCode(p currency.Code) (string, error) {
pairFmt, err := h.GetPairFormat(asset.Futures, true)
if err != nil {
return "", err
}
if pairFmt.Uppercase {
return p.Upper().String(), nil
}
return p.Lower().String(), nil
}