mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 15:09:42 +00:00
GateIO: Split futures into USDTM and CoinM futures (#1786)
* Config: v5 Split GateIO futures into CoinM and USDT * GateIO: Split asset.Futures into CoinM and USDT * Fix CancelBatchOrders using wrong endpoint for CoinMarginedFutures * Fix TestGetActiveOrders expecting currency.ErrCurrencyPairsEmpty * Config: Add config version continuity step to CI * GateIO: Pin CoinM futures to just BTC/USD Right now we only have a /btc endpoint available, and only BTCUSD is available. If GateIO offers more, we'll need to add a settlement currencies list again
This commit is contained in:
16
.github/workflows/config-versions-lint.yml
vendored
Normal file
16
.github/workflows/config-versions-lint.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: configs-versions-lint
|
||||
on: [push, pull_request]
|
||||
env:
|
||||
GO_VERSION: 1.24.x
|
||||
jobs:
|
||||
lint:
|
||||
name: config versions lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- name: Check config versions are continuous
|
||||
run: go test ./config/versions/ -tags config_versions -run Continuity
|
||||
20
config/versions/continuity_test.go
Normal file
20
config/versions/continuity_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
//go:build config_versions
|
||||
// +build config_versions
|
||||
|
||||
// This test is run independently from CI for developer convenience when developing out-of-sequence versions
|
||||
// Called from a separate github workflow to prevent a PR from being merged without failing the main unit tests
|
||||
|
||||
package versions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestVersionContinuity(t *testing.T) {
|
||||
t.Parallel()
|
||||
for ver, v := range Manager.versions {
|
||||
assert.NotNilf(t, v, "Version %d should not be empty", ver)
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
v4 "github.com/thrasher-corp/gocryptotrader/config/versions/v4"
|
||||
v5 "github.com/thrasher-corp/gocryptotrader/config/versions/v5"
|
||||
v6 "github.com/thrasher-corp/gocryptotrader/config/versions/v6"
|
||||
v7 "github.com/thrasher-corp/gocryptotrader/config/versions/v7"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -18,4 +19,5 @@ func init() {
|
||||
Manager.registerVersion(4, &v4.Version{})
|
||||
Manager.registerVersion(5, &v5.Version{})
|
||||
Manager.registerVersion(6, &v6.Version{})
|
||||
Manager.registerVersion(7, &v7.Version{})
|
||||
}
|
||||
|
||||
@@ -12,8 +12,11 @@ type PairsManager struct {
|
||||
Pairs FullStore `json:"pairs"`
|
||||
}
|
||||
|
||||
// FullStore contains a pair store by asset name
|
||||
type FullStore map[string]struct {
|
||||
// FullStore holds all supported asset types with the enabled and available pairs for an exchange.
|
||||
type FullStore map[string]*PairStore
|
||||
|
||||
// PairStore contains a pair store
|
||||
type PairStore struct {
|
||||
Enabled string `json:"enabled"`
|
||||
Available string `json:"available"`
|
||||
RequestFormat *v0.PairFormat `json:"requestFormat,omitempty"`
|
||||
|
||||
27
config/versions/v2/types.go
Normal file
27
config/versions/v2/types.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
v0 "github.com/thrasher-corp/gocryptotrader/config/versions/v0"
|
||||
)
|
||||
|
||||
// PairsManager contains exchange pair management config
|
||||
type PairsManager struct {
|
||||
BypassConfigFormatUpgrades bool `json:"bypassConfigFormatUpgrades"`
|
||||
RequestFormat *v0.PairFormat `json:"requestFormat,omitempty"`
|
||||
ConfigFormat *v0.PairFormat `json:"configFormat,omitempty"`
|
||||
UseGlobalFormat bool `json:"useGlobalFormat,omitempty"`
|
||||
LastUpdated int64 `json:"lastUpdated,omitempty"`
|
||||
Pairs FullStore `json:"pairs"`
|
||||
}
|
||||
|
||||
// FullStore holds all supported asset types with the enabled and available pairs for an exchange.
|
||||
type FullStore map[string]*PairStore
|
||||
|
||||
// PairStore contains a pair store
|
||||
type PairStore struct {
|
||||
AssetEnabled bool `json:"assetEnabled"`
|
||||
Enabled string `json:"enabled"`
|
||||
Available string `json:"available"`
|
||||
RequestFormat *v0.PairFormat `json:"requestFormat,omitempty"`
|
||||
ConfigFormat *v0.PairFormat `json:"configFormat,omitempty"`
|
||||
}
|
||||
92
config/versions/v7/v7.go
Normal file
92
config/versions/v7/v7.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package v7
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json" //nolint:depguard // Used instead of gct encoding/json so that we can ensure consistent library functionality between versions
|
||||
"strings"
|
||||
|
||||
"github.com/buger/jsonparser"
|
||||
v2 "github.com/thrasher-corp/gocryptotrader/config/versions/v2"
|
||||
)
|
||||
|
||||
// Version is an ExchangeVersion to split GateIO futures into CoinM and USDT margined futures assets
|
||||
type Version struct{}
|
||||
|
||||
// Exchanges returns just GateIO
|
||||
func (v *Version) Exchanges() []string { return []string{"GateIO"} }
|
||||
|
||||
// UpgradeExchange split GateIO futures into CoinM and USDT margined futures assets
|
||||
func (v *Version) UpgradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
fs := v2.FullStore{"coinmarginedfutures": {}, "usdtmarginedfutures": {}}
|
||||
fsJSON, _, _, err := jsonparser.Get(e, "currencyPairs", "pairs")
|
||||
if err != nil {
|
||||
return e, err
|
||||
}
|
||||
if err := json.Unmarshal(fsJSON, &fs); err != nil {
|
||||
return e, err
|
||||
}
|
||||
f, ok := fs["futures"]
|
||||
if !ok {
|
||||
// Version.UpgradeExchange should only split futures into CoinM and USDT
|
||||
// If the exchange config doesn't have futures, we have nothing to do
|
||||
return e, nil
|
||||
}
|
||||
for p := range strings.SplitSeq(f.Available, ",") {
|
||||
where := "usdtmarginedfutures"
|
||||
if strings.HasSuffix(p, "USD") {
|
||||
where = "coinmarginedfutures"
|
||||
}
|
||||
if fs[where].Available != "" {
|
||||
fs[where].Available += ","
|
||||
}
|
||||
fs[where].Available += p
|
||||
}
|
||||
for p := range strings.SplitSeq(f.Enabled, ",") {
|
||||
where := "usdtmarginedfutures"
|
||||
if strings.HasSuffix(p, "USD") {
|
||||
where = "coinmarginedfutures"
|
||||
}
|
||||
if fs[where].Enabled != "" {
|
||||
fs[where].Enabled += ","
|
||||
}
|
||||
fs[where].Enabled += p
|
||||
}
|
||||
fs["usdtmarginedfutures"].AssetEnabled = f.AssetEnabled
|
||||
fs["coinmarginedfutures"].AssetEnabled = f.AssetEnabled
|
||||
delete(fs, "futures")
|
||||
val, err := json.Marshal(fs)
|
||||
if err == nil {
|
||||
e, err = jsonparser.Set(e, val, "currencyPairs", "pairs")
|
||||
}
|
||||
return e, err
|
||||
}
|
||||
|
||||
// DowngradeExchange will merge GateIO CoinM and USDT margined futures assets into futures
|
||||
func (v *Version) DowngradeExchange(_ context.Context, e []byte) ([]byte, error) {
|
||||
fs := v2.FullStore{"futures": {}, "coinmarginedfutures": {}, "usdtmarginedfutures": {}}
|
||||
fsJSON, _, _, err := jsonparser.Get(e, "currencyPairs", "pairs")
|
||||
if err != nil {
|
||||
return e, err
|
||||
}
|
||||
if err := json.Unmarshal(fsJSON, &fs); err != nil {
|
||||
return e, err
|
||||
}
|
||||
fs["futures"].Enabled = fs["coinmarginedfutures"].Enabled
|
||||
if fs["futures"].Enabled != "" {
|
||||
fs["futures"].Enabled += ","
|
||||
}
|
||||
fs["futures"].Enabled += fs["usdtmarginedfutures"].Enabled
|
||||
fs["futures"].Available = fs["coinmarginedfutures"].Available
|
||||
if fs["futures"].Available != "" {
|
||||
fs["futures"].Available += ","
|
||||
}
|
||||
fs["futures"].Available += fs["usdtmarginedfutures"].Available
|
||||
fs["futures"].AssetEnabled = fs["usdtmarginedfutures"].AssetEnabled || fs["coinmarginedfutures"].AssetEnabled
|
||||
delete(fs, "coinmarginedfutures")
|
||||
delete(fs, "usdtmarginedfutures")
|
||||
val, err := json.Marshal(fs)
|
||||
if err == nil {
|
||||
e, err = jsonparser.Set(e, val, "currencyPairs", "pairs")
|
||||
}
|
||||
return e, err
|
||||
}
|
||||
62
config/versions/v7/v7_test.go
Normal file
62
config/versions/v7/v7_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package v7_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json" //nolint:depguard // Used instead of gct encoding/json so that we can ensure consistent library functionality between versions
|
||||
"testing"
|
||||
|
||||
"github.com/buger/jsonparser"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
v7 "github.com/thrasher-corp/gocryptotrader/config/versions/v7"
|
||||
)
|
||||
|
||||
func TestExchanges(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert.Equal(t, []string{"GateIO"}, new(v7.Version).Exchanges())
|
||||
}
|
||||
|
||||
func TestUpgrade(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
in := []byte(`{"name":"GateIO","currencyPairs":{}}`)
|
||||
_, err := new(v7.Version).UpgradeExchange(context.Background(), in)
|
||||
require.ErrorIs(t, err, jsonparser.KeyPathNotFoundError)
|
||||
|
||||
in = []byte(`{"name":"GateIO","currencyPairs":{"pairs":14}}`)
|
||||
_, err = new(v7.Version).UpgradeExchange(context.Background(), in)
|
||||
require.Error(t, err)
|
||||
var jsonErr *json.UnmarshalTypeError
|
||||
assert.ErrorAs(t, err, &jsonErr, "UpgradeExchange should return a json.UnmarshalTypeError on bad type for pairs")
|
||||
|
||||
in = []byte(`{"name":"GateIO","currencyPairs":{"pairs":{"spot":{"assetEnabled":true,"enabled":"BTC-USDT","available":"BTC-USDT"},"futures":{"assetEnabled":true,"enabled":"BTC_USD,BTC_USDT,ETH_USDT","available":"BTC_USD,BTC_USDT,ETH_USDT,LTC_USDT"}}}}`)
|
||||
out, err := new(v7.Version).UpgradeExchange(context.Background(), in)
|
||||
require.NoError(t, err)
|
||||
exp := `{"name":"GateIO","currencyPairs":{"pairs":{"coinmarginedfutures":{"assetEnabled":true,"enabled":"BTC_USD","available":"BTC_USD"},"spot":{"assetEnabled":true,"enabled":"BTC-USDT","available":"BTC-USDT"},"usdtmarginedfutures":{"assetEnabled":true,"enabled":"BTC_USDT,ETH_USDT","available":"BTC_USDT,ETH_USDT,LTC_USDT"}}}}`
|
||||
assert.Equal(t, exp, string(out))
|
||||
|
||||
out, err = new(v7.Version).UpgradeExchange(context.Background(), out)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, exp, string(out), "UpgradeExchange without futures should not alter the new entries")
|
||||
}
|
||||
|
||||
func TestDowngrade(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
in := []byte(`{"name":"GateIO","currencyPairs":{}}`)
|
||||
_, err := new(v7.Version).DowngradeExchange(context.Background(), in)
|
||||
require.ErrorIs(t, err, jsonparser.KeyPathNotFoundError)
|
||||
|
||||
in = []byte(`{"name":"GateIO","currencyPairs":{"pairs":14}}`)
|
||||
_, err = new(v7.Version).DowngradeExchange(context.Background(), in)
|
||||
require.Error(t, err)
|
||||
var jsonErr *json.UnmarshalTypeError
|
||||
assert.ErrorAs(t, err, &jsonErr)
|
||||
|
||||
in = []byte(`{"name":"GateIO","currencyPairs":{"pairs":{"spot":{"assetEnabled":true,"enabled":"BTC-USDT","available":"BTC-USDT,WIF-USDT"},"coinmarginedfutures":{"assetEnabled":true,"enabled":"BTC_USD","available":"BTC_USD"},"usdtmarginedfutures":{"assetEnabled":true,"enabled":"BTC_USDT,ETH_USDT","available":"BTC_USDT,ETH_USDT,LTC_USDT"}}}}`)
|
||||
out, err := new(v7.Version).DowngradeExchange(context.Background(), in)
|
||||
require.NoError(t, err)
|
||||
|
||||
exp := `{"name":"GateIO","currencyPairs":{"pairs":{"futures":{"assetEnabled":true,"enabled":"BTC_USD,BTC_USDT,ETH_USDT","available":"BTC_USD,BTC_USDT,ETH_USDT,LTC_USDT"},"spot":{"assetEnabled":true,"enabled":"BTC-USDT","available":"BTC-USDT,WIF-USDT"}}}}`
|
||||
assert.Equal(t, exp, string(out))
|
||||
}
|
||||
@@ -70,10 +70,7 @@ func NewPairWithDelimiter(base, quote, delimiter string) Pair {
|
||||
// with or without delimiter
|
||||
func NewPairFromString(currencyPair string) (Pair, error) {
|
||||
if len(currencyPair) < 3 {
|
||||
return EMPTYPAIR,
|
||||
fmt.Errorf("%w from %s string too short to be a currency pair",
|
||||
errCannotCreatePair,
|
||||
currencyPair)
|
||||
return EMPTYPAIR, fmt.Errorf("%w from %s string too short to be a currency pair", errCannotCreatePair, currencyPair)
|
||||
}
|
||||
|
||||
for x := range currencyPair {
|
||||
|
||||
@@ -29,9 +29,6 @@ const (
|
||||
gateioFuturesLiveTradingAlternative = "https://fx-api.gateio.ws/" + gateioAPIVersion
|
||||
gateioAPIVersion = "api/v4/"
|
||||
tradeBaseURL = "https://www.gate.io/"
|
||||
tradeSpot = "trade/"
|
||||
tradeFutures = "futures/usdt/"
|
||||
tradeDelivery = "futures-delivery/usdt/"
|
||||
|
||||
// SubAccount Endpoints
|
||||
subAccounts = "sub_accounts"
|
||||
@@ -140,7 +137,7 @@ var (
|
||||
errInvalidOrderSize = errors.New("invalid order size")
|
||||
errInvalidOrderID = errors.New("invalid order id")
|
||||
errInvalidAmount = errors.New("invalid amount")
|
||||
errInvalidOrEmptySubaccount = errors.New("invalid or empty subaccount")
|
||||
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")
|
||||
@@ -165,7 +162,8 @@ var (
|
||||
errMultipleOrders = errors.New("multiple orders passed")
|
||||
errMissingWithdrawalID = errors.New("missing withdrawal ID")
|
||||
errInvalidSubAccountUserID = errors.New("sub-account user id is required")
|
||||
errCannotParseSettlementCurrency = errors.New("cannot derive settlement currency")
|
||||
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")
|
||||
errInvalidTextValue = errors.New("invalid text value, requires prefix `t-`")
|
||||
)
|
||||
@@ -1185,7 +1183,7 @@ func (g *Gateio) SubAccountTransfer(ctx context.Context, arg SubAccountTransferP
|
||||
return currency.ErrCurrencyCodeEmpty
|
||||
}
|
||||
if arg.SubAccount == "" {
|
||||
return errInvalidOrEmptySubaccount
|
||||
return errInvalidSubAccount
|
||||
}
|
||||
arg.Direction = strings.ToLower(arg.Direction)
|
||||
if arg.Direction != "to" && arg.Direction != "from" {
|
||||
@@ -1194,8 +1192,10 @@ func (g *Gateio) SubAccountTransfer(ctx context.Context, arg SubAccountTransferP
|
||||
if arg.Amount <= 0 {
|
||||
return errInvalidAmount
|
||||
}
|
||||
if arg.SubAccountType != "" && arg.SubAccountType != asset.Spot.String() && arg.SubAccountType != asset.Futures.String() && arg.SubAccountType != asset.CrossMargin.String() {
|
||||
return fmt.Errorf("%v; only %v,%v, and %v are allowed", asset.ErrNotSupported, asset.Spot, asset.Futures, asset.CrossMargin)
|
||||
switch arg.SubAccountType {
|
||||
case "", "spot", "futures", "delivery":
|
||||
default:
|
||||
return fmt.Errorf("%w `%s` for SubAccountTransfer; Supported: [spot, futures, delivery]", asset.ErrNotSupported, arg.SubAccountType)
|
||||
}
|
||||
return g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, walletSubAccountTransferEPL, http.MethodPost, walletSubAccountTransfer, nil, &arg, nil)
|
||||
}
|
||||
@@ -3696,19 +3696,6 @@ func (g *Gateio) GetUnderlyingFromCurrencyPair(p currency.Pair) (currency.Pair,
|
||||
return currency.Pair{Base: currency.NewCode(ccies[0]), Delimiter: currency.UnderscoreDelimiter, Quote: currency.NewCode(ccies[1])}, nil
|
||||
}
|
||||
|
||||
func getSettlementFromCurrency(currencyPair currency.Pair) (settlement currency.Code, err error) {
|
||||
quote := currencyPair.Quote.Upper().String()
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(quote, currency.USDT.String()):
|
||||
return currency.USDT, nil
|
||||
case strings.HasPrefix(quote, currency.USD.String()):
|
||||
return currency.BTC, nil
|
||||
default:
|
||||
return currency.EMPTYCODE, fmt.Errorf("%w %v", errCannotParseSettlementCurrency, currencyPair)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAccountDetails retrieves account details
|
||||
func (g *Gateio) GetAccountDetails(ctx context.Context) (*AccountDetails, error) {
|
||||
var resp *AccountDetails
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,8 +27,6 @@ const (
|
||||
sideBorrow = "borrow"
|
||||
)
|
||||
|
||||
var settlementCurrencies = []currency.Code{currency.BTC, currency.USDT}
|
||||
|
||||
// WithdrawalFees the large list of predefined withdrawal fees
|
||||
// Prone to change
|
||||
var WithdrawalFees = map[currency.Code]float64{
|
||||
|
||||
@@ -3,9 +3,7 @@ package gateio
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -26,8 +24,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
futuresWebsocketBtcURL = "wss://fx-ws.gateio.ws/v4/ws/btc"
|
||||
futuresWebsocketUsdtURL = "wss://fx-ws.gateio.ws/v4/ws/usdt"
|
||||
btcFuturesWebsocketURL = "wss://fx-ws.gateio.ws/v4/ws/btc"
|
||||
usdtFuturesWebsocketURL = "wss://fx-ws.gateio.ws/v4/ws/usdt"
|
||||
|
||||
futuresPingChannel = "futures.ping"
|
||||
futuresTickersChannel = "futures.tickers"
|
||||
@@ -59,12 +57,14 @@ var defaultFuturesSubscriptions = []string{
|
||||
|
||||
// WsFuturesConnect initiates a websocket connection for futures account
|
||||
func (g *Gateio) WsFuturesConnect(ctx context.Context, conn websocket.Connection) error {
|
||||
err := g.CurrencyPairs.IsAssetEnabled(asset.Futures)
|
||||
if err != nil {
|
||||
a := asset.USDTMarginedFutures
|
||||
if conn.GetURL() == btcFuturesWebsocketURL {
|
||||
a = asset.CoinMarginedFutures
|
||||
}
|
||||
if err := g.CurrencyPairs.IsAssetEnabled(a); err != nil {
|
||||
return err
|
||||
}
|
||||
err = conn.DialContext(ctx, &gws.Dialer{}, http.Header{})
|
||||
if err != nil {
|
||||
if err := conn.DialContext(ctx, &gws.Dialer{}, http.Header{}); err != nil {
|
||||
return err
|
||||
}
|
||||
pingMessage, err := json.Marshal(WsInput{
|
||||
@@ -85,13 +85,13 @@ func (g *Gateio) WsFuturesConnect(ctx context.Context, conn websocket.Connection
|
||||
}
|
||||
|
||||
// GenerateFuturesDefaultSubscriptions returns default subscriptions information.
|
||||
func (g *Gateio) GenerateFuturesDefaultSubscriptions(settlement currency.Code) (subscription.List, error) {
|
||||
func (g *Gateio) GenerateFuturesDefaultSubscriptions(a asset.Item) (subscription.List, error) {
|
||||
channelsToSubscribe := defaultFuturesSubscriptions
|
||||
if g.Websocket.CanUseAuthenticatedEndpoints() {
|
||||
channelsToSubscribe = append(channelsToSubscribe, futuresOrdersChannel, futuresUserTradesChannel, futuresBalancesChannel)
|
||||
}
|
||||
|
||||
pairs, err := g.GetEnabledPairs(asset.Futures)
|
||||
pairs, err := g.GetEnabledPairs(a)
|
||||
if err != nil {
|
||||
if errors.Is(err, asset.ErrNotEnabled) {
|
||||
return nil, nil // no enabled pairs, subscriptions require an associated pair.
|
||||
@@ -99,15 +99,6 @@ func (g *Gateio) GenerateFuturesDefaultSubscriptions(settlement currency.Code) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case settlement.Equal(currency.USDT):
|
||||
pairs = slices.DeleteFunc(pairs, func(p currency.Pair) bool { return !p.Quote.Equal(currency.USDT) })
|
||||
case settlement.Equal(currency.BTC):
|
||||
pairs = slices.DeleteFunc(pairs, func(p currency.Pair) bool { return p.Quote.Equal(currency.USDT) })
|
||||
default:
|
||||
return nil, fmt.Errorf("settlement currency %s not supported", settlement)
|
||||
}
|
||||
|
||||
var subscriptions subscription.List
|
||||
for i := range channelsToSubscribe {
|
||||
for j := range pairs {
|
||||
@@ -122,7 +113,7 @@ func (g *Gateio) GenerateFuturesDefaultSubscriptions(settlement currency.Code) (
|
||||
params["frequency"] = kline.ThousandMilliseconds
|
||||
params["level"] = "100"
|
||||
}
|
||||
fPair, err := g.FormatExchangeCurrency(pairs[j], asset.Futures)
|
||||
fPair, err := g.FormatExchangeCurrency(pairs[j], a)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -130,7 +121,7 @@ func (g *Gateio) GenerateFuturesDefaultSubscriptions(settlement currency.Code) (
|
||||
Channel: channelsToSubscribe[i],
|
||||
Pairs: currency.Pairs{fPair.Upper()},
|
||||
Params: params,
|
||||
Asset: asset.Futures,
|
||||
Asset: a,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,37 +168,11 @@ func (g *Gateio) WebsocketFuturesGetOrderStatus(ctx context.Context, contract cu
|
||||
return &resp, g.SendWebsocketRequest(ctx, perpetualFetchOrderEPL, "futures.order_status", a, params, &resp, 1)
|
||||
}
|
||||
|
||||
func getAssetFromFuturesPair(pair currency.Pair) (asset.Item, error) {
|
||||
if pair.IsEmpty() {
|
||||
return asset.Empty, currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
switch pair.Quote.Item {
|
||||
case currency.USDT.Item:
|
||||
return asset.USDTMarginedFutures, nil
|
||||
case currency.USD.Item:
|
||||
return asset.CoinMarginedFutures, nil
|
||||
default:
|
||||
return asset.Empty, fmt.Errorf("%w futures pair: `%v`", asset.ErrNotSupported, pair)
|
||||
}
|
||||
}
|
||||
|
||||
// validateFuturesPairAsset enforces the asset.Item to be either USDT or Coin margined futures in relation to the pair
|
||||
// for correct routing.
|
||||
// validateFuturesPairAsset enforces that a futures pair's quote currency matches the given asset
|
||||
func validateFuturesPairAsset(pair currency.Pair, a asset.Item) error {
|
||||
if pair.IsEmpty() {
|
||||
return currency.ErrCurrencyPairEmpty
|
||||
}
|
||||
switch a {
|
||||
case asset.USDTMarginedFutures:
|
||||
if pair.Quote.Item != currency.USDT.Item {
|
||||
return fmt.Errorf("%w: '%v' for pair '%v'", asset.ErrNotSupported, a, pair)
|
||||
}
|
||||
case asset.CoinMarginedFutures:
|
||||
if pair.Quote.Item != currency.USD.Item {
|
||||
return fmt.Errorf("%w: '%v' for pair '%v'", asset.ErrNotSupported, a, pair)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%w: '%v' for pair '%v'", asset.ErrNotSupported, a, pair)
|
||||
}
|
||||
return nil
|
||||
_, err := getSettlementCurrency(pair, a)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ func TestWebsocketFuturesCancelOrder(t *testing.T) {
|
||||
_, err = g.WebsocketFuturesCancelOrder(t.Context(), "42069", currency.EMPTYPAIR, asset.Empty)
|
||||
require.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
|
||||
|
||||
_, err = g.WebsocketFuturesCancelOrder(t.Context(), "42069", BTCUSDT, asset.CoinMarginedFutures)
|
||||
_, err = g.WebsocketFuturesCancelOrder(t.Context(), "42069", BTCUSDT, asset.Empty)
|
||||
require.ErrorIs(t, err, asset.ErrNotSupported)
|
||||
|
||||
sharedtestvalues.SkipTestIfCredentialsUnset(t, g, canManipulateRealOrders)
|
||||
@@ -204,32 +204,3 @@ func TestWebsocketFuturesGetOrderStatus(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, got)
|
||||
}
|
||||
|
||||
func TestGetAssetFromFuturesPair(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := getAssetFromFuturesPair(currency.Pair{})
|
||||
require.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
|
||||
|
||||
_, err = getAssetFromFuturesPair(currency.NewPair(currency.BTC, currency.USDC))
|
||||
require.ErrorIs(t, err, asset.ErrNotSupported)
|
||||
|
||||
a, err := getAssetFromFuturesPair(BTCUSDT)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, asset.USDTMarginedFutures, a)
|
||||
|
||||
a, err = getAssetFromFuturesPair(BTCUSD)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, asset.CoinMarginedFutures, a)
|
||||
}
|
||||
|
||||
func TestValidateFuturesPairAsset(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := validateFuturesPairAsset(currency.Pair{}, asset.USDTMarginedFutures)
|
||||
require.ErrorIs(t, err, currency.ErrCurrencyPairEmpty)
|
||||
|
||||
err = validateFuturesPairAsset(BTCUSDT, asset.USDTMarginedFutures)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = validateFuturesPairAsset(BTCUSD, asset.USDTMarginedFutures)
|
||||
require.ErrorIs(t, err, asset.ErrNotSupported)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
15
exchanges/gateio/testdata/wsFutures.json
vendored
Normal file
15
exchanges/gateio/testdata/wsFutures.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{"time":1541659086,"channel":"futures.tickers","event":"update","error":null,"result":[{"contract":"BTC_USD","last":"118.4","change_percentage":"0.77","funding_rate":"-0.000114","funding_rate_indicative":"0.01875","mark_price":"118.35","index_price":"118.36","total_size":"73648","volume_24h":"745487577","volume_24h_btc":"117","volume_24h_usd":"419950","quanto_base_rate":"","volume_24h_quote":"1665006","volume_24h_settle":"178","volume_24h_base":"5526","low_24h":"99.2","high_24h":"132.5"}]}
|
||||
{"channel":"futures.trades","event":"update","time":1541503698,"result":[{"size":-108,"id":27753479,"create_time":1545136464,"create_time_ms":1545136464123,"price":"96.4","contract":"BTC_USD"}]}
|
||||
{"time":1615366379,"channel":"futures.book_ticker","event":"update","error":null,"result":{"t":1615366379123,"u":2517661076,"s":"BTC_USD","b":"54696.6","B":37000,"a":"54696.7","A":47061}}
|
||||
{"channel":"futures.orders","event":"update","time":1541505434,"result":[{"contract":"BTC_USD","create_time":1628736847,"create_time_ms":1628736847325,"fill_price":40000.4,"finish_as":"filled","finish_time":1628736848,"finish_time_ms":1628736848321,"iceberg":0,"id":4872460,"is_close":false,"is_liq":false,"is_reduce_only":false,"left":0,"mkfr":-0.00025,"price":40000.4,"refr":0,"refu":0,"size":1,"status":"finished","text":"-","tif":"gtc","tkfr":0.0005,"user":"110xxxxx"}]}
|
||||
{"time":1543205083,"channel":"futures.usertrades","event":"update","error":null,"result":[{"id":"3335259","create_time":1628736848,"create_time_ms":1628736848321,"contract":"BTC_USD","order_id":"4872460","size":1,"price":"40000.4","role":"maker","text":"api","fee":0.0009290592,"point_fee":0}]}
|
||||
{"channel":"futures.liquidates","event":"update","time":1541505434,"result":[{"entry_price":209,"fill_price":215.1,"left":0,"leverage":0,"liq_price":213,"margin":0.007816722941,"mark_price":213,"order_id":4093362,"order_price":215.1,"size":-124,"time":1541486601,"time_ms":1541486601123,"contract":"BTC_USD","user":"1040xxxx"}]}
|
||||
{"channel": "futures.auto_deleverages", "event": "update", "time": 1541505434, "result": [{"entry_price": 209,"fill_price": 215.1,"position_size": 10,"trade_size": 10,"time": 1541486601,"time_ms": 1541486601123,"contract": "BTC_USD","user": "1040"} ]}
|
||||
{"channel":"futures.position_closes","event":"update","time":1541505434,"result":[{"contract":"BTC_USD","pnl":-0.000624354791,"side":"long","text":"web","time":1547198562,"time_ms":1547198562123,"user":"211xxxx"}]}
|
||||
{"channel":"futures.balances","event":"update","time":1541505434,"result":[{"balance":9.998739899488,"change":-2.074115e-06,"text":"BTC_USD:3914424","time":1547199246,"time_ms":1547199246123,"type":"fee","user":"211xxx"}]}
|
||||
{"time":1551858330,"channel":"futures.reduce_risk_limits","event":"update","error":null,"result":[{"cancel_orders":0,"contract":"BTC_USD","leverage_max":10,"liq_price":136.53,"maintenance_rate":0.09,"risk_limit":450,"time":1551858330,"time_ms":1551858330123,"user":"20011"}]}
|
||||
{"time": 1588212926,"channel": "futures.positions", "event": "update", "error": null, "result": [ { "contract": "BTC_USD", "cross_leverage_limit": 0, "entry_price": 40000.36666661111, "history_pnl": -0.000108569505, "history_point": 0, "last_close_pnl": -0.000050123368,"leverage": 0,"leverage_max": 100,"liq_price": 0.1,"maintenance_rate": 0.005,"margin": 49.999890611186,"mode": "single","realised_pnl": -1.25e-8,"realised_point": 0,"risk_limit": 100,"size": 3,"time": 1628736848,"time_ms": 1628736848321,"user": "110xxxxx"}]}
|
||||
{"time":1596798126,"channel":"futures.autoorders","event":"update","error":null,"result":[{"user":123456,"trigger":{"strategy_type":0,"price_type":0,"price":"10000","rule":2,"expiration":86400},"initial":{"contract":"BTC_USDT","size":10,"price":"10000","tif":"gtc","text":"web","iceberg":0,"is_close":false,"is_reduce_only":false},"id":9256,"trade_id":0,"status":"open","reason":"","create_time":1596798126,"name":"price_autoorders","is_stop_order":false,"stop_trigger":{"rule":0,"trigger_price":"","order_price":""}}]}
|
||||
{"time":1678468497,"time_ms":1678468497232,"channel":"futures.order_book","event":"all","result":{"t":1678468497168,"id":4010394406,"contract":"BTC_USD","asks":[{"p":"19909","s":3100},{"p":"19909.1","s":5000},{"p":"19910","s":3100},{"p":"19914.4","s":4400},{"p":"19916.6","s":5000},{"p":"19917.2","s":8255},{"p":"19919.2","s":5000},{"p":"19920.3","s":11967},{"p":"19922.2","s":5000},{"p":"19924.2","s":5000},{"p":"19927.1","s":17129},{"p":"19927.2","s":5000},{"p":"19929","s":20864},{"p":"19929.3","s":5000},{"p":"19929.7","s":24683},{"p":"19930.3","s":750},{"p":"19931.4","s":5000},{"p":"19931.5","s":1},{"p":"19934.2","s":5000},{"p":"19935.4","s":1}],"bids":[{"p":"19901.2","s":5000},{"p":"19900.3","s":3100},{"p":"19900.2","s":5000},{"p":"19899.3","s":2983},{"p":"19899.2","s":6035},{"p":"19897.2","s":5000},{"p":"19895.7","s":5984},{"p":"19895","s":5000},{"p":"19892.9","s":195},{"p":"19892.8","s":5000},{"p":"19889.4","s":5000},{"p":"19889","s":8800},{"p":"19888.5","s":11968},{"p":"19887.1","s":5000},{"p":"19886.4","s":24683},{"p":"19885.7","s":1},{"p":"19883.8","s":5000},{"p":"19880.2","s":5000},{"p":"19878.2","s":5000},{"p":"19876.8","s":1}]}}
|
||||
{"time":1678469222,"time_ms":1678469222982,"channel":"futures.order_book_update","event":"update","result":{"t":1678469222617,"s":"BTC_USD","U":4010424331,"u":4010424361,"b":[{"p":"19860.7","s":5984},{"p":"19858.6","s":5000},{"p":"19845.4","s":20864},{"p":"19859.1","s":0},{"p":"19862.5","s":0},{"p":"19358","s":0},{"p":"19864.5","s":5000},{"p":"19840.7","s":0},{"p":"19863.6","s":3100},{"p":"19839.3","s":0},{"p":"19851.5","s":8800},{"p":"19720","s":0},{"p":"19333","s":0},{"p":"19852.7","s":5000},{"p":"19861.5","s":0},{"p":"19860.6","s":3100},{"p":"19833.6","s":0},{"p":"19360","s":0},{"p":"19863.5","s":5000},{"p":"19736.9","s":0},{"p":"19838.5","s":0},{"p":"19841.3","s":0},{"p":"19858.1","s":3100},{"p":"19710.9","s":0},{"p":"19342","s":0},{"p":"19852.1","s":11967},{"p":"19343","s":0},{"p":"19705","s":0},{"p":"19836.5","s":0},{"p":"19862.6","s":3100},{"p":"19729.6","s":0},{"p":"19849.9","s":5000}],"a":[{"p":"19900.5","s":0},{"p":"19883.1","s":11967},{"p":"19910.9","s":0},{"p":"19897.7","s":5000},{"p":"19875.9","s":5984},{"p":"19899.6","s":0},{"p":"19878","s":4400},{"p":"19877.6","s":0},{"p":"19889.5","s":5000},{"p":"19875.5","s":3100},{"p":"19875.3","s":0},{"p":"19878.5","s":0},{"p":"19895.2","s":0},{"p":"20284.6","s":0},{"p":"19880.7","s":5000},{"p":"19875.4","s":0},{"p":"19985.8","s":0},{"p":"19887.1","s":5000},{"p":"19896","s":1},{"p":"19869.3","s":0},{"p":"19900","s":0},{"p":"19875.6","s":5000},{"p":"19980.6","s":0},{"p":"19885.1","s":5000},{"p":"19877.7","s":5000},{"p":"20000","s":0},{"p":"19892.2","s":8255},{"p":"19886.8","s":0},{"p":"20257.4","s":0},{"p":"20280","s":0},{"p":"20002.5","s":0},{"p":"20263.1","s":0},{"p":"19900.2","s":0}]}}
|
||||
{"time":1678469467,"time_ms":1678469467981,"channel":"futures.candlesticks","event":"update","result":[{"t":1678469460,"v":0,"c":"19896","h":"19896","l":"19896","o":"19896","n":"1m_BTC_USD"}]}
|
||||
Reference in New Issue
Block a user