Skip to content

Commit

Permalink
GateIO: Fix GetFuturesContractDetails for delivery futures and minor …
Browse files Browse the repository at this point in the history
…other fixes (#1766)

* GateIO: Fix GetFuturesContractDetails for Deliveries

Was returning the product of all the contracts, so 1444 instead of 38
contracts.

* GateIO: Fix GetOpenInterest returning asset.ErrNotEnabled

Using wrong error for pair not enabled

* GateIO: Rename GetSingleContract and GetSingleDeliveryContracts

Especially fixes GetSingleContract, which seems misleading to not say
Futures.
There's a load of `GetSingle*` here that should probably also be fixed,
but these two justified a dyno

* GateIO: Rename GateIOGetPersonalTradingHistory to GetMySpotTradingHistory

* GateIO: Rename GetMyPersonalTradingHistory to GetMyFuturesTradingHistory

* GateIO: Remove duplicate DeliveryTradingHistory

* GateIO: Rename Get*PersonalTradingHistory to GetMy*TradingHistory

* Linter: Disable shadow linting for err

It's been a year, and I'm still getting caught out by govet demanding I
don't shadow a var I was deliberately shadowing.
Made worse by an increase in clashes with stylecheck when they both want
opposite things on the same line.

* GateIO: Add missing Futures and tradinghistory fields

* GateIO: Improve WS Header parsing

This unifies handling for time_ms and time in response headers, since
options and delivery have only time, but spot has time_ms as well.
We use the better of the two results.

Also [improves performance 2x](https://gist.github.com/gbjk/7cacb63b9a256e745534bb05ca853c48)

* GateIO: Use time_ms WS fields where available

Removes the deprecated _time json fields and populates our Time fields
with the time_ms values
  • Loading branch information
gbjk authored Jan 14, 2025
1 parent 861054c commit 4c7f48a
Show file tree
Hide file tree
Showing 12 changed files with 316 additions and 311 deletions.
3 changes: 3 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ issues:
- text: "Expect WriteFile permissions to be 0600 or less"
linters:
- gosec
- text: 'shadow: declaration of "err" shadows declaration at'
linters: [ govet ]


exclude-dirs:
- vendor
Expand Down
9 changes: 5 additions & 4 deletions currency/pairs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import (
"strings"
)

// Public Errors
var (
ErrPairDuplication = errors.New("currency pair duplication")
)

var (
errSymbolEmpty = errors.New("symbol is empty")
errNoDelimiter = errors.New("no delimiter was supplied")
errPairFormattingInconsistent = errors.New("pair formatting is inconsistent")

// ErrPairDuplication defines an error when there is multiple of the same
// currency pairs found.
ErrPairDuplication = errors.New("currency pair duplication")
)

// NewPairsFromStrings takes in currency pair strings and returns a currency
Expand Down
5 changes: 2 additions & 3 deletions engine/rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ var (
errGRPCShutdownSignalIsNil = errors.New("cannot shutdown, gRPC shutdown channel is nil")
errInvalidStrategy = errors.New("invalid strategy")
errSpecificPairNotEnabled = errors.New("specified pair is not enabled")
errPairNotEnabled = errors.New("pair is not enabled")
)

// RPCServer struct
Expand Down Expand Up @@ -4723,7 +4722,7 @@ func (s *RPCServer) GetFundingRates(ctx context.Context, r *gctrpc.GetFundingRat
}

if !pairs.Contains(cp, true) {
return nil, fmt.Errorf("%w %v", errPairNotEnabled, cp)
return nil, fmt.Errorf("%w %v", currency.ErrPairNotEnabled, cp)
}

funding, err := exch.GetHistoricalFundingRates(ctx, &fundingrate.HistoricalRatesRequest{
Expand Down Expand Up @@ -4821,7 +4820,7 @@ func (s *RPCServer) GetLatestFundingRate(ctx context.Context, r *gctrpc.GetLates
}

if !pairs.Contains(cp, true) {
return nil, fmt.Errorf("%w %v", errPairNotEnabled, cp)
return nil, fmt.Errorf("%w %v", currency.ErrPairNotEnabled, cp)
}

fundingRates, err := exch.GetLatestFundingRates(ctx, &fundingrate.LatestRateRequest{
Expand Down
2 changes: 1 addition & 1 deletion exchanges/btse/btse_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -1281,7 +1281,7 @@ func (b *BTSE) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item) err
var errs error
limits := make([]order.MinMaxLevel, 0, len(summary))
for _, marketInfo := range summary {
p, err := marketInfo.Pair() //nolint:govet // Deliberately shadow err
p, err := marketInfo.Pair()
if err != nil {
errs = common.AppendError(err, fmt.Errorf("%s: %w", p, err))
continue
Expand Down
35 changes: 17 additions & 18 deletions exchanges/gateio/gateio.go
Original file line number Diff line number Diff line change
Expand Up @@ -775,12 +775,11 @@ func (g *Gateio) CancelSingleSpotOrder(ctx context.Context, orderID, currencyPai
return response, g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, spotCancelSingleOrderEPL, http.MethodDelete, gateioSpotOrders+"/"+orderID, params, nil, &response)
}

// GateIOGetPersonalTradingHistory retrieves personal trading history
func (g *Gateio) GateIOGetPersonalTradingHistory(ctx context.Context, currencyPair currency.Pair,
orderID string, page, limit uint64, crossMarginAccount bool, from, to time.Time) ([]SpotPersonalTradeHistory, error) {
// GetMySpotTradingHistory retrieves personal trading history
func (g *Gateio) GetMySpotTradingHistory(ctx context.Context, p currency.Pair, orderID string, page, limit uint64, crossMargin bool, from, to time.Time) ([]SpotPersonalTradeHistory, error) {
params := url.Values{}
if currencyPair.IsPopulated() {
params.Set("currency_pair", currencyPair.String())
if p.IsPopulated() {
params.Set("currency_pair", p.String())
}
if orderID != "" {
params.Set("order_id", orderID)
Expand All @@ -791,7 +790,7 @@ func (g *Gateio) GateIOGetPersonalTradingHistory(ctx context.Context, currencyPa
if page > 0 {
params.Set("page", strconv.FormatUint(page, 10))
}
if crossMarginAccount {
if crossMargin {
params.Set("account", asset.CrossMargin.String())
}
if !from.IsZero() {
Expand Down Expand Up @@ -1842,8 +1841,8 @@ func (g *Gateio) GetAllFutureContracts(ctx context.Context, settle currency.Code
return contracts, g.SendHTTPRequest(ctx, exchange.RestSpot, publicFuturesContractsEPL, futuresPath+settle.Item.Lower+"/contracts", &contracts)
}

// GetSingleContract returns a single contract info for the specified settle and Currency Pair (contract << in this case)
func (g *Gateio) GetSingleContract(ctx context.Context, settle currency.Code, contract string) (*FuturesContract, error) {
// GetFuturesContract returns a single futures contract info for the specified settle and Currency Pair (contract << in this case)
func (g *Gateio) GetFuturesContract(ctx context.Context, settle currency.Code, contract string) (*FuturesContract, error) {
if contract == "" {
return nil, currency.ErrCurrencyPairEmpty
}
Expand Down Expand Up @@ -2428,8 +2427,8 @@ func (g *Gateio) AmendFuturesOrder(ctx context.Context, settle currency.Code, or
return response, g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, perpetualAmendOrderEPL, http.MethodPut, futuresPath+settle.Item.Lower+"/orders/"+orderID, nil, &arg, &response)
}

// GetMyPersonalTradingHistory retrieves my personal trading history
func (g *Gateio) GetMyPersonalTradingHistory(ctx context.Context, settle currency.Code, lastID, orderID string, contract currency.Pair, limit, offset, countTotal uint64) ([]TradingHistoryItem, error) {
// GetMyFuturesTradingHistory retrieves authenticated account's futures trading history
func (g *Gateio) 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
}
Expand Down Expand Up @@ -2623,8 +2622,8 @@ func (g *Gateio) GetAllDeliveryContracts(ctx context.Context, settle currency.Co
return contracts, g.SendHTTPRequest(ctx, exchange.RestSpot, publicDeliveryContractsEPL, deliveryPath+settle.Item.Lower+"/contracts", &contracts)
}

// GetSingleDeliveryContracts retrieves a single delivery contract instance.
func (g *Gateio) GetSingleDeliveryContracts(ctx context.Context, settle currency.Code, contract currency.Pair) (*DeliveryContract, error) {
// GetDeliveryContract retrieves a single delivery contract instance
func (g *Gateio) GetDeliveryContract(ctx context.Context, settle currency.Code, contract currency.Pair) (*DeliveryContract, error) {
if settle.IsEmpty() {
return nil, errEmptyOrInvalidSettlementCurrency
}
Expand Down Expand Up @@ -2656,7 +2655,7 @@ func (g *Gateio) GetDeliveryOrderbook(ctx context.Context, settle currency.Code,
}

// GetDeliveryTradingHistory retrieves futures trading history
func (g *Gateio) GetDeliveryTradingHistory(ctx context.Context, settle currency.Code, lastID string, contract currency.Pair, limit uint64, from, to time.Time) ([]DeliveryTradingHistory, error) {
func (g *Gateio) 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
}
Expand All @@ -2677,7 +2676,7 @@ func (g *Gateio) GetDeliveryTradingHistory(ctx context.Context, settle currency.
if lastID != "" {
params.Set("last_id", lastID)
}
var histories []DeliveryTradingHistory
var histories []TradingHistoryItem
return histories, g.SendHTTPRequest(ctx, exchange.RestSpot, publicTradingHistoryDeliveryEPL, common.EncodeURLValues(deliveryPath+settle.Item.Lower+"/trades", params), &histories)
}

Expand Down Expand Up @@ -2941,8 +2940,8 @@ func (g *Gateio) CancelSingleDeliveryOrder(ctx context.Context, settle currency.
return response, g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, deliveryCancelOrderEPL, http.MethodDelete, deliveryPath+settle.Item.Lower+"/orders/"+orderID, nil, nil, &response)
}

// GetDeliveryPersonalTradingHistory retrieves personal trading history
func (g *Gateio) GetDeliveryPersonalTradingHistory(ctx context.Context, settle currency.Code, orderID string, contract currency.Pair, limit, offset, countTotal uint64, lastID string) ([]TradingHistoryItem, error) {
// GetMyDeliveryTradingHistory retrieves authenticated account delivery futures trading history
func (g *Gateio) 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
}
Expand Down Expand Up @@ -3407,8 +3406,8 @@ func (g *Gateio) CancelOptionSingleOrder(ctx context.Context, orderID string) (*
return response, g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, optionsCancelOrderEPL, http.MethodDelete, "options/orders/"+orderID, nil, nil, &response)
}

// GetOptionsPersonalTradingHistory retrieves personal tradign histories given the underlying{Required}, contract, and other pagination params.
func (g *Gateio) GetOptionsPersonalTradingHistory(ctx context.Context, underlying string, contract currency.Pair, offset, limit uint64, from, to time.Time) ([]OptionTradingHistory, error) {
// GetMyOptionsTradingHistory retrieves authenticated account's option trading history
func (g *Gateio) GetMyOptionsTradingHistory(ctx context.Context, underlying string, contract currency.Pair, offset, limit uint64, from, to time.Time) ([]OptionTradingHistory, error) {
if underlying == "" {
return nil, errInvalidUnderlying
}
Expand Down
90 changes: 56 additions & 34 deletions exchanges/gateio/gateio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,12 +439,11 @@ func TestCancelSingleSpotOrder(t *testing.T) {
}
}

func TestGetPersonalTradingHistory(t *testing.T) {
func TestGetMySpotTradingHistory(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, g)
if _, err := g.GateIOGetPersonalTradingHistory(context.Background(), currency.Pair{Base: currency.BTC, Quote: currency.USDT, Delimiter: currency.UnderscoreDelimiter}, "", 0, 0, false, time.Time{}, time.Time{}); err != nil {
t.Errorf("%s GetPersonalTradingHistory() error %v", g.Name, err)
}
_, err := g.GetMySpotTradingHistory(context.Background(), currency.Pair{Base: currency.BTC, Quote: currency.USDT, Delimiter: currency.UnderscoreDelimiter}, "", 0, 0, false, time.Time{}, time.Time{})
require.NoError(t, err)
}

func TestGetServerTime(t *testing.T) {
Expand Down Expand Up @@ -968,12 +967,12 @@ func TestGetAllFutureContracts(t *testing.T) {
}
}

func TestGetSingleContract(t *testing.T) {
func TestGetFuturesContract(t *testing.T) {
t.Parallel()
settle, err := getSettlementFromCurrency(getPair(t, asset.Futures))
require.NoError(t, err, "getSettlementFromCurrency must not error")
_, err = g.GetSingleContract(context.Background(), settle, getPair(t, asset.Futures).String())
assert.NoError(t, err, "GetSingleContract should not error")
_, err = g.GetFuturesContract(context.Background(), settle, getPair(t, asset.Futures).String())
assert.NoError(t, err, "GetFuturesContract should not error")
}

func TestGetFuturesOrderbook(t *testing.T) {
Expand Down Expand Up @@ -1156,11 +1155,11 @@ func TestCancelSingleDeliveryOrder(t *testing.T) {
assert.NoError(t, err, "CancelSingleDeliveryOrder should not error")
}

func TestGetDeliveryPersonalTradingHistory(t *testing.T) {
func TestGetMyDeliveryTradingHistory(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, g)
_, err := g.GetDeliveryPersonalTradingHistory(context.Background(), currency.USDT, "", getPair(t, asset.DeliveryFutures), 0, 0, 1, "")
assert.NoError(t, err, "GetDeliveryPersonalTradingHistory should not error")
_, err := g.GetMyDeliveryTradingHistory(context.Background(), currency.USDT, "", getPair(t, asset.DeliveryFutures), 0, 0, 1, "")
assert.NoError(t, err, "GetMyDeliveryTradingHistory should not error")
}

func TestGetDeliveryPositionCloseHistory(t *testing.T) {
Expand Down Expand Up @@ -1368,11 +1367,11 @@ func TestAmendFuturesOrder(t *testing.T) {
assert.NoError(t, err, "AmendFuturesOrder should not error")
}

func TestGetMyPersonalTradingHistory(t *testing.T) {
func TestGetMyFuturesTradingHistory(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, g)
_, err := g.GetMyPersonalTradingHistory(context.Background(), currency.BTC, "", "", getPair(t, asset.Futures), 0, 0, 0)
assert.NoError(t, err, "GetMyPersonalTradingHistory should not error")
_, err := g.GetMyFuturesTradingHistory(context.Background(), currency.BTC, "", "", getPair(t, asset.Futures), 0, 0, 0)
assert.NoError(t, err, "GetMyFuturesTradingHistory should not error")
}

func TestGetFuturesPositionCloseHistory(t *testing.T) {
Expand Down Expand Up @@ -1457,12 +1456,12 @@ func TestGetAllDeliveryContracts(t *testing.T) {
}
}

func TestGetSingleDeliveryContracts(t *testing.T) {
func TestGetDeliveryContract(t *testing.T) {
t.Parallel()
settle, err := getSettlementFromCurrency(getPair(t, asset.DeliveryFutures))
require.NoError(t, err, "getSettlementFromCurrency must not error")
_, err = g.GetSingleDeliveryContracts(context.Background(), settle, getPair(t, asset.DeliveryFutures))
assert.NoError(t, err, "GetSingleDeliveryContracts should not error")
_, err = g.GetDeliveryContract(context.Background(), settle, getPair(t, asset.DeliveryFutures))
assert.NoError(t, err, "GetDeliveryContract should not error")
}

func TestGetDeliveryOrderbook(t *testing.T) {
Expand Down Expand Up @@ -1767,12 +1766,12 @@ func TestCancelSingleOrder(t *testing.T) {
}
}

func TestGetOptionsPersonalTradingHistory(t *testing.T) {
func TestGetMyOptionsTradingHistory(t *testing.T) {
t.Parallel()

sharedtestvalues.SkipTestIfCredentialsUnset(t, g)
if _, err := g.GetOptionsPersonalTradingHistory(context.Background(), "BTC_USDT", currency.EMPTYPAIR, 0, 0, time.Time{}, time.Time{}); err != nil {
t.Errorf("%s GetOptionPersonalTradingHistory() error %v", g.Name, err)
}
_, err := g.GetMyOptionsTradingHistory(context.Background(), "BTC_USDT", currency.EMPTYPAIR, 0, 0, time.Time{}, time.Time{})
require.NoError(t, err)
}

func TestWithdrawCurrency(t *testing.T) {
Expand Down Expand Up @@ -3186,22 +3185,20 @@ func TestForceFileStandard(t *testing.T) {
func TestGetFuturesContractDetails(t *testing.T) {
t.Parallel()
_, err := g.GetFuturesContractDetails(context.Background(), asset.Spot)
if !errors.Is(err, futures.ErrNotFuturesAsset) {
t.Error(err)
}
require.ErrorIs(t, err, futures.ErrNotFuturesAsset)

_, err = g.GetFuturesContractDetails(context.Background(), asset.PerpetualContract)
if !errors.Is(err, asset.ErrNotSupported) {
t.Error(err)
}
require.ErrorIs(t, err, asset.ErrNotSupported)

_, err = g.GetFuturesContractDetails(context.Background(), asset.DeliveryFutures)
if !errors.Is(err, nil) {
t.Error(err)
}
_, err = g.GetFuturesContractDetails(context.Background(), asset.Futures)
if !errors.Is(err, nil) {
t.Error(err)
}
exp, err := g.GetAllDeliveryContracts(context.Background(), currency.USDT)
require.NoError(t, err, "GetAllDeliveryContracts must not error")
c, err := g.GetFuturesContractDetails(context.Background(), asset.DeliveryFutures)
require.NoError(t, err, "GetFuturesContractDetails must not error for DeliveryFutures")
assert.Equal(t, len(exp), len(c), "GetFuturesContractDetails should return same number of Delivery contracts as exist")

c, err = g.GetFuturesContractDetails(context.Background(), asset.Futures)
require.NoError(t, err, "GetFuturesContractDetails must not error for DeliveryFutures")
assert.NotEmpty(t, c, "GetFuturesContractDetails should return same number of Future contracts as exist")
}

func TestGetLatestFundingRates(t *testing.T) {
Expand Down Expand Up @@ -3528,3 +3525,28 @@ func TestHandleSubscriptions(t *testing.T) {
})
require.NoError(t, err)
}

func TestParseWSHeader(t *testing.T) {
in := []string{
`{"time":1726121320,"time_ms":1726121320745,"id":1,"channel":"spot.tickers","event":"subscribe","result":{"status":"success"},"request_id":"a4"}`,
`{"time_ms":1726121320746,"id":2,"channel":"spot.tickers","event":"subscribe","result":{"status":"success"},"request_id":"a4"}`,
`{"time":1726121321,"id":3,"channel":"spot.tickers","event":"subscribe","result":{"status":"success"},"request_id":"a4"}`,
}
for _, i := range in {
h, err := parseWSHeader([]byte(i))
require.NoError(t, err)
require.NotEmpty(t, h.ID)
assert.Equal(t, "a4", h.RequestID)
assert.Equal(t, "spot.tickers", h.Channel)
assert.Equal(t, "subscribe", h.Event)
assert.NotEmpty(t, h.Result)
switch h.ID {
case 1:
assert.Equal(t, int64(1726121320745), h.Time.UnixMilli())
case 2:
assert.Equal(t, int64(1726121320746), h.Time.UnixMilli())
case 3:
assert.Equal(t, int64(1726121321), h.Time.Unix())
}
}
}
Loading

0 comments on commit 4c7f48a

Please sign in to comment.