-
Notifications
You must be signed in to change notification settings - Fork 874
bybit: Add websocket trading functionality across all assets #1672
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
bybit: Add websocket trading functionality across all assets #1672
Conversation
Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
There was a problem hiding this 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.
There was a problem hiding this 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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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"` |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shh you
There was a problem hiding this comment.
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.
PR Description
#1670 Requires dependency
Fixes # (issue)
Type of change
Please delete options that are not relevant and add an
x
in[]
as item is complete.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.
Checklist