Skip to content

Conversation

@wanxsb
Copy link

@wanxsb wanxsb commented Jan 8, 2026

No description provided.

@carlosmiei
Copy link
Collaborator

@wanxsb thank you for your contribution

Copy link
Contributor

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 pull request adds WebSocket API support for algorithmic orders in the Binance futures trading API, specifically for placing and canceling algo orders. The implementation follows the established patterns used in other WebSocket services within the codebase.

Key changes:

  • Added AlgoOrderPlaceWsService for placing algorithmic orders via WebSocket
  • Added AlgoOrderCancelWsService for canceling algorithmic orders via WebSocket
  • Both services support synchronous and asynchronous operation modes

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
v2/algo_order_place_service_ws.go Implements WebSocket service for placing algo orders with full parameter support (symbol, side, type, price, quantity, etc.)
v2/algo_order_cancel_service_ws.go Implements WebSocket service for canceling algo orders by algoId or clientAlgoId

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1 to +187
package binance

import (
"encoding/json"
"time"

"github.com/adshao/go-binance/v2/common"
"github.com/adshao/go-binance/v2/common/websocket"
"github.com/adshao/go-binance/v2/futures"
)

// AlgoOrderCancelWsService cancels algo order using WebSocket API
type AlgoOrderCancelWsService struct {
c websocket.Client
ApiKey string
SecretKey string
KeyType string
TimeOffset int64
}

// NewAlgoOrderCancelWsService init AlgoOrderCancelWsService
func NewAlgoOrderCancelWsService(apiKey, secretKey string) (*AlgoOrderCancelWsService, error) {
conn, err := websocket.NewConnection(futures.WsApiInitReadWriteConn, futures.WebsocketKeepalive, futures.WebsocketTimeoutReadWriteConnection)
if err != nil {
return nil, err
}

client, err := websocket.NewClient(conn)
if err != nil {
return nil, err
}

return &AlgoOrderCancelWsService{
c: client,
ApiKey: apiKey,
SecretKey: secretKey,
KeyType: common.KeyTypeHmac,
}, nil
}

// AlgoOrderCancelWsRequest parameters for 'algoOrder.cancel' websocket API
type AlgoOrderCancelWsRequest struct {
algoId *int64
clientAlgoId *string
recvWindow *int64
}

// NewAlgoOrderCancelWsRequest init AlgoOrderCancelWsRequest
func NewAlgoOrderCancelWsRequest() *AlgoOrderCancelWsRequest {
return &AlgoOrderCancelWsRequest{}
}

// AlgoID set algoID
func (s *AlgoOrderCancelWsRequest) AlgoID(algoID int64) *AlgoOrderCancelWsRequest {
s.algoId = &algoID
return s
}

// ClientAlgoID set clientAlgoID
func (s *AlgoOrderCancelWsRequest) ClientAlgoID(clientAlgoID string) *AlgoOrderCancelWsRequest {
s.clientAlgoId = &clientAlgoID
return s
}

// RecvWindow set recvWindow
func (s *AlgoOrderCancelWsRequest) RecvWindow(recvWindow int64) *AlgoOrderCancelWsRequest {
s.recvWindow = &recvWindow
return s
}

// buildParams builds params
func (s *AlgoOrderCancelWsRequest) buildParams() map[string]interface{} {
m := map[string]interface{}{}

if s.algoId != nil {
m["algoid"] = *s.algoId
}

if s.clientAlgoId != nil {
m["clientalgoid"] = *s.clientAlgoId
}

if s.recvWindow != nil {
m["recvWindow"] = *s.recvWindow
}

return m
}

// CancelAlgoOrderResult define algo order cancel result
type CancelAlgoOrderResult struct {
AlgoId int64 `json:"algoId"`
ClientAlgoId string `json:"clientAlgoId"`
Code string `json:"code"`
Message string `json:"msg"`
}

// CancelAlgoOrderWsResponse define 'algoOrder.cancel' websocket API response
type CancelAlgoOrderWsResponse struct {
Id string `json:"id"`
Status int `json:"status"`
Result CancelAlgoOrderResult `json:"result"`

// error response
Error *common.APIError `json:"error,omitempty"`
}

