Skip to content

Conversation

shazbert
Copy link
Collaborator

PR Description

  • Implement trading functions through specific dedicated websocket connection
  • Expands IBotExchange wrapper functions to include websocket order operations
  • Adds validation methods on PlaceOrderParams, AmendOrderParams, CancelOrderParams to be used across websocket and rest.

#1670 Requires dependency

Fixes # (issue)

Type of change

Please delete options that are not relevant and add an x in [] as item is complete.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How has this been tested

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration and
also consider improving test coverage whilst working on a certain feature or package.

  • go test ./... -race
  • golangci-lint run
  • Test X

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation and regenerated documentation via the documentation tool
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally and on Github Actions with my changes
  • Any dependent changes have been merged and published in downstream modules

@shazbert shazbert added review me This pull request is ready for review and removed nomerge requires dependency This pull request is dependent on another, so it can't be merged until the dependent one is merged labels Aug 8, 2025
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements websocket trading functionality for the Bybit exchange, expanding the trading capabilities from REST-only to include real-time websocket operations. The implementation adds a dedicated websocket connection for outbound trading requests and expands the exchange wrapper to support websocket-based order operations.

Key changes:

  • Adds websocket trading operations (create, modify, cancel orders) through dedicated trade connection
  • Implements validation methods for order parameters to be shared across REST and websocket endpoints
  • Expands exchange interface with websocket order management methods

Reviewed Changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated no comments.

Show a summary per file
File Description
exchanges/request/limit.go Adds no-op rate limiter constant for websocket subscriptions
exchanges/interfaces.go Extends OrderManagement interface with websocket order operations
exchanges/exchange_types.go Adds WebsocketTrade URL type for dedicated trading connections
exchanges/exchange.go Implements base websocket order methods returning not supported errors
exchanges/bybit/bybit_wrapper.go Major refactoring to support websocket trading with shared validation logic
exchanges/bybit/bybit_websocket_requests.go New file implementing websocket trading request handlers
exchanges/bybit/ratelimit.go Adds websocket-specific rate limits and category mapping
exchanges/bybit/bybit_websocket.go Adds trade connection authentication and data handling
exchanges/bybit/bybit_types.go Adds validation methods and websocket response types
exchange/websocket/*.go Extends websocket framework to support subscription-optional connections
Comments suppressed due to low confidence (4)

exchanges/bybit/ratelimit.go:81

  • The comment references a function 'GetRateLimit' but the function was removed and replaced with a variable 'rateLimits'. The comment should be updated to reflect the current implementation or removed entirely.
var rateLimits = request.RateLimitDefinitions{

exchanges/bybit/ratelimit.go:82

  • The comment above this line references the removed 'GetRateLimit' function. This function signature line appears to be leftover from a refactoring and should be removed.
	defaultEPL:                               request.NewRateLimitWithWeight(time.Second*5 /* See: https://bybit-exchange.github.io/docs/v5/rate-limit */, 600, 1),

exchanges/bybit/ratelimit.go:83

  • This return statement appears to be leftover from the removed GetRateLimit function and should be removed since it's now replaced by the rateLimits variable.
	createOrderEPL:                           request.NewRateLimitWithWeight(time.Second, 10, 10),

exchanges/bybit/bybit_types.go:326

  • The field name 'WhetherToBorrow' is verbose and unclear. It has been replaced with 'EnableBorrow' which is more concise and descriptive. This line should be removed as it appears to be leftover from refactoring.
	EnableBorrow           bool          `json:"-"`                          // '0' for default spot, '1' for Margin trading.

Copy link
Collaborator

@cranktakular cranktakular left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All my suggestions are pretty minor, those aside I can tACK this.

I don't have the credentials to check authenticated stuff, and I haven't had time to check implementations against the documentation, yet.

