diff --git a/.github/workflows/connector.yml b/.github/workflows/connector.yml index 46c49b5..88f0f40 100644 --- a/.github/workflows/connector.yml +++ b/.github/workflows/connector.yml @@ -10,17 +10,41 @@ on: - rc-** jobs: + Checks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version-file: './go.mod' + - name: Check Formatting + run: go fmt ./... + - name: Vet Project + run: go vet ./... + - name: Initialise StaticCheck + run: go get honnef.co/go/tools/cmd/staticcheck@latest && go install honnef.co/go/tools/cmd/staticcheck@latest + - name: StaticCheck - Check for unused functions, duplicate imports + more + run: staticcheck ./... + - name: Initialise DeadCode + run: go get github.com/remyoudompheng/go-misc/deadcode && go install github.com/remyoudompheng/go-misc/deadcode + - name: DeadCode - Check for unused variables + run: deadcode -test + - name: Initialise ErrCheck + run: go get github.com/kisielk/errcheck@latest && go install github.com/kisielk/errcheck@latest + - name: ErrCheck - Detect any unchecked errors + run: errcheck ./... + - name: Install GoSec + run: go get github.com/securego/gosec/v2/cmd/gosec@latest && go install github.com/securego/gosec/v2/cmd/gosec@latest + - name: GoSec - Scan for potential security issues with gosec + run: gosec -tests -confidence high ./... UnitTest: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version-file: './go.mod' - - name: Format - run: ./scripts/checks.sh format - - name: Vet - run: ./scripts/checks.sh vet - - name: UnitTest - run: go test -v . + - name: Run Unit Tests + run: go test -v -race -covermode=atomic . diff --git a/CHANGELOG.md b/CHANGELOG.md index 2322310..faa7624 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ # Change log +## v0.2.0 - 2023-04-21 + +### Added +- WebsocketStreamClient + - Websocket Client can be initialized with 2 parameters, `NewWebsocketStreamClient(isCombined, baseURL)`: + - `isCombined` is a MANDATORY boolean value that specifies whether you are calling a combined stream or not. + - If `isCombined` is set to `true`, `"/stream?streams="` will be appended to the `baseURL` to allow for Combining streams. + - Otherwise, if set to `false`, `"/ws/"` will be appended to the `baseURL`. + - `baseURL` is an OPTIONAL string value that determines the base URL to use for the websocket connection. + - If `baseURL` is not set, it will default to the Live Exchange URL: `"wss://stream.binance.com:9443"`. + +### Fixed +- Order Response Types for `CreateOrderService` - `POST /api/v3/order` + - Added support for all 3 Order Response Types - `ACK`, `RESULT` and `FULL` +- Order Response Types for `MarginAccountNewOrderService` - `POST /sapi/v1/margin/order` + - Added support for all 3 Order Response Types - `ACK`, `RESULT` and `FULL` +- Different Response Types for `MarginIsolatedAccountInfoService` - `GET /sapi/v1/margin/isolated/account` + - Added support for both Response Types, depending on whether `symbols` is set or not. + +### Changed +- Renamed `WsAllMiniMarketsStatServe` to `WsAllMarketMiniTickersStatServe`. +- Renamed `WsMarketsStatServe` to `WsMarketTickersStatServe`. +- Renamed `WsAllMarketsStatServe` to `WsAllMarketTickersStatServe`. +- Updated Github Action `UnitTest`. + +### Removed +- Removed unused `setFormParam`, `setFormParams`, `WithRecvWindow`, `WithHeader` and `WithHeaders` functions from `request.go`. + ## v0.1.0 - 2023-03-31 - First release diff --git a/README.md b/README.md index 2661939..52213e5 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,23 @@ func main() { Please find more examples for each supported endpoint in the `examples` folder. ## Websocket API +Initialising Websocket Client +- Websocket Client can be initialized with 2 parameters, `NewWebsocketStreamClient(isCombined, baseURL)`: +- `isCombined` is a MANDATORY boolean value that specifies whether you are calling a combined stream or not. + - If `isCombined` is set to `true`, `"/stream?streams="` will be appended to the `baseURL` to allow for Combining streams. + - Otherwise, if set to `false`, `"/ws/"` will be appended to the `baseURL`. +- `baseURL` is an OPTIONAL string value that determines the base URL to use for the websocket connection. + - If `baseURL` is not set, it will default to the Live Exchange URL: `"wss://stream.binance.com:9443"`. + +```go +// Initialise Websocket Client with Production baseURL and false for "isCombined" parameter + +websocketStreamClient := binance_connector.NewWebsocketStreamClient(false, "wss://testnet.binance.vision") + +// Initialise Websocket Client with Production baseURL and true for "isCombined" parameter + +websocketStreamClient := binance_connector.NewWebsocketStreamClient(true) +``` Diff. Depth Stream Example @@ -91,6 +108,9 @@ import ( ) func main() { + // Initialise Websocket Client with Testnet BaseURL and false for "isCombined" parameter + websocketStreamClient := binance_connector.NewWebsocketStreamClient(false, "wss://testnet.binance.vision") + wsDepthHandler := func(event *binance_connector.WsDepthEvent) { fmt.Println(binance_connector.PrettyPrint(event)) } @@ -100,7 +120,7 @@ func main() { } // Depth stream subscription - doneCh, stopCh, err := binance_connector.WsDepthServe("BNBUSDT", wsDepthHandler, errHandler) + doneCh, stopCh, err := websocketStreamClient.WsDepthServe("BNBUSDT", wsDepthHandler, errHandler) if err != nil { fmt.Println(err) return @@ -108,13 +128,49 @@ func main() { go func() { time.Sleep(30 * time.Second) - stopCh <- struct{}{} // use stopC to stop streaming + stopCh <- struct{}{} // use stopCh to stop streaming }() <-doneCh } ``` +Combined Diff. Depth Stream Example + +```go +package main + +import ( + "fmt" + "time" + + binance_connector "github.com/binance/binance-connector-go" +) + +func main() { + // Set isCombined parameter to true as we are using Combined Depth Stream + websocketStreamClient := binance_connector.NewWebsocketStreamClient(true) + + wsCombinedDepthHandler := func(event *binance_connector.WsDepthEvent) { + fmt.Println(binance_connector.PrettyPrint(event)) + } + errHandler := func(err error) { + fmt.Println(err) + } + // Use WsCombinedDepthServe to subscribe to multiple streams + doneCh, stopCh, err := websocketStreamClient.WsCombinedDepthServe([]string{"LTCBTC", "BTCUSDT", "MATICUSDT"}, wsCombinedDepthHandler, errHandler) + if err != nil { + fmt.Println(err) + return + } + go func() { + time.Sleep(5 * time.Second) + stopCh <- struct{}{} + }() + <-doneCh +} +``` + ## Base URL - Binance provides alternative Production URLs in case of performance issues: - https://api1.binance.com diff --git a/account.go b/account.go index 9b73b56..b9594fe 100644 --- a/account.go +++ b/account.go @@ -266,8 +266,15 @@ func (s *CreateOrderService) SelfTradePreventionMode(selfTradePreventionMode str return s } +const ( + ACK = 1 + RESULT = 2 + FULL = 3 +) + // Do send request -func (s *CreateOrderService) Do(ctx context.Context, opts ...RequestOption) (res *CreateOrderResponse, err error) { +func (s *CreateOrderService) Do(ctx context.Context, opts ...RequestOption) (res interface{}, err error) { + respType := ACK r := &request{ method: http.MethodPost, endpoint: "/api/v3/order", @@ -276,6 +283,12 @@ func (s *CreateOrderService) Do(ctx context.Context, opts ...RequestOption) (res r.setParam("symbol", s.symbol) r.setParam("side", s.side) r.setParam("type", s.orderType) + switch s.orderType { + case "MARKET": + respType = FULL + case "LIMIT": + respType = FULL + } if s.timeInForce != nil { r.setParam("timeInForce", *s.timeInForce) } @@ -305,6 +318,15 @@ func (s *CreateOrderService) Do(ctx context.Context, opts ...RequestOption) (res } if s.newOrderRespType != nil { r.setParam("newOrderRespType", *s.newOrderRespType) + switch *s.newOrderRespType { + case "ACK": + respType = ACK + case "RESULT": + respType = RESULT + case "FULL": + respType = FULL + } + } if s.selfTradePreventionMode != nil { r.setParam("selfTradePreventionMode", *s.selfTradePreventionMode) @@ -313,7 +335,14 @@ func (s *CreateOrderService) Do(ctx context.Context, opts ...RequestOption) (res if err != nil { return nil, err } - res = new(CreateOrderResponse) + switch respType { + case ACK: + res = new(CreateOrderResponseACK) + case RESULT: + res = new(CreateOrderResponseRESULT) + case FULL: + res = new(CreateOrderResponseFULL) + } err = json.Unmarshal(data, res) if err != nil { return nil, err @@ -321,13 +350,58 @@ func (s *CreateOrderService) Do(ctx context.Context, opts ...RequestOption) (res return res, nil } -// Create CreateOrderResponse -type CreateOrderResponse struct { - Symbol string `json:"symbol"` - OrderId int64 `json:"orderId"` - OrderIdList []int64 `json:"orderIdList"` - ClientOrderId string `json:"clientOrderId"` - TransactTime uint64 `json:"transactTime"` +// Create CreateOrderResponseACK +type CreateOrderResponseACK struct { + Symbol string `json:"symbol"` + OrderId int64 `json:"orderId"` + OrderListId int64 `json:"orderListId"` + ClientOrderId string `json:"clientOrderId"` + TransactTime uint64 `json:"transactTime"` +} + +// Create CreateOrderResponseRESULT +type CreateOrderResponseRESULT struct { + Symbol string `json:"symbol"` + OrderId int64 `json:"orderId"` + OrderListId int64 `json:"orderListId"` + ClientOrderId string `json:"clientOrderId"` + TransactTime uint64 `json:"transactTime"` + Price string `json:"price"` + OrigQty string `json:"origQty"` + ExecutedQty string `json:"executedQty"` + CumulativeQuoteQty string `json:"cummulativeQuoteQty"` + Status string `json:"status"` + TimeInForce string `json:"timeInForce"` + Type string `json:"type"` + Side string `json:"side"` + WorkingTime uint64 `json:"workingTime"` + SelfTradePreventionMode string `json:"selfTradePreventionMode"` +} + +// Create CreateOrderResponseFULL +type CreateOrderResponseFULL struct { + Symbol string `json:"symbol"` + OrderId int64 `json:"orderId"` + OrderListId int64 `json:"orderListId"` + ClientOrderId string `json:"clientOrderId"` + TransactTime uint64 `json:"transactTime"` + Price string `json:"price"` + OrigQty string `json:"origQty"` + ExecutedQty string `json:"executedQty"` + CumulativeQuoteQty string `json:"cummulativeQuoteQty"` + Status string `json:"status"` + TimeInForce string `json:"timeInForce"` + Type string `json:"type"` + Side string `json:"side"` + WorkingTime uint64 `json:"workingTime"` + SelfTradePreventionMode string `json:"selfTradePreventionMode"` + Fills []struct { + Price string `json:"price"` + Qty string `json:"qty"` + Commission string `json:"commission"` + CommissionAsset string `json:"commissionAsset"` + TradeId int64 `json:"tradeId"` + } `json:"fills"` } // Binance Cancel Order endpoint (DELETE /api/v3/order) diff --git a/account_test.go b/account_test.go index b954659..8116571 100644 --- a/account_test.go +++ b/account_test.go @@ -201,14 +201,14 @@ func (s *accountTestSuite) TestNewOrder() { Do(ctx) s.r().NoError(err) - e := &CreateOrderResponse{ + e := &CreateOrderResponseFULL{ Symbol: "BTCUSDT", OrderId: 28, } - s.assertCreateOrderResponseEqual(e, res) + s.assertCreateOrderResponseEqual(e, res.(*CreateOrderResponseFULL)) } -func (s *accountTestSuite) assertCreateOrderResponseEqual(e, a *CreateOrderResponse) { +func (s *accountTestSuite) assertCreateOrderResponseEqual(e, a *CreateOrderResponseFULL) { r := s.r() r.Equal(e.Symbol, a.Symbol, "Symbol") r.Equal(e.OrderId, a.OrderId, "OrderId") diff --git a/client.go b/client.go index 536fa53..8e5184c 100644 --- a/client.go +++ b/client.go @@ -7,7 +7,7 @@ import ( "crypto/sha256" "encoding/json" "fmt" - "io/ioutil" + "io" "log" "net/http" "net/url" @@ -40,11 +40,9 @@ type doFunc func(req *http.Request) (*http.Response, error) // Globals const ( - baseAPIMainURL = "https://api.binance.com" - baseAPITestnetURL = "https://testnet.binance.vision" - timestampKey = "timestamp" - signatureKey = "signature" - recvWindowKey = "recvWindow" + timestampKey = "timestamp" + signatureKey = "signature" + recvWindowKey = "recvWindow" ) func currentTimestamp() int64 { @@ -162,7 +160,7 @@ func (c *Client) callAPI(ctx context.Context, r *request, opts ...RequestOption) if err != nil { return []byte{}, err } - data, err = ioutil.ReadAll(res.Body) + data, err = io.ReadAll(res.Body) if err != nil { return []byte{}, err } diff --git a/consts.go b/consts.go index 7997b9f..786ba7a 100644 --- a/consts.go +++ b/consts.go @@ -2,4 +2,4 @@ package binance_connector const Name = "binance-connector-go" -const Version = "0.1.0" +const Version = "0.2.0" diff --git a/examples/websocket/AllMarketMiniTickers/AllMarketMiniTickers.go b/examples/websocket/AllMarketMiniTickers/AllMarketMiniTickers.go index b7de99d..ab3e171 100644 --- a/examples/websocket/AllMarketMiniTickers/AllMarketMiniTickers.go +++ b/examples/websocket/AllMarketMiniTickers/AllMarketMiniTickers.go @@ -11,13 +11,14 @@ func main() { } func WsAllMarketMiniTickers() { - wsAllMarketMiniTickersHandler := func(event binance_connector.WsAllMiniMarketsStatEvent) { + websocketStreamClient := binance_connector.NewWebsocketStreamClient(false) + wsAllMarketMiniTickersHandler := func(event binance_connector.WsAllMarketMiniTickersStatEvent) { fmt.Println(binance_connector.PrettyPrint(event)) } errHandler := func(err error) { fmt.Println(err) } - doneCh, _, err := binance_connector.WsAllMiniMarketsStatServe(wsAllMarketMiniTickersHandler, errHandler) + doneCh, _, err := websocketStreamClient.WsAllMarketMiniTickersStatServe(wsAllMarketMiniTickersHandler, errHandler) if err != nil { fmt.Println(err) return diff --git a/examples/websocket/AllMarketTickers/AllMarketTickers.go b/examples/websocket/AllMarketTickers/AllMarketTickers.go index 26adfb4..4fce408 100644 --- a/examples/websocket/AllMarketTickers/AllMarketTickers.go +++ b/examples/websocket/AllMarketTickers/AllMarketTickers.go @@ -11,13 +11,14 @@ func main() { } func WsAllMarketTickersExample() { - wsAllMarketTickersHandler := func(event binance_connector.WsAllMarketsStatEvent) { + websocketStreamClient := binance_connector.NewWebsocketStreamClient(false) + wsAllMarketTickersHandler := func(event binance_connector.WsAllMarketTickersStatEvent) { fmt.Println(binance_connector.PrettyPrint(event)) } errHandler := func(err error) { fmt.Println(err) } - doneCh, _, err := binance_connector.WsAllMarketsStatServe(wsAllMarketTickersHandler, errHandler) + doneCh, _, err := websocketStreamClient.WsAllMarketTickersStatServe(wsAllMarketTickersHandler, errHandler) if err != nil { fmt.Println(err) return diff --git a/examples/websocket/CombinedDepth/CombinedDepth.go b/examples/websocket/CombinedDepth/CombinedDepth.go new file mode 100644 index 0000000..d960a04 --- /dev/null +++ b/examples/websocket/CombinedDepth/CombinedDepth.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "time" + + binance_connector "github.com/binance/binance-connector-go" +) + +func main() { + WsCombinedDepthHandlerExample() +} + +func WsCombinedDepthHandlerExample() { + websocketStreamClient := binance_connector.NewWebsocketStreamClient(true) + wsCombinedDepthHandler := func(event *binance_connector.WsDepthEvent) { + fmt.Println(binance_connector.PrettyPrint(event)) + } + errHandler := func(err error) { + fmt.Println(err) + } + doneCh, stopCh, err := websocketStreamClient.WsCombinedDepthServe([]string{"LTCBTC", "BTCUSDT", "MATICUSDT"}, wsCombinedDepthHandler, errHandler) + if err != nil { + fmt.Println(err) + return + } + // use stopCh to exit + go func() { + time.Sleep(5 * time.Second) + stopCh <- struct{}{} + }() + // remove this if you do not want to be blocked here + <-doneCh +} diff --git a/examples/websocket/UserDataStream/UserDataStream.go b/examples/websocket/UserDataStream/UserDataStream.go index bb04248..b9517cc 100644 --- a/examples/websocket/UserDataStream/UserDataStream.go +++ b/examples/websocket/UserDataStream/UserDataStream.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" binance_connector "github.com/binance/binance-connector-go" @@ -11,13 +12,28 @@ func main() { } func WsUserData() { + apiKey := "your api key" + secretKey := "your secret key" + baseURL := "https://api.binance.com" + + client := binance_connector.NewClient(apiKey, secretKey, baseURL) + + listenKey, err := client.NewCreateListenKeyService(). + Do(context.Background()) + if err != nil { + fmt.Println(err) + return + } + + websocketStreamClient := binance_connector.NewWebsocketStreamClient(false) + wsUserDataHandler := func(event *binance_connector.WsUserDataEvent) { fmt.Println(binance_connector.PrettyPrint(event)) } errHandler := func(err error) { fmt.Println(err) } - doneCh, _, err := binance_connector.WsUserDataServe("YourListenKey", wsUserDataHandler, errHandler) + doneCh, _, err := websocketStreamClient.WsUserDataServe(listenKey, wsUserDataHandler, errHandler) if err != nil { fmt.Println(err) return diff --git a/examples/websocket/aggtrades/aggtrades.go b/examples/websocket/aggtrades/aggtrades.go index c37270f..1f9062e 100644 --- a/examples/websocket/aggtrades/aggtrades.go +++ b/examples/websocket/aggtrades/aggtrades.go @@ -11,13 +11,16 @@ func main() { } func AggTradesExample() { + // Initialise Websocket Client with Testnet base URL and false for "isCombined" parameter + websocketStreamClient := binance_connector.NewWebsocketStreamClient(false, "wss://testnet.binance.vision") + wsAggTradeHandler := func(event *binance_connector.WsAggTradeEvent) { fmt.Println(binance_connector.PrettyPrint(event)) } errHandler := func(err error) { fmt.Println(err) } - doneCh, _, err := binance_connector.WsAggTradeServe("LTCBTC", wsAggTradeHandler, errHandler) + doneCh, _, err := websocketStreamClient.WsAggTradeServe("BTCUSDT", wsAggTradeHandler, errHandler) if err != nil { fmt.Println(err) return diff --git a/examples/websocket/bookticker/bookticker.go b/examples/websocket/bookticker/bookticker.go index 9580d4c..927b411 100644 --- a/examples/websocket/bookticker/bookticker.go +++ b/examples/websocket/bookticker/bookticker.go @@ -11,13 +11,14 @@ func main() { } func WsBookTickerExample() { + websocketStreamClient := binance_connector.NewWebsocketStreamClient(false) wsBookTickerHandler := func(event *binance_connector.WsBookTickerEvent) { fmt.Println(binance_connector.PrettyPrint(event)) } errHandler := func(err error) { fmt.Println(err) } - doneCh, _, err := binance_connector.WsBookTickerServe("LTCBTC", wsBookTickerHandler, errHandler) + doneCh, _, err := websocketStreamClient.WsBookTickerServe("LTCBTC", wsBookTickerHandler, errHandler) if err != nil { fmt.Println(err) return diff --git a/examples/websocket/depth/depth.go b/examples/websocket/depth/depth.go index 35d9f41..69f3ce4 100644 --- a/examples/websocket/depth/depth.go +++ b/examples/websocket/depth/depth.go @@ -12,18 +12,19 @@ func main() { } func WsDepthHandlerExample() { + websocketStreamClient := binance_connector.NewWebsocketStreamClient(false) wsDepthHandler := func(event *binance_connector.WsDepthEvent) { fmt.Println(binance_connector.PrettyPrint(event)) } errHandler := func(err error) { fmt.Println(err) } - doneCh, stopCh, err := binance_connector.WsDepthServe("LTCBTC", wsDepthHandler, errHandler) + doneCh, stopCh, err := websocketStreamClient.WsDepthServe("LTCBTC", wsDepthHandler, errHandler) if err != nil { fmt.Println(err) return } - // use stopC to exit + // use stopCh to exit go func() { time.Sleep(5 * time.Second) stopCh <- struct{}{} diff --git a/examples/websocket/kline/kline.go b/examples/websocket/kline/kline.go index eb76864..146a098 100644 --- a/examples/websocket/kline/kline.go +++ b/examples/websocket/kline/kline.go @@ -11,13 +11,14 @@ func main() { } func WsKlineExample() { + websocketStreamClient := binance_connector.NewWebsocketStreamClient(false) wsKlineHandler := func(event *binance_connector.WsKlineEvent) { fmt.Println(binance_connector.PrettyPrint(event)) } errHandler := func(err error) { fmt.Println(err) } - doneCh, _, err := binance_connector.WsKlineServe("LTCBTC", "1m", wsKlineHandler, errHandler) + doneCh, _, err := websocketStreamClient.WsKlineServe("LTCBTC", "1m", wsKlineHandler, errHandler) if err != nil { fmt.Println(err) return diff --git a/examples/websocket/trades/trades.go b/examples/websocket/trades/trades.go index af3b167..ebd9fd0 100644 --- a/examples/websocket/trades/trades.go +++ b/examples/websocket/trades/trades.go @@ -11,13 +11,14 @@ func main() { } func WsTradeExample() { + websocketStreamClient := binance_connector.NewWebsocketStreamClient(false) wsTradeHandler := func(event *binance_connector.WsTradeEvent) { fmt.Println(binance_connector.PrettyPrint(event)) } errHandler := func(err error) { fmt.Println(err) } - doneCh, _, err := binance_connector.WsTradeServe("LTCBTC", wsTradeHandler, errHandler) + doneCh, _, err := websocketStreamClient.WsTradeServe("LTCBTC", wsTradeHandler, errHandler) if err != nil { fmt.Println(err) return diff --git a/go.mod b/go.mod index 97cdb92..e5ce9da 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/binance/binance-connector-go -go 1.14 +go 1.19 require ( github.com/bitly/go-simplejson v0.5.0 @@ -10,5 +10,9 @@ require ( require ( github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/kr/pretty v0.3.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/margin.go b/margin.go index 8f7f645..87bb939 100644 --- a/margin.go +++ b/margin.go @@ -524,7 +524,8 @@ func (s *MarginAccountNewOrderService) TimeInForce(timeInForce string) *MarginAc } // Do send request -func (s *MarginAccountNewOrderService) Do(ctx context.Context, opts ...RequestOption) (res *MarginAccountNewOrderResponse, err error) { +func (s *MarginAccountNewOrderService) Do(ctx context.Context, opts ...RequestOption) (res interface{}, err error) { + respType := ACK r := &request{ method: http.MethodPost, endpoint: marginAccountNewOrderEndpoint, @@ -535,6 +536,12 @@ func (s *MarginAccountNewOrderService) Do(ctx context.Context, opts ...RequestOp "side": s.side, "type": s.orderType, } + switch s.orderType { + case "MARKET": + respType = FULL + case "LIMIT": + respType = FULL + } if s.isIsolated != nil { m["isIsolated"] = *s.isIsolated } @@ -558,6 +565,14 @@ func (s *MarginAccountNewOrderService) Do(ctx context.Context, opts ...RequestOp } if s.newOrderRespType != nil { m["newOrderRespType"] = *s.newOrderRespType + switch *s.newOrderRespType { + case "ACK": + respType = ACK + case "RESULT": + respType = RESULT + case "FULL": + respType = FULL + } } if s.sideEffectType != nil { m["sideEffectType"] = *s.sideEffectType @@ -568,18 +583,34 @@ func (s *MarginAccountNewOrderService) Do(ctx context.Context, opts ...RequestOp r.setParams(m) data, err := s.c.callAPI(ctx, r, opts...) if err != nil { - return &MarginAccountNewOrderResponse{}, err + return nil, err + } + switch respType { + case ACK: + res = new(MarginAccountNewOrderResponseACK) + case RESULT: + res = new(MarginAccountNewOrderResponseRESULT) + case FULL: + res = new(MarginAccountNewOrderResponseFULL) } - res = new(MarginAccountNewOrderResponse) err = json.Unmarshal(data, res) if err != nil { - return &MarginAccountNewOrderResponse{}, err + return nil, err } return res, nil } -// MarginAccountNewOrderResponse define margin account new order response -type MarginAccountNewOrderResponse struct { +// Create MarginAccountNewOrderResponseACK +type MarginAccountNewOrderResponseACK struct { + Symbol string `json:"symbol"` + OrderId int64 `json:"orderId"` + ClientOrderId int64 `json:"clientOrderId"` + IsIsolated bool `json:"isIsolated"` + TransactTime uint64 `json:"transactTime"` +} + +// Create MarginAccountNewOrderResponseRESULT +type MarginAccountNewOrderResponseRESULT struct { Symbol string `json:"symbol"` OrderId int64 `json:"orderId"` ClientOrderId string `json:"clientOrderId"` @@ -595,6 +626,31 @@ type MarginAccountNewOrderResponse struct { Side string `json:"side"` } +// Create MarginAccountNewOrderResponseFULL +type MarginAccountNewOrderResponseFULL struct { + Symbol string `json:"symbol"` + OrderId int64 `json:"orderId"` + ClientOrderId string `json:"clientOrderId"` + TransactTime uint64 `json:"transactTime"` + Price string `json:"price"` + OrigQty string `json:"origQty"` + ExecutedQty string `json:"executedQty"` + CumulativeQuoteQty string `json:"cummulativeQuoteQty"` + Status string `json:"status"` + TimeInForce string `json:"timeInForce"` + Type string `json:"type"` + Side string `json:"side"` + MarginBuyBorrowAmount float64 `json:"marginBuyBorrowAmount"` + MarginBuyBorrowAsset string `json:"marginBuyBorrowAsset"` + IsIsolated bool `json:"isIsolated"` + Fills []struct { + Price string `json:"price"` + Qty string `json:"qty"` + Commission string `json:"commission"` + CommissionAsset string `json:"commissionAsset"` + } `json:"fills"` +} + // Margin Account Cancel Order (TRADE) API Endpoint const ( marginAccountCancelOrderEndpoint = "/sapi/v1/margin/order" @@ -2713,7 +2769,7 @@ func (s *MarginIsolatedAccountInfoService) Symbols(symbols string) *MarginIsolat } // Do send request -func (s *MarginIsolatedAccountInfoService) Do(ctx context.Context, opts ...RequestOption) (res *MarginIsolatedAccountInfoResponse, err error) { +func (s *MarginIsolatedAccountInfoService) Do(ctx context.Context, opts ...RequestOption) (res interface{}, err error) { r := &request{ method: http.MethodGet, endpoint: marginIsolatedAccountInfoEndpoint, @@ -2724,52 +2780,74 @@ func (s *MarginIsolatedAccountInfoService) Do(ctx context.Context, opts ...Reque } data, err := s.c.callAPI(ctx, r, opts...) if err != nil { - return &MarginIsolatedAccountInfoResponse{}, err + if s.symbols != nil { + return &MarginIsolatedAccountInfoResponseSymbols{}, err + } else { + return &MarginIsolatedAccountInfoResponse{}, err + } + } + if s.symbols != nil { + res = new(MarginIsolatedAccountInfoResponseSymbols) + } else { + res = new(MarginIsolatedAccountInfoResponse) } - res = new(MarginIsolatedAccountInfoResponse) err = json.Unmarshal(data, res) if err != nil { - return &MarginIsolatedAccountInfoResponse{}, err + if s.symbols != nil { + return &MarginIsolatedAccountInfoResponseSymbols{}, err + } else { + return &MarginIsolatedAccountInfoResponse{}, err + } } return res, nil } -// MarginIsolatedAccountInfoService response +type MarginIsolatedAccountInfoAssets struct { + BaseAsset struct { + Asset string `json:"asset"` + BorrowEnabled bool `json:"borrowEnabled"` + Free string `json:"free"` + Interest string `json:"interest"` + Locked string `json:"locked"` + NetAsset string `json:"netAsset"` + NetAssetOfBtc string `json:"netAssetOfBtc"` + RepayEnabled bool `json:"repayEnabled"` + TotalAsset string `json:"totalAsset"` + } `json:"baseAsset"` + QuoteAsset struct { + Asset string `json:"asset"` + BorrowEnabled bool `json:"borrowEnabled"` + Free string `json:"free"` + Interest string `json:"interest"` + Locked string `json:"locked"` + NetAsset string `json:"netAsset"` + NetAssetOfBtc string `json:"netAssetOfBtc"` + RepayEnabled bool `json:"repayEnabled"` + TotalAsset string `json:"totalAsset"` + } `json:"quoteAsset"` + Symbol string `json:"symbol"` + IsolatedCreated bool `json:"isolatedCreated"` + Enabled bool `json:"enabled"` + MarginLevel string `json:"marginLevel"` + MarginLevelStatus string `json:"marginLevelStatus"` + MarginRatio string `json:"marginRatio"` + IndexPrice string `json:"indexPrice"` + LiquidatePrice string `json:"liquidatePrice"` + LiquidateRate string `json:"liquidateRate"` + TradeEnabled bool `json:"tradeEnabled"` +} + +// MarginIsolatedAccountInfoService response if symbols parameter is not sent +type MarginIsolatedAccountInfoResponseSymbols struct { + Assets []*MarginIsolatedAccountInfoAssets `json:"assets"` +} + +// MarginIsolatedAccountInfoService response if symbols parameter is sent type MarginIsolatedAccountInfoResponse struct { - Assets []struct { - BaseAsset struct { - Asset string `json:"asset"` - BorrowEnabled bool `json:"borrowEnabled"` - Free string `json:"free"` - Interest string `json:"interest"` - Locked string `json:"locked"` - NetAsset string `json:"netAsset"` - NetAssetOfBtc string `json:"netAssetOfBtc"` - RepayEnabled bool `json:"repayEnabled"` - TotalAsset string `json:"totalAsset"` - } `json:"baseAsset"` - QuoteAsset struct { - Asset string `json:"asset"` - BorrowEnabled bool `json:"borrowEnabled"` - Free string `json:"free"` - Interest string `json:"interest"` - Locked string `json:"locked"` - NetAsset string `json:"netAsset"` - NetAssetOfBtc string `json:"netAssetOfBtc"` - RepayEnabled bool `json:"repayEnabled"` - TotalAsset string `json:"totalAsset"` - } `json:"quoteAsset"` - Symbol string `json:"symbol"` - IsolatedCreated bool `json:"isolatedCreated"` - Enabled bool `json:"enabled"` - MarginLevel string `json:"marginLevel"` - MarginLevelStatus string `json:"marginLevelStatus"` - MarginRatio string `json:"marginRatio"` - IndexPrice string `json:"indexPrice"` - LiquidatePrice string `json:"liquidatePrice"` - LiquidateRate string `json:"liquidateRate"` - TradeEnabled bool `json:"tradeEnabled"` - } `json:"assets"` + Assets []*MarginIsolatedAccountInfoAssets `json:"assets"` + TotalAssetOfBtc string `json:"totalAssetOfBtc"` + TotalLiabilityOfBtc string `json:"totalLiabilityOfBtc"` + TotalNetAssetOfBtc string `json:"totalNetAssetOfBtc"` } // Disable Isolated Margin Account (TRADE) diff --git a/margin_test.go b/margin_test.go index c0e06ce..2f3a272 100644 --- a/margin_test.go +++ b/margin_test.go @@ -803,22 +803,24 @@ func (s *marginTestSuite) TestMarginAccountNewOrder() { NewClientOrderId("6gCrw2kRUAF9CvJDGP16IP"). TimeInForce("GTC"). OrderType("MARKET"). + NewOrderRespType("RESULT"). Do(context.Background()) s.r().NoError(err) - s.Equal("BTCUSDT", resp.Symbol) - s.Equal(int64(28), resp.OrderId) - s.Equal("6gCrw2kRUAF9CvJDGP16IP", resp.ClientOrderId) - s.Equal(uint64(1507725176595), resp.TransactTime) - s.Equal("1.00000000", resp.Price) - s.Equal("10.00000000", resp.OrigQty) - s.Equal("10.00000000", resp.ExecutedQty) - s.Equal("10.00000000", resp.CumulativeQuoteQty) - s.Equal("FILLED", resp.Status) - s.Equal("GTC", resp.TimeInForce) - s.Equal("MARKET", resp.Type) - s.True(resp.IsIsolated) - s.Equal("SELL", resp.Side) + a := resp.(*MarginAccountNewOrderResponseRESULT) + s.Equal("BTCUSDT", a.Symbol) + s.Equal(int64(28), a.OrderId) + s.Equal("6gCrw2kRUAF9CvJDGP16IP", a.ClientOrderId) + s.Equal(uint64(1507725176595), a.TransactTime) + s.Equal("1.00000000", a.Price) + s.Equal("10.00000000", a.OrigQty) + s.Equal("10.00000000", a.ExecutedQty) + s.Equal("10.00000000", a.CumulativeQuoteQty) + s.Equal("FILLED", a.Status) + s.Equal("GTC", a.TimeInForce) + s.Equal("MARKET", a.Type) + s.True(a.IsIsolated) + s.Equal("SELL", a.Side) } func (s *marginTestSuite) TestMarginAccountOpenOrder() { @@ -1438,11 +1440,12 @@ func (s *marginTestSuite) TestMarginIsolatedAccountInfo() { s.mockDo(data, nil) defer s.assertDo() - resp, err := s.client.NewMarginIsolatedAccountInfoService().Do(context.Background()) + resp, err := s.client.NewMarginIsolatedAccountInfoService().Symbols("LTCBTC, BTCUSDT").Do(context.Background()) s.r().NoError(err) - s.Len(resp.Assets, 1) + a := resp.(*MarginIsolatedAccountInfoResponseSymbols) + s.Len(a.Assets, 1) - baseAsset := resp.Assets[0].BaseAsset + baseAsset := a.Assets[0].BaseAsset s.Equal("BNB", baseAsset.Asset) s.True(baseAsset.BorrowEnabled) s.Equal("0.00000000", baseAsset.Free) @@ -1453,7 +1456,7 @@ func (s *marginTestSuite) TestMarginIsolatedAccountInfo() { s.True(baseAsset.RepayEnabled) s.Equal("0.00000000", baseAsset.TotalAsset) - quoteAsset := resp.Assets[0].QuoteAsset + quoteAsset := a.Assets[0].QuoteAsset s.Equal("USDT", quoteAsset.Asset) s.True(quoteAsset.BorrowEnabled) s.Equal("10000.00000000", quoteAsset.Free) @@ -1464,16 +1467,16 @@ func (s *marginTestSuite) TestMarginIsolatedAccountInfo() { s.True(quoteAsset.RepayEnabled) s.Equal("10000.00000000", quoteAsset.TotalAsset) - s.Equal("BNBUSDT", resp.Assets[0].Symbol) - s.True(resp.Assets[0].IsolatedCreated) - s.True(resp.Assets[0].Enabled) - s.Equal("0.00000", resp.Assets[0].MarginLevel) - s.Equal("EXCESSIVE", resp.Assets[0].MarginLevelStatus) - s.Equal("0.00000", resp.Assets[0].MarginRatio) - s.Equal("48.81190000", resp.Assets[0].IndexPrice) - s.Equal("0.00000000", resp.Assets[0].LiquidatePrice) - s.Equal("0.00000000", resp.Assets[0].LiquidateRate) - s.True(resp.Assets[0].TradeEnabled) + s.Equal("BNBUSDT", a.Assets[0].Symbol) + s.True(a.Assets[0].IsolatedCreated) + s.True(a.Assets[0].Enabled) + s.Equal("0.00000", a.Assets[0].MarginLevel) + s.Equal("EXCESSIVE", a.Assets[0].MarginLevelStatus) + s.Equal("0.00000", a.Assets[0].MarginRatio) + s.Equal("48.81190000", a.Assets[0].IndexPrice) + s.Equal("0.00000000", a.Assets[0].LiquidatePrice) + s.Equal("0.00000000", a.Assets[0].LiquidateRate) + s.True(a.Assets[0].TradeEnabled) } func (s *marginTestSuite) TestMarginIsolatedAccountLimit() { diff --git a/request.go b/request.go index 3d99cbc..1750704 100644 --- a/request.go +++ b/request.go @@ -56,23 +56,6 @@ func (r *request) setParams(m params) *request { return r } -// setFormParam set param with key/value to request form body -func (r *request) setFormParam(key string, value interface{}) *request { - if r.form == nil { - r.form = url.Values{} - } - r.form.Set(key, fmt.Sprintf("%v", value)) - return r -} - -// setFormParams set params with key/values to request form body -func (r *request) setFormParams(m params) *request { - for k, v := range m { - r.setFormParam(k, v) - } - return r -} - func (r *request) validate() (err error) { if r.query == nil { r.query = url.Values{} @@ -85,31 +68,3 @@ func (r *request) validate() (err error) { // RequestOption define option type for request type RequestOption func(*request) - -// WithRecvWindow set recvWindow param for the request -func WithRecvWindow(recvWindow int64) RequestOption { - return func(r *request) { - r.recvWindow = recvWindow - } -} - -// WithHeader set or add a header value to the request -func WithHeader(key, value string, replace bool) RequestOption { - return func(r *request) { - if r.header == nil { - r.header = http.Header{} - } - if replace { - r.header.Set(key, value) - } else { - r.header.Add(key, value) - } - } -} - -// WithHeaders set or replace the headers of the request -func WithHeaders(header http.Header) RequestOption { - return func(r *request) { - r.header = header.Clone() - } -} diff --git a/scripts/checks.sh b/scripts/checks.sh deleted file mode 100755 index 617a7b6..0000000 --- a/scripts/checks.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -set -e - -ACTION=$1 - -function format() { - echo "Running gofmt ..." - if [[ $1 == "-w" ]]; then - gofmt -w $(find . -type f -name '*.go' -not -path "./vendor/*") - elif [[ $1 == "-l" ]]; then - gofmt -l $(find . -type f -name '*.go' -not -path "./vendor/*") - else - test -z "$(gofmt -l $(find . -type f -name '*.go' -not -path "./vendor/*"))" - fi -} - -function lint() { - echo "Running golint ..." - go install golang.org/x/lint/golint - golint -set_exit_status ./... -} - -function vet() { - echo "Running go vet ..." - ( - go vet ./... - ) -} - -function unittest() { - echo "Running go test ..." - ( - go test -v -race -coverprofile=coverage.txt -covermode=atomic ./... - ) -} - -if [[ -z $ACTION ]]; then - format - # lint - vet - unittest -else - shift - $ACTION "$@" -fi diff --git a/test_client.go b/test_client.go index 3bfd25d..88a139d 100644 --- a/test_client.go +++ b/test_client.go @@ -3,7 +3,7 @@ package binance_connector import ( "bytes" "context" - "io/ioutil" + "io" "net/http" "net/url" "testing" @@ -90,7 +90,7 @@ func anyHTTPRequest() mock.AnythingOfTypeArgument { func newHTTPResponse(data []byte, statusCode int) *http.Response { return &http.Response{ - Body: ioutil.NopCloser(bytes.NewBuffer(data)), + Body: io.NopCloser(bytes.NewBuffer(data)), StatusCode: statusCode, } } diff --git a/websocket.go b/websocket.go index b7dd48f..e673351 100644 --- a/websocket.go +++ b/websocket.go @@ -18,6 +18,32 @@ type WsConfig struct { Endpoint string } +type WebsocketStreamClient struct { + Endpoint string + IsCombined bool +} + +func NewWebsocketStreamClient(isCombined bool, baseURL ...string) *WebsocketStreamClient { + // Set default base URL to production WS URL + url := "wss://stream.binance.com:9443" + + if len(baseURL) > 0 { + url = baseURL[0] + } + + // Append to baseURL based on whether the client is for combined streams or not + if isCombined { + url += "/stream?streams=" + } else { + url += "/ws" + } + + return &WebsocketStreamClient{ + Endpoint: url, + IsCombined: isCombined, + } +} + func newWsConfig(endpoint string) *WsConfig { return &WsConfig{ Endpoint: endpoint, @@ -56,7 +82,6 @@ var wsServe = func(cfg *WsConfig, handler WsHandler, errHandler ErrHandler) (don silent = true case <-doneCh: } - c.Close() }() for { _, message, err := c.ReadMessage() @@ -91,7 +116,6 @@ func keepAlive(c *websocket.Conn, timeout time.Duration) { } <-ticker.C if time.Since(lastResponse) > timeout { - c.Close() return } } diff --git a/websocket_service.go b/websocket_service.go index 0e75c3c..90154ac 100644 --- a/websocket_service.go +++ b/websocket_service.go @@ -7,13 +7,6 @@ import ( "time" "encoding/json" - stdjson "encoding/json" -) - -// Endpoints -const ( - baseWsMainURL = "wss://stream.binance.com:9443/ws" - baseCombinedMainURL = "wss://stream.binance.com:9443/stream?streams=" ) type PriceLevel struct { @@ -56,16 +49,6 @@ var ( WebsocketKeepalive = false ) -// getWsEndpoint return the base endpoint of the WS -func getWsEndpoint() string { - return baseWsMainURL -} - -// getCombinedEndpoint return the base endpoint of the combined stream -func getCombinedEndpoint() string { - return baseCombinedMainURL -} - // WsPartialDepthEvent define websocket partial depth book event type WsPartialDepthEvent struct { Symbol string @@ -78,14 +61,14 @@ type WsPartialDepthEvent struct { type WsPartialDepthHandler func(event *WsPartialDepthEvent) // WsPartialDepthServe serve websocket partial depth handler with a symbol, using 1sec updates -func WsPartialDepthServe(symbol string, levels string, handler WsPartialDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := fmt.Sprintf("%s/%s@depth%s", getWsEndpoint(), strings.ToLower(symbol), levels) +func (c *WebsocketStreamClient) WsPartialDepthServe(symbol string, levels string, handler WsPartialDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := fmt.Sprintf("%s/%s@depth%s", c.Endpoint, strings.ToLower(symbol), levels) return wsPartialDepthServe(endpoint, symbol, handler, errHandler) } // WsPartialDepthServe100Ms serve websocket partial depth handler with a symbol, using 100msec updates -func WsPartialDepthServe100Ms(symbol string, levels string, handler WsPartialDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := fmt.Sprintf("%s/%s@depth%s@100ms", getWsEndpoint(), strings.ToLower(symbol), levels) +func (c *WebsocketStreamClient) WsPartialDepthServe100Ms(symbol string, levels string, handler WsPartialDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := fmt.Sprintf("%s/%s@depth%s@100ms", c.Endpoint, strings.ToLower(symbol), levels) return wsPartialDepthServe(endpoint, symbol, handler, errHandler) } @@ -125,8 +108,8 @@ func wsPartialDepthServe(endpoint string, symbol string, handler WsPartialDepthH } // WsCombinedPartialDepthServe is similar to WsPartialDepthServe, but it for multiple symbols -func WsCombinedPartialDepthServe(symbolLevels map[string]string, handler WsPartialDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := getCombinedEndpoint() +func (c *WebsocketStreamClient) WsCombinedPartialDepthServe(symbolLevels map[string]string, handler WsPartialDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := c.Endpoint for s, l := range symbolLevels { endpoint += fmt.Sprintf("%s@depth%s", strings.ToLower(s), l) + "/" } @@ -143,7 +126,7 @@ func WsCombinedPartialDepthServe(symbolLevels map[string]string, handler WsParti symbol := strings.Split(stream, "@")[0] event.Symbol = strings.ToUpper(symbol) data := j.Get("data").MustMap() - event.LastUpdateID, _ = data["lastUpdateId"].(stdjson.Number).Int64() + event.LastUpdateID, _ = data["lastUpdateId"].(json.Number).Int64() bidsLen := len(data["bids"].([]interface{})) event.Bids = make([]Bid, bidsLen) for i := 0; i < bidsLen; i++ { @@ -172,14 +155,14 @@ func WsCombinedPartialDepthServe(symbolLevels map[string]string, handler WsParti type WsDepthHandler func(event *WsDepthEvent) // WsDepthServe serve websocket depth handler with a symbol, using 1sec updates -func WsDepthServe(symbol string, handler WsDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := fmt.Sprintf("%s/%s@depth", getWsEndpoint(), strings.ToLower(symbol)) +func (c *WebsocketStreamClient) WsDepthServe(symbol string, handler WsDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := fmt.Sprintf("%s/%s@depth", c.Endpoint, strings.ToLower(symbol)) return wsDepthServe(endpoint, handler, errHandler) } // WsDepthServe100Ms serve websocket depth handler with a symbol, using 100msec updates -func WsDepthServe100Ms(symbol string, handler WsDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := fmt.Sprintf("%s/%s@depth@100ms", getWsEndpoint(), strings.ToLower(symbol)) +func (c *WebsocketStreamClient) WsDepthServe100Ms(symbol string, handler WsDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := fmt.Sprintf("%s/%s@depth@100ms", c.Endpoint, strings.ToLower(symbol)) return wsDepthServe(endpoint, handler, errHandler) } @@ -233,8 +216,8 @@ type WsDepthEvent struct { } // WsCombinedDepthServe is similar to WsDepthServe, but it for multiple symbols -func WsCombinedDepthServe(symbols []string, handler WsDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := getCombinedEndpoint() +func (c *WebsocketStreamClient) WsCombinedDepthServe(symbols []string, handler WsDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := c.Endpoint for _, s := range symbols { endpoint += fmt.Sprintf("%s@depth", strings.ToLower(s)) + "/" } @@ -242,8 +225,8 @@ func WsCombinedDepthServe(symbols []string, handler WsDepthHandler, errHandler E return wsCombinedDepthServe(endpoint, handler, errHandler) } -func WsCombinedDepthServe100Ms(symbols []string, handler WsDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := getCombinedEndpoint() +func (c *WebsocketStreamClient) WsCombinedDepthServe100Ms(symbols []string, handler WsDepthHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := c.Endpoint for _, s := range symbols { endpoint += fmt.Sprintf("%s@depth@100ms", strings.ToLower(s)) + "/" } @@ -264,9 +247,9 @@ func wsCombinedDepthServe(endpoint string, handler WsDepthHandler, errHandler Er symbol := strings.Split(stream, "@")[0] event.Symbol = strings.ToUpper(symbol) data := j.Get("data").MustMap() - event.Time, _ = data["E"].(stdjson.Number).Int64() - event.LastUpdateID, _ = data["u"].(stdjson.Number).Int64() - event.FirstUpdateID, _ = data["U"].(stdjson.Number).Int64() + event.Time, _ = data["E"].(json.Number).Int64() + event.LastUpdateID, _ = data["u"].(json.Number).Int64() + event.FirstUpdateID, _ = data["U"].(json.Number).Int64() bidsLen := len(data["b"].([]interface{})) event.Bids = make([]Bid, bidsLen) for i := 0; i < bidsLen; i++ { @@ -295,8 +278,8 @@ func wsCombinedDepthServe(endpoint string, handler WsDepthHandler, errHandler Er type WsKlineHandler func(event *WsKlineEvent) // WsCombinedKlineServe is similar to WsKlineServe, but it handles multiple symbols with it interval -func WsCombinedKlineServe(symbolIntervalPair map[string]string, handler WsKlineHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := getCombinedEndpoint() +func (c *WebsocketStreamClient) WsCombinedKlineServe(symbolIntervalPair map[string]string, handler WsKlineHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := c.Endpoint for symbol, interval := range symbolIntervalPair { endpoint += fmt.Sprintf("%s@kline_%s", strings.ToLower(symbol), interval) + "/" } @@ -330,8 +313,8 @@ func WsCombinedKlineServe(symbolIntervalPair map[string]string, handler WsKlineH } // WsKlineServe serve websocket kline handler with a symbol and interval like 15m, 30s -func WsKlineServe(symbol string, interval string, handler WsKlineHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := fmt.Sprintf("%s/%s@kline_%s", getWsEndpoint(), strings.ToLower(symbol), interval) +func (c *WebsocketStreamClient) WsKlineServe(symbol string, interval string, handler WsKlineHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := fmt.Sprintf("%s/%s@kline_%s", c.Endpoint, strings.ToLower(symbol), interval) cfg := newWsConfig(endpoint) wsHandler := func(message []byte) { event := new(WsKlineEvent) @@ -377,8 +360,8 @@ type WsKline struct { type WsAggTradeHandler func(event *WsAggTradeEvent) // WsAggTradeServe serve websocket aggregate handler with a symbol -func WsAggTradeServe(symbol string, handler WsAggTradeHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := fmt.Sprintf("%s/%s@aggTrade", getWsEndpoint(), strings.ToLower(symbol)) +func (c *WebsocketStreamClient) WsAggTradeServe(symbol string, handler WsAggTradeHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := fmt.Sprintf("%s/%s@aggTrade", c.Endpoint, strings.ToLower(symbol)) cfg := newWsConfig(endpoint) wsHandler := func(message []byte) { event := new(WsAggTradeEvent) @@ -393,8 +376,8 @@ func WsAggTradeServe(symbol string, handler WsAggTradeHandler, errHandler ErrHan } // WsCombinedAggTradeServe is similar to WsAggTradeServe, but it handles multiple symbolx -func WsCombinedAggTradeServe(symbols []string, handler WsAggTradeHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := getCombinedEndpoint() +func (c *WebsocketStreamClient) WsCombinedAggTradeServe(symbols []string, handler WsAggTradeHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := c.Endpoint for s := range symbols { endpoint += fmt.Sprintf("%s@aggTrade", strings.ToLower(symbols[s])) + "/" } @@ -448,8 +431,8 @@ type WsTradeHandler func(event *WsTradeEvent) type WsCombinedTradeHandler func(event *WsCombinedTradeEvent) // WsTradeServe serve websocket handler with a symbol -func WsTradeServe(symbol string, handler WsTradeHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := fmt.Sprintf("%s/%s@trade", getWsEndpoint(), strings.ToLower(symbol)) +func (c *WebsocketStreamClient) WsTradeServe(symbol string, handler WsTradeHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := fmt.Sprintf("%s/%s@trade", c.Endpoint, strings.ToLower(symbol)) cfg := newWsConfig(endpoint) wsHandler := func(message []byte) { event := new(WsTradeEvent) @@ -463,8 +446,8 @@ func WsTradeServe(symbol string, handler WsTradeHandler, errHandler ErrHandler) return wsServe(cfg, wsHandler, errHandler) } -func WsCombinedTradeServe(symbols []string, handler WsCombinedTradeHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := getCombinedEndpoint() +func (c *WebsocketStreamClient) WsCombinedTradeServe(symbols []string, handler WsCombinedTradeHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := c.Endpoint for _, s := range symbols { endpoint += fmt.Sprintf("%s@trade/", strings.ToLower(s)) } @@ -592,8 +575,8 @@ type WsOCOOrder struct { type WsUserDataHandler func(event *WsUserDataEvent) // WsUserDataServe serve user data handler with listen key -func WsUserDataServe(listenKey string, handler WsUserDataHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := fmt.Sprintf("%s/%s", getWsEndpoint(), listenKey) +func (c *WebsocketStreamClient) WsUserDataServe(listenKey string, handler WsUserDataHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := fmt.Sprintf("%s/%s", c.Endpoint, listenKey) cfg := newWsConfig(endpoint) wsHandler := func(message []byte) { j, err := newJSON(message) @@ -648,12 +631,12 @@ func WsUserDataServe(listenKey string, handler WsUserDataHandler, errHandler Err return wsServe(cfg, wsHandler, errHandler) } -// WsMarketStatHandler handle websocket that push single market statistics for 24hr -type WsMarketStatHandler func(event *WsMarketStatEvent) +// WsMarketTickersStatHandler handle websocket that push single market statistics for 24hr +type WsMarketTickersStatHandler func(event *WsMarketTickerStatEvent) -// WsCombinedMarketStatServe is similar to WsMarketStatServe, but it handles multiple symbolx -func WsCombinedMarketStatServe(symbols []string, handler WsMarketStatHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := getCombinedEndpoint() +// WsCombinedMarketTickersStatServe is similar to WsMarketTickersStatServe, but it handles multiple symbols +func (c *WebsocketStreamClient) WsCombinedMarketTickersStatServe(symbols []string, handler WsMarketTickersStatHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := c.Endpoint for s := range symbols { endpoint += fmt.Sprintf("%s@ticker", strings.ToLower(symbols[s])) + "/" } @@ -674,7 +657,7 @@ func WsCombinedMarketStatServe(symbols []string, handler WsMarketStatHandler, er jsonData, _ := json.Marshal(data) - event := new(WsMarketStatEvent) + event := new(WsMarketTickerStatEvent) err = json.Unmarshal(jsonData, event) if err != nil { errHandler(err) @@ -688,12 +671,12 @@ func WsCombinedMarketStatServe(symbols []string, handler WsMarketStatHandler, er return wsServe(cfg, wsHandler, errHandler) } -// WsMarketStatServe serve websocket that push 24hr statistics for single market every second -func WsMarketStatServe(symbol string, handler WsMarketStatHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := fmt.Sprintf("%s/%s@ticker", getWsEndpoint(), strings.ToLower(symbol)) +// WsMarketTickersStatServe serve websocket that push 24hr statistics for single market every second +func (c *WebsocketStreamClient) WsMarketTickersStatServe(symbol string, handler WsMarketTickersStatHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := fmt.Sprintf("%s/%s@ticker", c.Endpoint, strings.ToLower(symbol)) cfg := newWsConfig(endpoint) wsHandler := func(message []byte) { - var event WsMarketStatEvent + var event WsMarketTickerStatEvent err := json.Unmarshal(message, &event) if err != nil { errHandler(err) @@ -704,15 +687,15 @@ func WsMarketStatServe(symbol string, handler WsMarketStatHandler, errHandler Er return wsServe(cfg, wsHandler, errHandler) } -// WsAllMarketsStatHandler handle websocket that push all markets statistics for 24hr -type WsAllMarketsStatHandler func(event WsAllMarketsStatEvent) +// WsAllMarketTickersStatHandler handle websocket that push all markets statistics for 24hr +type WsAllMarketTickersStatHandler func(event WsAllMarketTickersStatEvent) -// WsAllMarketsStatServe serve websocket that push 24hr statistics for all market every second -func WsAllMarketsStatServe(handler WsAllMarketsStatHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := fmt.Sprintf("%s/!ticker@arr", getWsEndpoint()) +// WsAllMarketTickersStatServe serve websocket that push 24hr statistics for all market every second +func (c *WebsocketStreamClient) WsAllMarketTickersStatServe(handler WsAllMarketTickersStatHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := fmt.Sprintf("%s/!ticker@arr", c.Endpoint) cfg := newWsConfig(endpoint) wsHandler := func(message []byte) { - var event WsAllMarketsStatEvent + var event WsAllMarketTickersStatEvent err := json.Unmarshal(message, &event) if err != nil { errHandler(err) @@ -723,11 +706,11 @@ func WsAllMarketsStatServe(handler WsAllMarketsStatHandler, errHandler ErrHandle return wsServe(cfg, wsHandler, errHandler) } -// WsAllMarketsStatEvent define array of websocket market statistics events -type WsAllMarketsStatEvent []*WsMarketStatEvent +// WsAllMarketTickersStatEvent define array of websocket market statistics events +type WsAllMarketTickersStatEvent []*WsMarketTickerStatEvent -// WsMarketStatEvent define websocket market statistics event -type WsMarketStatEvent struct { +// WsMarketTickerStatEvent define websocket market statistics event +type WsMarketTickerStatEvent struct { Event string `json:"e"` Time int64 `json:"E"` Symbol string `json:"s"` @@ -753,15 +736,15 @@ type WsMarketStatEvent struct { Count int64 `json:"n"` } -// WsAllMiniMarketsStatServeHandler handle websocket that push all mini-ticker market statistics for 24hr -type WsAllMiniMarketsStatServeHandler func(event WsAllMiniMarketsStatEvent) +// WsAllMarketMiniTickersStatServeHandler handle websocket that push all mini-ticker market statistics for 24hr +type WsAllMarketMiniTickersStatServeHandler func(event WsAllMarketMiniTickersStatEvent) -// WsAllMiniMarketsStatServe serve websocket that push mini version of 24hr statistics for all market every second -func WsAllMiniMarketsStatServe(handler WsAllMiniMarketsStatServeHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := fmt.Sprintf("%s/!miniTicker@arr", getWsEndpoint()) +// WsAllMarketMiniTickersStatServe serve websocket that push mini version of 24hr statistics for all market every second +func (c *WebsocketStreamClient) WsAllMarketMiniTickersStatServe(handler WsAllMarketMiniTickersStatServeHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := fmt.Sprintf("%s/!miniTicker@arr", c.Endpoint) cfg := newWsConfig(endpoint) wsHandler := func(message []byte) { - var event WsAllMiniMarketsStatEvent + var event WsAllMarketMiniTickersStatEvent err := json.Unmarshal(message, &event) if err != nil { errHandler(err) @@ -772,11 +755,11 @@ func WsAllMiniMarketsStatServe(handler WsAllMiniMarketsStatServeHandler, errHand return wsServe(cfg, wsHandler, errHandler) } -// WsAllMiniMarketsStatEvent define array of websocket market mini-ticker statistics events -type WsAllMiniMarketsStatEvent []*WsMiniMarketsStatEvent +// WsAllMarketMiniTickersStatEvent define array of websocket market mini-ticker statistics events +type WsAllMarketMiniTickersStatEvent []*WsMarketMiniStatEvent -// WsMiniMarketsStatEvent define websocket market mini-ticker statistics event -type WsMiniMarketsStatEvent struct { +// WsMarketMiniStatEvent define websocket market mini-ticker statistics event +type WsMarketMiniStatEvent struct { Event string `json:"e"` Time int64 `json:"E"` Symbol string `json:"s"` @@ -807,8 +790,8 @@ type WsCombinedBookTickerEvent struct { type WsBookTickerHandler func(event *WsBookTickerEvent) // WsBookTickerServe serve websocket that pushes updates to the best bid or ask price or quantity in real-time for a specified symbol. -func WsBookTickerServe(symbol string, handler WsBookTickerHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := fmt.Sprintf("%s/%s@bookTicker", getWsEndpoint(), strings.ToLower(symbol)) +func (c *WebsocketStreamClient) WsBookTickerServe(symbol string, handler WsBookTickerHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := fmt.Sprintf("%s/%s@bookTicker", c.Endpoint, strings.ToLower(symbol)) cfg := newWsConfig(endpoint) wsHandler := func(message []byte) { event := new(WsBookTickerEvent) @@ -823,8 +806,8 @@ func WsBookTickerServe(symbol string, handler WsBookTickerHandler, errHandler Er } // WsCombinedBookTickerServe is similar to WsBookTickerServe, but it is for multiple symbols -func WsCombinedBookTickerServe(symbols []string, handler WsBookTickerHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { - endpoint := baseCombinedMainURL +func (c *WebsocketStreamClient) WsCombinedBookTickerServe(symbols []string, handler WsBookTickerHandler, errHandler ErrHandler) (doneCh, stopCh chan struct{}, err error) { + endpoint := c.Endpoint for _, s := range symbols { endpoint += fmt.Sprintf("%s@bookTicker", strings.ToLower(s)) + "/" } diff --git a/websocket_service_test.go b/websocket_service_test.go index 611d1bb..6c209d7 100644 --- a/websocket_service_test.go +++ b/websocket_service_test.go @@ -66,11 +66,13 @@ func (s *websocketTestSuite) assertUserDataEvent(e, a *WsUserDataEvent) { } func (s *websocketTestSuite) testWsUserDataServe(data []byte, expectedEvent *WsUserDataEvent) { + websocketStreamClient := NewWebsocketStreamClient(false, "wss://testnet.binance.vision") + fakeErrMsg := "fake error" s.mockWsServe(data, errors.New(fakeErrMsg)) defer s.assertWsServe() - doneC, stopC, err := WsUserDataServe("fakeListenKey", func(event *WsUserDataEvent) { + doneC, stopC, err := websocketStreamClient.WsUserDataServe("listenKey", func(event *WsUserDataEvent) { s.assertUserDataEvent(expectedEvent, event) }, func(err error) { s.r().EqualError(err, fakeErrMsg) @@ -156,6 +158,8 @@ func (s *websocketTestSuite) TestWsUserDataServeAccountUpdate() { } func (s *websocketTestSuite) TestWsTradeServe() { + websocketStreamClient := NewWebsocketStreamClient(false, "wss://testnet.binance.vision") + data := []byte(`{ "e": "trade", "E": 123456789, @@ -173,7 +177,7 @@ func (s *websocketTestSuite) TestWsTradeServe() { s.mockWsServe(data, errors.New(fakeErrMsg)) defer s.assertWsServe() - doneC, stopC, err := WsTradeServe("BNBBTC", func(event *WsTradeEvent) { + doneC, stopC, err := websocketStreamClient.WsTradeServe("BNBBTC", func(event *WsTradeEvent) { e := &WsTradeEvent{ Event: "trade", Time: 123456789, @@ -209,7 +213,8 @@ func (s *websocketTestSuite) assertWsTradeEventEqual(e, a *WsTradeEvent) { r.Equal(e.IsBuyerMaker, a.IsBuyerMaker, "IsBuyerMaker") } -func (s *websocketTestSuite) TestWsAllMiniMarketsStatServe() { +func (s *websocketTestSuite) TestWsAllMarketMiniTickersStatServe() { + websocketStreamClient := NewWebsocketStreamClient(false, "wss://testnet.binance.vision") data := []byte(`[{ "e": "24hrMiniTicker", "E": 1523658017154, @@ -235,9 +240,9 @@ func (s *websocketTestSuite) TestWsAllMiniMarketsStatServe() { s.mockWsServe(data, errors.New(fakeErrMsg)) defer s.assertWsServe() - doneC, stopC, err := WsAllMiniMarketsStatServe(func(event WsAllMiniMarketsStatEvent) { - e := WsAllMiniMarketsStatEvent{ - &WsMiniMarketsStatEvent{ + doneC, stopC, err := websocketStreamClient.WsAllMarketMiniTickersStatServe(func(event WsAllMarketMiniTickersStatEvent) { + e := WsAllMarketMiniTickersStatEvent{ + &WsMarketMiniStatEvent{ Event: "24hrMiniTicker", Time: 1523658017154, Symbol: "BNBBTC", @@ -248,7 +253,7 @@ func (s *websocketTestSuite) TestWsAllMiniMarketsStatServe() { BaseVolume: "3479863.89000000", QuoteVolume: "5725.90587704", }, - &WsMiniMarketsStatEvent{ + &WsMarketMiniStatEvent{ Event: "24hrMiniTicker", Time: 1523658017133, Symbol: "BNBETH", @@ -260,7 +265,7 @@ func (s *websocketTestSuite) TestWsAllMiniMarketsStatServe() { QuoteVolume: "11873.11095682", }, } - s.assertWsAllMiniMarketsStatEventEqual(e, event) + s.assertWsAllMarketMiniTickersStatEventEqual(e, event) }, func(err error) { s.r().EqualError(err, fakeErrMsg) }) @@ -269,13 +274,13 @@ func (s *websocketTestSuite) TestWsAllMiniMarketsStatServe() { <-doneC } -func (s *websocketTestSuite) assertWsAllMiniMarketsStatEventEqual(e, a WsAllMiniMarketsStatEvent) { +func (s *websocketTestSuite) assertWsAllMarketMiniTickersStatEventEqual(e, a WsAllMarketMiniTickersStatEvent) { for i := range e { - s.assertWsMiniMarketsStatEventEqual(e[i], a[i]) + s.assertWsMarketMiniTickersStatEventEqual(e[i], a[i]) } } -func (s *websocketTestSuite) assertWsMiniMarketsStatEventEqual(e, a *WsMiniMarketsStatEvent) { +func (s *websocketTestSuite) assertWsMarketMiniTickersStatEventEqual(e, a *WsMarketMiniStatEvent) { r := s.r() r.Equal(e.Event, a.Event, "Event") r.Equal(e.Time, a.Time, "Time") @@ -289,6 +294,7 @@ func (s *websocketTestSuite) assertWsMiniMarketsStatEventEqual(e, a *WsMiniMarke } func (s *websocketTestSuite) TestBookTickerServe() { + websocketStreamClient := NewWebsocketStreamClient(false, "wss://testnet.binance.vision") data := []byte(`{ "u":17242169, "s":"BTCUSD_200626", @@ -301,7 +307,7 @@ func (s *websocketTestSuite) TestBookTickerServe() { s.mockWsServe(data, errors.New(fakeErrMsg)) defer s.assertWsServe() - doneC, stopC, err := WsBookTickerServe("BTCUSD_200626", func(event *WsBookTickerEvent) { + doneC, stopC, err := websocketStreamClient.WsBookTickerServe("BTCUSD_200626", func(event *WsBookTickerEvent) { e := &WsBookTickerEvent{ UpdateID: 17242169, Symbol: "BTCUSD_200626", @@ -332,6 +338,7 @@ func (s *websocketTestSuite) assertWsBookTickerEvent(e, a *WsBookTickerEvent) { } func (s *websocketTestSuite) TestDepthServe() { + websocketStreamClient := NewWebsocketStreamClient(false, "wss://testnet.binance.vision") data := []byte(`{ "e": "depthUpdate", "E": 1499404630606, @@ -367,7 +374,7 @@ func (s *websocketTestSuite) TestDepthServe() { s.mockWsServe(data, errors.New(fakeErrMsg)) defer s.assertWsServe() - doneC, stopC, err := WsDepthServe("ETHBTC", func(event *WsDepthEvent) { + doneC, stopC, err := websocketStreamClient.WsDepthServe("ETHBTC", func(event *WsDepthEvent) { e := &WsDepthEvent{ Event: "depthUpdate", Time: 1499404630606, @@ -422,6 +429,7 @@ func (s *websocketTestSuite) assertWsDepthEventEqual(e, a *WsDepthEvent) { } func (s *websocketTestSuite) TestKlineServe() { + websocketStreamClient := NewWebsocketStreamClient(false, "wss://testnet.binance.vision") data := []byte(`{ "e": "kline", "E": 1499404907056, @@ -450,7 +458,7 @@ func (s *websocketTestSuite) TestKlineServe() { s.mockWsServe(data, errors.New(fakeErrMsg)) defer s.assertWsServe() - doneC, stopC, err := WsKlineServe("ETHBTC", "1m", func(event *WsKlineEvent) { + doneC, stopC, err := websocketStreamClient.WsKlineServe("ETHBTC", "1m", func(event *WsKlineEvent) { e := &WsKlineEvent{ Event: "kline", Time: 1499404907056, @@ -508,6 +516,7 @@ func (s *websocketTestSuite) assertWsKlineEventEqual(e, a *WsKlineEvent) { } func (s *websocketTestSuite) TestWsAggTradeServe() { + websocketStreamClient := NewWebsocketStreamClient(false, "wss://testnet.binance.vision") data := []byte(`{ "e": "aggTrade", "E": 1499405254326, @@ -525,7 +534,7 @@ func (s *websocketTestSuite) TestWsAggTradeServe() { s.mockWsServe(data, errors.New(fakeErrMsg)) defer s.assertWsServe() - doneC, stopC, err := WsAggTradeServe("ETHBTC", func(event *WsAggTradeEvent) { + doneC, stopC, err := websocketStreamClient.WsAggTradeServe("ETHBTC", func(event *WsAggTradeEvent) { e := &WsAggTradeEvent{ Event: "aggTrade", Time: 1499405254326, @@ -561,7 +570,8 @@ func (s *websocketTestSuite) assertWsAggTradeEventEqual(e, a *WsAggTradeEvent) { r.Equal(e.IsBuyerMaker, a.IsBuyerMaker, "IsBuyerMaker") } -func (s *websocketTestSuite) TestWsAllMarketsStatServe() { +func (s *websocketTestSuite) TestWsAllMarketTickersStatServe() { + websocketStreamClient := NewWebsocketStreamClient(false, "wss://testnet.binance.vision") data := []byte(`[{ "e": "24hrTicker", "E": 123456789, @@ -615,9 +625,9 @@ func (s *websocketTestSuite) TestWsAllMarketsStatServe() { s.mockWsServe(data, errors.New(fakeErrMsg)) defer s.assertWsServe() - doneC, stopC, err := WsAllMarketsStatServe(func(event WsAllMarketsStatEvent) { - e := WsAllMarketsStatEvent{ - &WsMarketStatEvent{ + doneC, stopC, err := websocketStreamClient.WsAllMarketTickersStatServe(func(event WsAllMarketTickersStatEvent) { + e := WsAllMarketTickersStatEvent{ + &WsMarketTickerStatEvent{ Event: "24hrTicker", Time: 123456789, Symbol: "BNBBTC", @@ -642,7 +652,7 @@ func (s *websocketTestSuite) TestWsAllMarketsStatServe() { LastID: 18150, Count: 18151, }, - &WsMarketStatEvent{ + &WsMarketTickerStatEvent{ Event: "24hrTicker", Time: 123456789, Symbol: "ETHBTC", @@ -668,7 +678,7 @@ func (s *websocketTestSuite) TestWsAllMarketsStatServe() { Count: 18151, }, } - s.assertWsAllMarketsStatEventEqual(e, event) + s.assertWsAllMarketTickersStatEventEqual(e, event) }, func(err error) { s.r().EqualError(err, fakeErrMsg) }) @@ -677,13 +687,13 @@ func (s *websocketTestSuite) TestWsAllMarketsStatServe() { <-doneC } -func (s *websocketTestSuite) assertWsAllMarketsStatEventEqual(e, a WsAllMarketsStatEvent) { +func (s *websocketTestSuite) assertWsAllMarketTickersStatEventEqual(e, a WsAllMarketTickersStatEvent) { for i := range e { - s.assertWsMarketStatEventEqual(e[i], a[i]) + s.assertWsMarketTickerStatEventEqual(e[i], a[i]) } } -func (s *websocketTestSuite) assertWsMarketStatEventEqual(e, a *WsMarketStatEvent) { +func (s *websocketTestSuite) assertWsMarketTickerStatEventEqual(e, a *WsMarketTickerStatEvent) { r := s.r() r.Equal(e.Event, a.Event, "Event") r.Equal(e.Time, a.Time, "Time")