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>
This commit is contained in:
Samuael A.
2025-07-17 03:46:36 +03:00
committed by GitHub
parent 485397a0c7
commit 3f534a15f1
163 changed files with 20453 additions and 20313 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -13,33 +13,33 @@ import (
)
// WsInverseConnect connects to inverse websocket feed
func (by *Bybit) WsInverseConnect() error {
func (e *Exchange) WsInverseConnect() error {
ctx := context.TODO()
if !by.Websocket.IsEnabled() || !by.IsEnabled() || !by.IsAssetWebsocketSupported(asset.CoinMarginedFutures) {
if !e.Websocket.IsEnabled() || !e.IsEnabled() || !e.IsAssetWebsocketSupported(asset.CoinMarginedFutures) {
return websocket.ErrWebsocketNotEnabled
}
by.Websocket.Conn.SetURL(inversePublic)
e.Websocket.Conn.SetURL(inversePublic)
var dialer gws.Dialer
err := by.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
err := e.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
if err != nil {
return err
}
by.Websocket.Conn.SetupPingHandler(request.Unset, websocket.PingHandler{
e.Websocket.Conn.SetupPingHandler(request.Unset, websocket.PingHandler{
MessageType: gws.TextMessage,
Message: []byte(`{"op": "ping"}`),
Delay: bybitWebsocketTimer,
})
by.Websocket.Wg.Add(1)
go by.wsReadData(ctx, asset.CoinMarginedFutures, by.Websocket.Conn)
e.Websocket.Wg.Add(1)
go e.wsReadData(ctx, asset.CoinMarginedFutures, e.Websocket.Conn)
return nil
}
// GenerateInverseDefaultSubscriptions generates default subscription
func (by *Bybit) GenerateInverseDefaultSubscriptions() (subscription.List, error) {
func (e *Exchange) GenerateInverseDefaultSubscriptions() (subscription.List, error) {
var subscriptions subscription.List
channels := []string{chanOrderbook, chanPublicTrade, chanPublicTicker}
pairs, err := by.GetEnabledPairs(asset.CoinMarginedFutures)
pairs, err := e.GetEnabledPairs(asset.CoinMarginedFutures)
if err != nil {
return nil, err
}
@@ -57,26 +57,26 @@ func (by *Bybit) GenerateInverseDefaultSubscriptions() (subscription.List, error
}
// InverseSubscribe sends a subscription message to linear public channels.
func (by *Bybit) InverseSubscribe(channelSubscriptions subscription.List) error {
func (e *Exchange) InverseSubscribe(channelSubscriptions subscription.List) error {
ctx := context.TODO()
return by.handleInversePayloadSubscription(ctx, "subscribe", channelSubscriptions)
return e.handleInversePayloadSubscription(ctx, "subscribe", channelSubscriptions)
}
// InverseUnsubscribe sends an unsubscription messages through linear public channels.
func (by *Bybit) InverseUnsubscribe(channelSubscriptions subscription.List) error {
func (e *Exchange) InverseUnsubscribe(channelSubscriptions subscription.List) error {
ctx := context.TODO()
return by.handleInversePayloadSubscription(ctx, "unsubscribe", channelSubscriptions)
return e.handleInversePayloadSubscription(ctx, "unsubscribe", channelSubscriptions)
}
func (by *Bybit) handleInversePayloadSubscription(ctx context.Context, operation string, channelSubscriptions subscription.List) error {
payloads, err := by.handleSubscriptions(operation, channelSubscriptions)
func (e *Exchange) handleInversePayloadSubscription(ctx context.Context, operation string, channelSubscriptions subscription.List) error {
payloads, err := e.handleSubscriptions(operation, channelSubscriptions)
if err != nil {
return err
}
for a := range payloads {
// The options connection does not send the subscription request id back with the subscription notification payload
// therefore the code doesn't wait for the response to check whether the subscription is successful or not.
err = by.Websocket.Conn.SendJSONMessage(ctx, request.Unset, payloads[a])
err = e.Websocket.Conn.SendJSONMessage(ctx, request.Unset, payloads[a])
if err != nil {
return err
}

View File

@@ -13,47 +13,47 @@ import (
)
// WsLinearConnect connects to linear a websocket feed
func (by *Bybit) WsLinearConnect() error {
func (e *Exchange) WsLinearConnect() error {
ctx := context.TODO()
if !by.Websocket.IsEnabled() || !by.IsEnabled() || !by.IsAssetWebsocketSupported(asset.LinearContract) {
if !e.Websocket.IsEnabled() || !e.IsEnabled() || !e.IsAssetWebsocketSupported(asset.LinearContract) {
return websocket.ErrWebsocketNotEnabled
}
by.Websocket.Conn.SetURL(linearPublic)
e.Websocket.Conn.SetURL(linearPublic)
var dialer gws.Dialer
err := by.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
err := e.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
if err != nil {
return err
}
by.Websocket.Conn.SetupPingHandler(request.Unset, websocket.PingHandler{
e.Websocket.Conn.SetupPingHandler(request.Unset, websocket.PingHandler{
MessageType: gws.TextMessage,
Message: []byte(`{"op": "ping"}`),
Delay: bybitWebsocketTimer,
})
by.Websocket.Wg.Add(1)
go by.wsReadData(ctx, asset.LinearContract, by.Websocket.Conn)
if by.IsWebsocketAuthenticationSupported() {
err = by.WsAuth(ctx)
e.Websocket.Wg.Add(1)
go e.wsReadData(ctx, asset.LinearContract, e.Websocket.Conn)
if e.IsWebsocketAuthenticationSupported() {
err = e.WsAuth(ctx)
if err != nil {
by.Websocket.DataHandler <- err
by.Websocket.SetCanUseAuthenticatedEndpoints(false)
e.Websocket.DataHandler <- err
e.Websocket.SetCanUseAuthenticatedEndpoints(false)
}
}
return nil
}
// GenerateLinearDefaultSubscriptions generates default subscription
func (by *Bybit) GenerateLinearDefaultSubscriptions() (subscription.List, error) {
func (e *Exchange) GenerateLinearDefaultSubscriptions() (subscription.List, error) {
var subscriptions subscription.List
channels := []string{chanOrderbook, chanPublicTrade, chanPublicTicker}
pairs, err := by.GetEnabledPairs(asset.USDTMarginedFutures)
pairs, err := e.GetEnabledPairs(asset.USDTMarginedFutures)
if err != nil {
return nil, err
}
linearPairMap := map[asset.Item]currency.Pairs{
asset.USDTMarginedFutures: pairs,
}
usdcPairs, err := by.GetEnabledPairs(asset.USDCMarginedFutures)
usdcPairs, err := e.GetEnabledPairs(asset.USDCMarginedFutures)
if err != nil {
return nil, err
}
@@ -75,26 +75,26 @@ func (by *Bybit) GenerateLinearDefaultSubscriptions() (subscription.List, error)
}
// LinearSubscribe sends a subscription message to linear public channels.
func (by *Bybit) LinearSubscribe(channelSubscriptions subscription.List) error {
func (e *Exchange) LinearSubscribe(channelSubscriptions subscription.List) error {
ctx := context.TODO()
return by.handleLinearPayloadSubscription(ctx, "subscribe", channelSubscriptions)
return e.handleLinearPayloadSubscription(ctx, "subscribe", channelSubscriptions)
}
// LinearUnsubscribe sends an unsubscription messages through linear public channels.
func (by *Bybit) LinearUnsubscribe(channelSubscriptions subscription.List) error {
func (e *Exchange) LinearUnsubscribe(channelSubscriptions subscription.List) error {
ctx := context.TODO()
return by.handleLinearPayloadSubscription(ctx, "unsubscribe", channelSubscriptions)
return e.handleLinearPayloadSubscription(ctx, "unsubscribe", channelSubscriptions)
}
func (by *Bybit) handleLinearPayloadSubscription(ctx context.Context, operation string, channelSubscriptions subscription.List) error {
payloads, err := by.handleSubscriptions(operation, channelSubscriptions)
func (e *Exchange) handleLinearPayloadSubscription(ctx context.Context, operation string, channelSubscriptions subscription.List) error {
payloads, err := e.handleSubscriptions(operation, channelSubscriptions)
if err != nil {
return err
}
for a := range payloads {
// The options connection does not send the subscription request id back with the subscription notification payload
// therefore the code doesn't wait for the response to check whether the subscription is successful or not.
err = by.Websocket.Conn.SendJSONMessage(ctx, request.Unset, payloads[a])
err = e.Websocket.Conn.SendJSONMessage(ctx, request.Unset, payloads[a])
if err != nil {
return err
}

View File

@@ -18,21 +18,21 @@ import (
var mockTests = false
func TestMain(m *testing.M) {
b = new(Bybit)
if err := testexch.Setup(b); err != nil {
e = new(Exchange)
if err := testexch.Setup(e); err != nil {
log.Fatalf("Bybit Setup error: %s", err)
}
if apiKey != "" && apiSecret != "" {
b.API.AuthenticatedSupport = true
b.API.AuthenticatedWebsocketSupport = true
b.SetCredentials(apiKey, apiSecret, "", "", "", "")
b.Websocket.SetCanUseAuthenticatedEndpoints(true)
e.API.AuthenticatedSupport = true
e.API.AuthenticatedWebsocketSupport = true
e.SetCredentials(apiKey, apiSecret, "", "", "", "")
e.Websocket.SetCanUseAuthenticatedEndpoints(true)
}
if b.API.AuthenticatedSupport {
if _, err := b.FetchAccountType(context.Background()); err != nil {
log.Printf("%s unable to FetchAccountType: %v", b.Name, err)
if e.API.AuthenticatedSupport {
if _, err := e.FetchAccountType(context.Background()); err != nil {
log.Printf("%s unable to FetchAccountType: %v", e.Name, err)
}
}
@@ -48,14 +48,14 @@ func instantiateTradablePairs() {
}
}
err := b.UpdateTradablePairs(context.Background(), true)
err := e.UpdateTradablePairs(context.Background(), true)
handleError("unable to UpdateTradablePairs", err)
setTradablePair := func(assetType asset.Item, p *currency.Pair) {
tradables, err := b.GetEnabledPairs(assetType)
tradables, err := e.GetEnabledPairs(assetType)
handleError("unable to GetEnabledPairs", err)
format, err := b.GetPairFormat(assetType, true)
format, err := e.GetPairFormat(assetType, true)
handleError("unable to GetPairFormat", err)
*p = tradables[0].Format(format)

View File

@@ -18,25 +18,25 @@ import (
var mockTests = true
func TestMain(m *testing.M) {
b = new(Bybit)
if err := testexch.Setup(b); err != nil {
e = new(Exchange)
if err := testexch.Setup(e); err != nil {
log.Fatalf("Bybit Setup error: %s", err)
}
b.SetCredentials("mock", "tester", "", "", "", "") // Hack for UpdateAccountInfo test
e.SetCredentials("mock", "tester", "", "", "", "") // Hack for UpdateAccountInfo test
if err := testexch.MockHTTPInstance(b); err != nil {
if err := testexch.MockHTTPInstance(e); err != nil {
log.Fatalf("Bybit MockHTTPInstance error: %s", err)
}
if err := b.UpdateTradablePairs(context.Background(), true); err != nil {
if err := e.UpdateTradablePairs(context.Background(), true); err != nil {
log.Fatalf("Bybit unable to UpdateTradablePairs: %s", err)
}
setEnabledPair := func(assetType asset.Item, pair currency.Pair) {
okay, err := b.IsPairEnabled(pair, assetType)
okay, err := e.IsPairEnabled(pair, assetType)
if !okay || err != nil {
err = b.CurrencyPairs.EnablePair(assetType, pair)
err = e.CurrencyPairs.EnablePair(assetType, pair)
if err != nil {
log.Fatal(err)
}

View File

@@ -15,38 +15,38 @@ import (
)
// WsOptionsConnect connects to options a websocket feed
func (by *Bybit) WsOptionsConnect() error {
func (e *Exchange) WsOptionsConnect() error {
ctx := context.TODO()
if !by.Websocket.IsEnabled() || !by.IsEnabled() || !by.IsAssetWebsocketSupported(asset.Options) {
if !e.Websocket.IsEnabled() || !e.IsEnabled() || !e.IsAssetWebsocketSupported(asset.Options) {
return websocket.ErrWebsocketNotEnabled
}
by.Websocket.Conn.SetURL(optionPublic)
e.Websocket.Conn.SetURL(optionPublic)
var dialer gws.Dialer
err := by.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
err := e.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
if err != nil {
return err
}
pingMessage := PingMessage{Operation: "ping", RequestID: strconv.FormatInt(by.Websocket.Conn.GenerateMessageID(false), 10)}
pingMessage := PingMessage{Operation: "ping", RequestID: strconv.FormatInt(e.Websocket.Conn.GenerateMessageID(false), 10)}
pingData, err := json.Marshal(pingMessage)
if err != nil {
return err
}
by.Websocket.Conn.SetupPingHandler(request.Unset, websocket.PingHandler{
e.Websocket.Conn.SetupPingHandler(request.Unset, websocket.PingHandler{
MessageType: gws.TextMessage,
Message: pingData,
Delay: bybitWebsocketTimer,
})
by.Websocket.Wg.Add(1)
go by.wsReadData(ctx, asset.Options, by.Websocket.Conn)
e.Websocket.Wg.Add(1)
go e.wsReadData(ctx, asset.Options, e.Websocket.Conn)
return nil
}
// GenerateOptionsDefaultSubscriptions generates default subscription
func (by *Bybit) GenerateOptionsDefaultSubscriptions() (subscription.List, error) {
func (e *Exchange) GenerateOptionsDefaultSubscriptions() (subscription.List, error) {
var subscriptions subscription.List
channels := []string{chanOrderbook, chanPublicTrade, chanPublicTicker}
pairs, err := by.GetEnabledPairs(asset.Options)
pairs, err := e.GetEnabledPairs(asset.Options)
if err != nil {
return nil, err
}
@@ -64,26 +64,26 @@ func (by *Bybit) GenerateOptionsDefaultSubscriptions() (subscription.List, error
}
// OptionSubscribe sends a subscription message to options public channels.
func (by *Bybit) OptionSubscribe(channelSubscriptions subscription.List) error {
func (e *Exchange) OptionSubscribe(channelSubscriptions subscription.List) error {
ctx := context.TODO()
return by.handleOptionsPayloadSubscription(ctx, "subscribe", channelSubscriptions)
return e.handleOptionsPayloadSubscription(ctx, "subscribe", channelSubscriptions)
}
// OptionUnsubscribe sends an unsubscription messages through options public channels.
func (by *Bybit) OptionUnsubscribe(channelSubscriptions subscription.List) error {
func (e *Exchange) OptionUnsubscribe(channelSubscriptions subscription.List) error {
ctx := context.TODO()
return by.handleOptionsPayloadSubscription(ctx, "unsubscribe", channelSubscriptions)
return e.handleOptionsPayloadSubscription(ctx, "unsubscribe", channelSubscriptions)
}
func (by *Bybit) handleOptionsPayloadSubscription(ctx context.Context, operation string, channelSubscriptions subscription.List) error {
payloads, err := by.handleSubscriptions(operation, channelSubscriptions)
func (e *Exchange) handleOptionsPayloadSubscription(ctx context.Context, operation string, channelSubscriptions subscription.List) error {
payloads, err := e.handleSubscriptions(operation, channelSubscriptions)
if err != nil {
return err
}
for a := range payloads {
// The options connection does not send the subscription request id back with the subscription notification payload
// therefore the code doesn't wait for the response to check whether the subscription is successful or not.
err = by.Websocket.Conn.SendJSONMessage(ctx, request.Unset, payloads[a])
err = e.Websocket.Conn.SendJSONMessage(ctx, request.Unset, payloads[a])
if err != nil {
return err
}

File diff suppressed because it is too large Load Diff

View File

@@ -79,54 +79,54 @@ var subscriptionNames = map[string]string{
}
// WsConnect connects to a websocket feed
func (by *Bybit) WsConnect() error {
func (e *Exchange) WsConnect() error {
ctx := context.TODO()
if !by.Websocket.IsEnabled() || !by.IsEnabled() || !by.IsAssetWebsocketSupported(asset.Spot) {
if !e.Websocket.IsEnabled() || !e.IsEnabled() || !e.IsAssetWebsocketSupported(asset.Spot) {
return websocket.ErrWebsocketNotEnabled
}
var dialer gws.Dialer
err := by.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
err := e.Websocket.Conn.Dial(ctx, &dialer, http.Header{})
if err != nil {
return err
}
by.Websocket.Conn.SetupPingHandler(request.Unset, websocket.PingHandler{
e.Websocket.Conn.SetupPingHandler(request.Unset, websocket.PingHandler{
MessageType: gws.TextMessage,
Message: []byte(`{"op": "ping"}`),
Delay: bybitWebsocketTimer,
})
by.Websocket.Wg.Add(1)
go by.wsReadData(ctx, asset.Spot, by.Websocket.Conn)
if by.Websocket.CanUseAuthenticatedEndpoints() {
err = by.WsAuth(ctx)
e.Websocket.Wg.Add(1)
go e.wsReadData(ctx, asset.Spot, e.Websocket.Conn)
if e.Websocket.CanUseAuthenticatedEndpoints() {
err = e.WsAuth(ctx)
if err != nil {
by.Websocket.DataHandler <- err
by.Websocket.SetCanUseAuthenticatedEndpoints(false)
e.Websocket.DataHandler <- err
e.Websocket.SetCanUseAuthenticatedEndpoints(false)
}
}
return nil
}
// WsAuth sends an authentication message to receive auth data
func (by *Bybit) WsAuth(ctx context.Context) error {
creds, err := by.GetCredentials(ctx)
func (e *Exchange) WsAuth(ctx context.Context) error {
creds, err := e.GetCredentials(ctx)
if err != nil {
return err
}
var dialer gws.Dialer
if err := by.Websocket.AuthConn.Dial(ctx, &dialer, http.Header{}); err != nil {
if err := e.Websocket.AuthConn.Dial(ctx, &dialer, http.Header{}); err != nil {
return err
}
by.Websocket.AuthConn.SetupPingHandler(request.Unset, websocket.PingHandler{
e.Websocket.AuthConn.SetupPingHandler(request.Unset, websocket.PingHandler{
MessageType: gws.TextMessage,
Message: []byte(`{"op":"ping"}`),
Delay: bybitWebsocketTimer,
})
by.Websocket.Wg.Add(1)
go by.wsReadData(ctx, asset.Spot, by.Websocket.AuthConn)
e.Websocket.Wg.Add(1)
go e.wsReadData(ctx, asset.Spot, e.Websocket.AuthConn)
intNonce := time.Now().Add(time.Hour * 6).UnixMilli()
strNonce := strconv.FormatInt(intNonce, 10)
@@ -140,11 +140,11 @@ func (by *Bybit) WsAuth(ctx context.Context) error {
}
sign := hex.EncodeToString(hmac)
req := Authenticate{
RequestID: strconv.FormatInt(by.Websocket.AuthConn.GenerateMessageID(false), 10),
RequestID: strconv.FormatInt(e.Websocket.AuthConn.GenerateMessageID(false), 10),
Operation: "auth",
Args: []any{creds.Key, intNonce, sign},
}
resp, err := by.Websocket.AuthConn.SendMessageReturnResponse(ctx, request.Unset, req.RequestID, req)
resp, err := e.Websocket.AuthConn.SendMessageReturnResponse(ctx, request.Unset, req.RequestID, req)
if err != nil {
return err
}
@@ -160,13 +160,13 @@ func (by *Bybit) WsAuth(ctx context.Context) error {
}
// Subscribe sends a websocket message to receive data from the channel
func (by *Bybit) Subscribe(channelsToSubscribe subscription.List) error {
func (e *Exchange) Subscribe(channelsToSubscribe subscription.List) error {
ctx := context.TODO()
return by.handleSpotSubscription(ctx, "subscribe", channelsToSubscribe)
return e.handleSpotSubscription(ctx, "subscribe", channelsToSubscribe)
}
func (by *Bybit) handleSubscriptions(operation string, subs subscription.List) (args []SubscriptionArgument, err error) {
subs, err = subs.ExpandTemplates(by)
func (e *Exchange) handleSubscriptions(operation string, subs subscription.List) (args []SubscriptionArgument, err error) {
subs, err = subs.ExpandTemplates(e)
if err != nil {
return
}
@@ -176,7 +176,7 @@ func (by *Bybit) handleSubscriptions(operation string, subs subscription.List) (
args = append(args, SubscriptionArgument{
auth: b[0].Authenticated,
Operation: operation,
RequestID: strconv.FormatInt(by.Websocket.Conn.GenerateMessageID(false), 10),
RequestID: strconv.FormatInt(e.Websocket.Conn.GenerateMessageID(false), 10),
Arguments: b.QualifiedChannels(),
associatedSubs: b,
})
@@ -187,25 +187,25 @@ func (by *Bybit) handleSubscriptions(operation string, subs subscription.List) (
}
// Unsubscribe sends a websocket message to stop receiving data from the channel
func (by *Bybit) Unsubscribe(channelsToUnsubscribe subscription.List) error {
func (e *Exchange) Unsubscribe(channelsToUnsubscribe subscription.List) error {
ctx := context.TODO()
return by.handleSpotSubscription(ctx, "unsubscribe", channelsToUnsubscribe)
return e.handleSpotSubscription(ctx, "unsubscribe", channelsToUnsubscribe)
}
func (by *Bybit) handleSpotSubscription(ctx context.Context, operation string, channelsToSubscribe subscription.List) error {
payloads, err := by.handleSubscriptions(operation, channelsToSubscribe)
func (e *Exchange) handleSpotSubscription(ctx context.Context, operation string, channelsToSubscribe subscription.List) error {
payloads, err := e.handleSubscriptions(operation, channelsToSubscribe)
if err != nil {
return err
}
for a := range payloads {
var response []byte
if payloads[a].auth {
response, err = by.Websocket.AuthConn.SendMessageReturnResponse(ctx, request.Unset, payloads[a].RequestID, payloads[a])
response, err = e.Websocket.AuthConn.SendMessageReturnResponse(ctx, request.Unset, payloads[a].RequestID, payloads[a])
if err != nil {
return err
}
} else {
response, err = by.Websocket.Conn.SendMessageReturnResponse(ctx, request.Unset, payloads[a].RequestID, payloads[a])
response, err = e.Websocket.Conn.SendMessageReturnResponse(ctx, request.Unset, payloads[a].RequestID, payloads[a])
if err != nil {
return err
}
@@ -221,15 +221,15 @@ func (by *Bybit) handleSpotSubscription(ctx context.Context, operation string, c
var conn websocket.Connection
if payloads[a].auth {
conn = by.Websocket.AuthConn
conn = e.Websocket.AuthConn
} else {
conn = by.Websocket.Conn
conn = e.Websocket.Conn
}
if operation == "unsubscribe" {
err = by.Websocket.RemoveSubscriptions(conn, payloads[a].associatedSubs...)
err = e.Websocket.RemoveSubscriptions(conn, payloads[a].associatedSubs...)
} else {
err = by.Websocket.AddSubscriptions(conn, payloads[a].associatedSubs...)
err = e.Websocket.AddSubscriptions(conn, payloads[a].associatedSubs...)
}
if err != nil {
return err
@@ -239,12 +239,12 @@ func (by *Bybit) handleSpotSubscription(ctx context.Context, operation string, c
}
// generateSubscriptions generates default subscription
func (by *Bybit) generateSubscriptions() (subscription.List, error) {
return by.Features.Subscriptions.ExpandTemplates(by)
func (e *Exchange) generateSubscriptions() (subscription.List, error) {
return e.Features.Subscriptions.ExpandTemplates(e)
}
// GetSubscriptionTemplate returns a subscription channel template
func (by *Bybit) GetSubscriptionTemplate(_ *subscription.Subscription) (*template.Template, error) {
func (e *Exchange) GetSubscriptionTemplate(_ *subscription.Subscription) (*template.Template, error) {
return template.New("master.tmpl").Funcs(template.FuncMap{
"channelName": channelName,
"isSymbolChannel": isSymbolChannel,
@@ -255,26 +255,26 @@ func (by *Bybit) GetSubscriptionTemplate(_ *subscription.Subscription) (*templat
}
// wsReadData receives and passes on websocket messages for processing
func (by *Bybit) wsReadData(ctx context.Context, assetType asset.Item, ws websocket.Connection) {
defer by.Websocket.Wg.Done()
func (e *Exchange) wsReadData(ctx context.Context, assetType asset.Item, ws websocket.Connection) {
defer e.Websocket.Wg.Done()
for {
select {
case <-by.Websocket.ShutdownC:
case <-e.Websocket.ShutdownC:
return
default:
resp := ws.ReadMessage()
if resp.Raw == nil {
return
}
err := by.wsHandleData(ctx, assetType, resp.Raw)
err := e.wsHandleData(ctx, assetType, resp.Raw)
if err != nil {
by.Websocket.DataHandler <- err
e.Websocket.DataHandler <- err
}
}
}
}
func (by *Bybit) wsHandleData(ctx context.Context, assetType asset.Item, respRaw []byte) error {
func (e *Exchange) wsHandleData(ctx context.Context, assetType asset.Item, respRaw []byte) error {
var result WebsocketResponse
err := json.Unmarshal(respRaw, &result)
if err != nil {
@@ -284,13 +284,13 @@ func (by *Bybit) wsHandleData(ctx context.Context, assetType asset.Item, respRaw
switch result.Operation {
case "subscribe", "unsubscribe", "auth":
if result.RequestID != "" {
if !by.Websocket.Match.IncomingWithData(result.RequestID, respRaw) {
if !e.Websocket.Match.IncomingWithData(result.RequestID, respRaw) {
return fmt.Errorf("could not match subscription with id %s data %s", result.RequestID, respRaw)
}
}
case "ping", "pong":
default:
by.Websocket.DataHandler <- websocket.UnhandledMessageWarning{
e.Websocket.DataHandler <- websocket.UnhandledMessageWarning{
Message: string(respRaw),
}
return nil
@@ -303,54 +303,54 @@ func (by *Bybit) wsHandleData(ctx context.Context, assetType asset.Item, respRaw
}
switch topicSplit[0] {
case chanOrderbook:
return by.wsProcessOrderbook(assetType, &result)
return e.wsProcessOrderbook(assetType, &result)
case chanPublicTrade:
return by.wsProcessPublicTrade(assetType, &result)
return e.wsProcessPublicTrade(assetType, &result)
case chanPublicTicker:
return by.wsProcessPublicTicker(assetType, &result)
return e.wsProcessPublicTicker(assetType, &result)
case chanKline:
return by.wsProcessKline(assetType, &result, topicSplit)
return e.wsProcessKline(assetType, &result, topicSplit)
case chanLiquidation:
return by.wsProcessLiquidation(&result)
return e.wsProcessLiquidation(&result)
case chanLeverageTokenKline:
return by.wsProcessLeverageTokenKline(assetType, &result, topicSplit)
return e.wsProcessLeverageTokenKline(assetType, &result, topicSplit)
case chanLeverageTokenTicker:
return by.wsProcessLeverageTokenTicker(assetType, &result)
return e.wsProcessLeverageTokenTicker(assetType, &result)
case chanLeverageTokenNav:
return by.wsLeverageTokenNav(&result)
return e.wsLeverageTokenNav(&result)
case chanPositions:
return by.wsProcessPosition(&result)
return e.wsProcessPosition(&result)
case chanExecution:
return by.wsProcessExecution(asset.Spot, &result)
return e.wsProcessExecution(asset.Spot, &result)
case chanOrder:
return by.wsProcessOrder(asset.Spot, &result)
return e.wsProcessOrder(asset.Spot, &result)
case chanWallet:
return by.wsProcessWalletPushData(ctx, asset.Spot, respRaw)
return e.wsProcessWalletPushData(ctx, asset.Spot, respRaw)
case chanGreeks:
return by.wsProcessGreeks(respRaw)
return e.wsProcessGreeks(respRaw)
case chanDCP:
return nil
}
return fmt.Errorf("unhandled stream data %s", string(respRaw))
}
func (by *Bybit) wsProcessGreeks(resp []byte) error {
func (e *Exchange) wsProcessGreeks(resp []byte) error {
var result GreeksResponse
err := json.Unmarshal(resp, &result)
if err != nil {
return err
}
by.Websocket.DataHandler <- &result
e.Websocket.DataHandler <- &result
return nil
}
func (by *Bybit) wsProcessWalletPushData(ctx context.Context, assetType asset.Item, resp []byte) error {
func (e *Exchange) wsProcessWalletPushData(ctx context.Context, assetType asset.Item, resp []byte) error {
var result WebsocketWallet
err := json.Unmarshal(resp, &result)
if err != nil {
return err
}
creds, err := by.GetCredentials(ctx)
creds, err := e.GetCredentials(ctx)
if err != nil {
return err
}
@@ -368,12 +368,12 @@ func (by *Bybit) wsProcessWalletPushData(ctx context.Context, assetType asset.It
})
}
}
by.Websocket.DataHandler <- changes
return account.ProcessChange(by.Name, changes, creds)
e.Websocket.DataHandler <- changes
return account.ProcessChange(e.Name, changes, creds)
}
// wsProcessOrder the order stream to see changes to your orders in real-time.
func (by *Bybit) wsProcessOrder(assetType asset.Item, resp *WebsocketResponse) error {
func (e *Exchange) wsProcessOrder(assetType asset.Item, resp *WebsocketResponse) error {
var result WsOrders
err := json.Unmarshal(resp.Data, &result)
if err != nil {
@@ -381,7 +381,7 @@ func (by *Bybit) wsProcessOrder(assetType asset.Item, resp *WebsocketResponse) e
}
execution := make([]order.Detail, len(result))
for x := range result {
cp, err := by.MatchSymbolWithAvailablePairs(result[x].Symbol, assetType, hasPotentialDelimiter(assetType))
cp, err := e.MatchSymbolWithAvailablePairs(result[x].Symbol, assetType, hasPotentialDelimiter(assetType))
if err != nil {
return err
}
@@ -395,7 +395,7 @@ func (by *Bybit) wsProcessOrder(assetType asset.Item, resp *WebsocketResponse) e
}
execution[x] = order.Detail{
Amount: result[x].Qty.Float64(),
Exchange: by.Name,
Exchange: e.Name,
OrderID: result[x].OrderID,
ClientOrderID: result[x].OrderLinkID,
Side: side,
@@ -410,11 +410,11 @@ func (by *Bybit) wsProcessOrder(assetType asset.Item, resp *WebsocketResponse) e
LastUpdated: result[x].UpdatedTime.Time(),
}
}
by.Websocket.DataHandler <- execution
e.Websocket.DataHandler <- execution
return nil
}
func (by *Bybit) wsProcessExecution(assetType asset.Item, resp *WebsocketResponse) error {
func (e *Exchange) wsProcessExecution(assetType asset.Item, resp *WebsocketResponse) error {
var result WsExecutions
err := json.Unmarshal(resp.Data, &result)
if err != nil {
@@ -422,7 +422,7 @@ func (by *Bybit) wsProcessExecution(assetType asset.Item, resp *WebsocketRespons
}
executions := make([]fill.Data, len(result))
for x := range result {
cp, err := by.MatchSymbolWithAvailablePairs(result[x].Symbol, assetType, hasPotentialDelimiter(assetType))
cp, err := e.MatchSymbolWithAvailablePairs(result[x].Symbol, assetType, hasPotentialDelimiter(assetType))
if err != nil {
return err
}
@@ -433,7 +433,7 @@ func (by *Bybit) wsProcessExecution(assetType asset.Item, resp *WebsocketRespons
executions[x] = fill.Data{
ID: result[x].ExecID,
Timestamp: result[x].ExecTime.Time(),
Exchange: by.Name,
Exchange: e.Name,
AssetType: assetType,
CurrencyPair: cp,
Side: side,
@@ -443,59 +443,59 @@ func (by *Bybit) wsProcessExecution(assetType asset.Item, resp *WebsocketRespons
Amount: result[x].ExecQty.Float64(),
}
}
by.Websocket.DataHandler <- executions
e.Websocket.DataHandler <- executions
return nil
}
func (by *Bybit) wsProcessPosition(resp *WebsocketResponse) error {
func (e *Exchange) wsProcessPosition(resp *WebsocketResponse) error {
var result WsPositions
err := json.Unmarshal(resp.Data, &result)
if err != nil {
return err
}
by.Websocket.DataHandler <- result
e.Websocket.DataHandler <- result
return nil
}
func (by *Bybit) wsLeverageTokenNav(resp *WebsocketResponse) error {
func (e *Exchange) wsLeverageTokenNav(resp *WebsocketResponse) error {
var result LTNav
err := json.Unmarshal(resp.Data, &result)
if err != nil {
return err
}
by.Websocket.DataHandler <- result
e.Websocket.DataHandler <- result
return nil
}
func (by *Bybit) wsProcessLeverageTokenTicker(assetType asset.Item, resp *WebsocketResponse) error {
func (e *Exchange) wsProcessLeverageTokenTicker(assetType asset.Item, resp *WebsocketResponse) error {
var result TickerItem
err := json.Unmarshal(resp.Data, &result)
if err != nil {
return err
}
cp, err := by.MatchSymbolWithAvailablePairs(result.Symbol, assetType, hasPotentialDelimiter(assetType))
cp, err := e.MatchSymbolWithAvailablePairs(result.Symbol, assetType, hasPotentialDelimiter(assetType))
if err != nil {
return err
}
by.Websocket.DataHandler <- &ticker.Price{
e.Websocket.DataHandler <- &ticker.Price{
Last: result.LastPrice.Float64(),
High: result.HighPrice24H.Float64(),
Low: result.LowPrice24H.Float64(),
Pair: cp,
ExchangeName: by.Name,
ExchangeName: e.Name,
AssetType: assetType,
LastUpdated: resp.PushTimestamp.Time(),
}
return nil
}
func (by *Bybit) wsProcessLeverageTokenKline(assetType asset.Item, resp *WebsocketResponse, topicSplit []string) error {
func (e *Exchange) wsProcessLeverageTokenKline(assetType asset.Item, resp *WebsocketResponse, topicSplit []string) error {
var result LTKlines
err := json.Unmarshal(resp.Data, &result)
if err != nil {
return err
}
cp, err := by.MatchSymbolWithAvailablePairs(topicSplit[2], assetType, hasPotentialDelimiter(assetType))
cp, err := e.MatchSymbolWithAvailablePairs(topicSplit[2], assetType, hasPotentialDelimiter(assetType))
if err != nil {
return err
}
@@ -509,7 +509,7 @@ func (by *Bybit) wsProcessLeverageTokenKline(assetType asset.Item, resp *Websock
Timestamp: result[x].Timestamp.Time(),
Pair: cp,
AssetType: assetType,
Exchange: by.Name,
Exchange: e.Name,
StartTime: result[x].Start.Time(),
CloseTime: result[x].End.Time(),
Interval: interval.String(),
@@ -519,27 +519,27 @@ func (by *Bybit) wsProcessLeverageTokenKline(assetType asset.Item, resp *Websock
LowPrice: result[x].Low.Float64(),
}
}
by.Websocket.DataHandler <- result
e.Websocket.DataHandler <- result
return nil
}
func (by *Bybit) wsProcessLiquidation(resp *WebsocketResponse) error {
func (e *Exchange) wsProcessLiquidation(resp *WebsocketResponse) error {
var result WebsocketLiquidation
err := json.Unmarshal(resp.Data, &result)
if err != nil {
return err
}
by.Websocket.DataHandler <- result
e.Websocket.DataHandler <- result
return nil
}
func (by *Bybit) wsProcessKline(assetType asset.Item, resp *WebsocketResponse, topicSplit []string) error {
func (e *Exchange) wsProcessKline(assetType asset.Item, resp *WebsocketResponse, topicSplit []string) error {
var result WsKlines
err := json.Unmarshal(resp.Data, &result)
if err != nil {
return err
}
cp, err := by.MatchSymbolWithAvailablePairs(topicSplit[2], assetType, hasPotentialDelimiter(assetType))
cp, err := e.MatchSymbolWithAvailablePairs(topicSplit[2], assetType, hasPotentialDelimiter(assetType))
if err != nil {
return err
}
@@ -553,7 +553,7 @@ func (by *Bybit) wsProcessKline(assetType asset.Item, resp *WebsocketResponse, t
Timestamp: result[x].Timestamp.Time(),
Pair: cp,
AssetType: assetType,
Exchange: by.Name,
Exchange: e.Name,
StartTime: result[x].Start.Time(),
CloseTime: result[x].End.Time(),
Interval: interval.String(),
@@ -564,21 +564,21 @@ func (by *Bybit) wsProcessKline(assetType asset.Item, resp *WebsocketResponse, t
Volume: result[x].Volume.Float64(),
}
}
by.Websocket.DataHandler <- spotCandlesticks
e.Websocket.DataHandler <- spotCandlesticks
return nil
}
func (by *Bybit) wsProcessPublicTicker(assetType asset.Item, resp *WebsocketResponse) error {
func (e *Exchange) wsProcessPublicTicker(assetType asset.Item, resp *WebsocketResponse) error {
tickResp := new(TickerItem)
if err := json.Unmarshal(resp.Data, tickResp); err != nil {
return err
}
p, err := by.MatchSymbolWithAvailablePairs(tickResp.Symbol, assetType, hasPotentialDelimiter(assetType))
p, err := e.MatchSymbolWithAvailablePairs(tickResp.Symbol, assetType, hasPotentialDelimiter(assetType))
if err != nil {
return err
}
pFmt, err := by.GetPairFormat(assetType, false)
pFmt, err := e.GetPairFormat(assetType, false)
if err != nil {
return err
}
@@ -588,12 +588,12 @@ func (by *Bybit) wsProcessPublicTicker(assetType asset.Item, resp *WebsocketResp
if resp.Type == "snapshot" {
tick = &ticker.Price{
Pair: p,
ExchangeName: by.Name,
ExchangeName: e.Name,
AssetType: assetType,
}
} else {
// ticker updates may be partial, so we need to update the current ticker
tick, err = ticker.GetTicker(by.Name, p, assetType)
tick, err = ticker.GetTicker(e.Name, p, assetType)
if err != nil {
return err
}
@@ -603,7 +603,7 @@ func (by *Bybit) wsProcessPublicTicker(assetType asset.Item, resp *WebsocketResp
tick.LastUpdated = resp.PushTimestamp.Time()
if err = ticker.ProcessTicker(tick); err == nil {
by.Websocket.DataHandler <- tick
e.Websocket.DataHandler <- tick
}
return err
@@ -667,7 +667,7 @@ func updateTicker(tick *ticker.Price, resp *TickerItem) {
}
}
func (by *Bybit) wsProcessPublicTrade(assetType asset.Item, resp *WebsocketResponse) error {
func (e *Exchange) wsProcessPublicTrade(assetType asset.Item, resp *WebsocketResponse) error {
var result WebsocketPublicTrades
err := json.Unmarshal(resp.Data, &result)
if err != nil {
@@ -675,7 +675,7 @@ func (by *Bybit) wsProcessPublicTrade(assetType asset.Item, resp *WebsocketRespo
}
tradeDatas := make([]trade.Data, len(result))
for x := range result {
cp, err := by.MatchSymbolWithAvailablePairs(result[x].Symbol, assetType, hasPotentialDelimiter(assetType))
cp, err := e.MatchSymbolWithAvailablePairs(result[x].Symbol, assetType, hasPotentialDelimiter(assetType))
if err != nil {
return err
}
@@ -687,7 +687,7 @@ func (by *Bybit) wsProcessPublicTrade(assetType asset.Item, resp *WebsocketRespo
Timestamp: result[x].OrderFillTimestamp.Time(),
CurrencyPair: cp,
AssetType: assetType,
Exchange: by.Name,
Exchange: e.Name,
Price: result[x].Price.Float64(),
Amount: result[x].Size.Float64(),
Side: side,
@@ -697,7 +697,7 @@ func (by *Bybit) wsProcessPublicTrade(assetType asset.Item, resp *WebsocketRespo
return trade.AddTradesToBuffer(tradeDatas...)
}
func (by *Bybit) wsProcessOrderbook(assetType asset.Item, resp *WebsocketResponse) error {
func (e *Exchange) wsProcessOrderbook(assetType asset.Item, resp *WebsocketResponse) error {
var result WsOrderbookDetail
if err := json.Unmarshal(resp.Data, &result); err != nil {
return err
@@ -706,7 +706,7 @@ func (by *Bybit) wsProcessOrderbook(assetType asset.Item, resp *WebsocketRespons
return nil
}
cp, err := by.MatchSymbolWithAvailablePairs(result.Symbol, assetType, hasPotentialDelimiter(assetType))
cp, err := e.MatchSymbolWithAvailablePairs(result.Symbol, assetType, hasPotentialDelimiter(assetType))
if err != nil {
return err
}
@@ -722,9 +722,9 @@ func (by *Bybit) wsProcessOrderbook(assetType asset.Item, resp *WebsocketRespons
}
if resp.Type == "snapshot" {
return by.Websocket.Orderbook.LoadSnapshot(&orderbook.Book{
return e.Websocket.Orderbook.LoadSnapshot(&orderbook.Book{
Pair: cp,
Exchange: by.Name,
Exchange: e.Name,
Asset: assetType,
LastUpdated: resp.OrderbookLastUpdated.Time(),
LastUpdateID: result.UpdateID,
@@ -733,7 +733,7 @@ func (by *Bybit) wsProcessOrderbook(assetType asset.Item, resp *WebsocketRespons
Bids: bids,
})
}
return by.Websocket.Orderbook.Update(&orderbook.Update{
return e.Websocket.Orderbook.Update(&orderbook.Update{
Pair: cp,
Asks: asks,
Bids: bids,

File diff suppressed because it is too large Load Diff