Files
gocryptotrader/exchanges/gateio/gateio_websocket_delivery_futures.go
Samuael A. 3f534a15f1 cmd/exchange_template, exchanges: Update templates and propogate to exchanges (#1777)
* Added TimeInForce type and updated related files

* Linter issue fix and minor coinbasepro type update

* Bitrex consts update

* added unit test and minor changes in bittrex

* Unit tests update

* Fix minor linter issues

* Update TestStringToTimeInForce unit test

* Exchange test template change

* A different approach

* fix conflict with gateio timeInForce

* minor exchange template update

* Minor fix to test_files template

* Update order tests

* Complete updating the order unit tests

* Updating exchange wrapper and test template files

* update kucoin and deribit wrapper to match the time in force change

* minor comment update

* fix time-in-force related test errors

* linter issue fix

* ADD_NEW_EXCHANGE documentation update

* time in force constants, functions and unit tests update

* shift tif policies to TimeInForce

* Update time-in-force, related functions, and unit tests

* fix linter issue and time-in-force processing

* added a good till crossing tif value

* order type fix and fix related tim-in-force entries

* update time-in-force unmarshaling and unit test

* consistency guideline added

* fix time-in-force error in gateio

* linter issue fix

* update based on review comments

* add unit test and fix missing issues

* minor fix and added benchmark unit test

* change GTT to GTC for limit

* fix linter issue

* added time-in-force value to place order param

* fix minor issues based on review comment and move tif code to separate files

* update on exchanges linked to time-in-force

* resolve missing review comments

* minor linter issues fix

* added time-in-force handler and update timeInForce parametered endpoint

* minor fixes based on review

* nits fix

* update based on review

* linter fix

* rm getTimeInForce func and minor change to time-in-force

* minor change

* update based on review comments

* wrappers and time-in-force calling approach

* minor change

* update gateio string to timeInForce conversion and unit test

* update exchange template

* update wrapper template file

* policy comments, and template files update

* rename all exchange types name to Exchange

* update on template files and template generation

* templates and generation code and other updates

* linter issue fix

* added subscriptions and websocket templates

* update ADD_NEW_EXCHANGE.md with recent binance functions and implementations

* rename template files and update unit tests

* minor template and unit test fix

* rename templates and fix on unit tests

* update on template files and documentation

* removed unnecessary tag fix and update templates

* fix Add_NEW_EXCHANGE.md doc file

* formatting, comments, and error checks update on template files

* rename exchange receivers to e and ex for consistency

* rename unit test exchange receiver and minor updates

* linter issues fix

* fix deribit issue and minor style update

* fix test issues caused by receiver change

* raname local variables exchange declaration variables

* update templates comments

* update templates and related comments

* renamed ex to e

* update template comments

* toggle WS to false to improve coverage

* template comments update

* added test coverage to Ws enabled and minor changes

---------

Co-authored-by: Samuel Reid <43227667+cranktakular@users.noreply.github.com>
2025-07-17 10:46:36 +10:00

217 lines
7.3 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/encoding/json"
"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
}
pingMessage, err := json.Marshal(WsInput{
ID: conn.GenerateMessageID(false),
Time: time.Now().Unix(), // TODO: Func for dynamic time as this will be the same time for every ping message.
Channel: futuresPingChannel,
})
if err != nil {
return err
}
conn.SetupPingHandler(websocketRateLimitNotNeededEPL, websocket.PingHandler{
Websocket: true,
Delay: time.Second * 5,
MessageType: gws.PingMessage,
Message: pingMessage,
})
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, conn websocket.Connection, 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: conn.GenerateMessageID(false),
Event: event,
Channel: channelsToSubscribe[i].Channel,
Payload: params,
Auth: auth,
Time: timestamp.Unix(),
})
}
return outbound, nil
}