1+ using Crypto . Websocket . Extensions . Core . Models ;
2+ using Crypto . Websocket . Extensions . Core . OrderBooks . Models ;
3+ using Crypto . Websocket . Extensions . Core . OrderBooks . Sources ;
4+ using Crypto . Websocket . Extensions . Core . Utils ;
5+ using Crypto . Websocket . Extensions . Core . Validations ;
6+ using Microsoft . Extensions . Logging ;
17using System ;
28using System . Collections . Generic ;
9+ using System . Collections . ObjectModel ;
310using System . Diagnostics ;
411using System . Linq ;
512using System . Reactive . Linq ;
613using System . Reactive . Subjects ;
714using System . Threading ;
815using System . Threading . Tasks ;
9- using Crypto . Websocket . Extensions . Core . Models ;
10- using Crypto . Websocket . Extensions . Core . OrderBooks . Models ;
11- using Crypto . Websocket . Extensions . Core . OrderBooks . Sources ;
12- using Crypto . Websocket . Extensions . Core . Utils ;
13- using Crypto . Websocket . Extensions . Core . Validations ;
14- using Microsoft . Extensions . Logging ;
1516
1617namespace Crypto . Websocket . Extensions . Core . OrderBooks
1718{
@@ -20,19 +21,19 @@ namespace Crypto.Websocket.Extensions.Core.OrderBooks
2021 /// </summary>
2122 public abstract class CryptoOrderBookBase < T > : ICryptoOrderBook
2223 {
23- /// <summary>
24- /// Object to use for synchronization.
25- /// </summary>
24+ /// <summary>
25+ /// Object to use for synchronization.
26+ /// </summary>
2627#if NET9_0_OR_GREATER
2728 protected readonly Lock Locker = new ( ) ;
2829#else
29- protected readonly object Locker = new ( ) ;
30+ protected readonly object Locker = new ( ) ;
3031#endif
3132
32- /// <summary>
33- /// The source.
34- /// </summary>
35- protected readonly IOrderBookSource Source ;
33+ /// <summary>
34+ /// The source.
35+ /// </summary>
36+ protected readonly IOrderBookSource Source ;
3637
3738 /// <summary>
3839 /// The internal collection of bid levels.
@@ -108,6 +109,9 @@ protected CryptoOrderBookBase(string targetPair, IOrderBookSource source)
108109 TargetPairOriginal = targetPair ;
109110 TargetPair = CryptoPairsHelper . Clean ( targetPair ) ;
110111 Source = source ;
112+
113+ // diffs are not supported by the source, we need to calculate metrics from snapshots
114+ CalculateMetricsFromSnapshots = ! Source . DiffsSupported ;
111115 }
112116
113117 /// <summary>
@@ -208,6 +212,11 @@ public bool ValidityCheckEnabled
208212 /// <inheritdoc />
209213 public bool IsIndexComputationEnabled { get ; set ; }
210214
215+ /// <summary>
216+ /// If exchange provides only snapshots with no diffs, enable this to calculate metrics
217+ /// </summary>
218+ public bool CalculateMetricsFromSnapshots { get ; set ; }
219+
211220 /// <inheritdoc />
212221 public IObservable < IOrderBookChangeInfo > BidAskUpdatedStream => BidAskUpdated . AsObservable ( ) ;
213222
@@ -382,32 +391,15 @@ private void NotifyOrderBookChanges(TopNLevelsChangeInfo levelsChange)
382391
383392 private void HandleSnapshot ( OrderBookLevel [ ] levels )
384393 {
385- ClearLevels ( ) ;
386-
387394 LogDebug ( $ "Handling snapshot: { levels . Length } levels") ;
388- foreach ( var level in levels )
389- {
390- var price = level . Price ;
391- if ( price is null or < 0 )
392- {
393- LogAlways ( $ "Received snapshot level with weird price, ignoring. [{ level . Side } ] Id: { level . Id } , price: { level . Price } , amount: { level . Amount } ") ;
394- continue ;
395- }
396-
397- level . AmountDifference = level . Amount ?? 0 ;
398- level . CountDifference = level . Count ?? 0 ;
399395
400- switch ( level . Side )
401- {
402- case CryptoOrderSide . Bid :
403- HandleSnapshotBidLevel ( level ) ;
404- AllBidLevels [ level . Id ] = level ;
405- break ;
406- case CryptoOrderSide . Ask :
407- HandleSnapshotAskLevel ( level ) ;
408- AllAskLevels [ level . Id ] = level ;
409- break ;
410- }
396+ if ( CalculateMetricsFromSnapshots )
397+ {
398+ HandleSnapshotWithMetrics ( levels ) ;
399+ }
400+ else
401+ {
402+ HandleSnapshotWithNoMetrics ( levels ) ;
411403 }
412404
413405 RecomputeAfterChangeAndSetIndexes ( levels ) ;
@@ -471,11 +463,11 @@ private void HandleDiff(OrderBookLevelBulk bulk, IReadOnlyCollection<OrderBookLe
471463 protected abstract void UpdateLevels ( IEnumerable < OrderBookLevel > levels ) ;
472464
473465 /// <summary>
474- /// Computes differences and copies state between existing and new level.
466+ /// Calculates differences and copies state between existing and new level.
475467 /// </summary>
476468 /// <param name="existing">The existing level.</param>
477469 /// <param name="level">The new level.</param>
478- protected static void ComputeUpdate ( OrderBookLevel existing , OrderBookLevel level )
470+ protected static void CalculateMetricsForUpdatedLevel ( OrderBookLevel existing , OrderBookLevel level )
479471 {
480472 var amountDiff = ( level . Amount ?? existing . Amount ?? 0 ) - ( existing . Amount ?? 0 ) ;
481473 var countDiff = ( level . Count ?? existing . Count ?? 0 ) - ( existing . Count ?? 0 ) ;
@@ -497,6 +489,9 @@ protected static void ComputeUpdate(OrderBookLevel existing, OrderBookLevel leve
497489 level . CountDifferenceAggregated += countDiff ;
498490 existing . CountDifferenceAggregated += countDiff ;
499491
492+ existing . AmountUpdatedCount += amountDiff != 0 ? 1 : 0 ;
493+ level . AmountUpdatedCount = existing . AmountUpdatedCount ;
494+
500495 level . Amount ??= existing . Amount ;
501496 level . Count ??= existing . Count ;
502497 level . Price ??= existing . Price ;
@@ -523,7 +518,7 @@ protected static bool IsInvalidLevel(OrderBookLevel level) =>
523518 /// </summary>
524519 /// <param name="existing">The existing level.</param>
525520 /// <param name="level">The new level.</param>
526- protected static void ComputeDelete ( OrderBookLevel existing , OrderBookLevel level )
521+ protected static void CalculateMetricsForDeletedLevel ( OrderBookLevel existing , OrderBookLevel level )
527522 {
528523 var amountDiff = - ( existing . Amount ?? 0 ) ;
529524 var countDiff = - ( existing . Count ?? 0 ) ;
@@ -545,6 +540,81 @@ protected static void ComputeDelete(OrderBookLevel existing, OrderBookLevel leve
545540 existing . CountDifferenceAggregated += countDiff ;
546541 }
547542
543+ private void HandleSnapshotWithNoMetrics ( OrderBookLevel [ ] levels )
544+ {
545+ ClearLevels ( ) ;
546+ foreach ( var level in levels )
547+ {
548+ var price = level . Price ;
549+ if ( price is null or < 0 )
550+ {
551+ LogAlways ( $ "Received snapshot level with weird price, ignoring. [{ level . Side } ] Id: { level . Id } , price: { level . Price } , amount: { level . Amount } ") ;
552+ continue ;
553+ }
554+
555+ level . AmountDifference = level . Amount ?? 0 ;
556+ level . CountDifference = level . Count ?? 0 ;
557+
558+ switch ( level . Side )
559+ {
560+ case CryptoOrderSide . Bid :
561+ HandleSnapshotBidLevel ( level ) ;
562+ AllBidLevels [ level . Id ] = level ;
563+ break ;
564+ case CryptoOrderSide . Ask :
565+ HandleSnapshotAskLevel ( level ) ;
566+ AllAskLevels [ level . Id ] = level ;
567+ break ;
568+ }
569+ }
570+ }
571+
572+ private void HandleSnapshotWithMetrics ( OrderBookLevel [ ] levels )
573+ {
574+ foreach ( var level in levels )
575+ {
576+ var price = level . Price ;
577+ if ( price is null or < 0 )
578+ {
579+ LogAlways ( $ "Received snapshot level with weird price, ignoring. [{ level . Side } ] Id: { level . Id } , price: { level . Price } , amount: { level . Amount } ") ;
580+ continue ;
581+ }
582+
583+ var existing = FindLevelById ( level . Id , level . Side ) ;
584+ if ( existing == null )
585+ {
586+ level . AmountDifference = level . Amount ?? 0 ;
587+ level . CountDifference = level . Count ?? 0 ;
588+ level . AmountUpdatedCount = 0 ;
589+ continue ;
590+ }
591+
592+ CalculateMetricsForUpdatedLevel ( existing , level ) ;
593+ }
594+
595+ ClearLevels ( ) ;
596+ foreach ( var level in levels )
597+ {
598+ var price = level . Price ;
599+ if ( price is null or < 0 )
600+ {
601+ continue ;
602+ }
603+
604+ switch ( level . Side )
605+ {
606+ case CryptoOrderSide . Bid :
607+ HandleSnapshotBidLevel ( level ) ;
608+ AllBidLevels [ level . Id ] = level ;
609+ break ;
610+ case CryptoOrderSide . Ask :
611+ HandleSnapshotAskLevel ( level ) ;
612+ AllAskLevels [ level . Id ] = level ;
613+ break ;
614+ }
615+ }
616+ }
617+
548618 private void RecomputeAfterChange ( )
549619 {
550620 var firstBid = GetFirstBid ( ) ;
0 commit comments