mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
GateIO: Add various risk API endpoints (#2106)
* gateio: risk update and tests (cherry-pick) * Update exchanges/gateio/risk.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update exchanges/gateio/risk.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * ai: nits * Update exchanges/gateio/gateio_types.go Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> * gk: nits * Update exchanges/gateio/risk.go Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> * gk: paging mr pedantic * Update exchanges/gateio/risk.go Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io> * linter: fix * crank: nits --------- Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Gareth Kirwan <gbjkirwan@gmail.com> Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
This commit is contained in:
@@ -118,6 +118,7 @@ const (
|
||||
deliveryPath = "delivery/"
|
||||
ordersPath = "/orders"
|
||||
positionsPath = "/positions/"
|
||||
hedgeModePath = "/dual_comp/positions/"
|
||||
subAccountsPath = "sub_accounts/"
|
||||
priceOrdersPaths = "/price_orders"
|
||||
|
||||
@@ -151,7 +152,6 @@ var (
|
||||
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")
|
||||
@@ -2164,20 +2164,6 @@ func (e *Exchange) UpdateFuturesPositionLeverage(ctx context.Context, settle cur
|
||||
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) {
|
||||
@@ -2199,7 +2185,7 @@ func (e *Exchange) RetrivePositionDetailInDualMode(ctx context.Context, settle c
|
||||
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)
|
||||
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualPositionsDualModeEPL, http.MethodGet, futuresPath+settle.Item.Lower+hedgeModePath+contract.String(), nil, nil, &response)
|
||||
}
|
||||
|
||||
// UpdatePositionMarginInDualMode update position margin in dual mode
|
||||
@@ -2217,7 +2203,7 @@ func (e *Exchange) UpdatePositionMarginInDualMode(ctx context.Context, settle cu
|
||||
}
|
||||
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)
|
||||
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualUpdateMarginDualModeEPL, http.MethodPost, futuresPath+settle.Item.Lower+hedgeModePath+contract.String()+"/margin", params, nil, &response)
|
||||
}
|
||||
|
||||
// UpdatePositionLeverageInDualMode update position leverage in dual mode
|
||||
@@ -2237,24 +2223,7 @@ func (e *Exchange) UpdatePositionLeverageInDualMode(ctx context.Context, settle
|
||||
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)
|
||||
return response, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualUpdateLeverageDualModeEPL, http.MethodPost, futuresPath+settle.Item.Lower+hedgeModePath+contract.String()+"/leverage", params, nil, &response)
|
||||
}
|
||||
|
||||
// PlaceFuturesOrder creates futures order
|
||||
@@ -2782,20 +2751,6 @@ func (e *Exchange) UpdateDeliveryPositionLeverage(ctx context.Context, settle cu
|
||||
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) {
|
||||
|
||||
@@ -1026,15 +1026,6 @@ func TestUpdateFuturesPositionLeverage(t *testing.T) {
|
||||
assert.NoError(t, err, "UpdateFuturesPositionLeverage should not error for USDTMarginedFutures")
|
||||
}
|
||||
|
||||
func TestUpdateFuturesPositionRiskLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
|
||||
_, err := e.UpdateFuturesPositionRiskLimit(t.Context(), currency.BTC, getPair(t, asset.CoinMarginedFutures), 10)
|
||||
assert.NoError(t, err, "UpdateFuturesPositionRiskLimit should not error for CoinMarginedFutures")
|
||||
_, err = e.UpdateFuturesPositionRiskLimit(t.Context(), currency.USDT, getPair(t, asset.USDTMarginedFutures), 10)
|
||||
assert.NoError(t, err, "UpdateFuturesPositionRiskLimit should not error for USDTMarginedFutures")
|
||||
}
|
||||
|
||||
func TestPlaceDeliveryOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
|
||||
@@ -1188,15 +1179,6 @@ func TestUpdatePositionLeverageInDualMode(t *testing.T) {
|
||||
assert.NoError(t, err, "UpdatePositionLeverageInDualMode should not error for USDTMarginedFutures")
|
||||
}
|
||||
|
||||
func TestUpdatePositionRiskLimitInDualMode(t *testing.T) {
|
||||
t.Parallel()
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
|
||||
_, err := e.UpdatePositionRiskLimitInDualMode(t.Context(), currency.BTC, getPair(t, asset.CoinMarginedFutures), 10)
|
||||
assert.NoError(t, err, "UpdatePositionRiskLimitInDualMode should not error for CoinMarginedFutures")
|
||||
_, err = e.UpdatePositionRiskLimitInDualMode(t.Context(), currency.USDT, getPair(t, asset.USDTMarginedFutures), 10)
|
||||
assert.NoError(t, err, "UpdatePositionRiskLimitInDualMode should not error for USDTMarginedFutures")
|
||||
}
|
||||
|
||||
func TestPlaceFuturesOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
|
||||
@@ -1455,15 +1437,6 @@ func TestUpdateDeliveryPositionLeverage(t *testing.T) {
|
||||
assert.NoError(t, err, "UpdateDeliveryPositionLeverage should not error")
|
||||
}
|
||||
|
||||
func TestUpdateDeliveryPositionRiskLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := e.UpdateDeliveryPositionRiskLimit(t.Context(), currency.EMPTYCODE, currency.Pair{}, 0)
|
||||
assert.ErrorIs(t, err, errEmptyOrInvalidSettlementCurrency)
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
|
||||
_, err = e.UpdateDeliveryPositionRiskLimit(t.Context(), currency.USDT, getPair(t, asset.DeliveryFutures), 30)
|
||||
assert.NoError(t, err, "UpdateDeliveryPositionRiskLimit should not error")
|
||||
}
|
||||
|
||||
func TestGetAllOptionsUnderlyings(t *testing.T) {
|
||||
t.Parallel()
|
||||
if _, err := e.GetAllOptionsUnderlyings(t.Context()); err != nil {
|
||||
|
||||
@@ -982,11 +982,7 @@ type UsersPositionForUnderlying struct {
|
||||
MarkPrice types.Number `json:"mark_price"`
|
||||
UnrealisedPnl types.Number `json:"unrealised_pnl"`
|
||||
PendingOrders int64 `json:"pending_orders"`
|
||||
CloseOrder struct {
|
||||
ID int64 `json:"id"`
|
||||
Price types.Number `json:"price"`
|
||||
IsLiq bool `json:"is_liq"`
|
||||
} `json:"close_order"`
|
||||
CloseOrder CloseOrder `json:"close_order"`
|
||||
}
|
||||
|
||||
// ContractClosePosition represents user's liquidation history
|
||||
@@ -1779,19 +1775,27 @@ type Position struct {
|
||||
LastClosePNL types.Number `json:"last_close_pnl"`
|
||||
RealisedPNLPoint types.Number `json:"realised_point"`
|
||||
RealisedPNLHistoryPoint types.Number `json:"history_point"`
|
||||
ADLRanking int64 `json:"adl_ranking"` // Ranking of auto deleveraging, a total of 1-5 grades, 1 is the highest, 5 is the lowest, and 6 is the special case when there is no position held or in liquidation
|
||||
PendingOrders int64 `json:"pending_orders"`
|
||||
CloseOrder struct {
|
||||
ID int64 `json:"id"`
|
||||
Price types.Number `json:"price"`
|
||||
IsLiq bool `json:"is_liq"`
|
||||
} `json:"close_order"`
|
||||
Mode string `json:"mode"`
|
||||
CrossLeverageLimit types.Number `json:"cross_leverage_limit"`
|
||||
UpdateTime types.Time `json:"update_time"`
|
||||
OpenTime types.Time `json:"open_time"`
|
||||
UpdateID int64 `json:"update_id"`
|
||||
TradeMaxSize types.Number `json:"trade_max_size"`
|
||||
ADLRanking uint8 `json:"adl_ranking"` // Ranking of auto deleveraging, a total of 1-5 grades, 1 is the highest, 5 is the lowest, and 6 is the special case when there is no position held or in liquidation
|
||||
PendingOrders uint64 `json:"pending_orders"`
|
||||
CloseOrder CloseOrder `json:"close_order"`
|
||||
Mode string `json:"mode"`
|
||||
CrossLeverageLimit types.Number `json:"cross_leverage_limit"`
|
||||
UpdateTime types.Time `json:"update_time"`
|
||||
OpenTime types.Time `json:"open_time"`
|
||||
UpdateID int64 `json:"update_id"`
|
||||
TradeMaxSize types.Number `json:"trade_max_size"`
|
||||
RiskLimitTable string `json:"risk_limit_table"`
|
||||
AverageMaintenanceRate types.Number `json:"average_maintenance_rate"`
|
||||
VoucherSize types.Number `json:"voucher_size"`
|
||||
VoucherMargin types.Number `json:"voucher_margin"`
|
||||
VoucherID int64 `json:"voucher_id"`
|
||||
}
|
||||
|
||||
// CloseOrder represents close order information
|
||||
type CloseOrder struct {
|
||||
ID int64 `json:"id"`
|
||||
Price types.Number `json:"price"`
|
||||
IsLiquidation bool `json:"is_liq"`
|
||||
}
|
||||
|
||||
// DualModeResponse represents dual mode enable or disable
|
||||
|
||||
@@ -138,12 +138,10 @@ const (
|
||||
perpetualPositionEPL
|
||||
perpetualUpdateMarginEPL
|
||||
perpetualUpdateLeverageEPL
|
||||
perpetualUpdateRiskEPL
|
||||
perpetualToggleDualModeEPL
|
||||
perpetualPositionsDualModeEPL
|
||||
perpetualUpdateMarginDualModeEPL
|
||||
perpetualUpdateLeverageDualModeEPL
|
||||
perpetualUpdateRiskDualModeEPL
|
||||
perpetualSubmitOrderEPL
|
||||
perpetualGetOrdersEPL
|
||||
perpetualSubmitBatchOrdersEPL
|
||||
@@ -165,7 +163,6 @@ const (
|
||||
deliveryPositionsEPL
|
||||
deliveryUpdateMarginEPL
|
||||
deliveryUpdateLeverageEPL
|
||||
deliveryUpdateRiskLimitEPL
|
||||
deliverySubmitOrderEPL
|
||||
deliveryGetOrdersEPL
|
||||
deliveryCancelOrdersEPL
|
||||
@@ -194,6 +191,15 @@ const (
|
||||
optionsTradingHistoryEPL
|
||||
|
||||
websocketRateLimitNotNeededEPL
|
||||
|
||||
// Risk EPLs
|
||||
publicFuturesRiskTableEPL
|
||||
publicFuturesRiskLimitTiersEPL
|
||||
publicDeliveryRiskLimitTiersEPL
|
||||
unifiedUserRiskUnitDetailsEPL
|
||||
deliveryUpdateRiskLimitEPL
|
||||
perpetualUpdateRiskDualModeEPL
|
||||
perpetualUpdateRiskEPL
|
||||
)
|
||||
|
||||
// package level rate limits for REST API
|
||||
@@ -326,12 +332,10 @@ var packageRateLimits = request.RateLimitDefinitions{
|
||||
perpetualPositionEPL: standardRateLimit(),
|
||||
perpetualUpdateMarginEPL: standardRateLimit(),
|
||||
perpetualUpdateLeverageEPL: standardRateLimit(),
|
||||
perpetualUpdateRiskEPL: standardRateLimit(),
|
||||
perpetualToggleDualModeEPL: standardRateLimit(),
|
||||
perpetualPositionsDualModeEPL: standardRateLimit(),
|
||||
perpetualUpdateMarginDualModeEPL: standardRateLimit(),
|
||||
perpetualUpdateLeverageDualModeEPL: standardRateLimit(),
|
||||
perpetualUpdateRiskDualModeEPL: standardRateLimit(),
|
||||
perpetualSubmitOrderEPL: perpetualOrderplacementRateLimit(),
|
||||
perpetualGetOrdersEPL: standardRateLimit(),
|
||||
perpetualSubmitBatchOrdersEPL: perpetualOrderplacementRateLimit(),
|
||||
@@ -353,7 +357,6 @@ var packageRateLimits = request.RateLimitDefinitions{
|
||||
deliveryPositionsEPL: standardRateLimit(),
|
||||
deliveryUpdateMarginEPL: standardRateLimit(),
|
||||
deliveryUpdateLeverageEPL: standardRateLimit(),
|
||||
deliveryUpdateRiskLimitEPL: standardRateLimit(),
|
||||
deliverySubmitOrderEPL: deliverySubmitCancelAmendRateLimit(),
|
||||
deliveryGetOrdersEPL: standardRateLimit(),
|
||||
deliveryCancelOrdersEPL: deliverySubmitCancelAmendRateLimit(),
|
||||
@@ -384,6 +387,15 @@ var packageRateLimits = request.RateLimitDefinitions{
|
||||
privateUnifiedSpotEPL: standardRateLimit(),
|
||||
|
||||
websocketRateLimitNotNeededEPL: nil, // no rate limit for certain websocket functions
|
||||
|
||||
// Risk limits
|
||||
publicFuturesRiskTableEPL: standardRateLimit(),
|
||||
publicFuturesRiskLimitTiersEPL: standardRateLimit(),
|
||||
publicDeliveryRiskLimitTiersEPL: standardRateLimit(),
|
||||
unifiedUserRiskUnitDetailsEPL: standardRateLimit(),
|
||||
deliveryUpdateRiskLimitEPL: standardRateLimit(),
|
||||
perpetualUpdateRiskDualModeEPL: standardRateLimit(),
|
||||
perpetualUpdateRiskEPL: standardRateLimit(),
|
||||
}
|
||||
|
||||
func standardRateLimit() *request.RateLimiterWithWeight {
|
||||
|
||||
124
exchanges/gateio/risk.go
Normal file
124
exchanges/gateio/risk.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package gateio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
)
|
||||
|
||||
var (
|
||||
errTableIDEmpty = errors.New("tableID cannot be empty")
|
||||
errInvalidRiskLimit = errors.New("invalid risk limit")
|
||||
errPagingNotAllowed = errors.New("limit/offset pagination params not allowed when contract supplied")
|
||||
)
|
||||
|
||||
// GetUnifiedUserRiskUnitDetails retrieves the user's risk unit details
|
||||
func (e *Exchange) GetUnifiedUserRiskUnitDetails(ctx context.Context) (*UserRiskUnitDetails, error) {
|
||||
var result *UserRiskUnitDetails
|
||||
return result, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, unifiedUserRiskUnitDetailsEPL, http.MethodGet, "unified/risk_units", nil, nil, &result)
|
||||
}
|
||||
|
||||
// GetFuturesRiskTable retrieves the futures risk table for a given settlement currency and table ID
|
||||
func (e *Exchange) GetFuturesRiskTable(ctx context.Context, settleCurrency currency.Code, tableID string) ([]RiskTable, error) {
|
||||
if settleCurrency.IsEmpty() {
|
||||
return nil, currency.ErrCurrencyCodeEmpty
|
||||
}
|
||||
if tableID == "" {
|
||||
return nil, errTableIDEmpty
|
||||
}
|
||||
var result []RiskTable
|
||||
path := futuresPath + settleCurrency.Lower().String() + "/risk_limit_table?table_id=" + tableID
|
||||
return result, e.SendHTTPRequest(ctx, exchange.RestSpot, publicFuturesRiskTableEPL, path, &result)
|
||||
}
|
||||
|
||||
// GetFuturesRiskLimitTiers retrieves the futures risk limit tiers
|
||||
// NOTE: 'limit' and 'offset' correspond to pagination queries at the market level, not to the length of the returned
|
||||
// array. This only takes effect when the contract parameter is empty.
|
||||
func (e *Exchange) GetFuturesRiskLimitTiers(ctx context.Context, settleCurrency currency.Code, contract currency.Pair, limit, offset uint64) ([]RiskTable, error) {
|
||||
return e.getRiskLimitTiers(ctx, futuresPath, publicFuturesRiskLimitTiersEPL, settleCurrency, contract, limit, offset)
|
||||
}
|
||||
|
||||
// GetDeliveryRiskLimitTiers retrieves the delivery risk limit tiers
|
||||
// NOTE: 'limit' and 'offset' correspond to pagination queries at the market level, not to the length of the returned
|
||||
// array. This only takes effect when the contract parameter is empty.
|
||||
func (e *Exchange) GetDeliveryRiskLimitTiers(ctx context.Context, settleCurrency currency.Code, contract currency.Pair, limit, offset uint64) ([]RiskTable, error) {
|
||||
return e.getRiskLimitTiers(ctx, deliveryPath, publicDeliveryRiskLimitTiersEPL, settleCurrency, contract, limit, offset)
|
||||
}
|
||||
|
||||
func (e *Exchange) getRiskLimitTiers(ctx context.Context, assetPath string, epl request.EndpointLimit, settleCurrency currency.Code, contract currency.Pair, limit, offset uint64) ([]RiskTable, error) {
|
||||
if settleCurrency.IsEmpty() {
|
||||
return nil, currency.ErrCurrencyCodeEmpty
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
if !contract.IsEmpty() {
|
||||
if limit > 0 || offset > 0 {
|
||||
return nil, errPagingNotAllowed
|
||||
}
|
||||
params.Set("contract", contract.Upper().String())
|
||||
} else {
|
||||
if limit > 0 {
|
||||
params.Set("limit", strconv.FormatUint(limit, 10))
|
||||
}
|
||||
if offset > 0 {
|
||||
params.Set("offset", strconv.FormatUint(offset, 10))
|
||||
}
|
||||
}
|
||||
|
||||
path := common.EncodeURLValues(assetPath+settleCurrency.Lower().String()+"/risk_limit_tiers", params)
|
||||
|
||||
var result []RiskTable
|
||||
return result, e.SendHTTPRequest(ctx, exchange.RestSpot, epl, path, &result)
|
||||
}
|
||||
|
||||
// DeliveryUpdatePositionRiskLimit updates the position risk limit for a delivery contract
|
||||
func (e *Exchange) DeliveryUpdatePositionRiskLimit(ctx context.Context, settleCurrency currency.Code, contract currency.Pair, riskLimit float64) (*Position, error) {
|
||||
return e.updatePositionRiskLimit(ctx, deliveryPath, positionsPath, deliveryUpdateRiskLimitEPL, settleCurrency, contract, riskLimit)
|
||||
}
|
||||
|
||||
// FuturesUpdatePositionRiskLimit updates the position risk limit for a futures contract
|
||||
func (e *Exchange) FuturesUpdatePositionRiskLimit(ctx context.Context, settleCurrency currency.Code, contract currency.Pair, riskLimit float64) (*Position, error) {
|
||||
return e.updatePositionRiskLimit(ctx, futuresPath, positionsPath, perpetualUpdateRiskEPL, settleCurrency, contract, riskLimit)
|
||||
}
|
||||
|
||||
// FuturesUpdatePositionRiskLimitDualMode updates the position risk limit for a futures contract in dual/hedge mode
|
||||
func (e *Exchange) FuturesUpdatePositionRiskLimitDualMode(ctx context.Context, settleCurrency currency.Code, contract currency.Pair, riskLimit float64) (*Position, error) {
|
||||
return e.updatePositionRiskLimit(ctx, futuresPath, hedgeModePath, perpetualUpdateRiskDualModeEPL, settleCurrency, contract, riskLimit)
|
||||
}
|
||||
|
||||
func (e *Exchange) updatePositionRiskLimit(ctx context.Context, assetPath, positionsTypePath string, epl request.EndpointLimit, settleCurrency currency.Code, contract currency.Pair, riskLimit float64) (*Position, error) {
|
||||
if settleCurrency.IsEmpty() {
|
||||
return nil, currency.ErrCurrencyCodeEmpty
|
||||
}
|
||||
if contract.IsEmpty() {
|
||||
return nil, currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
if riskLimit <= 0 {
|
||||
return nil, errInvalidRiskLimit
|
||||
}
|
||||
path := assetPath + settleCurrency.Lower().String() + positionsTypePath + contract.Upper().String() + "/risk_limit"
|
||||
param := url.Values{}
|
||||
param.Set("risk_limit", strconv.FormatFloat(riskLimit, 'f', -1, 64))
|
||||
|
||||
if positionsTypePath == hedgeModePath {
|
||||
var result []Position
|
||||
if err := e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, epl, http.MethodPost, path, param, nil, &result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Endpoint returns an array but only one position is expected
|
||||
if len(result) != 1 {
|
||||
return nil, common.ErrNoResults
|
||||
}
|
||||
return &result[0], nil
|
||||
}
|
||||
|
||||
var result Position
|
||||
return &result, e.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, epl, http.MethodPost, path, param, nil, &result)
|
||||
}
|
||||
178
exchanges/gateio/risk_test.go
Normal file
178
exchanges/gateio/risk_test.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package gateio
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/sharedtestvalues"
|
||||
testexch "github.com/thrasher-corp/gocryptotrader/internal/testing/exchange"
|
||||
)
|
||||
|
||||
func TestGetUnifiedUserRiskUnitDetails(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, e)
|
||||
|
||||
got, err := e.GetUnifiedUserRiskUnitDetails(t.Context())
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, got)
|
||||
}
|
||||
|
||||
func TestGetFuturesRiskTable(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := e.GetFuturesRiskTable(t.Context(), currency.EMPTYCODE, "")
|
||||
require.ErrorIs(t, err, currency.ErrCurrencyCodeEmpty)
|
||||
|
||||
_, err = e.GetFuturesRiskTable(t.Context(), currency.USDT, "")
|
||||
require.ErrorIs(t, err, errTableIDEmpty)
|
||||
|
||||
// mock HTTP response due to dynamically generated table IDs, which can only be retrieved via authenticated endpoint
|
||||
e := new(Exchange)
|
||||
require.NoError(t, testexch.Setup(e))
|
||||
require.NoError(t, testexch.MockHTTPInstance(e, "/"))
|
||||
|
||||
got, err := e.GetFuturesRiskTable(t.Context(), currency.USDT, "BTC_USDT_202507040223")
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, got)
|
||||
}
|
||||
|
||||
func TestGetFuturesRiskLimitTiers(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := e.GetFuturesRiskLimitTiers(t.Context(), currency.EMPTYCODE, currency.EMPTYPAIR, 0, 0)
|
||||
require.ErrorIs(t, err, currency.ErrCurrencyCodeEmpty)
|
||||
|
||||
_, err = e.GetFuturesRiskLimitTiers(t.Context(), currency.USDT, currency.NewBTCUSDT(), 10, 10)
|
||||
require.ErrorIs(t, err, errPagingNotAllowed)
|
||||
|
||||
_, err = e.GetFuturesRiskLimitTiers(t.Context(), currency.USDT, currency.NewBTCUSDT(), 0, 10)
|
||||
require.ErrorIs(t, err, errPagingNotAllowed)
|
||||
|
||||
got, err := e.GetFuturesRiskLimitTiers(t.Context(), currency.USDT, currency.EMPTYPAIR, 10, 10)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, got)
|
||||
|
||||
testexch.UpdatePairsOnce(t, e)
|
||||
avail, err := e.GetAvailablePairs(asset.USDTMarginedFutures)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, avail)
|
||||
|
||||
got, err = e.GetFuturesRiskLimitTiers(t.Context(), currency.USDT, avail[0], 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, got)
|
||||
}
|
||||
|
||||
func TestGetDeliveryRiskLimitTiers(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := e.GetDeliveryRiskLimitTiers(t.Context(), currency.EMPTYCODE, currency.EMPTYPAIR, 0, 0)
|
||||
require.ErrorIs(t, err, currency.ErrCurrencyCodeEmpty)
|
||||
|
||||
_, err = e.GetDeliveryRiskLimitTiers(t.Context(), currency.USDT, currency.NewBTCUSDT(), 10, 10)
|
||||
require.ErrorIs(t, err, errPagingNotAllowed)
|
||||
|
||||
_, err = e.GetDeliveryRiskLimitTiers(t.Context(), currency.USDT, currency.NewBTCUSDT(), 0, 10)
|
||||
require.ErrorIs(t, err, errPagingNotAllowed)
|
||||
|
||||
got, err := e.GetDeliveryRiskLimitTiers(t.Context(), currency.USDT, currency.EMPTYPAIR, 10, 10)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, got)
|
||||
|
||||
testexch.UpdatePairsOnce(t, e)
|
||||
avail, err := e.GetAvailablePairs(asset.DeliveryFutures)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, avail)
|
||||
|
||||
got, err = e.GetDeliveryRiskLimitTiers(t.Context(), currency.USDT, avail[0], 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, got)
|
||||
}
|
||||
|
||||
func TestDeliveryUpdatePositionRiskLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := e.DeliveryUpdatePositionRiskLimit(t.Context(), currency.EMPTYCODE, currency.EMPTYPAIR, 0)
|
||||
require.ErrorIs(t, err, currency.ErrCurrencyCodeEmpty)
|
||||
|
||||
_, err = e.DeliveryUpdatePositionRiskLimit(t.Context(), currency.USDT, currency.EMPTYPAIR, 0)
|
||||
require.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
|
||||
|
||||
_, err = e.DeliveryUpdatePositionRiskLimit(t.Context(), currency.USDT, currency.NewBTCUSD(), 0)
|
||||
require.ErrorIs(t, err, errInvalidRiskLimit)
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
|
||||
|
||||
testexch.UpdatePairsOnce(t, e)
|
||||
avail, err := e.GetAvailablePairs(asset.DeliveryFutures)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, avail)
|
||||
|
||||
tiers, err := e.GetDeliveryRiskLimitTiers(t.Context(), currency.USDT, avail[0], 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, tiers)
|
||||
|
||||
lowestTierRiskLimit := float64(tiers[0].RiskLimit)
|
||||
got, err := e.DeliveryUpdatePositionRiskLimit(request.WithVerbose(t.Context()), currency.USDT, avail[0], lowestTierRiskLimit)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, got)
|
||||
}
|
||||
|
||||
func TestFuturesUpdatePositionRiskLimit(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := e.FuturesUpdatePositionRiskLimit(t.Context(), currency.EMPTYCODE, currency.EMPTYPAIR, 0)
|
||||
require.ErrorIs(t, err, currency.ErrCurrencyCodeEmpty)
|
||||
|
||||
_, err = e.FuturesUpdatePositionRiskLimit(t.Context(), currency.USDT, currency.EMPTYPAIR, 0)
|
||||
require.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
|
||||
|
||||
_, err = e.FuturesUpdatePositionRiskLimit(t.Context(), currency.USDT, currency.NewBTCUSD(), 0)
|
||||
require.ErrorIs(t, err, errInvalidRiskLimit)
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
|
||||
|
||||
testexch.UpdatePairsOnce(t, e)
|
||||
avail, err := e.GetAvailablePairs(asset.USDTMarginedFutures)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, avail)
|
||||
|
||||
tiers, err := e.GetFuturesRiskLimitTiers(t.Context(), currency.USDT, avail[0], 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, tiers)
|
||||
|
||||
lowestTierRiskLimit := float64(tiers[0].RiskLimit)
|
||||
got, err := e.FuturesUpdatePositionRiskLimit(request.WithVerbose(t.Context()), currency.USDT, avail[0], lowestTierRiskLimit)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, got)
|
||||
assert.Equal(t, lowestTierRiskLimit, got.RiskLimit.Float64())
|
||||
}
|
||||
|
||||
func TestFuturesUpdatePositionRiskLimitDualMode(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := e.FuturesUpdatePositionRiskLimitDualMode(t.Context(), currency.EMPTYCODE, currency.EMPTYPAIR, 0)
|
||||
require.ErrorIs(t, err, currency.ErrCurrencyCodeEmpty)
|
||||
|
||||
_, err = e.FuturesUpdatePositionRiskLimitDualMode(t.Context(), currency.USDT, currency.EMPTYPAIR, 0)
|
||||
require.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
|
||||
|
||||
_, err = e.FuturesUpdatePositionRiskLimitDualMode(t.Context(), currency.USDT, currency.NewBTCUSD(), 0)
|
||||
require.ErrorIs(t, err, errInvalidRiskLimit)
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, e, canManipulateRealOrders)
|
||||
|
||||
testexch.UpdatePairsOnce(t, e)
|
||||
avail, err := e.GetAvailablePairs(asset.USDTMarginedFutures)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, avail)
|
||||
|
||||
tiers, err := e.GetFuturesRiskLimitTiers(t.Context(), currency.USDT, avail[0], 0, 0)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, tiers)
|
||||
|
||||
lowestTierRiskLimit := float64(tiers[0].RiskLimit)
|
||||
got, err := e.FuturesUpdatePositionRiskLimitDualMode(t.Context(), currency.USDT, avail[0], lowestTierRiskLimit)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, got)
|
||||
assert.Equal(t, lowestTierRiskLimit, got.RiskLimit.Float64())
|
||||
}
|
||||
36
exchanges/gateio/risk_types.go
Normal file
36
exchanges/gateio/risk_types.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package gateio
|
||||
|
||||
import (
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/types"
|
||||
)
|
||||
|
||||
// UserRiskUnitDetails represents the risk unit details for a user
|
||||
type UserRiskUnitDetails struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
SpotHedge bool `json:"spot_hedge"`
|
||||
RiskUnits []RiskUnits `json:"risk_units"`
|
||||
}
|
||||
|
||||
// RiskUnits represents risk unit details for a specific currency
|
||||
type RiskUnits struct {
|
||||
Symbol currency.Code `json:"symbol"`
|
||||
SpotInUse types.Number `json:"spot_in_use"`
|
||||
MaintainMargin types.Number `json:"maintain_margin"`
|
||||
InitialMargin types.Number `json:"initial_margin"`
|
||||
Delta types.Number `json:"delta"`
|
||||
Gamma types.Number `json:"gamma"`
|
||||
Theta types.Number `json:"theta"`
|
||||
Vega types.Number `json:"vega"`
|
||||
}
|
||||
|
||||
// RiskTable represents the risk table information
|
||||
type RiskTable struct {
|
||||
Tier uint8 `json:"tier"`
|
||||
RiskLimit types.Number `json:"risk_limit"`
|
||||
InitialRate types.Number `json:"initial_rate"`
|
||||
MaintenanceRate types.Number `json:"maintenance_rate"`
|
||||
LeverageMax types.Number `json:"leverage_max"`
|
||||
Deduction types.Number `json:"deduction"`
|
||||
Contract currency.Pair `json:"contract"` // Only available when fetching all risk limit tiers
|
||||
}
|
||||
55
exchanges/gateio/testdata/http.json
vendored
Normal file
55
exchanges/gateio/testdata/http.json
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"routes": {
|
||||
"/futures/usdt/risk_limit_table": {
|
||||
"GET": [
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"deduction": "0",
|
||||
"initial_rate": "0.008",
|
||||
"leverage_max": "125",
|
||||
"maintenance_rate": "0.004",
|
||||
"risk_limit": "1000000",
|
||||
"tier": 1
|
||||
},
|
||||
{
|
||||
"deduction": "500",
|
||||
"initial_rate": "0.009009",
|
||||
"leverage_max": "111",
|
||||
"maintenance_rate": "0.0045",
|
||||
"risk_limit": "1500000",
|
||||
"tier": 2
|
||||
},
|
||||
{
|
||||
"deduction": "1250",
|
||||
"initial_rate": "0.01",
|
||||
"leverage_max": "100",
|
||||
"maintenance_rate": "0.005",
|
||||
"risk_limit": "2000000",
|
||||
"tier": 3
|
||||
},
|
||||
{
|
||||
"deduction": "5250",
|
||||
"initial_rate": "0.013333",
|
||||
"leverage_max": "75",
|
||||
"maintenance_rate": "0.007",
|
||||
"risk_limit": "3000000",
|
||||
"tier": 4
|
||||
},
|
||||
{
|
||||
"deduction": "6750",
|
||||
"initial_rate": "0.01515",
|
||||
"leverage_max": "66",
|
||||
"maintenance_rate": "0.0075",
|
||||
"risk_limit": "5000000",
|
||||
"tier": 5
|
||||
}
|
||||
],
|
||||
"queryString": "table_id=BTC_USDT_202507040223",
|
||||
"bodyParams": "",
|
||||
"headers": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user