From ddc4ebe638585e55605a1a27c4144769f746bc0a Mon Sep 17 00:00:00 2001 From: Jan Korf Date: Sat, 20 Jun 2020 20:34:48 +0200 Subject: [PATCH 1/4] Rework order book; added support for checksum --- CryptoExchange.Net/CryptoExchange.Net.xml | 155 ++++++++++++++++++ CryptoExchange.Net/Objects/Enums.cs | 4 + .../OrderBook/ProcessQueueItem.cs | 13 ++ .../OrderBook/SymbolOrderBook.cs | 123 +++++++++----- 4 files changed, 257 insertions(+), 38 deletions(-) diff --git a/CryptoExchange.Net/CryptoExchange.Net.xml b/CryptoExchange.Net/CryptoExchange.Net.xml index 4f03bd5..bf6762e 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.xml +++ b/CryptoExchange.Net/CryptoExchange.Net.xml @@ -1333,6 +1333,11 @@ Connecting + + + Reconnecting + + Syncing data @@ -1858,6 +1863,13 @@ + + + Validate a checksum with the current order book + + + + Set the initial data for the order book @@ -2931,5 +2943,148 @@ + + + Specifies that is allowed as an input even if the + corresponding type disallows it. + + + + + Initializes a new instance of the class. + + + + + Specifies that is disallowed as an input even if the + corresponding type allows it. + + + + + Initializes a new instance of the class. + + + + + Specifies that a method that will never return under any circumstance. + + + + + Initializes a new instance of the class. + + + + + Specifies that the method will not return if the associated + parameter is passed the specified value. + + + + + Gets the condition parameter value. + Code after the method is considered unreachable by diagnostics if the argument + to the associated parameter matches this value. + + + + + Initializes a new instance of the + class with the specified parameter value. + + + The condition parameter value. + Code after the method is considered unreachable by diagnostics if the argument + to the associated parameter matches this value. + + + + + Specifies that an output may be even if the + corresponding type disallows it. + + + + + Initializes a new instance of the class. + + + + + Specifies that when a method returns , + the parameter may be even if the corresponding type disallows it. + + + + + Gets the return value condition. + If the method returns this value, the associated parameter may be . + + + + + Initializes the attribute with the specified return value condition. + + + The return value condition. + If the method returns this value, the associated parameter may be . + + + + + Specifies that an output is not even if the + corresponding type allows it. + + + + + Initializes a new instance of the class. + + + + + Specifies that the output will be non- if the + named parameter is non-. + + + + + Gets the associated parameter name. + The output will be non- if the argument to the + parameter specified is non-. + + + + + Initializes the attribute with the associated parameter name. + + + The associated parameter name. + The output will be non- if the argument to the + parameter specified is non-. + + + + + Specifies that when a method returns , + the parameter will not be even if the corresponding type allows it. + + + + + Gets the return value condition. + If the method returns this value, the associated parameter will not be . + + + + + Initializes the attribute with the specified return value condition. + + + The return value condition. + If the method returns this value, the associated parameter will not be . + + diff --git a/CryptoExchange.Net/Objects/Enums.cs b/CryptoExchange.Net/Objects/Enums.cs index 3af3694..785b8ff 100644 --- a/CryptoExchange.Net/Objects/Enums.cs +++ b/CryptoExchange.Net/Objects/Enums.cs @@ -59,6 +59,10 @@ /// Connecting, /// + /// Reconnecting + /// + Reconnecting, + /// /// Syncing data /// Syncing, diff --git a/CryptoExchange.Net/OrderBook/ProcessQueueItem.cs b/CryptoExchange.Net/OrderBook/ProcessQueueItem.cs index 996e6d4..14832a0 100644 --- a/CryptoExchange.Net/OrderBook/ProcessQueueItem.cs +++ b/CryptoExchange.Net/OrderBook/ProcessQueueItem.cs @@ -10,4 +10,17 @@ namespace CryptoExchange.Net.OrderBook public IEnumerable Bids { get; set; } = new List(); public IEnumerable Asks { get; set; } = new List(); } + + internal class InitialOrderBookItem + { + public long StartUpdateId { get; set; } + public long EndUpdateId { get; set; } + public IEnumerable Bids { get; set; } = new List(); + public IEnumerable Asks { get; set; } = new List(); + } + + internal class ChecksumItem + { + public int Checksum { get; set; } + } } diff --git a/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs b/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs index 471dc45..8f4e32f 100644 --- a/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs +++ b/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs @@ -40,7 +40,7 @@ namespace CryptoExchange.Net.OrderBook private Task _processTask; private AutoResetEvent _queueEvent; - private ConcurrentQueue _processQueue; + private ConcurrentQueue _processQueue; /// /// Order book implementation id @@ -197,7 +197,7 @@ namespace CryptoExchange.Net.OrderBook Id = options.OrderBookName; processBuffer = new List(); - _processQueue = new ConcurrentQueue(); + _processQueue = new ConcurrentQueue(); _queueEvent = new AutoResetEvent(false); sequencesAreConsecutive = options.SequenceNumbersAreConsecutive; @@ -243,7 +243,7 @@ namespace CryptoExchange.Net.OrderBook private void Reset() { log.Write(LogVerbosity.Warning, $"{Id} order book {Symbol} connection lost"); - Status = OrderBookStatus.Connecting; + Status = OrderBookStatus.Reconnecting; _queueEvent.Set(); // Clear queue while(_processQueue.TryDequeue(out _)) @@ -306,14 +306,58 @@ namespace CryptoExchange.Net.OrderBook /// protected abstract Task> DoResync(); + /// + /// Validate a checksum with the current order book + /// + /// + /// + protected virtual bool DoChecksum(int checksum) => true; + private void ProcessQueue() { while(Status != OrderBookStatus.Disconnected) { _queueEvent.WaitOne(); - + while (_processQueue.TryDequeue(out var item)) - ProcessQueueItem(item); + { + if (Status == OrderBookStatus.Disconnected) + break; + + if (item is InitialOrderBookItem iobi) + ProcessInitialOrderBookItem(iobi); + if (item is ProcessQueueItem pqi) + ProcessQueueItem(pqi); + else if (item is ChecksumItem ci) + ProcessChecksum(ci); + } + } + } + + private void ProcessInitialOrderBookItem(InitialOrderBookItem item) + { + lock (bookLock) + { + if (Status == OrderBookStatus.Connecting || Status == OrderBookStatus.Disconnected) + return; + + asks.Clear(); + foreach (var ask in item.Asks) + asks.Add(ask.Price, ask); + bids.Clear(); + foreach (var bid in item.Bids) + bids.Add(bid.Price, bid); + + LastSequenceNumber = item.EndUpdateId; + + AskCount = asks.Count; + BidCount = bids.Count; + + LastOrderBookUpdate = DateTime.UtcNow; + log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} data set: {BidCount} bids, {AskCount} asks. #{item.EndUpdateId}"); + CheckProcessBuffer(); + OnOrderBookUpdate?.Invoke(item.Asks, item.Bids); + OnBestOffersChanged?.Invoke(BestBid, BestAsk); } } @@ -354,6 +398,20 @@ namespace CryptoExchange.Net.OrderBook } } + private void ProcessChecksum(ChecksumItem ci) + { + lock (bookLock) + { + var checksumResult = DoChecksum(ci.Checksum); + if(!checksumResult) + { + log.Write(LogVerbosity.Warning, $"{Id} order book {Symbol} out of sync. Resyncing"); + _ = subscription?.Reconnect(); + return; + } + } + } + /// /// Set the initial data for the order book /// @@ -362,38 +420,10 @@ namespace CryptoExchange.Net.OrderBook /// List of bids protected void SetInitialOrderBook(long orderBookSequenceNumber, IEnumerable bidList, IEnumerable askList) { - lock (bookLock) - { - if (Status == OrderBookStatus.Connecting || Status == OrderBookStatus.Disconnected) - return; + bookSet = true; - asks.Clear(); - foreach (var ask in askList) - asks.Add(ask.Price, ask); - bids.Clear(); - foreach (var bid in bidList) - bids.Add(bid.Price, bid); - - LastSequenceNumber = orderBookSequenceNumber; - - AskCount = asks.Count; - BidCount = asks.Count; - - bookSet = true; - LastOrderBookUpdate = DateTime.UtcNow; - log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} data set: {BidCount} bids, {AskCount} asks. #{orderBookSequenceNumber}"); - CheckProcessBuffer(); - OnOrderBookUpdate?.Invoke(bidList, askList); - OnBestOffersChanged?.Invoke(BestBid, BestAsk); - } - } - - private void CheckBestOffersChanged(ISymbolOrderBookEntry prevBestBid, ISymbolOrderBookEntry prevBestAsk) - { - var (bestBid, bestAsk) = BestOffers; - if (bestBid.Price != prevBestBid.Price || bestBid.Quantity != prevBestBid.Quantity || - bestAsk.Price != prevBestAsk.Price || bestAsk.Quantity != prevBestAsk.Quantity) - OnBestOffersChanged?.Invoke(bestBid, bestAsk); + _processQueue.Enqueue(new InitialOrderBookItem { StartUpdateId = orderBookSequenceNumber, EndUpdateId = orderBookSequenceNumber, Asks = askList, Bids = bidList }); + _queueEvent.Set(); } /// @@ -408,6 +438,16 @@ namespace CryptoExchange.Net.OrderBook _queueEvent.Set(); } + /// + /// Add a checksum to the process queue + /// + /// + protected void AddChecksum(int checksum) + { + _processQueue.Enqueue(new ChecksumItem() { Checksum = checksum }); + _queueEvent.Set(); + } + /// /// Update the order book using a first/last update id /// @@ -505,7 +545,6 @@ namespace CryptoExchange.Net.OrderBook { // Out of sync log.Write(LogVerbosity.Warning, $"{Id} order book {Symbol} out of sync (expected { LastSequenceNumber + 1}, was {sequence}), reconnecting"); - Status = OrderBookStatus.Connecting; subscription?.Reconnect(); return false; } @@ -531,7 +570,7 @@ namespace CryptoExchange.Net.OrderBook } else { - listToChange[entry.Price].Quantity = entry.Quantity; + listToChange[entry.Price] = entry; } } @@ -557,6 +596,14 @@ namespace CryptoExchange.Net.OrderBook return new CallResult(true, null); } + private void CheckBestOffersChanged(ISymbolOrderBookEntry prevBestBid, ISymbolOrderBookEntry prevBestAsk) + { + var (bestBid, bestAsk) = BestOffers; + if (bestBid.Price != prevBestBid.Price || bestBid.Quantity != prevBestBid.Quantity || + bestAsk.Price != prevBestAsk.Price || bestAsk.Quantity != prevBestAsk.Quantity) + OnBestOffersChanged?.Invoke(bestBid, bestAsk); + } + /// /// Dispose the order book /// From ed5abb6f2266db7cb2ea2935722da11f806b10cf Mon Sep 17 00:00:00 2001 From: Jan Korf Date: Sat, 20 Jun 2020 20:36:15 +0200 Subject: [PATCH 2/4] Updated version --- CryptoExchange.Net/CryptoExchange.Net.csproj | 4 ++-- README.md | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CryptoExchange.Net/CryptoExchange.Net.csproj b/CryptoExchange.Net/CryptoExchange.Net.csproj index 62bb836..dab827b 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.csproj +++ b/CryptoExchange.Net/CryptoExchange.Net.csproj @@ -6,12 +6,12 @@ CryptoExchange.Net JKorf A base package for implementing cryptocurrency exchange API's - 3.0.10 + 3.0.11 false https://github.com/JKorf/CryptoExchange.Net en true - 3.0.10 - Fix for order book synchronization + 3.0.11 - Added support for checksum in SymbolOrderBook enable 8.0 MIT diff --git a/README.md b/README.md index 664643c..3d85219 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,9 @@ The order book will automatically reconnect when the connection is lost and resy To stop synchronizing an order book use the `Stop` method. ## Release notes +* Version 3.0.11 - 20 Jun 2020 + * Added support for checksum in SymbolOrderBook + * Version 3.0.10 - 16 Jun 2020 * Fix for order book synchronization From 1b4b4007810d903e70840193599fbb1f32efd3a4 Mon Sep 17 00:00:00 2001 From: Jan Korf Date: Sun, 21 Jun 2020 11:48:01 +0200 Subject: [PATCH 3/4] Update CryptoExchange.Net.xml --- CryptoExchange.Net/CryptoExchange.Net.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CryptoExchange.Net/CryptoExchange.Net.xml b/CryptoExchange.Net/CryptoExchange.Net.xml index bf6762e..abf449e 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.xml +++ b/CryptoExchange.Net/CryptoExchange.Net.xml @@ -1886,6 +1886,12 @@ + + + Add a checksum to the process queue + + + Update the order book using a first/last update id From b4e146d5caf099444bac16f922d2f26345634781 Mon Sep 17 00:00:00 2001 From: Artem Kurianov Date: Wed, 1 Jul 2020 18:12:48 +0300 Subject: [PATCH 4/4] added Bitmex --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 3d85219..5df343d 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,11 @@ Implementations from third parties
Liquid + + + +
+Bitmex