mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-30 15:10:40 +00:00
* Exchanges: Remove example BespokeGenerateMessageID * Okx: Replace conn.RequestIDGenerator with MesssageID Continued overall direction to remove the closed-loop of e => conn => e roundtrip for message ids * Exchanges: Add MessageSequence This method removes the either/or nature of message id generation. We don't tie the message ids to connections, or to anything. Consumers just call whichever they want, or even combine them as they want. Anything more complicated will need a separate installation anyway * GateIO: Split usage of MessageID and MessageSequence * Binance: Switch to UUID message IDs * Kraken: Switch to e.MessageSequence * Kucoin: Switch to MessageID * HitBTC: Switch to UUIDv7 for ws message ID * Bybit: Switch to UUIDv7 for ws message ID * Bitfinex: Switch to UUIDv7 and MessageSequence Tested CID - It accepts 53 bits only for an int, so MessageSequence makes sense. Can't use MessageID * Websocket: Remove now unused MessageID function Moved all MessageID usage into funcs and onto base methods, to remove the closed loop of message IDs * Docs: Update guidance for message signatures
207 lines
7.0 KiB
Go
207 lines
7.0 KiB
Go
package gateio
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
gws "github.com/gorilla/websocket"
|
|
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
"github.com/thrasher-corp/gocryptotrader/exchange/websocket"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/subscription"
|
|
)
|
|
|
|
const (
|
|
// delivery real trading urls
|
|
deliveryRealUSDTTradingURL = "wss://fx-ws.gateio.ws/v4/ws/delivery/usdt"
|
|
deliveryRealBTCTradingURL = "wss://fx-ws.gateio.ws/v4/ws/delivery/btc"
|
|
|
|
// delivery testnet urls
|
|
deliveryTestNetBTCTradingURL = "wss://fx-ws-testnet.gateio.ws/v4/ws/delivery/btc" //nolint:unused // Can be used for testing
|
|
deliveryTestNetUSDTTradingURL = "wss://fx-ws-testnet.gateio.ws/v4/ws/delivery/usdt" //nolint:unused // Can be used for testing
|
|
|
|
deliveryFuturesUpdateLimit uint64 = 100
|
|
)
|
|
|
|
var defaultDeliveryFuturesSubscriptions = []string{
|
|
futuresTickersChannel,
|
|
futuresTradesChannel,
|
|
futuresOrderbookUpdateChannel,
|
|
futuresCandlesticksChannel,
|
|
}
|
|
|
|
// WsDeliveryFuturesConnect initiates a websocket connection for delivery futures account
|
|
func (e *Exchange) WsDeliveryFuturesConnect(ctx context.Context, conn websocket.Connection) error {
|
|
if err := e.CurrencyPairs.IsAssetEnabled(asset.DeliveryFutures); err != nil {
|
|
return err
|
|
}
|
|
if err := conn.Dial(ctx, &gws.Dialer{}, http.Header{}); err != nil {
|
|
return err
|
|
}
|
|
pingHandler, err := getWSPingHandler(futuresPingChannel)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
conn.SetupPingHandler(websocketRateLimitNotNeededEPL, pingHandler)
|
|
return nil
|
|
}
|
|
|
|
// GenerateDeliveryFuturesDefaultSubscriptions returns delivery futures default subscriptions params.
|
|
// TODO: Update to use the new subscription template system
|
|
func (e *Exchange) GenerateDeliveryFuturesDefaultSubscriptions() (subscription.List, error) {
|
|
ctx := context.TODO()
|
|
_, err := e.GetCredentials(ctx)
|
|
if err != nil {
|
|
e.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
|
}
|
|
channelsToSubscribe := defaultDeliveryFuturesSubscriptions
|
|
if e.Websocket.CanUseAuthenticatedEndpoints() {
|
|
channelsToSubscribe = append(channelsToSubscribe, futuresOrdersChannel, futuresUserTradesChannel, futuresBalancesChannel)
|
|
}
|
|
|
|
pairs, err := e.GetEnabledPairs(asset.DeliveryFutures)
|
|
if err != nil {
|
|
if errors.Is(err, asset.ErrNotEnabled) {
|
|
return nil, nil // no enabled pairs, subscriptions require an associated pair.
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
var subscriptions subscription.List
|
|
for i := range channelsToSubscribe {
|
|
for j := range pairs {
|
|
params := make(map[string]any)
|
|
switch channelsToSubscribe[i] {
|
|
case futuresOrderbookChannel:
|
|
params["limit"] = 20
|
|
params["interval"] = "0"
|
|
case futuresCandlesticksChannel:
|
|
params["interval"] = kline.FiveMin
|
|
case futuresOrderbookUpdateChannel:
|
|
params["frequency"] = kline.HundredMilliseconds
|
|
params["level"] = strconv.FormatUint(deliveryFuturesUpdateLimit, 10)
|
|
}
|
|
fPair, err := e.FormatExchangeCurrency(pairs[j], asset.DeliveryFutures)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
subscriptions = append(subscriptions, &subscription.Subscription{
|
|
Channel: channelsToSubscribe[i],
|
|
Pairs: currency.Pairs{fPair.Upper()},
|
|
Params: params,
|
|
Asset: asset.DeliveryFutures,
|
|
})
|
|
}
|
|
}
|
|
return subscriptions, nil
|
|
}
|
|
|
|
// DeliveryFuturesSubscribe sends a websocket message to stop receiving data from the channel
|
|
func (e *Exchange) DeliveryFuturesSubscribe(ctx context.Context, conn websocket.Connection, channelsToUnsubscribe subscription.List) error {
|
|
return e.handleSubscription(ctx, conn, subscribeEvent, channelsToUnsubscribe, e.generateDeliveryFuturesPayload)
|
|
}
|
|
|
|
// DeliveryFuturesUnsubscribe sends a websocket message to stop receiving data from the channel
|
|
func (e *Exchange) DeliveryFuturesUnsubscribe(ctx context.Context, conn websocket.Connection, channelsToUnsubscribe subscription.List) error {
|
|
return e.handleSubscription(ctx, conn, unsubscribeEvent, channelsToUnsubscribe, e.generateDeliveryFuturesPayload)
|
|
}
|
|
|
|
func (e *Exchange) generateDeliveryFuturesPayload(ctx context.Context, event string, channelsToSubscribe subscription.List) ([]WsInput, error) {
|
|
if len(channelsToSubscribe) == 0 {
|
|
return nil, errors.New("cannot generate payload, no channels supplied")
|
|
}
|
|
var creds *account.Credentials
|
|
var err error
|
|
if e.Websocket.CanUseAuthenticatedEndpoints() {
|
|
creds, err = e.GetCredentials(ctx)
|
|
if err != nil {
|
|
e.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
|
}
|
|
}
|
|
outbound := make([]WsInput, 0, len(channelsToSubscribe))
|
|
for i := range channelsToSubscribe {
|
|
if len(channelsToSubscribe[i].Pairs) != 1 {
|
|
return nil, subscription.ErrNotSinglePair
|
|
}
|
|
var auth *WsAuthInput
|
|
timestamp := time.Now()
|
|
var params []string
|
|
params = []string{channelsToSubscribe[i].Pairs[0].String()}
|
|
if e.Websocket.CanUseAuthenticatedEndpoints() {
|
|
switch channelsToSubscribe[i].Channel {
|
|
case futuresOrdersChannel, futuresUserTradesChannel,
|
|
futuresLiquidatesChannel, futuresAutoDeleveragesChannel,
|
|
futuresAutoPositionCloseChannel, futuresBalancesChannel,
|
|
futuresReduceRiskLimitsChannel, futuresPositionsChannel,
|
|
futuresAutoOrdersChannel:
|
|
value, ok := channelsToSubscribe[i].Params["user"].(string)
|
|
if ok {
|
|
params = append([]string{value}, params...)
|
|
}
|
|
var sigTemp string
|
|
sigTemp, err = e.generateWsSignature(creds.Secret, event, channelsToSubscribe[i].Channel, timestamp.Unix())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
auth = &WsAuthInput{
|
|
Method: "api_key",
|
|
Key: creds.Key,
|
|
Sign: sigTemp,
|
|
}
|
|
}
|
|
}
|
|
frequency, okay := channelsToSubscribe[i].Params["frequency"].(kline.Interval)
|
|
if okay {
|
|
var frequencyString string
|
|
frequencyString, err = getIntervalString(frequency)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params = append(params, frequencyString)
|
|
}
|
|
levelString, okay := channelsToSubscribe[i].Params["level"].(string)
|
|
if okay {
|
|
params = append(params, levelString)
|
|
}
|
|
limit, okay := channelsToSubscribe[i].Params["limit"].(int)
|
|
if okay {
|
|
params = append(params, strconv.Itoa(limit))
|
|
}
|
|
accuracy, okay := channelsToSubscribe[i].Params["accuracy"].(string)
|
|
if okay {
|
|
params = append(params, accuracy)
|
|
}
|
|
switch channelsToSubscribe[i].Channel {
|
|
case futuresCandlesticksChannel:
|
|
interval, okay := channelsToSubscribe[i].Params["interval"].(kline.Interval)
|
|
if okay {
|
|
var intervalString string
|
|
intervalString, err = getIntervalString(interval)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
params = append([]string{intervalString}, params...)
|
|
}
|
|
case futuresOrderbookChannel:
|
|
intervalString, okay := channelsToSubscribe[i].Params["interval"].(string)
|
|
if okay {
|
|
params = append(params, intervalString)
|
|
}
|
|
}
|
|
outbound = append(outbound, WsInput{
|
|
ID: e.MessageSequence(),
|
|
Event: event,
|
|
Channel: channelsToSubscribe[i].Channel,
|
|
Payload: params,
|
|
Auth: auth,
|
|
Time: timestamp.Unix(),
|
|
})
|
|
}
|
|
return outbound, nil
|
|
}
|