mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
* order/gateio: update USDT margined futures pathway for cancel all orders and drop count field * gateio: add and expand tests for CancelAllOrders and getExchangeSide * Add test for load * linter: fix * Update exchanges/kraken/kraken_wrapper.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * glorious: nits * Update exchanges/order/orders.go Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> * gk: nits * glorious: nits * reverted change for options * Update exchanges/gateio/gateio_wrapper_test.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * Update exchanges/gateio/gateio_wrapper.go Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> * Update exchanges/deribit/deribit_wrapper.go Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> * Update exchanges/gateio/gateio_wrapper_test.go Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> * Update exchanges/gateio/gateio_wrapper_test.go Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> * Add consts for cancel side references * Update exchanges/gateio/gateio_wrapper.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * Update exchanges/gateio/gateio_wrapper.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * Update exchanges/gateio/gateio_websocket_request_futures.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * thrasher-: nits --------- Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io> Co-authored-by: Scott <gloriousCode@users.noreply.github.com> Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
3628 lines
155 KiB
Go
3628 lines
155 KiB
Go
package gateio
|
|
|
|
import (
|
|
"context"
|
|
"crypto/hmac"
|
|
"crypto/sha512"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/thrasher-corp/gocryptotrader/common"
|
|
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
"github.com/thrasher-corp/gocryptotrader/encoding/json"
|
|
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
|
"github.com/thrasher-corp/gocryptotrader/types"
|
|
)
|
|
|
|
const (
|
|
gateioTradeURL = "https://api.gateio.ws/" + gateioAPIVersion
|
|
gateioFuturesTestnetTrading = "https://fx-api-testnet.gateio.ws"
|
|
gateioFuturesLiveTradingAlternative = "https://fx-api.gateio.ws/" + gateioAPIVersion
|
|
gateioAPIVersion = "api/v4/"
|
|
tradeBaseURL = "https://www.gate.io/"
|
|
|
|
// SubAccount Endpoints
|
|
subAccounts = "sub_accounts"
|
|
|
|
// Spot
|
|
gateioSpotCurrencies = "spot/currencies"
|
|
gateioSpotCurrencyPairs = "spot/currency_pairs"
|
|
gateioSpotTickers = "spot/tickers"
|
|
gateioSpotOrderbook = "spot/order_book"
|
|
gateioSpotMarketTrades = "spot/trades"
|
|
gateioSpotCandlesticks = "spot/candlesticks"
|
|
gateioSpotFeeRate = "spot/fee"
|
|
gateioSpotAccounts = "spot/accounts"
|
|
gateioUnifiedAccounts = "unified/accounts"
|
|
gateioSpotBatchOrders = "spot/batch_orders"
|
|
gateioSpotOpenOrders = "spot/open_orders"
|
|
gateioSpotClosePositionWhenCrossCurrencyDisabledPath = "spot/cross_liquidate_orders"
|
|
gateioSpotOrders = "spot/orders"
|
|
gateioSpotCancelBatchOrders = "spot/cancel_batch_orders"
|
|
gateioSpotMyTrades = "spot/my_trades"
|
|
gateioSpotServerTime = "spot/time"
|
|
gateioSpotAllCountdown = "spot/countdown_cancel_all"
|
|
gateioSpotPriceOrders = "spot/price_orders"
|
|
|
|
// Wallets
|
|
walletCurrencyChain = "wallet/currency_chains"
|
|
walletDepositAddress = "wallet/deposit_address"
|
|
walletWithdrawals = "wallet/withdrawals"
|
|
walletDeposits = "wallet/deposits"
|
|
walletTransfer = "wallet/transfers"
|
|
walletSubAccountTransfer = "wallet/sub_account_transfers"
|
|
walletInterSubAccountTransfer = "wallet/sub_account_to_sub_account"
|
|
walletWithdrawStatus = "wallet/withdraw_status"
|
|
walletSubAccountBalance = "wallet/sub_account_balances"
|
|
walletSubAccountMarginBalance = "wallet/sub_account_margin_balances"
|
|
walletSubAccountFuturesBalance = "wallet/sub_account_futures_balances"
|
|
walletSubAccountCrossMarginBalances = "wallet/sub_account_cross_margin_balances"
|
|
walletSavedAddress = "wallet/saved_address"
|
|
walletTradingFee = "wallet/fee"
|
|
walletTotalBalance = "wallet/total_balance"
|
|
|
|
// Margin
|
|
gateioMarginCurrencyPairs = "margin/currency_pairs"
|
|
gateioMarginFundingBook = "margin/funding_book"
|
|
gateioMarginAccount = "margin/accounts"
|
|
gateioMarginAccountBook = "margin/account_book"
|
|
gateioMarginFundingAccounts = "margin/funding_accounts"
|
|
gateioMarginLoans = "margin/loans"
|
|
gateioMarginMergedLoans = "margin/merged_loans"
|
|
gateioMarginLoanRecords = "margin/loan_records"
|
|
gateioMarginAutoRepay = "margin/auto_repay"
|
|
gateioMarginTransfer = "margin/transferable"
|
|
gateioMarginBorrowable = "margin/borrowable"
|
|
gateioCrossMarginCurrencies = "margin/cross/currencies"
|
|
gateioCrossMarginAccounts = "margin/cross/accounts"
|
|
gateioCrossMarginAccountBook = "margin/cross/account_book"
|
|
gateioCrossMarginLoans = "margin/cross/loans"
|
|
gateioCrossMarginRepayments = "margin/cross/repayments"
|
|
gateioCrossMarginTransferable = "margin/cross/transferable"
|
|
gateioCrossMarginBorrowable = "margin/cross/borrowable"
|
|
|
|
// Options
|
|
gateioOptionUnderlyings = "options/underlyings"
|
|
gateioOptionExpiration = "options/expirations"
|
|
gateioOptionContracts = "options/contracts"
|
|
gateioOptionSettlement = "options/settlements"
|
|
gateioOptionMySettlements = "options/my_settlements"
|
|
gateioOptionsOrderbook = "options/order_book"
|
|
gateioOptionsTickers = "options/tickers"
|
|
gateioOptionCandlesticks = "options/candlesticks"
|
|
gateioOptionUnderlyingCandlesticks = "options/underlying/candlesticks"
|
|
gateioOptionsTrades = "options/trades"
|
|
gateioOptionAccounts = "options/accounts"
|
|
gateioOptionsAccountbook = "options/account_book"
|
|
gateioOptionsPosition = "options/positions"
|
|
gateioOptionsPositionClose = "options/position_close"
|
|
gateioOptionsOrders = "options/orders"
|
|
gateioOptionsMyTrades = "options/my_trades"
|
|
|
|
// Flash Swap
|
|
gateioFlashSwapCurrencies = "flash_swap/currencies"
|
|
gateioFlashSwapOrders = "flash_swap/orders"
|
|
gateioFlashSwapOrdersPreview = "flash_swap/orders/preview"
|
|
|
|
futuresPath = "futures/"
|
|
deliveryPath = "delivery/"
|
|
ordersPath = "/orders"
|
|
positionsPath = "/positions/"
|
|
subAccountsPath = "sub_accounts/"
|
|
priceOrdersPaths = "/price_orders"
|
|
|
|
// Withdrawals
|
|
withdrawal = "withdrawals"
|
|
)
|
|
|
|
const (
|
|
utc0TimeZone = "utc0"
|
|
utc8TimeZone = "utc8"
|
|
)
|
|
|
|
var (
|
|
errEmptyOrInvalidSettlementCurrency = errors.New("empty or invalid settlement currency")
|
|
errInvalidOrMissingContractParam = errors.New("invalid or empty contract")
|
|
errNoValidResponseFromServer = errors.New("no valid response from server")
|
|
errInvalidUnderlying = errors.New("missing underlying")
|
|
errInvalidOrderSize = errors.New("invalid order size")
|
|
errInvalidOrderID = errors.New("invalid order id")
|
|
errInvalidAmount = errors.New("invalid amount")
|
|
errInvalidSubAccount = errors.New("invalid or empty subaccount")
|
|
errInvalidTransferDirection = errors.New("invalid transfer direction")
|
|
errDifferentAccount = errors.New("account type must be identical for all orders")
|
|
errInvalidPrice = errors.New("invalid price")
|
|
errNoValidParameterPassed = errors.New("no valid parameter passed")
|
|
errInvalidCountdown = errors.New("invalid countdown, Countdown time, in seconds At least 5 seconds, 0 means cancel the countdown")
|
|
errInvalidOrderStatus = errors.New("invalid order status")
|
|
errInvalidLoanSide = errors.New("invalid loan side, only 'lend' and 'borrow'")
|
|
errInvalidLoanID = errors.New("missing loan ID")
|
|
errInvalidRepayMode = errors.New("invalid repay mode specified, must be 'all' or 'partial'")
|
|
errMissingPreviewID = errors.New("missing required parameter: preview_id")
|
|
errChangeHasToBePositive = errors.New("change has to be positive")
|
|
errInvalidLeverage = errors.New("invalid leverage value")
|
|
errInvalidRiskLimit = errors.New("new position risk limit")
|
|
errInvalidAutoSize = errors.New("invalid autoSize")
|
|
errTooManyOrderRequest = errors.New("too many order creation request")
|
|
errInvalidTimeout = errors.New("invalid timeout, should be in seconds At least 5 seconds, 0 means cancel the countdown")
|
|
errNoTickerData = errors.New("no ticker data available")
|
|
errNilArgument = errors.New("null argument")
|
|
errInvalidTimezone = errors.New("invalid timezone")
|
|
errMultipleOrders = errors.New("multiple orders passed")
|
|
errMissingWithdrawalID = errors.New("missing withdrawal ID")
|
|
errInvalidSubAccountUserID = errors.New("sub-account user id is required")
|
|
errInvalidSettlementQuote = errors.New("symbol quote currency does not match asset settlement currency")
|
|
errInvalidSettlementBase = errors.New("symbol base currency does not match asset settlement currency")
|
|
errMissingAPIKey = errors.New("missing API key information")
|
|
errInvalidTextPrefix = errors.New("invalid text value, requires prefix `t-`")
|
|
errSingleAssetRequired = errors.New("single asset type required")
|
|
)
|
|
|
|
// validTimesInForce holds a list of supported time-in-force values and corresponding string representations.
|
|
// slice iteration outperforms map with this few elements
|
|
var validTimesInForce = []struct {
|
|
String string
|
|
TimeInForce order.TimeInForce
|
|
}{
|
|
{gtcTIF, order.GoodTillCancel}, {iocTIF, order.ImmediateOrCancel}, {pocTIF, order.PostOnly}, {fokTIF, order.FillOrKill},
|
|
}
|
|
|
|
func timeInForceFromString(tif string) (order.TimeInForce, error) {
|
|
for a := range validTimesInForce {
|
|
if validTimesInForce[a].String == tif {
|
|
return validTimesInForce[a].TimeInForce, nil
|
|
}
|
|
}
|
|
return order.UnknownTIF, fmt.Errorf("%w: %q", order.ErrUnsupportedTimeInForce, tif)
|
|
}
|
|
|
|
// Exchange implements exchange.IBotExchange and contains additional specific api methods for interacting with GateIO
|
|
type Exchange struct {
|
|
exchange.Base
|
|
|
|
messageIDSeq common.Counter
|
|
wsOBUpdateMgr *wsOBUpdateManager
|
|
}
|
|
|
|
// ***************************************** SubAccounts ********************************
|
|
|
|
// CreateNewSubAccount creates a new sub-account
|
|
func (e *Exchange) CreateNewSubAccount(ctx context.Context, arg SubAccountParams) (*SubAccount, error) {
|
|
if arg.LoginName == "" {
|
|
return nil, errors.New("login name can not be empty")
|
|
}
|
|
var response *SubAccount
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, subAccountEPL, http.MethodPost, subAccounts, nil, &arg, &response)
|
|
}
|
|
|
|
// GetSubAccounts retrieves list of sub-accounts for given account
|
|
func (e *Exchange) GetSubAccounts(ctx context.Context) ([]SubAccount, error) {
|
|
var response []SubAccount
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, subAccountEPL, http.MethodGet, subAccounts, nil, nil, &response)
|
|
}
|
|
|
|
// GetSingleSubAccount retrieves a single sub-account for given account
|
|
func (e *Exchange) GetSingleSubAccount(ctx context.Context, userID string) (*SubAccount, error) {
|
|
if userID == "" {
|
|
return nil, errors.New("user ID can not be empty")
|
|
}
|
|
var response *SubAccount
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, subAccountEPL, http.MethodGet, subAccounts+"/"+userID, nil, nil, &response)
|
|
}
|
|
|
|
// CreateAPIKeysOfSubAccount creates a sub-account for the sub-account
|
|
//
|
|
// name: Permission name (all permissions will be removed if no value is passed)
|
|
// >> wallet: wallet, spot: spot/margin, futures: perpetual contract, delivery: delivery, earn: earn, options: options
|
|
func (e *Exchange) CreateAPIKeysOfSubAccount(ctx context.Context, arg CreateAPIKeySubAccountParams) (*CreateAPIKeyResponse, error) {
|
|
if arg.SubAccountUserID == 0 {
|
|
return nil, errInvalidSubAccountUserID
|
|
}
|
|
if arg.Body == nil {
|
|
return nil, errors.New("sub-account key information is required")
|
|
}
|
|
var resp *CreateAPIKeyResponse
|
|
return resp, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, subAccountEPL, http.MethodPost, subAccountsPath+strconv.FormatInt(arg.SubAccountUserID, 10)+"/keys", nil, &arg, &resp)
|
|
}
|
|
|
|
// GetAllAPIKeyOfSubAccount list all API Key of the sub-account
|
|
func (e *Exchange) GetAllAPIKeyOfSubAccount(ctx context.Context, userID int64) ([]CreateAPIKeyResponse, error) {
|
|
var resp []CreateAPIKeyResponse
|
|
return resp, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, subAccountEPL, http.MethodGet, subAccountsPath+strconv.FormatInt(userID, 10)+"/keys", nil, nil, &resp)
|
|
}
|
|
|
|
// UpdateAPIKeyOfSubAccount update API key of the sub-account
|
|
func (e *Exchange) UpdateAPIKeyOfSubAccount(ctx context.Context, subAccountAPIKey string, arg CreateAPIKeySubAccountParams) error {
|
|
return e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, subAccountEPL, http.MethodPut, subAccountsPath+strconv.FormatInt(arg.SubAccountUserID, 10)+"/keys/"+subAccountAPIKey, nil, &arg, nil)
|
|
}
|
|
|
|
// GetAPIKeyOfSubAccount retrieves the API Key of the sub-account
|
|
func (e *Exchange) GetAPIKeyOfSubAccount(ctx context.Context, subAccountUserID int64, apiKey string) (*CreateAPIKeyResponse, error) {
|
|
if subAccountUserID == 0 {
|
|
return nil, errInvalidSubAccountUserID
|
|
}
|
|
if apiKey == "" {
|
|
return nil, errMissingAPIKey
|
|
}
|
|
var resp *CreateAPIKeyResponse
|
|
return resp, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, subAccountEPL, http.MethodGet, subAccountsPath+strconv.FormatInt(subAccountUserID, 10)+"/keys/"+apiKey, nil, nil, &resp)
|
|
}
|
|
|
|
// LockSubAccount locks the sub-account
|
|
func (e *Exchange) LockSubAccount(ctx context.Context, subAccountUserID int64) error {
|
|
if subAccountUserID == 0 {
|
|
return errInvalidSubAccountUserID
|
|
}
|
|
return e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, subAccountEPL, http.MethodPost, subAccountsPath+strconv.FormatInt(subAccountUserID, 10)+"/lock", nil, nil, nil)
|
|
}
|
|
|
|
// UnlockSubAccount locks the sub-account
|
|
func (e *Exchange) UnlockSubAccount(ctx context.Context, subAccountUserID int64) error {
|
|
if subAccountUserID == 0 {
|
|
return errInvalidSubAccountUserID
|
|
}
|
|
return e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, subAccountEPL, http.MethodPost, subAccountsPath+strconv.FormatInt(subAccountUserID, 10)+"/unlock", nil, nil, nil)
|
|
}
|
|
|
|
// ***************************************** Spot **************************************
|
|
|
|
// ListSpotCurrencies to retrieve detailed list of each currency.
|
|
func (e *Exchange) ListSpotCurrencies(ctx context.Context) ([]CurrencyInfo, error) {
|
|
var resp []CurrencyInfo
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, publicCurrenciesSpotEPL, gateioSpotCurrencies, &resp)
|
|
}
|
|
|
|
// GetCurrencyDetail details of a specific currency.
|
|
func (e *Exchange) GetCurrencyDetail(ctx context.Context, ccy currency.Code) (*CurrencyInfo, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
var resp *CurrencyInfo
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, publicCurrenciesSpotEPL, gateioSpotCurrencies+"/"+ccy.String(), &resp)
|
|
}
|
|
|
|
// ListSpotCurrencyPairs retrieve all currency pairs supported by the exchange.
|
|
func (e *Exchange) ListSpotCurrencyPairs(ctx context.Context) ([]CurrencyPairDetail, error) {
|
|
var resp []CurrencyPairDetail
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, publicListCurrencyPairsSpotEPL, gateioSpotCurrencyPairs, &resp)
|
|
}
|
|
|
|
// GetCurrencyPairDetail to get details of a specific order for spot/margin accounts.
|
|
func (e *Exchange) GetCurrencyPairDetail(ctx context.Context, currencyPair string) (*CurrencyPairDetail, error) {
|
|
if currencyPair == "" {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
var resp *CurrencyPairDetail
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, publicCurrencyPairDetailSpotEPL, gateioSpotCurrencyPairs+"/"+currencyPair, &resp)
|
|
}
|
|
|
|
// GetTickers retrieve ticker information
|
|
// Return only related data if currency_pair is specified; otherwise return all of them
|
|
func (e *Exchange) GetTickers(ctx context.Context, currencyPair, timezone string) ([]Ticker, error) {
|
|
params := url.Values{}
|
|
if currencyPair != "" {
|
|
params.Set("currency_pair", currencyPair)
|
|
}
|
|
if timezone != "" && timezone != utc8TimeZone && timezone != utc0TimeZone {
|
|
return nil, errInvalidTimezone
|
|
} else if timezone != "" {
|
|
params.Set("timezone", timezone)
|
|
}
|
|
var tickers []Ticker
|
|
return tickers, e.SendHTTPRequest(ctx, exchange.RestSpot, publicTickersSpotEPL, common.EncodeURLValues(gateioSpotTickers, params), &tickers)
|
|
}
|
|
|
|
// GetTicker retrieves a single ticker information for a currency pair.
|
|
func (e *Exchange) GetTicker(ctx context.Context, currencyPair, timezone string) (*Ticker, error) {
|
|
if currencyPair == "" {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
tickers, err := e.GetTickers(ctx, currencyPair, timezone)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(tickers) > 0 {
|
|
return &tickers[0], err
|
|
}
|
|
return nil, fmt.Errorf("no ticker data found for currency pair %v", currencyPair)
|
|
}
|
|
|
|
// getIntervalString returns a string representation of the interval according to the Gateio exchange representation
|
|
func getIntervalString(interval kline.Interval) (string, error) {
|
|
switch interval {
|
|
case kline.ThousandMilliseconds:
|
|
return "1000ms", nil
|
|
case kline.OneDay:
|
|
return "1d", nil
|
|
case kline.SevenDay:
|
|
return "7d", nil
|
|
case kline.OneMonth:
|
|
return "30d", nil
|
|
case kline.TenMilliseconds, kline.TwentyMilliseconds, kline.HundredMilliseconds, kline.TwoHundredAndFiftyMilliseconds,
|
|
kline.TenSecond, kline.ThirtySecond, kline.OneMin, kline.FiveMin, kline.FifteenMin, kline.ThirtyMin,
|
|
kline.OneHour, kline.TwoHour, kline.FourHour, kline.EightHour, kline.TwelveHour:
|
|
return interval.Short(), nil
|
|
default:
|
|
return "", fmt.Errorf("%q: %w", interval.String(), kline.ErrUnsupportedInterval)
|
|
}
|
|
}
|
|
|
|
// GetIntervalFromString returns a kline.Interval representation of the interval string
|
|
func (e *Exchange) GetIntervalFromString(interval string) (kline.Interval, error) {
|
|
switch interval {
|
|
case "10s":
|
|
return kline.TenSecond, nil
|
|
case "30s":
|
|
return kline.ThirtySecond, nil
|
|
case "1m":
|
|
return kline.OneMin, nil
|
|
case "5m":
|
|
return kline.FiveMin, nil
|
|
case "15m":
|
|
return kline.FifteenMin, nil
|
|
case "30m":
|
|
return kline.ThirtyMin, nil
|
|
case "1h":
|
|
return kline.OneHour, nil
|
|
case "2h":
|
|
return kline.TwoHour, nil
|
|
case "4h":
|
|
return kline.FourHour, nil
|
|
case "8h":
|
|
return kline.EightHour, nil
|
|
case "12h":
|
|
return kline.TwelveHour, nil
|
|
case "1d":
|
|
return kline.OneDay, nil
|
|
case "7d":
|
|
return kline.SevenDay, nil
|
|
case "30d":
|
|
return kline.OneMonth, nil
|
|
case "100ms":
|
|
return kline.HundredMilliseconds, nil
|
|
case "1000ms":
|
|
return kline.ThousandMilliseconds, nil
|
|
default:
|
|
return kline.Interval(0), kline.ErrInvalidInterval
|
|
}
|
|
}
|
|
|
|
// GetOrderbook returns the orderbook data for a suppled currency pair
|
|
func (e *Exchange) GetOrderbook(ctx context.Context, pairString, interval string, limit uint64, withOrderbookID bool) (*Orderbook, error) {
|
|
if pairString == "" {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency_pair", pairString)
|
|
if interval != "" {
|
|
params.Set("interval", interval)
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
params.Set("with_id", strconv.FormatBool(withOrderbookID))
|
|
var response *OrderbookData
|
|
if err := e.SendHTTPRequest(ctx, exchange.RestSpot, publicOrderbookSpotEPL, common.EncodeURLValues(gateioSpotOrderbook, params), &response); err != nil {
|
|
return nil, err
|
|
}
|
|
return response.MakeOrderbook(), nil
|
|
}
|
|
|
|
// GetMarketTrades retrieve market trades
|
|
func (e *Exchange) GetMarketTrades(ctx context.Context, pairString currency.Pair, limit uint64, lastID string, reverse bool, from, to time.Time, page uint64) ([]Trade, error) {
|
|
params := url.Values{}
|
|
if pairString.IsEmpty() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params.Set("currency_pair", pairString.String())
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if lastID != "" {
|
|
params.Set("last_id", lastID)
|
|
}
|
|
if reverse {
|
|
params.Set("reverse", strconv.FormatBool(reverse))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
if page != 0 {
|
|
params.Set("page", strconv.FormatUint(page, 10))
|
|
}
|
|
var response []Trade
|
|
return response, e.SendHTTPRequest(ctx, exchange.RestSpot, publicMarketTradesSpotEPL, common.EncodeURLValues(gateioSpotMarketTrades, params), &response)
|
|
}
|
|
|
|
// GetCandlesticks retrieves market candlesticks.
|
|
func (e *Exchange) GetCandlesticks(ctx context.Context, currencyPair currency.Pair, limit uint64, from, to time.Time, interval kline.Interval) ([]Candlestick, error) {
|
|
params := url.Values{}
|
|
if currencyPair.IsEmpty() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params.Set("currency_pair", currencyPair.String())
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var err error
|
|
if interval.Duration().Microseconds() != 0 {
|
|
var intervalString string
|
|
intervalString, err = getIntervalString(interval)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("interval", intervalString)
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
var candles []Candlestick
|
|
return candles, e.SendHTTPRequest(ctx, exchange.RestSpot, publicCandleStickSpotEPL, common.EncodeURLValues(gateioSpotCandlesticks, params), &candles)
|
|
}
|
|
|
|
// GetTradingFeeRatio retrieves user trading fee rates
|
|
func (e *Exchange) GetTradingFeeRatio(ctx context.Context, currencyPair currency.Pair) (*SpotTradingFeeRate, error) {
|
|
params := url.Values{}
|
|
if currencyPair.IsPopulated() {
|
|
// specify a currency pair to retrieve precise fee rate
|
|
params.Set("currency_pair", currencyPair.String())
|
|
}
|
|
var response *SpotTradingFeeRate
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotTradingFeeEPL, http.MethodGet, gateioSpotFeeRate, params, nil, &response)
|
|
}
|
|
|
|
// GetSpotAccounts retrieves spot account.
|
|
func (e *Exchange) GetSpotAccounts(ctx context.Context, ccy currency.Code) ([]SpotAccount, error) {
|
|
params := url.Values{}
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
var response []SpotAccount
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotAccountsEPL, http.MethodGet, gateioSpotAccounts, params, nil, &response)
|
|
}
|
|
|
|
// GetUnifiedAccount retrieves unified account.
|
|
func (e *Exchange) GetUnifiedAccount(ctx context.Context, ccy currency.Code) (*UnifiedUserAccount, error) {
|
|
params := url.Values{}
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
var response UnifiedUserAccount
|
|
return &response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, privateUnifiedSpotEPL, http.MethodGet, gateioUnifiedAccounts, params, nil, &response)
|
|
}
|
|
|
|
// CreateBatchOrders Create a batch of orders Batch orders requirements: custom order field text is required At most 4 currency pairs,
|
|
// maximum 10 orders each, are allowed in one request No mixture of spot orders and margin orders, i.e. account must be identical for all orders
|
|
func (e *Exchange) CreateBatchOrders(ctx context.Context, args []CreateOrderRequest) ([]SpotOrder, error) {
|
|
if len(args) > 10 {
|
|
return nil, fmt.Errorf("%w only 10 orders are canceled at once", errMultipleOrders)
|
|
}
|
|
for x := range args {
|
|
if (x != 0) && args[x-1].Account != args[x].Account {
|
|
return nil, errDifferentAccount
|
|
}
|
|
if args[x].CurrencyPair.IsEmpty() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
if args[x].Type != "limit" {
|
|
return nil, errors.New("only order type limit is allowed")
|
|
}
|
|
args[x].Side = strings.ToLower(args[x].Side)
|
|
if args[x].Side != "buy" && args[x].Side != "sell" {
|
|
return nil, order.ErrSideIsInvalid
|
|
}
|
|
if !strings.EqualFold(args[x].Account, asset.Spot.String()) &&
|
|
!strings.EqualFold(args[x].Account, asset.CrossMargin.String()) &&
|
|
!strings.EqualFold(args[x].Account, asset.Margin.String()) {
|
|
return nil, errors.New("only spot, margin, and cross_margin area allowed")
|
|
}
|
|
if args[x].Amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
if args[x].Price <= 0 {
|
|
return nil, errInvalidPrice
|
|
}
|
|
}
|
|
var response []SpotOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotBatchOrdersEPL, http.MethodPost, gateioSpotBatchOrders, nil, &args, &response)
|
|
}
|
|
|
|
// GetSpotOpenOrders retrieves all open orders
|
|
// List open orders in all currency pairs.
|
|
// Note that pagination parameters affect record number in each currency pair's open order list. No pagination is applied to the number of currency pairs returned. All currency pairs with open orders will be returned.
|
|
// Spot and margin orders are returned by default. To list cross margin orders, account must be set to cross_margin
|
|
func (e *Exchange) GetSpotOpenOrders(ctx context.Context, page, limit uint64, isCrossMargin bool) ([]SpotOrdersDetail, error) {
|
|
params := url.Values{}
|
|
if page > 0 {
|
|
params.Set("page", strconv.FormatUint(page, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if isCrossMargin {
|
|
params.Set("account", asset.CrossMargin.String())
|
|
}
|
|
var response []SpotOrdersDetail
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotGetOpenOrdersEPL, http.MethodGet, gateioSpotOpenOrders, params, nil, &response)
|
|
}
|
|
|
|
// SpotClosePositionWhenCrossCurrencyDisabled set close position when cross-currency is disabled
|
|
func (e *Exchange) SpotClosePositionWhenCrossCurrencyDisabled(ctx context.Context, arg *ClosePositionRequestParam) (*SpotOrder, error) {
|
|
if arg == nil {
|
|
return nil, errNilArgument
|
|
}
|
|
if arg.CurrencyPair.IsInvalid() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
if arg.Amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
if arg.Price <= 0 {
|
|
return nil, errInvalidPrice
|
|
}
|
|
var response *SpotOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotClosePositionEPL, http.MethodPost, gateioSpotClosePositionWhenCrossCurrencyDisabledPath, nil, &arg, &response)
|
|
}
|
|
|
|
// PlaceSpotOrder creates a spot order you can place orders with spot, margin or cross margin account through setting the accountfield.
|
|
// It defaults to spot, which means spot account is used to place orders.
|
|
func (e *Exchange) PlaceSpotOrder(ctx context.Context, arg *CreateOrderRequest) (*SpotOrder, error) {
|
|
if arg == nil {
|
|
return nil, errNilArgument
|
|
}
|
|
if arg.CurrencyPair.IsInvalid() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
arg.Side = strings.ToLower(arg.Side)
|
|
if arg.Side != "buy" && arg.Side != "sell" {
|
|
return nil, order.ErrSideIsInvalid
|
|
}
|
|
if !strings.EqualFold(arg.Account, asset.Spot.String()) &&
|
|
!strings.EqualFold(arg.Account, asset.CrossMargin.String()) &&
|
|
!strings.EqualFold(arg.Account, asset.Margin.String()) {
|
|
return nil, errors.New("only 'spot', 'cross_margin', and 'margin' area allowed")
|
|
}
|
|
if arg.Amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
if arg.Price < 0 {
|
|
return nil, errInvalidPrice
|
|
}
|
|
var response *SpotOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotPlaceOrderEPL, http.MethodPost, gateioSpotOrders, nil, &arg, &response)
|
|
}
|
|
|
|
// GetSpotOrders retrieves spot orders.
|
|
func (e *Exchange) GetSpotOrders(ctx context.Context, currencyPair currency.Pair, status string, page, limit uint64) ([]SpotOrder, error) {
|
|
if currencyPair.IsEmpty() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency_pair", currencyPair.String())
|
|
if status != "" {
|
|
params.Set("status", status)
|
|
}
|
|
if page > 0 {
|
|
params.Set("page", strconv.FormatUint(page, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var response []SpotOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotGetOrdersEPL, http.MethodGet, gateioSpotOrders, params, nil, &response)
|
|
}
|
|
|
|
// CancelAllOpenOrdersSpecifiedCurrencyPair cancel all open orders in specified currency pair
|
|
func (e *Exchange) CancelAllOpenOrdersSpecifiedCurrencyPair(ctx context.Context, currencyPair currency.Pair, side order.Side, account asset.Item) ([]SpotOrder, error) {
|
|
if currencyPair.IsEmpty() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency_pair", currencyPair.String())
|
|
if side == order.Buy || side == order.Sell {
|
|
params.Set("side", strings.ToLower(side.Title()))
|
|
}
|
|
if account == asset.Spot || account == asset.Margin || account == asset.CrossMargin {
|
|
params.Set("account", account.String())
|
|
}
|
|
var response []SpotOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotCancelAllOpenOrdersEPL, http.MethodDelete, gateioSpotOrders, params, nil, &response)
|
|
}
|
|
|
|
// CancelBatchOrdersWithIDList cancels batch orders specifying the order ID and currency pair information
|
|
// Multiple currency pairs can be specified, but maximum 20 orders are allowed per request
|
|
func (e *Exchange) CancelBatchOrdersWithIDList(ctx context.Context, args []CancelOrderByIDParam) ([]CancelOrderByIDResponse, error) {
|
|
var response []CancelOrderByIDResponse
|
|
if len(args) == 0 {
|
|
return nil, errNoValidParameterPassed
|
|
} else if len(args) > 20 {
|
|
return nil, fmt.Errorf("%w maximum order size to cancel is 20", errInvalidOrderSize)
|
|
}
|
|
for x := range args {
|
|
if args[x].CurrencyPair.IsEmpty() || args[x].ID == "" {
|
|
return nil, errors.New("currency pair and order ID are required")
|
|
}
|
|
}
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotCancelBatchOrdersEPL, http.MethodPost, gateioSpotCancelBatchOrders, nil, &args, &response)
|
|
}
|
|
|
|
// GetSpotOrder retrieves a single spot order using the order id and currency pair information.
|
|
func (e *Exchange) GetSpotOrder(ctx context.Context, orderID string, currencyPair currency.Pair, account asset.Item) (*SpotOrder, error) {
|
|
if orderID == "" {
|
|
return nil, errInvalidOrderID
|
|
}
|
|
if currencyPair.IsEmpty() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency_pair", currencyPair.String())
|
|
if accountType := account.String(); accountType != "" {
|
|
params.Set("account", accountType)
|
|
}
|
|
var response *SpotOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotGetOrderEPL, http.MethodGet, gateioSpotOrders+"/"+orderID, params, nil, &response)
|
|
}
|
|
|
|
// AmendSpotOrder amend an order
|
|
// By default, the orders of spot and margin account are updated.
|
|
// If you need to modify orders of the cross-margin account, you must specify account as cross_margin.
|
|
// For portfolio margin account, only cross_margin account is supported.
|
|
func (e *Exchange) AmendSpotOrder(ctx context.Context, orderID string, currencyPair currency.Pair, isCrossMarginAccount bool, arg *PriceAndAmount) (*SpotOrder, error) {
|
|
if arg == nil {
|
|
return nil, errNilArgument
|
|
}
|
|
if orderID == "" {
|
|
return nil, errInvalidOrderID
|
|
}
|
|
if currencyPair.IsEmpty() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency_pair", currencyPair.String())
|
|
if isCrossMarginAccount {
|
|
params.Set("account", asset.CrossMargin.String())
|
|
}
|
|
if arg.Amount != 0 && arg.Price != 0 {
|
|
return nil, errors.New("only can chose one of amount or price")
|
|
}
|
|
var resp *SpotOrder
|
|
return resp, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotAmendOrderEPL, http.MethodPatch, gateioSpotOrders+"/"+orderID, params, arg, &resp)
|
|
}
|
|
|
|
// CancelSingleSpotOrder cancels a single order
|
|
// Spot and margin orders are cancelled by default.
|
|
// If trying to cancel cross margin orders or portfolio margin account are used, account must be set to cross_margin
|
|
func (e *Exchange) CancelSingleSpotOrder(ctx context.Context, orderID, currencyPair string, isCrossMarginAccount bool) (*SpotOrder, error) {
|
|
if orderID == "" {
|
|
return nil, errInvalidOrderID
|
|
}
|
|
if currencyPair == "" {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency_pair", currencyPair)
|
|
if isCrossMarginAccount {
|
|
params.Set("account", asset.CrossMargin.String())
|
|
}
|
|
var response *SpotOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotCancelSingleOrderEPL, http.MethodDelete, gateioSpotOrders+"/"+orderID, params, nil, &response)
|
|
}
|
|
|
|
// GetMySpotTradingHistory retrieves personal trading history
|
|
func (e *Exchange) GetMySpotTradingHistory(ctx context.Context, p currency.Pair, orderID string, page, limit uint64, crossMargin bool, from, to time.Time) ([]SpotPersonalTradeHistory, error) {
|
|
params := url.Values{}
|
|
if p.IsPopulated() {
|
|
params.Set("currency_pair", p.String())
|
|
}
|
|
if orderID != "" {
|
|
params.Set("order_id", orderID)
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if page > 0 {
|
|
params.Set("page", strconv.FormatUint(page, 10))
|
|
}
|
|
if crossMargin {
|
|
params.Set("account", asset.CrossMargin.String())
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() && to.After(from) {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
var response []SpotPersonalTradeHistory
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotTradingHistoryEPL, http.MethodGet, gateioSpotMyTrades, params, nil, &response)
|
|
}
|
|
|
|
// GetServerTime retrieves current server time
|
|
func (e *Exchange) GetServerTime(ctx context.Context, _ asset.Item) (time.Time, error) {
|
|
var resp struct {
|
|
ServerTime types.Time `json:"server_time"`
|
|
}
|
|
if err := e.SendHTTPRequest(ctx, exchange.RestSpot, publicGetServerTimeEPL, gateioSpotServerTime, &resp); err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
return resp.ServerTime.Time(), nil
|
|
}
|
|
|
|
// CountdownCancelorders Countdown cancel orders
|
|
// When the timeout set by the user is reached, if there is no cancel or set a new countdown, the related pending orders will be automatically cancelled.
|
|
// This endpoint can be called repeatedly to set a new countdown or cancel the countdown.
|
|
func (e *Exchange) CountdownCancelorders(ctx context.Context, arg CountdownCancelOrderParam) (*TriggerTimeResponse, error) {
|
|
if arg.Timeout <= 0 {
|
|
return nil, errInvalidCountdown
|
|
}
|
|
var response *TriggerTimeResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotCountdownCancelEPL, http.MethodPost, gateioSpotAllCountdown, nil, &arg, &response)
|
|
}
|
|
|
|
// CreatePriceTriggeredOrder create a price-triggered order
|
|
func (e *Exchange) CreatePriceTriggeredOrder(ctx context.Context, arg *PriceTriggeredOrderParam) (*OrderID, error) {
|
|
if arg == nil {
|
|
return nil, errNilArgument
|
|
}
|
|
if arg.Put.TimeInForce != gtcTIF && arg.Put.TimeInForce != iocTIF {
|
|
return nil, fmt.Errorf("%w: %q only 'gct' and 'ioc' are supported", order.ErrUnsupportedTimeInForce, arg.Put.TimeInForce)
|
|
}
|
|
if arg.Market.IsEmpty() {
|
|
return nil, fmt.Errorf("%w, %s", currency.ErrCurrencyPairEmpty, "field market is required")
|
|
}
|
|
if arg.Trigger.Price < 0 {
|
|
return nil, fmt.Errorf("%w trigger price found %f, but expected trigger_price >=0", errInvalidPrice, arg.Trigger.Price)
|
|
}
|
|
if arg.Trigger.Rule != "<=" && arg.Trigger.Rule != ">=" {
|
|
return nil, fmt.Errorf("invalid price trigger condition or rule %q but expected '>=' or '<='", arg.Trigger.Rule)
|
|
}
|
|
if arg.Trigger.Expiration <= 0 {
|
|
return nil, errors.New("invalid expiration(seconds to wait for the condition to be triggered before cancelling the order)")
|
|
}
|
|
arg.Put.Side = strings.ToLower(arg.Put.Side)
|
|
arg.Put.Type = strings.ToLower(arg.Put.Type)
|
|
if arg.Put.Type != "limit" {
|
|
return nil, errors.New("invalid order type, only order type 'limit' is allowed")
|
|
}
|
|
if arg.Put.Side != "buy" && arg.Put.Side != "sell" {
|
|
return nil, order.ErrSideIsInvalid
|
|
}
|
|
if arg.Put.Price < 0 {
|
|
return nil, fmt.Errorf("%w, %s", errInvalidPrice, "put price has to be greater than 0")
|
|
}
|
|
if arg.Put.Amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
arg.Put.Account = strings.ToLower(arg.Put.Account)
|
|
if arg.Put.Account == "" {
|
|
arg.Put.Account = "normal"
|
|
}
|
|
var response *OrderID
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotCreateTriggerOrderEPL, http.MethodPost, gateioSpotPriceOrders, nil, &arg, &response)
|
|
}
|
|
|
|
// GetPriceTriggeredOrderList retrieves price orders created with an order detail and trigger price information.
|
|
func (e *Exchange) GetPriceTriggeredOrderList(ctx context.Context, status string, market currency.Pair, account asset.Item, offset, limit uint64) ([]SpotPriceTriggeredOrder, error) {
|
|
if status != statusOpen && status != statusFinished {
|
|
return nil, fmt.Errorf("%w status %s", errInvalidOrderStatus, status)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("status", status)
|
|
if market.IsPopulated() {
|
|
params.Set("market", market.String())
|
|
}
|
|
if account == asset.CrossMargin {
|
|
params.Set("account", account.String())
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
var response []SpotPriceTriggeredOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotGetTriggerOrderListEPL, http.MethodGet, gateioSpotPriceOrders, params, nil, &response)
|
|
}
|
|
|
|
// CancelMultipleSpotOpenOrders deletes price triggered orders.
|
|
func (e *Exchange) CancelMultipleSpotOpenOrders(ctx context.Context, currencyPair currency.Pair, account asset.Item) ([]SpotPriceTriggeredOrder, error) {
|
|
params := url.Values{}
|
|
if currencyPair.IsPopulated() {
|
|
params.Set("market", currencyPair.String())
|
|
}
|
|
switch account {
|
|
case asset.Empty:
|
|
return nil, asset.ErrNotSupported
|
|
case asset.Spot:
|
|
params.Set("account", "normal")
|
|
default:
|
|
params.Set("account", account.String())
|
|
}
|
|
var response []SpotPriceTriggeredOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotCancelTriggerOrdersEPL, http.MethodDelete, gateioSpotPriceOrders, params, nil, &response)
|
|
}
|
|
|
|
// GetSinglePriceTriggeredOrder get a single order
|
|
func (e *Exchange) GetSinglePriceTriggeredOrder(ctx context.Context, orderID string) (*SpotPriceTriggeredOrder, error) {
|
|
if orderID == "" {
|
|
return nil, errInvalidOrderID
|
|
}
|
|
var response *SpotPriceTriggeredOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotGetTriggerOrderEPL, http.MethodGet, gateioSpotPriceOrders+"/"+orderID, nil, nil, &response)
|
|
}
|
|
|
|
// CancelPriceTriggeredOrder cancel a price-triggered order
|
|
func (e *Exchange) CancelPriceTriggeredOrder(ctx context.Context, orderID string) (*SpotPriceTriggeredOrder, error) {
|
|
if orderID == "" {
|
|
return nil, errInvalidOrderID
|
|
}
|
|
var response *SpotPriceTriggeredOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotCancelTriggerOrderEPL, http.MethodGet, gateioSpotPriceOrders+"/"+orderID, nil, nil, &response)
|
|
}
|
|
|
|
// GenerateSignature returns hash for authenticated requests
|
|
func (e *Exchange) GenerateSignature(secret, method, path, query string, body any, dtime time.Time) (string, error) {
|
|
rawQuery, err := url.QueryUnescape(query)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
h := sha512.New()
|
|
if body != nil {
|
|
val, err := json.Marshal(body)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
h.Write(val)
|
|
}
|
|
h.Write(nil)
|
|
hashedPayload := hex.EncodeToString(h.Sum(nil))
|
|
|
|
msg := method + "\n" + path + "\n" + rawQuery + "\n" + hashedPayload + "\n" + strconv.FormatInt(dtime.Unix(), 10)
|
|
mac := hmac.New(sha512.New, []byte(secret))
|
|
mac.Write([]byte(msg))
|
|
return hex.EncodeToString(mac.Sum(nil)), nil
|
|
}
|
|
|
|
// SendAuthenticatedHTTPRequest sends authenticated requests to the Gateio API
|
|
// To use this you must setup an APIKey and APISecret from the exchange
|
|
func (e *Exchange) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange.URL, epl request.EndpointLimit, method, endpoint string, param url.Values, data, result any) error {
|
|
creds, err := e.GetCredentials(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ePoint, err := e.API.Endpoints.GetURL(ep)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var intermediary json.RawMessage
|
|
err = e.SendPayload(ctx, epl, func() (*request.Item, error) {
|
|
headers := make(map[string]string)
|
|
urlPath := endpoint
|
|
timestamp := time.Now()
|
|
var paramValue string
|
|
if param != nil {
|
|
paramValue = param.Encode()
|
|
}
|
|
var sig string
|
|
sig, err = e.GenerateSignature(creds.Secret, method, "/"+gateioAPIVersion+endpoint, paramValue, data, timestamp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
headers["Content-Type"] = "application/json"
|
|
headers["KEY"] = creds.Key
|
|
headers["TIMESTAMP"] = strconv.FormatInt(timestamp.Unix(), 10)
|
|
headers["Accept"] = "application/json"
|
|
headers["SIGN"] = sig
|
|
urlPath = ePoint + urlPath
|
|
if param != nil {
|
|
urlPath = common.EncodeURLValues(urlPath, param)
|
|
}
|
|
var payload string
|
|
if data != nil {
|
|
var byteData []byte
|
|
byteData, err = json.Marshal(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
payload = string(byteData)
|
|
}
|
|
return &request.Item{
|
|
Method: method,
|
|
Path: urlPath,
|
|
Headers: headers,
|
|
Body: strings.NewReader(payload),
|
|
Result: &intermediary,
|
|
Verbose: e.Verbose,
|
|
HTTPDebugging: e.HTTPDebugging,
|
|
HTTPRecording: e.HTTPRecording,
|
|
HTTPMockDataSliceLimit: e.HTTPMockDataSliceLimit,
|
|
}, nil
|
|
}, request.AuthenticatedRequest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
errCap := struct {
|
|
Label string `json:"label"`
|
|
Code string `json:"code"`
|
|
Message string `json:"message"`
|
|
}{}
|
|
|
|
if err := json.Unmarshal(intermediary, &errCap); err == nil && errCap.Code != "" {
|
|
return fmt.Errorf("%s auth request error, code: %s message: %s",
|
|
e.Name,
|
|
errCap.Label,
|
|
errCap.Message)
|
|
}
|
|
if result == nil {
|
|
return nil
|
|
}
|
|
return json.Unmarshal(intermediary, result)
|
|
}
|
|
|
|
// SendHTTPRequest sends an unauthenticated HTTP request
|
|
func (e *Exchange) SendHTTPRequest(ctx context.Context, ep exchange.URL, epl request.EndpointLimit, path string, result any) error {
|
|
endpoint, err := e.API.Endpoints.GetURL(ep)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
item := &request.Item{
|
|
Method: http.MethodGet,
|
|
Path: endpoint + path,
|
|
Result: result,
|
|
Verbose: e.Verbose,
|
|
HTTPDebugging: e.HTTPDebugging,
|
|
HTTPRecording: e.HTTPRecording,
|
|
HTTPMockDataSliceLimit: e.HTTPMockDataSliceLimit,
|
|
}
|
|
return e.SendPayload(ctx, epl, func() (*request.Item, error) {
|
|
return item, nil
|
|
}, request.UnauthenticatedRequest)
|
|
}
|
|
|
|
// *********************************** Withdrawals ******************************
|
|
|
|
// WithdrawCurrency to withdraw a currency.
|
|
func (e *Exchange) WithdrawCurrency(ctx context.Context, arg WithdrawalRequestParam) (*WithdrawalResponse, error) {
|
|
if arg.Amount <= 0 {
|
|
return nil, fmt.Errorf("%w currency amount must be greater than zero", errInvalidAmount)
|
|
}
|
|
if arg.Currency.IsEmpty() {
|
|
return nil, fmt.Errorf("%w currency to be withdrawal nust be specified", currency.ErrCurrencyCodeEmpty)
|
|
}
|
|
if arg.Chain == "" {
|
|
return nil, errors.New("name of the chain used for withdrawal must be specified")
|
|
}
|
|
var response *WithdrawalResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletWithdrawEPL, http.MethodPost, withdrawal, nil, &arg, &response)
|
|
}
|
|
|
|
// CancelWithdrawalWithSpecifiedID cancels withdrawal with specified ID.
|
|
func (e *Exchange) CancelWithdrawalWithSpecifiedID(ctx context.Context, withdrawalID string) (*WithdrawalResponse, error) {
|
|
if withdrawalID == "" {
|
|
return nil, errMissingWithdrawalID
|
|
}
|
|
var response *WithdrawalResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletCancelWithdrawEPL, http.MethodDelete, withdrawal+"/"+withdrawalID, nil, nil, &response)
|
|
}
|
|
|
|
// *********************************** Wallet ***********************************
|
|
|
|
// ListCurrencyChain retrieves a list of currency chain name
|
|
func (e *Exchange) ListCurrencyChain(ctx context.Context, ccy currency.Code) ([]CurrencyChain, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
var resp []CurrencyChain
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, publicListCurrencyChainEPL, common.EncodeURLValues(walletCurrencyChain, params), &resp)
|
|
}
|
|
|
|
// GenerateCurrencyDepositAddress generate currency deposit address
|
|
func (e *Exchange) GenerateCurrencyDepositAddress(ctx context.Context, ccy currency.Code) (*CurrencyDepositAddressInfo, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
var response *CurrencyDepositAddressInfo
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletDepositAddressEPL, http.MethodGet, walletDepositAddress, params, nil, &response)
|
|
}
|
|
|
|
// GetWithdrawalRecords retrieves withdrawal records. Record time range cannot exceed 30 days
|
|
func (e *Exchange) GetWithdrawalRecords(ctx context.Context, ccy currency.Code, from, to time.Time, offset, limit uint64) ([]WithdrawalResponse, error) {
|
|
params := url.Values{}
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
if err := common.StartEndTimeCheck(from, to); err != nil && !to.IsZero() {
|
|
return nil, err
|
|
} else if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
}
|
|
var withdrawals []WithdrawalResponse
|
|
return withdrawals, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletWithdrawalRecordsEPL, http.MethodGet, walletWithdrawals, params, nil, &withdrawals)
|
|
}
|
|
|
|
// GetDepositRecords retrieves deposit records. Record time range cannot exceed 30 days
|
|
func (e *Exchange) GetDepositRecords(ctx context.Context, ccy currency.Code, from, to time.Time, offset, limit uint64) ([]DepositRecord, error) {
|
|
params := url.Values{}
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
if err := common.StartEndTimeCheck(from, to); err != nil {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
}
|
|
var depositHistories []DepositRecord
|
|
return depositHistories, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletDepositRecordsEPL, http.MethodGet, walletDeposits, params, nil, &depositHistories)
|
|
}
|
|
|
|
// TransferCurrency Transfer between different accounts. Currently support transfers between the following:
|
|
// spot - margin, spot - futures(perpetual), spot - delivery
|
|
// spot - cross margin, spot - options
|
|
func (e *Exchange) TransferCurrency(ctx context.Context, arg *TransferCurrencyParam) (*TransactionIDResponse, error) {
|
|
if arg == nil {
|
|
return nil, errNilArgument
|
|
}
|
|
if arg.Currency.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if arg.From == "" {
|
|
return nil, errors.New("from account is required")
|
|
}
|
|
if arg.To == "" {
|
|
return nil, errors.New("to account is required")
|
|
}
|
|
if arg.To == arg.From {
|
|
return nil, errors.New("from and to account cannot be the same")
|
|
}
|
|
if (arg.To == "margin" || arg.From == "margin") && arg.CurrencyPair.IsEmpty() {
|
|
return nil, errors.New("currency pair is required for margin account transfer")
|
|
}
|
|
if (arg.To == "futures" || arg.From == "futures") && arg.Settle == "" {
|
|
return nil, errors.New("settle is required for futures account transfer")
|
|
}
|
|
if arg.Amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
var response *TransactionIDResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletTransferCurrencyEPL, http.MethodPost, walletTransfer, nil, &arg, &response)
|
|
}
|
|
|
|
func (e *Exchange) assetTypeToString(acc asset.Item) string {
|
|
if acc == asset.Options {
|
|
return "options"
|
|
}
|
|
return acc.String()
|
|
}
|
|
|
|
// SubAccountTransfer to transfer between main and sub accounts
|
|
// Support transferring with sub user's spot or futures account. Note that only main user's spot account is used no matter which sub user's account is operated.
|
|
func (e *Exchange) SubAccountTransfer(ctx context.Context, arg SubAccountTransferParam) error {
|
|
if arg.Currency.IsEmpty() {
|
|
return currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if arg.SubAccount == "" {
|
|
return errInvalidSubAccount
|
|
}
|
|
arg.Direction = strings.ToLower(arg.Direction)
|
|
if arg.Direction != "to" && arg.Direction != "from" {
|
|
return errInvalidTransferDirection
|
|
}
|
|
if arg.Amount <= 0 {
|
|
return errInvalidAmount
|
|
}
|
|
switch arg.SubAccountType {
|
|
case "", "spot", "futures", "delivery":
|
|
default:
|
|
return fmt.Errorf("%w %q for SubAccountTransfer; Supported: [spot, futures, delivery]", asset.ErrNotSupported, arg.SubAccountType)
|
|
}
|
|
return e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletSubAccountTransferEPL, http.MethodPost, walletSubAccountTransfer, nil, &arg, nil)
|
|
}
|
|
|
|
// GetSubAccountTransferHistory retrieve transfer records between main and sub accounts.
|
|
// retrieve transfer records between main and sub accounts. Record time range cannot exceed 30 days
|
|
// Note: only records after 2020-04-10 can be retrieved
|
|
func (e *Exchange) GetSubAccountTransferHistory(ctx context.Context, subAccountUserID string, from, to time.Time, offset, limit uint64) ([]SubAccountTransferResponse, error) {
|
|
params := url.Values{}
|
|
if subAccountUserID != "" {
|
|
params.Set("sub_uid", subAccountUserID)
|
|
}
|
|
startingTime, err := time.Parse("2006-Jan-02", "2020-Apr-10")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := common.StartEndTimeCheck(startingTime, from); err == nil {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if err := common.StartEndTimeCheck(from, to); err == nil {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var response []SubAccountTransferResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletSubAccountTransferHistoryEPL, http.MethodGet, walletSubAccountTransfer, params, nil, &response)
|
|
}
|
|
|
|
// SubAccountTransferToSubAccount performs sub-account transfers to sub-account
|
|
func (e *Exchange) SubAccountTransferToSubAccount(ctx context.Context, arg *InterSubAccountTransferParams) error {
|
|
if arg.Currency.IsEmpty() {
|
|
return currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if arg.SubAccountFromUserID == "" {
|
|
return errors.New("sub-account from user-id is required")
|
|
}
|
|
if arg.SubAccountFromAssetType == asset.Empty {
|
|
return errors.New("sub-account to transfer the asset from is required")
|
|
}
|
|
if arg.SubAccountToUserID == "" {
|
|
return errors.New("sub-account to user-id is required")
|
|
}
|
|
if arg.SubAccountToAssetType == asset.Empty {
|
|
return errors.New("sub-account to transfer to is required")
|
|
}
|
|
if arg.Amount <= 0 {
|
|
return errInvalidAmount
|
|
}
|
|
return e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletSubAccountToSubAccountTransferEPL, http.MethodPost, walletInterSubAccountTransfer, nil, &arg, nil)
|
|
}
|
|
|
|
// GetWithdrawalStatus retrieves withdrawal status
|
|
func (e *Exchange) GetWithdrawalStatus(ctx context.Context, ccy currency.Code) ([]WithdrawalStatus, error) {
|
|
params := url.Values{}
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
var response []WithdrawalStatus
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletWithdrawStatusEPL, http.MethodGet, walletWithdrawStatus, params, nil, &response)
|
|
}
|
|
|
|
// GetSubAccountBalances retrieve sub account balances
|
|
func (e *Exchange) GetSubAccountBalances(ctx context.Context, subAccountUserID string) ([]FuturesSubAccountBalance, error) {
|
|
params := url.Values{}
|
|
if subAccountUserID != "" {
|
|
params.Set("sub_uid", subAccountUserID)
|
|
}
|
|
var response []FuturesSubAccountBalance
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletSubAccountBalancesEPL, http.MethodGet, walletSubAccountBalance, params, nil, &response)
|
|
}
|
|
|
|
// GetSubAccountMarginBalances query sub accounts' margin balances
|
|
func (e *Exchange) GetSubAccountMarginBalances(ctx context.Context, subAccountUserID string) ([]SubAccountMarginBalance, error) {
|
|
params := url.Values{}
|
|
if subAccountUserID != "" {
|
|
params.Set("sub_uid", subAccountUserID)
|
|
}
|
|
var response []SubAccountMarginBalance
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletSubAccountMarginBalancesEPL, http.MethodGet, walletSubAccountMarginBalance, params, nil, &response)
|
|
}
|
|
|
|
// GetSubAccountFuturesBalances retrieves sub accounts' futures account balances
|
|
func (e *Exchange) GetSubAccountFuturesBalances(ctx context.Context, subAccountUserID string, settle currency.Code) ([]FuturesSubAccountBalance, error) {
|
|
params := url.Values{}
|
|
if subAccountUserID != "" {
|
|
params.Set("sub_uid", subAccountUserID)
|
|
}
|
|
if !settle.IsEmpty() {
|
|
params.Set("settle", settle.Item.Lower)
|
|
}
|
|
var response []FuturesSubAccountBalance
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletSubAccountFuturesBalancesEPL, http.MethodGet, walletSubAccountFuturesBalance, params, nil, &response)
|
|
}
|
|
|
|
// GetSubAccountCrossMarginBalances query subaccount's cross_margin account info
|
|
func (e *Exchange) GetSubAccountCrossMarginBalances(ctx context.Context, subAccountUserID string) ([]SubAccountCrossMarginInfo, error) {
|
|
params := url.Values{}
|
|
if subAccountUserID != "" {
|
|
params.Set("sub_uid", subAccountUserID)
|
|
}
|
|
var response []SubAccountCrossMarginInfo
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletSubAccountCrossMarginBalancesEPL, http.MethodGet, walletSubAccountCrossMarginBalances, params, nil, &response)
|
|
}
|
|
|
|
// GetSavedAddresses retrieves saved currency address info and related details.
|
|
func (e *Exchange) GetSavedAddresses(ctx context.Context, ccy currency.Code, chain string, limit uint64) ([]WalletSavedAddress, error) {
|
|
params := url.Values{}
|
|
if ccy.IsEmpty() {
|
|
return nil, fmt.Errorf("%w address is required", currency.ErrCurrencyPairEmpty)
|
|
}
|
|
params.Set("currency", ccy.String())
|
|
if chain != "" {
|
|
params.Set("chain", chain)
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var response []WalletSavedAddress
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletSavedAddressesEPL, http.MethodGet, walletSavedAddress, params, nil, &response)
|
|
}
|
|
|
|
// GetPersonalTradingFee retrieves personal trading fee
|
|
func (e *Exchange) GetPersonalTradingFee(ctx context.Context, currencyPair currency.Pair, settle currency.Code) (*PersonalTradingFee, error) {
|
|
params := url.Values{}
|
|
if currencyPair.IsPopulated() {
|
|
// specify a currency pair to retrieve precise fee rate
|
|
params.Set("currency_pair", currencyPair.String())
|
|
}
|
|
if !settle.IsEmpty() {
|
|
params.Set("settle", settle.Item.Lower)
|
|
}
|
|
var response *PersonalTradingFee
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletTradingFeeEPL, http.MethodGet, walletTradingFee, params, nil, &response)
|
|
}
|
|
|
|
// GetUsersTotalBalance retrieves user's total balances
|
|
func (e *Exchange) GetUsersTotalBalance(ctx context.Context, ccy currency.Code) (*UsersAllAccountBalance, error) {
|
|
params := url.Values{}
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
var response *UsersAllAccountBalance
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletTotalBalanceEPL, http.MethodGet, walletTotalBalance, params, nil, &response)
|
|
}
|
|
|
|
// ConvertSmallBalances converts small balances of provided currencies into GT.
|
|
// If no currencies are provided, all supported currencies will be converted
|
|
// See [this documentation](https://www.gate.io/help/guide/functional_guidelines/22367) for details and restrictions.
|
|
func (e *Exchange) ConvertSmallBalances(ctx context.Context, currs ...currency.Code) error {
|
|
currencyList := make([]string, len(currs))
|
|
for i := range currs {
|
|
if currs[i].IsEmpty() {
|
|
return currency.ErrCurrencyCodeEmpty
|
|
}
|
|
currencyList[i] = currs[i].Upper().String()
|
|
}
|
|
|
|
payload := struct {
|
|
Currency []string `json:"currency"`
|
|
IsAll bool `json:"is_all"`
|
|
}{
|
|
Currency: currencyList,
|
|
IsAll: len(currs) == 0,
|
|
}
|
|
return e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletConvertSmallBalancesEPL, http.MethodPost, "wallet/small_balance", nil, payload, nil)
|
|
}
|
|
|
|
// ********************************* Margin *******************************************
|
|
|
|
// GetMarginSupportedCurrencyPairs retrieves margin supported currency pairs.
|
|
func (e *Exchange) GetMarginSupportedCurrencyPairs(ctx context.Context) ([]MarginCurrencyPairInfo, error) {
|
|
var currenciePairsInfo []MarginCurrencyPairInfo
|
|
return currenciePairsInfo, e.SendHTTPRequest(ctx, exchange.RestSpot, publicCurrencyPairsMarginEPL, gateioMarginCurrencyPairs, ¤ciePairsInfo)
|
|
}
|
|
|
|
// GetSingleMarginSupportedCurrencyPair retrieves margin supported currency pair detail given the currency pair.
|
|
func (e *Exchange) GetSingleMarginSupportedCurrencyPair(ctx context.Context, market currency.Pair) (*MarginCurrencyPairInfo, error) {
|
|
if market.IsEmpty() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
var currencyPairInfo *MarginCurrencyPairInfo
|
|
return currencyPairInfo, e.SendHTTPRequest(ctx, exchange.RestSpot, publicCurrencyPairsMarginEPL, gateioMarginCurrencyPairs+"/"+market.String(), ¤cyPairInfo)
|
|
}
|
|
|
|
// GetOrderbookOfLendingLoans retrieves order book of lending loans for specific currency
|
|
func (e *Exchange) GetOrderbookOfLendingLoans(ctx context.Context, ccy currency.Code) ([]OrderbookOfLendingLoan, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
var lendingLoans []OrderbookOfLendingLoan
|
|
return lendingLoans, e.SendHTTPRequest(ctx, exchange.RestSpot, publicOrderbookMarginEPL, gateioMarginFundingBook+"?currency="+ccy.String(), &lendingLoans)
|
|
}
|
|
|
|
// GetMarginAccountList margin account list
|
|
func (e *Exchange) GetMarginAccountList(ctx context.Context, currencyPair currency.Pair) ([]MarginAccountItem, error) {
|
|
params := url.Values{}
|
|
if currencyPair.IsPopulated() {
|
|
params.Set("currency_pair", currencyPair.String())
|
|
}
|
|
var response []MarginAccountItem
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginAccountListEPL, http.MethodGet, gateioMarginAccount, params, nil, &response)
|
|
}
|
|
|
|
// ListMarginAccountBalanceChangeHistory retrieves margin account balance change history
|
|
// Only transferals from and to margin account are provided for now. Time range allows 30 days at most
|
|
func (e *Exchange) ListMarginAccountBalanceChangeHistory(ctx context.Context, ccy currency.Code, currencyPair currency.Pair, from, to time.Time, page, limit uint64) ([]MarginAccountBalanceChangeInfo, error) {
|
|
params := url.Values{}
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
if currencyPair.IsPopulated() {
|
|
params.Set("currency_pair", currencyPair.String())
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() && ((!from.IsZero() && to.After(from)) || from.IsZero()) {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
if page > 0 {
|
|
params.Set("page", strconv.FormatUint(page, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var response []MarginAccountBalanceChangeInfo
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginAccountBalanceEPL, http.MethodGet, gateioMarginAccountBook, params, nil, &response)
|
|
}
|
|
|
|
// GetMarginFundingAccountList retrieves funding account list
|
|
func (e *Exchange) GetMarginFundingAccountList(ctx context.Context, ccy currency.Code) ([]MarginFundingAccountItem, error) {
|
|
params := url.Values{}
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
var response []MarginFundingAccountItem
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginFundingAccountListEPL, http.MethodGet, gateioMarginFundingAccounts, params, nil, &response)
|
|
}
|
|
|
|
// MarginLoan represents lend or borrow request
|
|
func (e *Exchange) MarginLoan(ctx context.Context, arg *MarginLoanRequestParam) (*MarginLoanResponse, error) {
|
|
if arg == nil {
|
|
return nil, errNilArgument
|
|
}
|
|
if arg.Side != sideLend && arg.Side != sideBorrow {
|
|
return nil, errInvalidLoanSide
|
|
}
|
|
if arg.Side == sideBorrow && arg.Rate == 0 {
|
|
return nil, errors.New("`rate` is required in borrowing")
|
|
}
|
|
if arg.Currency.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if arg.Amount <= 0 {
|
|
return nil, errInvalidAmount
|
|
}
|
|
if arg.Rate != 0 && arg.Rate > 0.002 || arg.Rate < 0.0002 {
|
|
return nil, errors.New("invalid loan rate, rate must be between 0.0002 and 0.002")
|
|
}
|
|
var response *MarginLoanResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginLendBorrowEPL, http.MethodPost, gateioMarginLoans, nil, &arg, &response)
|
|
}
|
|
|
|
// GetMarginAllLoans retrieves all loans (borrow and lending) orders.
|
|
func (e *Exchange) GetMarginAllLoans(ctx context.Context, status, side, sortBy string, ccy currency.Code, currencyPair currency.Pair, reverseSort bool, page, limit uint64) ([]MarginLoanResponse, error) {
|
|
if side != sideLend && side != sideBorrow {
|
|
return nil, fmt.Errorf("%w, only 'lend' and 'borrow' are supported", order.ErrSideIsInvalid)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("side", side)
|
|
if status == statusOpen || status == "loaned" || status == statusFinished || status == "auto_repair" {
|
|
params.Set("status", status)
|
|
} else {
|
|
return nil, errors.New("loan status \"status\" is required")
|
|
}
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
if currencyPair.IsPopulated() {
|
|
params.Set("currency_pair", currencyPair.String())
|
|
}
|
|
if sortBy == "create_time" || sortBy == "rate" {
|
|
params.Set("sort_by", sortBy)
|
|
}
|
|
if reverseSort {
|
|
params.Set("reverse_sort", strconv.FormatBool(reverseSort))
|
|
}
|
|
if page > 0 {
|
|
params.Set("page", strconv.FormatUint(page, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var response []MarginLoanResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginAllLoansEPL, http.MethodGet, gateioMarginLoans, params, nil, &response)
|
|
}
|
|
|
|
// MergeMultipleLendingLoans merge multiple lending loans
|
|
func (e *Exchange) MergeMultipleLendingLoans(ctx context.Context, ccy currency.Code, ids []string) (*MarginLoanResponse, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if len(ids) < 2 || len(ids) > 20 {
|
|
return nil, errors.New("number of loans to be merged must be between [2-20], inclusive")
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
params.Set("ids", strings.Join(ids, ","))
|
|
var response *MarginLoanResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginMergeLendingLoansEPL, http.MethodPost, gateioMarginMergedLoans, params, nil, &response)
|
|
}
|
|
|
|
// RetriveOneSingleLoanDetail retrieve one single loan detail
|
|
// "side" represents loan side: Lend or Borrow
|
|
func (e *Exchange) RetriveOneSingleLoanDetail(ctx context.Context, side, loanID string) (*MarginLoanResponse, error) {
|
|
if side != sideBorrow && side != sideLend {
|
|
return nil, errInvalidLoanSide
|
|
}
|
|
if loanID == "" {
|
|
return nil, errInvalidLoanID
|
|
}
|
|
params := url.Values{}
|
|
params.Set("side", side)
|
|
var response *MarginLoanResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginGetLoanEPL, http.MethodGet, gateioMarginLoans+"/"+loanID+"/", params, nil, &response)
|
|
}
|
|
|
|
// ModifyALoan Modify a loan
|
|
// only auto_renew modification is supported currently
|
|
func (e *Exchange) ModifyALoan(ctx context.Context, loanID string, arg *ModifyLoanRequestParam) (*MarginLoanResponse, error) {
|
|
if arg == nil {
|
|
return nil, errNilArgument
|
|
}
|
|
if loanID == "" {
|
|
return nil, fmt.Errorf("%w, %v", errInvalidLoanID, " loan_id is required")
|
|
}
|
|
if arg.Currency.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if arg.Side != sideBorrow && arg.Side != sideLend {
|
|
return nil, errInvalidLoanSide
|
|
}
|
|
if arg.CurrencyPair.IsEmpty() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
var response *MarginLoanResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginModifyLoanEPL, http.MethodPatch, gateioMarginLoans+"/"+loanID, nil, &arg, &response)
|
|
}
|
|
|
|
// CancelLendingLoan cancels lending loans. only lent loans can be canceled.
|
|
func (e *Exchange) CancelLendingLoan(ctx context.Context, ccy currency.Code, loanID string) (*MarginLoanResponse, error) {
|
|
if loanID == "" {
|
|
return nil, fmt.Errorf("%w, %s", errInvalidLoanID, " loan_id is required")
|
|
}
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
var response *MarginLoanResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginCancelLoanEPL, http.MethodDelete, gateioMarginLoans+"/"+loanID, params, nil, &response)
|
|
}
|
|
|
|
// RepayALoan execute a loan repay.
|
|
func (e *Exchange) RepayALoan(ctx context.Context, loanID string, arg *RepayLoanRequestParam) (*MarginLoanResponse, error) {
|
|
if arg == nil {
|
|
return nil, errNilArgument
|
|
}
|
|
if loanID == "" {
|
|
return nil, fmt.Errorf("%w, %v", errInvalidLoanID, " loan_id is required")
|
|
}
|
|
if arg.Currency.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if arg.CurrencyPair.IsEmpty() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
if arg.Mode != "all" && arg.Mode != "partial" {
|
|
return nil, errInvalidRepayMode
|
|
}
|
|
if arg.Mode == "partial" && arg.Amount <= 0 {
|
|
return nil, fmt.Errorf("%w, repay amount for partial repay mode must be greater than 0", errInvalidAmount)
|
|
}
|
|
var response *MarginLoanResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginRepayLoanEPL, http.MethodPost, gateioMarginLoans+"/"+loanID+"/repayment", nil, &arg, &response)
|
|
}
|
|
|
|
// ListLoanRepaymentRecords retrieves loan repayment records for specified loan ID
|
|
func (e *Exchange) ListLoanRepaymentRecords(ctx context.Context, loanID string) ([]LoanRepaymentRecord, error) {
|
|
if loanID == "" {
|
|
return nil, fmt.Errorf("%w, %v", errInvalidLoanID, " loan_id is required")
|
|
}
|
|
var response []LoanRepaymentRecord
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginListLoansEPL, http.MethodGet, gateioMarginLoans+"/"+loanID+"/repayment", nil, nil, &response)
|
|
}
|
|
|
|
// ListRepaymentRecordsOfSpecificLoan retrieves repayment records of specific loan
|
|
func (e *Exchange) ListRepaymentRecordsOfSpecificLoan(ctx context.Context, loanID, status string, page, limit uint64) ([]LoanRecord, error) {
|
|
if loanID == "" {
|
|
return nil, fmt.Errorf("%w, %v", errInvalidLoanID, " loan_id is required")
|
|
}
|
|
params := url.Values{}
|
|
params.Set("loan_id", loanID)
|
|
if status == statusLoaned || status == statusFinished {
|
|
params.Set("status", status)
|
|
}
|
|
if page > 0 {
|
|
params.Set("page", strconv.FormatUint(page, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var response []LoanRecord
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginRepaymentRecordEPL, http.MethodGet, gateioMarginLoanRecords, params, nil, &response)
|
|
}
|
|
|
|
// GetOneSingleLoanRecord get one single loan record
|
|
func (e *Exchange) GetOneSingleLoanRecord(ctx context.Context, loanID, loanRecordID string) (*LoanRecord, error) {
|
|
if loanID == "" {
|
|
return nil, fmt.Errorf("%w, %v", errInvalidLoanID, " loan_id is required")
|
|
}
|
|
if loanRecordID == "" {
|
|
return nil, fmt.Errorf("%w, %v", errInvalidLoanID, " loan_record_id is required")
|
|
}
|
|
params := url.Values{}
|
|
params.Set("loan_id", loanID)
|
|
var response *LoanRecord
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginSingleRecordEPL, http.MethodGet, gateioMarginLoanRecords+"/"+loanRecordID, params, nil, &response)
|
|
}
|
|
|
|
// ModifyALoanRecord modify a loan record
|
|
// Only auto_renew modification is supported currently
|
|
func (e *Exchange) ModifyALoanRecord(ctx context.Context, loanRecordID string, arg *ModifyLoanRequestParam) (*LoanRecord, error) {
|
|
if arg == nil {
|
|
return nil, errNilArgument
|
|
}
|
|
if loanRecordID == "" {
|
|
return nil, fmt.Errorf("%w, %v", errInvalidLoanID, " loan_record_id is required")
|
|
}
|
|
if arg.LoanID == "" {
|
|
return nil, fmt.Errorf("%w, %v", errInvalidLoanID, " loan_id is required")
|
|
}
|
|
if arg.Currency.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if arg.Side != sideBorrow && arg.Side != sideLend {
|
|
return nil, errInvalidLoanSide
|
|
}
|
|
var response *LoanRecord
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginModifyLoanRecordEPL, http.MethodPatch, gateioMarginLoanRecords+"/"+loanRecordID, nil, &arg, &response)
|
|
}
|
|
|
|
// UpdateUsersAutoRepaymentSetting represents update user's auto repayment setting
|
|
func (e *Exchange) UpdateUsersAutoRepaymentSetting(ctx context.Context, statusOn bool) (*OnOffStatus, error) {
|
|
var statusStr string
|
|
if statusOn {
|
|
statusStr = "on"
|
|
} else {
|
|
statusStr = "off"
|
|
}
|
|
params := url.Values{}
|
|
params.Set("status", statusStr)
|
|
var response *OnOffStatus
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginAutoRepayEPL, http.MethodPost, gateioMarginAutoRepay, params, nil, &response)
|
|
}
|
|
|
|
// GetUserAutoRepaymentSetting retrieve user auto repayment setting
|
|
func (e *Exchange) GetUserAutoRepaymentSetting(ctx context.Context) (*OnOffStatus, error) {
|
|
var response *OnOffStatus
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginGetAutoRepaySettingsEPL, http.MethodGet, gateioMarginAutoRepay, nil, nil, &response)
|
|
}
|
|
|
|
// GetMaxTransferableAmountForSpecificMarginCurrency get the max transferable amount for a specific margin currency.
|
|
func (e *Exchange) GetMaxTransferableAmountForSpecificMarginCurrency(ctx context.Context, ccy currency.Code, currencyPair currency.Pair) (*MaxTransferAndLoanAmount, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
if currencyPair.IsPopulated() {
|
|
params.Set("currency_pair", currencyPair.String())
|
|
}
|
|
params.Set("currency", ccy.String())
|
|
var response *MaxTransferAndLoanAmount
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginGetMaxTransferEPL, http.MethodGet, gateioMarginTransfer, params, nil, &response)
|
|
}
|
|
|
|
// GetMaxBorrowableAmountForSpecificMarginCurrency retrieves the max borrowble amount for specific currency
|
|
func (e *Exchange) GetMaxBorrowableAmountForSpecificMarginCurrency(ctx context.Context, ccy currency.Code, currencyPair currency.Pair) (*MaxTransferAndLoanAmount, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
if currencyPair.IsPopulated() {
|
|
params.Set("currency_pair", currencyPair.String())
|
|
}
|
|
params.Set("currency", ccy.String())
|
|
var response *MaxTransferAndLoanAmount
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginGetMaxBorrowEPL, http.MethodGet, gateioMarginBorrowable, params, nil, &response)
|
|
}
|
|
|
|
// CurrencySupportedByCrossMargin currencies supported by cross margin.
|
|
func (e *Exchange) CurrencySupportedByCrossMargin(ctx context.Context) ([]CrossMarginCurrencies, error) {
|
|
var response []CrossMarginCurrencies
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginSupportedCurrencyCrossListEPL, http.MethodGet, gateioCrossMarginCurrencies, nil, nil, &response)
|
|
}
|
|
|
|
// GetCrossMarginSupportedCurrencyDetail retrieve detail of one single currency supported by cross margin
|
|
func (e *Exchange) GetCrossMarginSupportedCurrencyDetail(ctx context.Context, ccy currency.Code) (*CrossMarginCurrencies, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
var response *CrossMarginCurrencies
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginSupportedCurrencyCrossEPL, http.MethodGet, gateioCrossMarginCurrencies+"/"+ccy.String(), nil, nil, &response)
|
|
}
|
|
|
|
// GetCrossMarginAccounts retrieve cross margin account
|
|
func (e *Exchange) GetCrossMarginAccounts(ctx context.Context) (*CrossMarginAccount, error) {
|
|
var response *CrossMarginAccount
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginAccountsEPL, http.MethodGet, gateioCrossMarginAccounts, nil, nil, &response)
|
|
}
|
|
|
|
// GetCrossMarginAccountChangeHistory retrieve cross margin account change history
|
|
// Record time range cannot exceed 30 days
|
|
func (e *Exchange) GetCrossMarginAccountChangeHistory(ctx context.Context, ccy currency.Code, from, to time.Time, page, limit uint64, accountChangeType string) ([]CrossMarginAccountHistoryItem, error) {
|
|
params := url.Values{}
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
if page > 0 {
|
|
params.Set("page", strconv.FormatUint(page, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if accountChangeType != "" { // "in", "out", "repay", "new_order", "order_fill", "referral_fee", "order_fee", "unknown" are supported
|
|
params.Set("type", accountChangeType)
|
|
}
|
|
var response []CrossMarginAccountHistoryItem
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginAccountHistoryEPL, http.MethodGet, gateioCrossMarginAccountBook, params, nil, &response)
|
|
}
|
|
|
|
// CreateCrossMarginBorrowLoan create a cross margin borrow loan
|
|
// Borrow amount cannot be less than currency minimum borrow amount
|
|
func (e *Exchange) CreateCrossMarginBorrowLoan(ctx context.Context, arg CrossMarginBorrowLoanParams) (*CrossMarginLoanResponse, error) {
|
|
if arg.Currency.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if arg.Amount <= 0 {
|
|
return nil, fmt.Errorf("%w, borrow amount must be greater than 0", errInvalidAmount)
|
|
}
|
|
var response CrossMarginLoanResponse
|
|
return &response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginCreateCrossBorrowLoanEPL, http.MethodPost, gateioCrossMarginLoans, nil, &arg, &response)
|
|
}
|
|
|
|
// ExecuteRepayment when the liquidity of the currency is insufficient and the transaction risk is high, the currency will be disabled,
|
|
// and funds cannot be transferred.When the available balance of cross-margin is insufficient, the balance of the spot account can be used for repayment.
|
|
// Please ensure that the balance of the spot account is sufficient, and system uses cross-margin account for repayment first
|
|
func (e *Exchange) ExecuteRepayment(ctx context.Context, arg CurrencyAndAmount) ([]CrossMarginLoanResponse, error) {
|
|
if arg.Currency.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
if arg.Amount <= 0 {
|
|
return nil, fmt.Errorf("%w, repay amount must be greater than 0", errInvalidAmount)
|
|
}
|
|
var response []CrossMarginLoanResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginExecuteRepaymentsEPL, http.MethodPost, gateioCrossMarginRepayments, nil, &arg, &response)
|
|
}
|
|
|
|
// GetCrossMarginRepayments retrieves list of cross margin repayments
|
|
func (e *Exchange) GetCrossMarginRepayments(ctx context.Context, ccy currency.Code, loanID string, limit, offset uint64, reverse bool) ([]CrossMarginLoanResponse, error) {
|
|
params := url.Values{}
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
if loanID != "" {
|
|
params.Set("loanId", loanID)
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if reverse {
|
|
params.Set("reverse", "true")
|
|
}
|
|
var response []CrossMarginLoanResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginGetCrossMarginRepaymentsEPL, http.MethodGet, gateioCrossMarginRepayments, params, nil, &response)
|
|
}
|
|
|
|
// GetMaxTransferableAmountForSpecificCrossMarginCurrency get the max transferable amount for a specific cross margin currency
|
|
func (e *Exchange) GetMaxTransferableAmountForSpecificCrossMarginCurrency(ctx context.Context, ccy currency.Code) (*CurrencyAndAmount, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
var response *CurrencyAndAmount
|
|
params.Set("currency", ccy.String())
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginGetMaxTransferCrossEPL, http.MethodGet, gateioCrossMarginTransferable, params, nil, &response)
|
|
}
|
|
|
|
// GetMaxBorrowableAmountForSpecificCrossMarginCurrency returns the max borrowable amount for a specific cross margin currency
|
|
func (e *Exchange) GetMaxBorrowableAmountForSpecificCrossMarginCurrency(ctx context.Context, ccy currency.Code) (*CurrencyAndAmount, error) {
|
|
if ccy.IsEmpty() {
|
|
return nil, currency.ErrCurrencyCodeEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("currency", ccy.String())
|
|
var response *CurrencyAndAmount
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginGetMaxBorrowCrossEPL, http.MethodGet, gateioCrossMarginBorrowable, params, nil, &response)
|
|
}
|
|
|
|
// GetCrossMarginBorrowHistory retrieves cross margin borrow history sorted by creation time in descending order by default.
|
|
// Set reverse=false to return ascending results.
|
|
func (e *Exchange) GetCrossMarginBorrowHistory(ctx context.Context, status uint64, ccy currency.Code, limit, offset uint64, reverse bool) ([]CrossMarginLoanResponse, error) {
|
|
if status < 1 || status > 3 {
|
|
return nil, fmt.Errorf("%s %v, only allowed status values are 1:failed, 2:borrowed, and 3:repayment", e.Name, errInvalidOrderStatus)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("status", strconv.FormatUint(status, 10))
|
|
if !ccy.IsEmpty() {
|
|
params.Set("currency", ccy.String())
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if reverse {
|
|
params.Set("reverse", strconv.FormatBool(reverse))
|
|
}
|
|
var response []CrossMarginLoanResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginGetCrossBorrowHistoryEPL, http.MethodGet, gateioCrossMarginLoans, params, nil, &response)
|
|
}
|
|
|
|
// GetSingleBorrowLoanDetail retrieve single borrow loan detail
|
|
func (e *Exchange) GetSingleBorrowLoanDetail(ctx context.Context, loanID string) (*CrossMarginLoanResponse, error) {
|
|
if loanID == "" {
|
|
return nil, errInvalidLoanID
|
|
}
|
|
var response *CrossMarginLoanResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, marginGetBorrowEPL, http.MethodGet, gateioCrossMarginLoans+"/"+loanID, nil, nil, &response)
|
|
}
|
|
|
|
// *********************************Futures***************************************
|
|
|
|
// GetAllFutureContracts retrieves list all futures contracts
|
|
func (e *Exchange) GetAllFutureContracts(ctx context.Context, settle currency.Code) ([]FuturesContract, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
var contracts []FuturesContract
|
|
return contracts, e.SendHTTPRequest(ctx, exchange.RestSpot, publicFuturesContractsEPL, futuresPath+settle.Item.Lower+"/contracts", &contracts)
|
|
}
|
|
|
|
// GetFuturesContract returns a single futures contract info for the specified settle and Currency Pair (contract << in this case)
|
|
func (e *Exchange) GetFuturesContract(ctx context.Context, settle currency.Code, contract string) (*FuturesContract, error) {
|
|
if contract == "" {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
var futureContract *FuturesContract
|
|
return futureContract, e.SendHTTPRequest(ctx, exchange.RestSpot, publicFuturesContractsEPL, futuresPath+settle.Item.Lower+"/contracts/"+contract, &futureContract)
|
|
}
|
|
|
|
// GetFuturesOrderbook retrieves futures order book data
|
|
func (e *Exchange) GetFuturesOrderbook(ctx context.Context, settle currency.Code, contract, interval string, limit uint64, withOrderbookID bool) (*Orderbook, error) {
|
|
if contract == "" {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", contract)
|
|
if interval != "" {
|
|
params.Set("interval", interval)
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if withOrderbookID {
|
|
params.Set("with_id", "true")
|
|
}
|
|
var response *Orderbook
|
|
return response, e.SendHTTPRequest(ctx, exchange.RestSpot, publicOrderbookFuturesEPL, common.EncodeURLValues(futuresPath+settle.Item.Lower+"/order_book", params), &response)
|
|
}
|
|
|
|
// GetFuturesTradingHistory retrieves futures trading history
|
|
func (e *Exchange) GetFuturesTradingHistory(ctx context.Context, settle currency.Code, contract currency.Pair, limit, offset uint64, lastID string, from, to time.Time) ([]TradingHistoryItem, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsEmpty() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", contract.Upper().String())
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if lastID != "" {
|
|
params.Set("last_id", lastID)
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
var response []TradingHistoryItem
|
|
return response, e.SendHTTPRequest(ctx, exchange.RestSpot, publicTradingHistoryFuturesEPL, common.EncodeURLValues(futuresPath+settle.Item.Lower+"/trades", params), &response)
|
|
}
|
|
|
|
// GetFuturesCandlesticks retrieves specified contract candlesticks.
|
|
func (e *Exchange) GetFuturesCandlesticks(ctx context.Context, settle currency.Code, contract string, from, to time.Time, limit uint64, interval kline.Interval) ([]FuturesCandlestick, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract == "" {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", strings.ToUpper(contract))
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if interval.Duration().Microseconds() != 0 {
|
|
intervalString, err := getIntervalString(interval)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("interval", intervalString)
|
|
}
|
|
var candlesticks []FuturesCandlestick
|
|
return candlesticks, e.SendHTTPRequest(ctx, exchange.RestFutures, publicCandleSticksFuturesEPL, common.EncodeURLValues(futuresPath+settle.Item.Lower+"/candlesticks", params), &candlesticks)
|
|
}
|
|
|
|
// PremiumIndexKLine retrieves premium Index K-Line
|
|
// Maximum of 1000 points can be returned in a query. Be sure not to exceed the limit when specifying from, to and interval
|
|
func (e *Exchange) PremiumIndexKLine(ctx context.Context, settleCurrency currency.Code, contract currency.Pair, from, to time.Time, limit int64, interval kline.Interval) ([]FuturesPremiumIndexKLineResponse, error) {
|
|
if settleCurrency.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsEmpty() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", contract.String())
|
|
if from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatInt(limit, 10))
|
|
}
|
|
intervalString, err := getIntervalString(interval)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("interval", intervalString)
|
|
var resp []FuturesPremiumIndexKLineResponse
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, publicPremiumIndexEPL, common.EncodeURLValues(futuresPath+settleCurrency.Item.Lower+"/premium_index", params), &resp)
|
|
}
|
|
|
|
// GetFuturesTickers retrieves futures ticker information for a specific settle and contract info.
|
|
func (e *Exchange) GetFuturesTickers(ctx context.Context, settle currency.Code, contract currency.Pair) ([]FuturesTicker, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
var tickers []FuturesTicker
|
|
return tickers, e.SendHTTPRequest(ctx, exchange.RestSpot, publicTickersFuturesEPL, common.EncodeURLValues(futuresPath+settle.Item.Lower+"/tickers", params), &tickers)
|
|
}
|
|
|
|
// GetFutureFundingRates retrieves funding rate information.
|
|
func (e *Exchange) GetFutureFundingRates(ctx context.Context, settle currency.Code, contract currency.Pair, limit uint64) ([]FuturesFundingRate, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", contract.String())
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var rates []FuturesFundingRate
|
|
return rates, e.SendHTTPRequest(ctx, exchange.RestSpot, publicFundingRatesEPL, common.EncodeURLValues(futuresPath+settle.Item.Lower+"/funding_rate", params), &rates)
|
|
}
|
|
|
|
// GetFuturesInsuranceBalanceHistory retrieves futures insurance balance history
|
|
func (e *Exchange) GetFuturesInsuranceBalanceHistory(ctx context.Context, settle currency.Code, limit uint64) ([]InsuranceBalance, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var balances []InsuranceBalance
|
|
return balances, e.SendHTTPRequest(ctx, exchange.RestSpot, publicInsuranceFuturesEPL, common.EncodeURLValues(futuresPath+settle.Item.Lower+"/insurance", params), &balances)
|
|
}
|
|
|
|
// GetFutureStats retrieves futures stats
|
|
func (e *Exchange) GetFutureStats(ctx context.Context, settle currency.Code, contract currency.Pair, from time.Time, interval kline.Interval, limit uint64) ([]ContractStat, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", contract.String())
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if int64(interval) != 0 {
|
|
intervalString, err := getIntervalString(interval)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("interval", intervalString)
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var stats []ContractStat
|
|
return stats, e.SendHTTPRequest(ctx, exchange.RestSpot, publicStatsFuturesEPL, common.EncodeURLValues(futuresPath+settle.Item.Lower+"/contract_stats", params), &stats)
|
|
}
|
|
|
|
// GetIndexConstituent retrieves index constituents
|
|
func (e *Exchange) GetIndexConstituent(ctx context.Context, settle currency.Code, index string) (*IndexConstituent, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if index == "" {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
indexString := strings.ToUpper(index)
|
|
var constituents *IndexConstituent
|
|
return constituents, e.SendHTTPRequest(ctx, exchange.RestSpot, publicIndexConstituentsEPL, futuresPath+settle.Item.Lower+"/index_constituents/"+indexString, &constituents)
|
|
}
|
|
|
|
// GetLiquidationHistory retrieves liqudiation history
|
|
func (e *Exchange) GetLiquidationHistory(ctx context.Context, settle currency.Code, contract currency.Pair, from, to time.Time, limit uint64) ([]LiquidationHistory, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, errInvalidOrMissingContractParam
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", contract.String())
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var histories []LiquidationHistory
|
|
return histories, e.SendHTTPRequest(ctx, exchange.RestSpot, publicLiquidationHistoryEPL, common.EncodeURLValues(futuresPath+settle.Item.Lower+"/liq_orders", params), &histories)
|
|
}
|
|
|
|
// QueryFuturesAccount retrieves futures account
|
|
func (e *Exchange) QueryFuturesAccount(ctx context.Context, settle currency.Code) (*FuturesAccount, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
var response *FuturesAccount
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualAccountEPL, http.MethodGet, futuresPath+settle.Item.Lower+"/accounts", nil, nil, &response)
|
|
}
|
|
|
|
// GetFuturesAccountBooks retrieves account books
|
|
func (e *Exchange) GetFuturesAccountBooks(ctx context.Context, settle currency.Code, limit uint64, from, to time.Time, changingType string) ([]AccountBookItem, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
if changingType != "" {
|
|
params.Set("type", changingType)
|
|
}
|
|
var response []AccountBookItem
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualAccountBooksEPL, http.MethodGet, futuresPath+settle.Item.Lower+"/account_book", params, nil, &response)
|
|
}
|
|
|
|
// GetAllFuturesPositionsOfUsers list all positions of users.
|
|
func (e *Exchange) GetAllFuturesPositionsOfUsers(ctx context.Context, settle currency.Code, realPositionsOnly bool) ([]Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if realPositionsOnly {
|
|
params.Set("holding", "true")
|
|
}
|
|
var response []Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualPositionsEPL, http.MethodGet, futuresPath+settle.Item.Lower+"/positions", params, nil, &response)
|
|
}
|
|
|
|
// GetSinglePosition returns a single position
|
|
func (e *Exchange) GetSinglePosition(ctx context.Context, settle currency.Code, contract currency.Pair) (*Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
var response *Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualPositionEPL, http.MethodPost, futuresPath+settle.Item.Lower+positionsPath+contract.String(), nil, nil, &response)
|
|
}
|
|
|
|
// UpdateFuturesPositionMargin represents account position margin for a futures contract.
|
|
func (e *Exchange) UpdateFuturesPositionMargin(ctx context.Context, settle currency.Code, change float64, contract currency.Pair) (*Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
if change <= 0 {
|
|
return nil, fmt.Errorf("%w, futures margin change must be positive", errChangeHasToBePositive)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("change", strconv.FormatFloat(change, 'f', -1, 64))
|
|
var response *Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualUpdateMarginEPL, http.MethodPost, futuresPath+settle.Item.Lower+positionsPath+contract.String()+"/margin", params, nil, &response)
|
|
}
|
|
|
|
// UpdateFuturesPositionLeverage update position leverage
|
|
func (e *Exchange) UpdateFuturesPositionLeverage(ctx context.Context, settle currency.Code, contract currency.Pair, leverage, crossLeverageLimit float64) (*Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
if leverage < 0 {
|
|
return nil, fmt.Errorf("%w: %f", errInvalidLeverage, leverage)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("leverage", strconv.FormatFloat(leverage, 'f', -1, 64))
|
|
if leverage == 0 && crossLeverageLimit > 0 {
|
|
params.Set("cross_leverage_limit", strconv.FormatFloat(crossLeverageLimit, 'f', -1, 64))
|
|
}
|
|
var response *Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualUpdateLeverageEPL, http.MethodPost, futuresPath+settle.Item.Lower+positionsPath+contract.String()+"/leverage", params, nil, &response)
|
|
}
|
|
|
|
// UpdateFuturesPositionRiskLimit updates the position risk limit
|
|
func (e *Exchange) UpdateFuturesPositionRiskLimit(ctx context.Context, settle currency.Code, contract currency.Pair, riskLimit uint64) (*Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("risk_limit", strconv.FormatUint(riskLimit, 10))
|
|
var response *Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualUpdateRiskEPL, http.MethodPost, futuresPath+settle.Item.Lower+positionsPath+contract.String()+"/risk_limit", params, nil, &response)
|
|
}
|
|
|
|
// EnableOrDisableDualMode enable or disable dual mode
|
|
// Before setting dual mode, make sure all positions are closed and no orders are open
|
|
func (e *Exchange) EnableOrDisableDualMode(ctx context.Context, settle currency.Code, dualMode bool) (*DualModeResponse, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
params.Set("dual_mode", strconv.FormatBool(dualMode))
|
|
var response *DualModeResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualToggleDualModeEPL, http.MethodGet, futuresPath+settle.Item.Lower+"/dual_mode", params, nil, &response)
|
|
}
|
|
|
|
// RetrivePositionDetailInDualMode retrieve position detail in dual mode
|
|
func (e *Exchange) RetrivePositionDetailInDualMode(ctx context.Context, settle currency.Code, contract currency.Pair) ([]Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
var response []Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualPositionsDualModeEPL, http.MethodGet, futuresPath+settle.Item.Lower+"/dual_comp/positions/"+contract.String(), nil, nil, &response)
|
|
}
|
|
|
|
// UpdatePositionMarginInDualMode update position margin in dual mode
|
|
func (e *Exchange) UpdatePositionMarginInDualMode(ctx context.Context, settle currency.Code, contract currency.Pair, change float64, dualSide string) ([]Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("change", strconv.FormatFloat(change, 'f', -1, 64))
|
|
if dualSide != "dual_long" && dualSide != "dual_short" {
|
|
return nil, errors.New("invalid 'dual_side' should be 'dual_short' or 'dual_long'")
|
|
}
|
|
params.Set("dual_side", dualSide)
|
|
var response []Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualUpdateMarginDualModeEPL, http.MethodPost, futuresPath+settle.Item.Lower+"/dual_comp/positions/"+contract.String()+"/margin", params, nil, &response)
|
|
}
|
|
|
|
// UpdatePositionLeverageInDualMode update position leverage in dual mode
|
|
func (e *Exchange) UpdatePositionLeverageInDualMode(ctx context.Context, settle currency.Code, contract currency.Pair, leverage, crossLeverageLimit float64) (*Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
if leverage < 0 {
|
|
return nil, fmt.Errorf("%w: %f", errInvalidLeverage, leverage)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("leverage", strconv.FormatFloat(leverage, 'f', -1, 64))
|
|
if leverage == 0 && crossLeverageLimit > 0 {
|
|
params.Set("cross_leverage_limit", strconv.FormatFloat(crossLeverageLimit, 'f', -1, 64))
|
|
}
|
|
var response *Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualUpdateLeverageDualModeEPL, http.MethodPost, futuresPath+settle.Item.Lower+"/dual_comp/positions/"+contract.String()+"/leverage", params, nil, &response)
|
|
}
|
|
|
|
// UpdatePositionRiskLimitInDualMode update position risk limit in dual mode
|
|
func (e *Exchange) UpdatePositionRiskLimitInDualMode(ctx context.Context, settle currency.Code, contract currency.Pair, riskLimit float64) ([]Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
if riskLimit < 0 {
|
|
return nil, errInvalidRiskLimit
|
|
}
|
|
params := url.Values{}
|
|
params.Set("risk_limit", strconv.FormatFloat(riskLimit, 'f', -1, 64))
|
|
var response []Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualUpdateRiskDualModeEPL, http.MethodPost, futuresPath+settle.Item.Lower+"/dual_comp/positions/"+contract.String()+"/risk_limit", params, nil, &response)
|
|
}
|
|
|
|
// PlaceFuturesOrder creates futures order
|
|
// Create a futures order
|
|
// Creating futures orders requires size, which is number of contracts instead of currency amount. You can use quanto_multiplier in contract detail response to know how much currency 1 size contract represents
|
|
// Zero-filled order cannot be retrieved 10 minutes after order cancellation. You will get a 404 not found for such orders
|
|
// Set reduce_only to true can keep the position from changing side when reducing position size
|
|
// In single position mode, to close a position, you need to set size to 0 and close to true
|
|
// In dual position mode, to close one side position, you need to set auto_size side, reduce_only to true and size to 0
|
|
func (e *Exchange) PlaceFuturesOrder(ctx context.Context, arg *ContractOrderCreateParams) (*Order, error) {
|
|
if err := arg.validate(true); err != nil {
|
|
return nil, err
|
|
}
|
|
var response *Order
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualSubmitOrderEPL, http.MethodPost, futuresPath+arg.Settle.Item.Lower+ordersPath, nil, &arg, &response)
|
|
}
|
|
|
|
// GetFuturesOrders retrieves list of futures orders
|
|
// Zero-filled order cannot be retrieved 10 minutes after order cancellation
|
|
func (e *Exchange) GetFuturesOrders(ctx context.Context, contract currency.Pair, status, lastID string, settle currency.Code, limit, offset uint64, countTotal bool) ([]Order, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if !contract.IsEmpty() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if status != statusOpen && status != statusFinished {
|
|
return nil, fmt.Errorf("%w, only 'open' and 'finished' status are supported", errInvalidOrderStatus)
|
|
}
|
|
params.Set("status", status)
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if lastID != "" {
|
|
params.Set("last_id", lastID)
|
|
}
|
|
if countTotal && status != statusOpen {
|
|
params.Set("count_total", "1")
|
|
}
|
|
var response []Order
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualGetOrdersEPL, http.MethodGet, futuresPath+settle.Item.Lower+ordersPath, params, nil, &response)
|
|
}
|
|
|
|
// CancelMultipleFuturesOpenOrders ancel all open orders
|
|
// Zero-filled order cannot be retrieved 10 minutes after order cancellation
|
|
func (e *Exchange) CancelMultipleFuturesOpenOrders(ctx context.Context, contract currency.Pair, side string, settle currency.Code) ([]Order, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
params := url.Values{}
|
|
if side != "" {
|
|
params.Set("side", side)
|
|
}
|
|
params.Set("contract", contract.String())
|
|
var response []Order
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualGetOrdersEPL, http.MethodDelete, futuresPath+settle.Item.Lower+ordersPath, params, nil, &response)
|
|
}
|
|
|
|
// PlaceBatchFuturesOrders creates a list of futures orders
|
|
// Up to 10 orders per request
|
|
// If any of the order's parameters are missing or in the wrong format, all of them will not be executed, and a http status 400 error will be returned directly
|
|
// If the parameters are checked and passed, all are executed. Even if there is a business logic error in the middle (such as insufficient funds), it will not affect other execution orders
|
|
// The returned result is in array format, and the order corresponds to the orders in the request body
|
|
// In the returned result, the succeeded field of type bool indicates whether the execution was successful or not
|
|
// If the execution is successful, the normal order content is included; if the execution fails, the label field is included to indicate the cause of the error
|
|
// In the rate limiting, each order is counted individually
|
|
func (e *Exchange) PlaceBatchFuturesOrders(ctx context.Context, settle currency.Code, args []ContractOrderCreateParams) ([]Order, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if len(args) > 10 {
|
|
return nil, errTooManyOrderRequest
|
|
}
|
|
for x := range args {
|
|
if err := args[x].validate(true); err != nil {
|
|
return nil, err
|
|
}
|
|
if !args[x].Settle.Equal(settle) {
|
|
return nil, fmt.Errorf("%w: %q expected %q", errEmptyOrInvalidSettlementCurrency, args[x].Settle, settle)
|
|
}
|
|
}
|
|
var response []Order
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualSubmitBatchOrdersEPL, http.MethodPost, futuresPath+settle.Item.Lower+"/batch_orders", nil, &args, &response)
|
|
}
|
|
|
|
// GetSingleFuturesOrder retrieves a single order by its identifier
|
|
func (e *Exchange) GetSingleFuturesOrder(ctx context.Context, settle currency.Code, orderID string) (*Order, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if orderID == "" {
|
|
return nil, fmt.Errorf("%w, 'order_id' cannot be empty", errInvalidOrderID)
|
|
}
|
|
var response *Order
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualFetchOrderEPL, http.MethodGet, futuresPath+settle.Item.Lower+"/orders/"+orderID, nil, nil, &response)
|
|
}
|
|
|
|
// CancelSingleFuturesOrder cancel a single order
|
|
func (e *Exchange) CancelSingleFuturesOrder(ctx context.Context, settle currency.Code, orderID string) (*Order, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if orderID == "" {
|
|
return nil, fmt.Errorf("%w, 'order_id' cannot be empty", errInvalidOrderID)
|
|
}
|
|
var response *Order
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualCancelOrderEPL, http.MethodDelete, futuresPath+settle.Item.Lower+"/orders/"+orderID, nil, nil, &response)
|
|
}
|
|
|
|
// AmendFuturesOrder amends an existing futures order
|
|
func (e *Exchange) AmendFuturesOrder(ctx context.Context, settle currency.Code, orderID string, arg AmendFuturesOrderParam) (*Order, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if orderID == "" {
|
|
return nil, fmt.Errorf("%w, 'order_id' cannot be empty", errInvalidOrderID)
|
|
}
|
|
if arg.Size <= 0 && arg.Price <= 0 {
|
|
return nil, errors.New("missing update 'size' or 'price', please specify 'size' or 'price' or both information")
|
|
}
|
|
var response *Order
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualAmendOrderEPL, http.MethodPut, futuresPath+settle.Item.Lower+"/orders/"+orderID, nil, &arg, &response)
|
|
}
|
|
|
|
// GetMyFuturesTradingHistory retrieves authenticated account's futures trading history
|
|
func (e *Exchange) GetMyFuturesTradingHistory(ctx context.Context, settle currency.Code, lastID, orderID string, contract currency.Pair, limit, offset, countTotal uint64) ([]TradingHistoryItem, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if orderID != "" {
|
|
params.Set("order", orderID)
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if lastID != "" {
|
|
params.Set("last_id", lastID)
|
|
}
|
|
if countTotal == 1 {
|
|
params.Set("count_total", strconv.FormatUint(countTotal, 10))
|
|
}
|
|
var response []TradingHistoryItem
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualTradingHistoryEPL, http.MethodGet, futuresPath+settle.Item.Lower+"/my_trades", params, nil, &response)
|
|
}
|
|
|
|
// GetFuturesPositionCloseHistory lists position close history
|
|
func (e *Exchange) GetFuturesPositionCloseHistory(ctx context.Context, settle currency.Code, contract currency.Pair, limit, offset uint64, from, to time.Time) ([]PositionCloseHistoryResponse, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
var response []PositionCloseHistoryResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualClosePositionEPL, http.MethodGet, futuresPath+settle.Item.Lower+"/position_close", params, nil, &response)
|
|
}
|
|
|
|
// GetFuturesLiquidationHistory list liquidation history
|
|
func (e *Exchange) GetFuturesLiquidationHistory(ctx context.Context, settle currency.Code, contract currency.Pair, limit uint64, at time.Time) ([]LiquidationHistoryItem, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if !at.IsZero() {
|
|
params.Set("at", strconv.FormatInt(at.Unix(), 10))
|
|
}
|
|
var response []LiquidationHistoryItem
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualLiquidationHistoryEPL, http.MethodGet, futuresPath+settle.Item.Lower+"/liquidates", params, nil, &response)
|
|
}
|
|
|
|
// CountdownCancelOrders represents a trigger time response
|
|
func (e *Exchange) CountdownCancelOrders(ctx context.Context, settle currency.Code, arg CountdownParams) (*TriggerTimeResponse, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if arg.Timeout < 0 {
|
|
return nil, errInvalidTimeout
|
|
}
|
|
var response *TriggerTimeResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualCancelTriggerOrdersEPL, http.MethodPost, futuresPath+settle.Item.Lower+"/countdown_cancel_all", nil, &arg, &response)
|
|
}
|
|
|
|
// CreatePriceTriggeredFuturesOrder create a price-triggered order
|
|
func (e *Exchange) CreatePriceTriggeredFuturesOrder(ctx context.Context, settle currency.Code, arg *FuturesPriceTriggeredOrderParam) (*OrderID, error) {
|
|
if arg == nil {
|
|
return nil, errNilArgument
|
|
}
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if arg.Initial.Contract.IsEmpty() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
if arg.Initial.Price < 0 {
|
|
return nil, fmt.Errorf("%w, price must be greater than 0", errInvalidPrice)
|
|
}
|
|
if arg.Initial.TimeInForce != "" && arg.Initial.TimeInForce != gtcTIF && arg.Initial.TimeInForce != iocTIF {
|
|
return nil, fmt.Errorf("%w: %q; only 'gtc' and 'ioc' are allowed", order.ErrInvalidTimeInForce, arg.Initial.TimeInForce)
|
|
}
|
|
if arg.Trigger.StrategyType != 0 && arg.Trigger.StrategyType != 1 {
|
|
return nil, errors.New("strategy type must be 0 or 1, 0: by price, and 1: by price gap")
|
|
}
|
|
if arg.Trigger.Rule != 1 && arg.Trigger.Rule != 2 {
|
|
return nil, errors.New("invalid trigger condition('rule') value, rule must be 1 or 2")
|
|
}
|
|
if arg.Trigger.PriceType != 0 && arg.Trigger.PriceType != 1 && arg.Trigger.PriceType != 2 {
|
|
return nil, errors.New("price type must be 0, 1 or 2")
|
|
}
|
|
if arg.Trigger.OrderType != "" &&
|
|
arg.Trigger.OrderType != "close-long-order" &&
|
|
arg.Trigger.OrderType != "close-short-order" &&
|
|
arg.Trigger.OrderType != "close-long-position" &&
|
|
arg.Trigger.OrderType != "close-short-position" &&
|
|
arg.Trigger.OrderType != "plan-close-long-position" &&
|
|
arg.Trigger.OrderType != "plan-close-short-position" {
|
|
return nil, errors.New("invalid order type, only 'close-long-order', 'close-short-order', 'close-long-position', 'close-short-position', 'plan-close-long-position', and 'plan-close-short-position'")
|
|
}
|
|
var response *OrderID
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualSubmitTriggerOrderEPL, http.MethodPost, futuresPath+settle.Item.Lower+priceOrdersPaths, nil, &arg, &response)
|
|
}
|
|
|
|
// ListAllFuturesAutoOrders lists all open orders
|
|
func (e *Exchange) ListAllFuturesAutoOrders(ctx context.Context, status string, settle currency.Code, contract currency.Pair, limit, offset uint64) ([]PriceTriggeredOrder, error) {
|
|
if status != statusOpen && status != statusFinished {
|
|
return nil, fmt.Errorf("%w status: %s", errInvalidOrderStatus, status)
|
|
}
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
params.Set("status", status)
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
var response []PriceTriggeredOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualListOpenOrdersEPL, http.MethodGet, futuresPath+settle.Item.Lower+priceOrdersPaths, params, nil, &response)
|
|
}
|
|
|
|
// CancelAllFuturesOpenOrders cancels all futures open orders
|
|
func (e *Exchange) CancelAllFuturesOpenOrders(ctx context.Context, settle currency.Code, contract currency.Pair) ([]PriceTriggeredOrder, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", contract.String())
|
|
var response []PriceTriggeredOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualCancelOpenOrdersEPL, http.MethodDelete, futuresPath+settle.Item.Lower+priceOrdersPaths, params, nil, &response)
|
|
}
|
|
|
|
// GetSingleFuturesPriceTriggeredOrder retrieves a single price triggered order
|
|
func (e *Exchange) GetSingleFuturesPriceTriggeredOrder(ctx context.Context, settle currency.Code, orderID string) (*PriceTriggeredOrder, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if orderID == "" {
|
|
return nil, errInvalidOrderID
|
|
}
|
|
var response *PriceTriggeredOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualGetTriggerOrderEPL, http.MethodGet, futuresPath+settle.Item.Lower+"/price_orders/"+orderID, nil, nil, &response)
|
|
}
|
|
|
|
// CancelFuturesPriceTriggeredOrder cancel a price-triggered order
|
|
func (e *Exchange) CancelFuturesPriceTriggeredOrder(ctx context.Context, settle currency.Code, orderID string) (*PriceTriggeredOrder, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if orderID == "" {
|
|
return nil, errInvalidOrderID
|
|
}
|
|
var response *PriceTriggeredOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualCancelTriggerOrderEPL, http.MethodDelete, futuresPath+settle.Item.Lower+"/price_orders/"+orderID, nil, nil, &response)
|
|
}
|
|
|
|
// *************************************** Delivery ***************************************
|
|
|
|
// GetAllDeliveryContracts retrieves all futures contracts
|
|
func (e *Exchange) GetAllDeliveryContracts(ctx context.Context, settle currency.Code) ([]DeliveryContract, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
var contracts []DeliveryContract
|
|
return contracts, e.SendHTTPRequest(ctx, exchange.RestSpot, publicDeliveryContractsEPL, deliveryPath+settle.Item.Lower+"/contracts", &contracts)
|
|
}
|
|
|
|
// GetDeliveryContract retrieves a single delivery contract instance
|
|
func (e *Exchange) GetDeliveryContract(ctx context.Context, settle currency.Code, contract currency.Pair) (*DeliveryContract, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
var deliveryContract *DeliveryContract
|
|
return deliveryContract, e.SendHTTPRequest(ctx, exchange.RestSpot, publicDeliveryContractsEPL, deliveryPath+settle.Item.Lower+"/contracts/"+contract.String(), &deliveryContract)
|
|
}
|
|
|
|
// GetDeliveryOrderbook delivery orderbook
|
|
func (e *Exchange) GetDeliveryOrderbook(ctx context.Context, settle currency.Code, interval string, contract currency.Pair, limit uint64, withOrderbookID bool) (*Orderbook, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, errInvalidOrMissingContractParam
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", contract.String())
|
|
if interval != "" {
|
|
params.Set("interval", interval)
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if withOrderbookID {
|
|
params.Set("with_id", strconv.FormatBool(withOrderbookID))
|
|
}
|
|
var orderbook *Orderbook
|
|
return orderbook, e.SendHTTPRequest(ctx, exchange.RestSpot, publicOrderbookDeliveryEPL, common.EncodeURLValues(deliveryPath+settle.Item.Lower+"/order_book", params), &orderbook)
|
|
}
|
|
|
|
// GetDeliveryTradingHistory retrieves futures trading history
|
|
func (e *Exchange) GetDeliveryTradingHistory(ctx context.Context, settle currency.Code, lastID string, contract currency.Pair, limit uint64, from, to time.Time) ([]TradingHistoryItem, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, errInvalidOrMissingContractParam
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", contract.String())
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if lastID != "" {
|
|
params.Set("last_id", lastID)
|
|
}
|
|
var histories []TradingHistoryItem
|
|
return histories, e.SendHTTPRequest(ctx, exchange.RestSpot, publicTradingHistoryDeliveryEPL, common.EncodeURLValues(deliveryPath+settle.Item.Lower+"/trades", params), &histories)
|
|
}
|
|
|
|
// GetDeliveryFuturesCandlesticks retrieves specified contract candlesticks
|
|
func (e *Exchange) GetDeliveryFuturesCandlesticks(ctx context.Context, settle currency.Code, contract currency.Pair, from, to time.Time, limit uint64, interval kline.Interval) ([]FuturesCandlestick, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, errInvalidOrMissingContractParam
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", contract.Upper().String())
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if int64(interval) != 0 {
|
|
intervalString, err := getIntervalString(interval)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("interval", intervalString)
|
|
}
|
|
var candlesticks []FuturesCandlestick
|
|
return candlesticks, e.SendHTTPRequest(ctx, exchange.RestSpot, publicCandleSticksDeliveryEPL, common.EncodeURLValues(deliveryPath+settle.Item.Lower+"/candlesticks", params), &candlesticks)
|
|
}
|
|
|
|
// GetDeliveryFutureTickers retrieves futures ticker information for a specific settle and contract info.
|
|
func (e *Exchange) GetDeliveryFutureTickers(ctx context.Context, settle currency.Code, contract currency.Pair) ([]FuturesTicker, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
var tickers []FuturesTicker
|
|
return tickers, e.SendHTTPRequest(ctx, exchange.RestSpot, publicTickersDeliveryEPL, common.EncodeURLValues(deliveryPath+settle.Item.Lower+"/tickers", params), &tickers)
|
|
}
|
|
|
|
// GetDeliveryInsuranceBalanceHistory retrieves delivery futures insurance balance history
|
|
func (e *Exchange) GetDeliveryInsuranceBalanceHistory(ctx context.Context, settle currency.Code, limit uint64) ([]InsuranceBalance, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var balances []InsuranceBalance
|
|
return balances, e.SendHTTPRequest(ctx, exchange.RestSpot, publicInsuranceDeliveryEPL, common.EncodeURLValues(deliveryPath+settle.Item.Lower+"/insurance", params), &balances)
|
|
}
|
|
|
|
// GetDeliveryFuturesAccounts retrieves futures account
|
|
func (e *Exchange) GetDeliveryFuturesAccounts(ctx context.Context, settle currency.Code) (*FuturesAccount, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
var response *FuturesAccount
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryAccountEPL, http.MethodGet, deliveryPath+settle.Item.Lower+"/accounts", nil, nil, &response)
|
|
}
|
|
|
|
// GetDeliveryAccountBooks retrieves account books
|
|
func (e *Exchange) GetDeliveryAccountBooks(ctx context.Context, settle currency.Code, limit uint64, from, to time.Time, changingType string) ([]AccountBookItem, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
if changingType != "" {
|
|
params.Set("type", changingType)
|
|
}
|
|
var response []AccountBookItem
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryAccountBooksEPL, http.MethodGet, deliveryPath+settle.Item.Lower+"/account_book", params, nil, &response)
|
|
}
|
|
|
|
// GetAllDeliveryPositionsOfUser retrieves all positions of user
|
|
func (e *Exchange) GetAllDeliveryPositionsOfUser(ctx context.Context, settle currency.Code) (*Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
var response *Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryPositionsEPL, http.MethodGet, deliveryPath+settle.Item.Lower+"/positions", nil, nil, &response)
|
|
}
|
|
|
|
// GetSingleDeliveryPosition get single position
|
|
func (e *Exchange) GetSingleDeliveryPosition(ctx context.Context, settle currency.Code, contract currency.Pair) (*Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
var response *Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryPositionsEPL, http.MethodGet, deliveryPath+settle.Item.Lower+positionsPath+contract.String(), nil, nil, &response)
|
|
}
|
|
|
|
// UpdateDeliveryPositionMargin updates position margin
|
|
func (e *Exchange) UpdateDeliveryPositionMargin(ctx context.Context, settle currency.Code, change float64, contract currency.Pair) (*Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
if change <= 0 {
|
|
return nil, fmt.Errorf("%w, futures margin change must be positive", errChangeHasToBePositive)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("change", strconv.FormatFloat(change, 'f', -1, 64))
|
|
var response *Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryUpdateMarginEPL, http.MethodPost, deliveryPath+settle.Item.Lower+positionsPath+contract.String()+"/margin", params, nil, &response)
|
|
}
|
|
|
|
// UpdateDeliveryPositionLeverage updates position leverage
|
|
func (e *Exchange) UpdateDeliveryPositionLeverage(ctx context.Context, settle currency.Code, contract currency.Pair, leverage float64) (*Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
if leverage < 0 {
|
|
return nil, errInvalidLeverage
|
|
}
|
|
params := url.Values{}
|
|
params.Set("leverage", strconv.FormatFloat(leverage, 'f', -1, 64))
|
|
var response *Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx,
|
|
exchange.RestSpot, deliveryUpdateLeverageEPL, http.MethodPost, deliveryPath+settle.Item.Lower+positionsPath+contract.String()+"/leverage", params, nil, &response)
|
|
}
|
|
|
|
// UpdateDeliveryPositionRiskLimit update position risk limit
|
|
func (e *Exchange) UpdateDeliveryPositionRiskLimit(ctx context.Context, settle currency.Code, contract currency.Pair, riskLimit uint64) (*Position, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("risk_limit", strconv.FormatUint(riskLimit, 10))
|
|
var response *Position
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryUpdateRiskLimitEPL, http.MethodPost, deliveryPath+settle.Item.Lower+positionsPath+contract.String()+"/risk_limit", params, nil, &response)
|
|
}
|
|
|
|
// PlaceDeliveryOrder create a futures order
|
|
// Zero-filled order cannot be retrieved 10 minutes after order cancellation
|
|
func (e *Exchange) PlaceDeliveryOrder(ctx context.Context, arg *ContractOrderCreateParams) (*Order, error) {
|
|
if err := arg.validate(true); err != nil {
|
|
return nil, err
|
|
}
|
|
var response *Order
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliverySubmitOrderEPL, http.MethodPost, deliveryPath+arg.Settle.Item.Lower+ordersPath, nil, &arg, &response)
|
|
}
|
|
|
|
// GetDeliveryOrders list futures orders
|
|
// Zero-filled order cannot be retrieved 10 minutes after order cancellation
|
|
func (e *Exchange) GetDeliveryOrders(ctx context.Context, contract currency.Pair, status string, settle currency.Code, lastID string, limit, offset uint64, countTotal bool) ([]Order, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if !contract.IsEmpty() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if status != statusOpen && status != statusFinished {
|
|
return nil, fmt.Errorf("%w, only 'open' and 'finished' status are supported", errInvalidOrderStatus)
|
|
}
|
|
params.Set("status", status)
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if lastID != "" {
|
|
params.Set("last_id", lastID)
|
|
}
|
|
if countTotal && status != statusOpen {
|
|
params.Set("count_total", "1")
|
|
}
|
|
var response []Order
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryGetOrdersEPL, http.MethodGet, deliveryPath+settle.Item.Lower+ordersPath, params, nil, &response)
|
|
}
|
|
|
|
// CancelMultipleDeliveryOrders cancel all open orders matched
|
|
// Zero-filled order cannot be retrieved 10 minutes after order cancellation
|
|
func (e *Exchange) CancelMultipleDeliveryOrders(ctx context.Context, contract currency.Pair, side string, settle currency.Code) ([]Order, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
params := url.Values{}
|
|
if side == order.Ask.Lower() || side == order.Bid.Lower() {
|
|
params.Set("side", side)
|
|
}
|
|
params.Set("contract", contract.String())
|
|
var response []Order
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryCancelOrdersEPL, http.MethodDelete, deliveryPath+settle.Item.Lower+ordersPath, params, nil, &response)
|
|
}
|
|
|
|
// GetSingleDeliveryOrder Get a single order
|
|
// Zero-filled order cannot be retrieved 10 minutes after order cancellation
|
|
func (e *Exchange) GetSingleDeliveryOrder(ctx context.Context, settle currency.Code, orderID string) (*Order, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if orderID == "" {
|
|
return nil, fmt.Errorf("%w, 'order_id' cannot be empty", errInvalidOrderID)
|
|
}
|
|
var response *Order
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryGetOrderEPL, http.MethodGet, deliveryPath+settle.Item.Lower+"/orders/"+orderID, nil, nil, &response)
|
|
}
|
|
|
|
// CancelSingleDeliveryOrder cancel a single order
|
|
func (e *Exchange) CancelSingleDeliveryOrder(ctx context.Context, settle currency.Code, orderID string) (*Order, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if orderID == "" {
|
|
return nil, fmt.Errorf("%w, 'order_id' cannot be empty", errInvalidOrderID)
|
|
}
|
|
var response *Order
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryCancelOrderEPL, http.MethodDelete, deliveryPath+settle.Item.Lower+"/orders/"+orderID, nil, nil, &response)
|
|
}
|
|
|
|
// GetMyDeliveryTradingHistory retrieves authenticated account delivery futures trading history
|
|
func (e *Exchange) GetMyDeliveryTradingHistory(ctx context.Context, settle currency.Code, orderID string, contract currency.Pair, limit, offset, countTotal uint64, lastID string) ([]TradingHistoryItem, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if orderID != "" {
|
|
params.Set("order", orderID)
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if lastID != "" {
|
|
params.Set("last_id", lastID)
|
|
}
|
|
if countTotal == 1 {
|
|
params.Set("count_total", strconv.FormatUint(countTotal, 10))
|
|
}
|
|
var response []TradingHistoryItem
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryTradingHistoryEPL, http.MethodGet, deliveryPath+settle.Item.Lower+"/my_trades", params, nil, &response)
|
|
}
|
|
|
|
// GetDeliveryPositionCloseHistory retrieves position history
|
|
func (e *Exchange) GetDeliveryPositionCloseHistory(ctx context.Context, settle currency.Code, contract currency.Pair, limit, offset uint64, from, to time.Time) ([]PositionCloseHistoryResponse, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
var response []PositionCloseHistoryResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryCloseHistoryEPL, http.MethodGet, deliveryPath+settle.Item.Lower+"/position_close", params, nil, &response)
|
|
}
|
|
|
|
// GetDeliveryLiquidationHistory lists liquidation history
|
|
func (e *Exchange) GetDeliveryLiquidationHistory(ctx context.Context, settle currency.Code, contract currency.Pair, limit uint64, at time.Time) ([]LiquidationHistoryItem, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if !at.IsZero() {
|
|
params.Set("at", strconv.FormatInt(at.Unix(), 10))
|
|
}
|
|
var response []LiquidationHistoryItem
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryLiquidationHistoryEPL, http.MethodGet, deliveryPath+settle.Item.Lower+"/liquidates", params, nil, &response)
|
|
}
|
|
|
|
// GetDeliverySettlementHistory retrieves settlement history
|
|
func (e *Exchange) GetDeliverySettlementHistory(ctx context.Context, settle currency.Code, contract currency.Pair, limit uint64, at time.Time) ([]SettlementHistoryItem, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if !at.IsZero() {
|
|
params.Set("at", strconv.FormatInt(at.Unix(), 10))
|
|
}
|
|
var response []SettlementHistoryItem
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliverySettlementHistoryEPL, http.MethodGet, deliveryPath+settle.Item.Lower+"/settlements", params, nil, &response)
|
|
}
|
|
|
|
// GetDeliveryPriceTriggeredOrder creates a price-triggered order
|
|
func (e *Exchange) GetDeliveryPriceTriggeredOrder(ctx context.Context, settle currency.Code, arg *FuturesPriceTriggeredOrderParam) (*OrderID, error) {
|
|
if arg == nil {
|
|
return nil, errNilArgument
|
|
}
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if arg.Initial.Contract.IsEmpty() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
if arg.Initial.Price < 0 {
|
|
return nil, fmt.Errorf("%w, price must be greater than 0", errInvalidPrice)
|
|
}
|
|
if arg.Initial.Size <= 0 {
|
|
return nil, errors.New("invalid argument: initial.size out of range")
|
|
}
|
|
if arg.Initial.TimeInForce != "" &&
|
|
arg.Initial.TimeInForce != gtcTIF && arg.Initial.TimeInForce != iocTIF {
|
|
return nil, fmt.Errorf("%w: %q; only 'gtc' and 'ioc' are allowed", order.ErrUnsupportedTimeInForce, arg.Initial.TimeInForce)
|
|
}
|
|
if arg.Trigger.StrategyType != 0 && arg.Trigger.StrategyType != 1 {
|
|
return nil, errors.New("strategy type must be 0 or 1, 0: by price, and 1: by price gap")
|
|
}
|
|
if arg.Trigger.Rule != 1 && arg.Trigger.Rule != 2 {
|
|
return nil, errors.New("invalid trigger condition('rule') value, rule must be 1 or 2")
|
|
}
|
|
if arg.Trigger.PriceType != 0 && arg.Trigger.PriceType != 1 && arg.Trigger.PriceType != 2 {
|
|
return nil, errors.New("price type must be 0 or 1 or 2")
|
|
}
|
|
if arg.Trigger.Price <= 0 {
|
|
return nil, errors.New("invalid argument: trigger.price")
|
|
}
|
|
if arg.Trigger.OrderType != "" &&
|
|
arg.Trigger.OrderType != "close-long-order" &&
|
|
arg.Trigger.OrderType != "close-short-order" &&
|
|
arg.Trigger.OrderType != "close-long-position" &&
|
|
arg.Trigger.OrderType != "close-short-position" &&
|
|
arg.Trigger.OrderType != "plan-close-long-position" &&
|
|
arg.Trigger.OrderType != "plan-close-short-position" {
|
|
return nil, errors.New("invalid order type, only 'close-long-order', 'close-short-order', 'close-long-position', 'close-short-position', 'plan-close-long-position', and 'plan-close-short-position'")
|
|
}
|
|
var response *OrderID
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryGetTriggerOrderEPL, http.MethodPost, deliveryPath+settle.Item.Lower+priceOrdersPaths, nil, &arg, &response)
|
|
}
|
|
|
|
// GetDeliveryAllAutoOrder retrieves all auto orders
|
|
func (e *Exchange) GetDeliveryAllAutoOrder(ctx context.Context, status string, settle currency.Code, contract currency.Pair, limit, offset uint64) ([]PriceTriggeredOrder, error) {
|
|
if status != statusOpen && status != statusFinished {
|
|
return nil, fmt.Errorf("%w status %s", errInvalidOrderStatus, status)
|
|
}
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
params := url.Values{}
|
|
params.Set("status", status)
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
var response []PriceTriggeredOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryAutoOrdersEPL, http.MethodGet, deliveryPath+settle.Item.Lower+priceOrdersPaths, params, nil, &response)
|
|
}
|
|
|
|
// CancelAllDeliveryPriceTriggeredOrder cancels all delivery price triggered orders
|
|
func (e *Exchange) CancelAllDeliveryPriceTriggeredOrder(ctx context.Context, settle currency.Code, contract currency.Pair) ([]PriceTriggeredOrder, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, fmt.Errorf("%w, currency pair for contract must not be empty", errInvalidOrMissingContractParam)
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", contract.String())
|
|
var response []PriceTriggeredOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryCancelTriggerOrdersEPL, http.MethodDelete, deliveryPath+settle.Item.Lower+priceOrdersPaths, params, nil, &response)
|
|
}
|
|
|
|
// GetSingleDeliveryPriceTriggeredOrder retrieves a single price triggered order
|
|
func (e *Exchange) GetSingleDeliveryPriceTriggeredOrder(ctx context.Context, settle currency.Code, orderID string) (*PriceTriggeredOrder, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if orderID == "" {
|
|
return nil, errInvalidOrderID
|
|
}
|
|
var response *PriceTriggeredOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryGetTriggerOrderEPL, http.MethodGet, deliveryPath+settle.Item.Lower+"/price_orders/"+orderID, nil, nil, &response)
|
|
}
|
|
|
|
// CancelDeliveryPriceTriggeredOrder cancel a price-triggered order
|
|
func (e *Exchange) CancelDeliveryPriceTriggeredOrder(ctx context.Context, settle currency.Code, orderID string) (*PriceTriggeredOrder, error) {
|
|
if settle.IsEmpty() {
|
|
return nil, errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
if orderID == "" {
|
|
return nil, errInvalidOrderID
|
|
}
|
|
var response *PriceTriggeredOrder
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryCancelTriggerOrderEPL, http.MethodDelete, deliveryPath+settle.Item.Lower+"/price_orders/"+orderID, nil, nil, &response)
|
|
}
|
|
|
|
// ********************************** Options ***************************************************
|
|
|
|
// GetAllOptionsUnderlyings retrieves all option underlyings
|
|
func (e *Exchange) GetAllOptionsUnderlyings(ctx context.Context) ([]OptionUnderlying, error) {
|
|
var response []OptionUnderlying
|
|
return response, e.SendHTTPRequest(ctx, exchange.RestSpot, publicUnderlyingOptionsEPL, gateioOptionUnderlyings, &response)
|
|
}
|
|
|
|
// GetExpirationTime return the expiration time for the provided underlying.
|
|
func (e *Exchange) GetExpirationTime(ctx context.Context, underlying string) (time.Time, error) {
|
|
if underlying == "" {
|
|
return time.Time{}, errInvalidUnderlying
|
|
}
|
|
var timestamps []types.Time
|
|
err := e.SendHTTPRequest(ctx, exchange.RestSpot, publicExpirationOptionsEPL, gateioOptionExpiration+"?underlying="+underlying, ×tamps)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
if len(timestamps) == 0 {
|
|
return time.Time{}, errNoValidResponseFromServer
|
|
}
|
|
return timestamps[0].Time(), nil
|
|
}
|
|
|
|
// GetAllContractOfUnderlyingWithinExpiryDate retrieves list of contracts of the specified underlying and expiry time.
|
|
func (e *Exchange) GetAllContractOfUnderlyingWithinExpiryDate(ctx context.Context, underlying string, expTime time.Time) ([]OptionContract, error) {
|
|
params := url.Values{}
|
|
if underlying == "" {
|
|
return nil, errInvalidUnderlying
|
|
}
|
|
params.Set("underlying", underlying)
|
|
if !expTime.IsZero() {
|
|
params.Set("expires", strconv.FormatInt(expTime.Unix(), 10))
|
|
}
|
|
var contracts []OptionContract
|
|
return contracts, e.SendHTTPRequest(ctx, exchange.RestSpot, publicContractsOptionsEPL, common.EncodeURLValues(gateioOptionContracts, params), &contracts)
|
|
}
|
|
|
|
// GetOptionsSpecifiedContractDetail query specified contract detail
|
|
func (e *Exchange) GetOptionsSpecifiedContractDetail(ctx context.Context, contract currency.Pair) (*OptionContract, error) {
|
|
if contract.IsInvalid() {
|
|
return nil, errInvalidOrMissingContractParam
|
|
}
|
|
var contr *OptionContract
|
|
return contr, e.SendHTTPRequest(ctx, exchange.RestSpot, publicContractsOptionsEPL, gateioOptionContracts+"/"+contract.String(), &contr)
|
|
}
|
|
|
|
// GetSettlementHistory retrieves list of settlement history
|
|
func (e *Exchange) GetSettlementHistory(ctx context.Context, underlying string, offset, limit uint64, from, to time.Time) ([]OptionSettlement, error) {
|
|
if underlying == "" {
|
|
return nil, errInvalidUnderlying
|
|
}
|
|
params := url.Values{}
|
|
params.Set("underlying", underlying)
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
var settlements []OptionSettlement
|
|
return settlements, e.SendHTTPRequest(ctx, exchange.RestSpot, publicSettlementOptionsEPL, common.EncodeURLValues(gateioOptionSettlement, params), &settlements)
|
|
}
|
|
|
|
// GetOptionsSpecifiedContractsSettlement retrieve a single contract settlement detail passing the underlying and contract name
|
|
func (e *Exchange) GetOptionsSpecifiedContractsSettlement(ctx context.Context, contract currency.Pair, underlying string, at int64) (*OptionSettlement, error) {
|
|
if underlying == "" {
|
|
return nil, errInvalidUnderlying
|
|
}
|
|
if contract.IsInvalid() {
|
|
return nil, errInvalidOrMissingContractParam
|
|
}
|
|
params := url.Values{}
|
|
params.Set("underlying", underlying)
|
|
params.Set("at", strconv.FormatInt(at, 10))
|
|
var settlement *OptionSettlement
|
|
return settlement, e.SendHTTPRequest(ctx, exchange.RestSpot, publicSettlementOptionsEPL, common.EncodeURLValues(gateioOptionSettlement+"/"+contract.String(), params), &settlement)
|
|
}
|
|
|
|
// GetMyOptionsSettlements retrieves accounts option settlements.
|
|
func (e *Exchange) GetMyOptionsSettlements(ctx context.Context, underlying string, contract currency.Pair, offset, limit uint64, to time.Time) ([]MyOptionSettlement, error) {
|
|
if underlying == "" {
|
|
return nil, errInvalidUnderlying
|
|
}
|
|
params := url.Values{}
|
|
params.Set("underlying", underlying)
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if to.After(time.Now()) {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var settlements []MyOptionSettlement
|
|
return settlements, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, optionsSettlementsEPL, http.MethodGet, gateioOptionMySettlements, params, nil, &settlements)
|
|
}
|
|
|
|
// GetOptionsOrderbook returns the orderbook data for the given contract.
|
|
func (e *Exchange) GetOptionsOrderbook(ctx context.Context, contract currency.Pair, interval string, limit uint64, withOrderbookID bool) (*Orderbook, error) {
|
|
if contract.IsEmpty() {
|
|
return nil, currency.ErrCurrencyPairEmpty
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", strings.ToUpper(contract.String()))
|
|
if interval != "" {
|
|
params.Set("interval", interval)
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
params.Set("with_id", strconv.FormatBool(withOrderbookID))
|
|
var response *Orderbook
|
|
return response, e.SendHTTPRequest(ctx, exchange.RestSpot, publicOrderbookOptionsEPL, common.EncodeURLValues(gateioOptionsOrderbook, params), &response)
|
|
}
|
|
|
|
// GetOptionAccounts lists option accounts
|
|
func (e *Exchange) GetOptionAccounts(ctx context.Context) (*OptionAccount, error) {
|
|
var resp *OptionAccount
|
|
return resp, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, optionsAccountsEPL, http.MethodGet, gateioOptionAccounts, nil, nil, &resp)
|
|
}
|
|
|
|
// GetAccountChangingHistory retrieves list of account changing history
|
|
func (e *Exchange) GetAccountChangingHistory(ctx context.Context, offset, limit uint64, from, to time.Time, changingType string) ([]AccountBook, error) {
|
|
params := url.Values{}
|
|
if changingType != "" {
|
|
params.Set("type", changingType)
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() && ((!from.IsZero() && to.After(from)) || to.Before(time.Now())) {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
var accountBook []AccountBook
|
|
return accountBook, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, optionsAccountBooksEPL, http.MethodGet, gateioOptionsAccountbook, params, nil, &accountBook)
|
|
}
|
|
|
|
// GetUsersPositionSpecifiedUnderlying lists user's positions of specified underlying
|
|
func (e *Exchange) GetUsersPositionSpecifiedUnderlying(ctx context.Context, underlying string) ([]UsersPositionForUnderlying, error) {
|
|
params := url.Values{}
|
|
if underlying != "" {
|
|
params.Set("underlying", underlying)
|
|
}
|
|
var response []UsersPositionForUnderlying
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, optionsPositions, http.MethodGet, gateioOptionsPosition, params, nil, &response)
|
|
}
|
|
|
|
// GetSpecifiedContractPosition retrieves specified contract position
|
|
func (e *Exchange) GetSpecifiedContractPosition(ctx context.Context, contract currency.Pair) (*UsersPositionForUnderlying, error) {
|
|
if contract.IsInvalid() {
|
|
return nil, errInvalidOrMissingContractParam
|
|
}
|
|
var response *UsersPositionForUnderlying
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, optionsPositions, http.MethodGet, gateioOptionsPosition+"/"+contract.String(), nil, nil, &response)
|
|
}
|
|
|
|
// GetUsersLiquidationHistoryForSpecifiedUnderlying retrieves user's liquidation history of specified underlying
|
|
func (e *Exchange) GetUsersLiquidationHistoryForSpecifiedUnderlying(ctx context.Context, underlying string, contract currency.Pair) ([]ContractClosePosition, error) {
|
|
if underlying == "" {
|
|
return nil, errInvalidUnderlying
|
|
}
|
|
params := url.Values{}
|
|
params.Set("underlying", underlying)
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
var response []ContractClosePosition
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, optionsLiquidationHistoryEPL, http.MethodGet, gateioOptionsPositionClose, params, nil, &response)
|
|
}
|
|
|
|
// PlaceOptionOrder creates an options order
|
|
func (e *Exchange) PlaceOptionOrder(ctx context.Context, arg *OptionOrderParam) (*OptionOrderResponse, error) {
|
|
if arg.Contract == "" {
|
|
return nil, errInvalidOrMissingContractParam
|
|
}
|
|
if arg.OrderSize == 0 {
|
|
return nil, errInvalidOrderSize
|
|
}
|
|
if arg.Iceberg < 0 {
|
|
arg.Iceberg = 0
|
|
}
|
|
if arg.TimeInForce != gtcTIF && arg.TimeInForce != iocTIF && arg.TimeInForce != pocTIF {
|
|
arg.TimeInForce = ""
|
|
}
|
|
if arg.TimeInForce == iocTIF || arg.Price < 0 {
|
|
arg.Price = 0
|
|
}
|
|
var response *OptionOrderResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, optionsSubmitOrderEPL, http.MethodPost, gateioOptionsOrders, nil, &arg, &response)
|
|
}
|
|
|
|
// GetOptionFuturesOrders retrieves futures orders
|
|
func (e *Exchange) GetOptionFuturesOrders(ctx context.Context, contract currency.Pair, underlying, status string, offset, limit uint64, from, to time.Time) ([]OptionOrderResponse, error) {
|
|
params := url.Values{}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if underlying != "" {
|
|
params.Set("underlying", underlying)
|
|
}
|
|
status = strings.ToLower(status)
|
|
if status == statusOpen || status == statusFinished {
|
|
params.Set("status", status)
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() && ((!from.IsZero() && to.After(from)) || to.Before(time.Now())) {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
var response []OptionOrderResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, optionsOrdersEPL, http.MethodGet, gateioOptionsOrders, params, nil, &response)
|
|
}
|
|
|
|
// CancelMultipleOptionOpenOrders cancels all open orders matched
|
|
func (e *Exchange) CancelMultipleOptionOpenOrders(ctx context.Context, contract currency.Pair, underlying, side string) ([]OptionOrderResponse, error) {
|
|
params := url.Values{}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if underlying != "" {
|
|
params.Set("underlying", underlying)
|
|
}
|
|
if side != "" {
|
|
params.Set("side", side)
|
|
}
|
|
var response []OptionOrderResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, optionsCancelOrdersEPL, http.MethodDelete, gateioOptionsOrders, params, nil, &response)
|
|
}
|
|
|
|
// GetSingleOptionOrder retrieves a single option order
|
|
func (e *Exchange) GetSingleOptionOrder(ctx context.Context, orderID string) (*OptionOrderResponse, error) {
|
|
if orderID == "" {
|
|
return nil, errInvalidOrderID
|
|
}
|
|
var o *OptionOrderResponse
|
|
return o, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, optionsOrderEPL, http.MethodGet, gateioOptionsOrders+"/"+orderID, nil, nil, &o)
|
|
}
|
|
|
|
// CancelOptionSingleOrder cancel a single order.
|
|
func (e *Exchange) CancelOptionSingleOrder(ctx context.Context, orderID string) (*OptionOrderResponse, error) {
|
|
if orderID == "" {
|
|
return nil, errInvalidOrderID
|
|
}
|
|
var response *OptionOrderResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, optionsCancelOrderEPL, http.MethodDelete, "options/orders/"+orderID, nil, nil, &response)
|
|
}
|
|
|
|
// GetMyOptionsTradingHistory retrieves authenticated account's option trading history
|
|
func (e *Exchange) GetMyOptionsTradingHistory(ctx context.Context, underlying string, contract currency.Pair, offset, limit uint64, from, to time.Time) ([]OptionTradingHistory, error) {
|
|
if underlying == "" {
|
|
return nil, errInvalidUnderlying
|
|
}
|
|
params := url.Values{}
|
|
params.Set("underlying", underlying)
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() && ((!from.IsZero() && to.After(from)) || to.Before(time.Now())) {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
var resp []OptionTradingHistory
|
|
return resp, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, optionsTradingHistoryEPL, http.MethodGet, gateioOptionsMyTrades, params, nil, &resp)
|
|
}
|
|
|
|
// GetOptionsTickers lists tickers of options contracts
|
|
func (e *Exchange) GetOptionsTickers(ctx context.Context, underlying string) ([]OptionsTicker, error) {
|
|
if underlying == "" {
|
|
return nil, errInvalidUnderlying
|
|
}
|
|
underlying = strings.ToUpper(underlying)
|
|
var response []OptionsTicker
|
|
return response, e.SendHTTPRequest(ctx, exchange.RestSpot, publicTickerOptionsEPL, gateioOptionsTickers+"?underlying="+underlying, &response)
|
|
}
|
|
|
|
// GetOptionUnderlyingTickers retrieves options underlying ticker
|
|
func (e *Exchange) GetOptionUnderlyingTickers(ctx context.Context, underlying string) (*OptionsUnderlyingTicker, error) {
|
|
if underlying == "" {
|
|
return nil, errInvalidUnderlying
|
|
}
|
|
var respos *OptionsUnderlyingTicker
|
|
return respos, e.SendHTTPRequest(ctx, exchange.RestSpot, publicUnderlyingTickerOptionsEPL, "options/underlying/tickers/"+underlying, &respos)
|
|
}
|
|
|
|
// GetOptionFuturesCandlesticks retrieves option futures candlesticks
|
|
func (e *Exchange) GetOptionFuturesCandlesticks(ctx context.Context, contract currency.Pair, limit uint64, from, to time.Time, interval kline.Interval) ([]FuturesCandlestick, error) {
|
|
if contract.IsInvalid() {
|
|
return nil, errInvalidOrMissingContractParam
|
|
}
|
|
params := url.Values{}
|
|
params.Set("contract", contract.String())
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
intervalString, err := getIntervalString(interval)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("interval", intervalString)
|
|
var candles []FuturesCandlestick
|
|
return candles, e.SendHTTPRequest(ctx, exchange.RestSpot, publicCandleSticksOptionsEPL, common.EncodeURLValues(gateioOptionCandlesticks, params), &candles)
|
|
}
|
|
|
|
// GetOptionFuturesMarkPriceCandlesticks retrieves mark price candlesticks of an underlying
|
|
func (e *Exchange) GetOptionFuturesMarkPriceCandlesticks(ctx context.Context, underlying string, limit uint64, from, to time.Time, interval kline.Interval) ([]FuturesCandlestick, error) {
|
|
if underlying == "" {
|
|
return nil, errInvalidUnderlying
|
|
}
|
|
params := url.Values{}
|
|
params.Set("underlying", underlying)
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
if int64(interval) != 0 {
|
|
intervalString, err := getIntervalString(interval)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params.Set("interval", intervalString)
|
|
}
|
|
var candles []FuturesCandlestick
|
|
return candles, e.SendHTTPRequest(ctx, exchange.RestSpot, publicMarkpriceCandleSticksOptionsEPL, common.EncodeURLValues(gateioOptionUnderlyingCandlesticks, params), &candles)
|
|
}
|
|
|
|
// GetOptionsTradeHistory retrieves options trade history
|
|
func (e *Exchange) GetOptionsTradeHistory(ctx context.Context, contract currency.Pair, callType string, offset, limit uint64, from, to time.Time) ([]TradingHistoryItem, error) {
|
|
params := url.Values{}
|
|
callType = strings.ToUpper(callType)
|
|
if callType == "C" || callType == "P" {
|
|
params.Set("type", callType)
|
|
}
|
|
if contract.IsPopulated() {
|
|
params.Set("contract", contract.String())
|
|
}
|
|
if offset > 0 {
|
|
params.Set("offset", strconv.FormatUint(offset, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
if !from.IsZero() {
|
|
params.Set("from", strconv.FormatInt(from.Unix(), 10))
|
|
}
|
|
if !to.IsZero() {
|
|
params.Set("to", strconv.FormatInt(to.Unix(), 10))
|
|
}
|
|
var trades []TradingHistoryItem
|
|
return trades, e.SendHTTPRequest(ctx, exchange.RestSpot, publicTradeHistoryOptionsEPL, common.EncodeURLValues(gateioOptionsTrades, params), &trades)
|
|
}
|
|
|
|
// ********************************** Flash_SWAP *************************
|
|
|
|
// GetSupportedFlashSwapCurrencies retrieves all supported currencies in flash swap
|
|
func (e *Exchange) GetSupportedFlashSwapCurrencies(ctx context.Context) ([]SwapCurrencies, error) {
|
|
var currencies []SwapCurrencies
|
|
return currencies, e.SendHTTPRequest(ctx, exchange.RestSpot, publicFlashSwapEPL, gateioFlashSwapCurrencies, ¤cies)
|
|
}
|
|
|
|
// CreateFlashSwapOrder creates a new flash swap order
|
|
// initiate a flash swap preview in advance because order creation requires a preview result
|
|
func (e *Exchange) CreateFlashSwapOrder(ctx context.Context, arg FlashSwapOrderParams) (*FlashSwapOrderResponse, error) {
|
|
if arg.PreviewID == "" {
|
|
return nil, errMissingPreviewID
|
|
}
|
|
if arg.BuyCurrency.IsEmpty() {
|
|
return nil, fmt.Errorf("%w, buy currency can not empty", currency.ErrCurrencyCodeEmpty)
|
|
}
|
|
if arg.SellCurrency.IsEmpty() {
|
|
return nil, fmt.Errorf("%w, sell currency can not empty", currency.ErrCurrencyCodeEmpty)
|
|
}
|
|
if arg.SellAmount <= 0 {
|
|
return nil, fmt.Errorf("%w, sell_amount can not be less than or equal to 0", errInvalidAmount)
|
|
}
|
|
if arg.BuyAmount <= 0 {
|
|
return nil, fmt.Errorf("%w, buy_amount amount can not be less than or equal to 0", errInvalidAmount)
|
|
}
|
|
var response *FlashSwapOrderResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, flashSwapOrderEPL, http.MethodPost, gateioFlashSwapOrders, nil, &arg, &response)
|
|
}
|
|
|
|
// GetAllFlashSwapOrders retrieves list of flash swap orders filtered by the params
|
|
func (e *Exchange) GetAllFlashSwapOrders(ctx context.Context, status int, sellCurrency, buyCurrency currency.Code, reverse bool, limit, page uint64) ([]FlashSwapOrderResponse, error) {
|
|
params := url.Values{}
|
|
if status == 1 || status == 2 {
|
|
params.Set("status", strconv.Itoa(status))
|
|
}
|
|
if !sellCurrency.IsEmpty() {
|
|
params.Set("sell_currency", sellCurrency.String())
|
|
}
|
|
if !buyCurrency.IsEmpty() {
|
|
params.Set("buy_currency", buyCurrency.String())
|
|
}
|
|
params.Set("reverse", strconv.FormatBool(reverse))
|
|
if page > 0 {
|
|
params.Set("page", strconv.FormatUint(page, 10))
|
|
}
|
|
if limit > 0 {
|
|
params.Set("limit", strconv.FormatUint(limit, 10))
|
|
}
|
|
var response []FlashSwapOrderResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, flashGetOrdersEPL, http.MethodGet, gateioFlashSwapOrders, params, nil, &response)
|
|
}
|
|
|
|
// GetSingleFlashSwapOrder get a single flash swap order's detail
|
|
func (e *Exchange) GetSingleFlashSwapOrder(ctx context.Context, orderID string) (*FlashSwapOrderResponse, error) {
|
|
if orderID == "" {
|
|
return nil, fmt.Errorf("%w, flash order order_id must not be empty", errInvalidOrderID)
|
|
}
|
|
var response *FlashSwapOrderResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, flashGetOrderEPL, http.MethodGet, gateioFlashSwapOrders+"/"+orderID, nil, nil, &response)
|
|
}
|
|
|
|
// InitiateFlashSwapOrderReview initiate a flash swap order preview
|
|
func (e *Exchange) InitiateFlashSwapOrderReview(ctx context.Context, arg FlashSwapOrderParams) (*InitFlashSwapOrderPreviewResponse, error) {
|
|
if arg.PreviewID == "" {
|
|
return nil, errMissingPreviewID
|
|
}
|
|
if arg.BuyCurrency.IsEmpty() {
|
|
return nil, fmt.Errorf("%w, buy currency can not empty", currency.ErrCurrencyCodeEmpty)
|
|
}
|
|
if arg.SellCurrency.IsEmpty() {
|
|
return nil, fmt.Errorf("%w, sell currency can not empty", currency.ErrCurrencyCodeEmpty)
|
|
}
|
|
var response *InitFlashSwapOrderPreviewResponse
|
|
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, flashOrderReviewEPL, http.MethodPost, gateioFlashSwapOrdersPreview, nil, &arg, &response)
|
|
}
|
|
|
|
// ********************************* Trading Fee calculation ********************************
|
|
|
|
// GetFee returns an estimate of fee based on type of transaction
|
|
func (e *Exchange) GetFee(ctx context.Context, feeBuilder *exchange.FeeBuilder) (fee float64, err error) {
|
|
switch feeBuilder.FeeType {
|
|
case exchange.CryptocurrencyTradeFee:
|
|
feePairs, err := e.GetPersonalTradingFee(ctx, feeBuilder.Pair, currency.EMPTYCODE)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if feeBuilder.IsMaker {
|
|
fee = calculateTradingFee(feePairs.MakerFee.Float64(),
|
|
feeBuilder.PurchasePrice,
|
|
feeBuilder.Amount)
|
|
} else {
|
|
fee = calculateTradingFee(feePairs.TakerFee.Float64(),
|
|
feeBuilder.PurchasePrice,
|
|
feeBuilder.Amount)
|
|
}
|
|
case exchange.CryptocurrencyWithdrawalFee:
|
|
fee = getCryptocurrencyWithdrawalFee(feeBuilder.Pair.Base)
|
|
case exchange.OfflineTradeFee:
|
|
fee = getOfflineTradeFee(feeBuilder.PurchasePrice, feeBuilder.Amount)
|
|
}
|
|
if fee < 0 {
|
|
fee = 0
|
|
}
|
|
return fee, nil
|
|
}
|
|
|
|
// getOfflineTradeFee calculates the worst case-scenario trading fee
|
|
func getOfflineTradeFee(price, amount float64) float64 {
|
|
return 0.002 * price * amount
|
|
}
|
|
|
|
func calculateTradingFee(feeForPair, purchasePrice, amount float64) float64 {
|
|
return feeForPair * purchasePrice * amount
|
|
}
|
|
|
|
func getCryptocurrencyWithdrawalFee(c currency.Code) float64 {
|
|
return WithdrawalFees[c]
|
|
}
|
|
|
|
// GetUnderlyingFromCurrencyPair returns an underlying string from a currency pair
|
|
func (e *Exchange) GetUnderlyingFromCurrencyPair(p currency.Pair) (currency.Pair, error) {
|
|
pairString := strings.ReplaceAll(p.Upper().String(), currency.DashDelimiter, currency.UnderscoreDelimiter)
|
|
ccies := strings.Split(pairString, currency.UnderscoreDelimiter)
|
|
if len(ccies) < 2 {
|
|
return currency.EMPTYPAIR, fmt.Errorf("invalid currency pair %v", p)
|
|
}
|
|
return currency.Pair{Base: currency.NewCode(ccies[0]), Delimiter: currency.UnderscoreDelimiter, Quote: currency.NewCode(ccies[1])}, nil
|
|
}
|
|
|
|
// GetAccountDetails retrieves account details
|
|
func (e *Exchange) GetAccountDetails(ctx context.Context) (*AccountDetails, error) {
|
|
var resp *AccountDetails
|
|
return resp, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotAccountsEPL, http.MethodGet, "account/detail", nil, nil, &resp)
|
|
}
|
|
|
|
// GetUserTransactionRateLimitInfo retrieves user transaction rate limit info
|
|
func (e *Exchange) GetUserTransactionRateLimitInfo(ctx context.Context) ([]UserTransactionRateLimitInfo, error) {
|
|
var resp []UserTransactionRateLimitInfo
|
|
return resp, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotAccountsEPL, http.MethodGet, "account/rate_limit", nil, nil, &resp)
|
|
}
|
|
|
|
// validate validates the ContractOrderCreateParams
|
|
func (c *ContractOrderCreateParams) validate(isRest bool) error {
|
|
if err := common.NilGuard(c); err != nil {
|
|
return err
|
|
}
|
|
if c.Contract.IsEmpty() {
|
|
return currency.ErrCurrencyPairEmpty
|
|
}
|
|
if c.Size == 0 && c.AutoSize == "" {
|
|
return errInvalidOrderSize
|
|
}
|
|
if c.TimeInForce != "" {
|
|
if _, err := timeInForceFromString(c.TimeInForce); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if c.Price == 0 && c.TimeInForce != iocTIF && c.TimeInForce != fokTIF {
|
|
return fmt.Errorf("%w: %q; only 'ioc' and 'fok' allowed for market order", order.ErrUnsupportedTimeInForce, c.TimeInForce)
|
|
}
|
|
if c.Text != "" && !strings.HasPrefix(c.Text, "t-") {
|
|
return errInvalidTextPrefix
|
|
}
|
|
if c.AutoSize != "" {
|
|
if c.AutoSize != "close_long" && c.AutoSize != "close_short" {
|
|
return fmt.Errorf("%w: %q", errInvalidAutoSize, c.AutoSize)
|
|
}
|
|
if c.Size != 0 {
|
|
return fmt.Errorf("%w: size needs to be zero when auto size is set", errInvalidOrderSize)
|
|
}
|
|
}
|
|
// REST requests require a settlement currency, but it can be anything
|
|
// Websocket requests may have an empty settlement currency, or it must be BTC or USDT
|
|
if (isRest && c.Settle.IsEmpty()) ||
|
|
(!isRest && !c.Settle.IsEmpty() && !c.Settle.Equal(currency.BTC) && !c.Settle.Equal(currency.USDT)) {
|
|
return errEmptyOrInvalidSettlementCurrency
|
|
}
|
|
return nil
|
|
}
|