66using Binance . Net . Interfaces ;
77using Binance . Net . Interfaces . Clients ;
88using Binance . Net . Objects . Models . Spot ;
9+ using Binance . Net . Objects . Models . Futures ;
910using BinanceBot . Market . Domain ;
1011using CryptoExchange . Net . Objects ;
1112using CryptoExchange . Net . Sockets ;
@@ -80,37 +81,78 @@ public async Task BuildAsync(MarketDepth marketDepth, short orderBookDepth = 10,
8081 _logger . Debug ( $ "1: Opening WebSocket stream for { marketDepth . Symbol } ") ;
8182
8283 var updateIntervalMs = updateInterval . HasValue ? ( int ) updateInterval . Value . TotalMilliseconds : ( int ) _defaultUpdateInterval . TotalMilliseconds ;
83- var subscriptionResult = await _webSocketClient . SpotStreams . SubscribeToOrderBookUpdatesAsync (
84- marketDepth . Symbol , updateIntervalMs ,
85- data => OnDepthUpdate ( marketDepth , data ) ,
86- ct )
87- . ConfigureAwait ( false ) ;
84+
85+ CallResult < UpdateSubscription > subscriptionResult ;
86+
87+ switch ( marketDepth . Symbol . ContractType )
88+ {
89+ case ContractType . Spot :
90+ subscriptionResult = await _webSocketClient . SpotStreams . SubscribeToOrderBookUpdatesAsync (
91+ marketDepth . Symbol . FullName , updateIntervalMs ,
92+ data => OnDepthUpdateSpot ( marketDepth , data ) ,
93+ ct )
94+ . ConfigureAwait ( false ) ;
95+ break ;
96+ case ContractType . Perpetual :
97+ subscriptionResult = await _webSocketClient . UsdFuturesStreams . SubscribeToOrderBookUpdatesAsync (
98+ marketDepth . Symbol . FullName , updateIntervalMs ,
99+ data => OnDepthUpdatePerp ( marketDepth , data ) ,
100+ ct )
101+ . ConfigureAwait ( false ) ;
102+ break ;
103+ default :
104+ throw new ArgumentOutOfRangeException ( nameof ( marketDepth . Symbol . ContractType ) , "Unknown contract type." ) ;
105+ }
88106
89107 if ( ! subscriptionResult . Success || subscriptionResult . Data == null )
90108 throw new InvalidOperationException ( $ "Failed to subscribe to order book updates: { subscriptionResult . Error ? . Message } ") ;
91109
92110 _subscription = subscriptionResult . Data ;
93111
94112 // Step 2: Wait a bit to buffer some events
95- _logger . Debug ( $ "2: Buffering events for { updateIntervalMs * 2 } ms") ;
96- await Task . Delay ( updateIntervalMs * 2 , ct ) . ConfigureAwait ( false ) ;
113+ // Use longer buffer time to ensure we have enough events before snapshot
114+ var bufferTimeMs = Math . Max ( updateIntervalMs * 5 , 500 ) ;
115+ _logger . Debug ( $ "2: Buffering events for { bufferTimeMs } ms") ;
116+ await Task . Delay ( bufferTimeMs , ct ) . ConfigureAwait ( false ) ;
97117
98- _logger . Debug ( $ "3: Getting order book snapshot for { marketDepth . Symbol } ") ;
99118 // Step 3: Get depth snapshot
100- WebCallResult < BinanceOrderBook > response = await _restClient . SpotApi . ExchangeData . GetOrderBookAsync ( marketDepth . Symbol , orderBookDepth , ct ) . ConfigureAwait ( false ) ;
119+ _logger . Debug ( $ "3: Getting order book snapshot for { marketDepth . Symbol } ") ;
120+
121+ ( bool Success , IBinanceOrderBook Data , Error Error ) response ;
122+
123+ switch ( marketDepth . Symbol . ContractType )
124+ {
125+ case ContractType . Spot :
126+ WebCallResult < BinanceOrderBook > spotResponse = await _restClient . SpotApi . ExchangeData . GetOrderBookAsync (
127+ marketDepth . Symbol . FullName , orderBookDepth , ct )
128+ . ConfigureAwait ( false ) ;
129+
130+ response = ( spotResponse . Success , spotResponse . Data , spotResponse . Error ) ;
131+ break ;
132+ case ContractType . Perpetual :
133+ WebCallResult < BinanceFuturesOrderBook > perpResponse = await _restClient . UsdFuturesApi . ExchangeData . GetOrderBookAsync (
134+ marketDepth . Symbol . FullName , orderBookDepth , ct )
135+ . ConfigureAwait ( false ) ;
136+
137+ response = ( perpResponse . Success , perpResponse . Data , perpResponse . Error ) ;
138+ break ;
139+ default :
140+ throw new ArgumentOutOfRangeException ( nameof ( marketDepth . Symbol . ContractType ) , "Unknown contract type." ) ;
141+ }
142+
101143 if ( ! response . Success || response . Data == null )
102144 throw new InvalidOperationException ( $ "Failed to get order book snapshot: { response . Error ? . Message } ") ;
103145
104- BinanceOrderBook snapshot = response . Data ;
146+ IBinanceOrderBook snapshot = response . Data ;
105147 _logger . Debug ( $ "Snapshot received: LastUpdateId={ snapshot . LastUpdateId } ") ;
106148
107149 // Step 4: Check if snapshot is valid
108150 // If buffered events exist and snapshot's lastUpdateId is strictly less than first event's U, retry
109- BinanceEventOrderBook firstEvent = null ;
151+ IBinanceEventOrderBook firstEvent = null ;
110152 lock ( _eventBuffer )
111153 {
112154 if ( _eventBuffer . Count > 0 )
113- firstEvent = _eventBuffer . Peek ( ) as BinanceEventOrderBook ;
155+ firstEvent = _eventBuffer . Peek ( ) ;
114156 }
115157
116158 if ( firstEvent != null )
@@ -122,15 +164,34 @@ public async Task BuildAsync(MarketDepth marketDepth, short orderBookDepth = 10,
122164 {
123165 _logger . Warn ( $ "Snapshot too old: LastUpdateId={ snapshot . LastUpdateId } < FirstEvent.U={ firstEvent . FirstUpdateId } . Retrying...") ;
124166 // Snapshot is too old, need to get a new one
125- response = await _restClient . SpotApi . ExchangeData . GetOrderBookAsync ( marketDepth . Symbol , orderBookDepth , ct ) . ConfigureAwait ( false ) ;
167+ switch ( marketDepth . Symbol . ContractType )
168+ {
169+ case ContractType . Spot :
170+ WebCallResult < BinanceOrderBook > spotResponse = await _restClient . SpotApi . ExchangeData . GetOrderBookAsync (
171+ marketDepth . Symbol . FullName , orderBookDepth , ct )
172+ . ConfigureAwait ( false ) ;
173+
174+ response = ( spotResponse . Success , spotResponse . Data , spotResponse . Error ) ;
175+ break ;
176+ case ContractType . Perpetual :
177+ WebCallResult < BinanceFuturesOrderBook > perpResponse = await _restClient . UsdFuturesApi . ExchangeData . GetOrderBookAsync (
178+ marketDepth . Symbol . FullName , orderBookDepth , ct )
179+ . ConfigureAwait ( false ) ;
180+
181+ response = ( perpResponse . Success , perpResponse . Data , perpResponse . Error ) ;
182+ break ;
183+ default :
184+ throw new ArgumentOutOfRangeException ( nameof ( marketDepth . Symbol . ContractType ) , "Unknown contract type." ) ;
185+ }
186+
126187 if ( ! response . Success || response . Data == null )
127188 throw new InvalidOperationException ( $ "Failed to get order book snapshot: { response . Error ? . Message } ") ;
128189 snapshot = response . Data ;
129190 _logger . Debug ( $ "New snapshot received: LastUpdateId={ snapshot . LastUpdateId } ") ;
130191
131192 lock ( _eventBuffer )
132193 {
133- firstEvent = _eventBuffer . Any ( ) ? _eventBuffer . Peek ( ) as BinanceEventOrderBook : null ;
194+ firstEvent = _eventBuffer . Any ( ) ? _eventBuffer . Peek ( ) : null ;
134195 }
135196 }
136197
@@ -155,13 +216,12 @@ public async Task BuildAsync(MarketDepth marketDepth, short orderBookDepth = 10,
155216 int appliedCount = 0 ;
156217 while ( _eventBuffer . Any ( ) )
157218 {
158- var bufferedEvent = _eventBuffer . Peek ( ) as BinanceEventOrderBook ;
219+ var bufferedEvent = _eventBuffer . Dequeue ( ) ;
159220 if ( bufferedEvent != null )
160221 {
161222 ApplyDepthUpdate ( marketDepth , bufferedEvent ) ;
162223 appliedCount ++ ;
163224 }
164- _eventBuffer . Dequeue ( ) ;
165225 }
166226 _logger . Debug ( $ "7: Applied { appliedCount } buffered events") ;
167227 }
@@ -182,11 +242,30 @@ public async Task StreamUpdatesAsync(MarketDepth marketDepth, TimeSpan? updateIn
182242 throw new ArgumentNullException ( nameof ( marketDepth ) ) ;
183243
184244 // Step 1 & 2: Open WebSocket and buffer events
185- var subscriptionResult = await _webSocketClient . SpotStreams . SubscribeToOrderBookUpdatesAsync (
186- marketDepth . Symbol ,
187- updateInterval . HasValue ? ( int ) updateInterval . Value . TotalMilliseconds : ( int ) _defaultUpdateInterval . TotalMilliseconds ,
188- data => OnDepthUpdate ( marketDepth , data ) ,
189- ct ) ;
245+ _logger . Debug ( $ "1 & 2: Streaming updates: Opening WebSocket stream for { marketDepth . Symbol } ") ;
246+ CallResult < UpdateSubscription > subscriptionResult ;
247+
248+ switch ( marketDepth . Symbol . ContractType )
249+ {
250+ case ContractType . Spot :
251+ subscriptionResult = await _webSocketClient . SpotStreams . SubscribeToOrderBookUpdatesAsync (
252+ marketDepth . Symbol . FullName ,
253+ updateInterval . HasValue ? ( int ) updateInterval . Value . TotalMilliseconds : ( int ) _defaultUpdateInterval . TotalMilliseconds ,
254+ data => OnDepthUpdateSpot ( marketDepth , data ) ,
255+ ct )
256+ . ConfigureAwait ( false ) ;
257+ break ;
258+ case ContractType . Perpetual :
259+ subscriptionResult = await _webSocketClient . UsdFuturesStreams . SubscribeToOrderBookUpdatesAsync (
260+ marketDepth . Symbol . FullName ,
261+ updateInterval . HasValue ? ( int ) updateInterval . Value . TotalMilliseconds : ( int ) _defaultUpdateInterval . TotalMilliseconds ,
262+ data => OnDepthUpdatePerp ( marketDepth , data ) ,
263+ ct )
264+ . ConfigureAwait ( false ) ;
265+ break ;
266+ default :
267+ throw new ArgumentOutOfRangeException ( nameof ( marketDepth . Symbol . ContractType ) , "Unknown contract type." ) ;
268+ }
190269
191270 _subscription = subscriptionResult . Data ;
192271 }
@@ -201,17 +280,22 @@ public async Task StopStreamingAsync(CancellationToken ct = default)
201280 }
202281 }
203282
204- private void OnDepthUpdate ( MarketDepth marketDepth , DataEvent < IBinanceEventOrderBook > dataEvent )
283+ private void OnDepthUpdateSpot ( MarketDepth marketDepth , DataEvent < IBinanceEventOrderBook > dataEvent ) =>
284+ OnDepthUpdate ( marketDepth , dataEvent . Data ) ;
285+
286+ private void OnDepthUpdatePerp ( MarketDepth marketDepth , DataEvent < IBinanceFuturesEventOrderBook > dataEvent ) =>
287+ OnDepthUpdate ( marketDepth , dataEvent . Data ) ;
288+
289+ private void OnDepthUpdate ( MarketDepth marketDepth , IBinanceEventOrderBook data )
205290 {
206- var data = dataEvent . Data as BinanceEventOrderBook ;
207291 if ( data == null ) return ;
208292
209293 lock ( _eventBuffer )
210294 {
211295 if ( ! _isSnapshotLoaded )
212296 {
213297 // Step 2: Buffer events before snapshot is loaded
214- _eventBuffer . Enqueue ( dataEvent . Data ) ;
298+ _eventBuffer . Enqueue ( data ) ;
215299 _logger . Debug ( $ "Step 2: Buffered event U={ data . FirstUpdateId } , u={ data . LastUpdateId } . Buffer size: { _eventBuffer . Count } ") ;
216300 return ;
217301 }
@@ -221,7 +305,7 @@ private void OnDepthUpdate(MarketDepth marketDepth, DataEvent<IBinanceEventOrder
221305 }
222306 }
223307
224- private void ApplyDepthUpdate ( MarketDepth marketDepth , BinanceEventOrderBook eventData )
308+ private void ApplyDepthUpdate ( MarketDepth marketDepth , IBinanceEventOrderBook eventData )
225309 {
226310 // Step 7: Apply update procedure
227311
@@ -253,4 +337,4 @@ private void ApplyDepthUpdate(MarketDepth marketDepth, BinanceEventOrderBook eve
253337 // 3. Set order book update ID
254338 _localOrderBookUpdateId = eventData . LastUpdateId ;
255339 }
256- }
340+ }
0 commit comments