diff --git a/CryptoExchange.Net/Clients/SocketApiClient.cs b/CryptoExchange.Net/Clients/SocketApiClient.cs
index 6fd7ea1..b2ef32e 100644
--- a/CryptoExchange.Net/Clients/SocketApiClient.cs
+++ b/CryptoExchange.Net/Clients/SocketApiClient.cs
@@ -142,6 +142,10 @@ namespace CryptoExchange.Net.Clients
///
public int? MaxIndividualSubscriptionsPerConnection { get; set; }
+ ///
+ /// Whether or not to enforce that sequence number updates are always (lastSequenceNumber + 1)
+ ///
+ public bool EnforceSequenceNumbers { get; set; }
#endregion
///
@@ -706,6 +710,10 @@ namespace CryptoExchange.Net.Clients
if (connection != null && !connection.DedicatedRequestConnection.Authenticated)
// Mark dedicated request connection as authenticated if the request is authenticated
connection.DedicatedRequestConnection.Authenticated = authenticated;
+
+ if (connection == null)
+ // Fall back to an existing connection if there is no dedicated request connection available
+ connection = socketQuery.OrderBy(s => s.UserSubscriptionCount).FirstOrDefault();
}
bool maxConnectionsReached = _socketConnections.Count >= (ApiOptions.MaxSocketConnections ?? ClientOptions.MaxSocketConnections);
diff --git a/CryptoExchange.Net/Converters/SystemTextJson/IntBoolConverter.cs b/CryptoExchange.Net/Converters/SystemTextJson/IntBoolConverter.cs
new file mode 100644
index 0000000..82f2b6f
--- /dev/null
+++ b/CryptoExchange.Net/Converters/SystemTextJson/IntBoolConverter.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace CryptoExchange.Net.Converters.SystemTextJson
+{
+ ///
+ /// Bool converter
+ ///
+ public class IntBoolConverter : JsonConverter
+ {
+ private readonly int _trueValue;
+
+ ///
+ /// ctor
+ ///
+ /// The int value representing the true value
+ public IntBoolConverter(int trueValue)
+ {
+ _trueValue = trueValue;
+ }
+
+ public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType != JsonTokenType.Number)
+ return false;
+
+ return reader.GetDecimal() == _trueValue;
+ }
+
+ public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)
+ {
+ writer.WriteNumberValue(_trueValue);
+ }
+ }
+}
diff --git a/CryptoExchange.Net/Logging/Extensions/SymbolOrderBookLoggingExtensions.cs b/CryptoExchange.Net/Logging/Extensions/SymbolOrderBookLoggingExtensions.cs
index 2997974..379cd62 100644
--- a/CryptoExchange.Net/Logging/Extensions/SymbolOrderBookLoggingExtensions.cs
+++ b/CryptoExchange.Net/Logging/Extensions/SymbolOrderBookLoggingExtensions.cs
@@ -22,7 +22,7 @@ namespace CryptoExchange.Net.Logging.Extensions
private static readonly Action _orderBookResyncing;
private static readonly Action _orderBookResynced;
private static readonly Action _orderBookMessageSkippedBecauseOfResubscribing;
- private static readonly Action _orderBookDataSet;
+ private static readonly Action _orderBookDataSet;
private static readonly Action _orderBookUpdateBuffered;
private static readonly Action _orderBookOutOfSyncDetected;
private static readonly Action _orderBookReconnectingSocket;
@@ -30,6 +30,7 @@ namespace CryptoExchange.Net.Logging.Extensions
private static readonly Action _orderBookProcessedMessage;
private static readonly Action _orderBookProcessedMessageSingle;
private static readonly Action _orderBookOutOfSync;
+ private static readonly Action _orderBookUpdateSkippedStartEnd;
static SymbolOrderBookLoggingExtensions()
{
@@ -74,7 +75,7 @@ namespace CryptoExchange.Net.Logging.Extensions
"{Api} order book {Symbol} Processing {NumberBufferedUpdated} buffered updates");
_orderBookUpdateSkipped = LoggerMessage.Define(
- LogLevel.Debug,
+ LogLevel.Trace,
new EventId(5008, "OrderBookUpdateSkipped"),
"{Api} order book {Symbol} update skipped #{SequenceNumber}, currently at #{LastSequenceNumber}");
@@ -93,10 +94,10 @@ namespace CryptoExchange.Net.Logging.Extensions
new EventId(5011, "OrderBookMessageSkippedResubscribing"),
"{Api} order book {Symbol} Skipping message because of resubscribing");
- _orderBookDataSet = LoggerMessage.Define(
- LogLevel.Debug,
+ _orderBookDataSet = LoggerMessage.Define(
+ LogLevel.Trace,
new EventId(5012, "OrderBookDataSet"),
- "{Api} order book {Symbol} data set: {BidCount} bids, {AskCount} asks. #{EndUpdateId}");
+ "{Api} order book {Symbol} snapshot set: {BidCount} bids, {AskCount} asks. #{EndUpdateId}");
_orderBookUpdateBuffered = LoggerMessage.Define(
LogLevel.Trace,
@@ -142,6 +143,12 @@ namespace CryptoExchange.Net.Logging.Extensions
LogLevel.Trace,
new EventId(5021, "OrderBookProcessedMessage"),
"{Api} order book {Symbol} update processed #{UpdateId}");
+
+ _orderBookUpdateSkippedStartEnd = LoggerMessage.Define(
+ LogLevel.Trace,
+ new EventId(5022, "OrderBookUpdateSkippedStartEnd"),
+ "{Api} order book {Symbol} update skipped #{SequenceStart}-#{SequenceEnd}, currently at #{LastSequenceNumber}");
+
}
public static void OrderBookStatusChanged(this ILogger logger, string api, string symbol, OrderBookStatus previousStatus, OrderBookStatus newStatus)
@@ -200,7 +207,7 @@ namespace CryptoExchange.Net.Logging.Extensions
{
_orderBookMessageSkippedBecauseOfResubscribing(logger, api, symbol, null);
}
- public static void OrderBookDataSet(this ILogger logger, string api, string symbol, long bidCount, long askCount, long endUpdateId)
+ public static void OrderBookDataSet(this ILogger logger, string api, string symbol, long bidCount, long askCount, long? endUpdateId)
{
_orderBookDataSet(logger, api, symbol, bidCount, askCount, endUpdateId, null);
}
@@ -243,5 +250,10 @@ namespace CryptoExchange.Net.Logging.Extensions
{
_orderBookOutOfSyncChecksum(logger, api, symbol, null);
}
+
+ public static void OrderBookUpdateSkipped(this ILogger logger, string api, string symbol, long sequenceStart, long sequenceEnd, long lastSequenceNumber)
+ {
+ _orderBookUpdateSkippedStartEnd(logger, api, symbol, sequenceStart, sequenceEnd, lastSequenceNumber, null);
+ }
}
}
diff --git a/CryptoExchange.Net/Objects/Sockets/DataEvent.cs b/CryptoExchange.Net/Objects/Sockets/DataEvent.cs
index cacf430..88a7d71 100644
--- a/CryptoExchange.Net/Objects/Sockets/DataEvent.cs
+++ b/CryptoExchange.Net/Objects/Sockets/DataEvent.cs
@@ -53,6 +53,11 @@ namespace CryptoExchange.Net.Objects.Sockets
///
public SocketUpdateType? UpdateType { get; set; }
+ ///
+ /// Sequence number of the update
+ ///
+ public long? SequenceNumber { get; set; }
+
///
/// ctor
///
@@ -126,6 +131,15 @@ namespace CryptoExchange.Net.Objects.Sockets
return this;
}
+ ///
+ /// Specify the sequence number of the update
+ ///
+ public DataEvent WithSequenceNumber(long? sequenceNumber)
+ {
+ SequenceNumber = sequenceNumber;
+ return this;
+ }
+
///
/// Specify the data timestamp
///
diff --git a/CryptoExchange.Net/OrderBook/ProcessQueueItem.cs b/CryptoExchange.Net/OrderBook/OrderBookUpdate.cs
similarity index 71%
rename from CryptoExchange.Net/OrderBook/ProcessQueueItem.cs
rename to CryptoExchange.Net/OrderBook/OrderBookUpdate.cs
index 290d492..f8fae79 100644
--- a/CryptoExchange.Net/OrderBook/ProcessQueueItem.cs
+++ b/CryptoExchange.Net/OrderBook/OrderBookUpdate.cs
@@ -3,28 +3,28 @@ using System;
namespace CryptoExchange.Net.OrderBook
{
- internal class ProcessQueueItem
+ internal class OrderBookUpdate
{
public DateTime? LocalDataTime { get; set; }
public DateTime? ServerDataTime { get; set; }
- public long StartUpdateId { get; set; }
- public long EndUpdateId { get; set; }
+ public long StartSequenceNumber { get; set; }
+ public long EndSequenceNumber { get; set; }
public ISymbolOrderBookEntry[] Bids { get; set; } = Array.Empty();
public ISymbolOrderBookEntry[] Asks { get; set; } = Array.Empty();
}
- internal class InitialOrderBookItem
+ internal class OrderBookSnapshot
{
public DateTime? LocalDataTime { get; set; }
public DateTime? ServerDataTime { get; set; }
- public long StartUpdateId { get; set; }
- public long EndUpdateId { get; set; }
+ public long? SequenceNumber { get; set; }
public ISymbolOrderBookEntry[] Bids { get; set; } = Array.Empty();
public ISymbolOrderBookEntry[] Asks { get; set; } = Array.Empty();
}
- internal class ChecksumItem
+ internal class OrderBookChecksum
{
+ public long? SequenceNumber { get; set; }
public int Checksum { get; set; }
}
}
diff --git a/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs b/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs
index a3cf80a..3106381 100644
--- a/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs
+++ b/CryptoExchange.Net/OrderBook/SymbolOrderBook.cs
@@ -39,6 +39,7 @@ namespace CryptoExchange.Net.OrderBook
private readonly AsyncResetEvent _queueEvent;
private readonly ConcurrentQueue