GateIO: Fix GetFuturesContractDetails for delivery futures and minor 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
This commit is contained in:
Gareth Kirwan
2025-01-14 04:19:17 +00:00
committed by GitHub
parent 861054ceb0
commit 4c7f48ae0e
12 changed files with 317 additions and 312 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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
@@ -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{
@@ -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{

View File

@@ -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

View File

@@ -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)
@@ -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() {
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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
}
@@ -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)
}
@@ -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
}
@@ -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
}

View File

@@ -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) {
@@ -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) {
@@ -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) {
@@ -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) {
@@ -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) {
@@ -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) {
@@ -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)
}
_, err = g.GetFuturesContractDetails(context.Background(), asset.PerpetualContract)
if !errors.Is(err, asset.ErrNotSupported) {
t.Error(err)
}
require.ErrorIs(t, err, futures.ErrNotFuturesAsset)
_, 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)
}
_, err = g.GetFuturesContractDetails(context.Background(), asset.PerpetualContract)
require.ErrorIs(t, err, asset.ErrNotSupported)
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) {
@@ -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())
}
}
}

View File

@@ -585,18 +585,17 @@ type Orderbook struct {
// Trade represents market trade.
type Trade struct {
ID int64 `json:"id,string"`
TradingTime types.Time `json:"create_time"`
CreateTimeMs types.Time `json:"create_time_ms"`
OrderID string `json:"order_id"`
Side string `json:"side"`
Role string `json:"role"`
Amount types.Number `json:"amount"`
Price types.Number `json:"price"`
Fee types.Number `json:"fee"`
FeeCurrency string `json:"fee_currency"`
PointFee string `json:"point_fee"`
GtFee string `json:"gt_fee"`
ID int64 `json:"id,string"`
CreateTime types.Time `json:"create_time_ms"`
OrderID string `json:"order_id"`
Side string `json:"side"`
Role string `json:"role"`
Amount types.Number `json:"amount"`
Price types.Number `json:"price"`
Fee types.Number `json:"fee"`
FeeCurrency string `json:"fee_currency"`
PointFee string `json:"point_fee"`
GtFee string `json:"gt_fee"`
}
// Candlestick represents candlestick data point detail.
@@ -679,17 +678,22 @@ type FuturesContract struct {
OrdersLimit int64 `json:"orders_limit"`
TradeID int64 `json:"trade_id"`
OrderbookID int64 `json:"orderbook_id"`
EnableBonus bool `json:"enable_bonus"`
EnableCredit bool `json:"enable_credit"`
CreateTime types.Time `json:"create_time"`
FundingCapRatio types.Number `json:"funding_cap_ratio"`
VoucherLeverage types.Number `json:"voucher_leverage"`
}
// TradingHistoryItem represents futures trading history item.
type TradingHistoryItem struct {
ID int64 `json:"id"`
CreateTime types.Time `json:"create_time"`
CreateTime types.Time `json:"create_time_ms"`
Contract string `json:"contract"`
Text string `json:"text"`
Size float64 `json:"size"`
Price types.Number `json:"price"`
// Added for Derived market trade history datas.
// Added for Derived market trade history data
Fee types.Number `json:"fee"`
PointFee types.Number `json:"point_fee"`
Role string `json:"role"`
@@ -704,9 +708,7 @@ type FuturesCandlestick struct {
LowestPrice types.Number `json:"l"`
OpenPrice types.Number `json:"o"`
Sum types.Number `json:"sum"` // Trading volume (unit: Quote currency)
// Added for websocket push data
Name string `json:"n,omitempty"`
Name string `json:"n,omitempty"`
}
// FuturesPremiumIndexKLineResponse represents premium index K-Line information.
@@ -836,15 +838,6 @@ type DeliveryContract struct {
InDelisting bool `json:"in_delisting"`
}
// DeliveryTradingHistory represents futures trading history
type DeliveryTradingHistory struct {
ID int64 `json:"id"`
CreateTime types.Time `json:"create_time"`
Contract string `json:"contract"`
Size float64 `json:"size"`
Price types.Number `json:"price"`
}
// OptionUnderlying represents option underlying and it's index price.
type OptionUnderlying struct {
Name string `json:"name"`
@@ -1223,8 +1216,7 @@ type AccountBalanceInformation struct {
// MarginAccountBalanceChangeInfo represents margin account balance
type MarginAccountBalanceChangeInfo struct {
ID string `json:"id"`
Time types.Time `json:"time"`
TimeMs types.Time `json:"time_ms"`
Time types.Time `json:"time_ms"`
Currency string `json:"currency"`
CurrencyPair string `json:"currency_pair"`
AmountChanged string `json:"change"`
@@ -1393,10 +1385,8 @@ type SpotOrder struct {
Succeeded bool `json:"succeeded"`
ErrorLabel string `json:"label,omitempty"`
Message string `json:"message,omitempty"`
CreateTime types.Time `json:"create_time,omitempty"`
CreateTimeMs types.Time `json:"create_time_ms,omitempty"`
UpdateTime types.Time `json:"update_time,omitempty"`
UpdateTimeMs types.Time `json:"update_time_ms,omitempty"`
CreateTime types.Time `json:"create_time_ms,omitempty"`
UpdateTime types.Time `json:"update_time_ms,omitempty"`
CurrencyPair string `json:"currency_pair,omitempty"`
Status string `json:"status,omitempty"`
Type string `json:"type,omitempty"`
@@ -1457,8 +1447,7 @@ type CancelOrderByIDResponse struct {
// SpotPersonalTradeHistory represents personal trading history.
type SpotPersonalTradeHistory struct {
TradeID string `json:"id"`
CreateTime types.Time `json:"create_time"`
CreateTimeMs types.Time `json:"create_time_ms"`
CreateTime types.Time `json:"create_time_ms"`
CurrencyPair string `json:"currency_pair"`
OrderID string `json:"order_id"`
Side string `json:"side"`
@@ -2006,11 +1995,10 @@ type WsEventResponse struct {
}
}
// WsResponse represents generalized websocket push data from the server.
type WsResponse struct {
// WSResponse represents generalized websocket push data from the server.
type WSResponse struct {
ID int64 `json:"id"`
Time types.Time `json:"time"`
TimeMs types.Time `json:"time_ms"`
Time time.Time `json:"time"`
Channel string `json:"channel"`
Event string `json:"event"`
Result json.RawMessage `json:"result"`
@@ -2033,8 +2021,7 @@ type WsTicker struct {
// WsTrade represents a websocket push data response for a trade
type WsTrade struct {
ID int64 `json:"id"`
CreateTime types.Time `json:"create_time"`
CreateTimeMs types.Time `json:"create_time_ms"`
CreateTime types.Time `json:"create_time_ms"`
Side string `json:"side"`
CurrencyPair currency.Pair `json:"currency_pair"`
Amount types.Number `json:"amount"`
@@ -2054,7 +2041,7 @@ type WsCandlesticks struct {
// WsOrderbookTickerData represents the websocket orderbook best bid or best ask push data
type WsOrderbookTickerData struct {
UpdateTimeMS types.Time `json:"t"`
UpdateTime types.Time `json:"t"`
UpdateOrderID int64 `json:"u"`
CurrencyPair currency.Pair `json:"s"`
BestBidPrice types.Number `json:"b"`
@@ -2065,9 +2052,7 @@ type WsOrderbookTickerData struct {
// WsOrderbookUpdate represents websocket orderbook update push data
type WsOrderbookUpdate struct {
UpdateTimeMs types.Time `json:"t"`
IgnoreField string `json:"e"`
UpdateTime types.Time `json:"E"`
UpdateTime types.Time `json:"t"`
CurrencyPair currency.Pair `json:"s"`
FirstOrderbookUpdatedID int64 `json:"U"` // First update order book id in this event since last update
LastOrderbookUpdatedID int64 `json:"u"`
@@ -2077,7 +2062,7 @@ type WsOrderbookUpdate struct {
// WsOrderbookSnapshot represents a websocket orderbook snapshot push data
type WsOrderbookSnapshot struct {
UpdateTimeMs types.Time `json:"t"`
UpdateTime types.Time `json:"t"`
LastUpdateID int64 `json:"lastUpdateId"`
CurrencyPair currency.Pair `json:"s"`
Bids [][2]types.Number `json:"bids"`
@@ -2110,10 +2095,8 @@ type WsSpotOrder struct {
RebatedFee string `json:"rebated_fee,omitempty"`
RebatedFeeCurrency string `json:"rebated_fee_currency,omitempty"`
Event string `json:"event"`
CreateTime types.Time `json:"create_time,omitempty"`
CreateTimeMs types.Time `json:"create_time_ms,omitempty"`
UpdateTime types.Time `json:"update_time,omitempty"`
UpdateTimeMs types.Time `json:"update_time_ms,omitempty"`
CreateTime types.Time `json:"create_time_ms,omitempty"`
UpdateTime types.Time `json:"update_time_ms,omitempty"`
}
// WsUserPersonalTrade represents a user's personal trade pushed through the websocket connection.
@@ -2122,8 +2105,7 @@ type WsUserPersonalTrade struct {
UserID int64 `json:"user_id"`
OrderID string `json:"order_id"`
CurrencyPair currency.Pair `json:"currency_pair"`
CreateTime types.Time `json:"create_time"`
CreateTimeMs types.Time `json:"create_time_ms"`
CreateTime types.Time `json:"create_time_ms"`
Side string `json:"side"`
Amount types.Number `json:"amount"`
Role string `json:"role"`
@@ -2136,19 +2118,17 @@ type WsUserPersonalTrade struct {
// WsSpotBalance represents a spot balance.
type WsSpotBalance struct {
Timestamp types.Time `json:"timestamp"`
TimestampMs types.Time `json:"timestamp_ms"`
User string `json:"user"`
Currency string `json:"currency"`
Change types.Number `json:"change"`
Total types.Number `json:"total"`
Available types.Number `json:"available"`
Timestamp types.Time `json:"timestamp_ms"`
User string `json:"user"`
Currency string `json:"currency"`
Change types.Number `json:"change"`
Total types.Number `json:"total"`
Available types.Number `json:"available"`
}
// WsMarginBalance represents margin account balance push data
type WsMarginBalance struct {
Timestamp types.Time `json:"timestamp"`
TimestampMs types.Time `json:"timestamp_ms"`
Timestamp types.Time `json:"timestamp_ms"`
User string `json:"user"`
CurrencyPair string `json:"currency_pair"`
Currency string `json:"currency"`
@@ -2161,24 +2141,22 @@ type WsMarginBalance struct {
// WsFundingBalance represents funding balance push data.
type WsFundingBalance struct {
Timestamp types.Time `json:"timestamp"`
TimestampMs types.Time `json:"timestamp_ms"`
User string `json:"user"`
Currency string `json:"currency"`
Change string `json:"change"`
Freeze string `json:"freeze"`
Lent string `json:"lent"`
Timestamp types.Time `json:"timestamp_ms"`
User string `json:"user"`
Currency string `json:"currency"`
Change string `json:"change"`
Freeze string `json:"freeze"`
Lent string `json:"lent"`
}
// WsCrossMarginBalance represents a cross margin balance detail
type WsCrossMarginBalance struct {
Timestamp types.Time `json:"timestamp"`
TimestampMs types.Time `json:"timestamp_ms"`
User string `json:"user"`
Currency string `json:"currency"`
Change string `json:"change"`
Total types.Number `json:"total"`
Available types.Number `json:"available"`
Timestamp types.Time `json:"timestamp_ms"`
User string `json:"user"`
Currency string `json:"currency"`
Change string `json:"change"`
Total types.Number `json:"total"`
Available types.Number `json:"available"`
}
// WsCrossMarginLoan represents a cross margin loan push data
@@ -2216,17 +2194,16 @@ type WsFutureTicker struct {
// WsFuturesTrades represents a list of trades push data
type WsFuturesTrades struct {
Size float64 `json:"size"`
ID int64 `json:"id"`
CreateTime types.Time `json:"create_time"`
CreateTimeMs types.Time `json:"create_time_ms"`
Price types.Number `json:"price"`
Contract currency.Pair `json:"contract"`
Size float64 `json:"size"`
ID int64 `json:"id"`
CreateTime types.Time `json:"create_time_ms"`
Price types.Number `json:"price"`
Contract currency.Pair `json:"contract"`
}
// WsFuturesOrderbookTicker represents the orderbook ticker push data
type WsFuturesOrderbookTicker struct {
TimestampMs types.Time `json:"t"`
Timestamp types.Time `json:"t"`
UpdateID int64 `json:"u"`
CurrencyPair string `json:"s"`
BestBidPrice types.Number `json:"b"`
@@ -2237,7 +2214,7 @@ type WsFuturesOrderbookTicker struct {
// WsFuturesAndOptionsOrderbookUpdate represents futures and options account orderbook update push data
type WsFuturesAndOptionsOrderbookUpdate struct {
TimestampInMs types.Time `json:"t"`
Timestamp types.Time `json:"t"`
ContractName currency.Pair `json:"s"`
FirstUpdatedID int64 `json:"U"`
LastUpdatedID int64 `json:"u"`
@@ -2253,10 +2230,10 @@ type WsFuturesAndOptionsOrderbookUpdate struct {
// WsFuturesOrderbookSnapshot represents a futures orderbook snapshot push data
type WsFuturesOrderbookSnapshot struct {
TimestampInMs types.Time `json:"t"`
Contract currency.Pair `json:"contract"`
OrderbookID int64 `json:"id"`
Asks []struct {
Timestamp types.Time `json:"t"`
Contract currency.Pair `json:"contract"`
OrderbookID int64 `json:"id"`
Asks []struct {
Price types.Number `json:"p"`
Size float64 `json:"s"`
} `json:"asks"`
@@ -2277,12 +2254,10 @@ type WsFuturesOrderbookUpdateEvent struct {
// WsFuturesOrder represents futures order
type WsFuturesOrder struct {
Contract currency.Pair `json:"contract"`
CreateTime types.Time `json:"create_time"`
CreateTimeMs types.Time `json:"create_time_ms"`
CreateTime types.Time `json:"create_time_ms"`
FillPrice float64 `json:"fill_price"`
FinishAs string `json:"finish_as"`
FinishTime types.Time `json:"finish_time"`
FinishTimeMs types.Time `json:"finish_time_ms"`
FinishTime types.Time `json:"finish_time_ms"`
Iceberg int64 `json:"iceberg"`
ID int64 `json:"id"`
IsClose bool `json:"is_close"`
@@ -2303,17 +2278,16 @@ type WsFuturesOrder struct {
// WsFuturesUserTrade represents a futures account user trade push data
type WsFuturesUserTrade struct {
ID string `json:"id"`
CreateTime types.Time `json:"create_time"`
CreateTimeMs types.Time `json:"create_time_ms"`
Contract currency.Pair `json:"contract"`
OrderID string `json:"order_id"`
Size float64 `json:"size"`
Price types.Number `json:"price"`
Role string `json:"role"`
Text string `json:"text"`
Fee float64 `json:"fee"`
PointFee int64 `json:"point_fee"`
ID string `json:"id"`
CreateTime types.Time `json:"create_time_ms"`
Contract currency.Pair `json:"contract"`
OrderID string `json:"order_id"`
Size float64 `json:"size"`
Price types.Number `json:"price"`
Role string `json:"role"`
Text string `json:"text"`
Fee float64 `json:"fee"`
PointFee int64 `json:"point_fee"`
}
// WsFuturesLiquidationNotification represents a liquidation notification push data
@@ -2328,8 +2302,7 @@ type WsFuturesLiquidationNotification struct {
OrderID int64 `json:"order_id"`
OrderPrice float64 `json:"order_price"`
Size float64 `json:"size"`
Time types.Time `json:"time"`
TimeMs types.Time `json:"time_ms"`
Time types.Time `json:"time_ms"`
Contract string `json:"contract"`
User string `json:"user"`
}
@@ -2340,8 +2313,7 @@ type WsFuturesAutoDeleveragesNotification struct {
FillPrice float64 `json:"fill_price"`
PositionSize int64 `json:"position_size"`
TradeSize int64 `json:"trade_size"`
Time types.Time `json:"time"`
TimeMs types.Time `json:"time_ms"`
Time types.Time `json:"time_ms"`
Contract string `json:"contract"`
User string `json:"user"`
}
@@ -2352,8 +2324,7 @@ type WsPositionClose struct {
ProfitAndLoss float64 `json:"pnl,omitempty"`
Side string `json:"side"`
Text string `json:"text"`
Time types.Time `json:"time"`
TimeMs types.Time `json:"time_ms"`
Time types.Time `json:"time_ms"`
User string `json:"user"`
// Added in options close position push datas
@@ -2366,8 +2337,7 @@ type WsBalance struct {
Balance float64 `json:"balance"`
Change float64 `json:"change"`
Text string `json:"text"`
Time types.Time `json:"time"`
TimeMs types.Time `json:"time_ms"`
Time types.Time `json:"time_ms"`
Type string `json:"type"`
User string `json:"user"`
}
@@ -2380,8 +2350,7 @@ type WsFuturesReduceRiskLimitNotification struct {
LiqPrice float64 `json:"liq_price"`
MaintenanceRate float64 `json:"maintenance_rate"`
RiskLimit int64 `json:"risk_limit"`
Time types.Time `json:"time"`
TimeMs types.Time `json:"time_ms"`
Time types.Time `json:"time_ms"`
User string `json:"user"`
}
@@ -2403,8 +2372,7 @@ type WsFuturesPosition struct {
RealisedPoint float64 `json:"realised_point"`
RiskLimit float64 `json:"risk_limit"`
Size float64 `json:"size"`
Time types.Time `json:"time"`
TimeMs types.Time `json:"time_ms"`
Time types.Time `json:"time_ms"`
User string `json:"user"`
}
@@ -2453,31 +2421,26 @@ type WsOptionUnderlyingTicker struct {
// WsOptionsTrades represents options trades for websocket push data.
type WsOptionsTrades struct {
ID int64 `json:"id"`
CreateTime types.Time `json:"create_time"`
CreateTime types.Time `json:"create_time_ms"`
Contract currency.Pair `json:"contract"`
Size float64 `json:"size"`
Price float64 `json:"price"`
// Added in options websocket push data
CreateTimeMs types.Time `json:"create_time_ms"`
Underlying string `json:"underlying"`
IsCall bool `json:"is_call"` // added in underlying trades
Underlying string `json:"underlying"`
IsCall bool `json:"is_call"` // added in underlying trades
}
// WsOptionsUnderlyingPrice represents the underlying price.
type WsOptionsUnderlyingPrice struct {
Underlying string `json:"underlying"`
Price float64 `json:"price"`
UpdateTime types.Time `json:"time"`
UpdateTimeMs types.Time `json:"time_ms"`
Underlying string `json:"underlying"`
Price float64 `json:"price"`
UpdateTime types.Time `json:"time_ms"`
}
// WsOptionsMarkPrice represents options mark price push data.
type WsOptionsMarkPrice struct {
Contract string `json:"contract"`
Price float64 `json:"price"`
UpdateTimeMs types.Time `json:"time_ms"`
UpdateTime types.Time `json:"time"`
Contract string `json:"contract"`
Price float64 `json:"price"`
UpdateTime types.Time `json:"time_ms"`
}
// WsOptionsSettlement represents a options settlement push data.
@@ -2492,8 +2455,7 @@ type WsOptionsSettlement struct {
TradeID int64 `json:"trade_id"`
TradeSize int64 `json:"trade_size"`
Underlying string `json:"underlying"`
UpdateTime types.Time `json:"time"`
UpdateTimeMs types.Time `json:"time_ms"`
UpdateTime types.Time `json:"time_ms"`
}
// WsOptionsContract represents an option contract push data.
@@ -2521,8 +2483,7 @@ type WsOptionsContract struct {
Tag string `json:"tag"`
TakerFeeRate float64 `json:"taker_fee_rate"`
Underlying string `json:"underlying"`
Time types.Time `json:"time"`
TimeMs types.Time `json:"time_ms"`
Time types.Time `json:"time_ms"`
}
// WsOptionsContractCandlestick represents an options contract candlestick push data.
@@ -2565,42 +2526,40 @@ type WsOptionsOrderbookSnapshot struct {
// WsOptionsOrder represents options order push data.
type WsOptionsOrder struct {
ID int64 `json:"id"`
Contract currency.Pair `json:"contract"`
CreateTime types.Time `json:"create_time"`
FillPrice float64 `json:"fill_price"`
FinishAs string `json:"finish_as"`
Iceberg float64 `json:"iceberg"`
IsClose bool `json:"is_close"`
IsLiq bool `json:"is_liq"`
IsReduceOnly bool `json:"is_reduce_only"`
Left float64 `json:"left"`
Mkfr float64 `json:"mkfr"`
Price float64 `json:"price"`
Refr float64 `json:"refr"`
Refu float64 `json:"refu"`
Size float64 `json:"size"`
Status string `json:"status"`
Text string `json:"text"`
Tif string `json:"tif"`
Tkfr float64 `json:"tkfr"`
Underlying string `json:"underlying"`
User string `json:"user"`
CreationTime types.Time `json:"time"`
CreationTimeMs types.Time `json:"time_ms"`
ID int64 `json:"id"`
Contract currency.Pair `json:"contract"`
CreateTime types.Time `json:"create_time"`
FillPrice float64 `json:"fill_price"`
FinishAs string `json:"finish_as"`
Iceberg float64 `json:"iceberg"`
IsClose bool `json:"is_close"`
IsLiq bool `json:"is_liq"`
IsReduceOnly bool `json:"is_reduce_only"`
Left float64 `json:"left"`
Mkfr float64 `json:"mkfr"`
Price float64 `json:"price"`
Refr float64 `json:"refr"`
Refu float64 `json:"refu"`
Size float64 `json:"size"`
Status string `json:"status"`
Text string `json:"text"`
Tif string `json:"tif"`
Tkfr float64 `json:"tkfr"`
Underlying string `json:"underlying"`
User string `json:"user"`
CreationTime types.Time `json:"time_ms"`
}
// WsOptionsUserTrade represents user's personal trades of option account.
type WsOptionsUserTrade struct {
ID string `json:"id"`
Underlying string `json:"underlying"`
OrderID string `json:"order"`
Contract currency.Pair `json:"contract"`
CreateTime types.Time `json:"create_time"`
CreateTimeMs types.Time `json:"create_time_ms"`
Price types.Number `json:"price"`
Role string `json:"role"`
Size float64 `json:"size"`
ID string `json:"id"`
Underlying string `json:"underlying"`
OrderID string `json:"order"`
Contract currency.Pair `json:"contract"`
CreateTime types.Time `json:"create_time_ms"`
Price types.Number `json:"price"`
Role string `json:"role"`
Size float64 `json:"size"`
}
// WsOptionsLiquidates represents the liquidates push data of option account.
@@ -2609,8 +2568,7 @@ type WsOptionsLiquidates struct {
InitMargin float64 `json:"init_margin"`
MaintMargin float64 `json:"maint_margin"`
OrderMargin float64 `json:"order_margin"`
Time types.Time `json:"time"`
TimeMs types.Time `json:"time_ms"`
Time types.Time `json:"time_ms"`
}
// WsOptionsUserSettlement represents user's personal settlements push data of options account.
@@ -2623,19 +2581,17 @@ type WsOptionsUserSettlement struct {
Size float64 `json:"size"`
StrikePrice float64 `json:"strike_price"`
Underlying string `json:"underlying"`
SettleTime types.Time `json:"time"`
SettleTimeMs types.Time `json:"time_ms"`
SettleTime types.Time `json:"time_ms"`
}
// WsOptionsPosition represents positions push data for options account.
type WsOptionsPosition struct {
EntryPrice float64 `json:"entry_price"`
RealisedPnl float64 `json:"realised_pnl"`
Size float64 `json:"size"`
Contract string `json:"contract"`
User string `json:"user"`
UpdateTime types.Time `json:"time"`
UpdateTimeMs types.Time `json:"time_ms"`
EntryPrice float64 `json:"entry_price"`
RealisedPnl float64 `json:"realised_pnl"`
Size float64 `json:"size"`
Contract string `json:"contract"`
User string `json:"user"`
UpdateTime types.Time `json:"time_ms"`
}
// InterSubAccountTransferParams represents parameters to transfer funds between sub-accounts.

View File

@@ -15,6 +15,7 @@ import (
"time"
"github.com/Masterminds/sprig/v3"
"github.com/buger/jsonparser"
"github.com/gorilla/websocket"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/currency"
@@ -166,8 +167,8 @@ func (g *Gateio) generateWsSignature(secret, event, channel string, t int64) (st
// WsHandleSpotData handles spot data
func (g *Gateio) WsHandleSpotData(_ context.Context, respRaw []byte) error {
var push WsResponse
if err := json.Unmarshal(respRaw, &push); err != nil {
push, err := parseWSHeader(respRaw)
if err != nil {
return err
}
@@ -181,17 +182,17 @@ func (g *Gateio) WsHandleSpotData(_ context.Context, respRaw []byte) error {
switch push.Channel { // TODO: Convert function params below to only use push.Result
case spotTickerChannel:
return g.processTicker(push.Result, push.TimeMs.Time())
return g.processTicker(push.Result, push.Time)
case spotTradesChannel:
return g.processTrades(push.Result)
case spotCandlesticksChannel:
return g.processCandlestick(push.Result)
case spotOrderbookTickerChannel:
return g.processOrderbookTicker(push.Result, push.TimeMs.Time())
return g.processOrderbookTicker(push.Result, push.Time)
case spotOrderbookUpdateChannel:
return g.processOrderbookUpdate(push.Result, push.TimeMs.Time())
return g.processOrderbookUpdate(push.Result, push.Time)
case spotOrderbookChannel:
return g.processOrderbookSnapshot(push.Result, push.TimeMs.Time())
return g.processOrderbookSnapshot(push.Result, push.Time)
case spotOrdersChannel:
return g.processSpotOrders(respRaw)
case spotUserTradesChannel:
@@ -216,6 +217,45 @@ func (g *Gateio) WsHandleSpotData(_ context.Context, respRaw []byte) error {
return nil
}
func parseWSHeader(msg []byte) (r *WSResponse, errs error) {
r = &WSResponse{}
paths := [][]string{{"time_ms"}, {"time"}, {"channel"}, {"event"}, {"request_id"}, {"id"}, {"result"}}
jsonparser.EachKey(msg, func(idx int, v []byte, _ jsonparser.ValueType, _ error) {
switch idx {
case 0: // time_ms
if ts, err := strconv.ParseInt(string(v), 10, 64); err != nil {
errs = common.AppendError(errs, fmt.Errorf("%w parsing `time_ms`", err))
} else {
r.Time = time.UnixMilli(ts)
}
case 1: // time
if r.Time.IsZero() {
if ts, err := strconv.ParseInt(string(v), 10, 64); err != nil {
errs = common.AppendError(errs, fmt.Errorf("%w parsing `time`", err))
} else {
r.Time = time.Unix(ts, 0)
}
}
case 2:
r.Channel = string(v)
case 3:
r.Event = string(v)
case 4:
r.RequestID = string(v)
case 5:
if id, err := strconv.ParseInt(string(v), 10, 64); err != nil {
errs = common.AppendError(errs, fmt.Errorf("%w parsing `id`", err))
} else {
r.ID = id
}
case 6:
r.Result = json.RawMessage(v)
}
}, paths...)
return r, errs
}
func (g *Gateio) processTicker(incoming []byte, pushTime time.Time) error {
var data WsTicker
if err := json.Unmarshal(incoming, &data); err != nil {
@@ -262,7 +302,7 @@ func (g *Gateio) processTrades(incoming []byte) error {
for _, a := range standardMarginAssetTypes {
if enabled, _ := g.CurrencyPairs.IsPairEnabled(data.CurrencyPair, a); enabled {
if err := g.Websocket.Trade.Update(saveTradeData, trade.Data{
Timestamp: data.CreateTimeMs.Time(),
Timestamp: data.CreateTime.Time(),
CurrencyPair: data.CurrencyPair,
AssetType: a,
Exchange: g.Name,
@@ -323,7 +363,7 @@ func (g *Gateio) processOrderbookTicker(incoming []byte, updatePushedAt time.Tim
Exchange: g.Name,
Pair: data.CurrencyPair,
Asset: asset.Spot,
LastUpdated: data.UpdateTimeMS.Time(),
LastUpdated: data.UpdateTime.Time(),
UpdatePushedAt: updatePushedAt,
Bids: []orderbook.Tranche{{Price: data.BestBidPrice.Float64(), Amount: data.BestBidAmount.Float64()}},
Asks: []orderbook.Tranche{{Price: data.BestAskPrice.Float64(), Amount: data.BestAskAmount.Float64()}},
@@ -378,7 +418,7 @@ func (g *Gateio) processOrderbookUpdate(incoming []byte, updatePushedAt time.Tim
for _, a := range enabledAssets {
if err := g.Websocket.Orderbook.Update(&orderbook.Update{
UpdateTime: data.UpdateTimeMs.Time(),
UpdateTime: data.UpdateTime.Time(),
UpdatePushedAt: updatePushedAt,
Pair: data.CurrencyPair,
Asset: a,
@@ -415,7 +455,7 @@ func (g *Gateio) processOrderbookSnapshot(incoming []byte, updatePushedAt time.T
Exchange: g.Name,
Pair: data.CurrencyPair,
Asset: a,
LastUpdated: data.UpdateTimeMs.Time(),
LastUpdated: data.UpdateTime.Time(),
UpdatePushedAt: updatePushedAt,
Bids: bids,
Asks: asks,
@@ -463,8 +503,8 @@ func (g *Gateio) processSpotOrders(data []byte) error {
AssetType: a,
Price: resp.Result[x].Price.Float64(),
ExecutedAmount: resp.Result[x].Amount.Float64() - resp.Result[x].Left.Float64(),
Date: resp.Result[x].CreateTimeMs.Time(),
LastUpdated: resp.Result[x].UpdateTimeMs.Time(),
Date: resp.Result[x].CreateTime.Time(),
LastUpdated: resp.Result[x].UpdateTime.Time(),
}
}
g.Websocket.DataHandler <- details
@@ -493,7 +533,7 @@ func (g *Gateio) processUserPersonalTrades(data []byte) error {
return err
}
fills[x] = fill.Data{
Timestamp: resp.Result[x].CreateTimeMs.Time(),
Timestamp: resp.Result[x].CreateTime.Time(),
Exchange: g.Name,
CurrencyPair: resp.Result[x].CurrencyPair,
Side: side,

View File

@@ -149,8 +149,7 @@ func (g *Gateio) FuturesUnsubscribe(ctx context.Context, conn stream.Connection,
// WsHandleFuturesData handles futures websocket data
func (g *Gateio) WsHandleFuturesData(_ context.Context, respRaw []byte, a asset.Item) error {
var push WsResponse
err := json.Unmarshal(respRaw, &push)
push, err := parseWSHeader(respRaw)
if err != nil {
return err
}
@@ -168,7 +167,7 @@ func (g *Gateio) WsHandleFuturesData(_ context.Context, respRaw []byte, a asset.
case futuresTradesChannel:
return g.processFuturesTrades(respRaw, a)
case futuresOrderbookChannel:
return g.processFuturesOrderbookSnapshot(push.Event, push.Result, a, push.TimeMs.Time())
return g.processFuturesOrderbookSnapshot(push.Event, push.Result, a, push.Time)
case futuresOrderbookTickerChannel:
return g.processFuturesOrderbookTicker(push.Result)
case futuresOrderbookUpdateChannel:
@@ -353,7 +352,7 @@ func (g *Gateio) processFuturesTrades(data []byte, assetType asset.Item) error {
trades := make([]trade.Data, len(resp.Result))
for x := range resp.Result {
trades[x] = trade.Data{
Timestamp: resp.Result[x].CreateTimeMs.Time(),
Timestamp: resp.Result[x].CreateTime.Time(),
CurrencyPair: resp.Result[x].Contract,
AssetType: assetType,
Exchange: g.Name,
@@ -439,7 +438,7 @@ func (g *Gateio) processFuturesAndOptionsOrderbookUpdate(incoming []byte, assetT
}
}
updates := orderbook.Update{
UpdateTime: data.TimestampInMs.Time(),
UpdateTime: data.Timestamp.Time(),
Pair: data.ContractName,
Asset: assetType,
}
@@ -470,7 +469,7 @@ func (g *Gateio) processFuturesOrderbookSnapshot(event string, incoming []byte,
Asset: assetType,
Exchange: g.Name,
Pair: data.Contract,
LastUpdated: data.TimestampInMs.Time(),
LastUpdated: data.Timestamp.Time(),
UpdatePushedAt: updatePushedAt,
VerifyOrderbook: g.CanVerifyOrderbook,
}
@@ -574,13 +573,13 @@ func (g *Gateio) processFuturesOrdersPushData(data []byte, assetType asset.Item)
OrderID: strconv.FormatInt(resp.Result[x].ID, 10),
Status: status,
Pair: resp.Result[x].Contract,
LastUpdated: resp.Result[x].FinishTimeMs.Time(),
Date: resp.Result[x].CreateTimeMs.Time(),
LastUpdated: resp.Result[x].FinishTime.Time(),
Date: resp.Result[x].CreateTime.Time(),
ExecutedAmount: resp.Result[x].Size - resp.Result[x].Left,
Price: resp.Result[x].Price,
AssetType: assetType,
AccountID: resp.Result[x].User,
CloseTime: resp.Result[x].FinishTimeMs.Time(),
CloseTime: resp.Result[x].FinishTime.Time(),
}
}
return orderDetails, nil
@@ -604,7 +603,7 @@ func (g *Gateio) procesFuturesUserTrades(data []byte, assetType asset.Item) erro
fills := make([]fill.Data, len(resp.Result))
for x := range resp.Result {
fills[x] = fill.Data{
Timestamp: resp.Result[x].CreateTimeMs.Time(),
Timestamp: resp.Result[x].CreateTime.Time(),
Exchange: g.Name,
CurrencyPair: resp.Result[x].Contract,
OrderID: resp.Result[x].OrderID,

View File

@@ -294,8 +294,7 @@ func (g *Gateio) OptionsUnsubscribe(ctx context.Context, conn stream.Connection,
// WsHandleOptionsData handles options websocket data
func (g *Gateio) WsHandleOptionsData(_ context.Context, respRaw []byte) error {
var push WsResponse
err := json.Unmarshal(respRaw, &push)
push, err := parseWSHeader(respRaw)
if err != nil {
return err
}
@@ -327,7 +326,7 @@ func (g *Gateio) WsHandleOptionsData(_ context.Context, respRaw []byte) error {
optionsUnderlyingCandlesticksChannel:
return g.processOptionsCandlestickPushData(respRaw)
case optionsOrderbookChannel:
return g.processOptionsOrderbookSnapshotPushData(push.Event, push.Result, push.Time.Time())
return g.processOptionsOrderbookSnapshotPushData(push.Event, push.Result, push.Time)
case optionsOrderbookTickerChannel:
return g.processOrderbookTickerPushData(respRaw)
case optionsOrderbookUpdateChannel:
@@ -402,7 +401,7 @@ func (g *Gateio) processOptionsTradesPushData(data []byte) error {
trades := make([]trade.Data, len(resp.Result))
for x := range resp.Result {
trades[x] = trade.Data{
Timestamp: resp.Result[x].CreateTimeMs.Time(),
Timestamp: resp.Result[x].CreateTime.Time(),
CurrencyPair: resp.Result[x].Contract,
AssetType: asset.Options,
Exchange: g.Name,
@@ -606,7 +605,7 @@ func (g *Gateio) processOptionsOrderPushData(data []byte) error {
OrderID: strconv.FormatInt(resp.Result[x].ID, 10),
Status: status,
Pair: resp.Result[x].Contract,
Date: resp.Result[x].CreationTimeMs.Time(),
Date: resp.Result[x].CreationTime.Time(),
ExecutedAmount: resp.Result[x].Size - resp.Result[x].Left,
Price: resp.Result[x].Price,
AssetType: asset.Options,
@@ -634,7 +633,7 @@ func (g *Gateio) processOptionsUserTradesPushData(data []byte) error {
fills := make([]fill.Data, len(resp.Result))
for x := range resp.Result {
fills[x] = fill.Data{
Timestamp: resp.Result[x].CreateTimeMs.Time(),
Timestamp: resp.Result[x].CreateTime.Time(),
Exchange: g.Name,
CurrencyPair: resp.Result[x].Contract,
OrderID: resp.Result[x].OrderID,

View File

@@ -945,18 +945,16 @@ func (g *Gateio) GetRecentTrades(ctx context.Context, p currency.Pair, a asset.I
var resp []trade.Data
switch a {
case asset.Spot, asset.Margin, asset.CrossMargin:
var tradeData []Trade
if p.IsEmpty() {
return nil, currency.ErrCurrencyPairEmpty
}
tradeData, err = g.GetMarketTrades(ctx, p, 0, "", false, time.Time{}, time.Time{}, 0)
tradeData, err := g.GetMarketTrades(ctx, p, 0, "", false, time.Time{}, time.Time{}, 0)
if err != nil {
return nil, err
}
resp = make([]trade.Data, len(tradeData))
for i := range tradeData {
var side order.Side
side, err = order.StringToOrderSide(tradeData[i].Side)
side, err := order.StringToOrderSide(tradeData[i].Side)
if err != nil {
return nil, err
}
@@ -968,17 +966,15 @@ func (g *Gateio) GetRecentTrades(ctx context.Context, p currency.Pair, a asset.I
Side: side,
Price: tradeData[i].Price.Float64(),
Amount: tradeData[i].Amount.Float64(),
Timestamp: tradeData[i].CreateTimeMs.Time(),
Timestamp: tradeData[i].CreateTime.Time(),
}
}
case asset.Futures:
var settle currency.Code
settle, err = getSettlementFromCurrency(p)
settle, err := getSettlementFromCurrency(p)
if err != nil {
return nil, err
}
var futuresTrades []TradingHistoryItem
futuresTrades, err = g.GetFuturesTradingHistory(ctx, settle, p, 0, 0, "", time.Time{}, time.Time{})
futuresTrades, err := g.GetFuturesTradingHistory(ctx, settle, p, 0, 0, "", time.Time{}, time.Time{})
if err != nil {
return nil, err
}
@@ -995,13 +991,11 @@ func (g *Gateio) GetRecentTrades(ctx context.Context, p currency.Pair, a asset.I
}
}
case asset.DeliveryFutures:
var settle currency.Code
settle, err = getSettlementFromCurrency(p)
settle, err := getSettlementFromCurrency(p)
if err != nil {
return nil, err
}
var deliveryTrades []DeliveryTradingHistory
deliveryTrades, err = g.GetDeliveryTradingHistory(ctx, settle, "", p.Upper(), 0, time.Time{}, time.Time{})
deliveryTrades, err := g.GetDeliveryTradingHistory(ctx, settle, "", p.Upper(), 0, time.Time{}, time.Time{})
if err != nil {
return nil, err
}
@@ -1018,8 +1012,7 @@ func (g *Gateio) GetRecentTrades(ctx context.Context, p currency.Pair, a asset.I
}
}
case asset.Options:
var trades []TradingHistoryItem
trades, err = g.GetOptionsTradeHistory(ctx, p.Upper(), "", 0, 0, time.Time{}, time.Time{})
trades, err := g.GetOptionsTradeHistory(ctx, p.Upper(), "", 0, 0, time.Time{}, time.Time{})
if err != nil {
return nil, err
}
@@ -1114,8 +1107,8 @@ func (g *Gateio) SubmitOrder(ctx context.Context, s *order.Submit) (*order.Submi
response.Pair = s.Pair
response.Date = sOrder.CreateTime.Time()
response.ClientOrderID = sOrder.Text
response.Date = sOrder.CreateTimeMs.Time()
response.LastUpdated = sOrder.UpdateTimeMs.Time()
response.Date = sOrder.CreateTime.Time()
response.LastUpdated = sOrder.UpdateTime.Time()
return response, nil
case asset.Futures:
// TODO: See https://www.gate.io/docs/developers/apiv4/en/#create-a-futures-order
@@ -1497,8 +1490,8 @@ func (g *Gateio) GetOrderInfo(ctx context.Context, orderID string, pair currency
Status: orderStatus,
Price: spotOrder.Price.Float64(),
ExecutedAmount: spotOrder.Amount.Float64() - spotOrder.Left.Float64(),
Date: spotOrder.CreateTimeMs.Time(),
LastUpdated: spotOrder.UpdateTimeMs.Time(),
Date: spotOrder.CreateTime.Time(),
LastUpdated: spotOrder.UpdateTime.Time(),
}, nil
case asset.Futures, asset.DeliveryFutures:
var settle currency.Code
@@ -1705,8 +1698,8 @@ func (g *Gateio) GetActiveOrders(ctx context.Context, req *order.MultiOrderReque
RemainingAmount: spotOrders[x].Orders[y].Left.Float64(),
Price: spotOrders[x].Orders[y].Price.Float64(),
AverageExecutedPrice: spotOrders[x].Orders[y].AverageFillPrice.Float64(),
Date: spotOrders[x].Orders[y].CreateTimeMs.Time(),
LastUpdated: spotOrders[x].Orders[y].UpdateTimeMs.Time(),
Date: spotOrders[x].Orders[y].CreateTime.Time(),
LastUpdated: spotOrders[x].Orders[y].UpdateTime.Time(),
Exchange: g.Name,
AssetType: req.AssetType,
ClientOrderID: spotOrders[x].Orders[y].Text,
@@ -1840,7 +1833,7 @@ func (g *Gateio) GetOrderHistory(ctx context.Context, req *order.MultiOrderReque
for x := range req.Pairs {
fPair := req.Pairs[x].Format(format)
var spotOrders []SpotPersonalTradeHistory
spotOrders, err = g.GateIOGetPersonalTradingHistory(ctx, fPair, req.FromOrderID, 0, 0, req.AssetType == asset.CrossMargin, req.StartTime, req.EndTime)
spotOrders, err = g.GetMySpotTradingHistory(ctx, fPair, req.FromOrderID, 0, 0, req.AssetType == asset.CrossMargin, req.StartTime, req.EndTime)
if err != nil {
return nil, err
}
@@ -1877,9 +1870,9 @@ func (g *Gateio) GetOrderHistory(ctx context.Context, req *order.MultiOrderReque
}
var futuresOrder []TradingHistoryItem
if req.AssetType == asset.Futures {
futuresOrder, err = g.GetMyPersonalTradingHistory(ctx, settle, "", req.FromOrderID, fPair, 0, 0, 0)
futuresOrder, err = g.GetMyFuturesTradingHistory(ctx, settle, "", req.FromOrderID, fPair, 0, 0, 0)
} else {
futuresOrder, err = g.GetDeliveryPersonalTradingHistory(ctx, settle, req.FromOrderID, fPair, 0, 0, 0, "")
futuresOrder, err = g.GetMyDeliveryTradingHistory(ctx, settle, req.FromOrderID, fPair, 0, 0, 0, "")
}
if err != nil {
return nil, err
@@ -1901,8 +1894,7 @@ func (g *Gateio) GetOrderHistory(ctx context.Context, req *order.MultiOrderReque
case asset.Options:
for x := range req.Pairs {
fPair := req.Pairs[x].Format(format)
var optionOrders []OptionTradingHistory
optionOrders, err = g.GetOptionsPersonalTradingHistory(ctx, fPair.String(), fPair.Upper(), 0, 0, req.StartTime, req.EndTime)
optionOrders, err := g.GetMyOptionsTradingHistory(ctx, fPair.String(), fPair.Upper(), 0, 0, req.StartTime, req.EndTime)
if err != nil {
return nil, err
}
@@ -2127,28 +2119,26 @@ func (g *Gateio) GetFuturesContractDetails(ctx context.Context, item asset.Item)
}
return resp, nil
case asset.DeliveryFutures:
var resp []futures.Contract
contracts, err := g.GetAllDeliveryContracts(ctx, currency.USDT)
if err != nil {
return nil, err
}
contractsToAdd := make([]futures.Contract, len(contracts))
for j := range contracts {
var name, underlying currency.Pair
name, err = currency.NewPairFromString(contracts[j].Name)
resp := make([]futures.Contract, len(contracts))
for i := range contracts {
name, err := currency.NewPairFromString(contracts[i].Name)
if err != nil {
return nil, err
}
underlying, err = currency.NewPairFromString(contracts[j].Underlying)
underlying, err := currency.NewPairFromString(contracts[i].Underlying)
if err != nil {
return nil, err
}
var ct futures.ContractType
// no start information, inferring it based on contract type
// gateio also reuses contracts for kline data, cannot use a lookup to see the first trade
var s, e time.Time
e = contracts[j].ExpireTime.Time()
switch contracts[j].Cycle {
var s time.Time
e := contracts[i].ExpireTime.Time()
ct := futures.LongDated
switch contracts[i].Cycle {
case "WEEKLY":
ct = futures.Weekly
s = e.Add(-kline.OneWeek.Duration())
@@ -2161,10 +2151,8 @@ func (g *Gateio) GetFuturesContractDetails(ctx context.Context, item asset.Item)
case "BI-QUARTERLY":
ct = futures.HalfYearly
s = e.Add(-kline.SixMonth.Duration())
default:
ct = futures.LongDated
}
contractsToAdd[j] = futures.Contract{
resp[i] = futures.Contract{
Exchange: g.Name,
Name: name,
Underlying: underlying,
@@ -2172,14 +2160,13 @@ func (g *Gateio) GetFuturesContractDetails(ctx context.Context, item asset.Item)
StartDate: s,
EndDate: e,
SettlementType: futures.Linear,
IsActive: !contracts[j].InDelisting,
IsActive: !contracts[i].InDelisting,
Type: ct,
SettlementCurrencies: currency.Currencies{currency.USDT},
MarginCurrency: currency.Code{},
Multiplier: contracts[j].QuantoMultiplier.Float64(),
MaxLeverage: contracts[j].LeverageMax.Float64(),
Multiplier: contracts[i].QuantoMultiplier.Float64(),
MaxLeverage: contracts[i].LeverageMax.Float64(),
}
resp = append(resp, contractsToAdd...)
}
return resp, nil
}
@@ -2338,7 +2325,7 @@ func (g *Gateio) GetLatestFundingRates(ctx context.Context, r *fundingrate.Lates
if err != nil {
return nil, err
}
contract, err := g.GetSingleContract(ctx, settle, fPair.String())
contract, err := g.GetFuturesContract(ctx, settle, fPair.String())
if err != nil {
return nil, err
}
@@ -2424,11 +2411,11 @@ func (g *Gateio) GetOpenInterest(ctx context.Context, k ...key.PairAsset) ([]fut
return nil, err
}
if !isEnabled {
return nil, fmt.Errorf("%w %v", asset.ErrNotEnabled, k[0].Pair())
return nil, fmt.Errorf("%w: %v", currency.ErrPairNotEnabled, k[0].Pair())
}
switch k[0].Asset {
case asset.DeliveryFutures:
contractResp, err := g.GetSingleDeliveryContracts(ctx, currency.USDT, p)
contractResp, err := g.GetDeliveryContract(ctx, currency.USDT, p)
if err != nil {
return nil, err
}
@@ -2446,7 +2433,7 @@ func (g *Gateio) GetOpenInterest(ctx context.Context, k ...key.PairAsset) ([]fut
}, nil
case asset.Futures:
for _, s := range settlementCurrencies {
contractResp, err := g.GetSingleContract(ctx, s, p.String())
contractResp, err := g.GetFuturesContract(ctx, s, p.String())
if err != nil {
continue
}

View File

@@ -129,7 +129,7 @@ func expandTemplate(e IExchange, s *Subscription, ap assetPairs, assets asset.It
}
buf := &bytes.Buffer{}
if err := t.Execute(buf, subCtx); err != nil { //nolint:govet // Shadow, or gocritic will complain sloppyReassign
if err := t.Execute(buf, subCtx); err != nil {
return nil, err
}