// Do - sends 'algoOrder.cancel' request
func (s *AlgoOrderCancelWsService) Do(requestID string, request *AlgoOrderCancelWsRequest) error {
// Use custom method "algoOrder.cancel"
method := websocket.WsApiMethodType("algoOrder.cancel")

rawData, err := websocket.CreateRequest(
websocket.NewRequestData(
requestID,
s.ApiKey,
s.SecretKey,
s.TimeOffset,
s.KeyType,
),
method,
request.buildParams(),
)
if err != nil {
return err
}

if err := s.c.Write(requestID, rawData); err != nil {
return err
}

return nil
}

// SyncDo - sends 'algoOrder.cancel' request and receives response
func (s *AlgoOrderCancelWsService) SyncDo(requestID string, request *AlgoOrderCancelWsRequest) (*CancelAlgoOrderWsResponse, error) {
// Use custom method "algoOrder.cancel"
method := websocket.WsApiMethodType("algoOrder.cancel")

rawData, err := websocket.CreateRequest(
websocket.NewRequestData(
requestID,
s.ApiKey,
s.SecretKey,
s.TimeOffset,
s.KeyType,
),
method,
request.buildParams(),
)
if err != nil {
return nil, err
}

response, err := s.c.WriteSync(requestID, rawData, websocket.WriteSyncWsTimeout)
if err != nil {
return nil, err
}

cancelAlgoOrderWsResponse := &CancelAlgoOrderWsResponse{}
if err := json.Unmarshal(response, cancelAlgoOrderWsResponse); err != nil {
return nil, err
}

return cancelAlgoOrderWsResponse, nil
}

// ReceiveAllDataBeforeStop waits until all responses will be received from websocket until timeout expired
func (s *AlgoOrderCancelWsService) ReceiveAllDataBeforeStop(timeout time.Duration) {
s.c.Wait(timeout)
}

// GetReadChannel returns channel with API response data (including API errors)
func (s *AlgoOrderCancelWsService) GetReadChannel() <-chan []byte {
return s.c.GetReadChannel()
}

// GetReadErrorChannel returns channel with errors which are occurred while reading websocket connection
func (s *AlgoOrderCancelWsService) GetReadErrorChannel() <-chan error {
return s.c.GetReadErrorChannel()
}

// GetReconnectCount returns count of reconnect attempts by client
func (s *AlgoOrderCancelWsService) GetReconnectCount() int64 {
return s.c.GetReconnectCount()
}

Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The new AlgoOrderCancelWsService lacks test coverage. Other similar WebSocket services in this codebase (e.g., order_service_ws_create.go, sor_order_place_service_ws.go, order_list_cancel_service_ws.go) all have corresponding test files. Consider adding comprehensive tests to verify the service initialization, request building, parameter handling, and response parsing.

Copilot uses AI. Check for mistakes.
m := map[string]interface{}{}

if s.algoId != nil {
m["algoid"] = *s.algoId
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The parameter name should be "algoId" (camelCase) instead of "algoid" to match the API specification and maintain consistency with the REST API implementation in futures/algo_order_service.go which uses "algoId".

Suggested change
m["algoid"] = *s.algoId
m["algoId"] = *s.algoId

Copilot uses AI. Check for mistakes.
}

