mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-23 23:16:49 +00:00
* CI: Bump go version, linters and fix minor issues * Bump version, fix loop variables * Revert * Rid TODOs now that 1.51 has been released
1229 lines
44 KiB
Go
1229 lines
44 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/v3/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(ctx context.Context, symbol, contractType string, code currency.Pair) (FContractInfoData, error) {
|
|
var resp FContractInfoData
|
|
params := url.Values{}
|
|
if symbol != "" {
|
|
params.Set("symbol", symbol)
|
|
}
|
|
if contractType != "" {
|
|
if !common.StringDataCompareInsensitive(validContractTypes, contractType) {
|
|
return resp, fmt.Errorf("invalid contractType")
|
|
}
|
|
params.Set("contract_type", contractType)
|
|
}
|
|
if !code.IsEmpty() {
|
|
codeValue, err := h.FormatSymbol(code, asset.Futures)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("contract_code", codeValue)
|
|
}
|
|
path := common.EncodeURLValues(fContractInfo, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FIndexPriceInfo gets index price info for a futures contract
|
|
func (h *HUOBI) FIndexPriceInfo(ctx context.Context, symbol currency.Code) (FContractIndexPriceInfo, error) {
|
|
var resp FContractIndexPriceInfo
|
|
params := url.Values{}
|
|
if !symbol.IsEmpty() {
|
|
codeValue, err := h.formatFuturesCode(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("symbol", codeValue)
|
|
}
|
|
path := common.EncodeURLValues(fContractIndexPrice, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FContractPriceLimitations gets price limits for a futures contract
|
|
func (h *HUOBI) FContractPriceLimitations(ctx context.Context, symbol, contractType string, code currency.Pair) (FContractIndexPriceInfo, error) {
|
|
var resp FContractIndexPriceInfo
|
|
params := url.Values{}
|
|
if symbol != "" {
|
|
params.Set("symbol", symbol)
|
|
}
|
|
if contractType != "" {
|
|
if !common.StringDataCompareInsensitive(validContractTypes, contractType) {
|
|
return resp, fmt.Errorf("invalid contractType: %s", contractType)
|
|
}
|
|
params.Set("contract_type", contractType)
|
|
}
|
|
if !code.IsEmpty() {
|
|
codeValue, err := h.FormatSymbol(code, asset.Futures)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("contract_code", codeValue)
|
|
}
|
|
path := common.EncodeURLValues(fContractPriceLimitation, params)
|
|
return resp, 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
|
|
params := url.Values{}
|
|
if symbol != "" {
|
|
params.Set("symbol", symbol)
|
|
}
|
|
if contractType != "" {
|
|
if !common.StringDataCompareInsensitive(validContractTypes, contractType) {
|
|
return resp, fmt.Errorf("invalid contractType")
|
|
}
|
|
params.Set("contract_type", contractType)
|
|
}
|
|
if !code.IsEmpty() {
|
|
codeValue, err := h.FormatSymbol(code, asset.Futures)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("contract_code", codeValue)
|
|
}
|
|
path := common.EncodeURLValues(fContractOpenInterest, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FGetEstimatedDeliveryPrice gets estimated delivery price info for futures
|
|
func (h *HUOBI) FGetEstimatedDeliveryPrice(ctx context.Context, 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 := common.EncodeURLValues(fEstimatedDeliveryPrice, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// 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)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params := url.Values{}
|
|
params.Set("symbol", symbolValue)
|
|
params.Set("type", dataType)
|
|
path := common.EncodeURLValues(fContractMarketDepth, params)
|
|
|
|
var tempData FMarketDepth
|
|
err = h.SendHTTPRequest(ctx, exchange.RestFutures, path, &tempData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp := OBData{
|
|
Symbol: symbolValue,
|
|
Bids: make([]obItem, len(tempData.Tick.Bids)),
|
|
Asks: make([]obItem, len(tempData.Tick.Asks)),
|
|
}
|
|
resp.Symbol = symbolValue
|
|
for x := range tempData.Tick.Asks {
|
|
resp.Asks[x] = obItem{
|
|
Price: tempData.Tick.Asks[x][0],
|
|
Quantity: tempData.Tick.Asks[x][1],
|
|
}
|
|
}
|
|
for y := range tempData.Tick.Bids {
|
|
resp.Bids[y] = 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(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)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("symbol", symbolValue)
|
|
if !common.StringDataCompareInsensitive(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 := common.EncodeURLValues(fContractKline, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FGetMarketOverviewData gets market overview data for futures
|
|
func (h *HUOBI) FGetMarketOverviewData(ctx context.Context, symbol currency.Pair) (FMarketOverviewData, error) {
|
|
var resp FMarketOverviewData
|
|
params := url.Values{}
|
|
symbolValue, err := h.formatFuturesPair(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("symbol", symbolValue)
|
|
path := common.EncodeURLValues(fMarketOverview, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FLastTradeData gets last trade data for a futures contract
|
|
func (h *HUOBI) FLastTradeData(ctx context.Context, symbol currency.Pair) (FLastTradeData, error) {
|
|
var resp FLastTradeData
|
|
params := url.Values{}
|
|
symbolValue, err := h.formatFuturesPair(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("symbol", symbolValue)
|
|
path := common.EncodeURLValues(fLastTradeContract, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FRequestPublicBatchTrades gets public batch trades for a futures contract
|
|
func (h *HUOBI) FRequestPublicBatchTrades(ctx context.Context, symbol currency.Pair, size int64) (FBatchTradesForContractData, error) {
|
|
var resp FBatchTradesForContractData
|
|
params := url.Values{}
|
|
symbolValue, err := h.formatFuturesPair(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("symbol", symbolValue)
|
|
if size > 1 && size < 2000 {
|
|
params.Set("size", strconv.FormatInt(size, 10))
|
|
}
|
|
path := common.EncodeURLValues(fContractBatchTradeRecords, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FQueryInsuranceAndClawbackData gets insurance and clawback data for a futures contract
|
|
func (h *HUOBI) FQueryInsuranceAndClawbackData(ctx context.Context, symbol currency.Code) (FClawbackRateAndInsuranceData, error) {
|
|
var resp FClawbackRateAndInsuranceData
|
|
params := url.Values{}
|
|
if !symbol.IsEmpty() {
|
|
codeValue, err := h.formatFuturesCode(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("symbol", codeValue)
|
|
}
|
|
path := common.EncodeURLValues(fInsuranceAndClawback, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FQueryHistoricalInsuranceData gets insurance data
|
|
func (h *HUOBI) FQueryHistoricalInsuranceData(ctx context.Context, symbol currency.Code) (FHistoricalInsuranceRecordsData, error) {
|
|
var resp FHistoricalInsuranceRecordsData
|
|
params := url.Values{}
|
|
if !symbol.IsEmpty() {
|
|
codeValue, err := h.formatFuturesCode(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("symbol", codeValue)
|
|
}
|
|
path := common.EncodeURLValues(fInsuranceBalanceHistory, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FQueryTieredAdjustmentFactor gets tiered adjustment factor for futures contracts
|
|
func (h *HUOBI) FQueryTieredAdjustmentFactor(ctx context.Context, symbol currency.Code) (FTieredAdjustmentFactorInfo, error) {
|
|
var resp FTieredAdjustmentFactorInfo
|
|
params := url.Values{}
|
|
if !symbol.IsEmpty() {
|
|
codeValue, err := h.formatFuturesCode(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("symbol", codeValue)
|
|
}
|
|
path := common.EncodeURLValues(fTieredAdjustmentFactor, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FQueryHisOpenInterest gets open interest for futures contract
|
|
func (h *HUOBI) FQueryHisOpenInterest(ctx context.Context, symbol, contractType, period, amountType string, size int64) (FOIData, error) {
|
|
var resp FOIData
|
|
params := url.Values{}
|
|
if symbol != "" {
|
|
params.Set("symbol", symbol)
|
|
}
|
|
if !common.StringDataCompareInsensitive(validContractTypes, contractType) {
|
|
return resp, fmt.Errorf("invalid contract type")
|
|
}
|
|
params.Set("contract_type", contractType)
|
|
if !common.StringDataCompareInsensitive(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 := common.EncodeURLValues(fHisContractOpenInterest, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FQuerySystemStatus gets system status data
|
|
func (h *HUOBI) FQuerySystemStatus(ctx context.Context, symbol currency.Code) (FContractOIData, error) {
|
|
var resp FContractOIData
|
|
params := url.Values{}
|
|
if !symbol.IsEmpty() {
|
|
codeValue, err := h.formatFuturesCode(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("symbol", codeValue)
|
|
}
|
|
path := common.EncodeURLValues(fSystemStatus, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FQueryTopAccountsRatio gets top accounts' ratio
|
|
func (h *HUOBI) FQueryTopAccountsRatio(ctx context.Context, symbol, period string) (FTopAccountsLongShortRatio, error) {
|
|
var resp FTopAccountsLongShortRatio
|
|
params := url.Values{}
|
|
if symbol != "" {
|
|
params.Set("symbol", symbol)
|
|
}
|
|
if !common.StringDataCompareInsensitive(validPeriods, period) {
|
|
return resp, fmt.Errorf("invalid period")
|
|
}
|
|
params.Set("period", period)
|
|
path := common.EncodeURLValues(fTopAccountsSentiment, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FQueryTopPositionsRatio gets top positions' long/short ratio for futures
|
|
func (h *HUOBI) FQueryTopPositionsRatio(ctx context.Context, symbol, period string) (FTopPositionsLongShortRatio, error) {
|
|
var resp FTopPositionsLongShortRatio
|
|
params := url.Values{}
|
|
if symbol != "" {
|
|
params.Set("symbol", symbol)
|
|
}
|
|
if !common.StringDataCompareInsensitive(validPeriods, period) {
|
|
return resp, fmt.Errorf("invalid period")
|
|
}
|
|
params.Set("period", period)
|
|
path := common.EncodeURLValues(fTopPositionsSentiment, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FLiquidationOrders gets liquidation orders for futures contracts
|
|
func (h *HUOBI) FLiquidationOrders(ctx context.Context, symbol currency.Code, tradeType string, startTime, endTime int64, direction string, fromID int64) (LiquidationOrdersData, error) {
|
|
var resp LiquidationOrdersData
|
|
tType, ok := validTradeTypes[tradeType]
|
|
if !ok {
|
|
return resp, fmt.Errorf("invalid trade type")
|
|
}
|
|
params := url.Values{}
|
|
params.Set("symbol", symbol.String())
|
|
params.Set("trade_type", strconv.FormatInt(tType, 10))
|
|
|
|
if startTime != 0 {
|
|
params.Set("start_time", strconv.FormatInt(startTime, 10))
|
|
}
|
|
if endTime != 0 {
|
|
params.Set("end_time", strconv.FormatInt(startTime, 10))
|
|
}
|
|
if direction != "" {
|
|
params.Set("direct", direction)
|
|
}
|
|
if fromID != 0 {
|
|
params.Set("from_id", strconv.FormatInt(fromID, 10))
|
|
}
|
|
path := common.EncodeURLValues(fLiquidationOrders, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FIndexKline gets index kline data for futures contracts
|
|
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)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("symbol", symbolValue)
|
|
if !common.StringDataCompareInsensitive(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 := common.EncodeURLValues(fIndexKline, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FGetBasisData gets basis data futures contracts
|
|
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)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
params.Set("symbol", symbolValue)
|
|
if !common.StringDataCompareInsensitive(validFuturesPeriods, period) {
|
|
return resp, fmt.Errorf("invalid period value received")
|
|
}
|
|
params.Set("period", period)
|
|
if basisPriceType != "" {
|
|
if common.StringDataCompareInsensitive(validBasisPriceTypes, basisPriceType) {
|
|
params.Set("basis_price_type", basisPriceType)
|
|
}
|
|
}
|
|
if size > 0 && size <= 2000 {
|
|
params.Set("size", strconv.FormatInt(size, 10))
|
|
}
|
|
path := common.EncodeURLValues(fBasisData, params)
|
|
return resp, h.SendHTTPRequest(ctx, exchange.RestFutures, path, &resp)
|
|
}
|
|
|
|
// FGetAccountInfo gets user info for futures account
|
|
func (h *HUOBI) FGetAccountInfo(ctx context.Context, symbol currency.Code) (FUserAccountData, error) {
|
|
var resp FUserAccountData
|
|
req := make(map[string]interface{})
|
|
if !symbol.IsEmpty() {
|
|
codeValue, err := h.formatFuturesCode(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
req["symbol"] = codeValue
|
|
}
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fAccountData, nil, req, &resp)
|
|
}
|
|
|
|
// FGetPositionsInfo gets positions info for futures account
|
|
func (h *HUOBI) FGetPositionsInfo(ctx context.Context, symbol currency.Code) (FUsersPositionsInfo, error) {
|
|
var resp FUsersPositionsInfo
|
|
req := make(map[string]interface{})
|
|
if !symbol.IsEmpty() {
|
|
codeValue, err := h.formatFuturesCode(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
req["symbol"] = codeValue
|
|
}
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fPositionInformation, nil, req, &resp)
|
|
}
|
|
|
|
// FGetAllSubAccountAssets gets assets info for all futures subaccounts
|
|
func (h *HUOBI) FGetAllSubAccountAssets(ctx context.Context, symbol currency.Code) (FSubAccountAssetsInfo, error) {
|
|
var resp FSubAccountAssetsInfo
|
|
req := make(map[string]interface{})
|
|
if !symbol.IsEmpty() {
|
|
codeValue, err := h.formatFuturesCode(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
req["symbol"] = codeValue
|
|
}
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fAllSubAccountAssets, nil, req, &resp)
|
|
}
|
|
|
|
// FGetSingleSubAccountInfo gets assets info for a futures subaccount
|
|
func (h *HUOBI) FGetSingleSubAccountInfo(ctx context.Context, 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(ctx, exchange.RestFutures, http.MethodPost, fSingleSubAccountAssets, nil, req, &resp)
|
|
}
|
|
|
|
// FGetSingleSubPositions gets positions info for a single sub account
|
|
func (h *HUOBI) FGetSingleSubPositions(ctx context.Context, 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(ctx, exchange.RestFutures, http.MethodPost, fSingleSubAccountPositions, nil, req, &resp)
|
|
}
|
|
|
|
// FGetFinancialRecords gets financial records for futures
|
|
func (h *HUOBI) FGetFinancialRecords(ctx context.Context, 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(ctx, exchange.RestFutures, http.MethodPost, fFinancialRecords, nil, req, &resp)
|
|
}
|
|
|
|
// FGetSettlementRecords gets settlement records for futures
|
|
func (h *HUOBI) FGetSettlementRecords(ctx context.Context, 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(ctx, exchange.RestFutures, http.MethodPost, fSettlementRecords, nil, req, &resp)
|
|
}
|
|
|
|
// FGetOrderLimits gets order limits for futures contracts
|
|
func (h *HUOBI) FGetOrderLimits(ctx context.Context, symbol, orderPriceType string) (FContractInfoOnOrderLimit, error) {
|
|
var resp FContractInfoOnOrderLimit
|
|
req := make(map[string]interface{})
|
|
if symbol != "" {
|
|
req["symbol"] = symbol
|
|
}
|
|
if orderPriceType != "" {
|
|
if !common.StringDataCompareInsensitive(validFuturesOrderPriceTypes, orderPriceType) {
|
|
return resp, fmt.Errorf("invalid orderPriceType")
|
|
}
|
|
req["order_price_type"] = orderPriceType
|
|
}
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fOrderLimitInfo, nil, req, &resp)
|
|
}
|
|
|
|
// FContractTradingFee gets futures contract trading fees
|
|
func (h *HUOBI) FContractTradingFee(ctx context.Context, symbol currency.Code) (FContractTradingFeeData, error) {
|
|
var resp FContractTradingFeeData
|
|
req := make(map[string]interface{})
|
|
if !symbol.IsEmpty() {
|
|
codeValue, err := h.formatFuturesCode(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
req["symbol"] = codeValue
|
|
}
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fContractTradingFee, nil, req, &resp)
|
|
}
|
|
|
|
// FGetTransferLimits gets transfer limits for futures
|
|
func (h *HUOBI) FGetTransferLimits(ctx context.Context, symbol currency.Code) (FTransferLimitData, error) {
|
|
var resp FTransferLimitData
|
|
req := make(map[string]interface{})
|
|
if !symbol.IsEmpty() {
|
|
codeValue, err := h.formatFuturesCode(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
req["symbol"] = codeValue
|
|
}
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fTransferLimitInfo, nil, req, &resp)
|
|
}
|
|
|
|
// FGetPositionLimits gets position limits for futures
|
|
func (h *HUOBI) FGetPositionLimits(ctx context.Context, symbol currency.Code) (FPositionLimitData, error) {
|
|
var resp FPositionLimitData
|
|
req := make(map[string]interface{})
|
|
if !symbol.IsEmpty() {
|
|
codeValue, err := h.formatFuturesCode(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
req["symbol"] = codeValue
|
|
}
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fPositionLimitInfo, nil, req, &resp)
|
|
}
|
|
|
|
// FGetAssetsAndPositions gets assets and positions for futures
|
|
func (h *HUOBI) FGetAssetsAndPositions(ctx context.Context, symbol currency.Code) (FAssetsAndPositionsData, error) {
|
|
var resp FAssetsAndPositionsData
|
|
req := make(map[string]interface{})
|
|
req["symbol"] = symbol
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fQueryAssetsAndPositions, nil, req, &resp)
|
|
}
|
|
|
|
// FTransfer transfers assets between master and subaccounts
|
|
func (h *HUOBI) FTransfer(ctx context.Context, 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.StringDataCompareInsensitive(validTransferType, transferType) {
|
|
return resp, fmt.Errorf("invalid transferType received")
|
|
}
|
|
req["type"] = transferType
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fTransfer, nil, req, &resp)
|
|
}
|
|
|
|
// FGetTransferRecords gets transfer records data for futures
|
|
func (h *HUOBI) FGetTransferRecords(ctx context.Context, symbol, transferType string, createDate, pageIndex, pageSize int64) (FTransferRecords, error) {
|
|
var resp FTransferRecords
|
|
req := make(map[string]interface{})
|
|
if symbol != "" {
|
|
req["symbol"] = symbol
|
|
}
|
|
if !common.StringDataCompareInsensitive(validTransferType, transferType) {
|
|
return resp, fmt.Errorf("invalid 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(ctx, exchange.RestFutures, http.MethodPost, fTransferRecords, nil, req, &resp)
|
|
}
|
|
|
|
// FGetAvailableLeverage gets available leverage data for futures
|
|
func (h *HUOBI) FGetAvailableLeverage(ctx context.Context, symbol currency.Code) (FAvailableLeverageData, error) {
|
|
var resp FAvailableLeverageData
|
|
req := make(map[string]interface{})
|
|
if !symbol.IsEmpty() {
|
|
codeValue, err := h.formatFuturesCode(symbol)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
req["symbol"] = codeValue
|
|
}
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fAvailableLeverage, nil, req, &resp)
|
|
}
|
|
|
|
// FOrder places an order for futures
|
|
func (h *HUOBI) FOrder(ctx context.Context, 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.StringDataCompareInsensitive(validContractTypes, contractType) {
|
|
return resp, fmt.Errorf("invalid contractType")
|
|
}
|
|
req["contract_type"] = contractType
|
|
}
|
|
if !contractCode.IsEmpty() {
|
|
codeValue, err := h.FormatSymbol(contractCode, asset.Futures)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
req["contract_code"] = codeValue
|
|
}
|
|
if clientOrderID != "" {
|
|
// Client order id is an integer, convert it here
|
|
// https://huobiapi.github.io/docs/dm/v1/en/#place-an-order
|
|
id, err := strconv.Atoi(clientOrderID)
|
|
if err != nil {
|
|
return resp,
|
|
fmt.Errorf("unable to convert client order id to integer, %s: %w", clientOrderID, err)
|
|
}
|
|
req["client_order_id"] = id
|
|
}
|
|
req["direction"] = direction
|
|
if !common.StringDataCompareInsensitive(validOffsetTypes, offset) {
|
|
return resp, fmt.Errorf("invalid offset amounts")
|
|
}
|
|
if !common.StringDataCompareInsensitive(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(ctx, exchange.RestFutures, http.MethodPost, fOrder, nil, req, &resp)
|
|
}
|
|
|
|
// FPlaceBatchOrder places a batch of orders for futures
|
|
func (h *HUOBI) FPlaceBatchOrder(ctx context.Context, 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.StringDataCompareInsensitive(validContractTypes, data[x].ContractType) {
|
|
return resp, fmt.Errorf("invalid contractType")
|
|
}
|
|
}
|
|
if !common.StringDataCompareInsensitive(validOffsetTypes, data[x].Offset) {
|
|
return resp, fmt.Errorf("invalid offset amounts")
|
|
}
|
|
if !common.StringDataCompareInsensitive(validFuturesOrderPriceTypes, data[x].OrderPriceType) {
|
|
return resp, fmt.Errorf("invalid orderPriceType")
|
|
}
|
|
}
|
|
req["orders_data"] = data
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fBatchOrder, nil, req, &resp)
|
|
}
|
|
|
|
// FCancelOrder cancels a futures order
|
|
func (h *HUOBI) FCancelOrder(ctx context.Context, baseCurrency currency.Code, orderID, clientOrderID string) (FCancelOrderData, error) {
|
|
var resp FCancelOrderData
|
|
req := make(map[string]interface{})
|
|
if baseCurrency.IsEmpty() {
|
|
return resp, fmt.Errorf("cannot cancel futures order %w", currency.ErrCurrencyCodeEmpty)
|
|
}
|
|
req["symbol"] = baseCurrency.String() // Upper and lower case are supported
|
|
if orderID != "" {
|
|
req["order_id"] = orderID
|
|
}
|
|
if clientOrderID != "" {
|
|
req["client_order_id"] = clientOrderID
|
|
}
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fCancelOrder, nil, req, &resp)
|
|
}
|
|
|
|
// FCancelAllOrders cancels all futures order for a given symbol
|
|
func (h *HUOBI) FCancelAllOrders(ctx context.Context, 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.StringDataCompareInsensitive(validContractTypes, contractType) {
|
|
return resp, fmt.Errorf("invalid contractType")
|
|
}
|
|
req["contract_type"] = contractType
|
|
}
|
|
if !contractCode.IsEmpty() {
|
|
codeValue, err := h.FormatSymbol(contractCode, asset.Futures)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
req["contract_code"] = codeValue
|
|
}
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fCancelAllOrders, nil, req, &resp)
|
|
}
|
|
|
|
// FFlashCloseOrder flash closes a futures order
|
|
func (h *HUOBI) FFlashCloseOrder(ctx context.Context, 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.StringDataCompareInsensitive(validContractTypes, contractType) {
|
|
return resp, fmt.Errorf("invalid contractType")
|
|
}
|
|
req["contract_type"] = contractType
|
|
}
|
|
if !contractCode.IsEmpty() {
|
|
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.StringDataCompareInsensitive(validOPTypes, orderPriceType) {
|
|
return resp, fmt.Errorf("invalid orderPriceType")
|
|
}
|
|
req["orderPriceType"] = orderPriceType
|
|
}
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fFlashCloseOrder, nil, req, &resp)
|
|
}
|
|
|
|
// FGetOrderInfo gets order info for futures
|
|
func (h *HUOBI) FGetOrderInfo(ctx context.Context, 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(ctx, exchange.RestFutures, http.MethodPost, fOrderInfo, nil, req, &resp)
|
|
}
|
|
|
|
// FOrderDetails gets order details for futures orders
|
|
func (h *HUOBI) FOrderDetails(ctx context.Context, 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(ctx, exchange.RestFutures, http.MethodPost, fOrderDetails, nil, req, &resp)
|
|
}
|
|
|
|
// FGetOpenOrders gets order details for futures orders
|
|
func (h *HUOBI) FGetOpenOrders(ctx context.Context, 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(ctx, exchange.RestFutures, http.MethodPost, fQueryOpenOrders, nil, req, &resp)
|
|
}
|
|
|
|
// FGetOrderHistory gets order history for futures
|
|
func (h *HUOBI) FGetOrderHistory(ctx context.Context, 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
|
|
reqStatus := "0"
|
|
if len(status) > 0 {
|
|
firstTime := 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.IsEmpty() {
|
|
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(ctx, exchange.RestFutures, http.MethodPost, fOrderHistory, nil, req, &resp)
|
|
}
|
|
|
|
// FTradeHistory gets trade history data for futures
|
|
func (h *HUOBI) FTradeHistory(ctx context.Context, 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.IsEmpty() {
|
|
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(ctx, exchange.RestFutures, http.MethodPost, fMatchResult, nil, req, &resp)
|
|
}
|
|
|
|
// FPlaceTriggerOrder places a trigger order for futures
|
|
func (h *HUOBI) FPlaceTriggerOrder(ctx context.Context, 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.StringDataCompareInsensitive(validContractTypes, contractType) {
|
|
return resp, fmt.Errorf("invalid contractType: %s", contractType)
|
|
}
|
|
req["contract_type"] = contractType
|
|
}
|
|
if !contractCode.IsEmpty() {
|
|
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.StringDataCompareInsensitive(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.StringDataCompareInsensitive(validOrderPriceType, orderPriceType) {
|
|
return resp, fmt.Errorf("invalid order price type")
|
|
}
|
|
req["order_price_type"] = orderPriceType
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fTriggerOrder, nil, req, &resp)
|
|
}
|
|
|
|
// FCancelTriggerOrder cancels trigger order for futures
|
|
func (h *HUOBI) FCancelTriggerOrder(ctx context.Context, symbol, orderID string) (FCancelOrderData, error) {
|
|
var resp FCancelOrderData
|
|
req := make(map[string]interface{})
|
|
req["symbol"] = symbol
|
|
req["order_id"] = orderID
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fCancelTriggerOrder, nil, req, &resp)
|
|
}
|
|
|
|
// FCancelAllTriggerOrders cancels all trigger order for futures
|
|
func (h *HUOBI) FCancelAllTriggerOrders(ctx context.Context, contractCode currency.Pair, symbol, contractType string) (FCancelOrderData, error) {
|
|
var resp FCancelOrderData
|
|
req := make(map[string]interface{})
|
|
req["symbol"] = symbol
|
|
if !contractCode.IsEmpty() {
|
|
codeValue, err := h.FormatSymbol(contractCode, asset.Futures)
|
|
if err != nil {
|
|
return resp, err
|
|
}
|
|
req["contract_code"] = codeValue
|
|
}
|
|
if contractType != "" {
|
|
if !common.StringDataCompareInsensitive(validContractTypes, contractType) {
|
|
return resp, nil
|
|
}
|
|
req["contract_type"] = contractType
|
|
}
|
|
return resp, h.FuturesAuthenticatedHTTPRequest(ctx, exchange.RestFutures, http.MethodPost, fCancelAllTriggerOrders, nil, req, &resp)
|
|
}
|
|
|
|
// FQueryTriggerOpenOrders queries open trigger orders for futures
|
|
func (h *HUOBI) FQueryTriggerOpenOrders(ctx context.Context, contractCode currency.Pair, symbol string, pageIndex, pageSize int64) (FTriggerOpenOrders, error) {
|
|
var resp FTriggerOpenOrders
|
|
req := make(map[string]interface{})
|
|
req["symbol"] = symbol
|
|
if !contractCode.IsEmpty() {
|
|
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(ctx, exchange.RestFutures, http.MethodPost, fTriggerOpenOrders, nil, req, &resp)
|
|
}
|
|
|
|
// FQueryTriggerOrderHistory queries trigger order history for futures
|
|
func (h *HUOBI) FQueryTriggerOrderHistory(ctx context.Context, 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.IsEmpty() {
|
|
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(ctx, exchange.RestFutures, http.MethodPost, fTriggerOrderHistory, nil, req, &resp)
|
|
}
|
|
|
|
// FuturesAuthenticatedHTTPRequest sends authenticated requests to the HUOBI API
|
|
func (h *HUOBI) FuturesAuthenticatedHTTPRequest(ctx context.Context, ep exchange.URL, method, endpoint string, values url.Values, data, result interface{}) error {
|
|
creds, err := h.GetCredentials(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ePoint, err := h.API.Endpoints.GetURL(ep)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if ep == exchange.RestFutures && ePoint[len(ePoint)-1] == '/' {
|
|
// prevent signature errors for non-standard paths until we can
|
|
// have a method to force update endpoints
|
|
ePoint = ePoint[:len(ePoint)-1]
|
|
}
|
|
if values == nil {
|
|
values = url.Values{}
|
|
}
|
|
|
|
var tempResp json.RawMessage
|
|
newRequest := func() (*request.Item, error) {
|
|
values.Set("AccessKeyId", creds.Key)
|
|
values.Set("SignatureMethod", "HmacSHA256")
|
|
values.Set("SignatureVersion", "2")
|
|
values.Set("Timestamp", time.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"
|
|
}
|
|
|
|
var hmac []byte
|
|
hmac, err = crypto.GetHMAC(crypto.HashSHA256,
|
|
[]byte(sigPath),
|
|
[]byte(creds.Secret))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
values.Add("Signature", crypto.Base64Encode(hmac))
|
|
urlPath := common.EncodeURLValues(ePoint+endpoint, values)
|
|
var body io.Reader
|
|
var payload []byte
|
|
if data != nil {
|
|
payload, err = json.Marshal(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
body = bytes.NewBuffer(payload)
|
|
}
|
|
|
|
return &request.Item{
|
|
Method: method,
|
|
Path: urlPath,
|
|
Headers: headers,
|
|
Body: body,
|
|
Result: &tempResp,
|
|
AuthRequest: true,
|
|
Verbose: h.Verbose,
|
|
HTTPDebugging: h.HTTPDebugging,
|
|
HTTPRecording: h.HTTPRecording,
|
|
}, nil
|
|
}
|
|
|
|
err = h.SendPayload(ctx, request.Unset, newRequest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var errCap errorCapture
|
|
if err := json.Unmarshal(tempResp, &errCap); err == nil {
|
|
if errCap.ErrMsgType1 != "" {
|
|
return fmt.Errorf("error code: %v error message: %s", errCap.CodeType1,
|
|
errors.New(errCap.ErrMsgType1))
|
|
}
|
|
if errCap.ErrMsgType2 != "" {
|
|
return fmt.Errorf("error code: %v error message: %s", errCap.CodeType2,
|
|
errors.New(errCap.ErrMsgType2))
|
|
}
|
|
}
|
|
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
|
|
}
|
|
|
|
// formatFuturesPair handles pairs in the format as "BTC-NW" and "BTC210827"
|
|
func (h *HUOBI) formatFuturesPair(p currency.Pair) (string, error) {
|
|
if common.StringDataCompareInsensitive(validContractShortTypes, p.Quote.String()) {
|
|
return p.Format(currency.PairFormat{Delimiter: "_", Uppercase: true}).String(), nil
|
|
}
|
|
return h.FormatSymbol(p, asset.Futures)
|
|
}
|