Relevant functions missing seemingly-achievable coverage: PlaceOrderParams.Validate, PlaceOrderParams.LoadID, AmendOrderParams.Validate, AmendOrderParams.LoadID, AllZero, CancelOrderParams.Validate, CancelOrderParams.LoadID, WebsocketAuthenticatePrivateConnection, WebsocketAuthenticateTradeConnection, wsHandleTradeData, wsHandleAuthenticatedData, Setup, DeriveSubmitOrderArguments, DeriveAmendOrderArguments, DeriveCancelOrderArguments
I've tried my best to prune ones which were missing coverage due to my lack of credentials, but I may have missed some.

Also, don't forget the checklist in the initial post!!

@@ -480,3 +513,8 @@ func removeURLQueryString(url string) string {
func (c *connection) RequireMatchWithData(signature any, incoming []byte) error {
return c.Match.RequireMatchWithData(signature, incoming)
}

// IncomingWithData routes incoming data using the connection specific match system to the correct handler
func (c *connection) IncomingWithData(signature any, data []byte) bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the point of this function? Both this and the function it's calling are exported, no data's being added, it's just a slightly less verbose way to call it.

Is that worth it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's for fall back, exchanges send requestIDs or similar but we don't need to match back to a caller if not present and the data can be handled sent through the data handler. That is the only difference. Probably can be refactored but OOS.

@@ -343,6 +350,60 @@ type PlaceOrderParams struct {
SlLimitPrice float64 `json:"slLimitPrice,omitempty,string"`
}

// Validate checks the input parameters and returns an error if they are invalid.
func (p *PlaceOrderParams) Validate() error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like us to have some overarching idea on whether there should be functions in type files or not.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generally hug validation, marshal, unmarshal methods to types because I am trying to quickly implement, and its easier for me to reference and doesn't pollute the API.go file. If you want me to move it I can.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would be nice 🕊️

}

// WebsocketOrderDetails is the order details from the websocket response.
type WebsocketOrderDetails struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm similarly unsure about including multiple beefy type declarations in a non-types file.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can shove them in bybit_websocket_requests_types.go file?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds nice 🕊️

creds, err := e.GetCredentials(ctx)
// WebsocketAuthenticatePrivateConnection sends an authentication message to the private websocket for inbound account
// data
func (e *Exchange) WebsocketAuthenticatePrivateConnection(ctx context.Context, conn websocket.Connection) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function deserves a different name. Both WebsocketAuthenticatePrivateConnection and WebsocketAuthenticateTradeConnection are handling private data.

How about WebsocketAuthenticateReadConnection and WebsocketAuthenticateWriteConnection? Not great, but they're the best I can come up with.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am just mapping the functions with bybit's API terminology for connection name.

// Below provides a way of matching an order change to a websocket request. There is no batch support for this
// so the first element will be used to match the order link ID.
if id, err := jsonparser.GetString(respRaw, "data", "[0]", "orderLinkId"); err == nil {
if conn.IncomingWithData(id, respRaw) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems conceptually weird to send the entire respRaw through, if it's only being matched with the first element. Smells like there's a risk of additional data getting swept in there and matched incorrectly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it does it will error. Then we can fix it.

CumExecQty types.Number `json:"cumExecQty"`
CumExecValue types.Number `json:"cumExecValue"`
CumExecFee types.Number `json:"cumExecFee"`
ClosedPnl types.Number `json:"closedPnl"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GK has said acrnoyms and the like should all be in the same case. That hits a few variable names here, Pnl > PNL, Oco > OCO, Tpsl > TPSL, etc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think because we aren't consistent with this across the codebase unlike ID; this is unnecessary pedantry. We can get an AI to standardise this and add in a misc linter for it if anybody cares, but I don't.

Copy link
Collaborator

@gloriousCode gloriousCode Aug 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would have taken you less keyboard presses to change it than to reply 😭
It is a rule we try to adhere to, it would. be preferable

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shh you

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥃 @cranktakular
Sorry; I was a bit lazy on your review, and didn't add citations.

Initialisms and casing is part of the go style guide which GCT now references explicitly

So, yes, I did say that - but I was just highlighting an existing rule for us.

@shazbert shazbert added reconstructing Based on PR feedback, this is currently being reworked and is not to be merged and removed review me This pull request is ready for review labels Aug 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
high priority reconstructing Based on PR feedback, this is currently being reworked and is not to be merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants