@@ -26,21 +26,16 @@ type MonitorCoinbase struct {
2626 productIdsPolling []string
2727 assetQuotesResponse []c.AssetQuote // Asset quotes filtered to the productIds set in input.productIds
2828 assetQuotesCache map [string ]* c.AssetQuote // Asset quotes for all assets retrieved at least once
29- chanStreamUpdateQuotePrice chan messageUpdate [c.QuotePrice ]
30- chanStreamUpdateQuoteExtended chan messageUpdate [c.QuoteExtended ]
31- chanStreamUpdateExchange chan messageUpdate [c.Exchange ]
32- chanPollUpdateAssetQuote chan messageUpdate [c.AssetQuote ]
29+ chanStreamUpdateQuotePrice chan c. MessageUpdate [c.QuotePrice ]
30+ chanStreamUpdateQuoteExtended chan c. MessageUpdate [c.QuoteExtended ]
31+ chanStreamUpdateExchange chan c. MessageUpdate [c.Exchange ]
32+ chanPollUpdateAssetQuote chan c. MessageUpdate [c.AssetQuote ]
3333 mu sync.RWMutex
3434 ctx context.Context
3535 cancel context.CancelFunc
3636 isStarted bool
3737}
3838
39- type messageUpdate [T any ] struct {
40- data T
41- productId string
42- }
43-
4439type input struct {
4540 productIds []string
4641 symbolsUnderlying []string
@@ -64,17 +59,22 @@ func NewMonitorCoinbase(config Config, opts ...Option) *MonitorCoinbase {
6459 monitor := & MonitorCoinbase {
6560 assetQuotesCache : make (map [string ]* c.AssetQuote ),
6661 assetQuotesResponse : make ([]c.AssetQuote , 0 ),
67- chanStreamUpdateQuotePrice : make (chan messageUpdate [c.QuotePrice ]),
68- chanStreamUpdateQuoteExtended : make (chan messageUpdate [c.QuoteExtended ]),
69- chanStreamUpdateExchange : make (chan messageUpdate [c.Exchange ]),
70- chanPollUpdateAssetQuote : make (chan messageUpdate [c.AssetQuote ]),
62+ chanStreamUpdateQuotePrice : make (chan c. MessageUpdate [c.QuotePrice ]),
63+ chanStreamUpdateQuoteExtended : make (chan c. MessageUpdate [c.QuoteExtended ]),
64+ chanStreamUpdateExchange : make (chan c. MessageUpdate [c.Exchange ]),
65+ chanPollUpdateAssetQuote : make (chan c. MessageUpdate [c.AssetQuote ]),
7166 poller : poller .NewPoller (ctx , unaryAPI ),
7267 unaryAPI : unaryAPI ,
7368 ctx : ctx ,
7469 cancel : cancel ,
7570 }
7671
77- monitor .streamer = streamer .NewStreamer (ctx , monitor .chanStreamUpdateQuotePrice , monitor .chanStreamUpdateQuoteExtended )
72+ streamerConfig := streamer.StreamerConfig {
73+ ChanStreamUpdateQuotePrice : monitor .chanStreamUpdateQuotePrice ,
74+ ChanStreamUpdateQuoteExtended : monitor .chanStreamUpdateQuoteExtended ,
75+ }
76+
77+ monitor .streamer = streamer .NewStreamer (ctx , streamerConfig )
7878
7979 for _ , opt := range opts {
8080 opt (monitor )
@@ -141,7 +141,7 @@ func (m *MonitorCoinbase) SetSymbols(productIds []string) {
141141 m .updateAssetQuotesCache (m .assetQuotesResponse )
142142
143143 // Coinbase steaming API for CBE (spot) only and not CDE (futures)
144- m .streamer .SetSymbolsAndUpdateSubscriptions (m .productIds ) // TODO: update to return and handle error
144+ m .streamer .SetSymbolsAndUpdateSubscriptions (m .productIdsStreaming ) // TODO: update to return and handle error
145145
146146}
147147
@@ -168,6 +168,8 @@ func (m *MonitorCoinbase) Start() error {
168168 return err
169169 }
170170
171+ go m .handleUpdates ()
172+
171173 m .isStarted = true
172174
173175 return nil
@@ -191,7 +193,7 @@ func (m *MonitorCoinbase) updateAssetQuotesCache(assetQuotes []c.AssetQuote) {
191193}
192194
193195func isStreamingProductId (productId string ) bool {
194- return ! strings .HasSuffix (productId , "-CDE" )
196+ return ! strings .HasSuffix (productId , "-CDE" ) && ! strings . HasPrefix ( productId , "CDE" )
195197}
196198
197199func partitionProductIds (productIds []string ) (productIdsStreaming []string , productIdsPolling []string ) {
@@ -217,49 +219,47 @@ func mergeAndDeduplicateProductIds(symbolsA, symbolsB []string) []string {
217219 return slices .Compact (merged )
218220}
219221
220- func (m * MonitorCoinbase ) handleStreamPriceUpdates () {
221- go func () {
222- for {
223- select {
224- case updateMessage := <- m .chanStreamUpdateQuotePrice :
225-
226- var assetQuote * c.AssetQuote
227- var exists bool
228-
229- // Check if cache exists and values have changed before acquiring write lock
230- m .mu .RLock ()
231- defer m .mu .RUnlock ()
232-
233- assetQuote , exists = m .assetQuotesCache [updateMessage .productId ]
234-
235- if ! exists {
236- // If product id does not exist in cache, skip update
237- // TODO: log product not found in cache - should not happen
238- continue
239- }
240-
241- // Skip update if price has not changed
242- if assetQuote .QuotePrice .Price == updateMessage .data .Price {
243- continue
244- }
245- m .mu .RUnlock ()
246-
247- // Price is different so update cache
248- m .mu .Lock ()
249- defer m .mu .Unlock ()
250-
251- assetQuote .QuotePrice .Price = updateMessage .data .Price
252- assetQuote .QuotePrice .Change = updateMessage .data .Change
253- assetQuote .QuotePrice .ChangePercent = updateMessage .data .ChangePercent
254- assetQuote .QuotePrice .PriceDayHigh = updateMessage .data .PriceDayHigh
255- assetQuote .QuotePrice .PriceDayLow = updateMessage .data .PriceDayLow
256- assetQuote .QuotePrice .PriceOpen = updateMessage .data .PriceOpen
257- assetQuote .QuotePrice .PricePrevClose = updateMessage .data .PricePrevClose
258-
259- case <- m .ctx .Done ():
260- return
261- default :
222+ func (m * MonitorCoinbase ) handleUpdates () {
223+ for {
224+ select {
225+ case updateMessage := <- m .chanStreamUpdateQuotePrice :
226+
227+ var assetQuote * c.AssetQuote
228+ var exists bool
229+
230+ // Check if cache exists and values have changed before acquiring write lock
231+ m .mu .RLock ()
232+ defer m .mu .RUnlock ()
233+
234+ assetQuote , exists = m .assetQuotesCache [updateMessage .ID ]
235+
236+ if ! exists {
237+ // If product id does not exist in cache, skip update
238+ // TODO: log product not found in cache - should not happen
239+ continue
262240 }
241+
242+ // Skip update if price has not changed
243+ if assetQuote .QuotePrice .Price == updateMessage .Data .Price {
244+ continue
245+ }
246+ m .mu .RUnlock ()
247+
248+ // Price is different so update cache
249+ m .mu .Lock ()
250+ defer m .mu .Unlock ()
251+
252+ assetQuote .QuotePrice .Price = updateMessage .Data .Price
253+ assetQuote .QuotePrice .Change = updateMessage .Data .Change
254+ assetQuote .QuotePrice .ChangePercent = updateMessage .Data .ChangePercent
255+ assetQuote .QuotePrice .PriceDayHigh = updateMessage .Data .PriceDayHigh
256+ assetQuote .QuotePrice .PriceDayLow = updateMessage .Data .PriceDayLow
257+ assetQuote .QuotePrice .PriceOpen = updateMessage .Data .PriceOpen
258+ assetQuote .QuotePrice .PricePrevClose = updateMessage .Data .PricePrevClose
259+
260+ case <- m .ctx .Done ():
261+ return
262+ default :
263263 }
264- }()
264+ }
265265}
0 commit comments