diff --git a/CryptoExchange.Net/BaseClient.cs b/CryptoExchange.Net/BaseClient.cs index 8c32f2b..a4bbc98 100644 --- a/CryptoExchange.Net/BaseClient.cs +++ b/CryptoExchange.Net/BaseClient.cs @@ -106,19 +106,16 @@ namespace CryptoExchange.Net catch (JsonReaderException jre) { var info = $"Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}. Data: {data}"; - log.Write(LogVerbosity.Error, info); return new CallResult(null, new DeserializeError(info)); } catch (JsonSerializationException jse) { var info = $"Deserialize JsonSerializationException: {jse.Message}. Data: {data}"; - log.Write(LogVerbosity.Error, info); return new CallResult(null, new DeserializeError(info)); } catch (Exception ex) { var info = $"Deserialize Unknown Exception: {ex.Message}. Data: {data}"; - log.Write(LogVerbosity.Error, info); return new CallResult(null, new DeserializeError(info)); } } @@ -134,7 +131,13 @@ namespace CryptoExchange.Net protected CallResult Deserialize(string data, bool checkObject = true, JsonSerializer? serializer = null) { var tokenResult = ValidateJson(data); - return !tokenResult ? new CallResult(default, tokenResult.Error) : Deserialize(tokenResult.Data, checkObject, serializer); + if (!tokenResult) + { + log.Write(LogVerbosity.Error, tokenResult.Error!.Message); + return new CallResult(default, tokenResult.Error); + } + + return Deserialize(tokenResult.Data, checkObject, serializer); } /// diff --git a/CryptoExchange.Net/CryptoExchange.Net.csproj b/CryptoExchange.Net/CryptoExchange.Net.csproj index b772e21..841462b 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.csproj +++ b/CryptoExchange.Net/CryptoExchange.Net.csproj @@ -5,12 +5,13 @@ CryptoExchange.Net JKorf - 3.0.2 + A base package for implementing cryptocurrency exchange API's + 3.0.4 false https://github.com/JKorf/CryptoExchange.Net en true - 3.0.2 - Removed invalid check for unauthenticated proxy + 3.0.4 - Removed unnecessary json serialization when sending string data enable 8.0 MIT diff --git a/CryptoExchange.Net/CryptoExchange.Net.xml b/CryptoExchange.Net/CryptoExchange.Net.xml index 800ba58..62e5487 100644 --- a/CryptoExchange.Net/CryptoExchange.Net.xml +++ b/CryptoExchange.Net/CryptoExchange.Net.xml @@ -797,6 +797,11 @@ Event when order book was updated. Be careful! It can generate a lot of events at high-liquidity markets + + + Event when the BestBid or BestAsk changes ie a Pricing Tick + + Timestamp of the last update @@ -1769,6 +1774,11 @@ Event when the state changes + + + Event when the BestBid or BestAsk changes ie a Pricing Tick + + Event when order book was updated, containing the changed bids and asks. Be careful! It can generate a lot of events at high-liquidity markets diff --git a/CryptoExchange.Net/Interfaces/ISymbolOrderBook.cs b/CryptoExchange.Net/Interfaces/ISymbolOrderBook.cs index 1571feb..9d0b502 100644 --- a/CryptoExchange.Net/Interfaces/ISymbolOrderBook.cs +++ b/CryptoExchange.Net/Interfaces/ISymbolOrderBook.cs @@ -33,6 +33,10 @@ namespace CryptoExchange.Net.Interfaces /// event Action, IEnumerable> OnOrderBookUpdate; /// + /// Event when the BestBid or BestAsk changes ie a Pricing Tick + /// + event Action OnBestOffersChanged; + /// /// Timestamp of the last update /// DateTime LastOrderBookUpdate { get; } diff --git a/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs b/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs index cc890c8..36e94b8 100644 --- a/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs +++ b/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs @@ -51,7 +51,7 @@ namespace CryptoExchange.Net.OrderBook /// /// The status of the order book. Order book is up to date when the status is `Synced` /// - public OrderBookStatus Status + public OrderBookStatus Status { get => status; set @@ -79,6 +79,12 @@ namespace CryptoExchange.Net.OrderBook /// Event when the state changes /// public event Action? OnStatusChange; + + /// + /// Event when the BestBid or BestAsk changes ie a Pricing Tick + /// + public event Action? OnBestOffersChanged; + /// /// Event when order book was updated, containing the changed bids and asks. Be careful! It can generate a lot of events at high-liquidity markets /// @@ -112,7 +118,7 @@ namespace CryptoExchange.Net.OrderBook /// /// The list of bids /// - public IEnumerable Bids + public IEnumerable Bids { get { @@ -136,7 +142,7 @@ namespace CryptoExchange.Net.OrderBook /// /// The best ask currently in the order book /// - public ISymbolOrderBookEntry BestAsk + public ISymbolOrderBookEntry BestAsk { get { @@ -286,9 +292,17 @@ namespace CryptoExchange.Net.OrderBook 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) + { + if (BestBid.Price != prevBestBid.Price || BestBid.Quantity != prevBestBid.Quantity || + BestAsk.Price != prevBestAsk.Price || BestAsk.Quantity != prevBestAsk.Quantity) + OnBestOffersChanged?.Invoke(BestBid, BestAsk); + } + /// /// Update the order book using a single id for an update /// @@ -315,8 +329,11 @@ namespace CryptoExchange.Net.OrderBook else { CheckProcessBuffer(); + var prevBestBid = BestBid; + var prevBestAsk = BestAsk; ProcessSingleSequenceUpdates(rangeUpdateId, bids, asks); OnOrderBookUpdate?.Invoke(bids, asks); + CheckBestOffersChanged(prevBestBid, prevBestAsk); } } } @@ -349,8 +366,11 @@ namespace CryptoExchange.Net.OrderBook else { CheckProcessBuffer(); + var prevBestBid = BestBid; + var prevBestAsk = BestAsk; ProcessRangeUpdates(firstUpdateId, lastUpdateId, bids, asks); OnOrderBookUpdate?.Invoke(bids, asks); + CheckBestOffersChanged(prevBestBid, prevBestAsk); } } } @@ -376,8 +396,11 @@ namespace CryptoExchange.Net.OrderBook else { CheckProcessBuffer(); + var prevBestBid = BestBid; + var prevBestAsk = BestAsk; ProcessUpdates(bids, asks); OnOrderBookUpdate?.Invoke(bids, asks); + CheckBestOffersChanged(prevBestBid, prevBestAsk); } } } diff --git a/CryptoExchange.Net/RestClient.cs b/CryptoExchange.Net/RestClient.cs index 521f9b9..048cf6c 100644 --- a/CryptoExchange.Net/RestClient.cs +++ b/CryptoExchange.Net/RestClient.cs @@ -181,7 +181,7 @@ namespace CryptoExchange.Net } string? paramString = null; - if (parameters != null && method == HttpMethod.Post) + if (method == HttpMethod.Post) paramString = " with request body " + request.Content; log.Write(LogVerbosity.Debug, $"Sending {method}{(signed ? " signed" : "")} request to {request.Uri}{paramString ?? " "}{(apiProxy == null? "": $" via proxy {apiProxy.Host}")}"); diff --git a/CryptoExchange.Net/SocketClient.cs b/CryptoExchange.Net/SocketClient.cs index b416553..c1848ef 100644 --- a/CryptoExchange.Net/SocketClient.cs +++ b/CryptoExchange.Net/SocketClient.cs @@ -552,7 +552,7 @@ namespace CryptoExchange.Net { log.Write(LogVerbosity.Debug, $"Closing all {sockets.Sum(s => s.Value.HandlerCount)} subscriptions"); - await Task.Run(() => + await Task.Run(async () => { var tasks = new List(); { @@ -561,7 +561,7 @@ namespace CryptoExchange.Net tasks.Add(sub.Close()); } - Task.WaitAll(tasks.ToArray()); + await Task.WhenAll(tasks.ToArray()).ConfigureAwait(false); }).ConfigureAwait(false); } diff --git a/CryptoExchange.Net/Sockets/SocketConnection.cs b/CryptoExchange.Net/Sockets/SocketConnection.cs index b7e8b9a..e1704e0 100644 --- a/CryptoExchange.Net/Sockets/SocketConnection.cs +++ b/CryptoExchange.Net/Sockets/SocketConnection.cs @@ -216,7 +216,10 @@ namespace CryptoExchange.Net.Sockets /// How null values should be serialized public virtual void Send(T obj, NullValueHandling nullValueHandling = NullValueHandling.Ignore) { - Send(JsonConvert.SerializeObject(obj, Formatting.None, new JsonSerializerSettings { NullValueHandling = nullValueHandling })); + if(obj is string str) + Send(str); + else + Send(JsonConvert.SerializeObject(obj, Formatting.None, new JsonSerializerSettings { NullValueHandling = nullValueHandling })); } /// diff --git a/README.md b/README.md index 970fd4b..4dfa2ed 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.3 - 23 Jan 2020 + * Added OnBestOffersChanged event to order book implementations + * Version 3.0.2 - 10 Dec 2019 * Removed invalid check for unauthenticated proxy