if s.clientAlgoId != nil {
m["clientalgoid"] = *s.clientAlgoId
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The parameter name should be "clientAlgoId" (camelCase) instead of "clientalgoid" to match the API specification and maintain consistency with the REST API implementation in futures/algo_order_service.go which uses "clientAlgoId".

Suggested change
m["clientalgoid"] = *s.clientAlgoId
m["clientAlgoId"] = *s.clientAlgoId

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +263
package binance

import (
"encoding/json"
"time"

"github.com/adshao/go-binance/v2/common"
"github.com/adshao/go-binance/v2/common/websocket"
"github.com/adshao/go-binance/v2/futures"
)

// AlgoOrderPlaceWsService creates algo order using WebSocket API
type AlgoOrderPlaceWsService struct {
c websocket.Client
ApiKey string
SecretKey string
KeyType string
TimeOffset int64
}

// NewAlgoOrderPlaceWsService init AlgoOrderPlaceWsService
func NewAlgoOrderPlaceWsService(apiKey, secretKey string) (*AlgoOrderPlaceWsService, error) {
conn, err := websocket.NewConnection(futures.WsApiInitReadWriteConn, futures.WebsocketKeepalive, futures.WebsocketTimeoutReadWriteConnection)
if err != nil {
return nil, err
}

client, err := websocket.NewClient(conn)
if err != nil {
return nil, err
}

return &AlgoOrderPlaceWsService{
c: client,
ApiKey: apiKey,
SecretKey: secretKey,
KeyType: common.KeyTypeHmac,
}, nil
}

// AlgoOrderPlaceWsRequest parameters for 'algoOrder.place' websocket API
type AlgoOrderPlaceWsRequest struct {
algoType futures.OrderAlgoType
symbol string
side futures.SideType
_type futures.AlgoOrderType
positionSide *futures.PositionSideType
timeInForce *futures.TimeInForceType
quantity *string
price *string
triggerPrice string
workingType *futures.WorkingType
closePosition *bool
reduceOnly *bool
newClientOrderID *string
newOrderRespType futures.NewOrderRespType
recvWindow *int64
}

// NewAlgoOrderPlaceWsRequest init AlgoOrderPlaceWsRequest
func NewAlgoOrderPlaceWsRequest() *AlgoOrderPlaceWsRequest {
return &AlgoOrderPlaceWsRequest{
algoType: futures.OrderAlgoTypeConditional,
newOrderRespType: futures.NewOrderRespTypeRESULT,
}
}

// Symbol set symbol
func (s *AlgoOrderPlaceWsRequest) Symbol(symbol string) *AlgoOrderPlaceWsRequest {
s.symbol = symbol
return s
}

// Side set side
func (s *AlgoOrderPlaceWsRequest) Side(side futures.SideType) *AlgoOrderPlaceWsRequest {
s.side = side
return s
}

// Type set type
func (s *AlgoOrderPlaceWsRequest) Type(_type futures.AlgoOrderType) *AlgoOrderPlaceWsRequest {
s._type = _type
return s
}

// PositionSide set positionSide
func (s *AlgoOrderPlaceWsRequest) PositionSide(positionSide futures.PositionSideType) *AlgoOrderPlaceWsRequest {
s.positionSide = &positionSide
return s
}

// TimeInForce set timeInForce
func (s *AlgoOrderPlaceWsRequest) TimeInForce(timeInForce futures.TimeInForceType) *AlgoOrderPlaceWsRequest {
s.timeInForce = &timeInForce
return s
}

// Quantity set quantity
func (s *AlgoOrderPlaceWsRequest) Quantity(quantity string) *AlgoOrderPlaceWsRequest {
s.quantity = &quantity
return s
}

// Price set price
func (s *AlgoOrderPlaceWsRequest) Price(price string) *AlgoOrderPlaceWsRequest {
s.price = &price
return s
}

// TriggerPrice set triggerPrice
func (s *AlgoOrderPlaceWsRequest) TriggerPrice(triggerPrice string) *AlgoOrderPlaceWsRequest {
s.triggerPrice = triggerPrice
return s
}

// WorkingType set workingType
func (s *AlgoOrderPlaceWsRequest) WorkingType(workingType futures.WorkingType) *AlgoOrderPlaceWsRequest {
s.workingType = &workingType
return s
}

// ClosePosition set closePosition
func (s *AlgoOrderPlaceWsRequest) ClosePosition(closePosition bool) *AlgoOrderPlaceWsRequest {
s.closePosition = &closePosition
return s
}

// ReduceOnly set reduceOnly
func (s *AlgoOrderPlaceWsRequest) ReduceOnly(reduceOnly bool) *AlgoOrderPlaceWsRequest {
s.reduceOnly = &reduceOnly
return s
}

// NewClientOrderID set newClientOrderID
func (s *AlgoOrderPlaceWsRequest) NewClientOrderID(newClientOrderID string) *AlgoOrderPlaceWsRequest {
s.newClientOrderID = &newClientOrderID
return s
}

// NewOrderResponseType set newOrderResponseType
func (s *AlgoOrderPlaceWsRequest) NewOrderResponseType(newOrderResponseType futures.NewOrderRespType) *AlgoOrderPlaceWsRequest {
s.newOrderRespType = newOrderResponseType
return s
}

// RecvWindow set recvWindow
func (s *AlgoOrderPlaceWsRequest) RecvWindow(recvWindow int64) *AlgoOrderPlaceWsRequest {
s.recvWindow = &recvWindow
return s
}

// buildParams builds params
func (s *AlgoOrderPlaceWsRequest) buildParams() map[string]interface{} {
m := map[string]interface{}{
"algoType": s.algoType,
"symbol": s.symbol,
"side": s.side,
"type": s._type,
"triggerPrice": s.triggerPrice,
"newOrderRespType": s.newOrderRespType,
}

if s.positionSide != nil {
m["positionSide"] = *s.positionSide
}
if s.timeInForce != nil {
m["timeInForce"] = *s.timeInForce
}
if s.quantity != nil {
m["quantity"] = *s.quantity
}
if s.price != nil {
m["price"] = *s.price
}
if s.workingType != nil {
m["workingType"] = *s.workingType
}
if s.closePosition != nil {
m["closePosition"] = *s.closePosition
}
if s.reduceOnly != nil {
m["reduceOnly"] = *s.reduceOnly
}
if s.newClientOrderID != nil {
m["newClientOrderId"] = *s.newClientOrderID
}
if s.recvWindow != nil {
m["recvWindow"] = *s.recvWindow
}

return m
}

// CreateAlgoOrderResult define algo order creation result
type CreateAlgoOrderResult struct {
AlgoId int64 `json:"algoId"`
ClientAlgoId string `json:"clientAlgoId"`
}

// CreateAlgoOrderWsResponse define 'algoOrder.place' websocket API response
type CreateAlgoOrderWsResponse struct {
Id string `json:"id"`
Status int `json:"status"`
Result CreateAlgoOrderResult `json:"result"`

// error response
Error *common.APIError `json:"error,omitempty"`
}

// SyncDo - sends 'algoOrder.place' request and receives response
func (s *AlgoOrderPlaceWsService) SyncDo(requestID string, request *AlgoOrderPlaceWsRequest) (*CreateAlgoOrderWsResponse, error) {
// Use custom method "algoOrder.place"
method := websocket.WsApiMethodType("algoOrder.place")

rawData, err := websocket.CreateRequest(
websocket.NewRequestData(
requestID,
s.ApiKey,
s.SecretKey,
s.TimeOffset,
s.KeyType,
),
method,
request.buildParams(),
)
if err != nil {
return nil, err
}

response, err := s.c.WriteSync(requestID, rawData, websocket.WriteSyncWsTimeout)
if err != nil {
return nil, err
}

createAlgoOrderWsResponse := &CreateAlgoOrderWsResponse{}
if err := json.Unmarshal(response, createAlgoOrderWsResponse); err != nil {
return nil, err
}

return createAlgoOrderWsResponse, nil
}

// ReceiveAllDataBeforeStop waits until all responses will be received from websocket until timeout expired
func (s *AlgoOrderPlaceWsService) ReceiveAllDataBeforeStop(timeout time.Duration) {
s.c.Wait(timeout)
}

// GetReadChannel returns channel with API response data (including API errors)
func (s *AlgoOrderPlaceWsService) GetReadChannel() <-chan []byte {
return s.c.GetReadChannel()
}

// GetReadErrorChannel returns channel with errors which are occurred while reading websocket connection
func (s *AlgoOrderPlaceWsService) GetReadErrorChannel() <-chan error {
return s.c.GetReadErrorChannel()
}

// GetReconnectCount returns count of reconnect attempts by client
func (s *AlgoOrderPlaceWsService) GetReconnectCount() int64 {
return s.c.GetReconnectCount()
}


Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The new AlgoOrderPlaceWsService lacks test coverage. Other similar WebSocket services in this codebase (e.g., order_service_ws_create.go, sor_order_place_service_ws.go, order_list_place_service_ws.go) all have corresponding test files. Consider adding comprehensive tests to verify the service initialization, request building, parameter handling, and response parsing.

Copilot uses AI. Check for mistakes.
@carlosmiei carlosmiei self-assigned this Jan 9, 2026
@carlosmiei
Copy link
Collaborator

@wanxsb can you please check the formatting?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants