@@ -77,58 +77,56 @@ func (msg subscribeMsgType2) String() string {
77
77
}
78
78
79
79
func (s * Stream ) handleConnect () {
80
- // subscribe to channels
81
- if len (s .Subscriptions ) == 0 {
82
- return
83
- }
84
- // bridge bbgo channels to coinbase channels
85
- // auth required: level2, full, user
86
80
subProductsMap := make (map [types.Channel ][]string )
87
- allProductsMap := make (map [string ]struct {})
88
- for _ , sub := range s .Subscriptions {
89
- localSymbol := toLocalSymbol (sub .Symbol )
90
- switch sub .Channel {
91
- case types .BookChannel :
92
- // bridge to level2 channel, which provides order book snapshot and book updates
93
- logStream .Infof ("bridge %s to level2_batch channel (%s)" , sub .Channel , sub .Symbol )
94
- subProductsMap [level2BatchChannel ] = append (subProductsMap [level2Channel ], localSymbol )
95
- case types .MarketTradeChannel :
96
- // full: all orders/trades on Coinbase Exchange
97
- if ! s .PublicOnly {
98
- panic ("subscribe to market trade channel for a public stream is not allowed" )
99
- }
100
- subProductsMap [matchesChannel ] = append (subProductsMap [matchesChannel ], localSymbol )
101
- logStream .Infof ("bridge %s to %s(%s)" , sub .Channel , matchesChannel , localSymbol )
102
- case types .BookTickerChannel :
103
- // ticker channel provides feeds on best bid/ask prices
104
- subProductsMap [tickerChannel ] = append (subProductsMap [tickerChannel ], localSymbol )
105
- logStream .Infof ("bridge %s to %s(%s)" , sub .Channel , tickerChannel , localSymbol )
106
- case types .KLineChannel :
107
- // TODO: add support to kline channel
108
- case types .AggTradeChannel , types .ForceOrderChannel , types .MarkPriceChannel , types .LiquidationOrderChannel , types .ContractInfoChannel :
109
- logStream .Warnf ("coinbase stream does not support subscription to %s, skipped" , sub .Channel )
110
- default :
111
- // rfqMatchChannel allow empty symbol
112
- if sub .Channel != rfqMatchChannel && sub .Channel != statusChannel && len (sub .Symbol ) == 0 {
113
- logStream .Warnf ("do not support subscription to %s without symbol, skipped" , sub .Channel )
114
- continue
115
- }
116
- subProductsMap [sub .Channel ] = append (subProductsMap [sub .Channel ], localSymbol )
117
- }
118
- }
119
81
120
- for _ , products := range subProductsMap {
121
- for _ , product := range products {
122
- allProductsMap [product ] = struct {}{}
123
- }
124
- }
125
82
// user data strea, subscribe to user channel for the user order/trade updates
126
83
if ! s .PublicOnly {
127
84
if ! s .authEnabled {
128
85
panic ("user channel requires authentication" )
129
86
}
130
- for product := range allProductsMap {
131
- subProductsMap [userChannel ] = append (subProductsMap [userChannel ], product )
87
+ // subscribe private symbols to user channel
88
+ // Once subscribe to the user channel, it will receive events for the following types:
89
+ // - order life cycle events: receive, open, done, change, activate(for stop orders)
90
+ // - order match
91
+ subProductsMap [userChannel ] = append (subProductsMap [userChannel ], s .privateChannelSymbols ... )
92
+ } else {
93
+ // market data stream: subscribe to channels
94
+ if len (s .Subscriptions ) == 0 {
95
+ return
96
+ }
97
+ // bridge bbgo channels to coinbase channels
98
+ // auth required: level2, full, user
99
+ for _ , sub := range s .Subscriptions {
100
+ localSymbol := toLocalSymbol (sub .Symbol )
101
+ switch sub .Channel {
102
+ case types .BookChannel :
103
+ // bridge to level2 channel, which provides order book snapshot and book updates
104
+ logStream .Infof ("bridge %s to level2_batch channel (%s)" , sub .Channel , sub .Symbol )
105
+ subProductsMap [level2BatchChannel ] = append (subProductsMap [level2Channel ], localSymbol )
106
+ case types .MarketTradeChannel :
107
+ // matches: all trades
108
+ if ! s .PublicOnly {
109
+ panic ("subscribe to market trade channel for a public stream is not allowed" )
110
+ }
111
+ subProductsMap [matchesChannel ] = append (subProductsMap [matchesChannel ], localSymbol )
112
+ logStream .Infof ("bridge %s to %s(%s)" , sub .Channel , matchesChannel , localSymbol )
113
+ case types .BookTickerChannel :
114
+ // ticker channel provides feeds on best bid/ask prices
115
+ subProductsMap [tickerChannel ] = append (subProductsMap [tickerChannel ], localSymbol )
116
+ logStream .Infof ("bridge %s to %s(%s)" , sub .Channel , tickerChannel , localSymbol )
117
+ case types .KLineChannel :
118
+ // TODO: add support to kline channel
119
+ // kline stream is available on Advanced Trade API only: https://docs.cdp.coinbase.com/advanced-trade/docs/ws-channels#candles-channel
120
+ case types .AggTradeChannel , types .ForceOrderChannel , types .MarkPriceChannel , types .LiquidationOrderChannel , types .ContractInfoChannel :
121
+ logStream .Warnf ("coinbase stream does not support subscription to %s, skipped" , sub .Channel )
122
+ default :
123
+ // rfqMatchChannel allow empty symbol
124
+ if sub .Channel != rfqMatchChannel && sub .Channel != statusChannel && len (sub .Symbol ) == 0 {
125
+ logStream .Warnf ("do not support subscription to %s without symbol, skipped" , sub .Channel )
126
+ continue
127
+ }
128
+ subProductsMap [sub .Channel ] = append (subProductsMap [sub .Channel ], localSymbol )
129
+ }
132
130
}
133
131
}
134
132
@@ -254,13 +252,15 @@ func (s *Stream) handleConnect() {
254
252
ProductIDs : productIDs ,
255
253
},
256
254
},
257
-
258
- authMsg : authMsg {
255
+ }
256
+ if v , _ := subCmd .(subscribeMsgType1 ); s .authEnabled {
257
+ v .authMsg = authMsg {
259
258
Signature : signature ,
260
259
Key : s .apiKey ,
261
260
Passphrase : s .passphrase ,
262
261
Timestamp : ts ,
263
- },
262
+ }
263
+ subCmd = v
264
264
}
265
265
}
266
266
subCmds = append (subCmds , subCmd )
@@ -277,11 +277,14 @@ func (s *Stream) handleConnect() {
277
277
s .clearSequenceNumber ()
278
278
s .clearWorkingOrders ()
279
279
go func () {
280
+ if s .PublicOnly {
281
+ return
282
+ }
280
283
ctx , cancel := context .WithTimeout (context .Background (), time .Second * 10 )
281
284
defer cancel ()
282
285
283
286
// emit balance snapshot on connection
284
- // query account balances
287
+ // query account balances for user stream
285
288
balances , err := s .exchange .QueryAccountBalances (ctx )
286
289
if err != nil {
287
290
logStream .WithError (err ).Warn ("failed to query account balances, the balance snapshot is initialized with empty balances" )
@@ -305,6 +308,7 @@ func (s *Stream) handleConnect() {
305
308
}
306
309
s .updateWorkingOrders (openOrders ... )
307
310
}
311
+
308
312
}()
309
313
}
310
314
@@ -314,7 +318,10 @@ func (s *Stream) handleDisconnect() {
314
318
s .clearWorkingOrders ()
315
319
}
316
320
321
+ // Local Handlers: handlers that deal with the messages from the Coinbase WebSocket
322
+
317
323
// ticker update (real-time price update when there is a match)
324
+ // To receive ticker messages, you need to subscribe bbgo BookTickerChannel
318
325
func (s * Stream ) handleTickerMessage (msg * TickerMessage ) {
319
326
// ignore outdated messages
320
327
if ! s .checkAndUpdateSequenceNumber (msg .Type , msg .ProductID , msg .Sequence ) {
@@ -331,6 +338,7 @@ func (s *Stream) handleTickerMessage(msg *TickerMessage) {
331
338
}
332
339
333
340
// matches channel (or match message from full/user channel)
341
+ // To receive match messages, you need to subscribe bbgo MarketTradeChannel on a public stream
334
342
func (s * Stream ) handleMatchMessage (msg * MatchMessage ) {
335
343
if msg .Type == "last_match" {
336
344
// TODO: fetch missing trades from the REST API and emit them
@@ -350,7 +358,9 @@ func (s *Stream) handleMatchMessage(msg *MatchMessage) {
350
358
}
351
359
}
352
360
353
- // level2 handlers
361
+ // level2 handlers: order book snapshot and order book updates
362
+ // To receive order book updates, you need to subscribe bbgo BookChannel
363
+ // level2 order book snapshot handler
354
364
func (s * Stream ) handleOrderBookSnapshotMessage (msg * OrderBookSnapshotMessage ) {
355
365
symbol := toGlobalSymbol (msg .ProductID )
356
366
var bids types.PriceVolumeSlice
@@ -382,6 +392,7 @@ func (s *Stream) handleOrderBookSnapshotMessage(msg *OrderBookSnapshotMessage) {
382
392
s .EmitBookSnapshot (book )
383
393
}
384
394
395
+ // level2 order book update handler
385
396
func (s * Stream ) handleOrderbookUpdateMessage (msg * OrderBookUpdateMessage ) {
386
397
var bids types.PriceVolumeSlice
387
398
var asks types.PriceVolumeSlice
@@ -422,7 +433,8 @@ func (s *Stream) handleOrderbookUpdateMessage(msg *OrderBookUpdateMessage) {
422
433
}
423
434
}
424
435
425
- // order update (full or user channel)
436
+ // full or user channel: all order updates
437
+ // a private stream will automatically subscribe to the user channel
426
438
func (s * Stream ) handleReceivedMessage (msg * ReceivedMessage ) {
427
439
if ! s .checkAndUpdateSequenceNumber (msg .Type , msg .ProductID , msg .Sequence ) {
428
440
return
0 commit comments