mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-24 15:10:19 +00:00
* Initial codes for a trade tracker * Moving everything in a broken fashion * Removes tradetracker. Removes some errors for subsystems * Cleans up some subsystems, renames stuttering types. Removes some global Bot usage * More basic subsystem renaming and file moving * Removes engine dependency from events,ntpserver,ordermanager,comms manager * Exports eventManager, fixes rpcserver. puts rpcserver back for now * Removes redundant error message, further removes engine dependencies * experimental end of day interface usage * adds ability to build the application * Withdraw and event manager handling * cleans up apiserver and communications manager * Cleans up some start/setup processes. Though should separate * More consistency with Setup Start Stop IsRunning funcs * Final consistency pass before testing phase * Fixes engine tests. Fixes stop nil issue * api server tests * Communications manager testing * Connection manager tests and nilsubsystem error * End of day currencypairsyncer tests * Adds databaseconnection/databaseconnection_test.go * Adds withdrawal manager tests * Deposit address testing. Moved orderbook sync first as its more important * Adds test for event manager * More full eventmanager testing * Adds testfile. Enables skipped test. * ntp manager tests * Adds ordermanager tests, Extracts a whole new subsystem from engine and fanangles import cycles * Adds websocket routine manager tests * Basic portfolio manager testing * Fixes issue with currency pair sync startup * Fixes issue with event manager startup * Starts the order manager before backtester starts * Fixes fee tests. Expands testing. Doesnt fix races * Fixes most test races * Resolves data races * Fixes subsystem test issues * currency pair syncer coverage tests * Refactors portfolio. Fixes tests. Withdraw validation Portfolio didn't need to exist with a portfolio manager. Now the porfolio manager is in charge how the portfolio is handled and all portfolio functions are attached to the base instead of just exported at the package level Withdrawal validation occurred at the exchange level when it can just be run at the withdrawal manager level. All withdrawal requests go through that endpoint * lint -fix * golang lint fixes * lints and comments everything * Updates GCT logo, adds documentation for some subsystems * More documentation and more logo updates * Fixes backtesting and apiserver errors encountered * Fixes errors and typos from reviewing * More minor fixes * Changes %h verb to %w * reverbs to %s * Humbly begins reverting to more flat engine package The main reasoning for this is that the subsystem split doesn't make sense in a golang environment. The subsystems are only meant to be used with engine and so by placing them in a non-engine area, it does not work and is inconsistent with the rest of the application's package layout. This will begin salvaging the changes made by reverting to a flat engine package, but maintaining the consistent designs introduced. Further, I will look to remove any TestMains and decrease the scope of testing to be more local and decrease the issues that have been caused from our style of testing. * Manages to re-flatten things. Everything is within its own file * mini fixes * Fixes tests and data races and lints * Updates docs tool for engine to create filename readmes * os -> ioutil * remove err * Appveyor version increase test * Removes tCleanup as its unsupported on appveyor * Adds stuff that I thought was in previous merge master commit * Removes cancel from test * Fixes really fun test-exclusive data race * minor nit fixes * niterinos * docs gen * rm;rf test * Remove typoline. expands startstop helper. Splits apiserver * Removes accidental folder * Uses update instead of replace for order upsert * addresses nits. Renames files. Regenerates documentation. * lint and removal of comments * Add new test for default scenario * Fixes typo * regen docs
1183 lines
41 KiB
Go
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("%s %w", h.Name, exchange.ErrAuthenticatedRequestWithoutCredentialsSet)
|
|
}
|
|
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
|
|
}
|