mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-06-09 00:46:19 +00:00
Renames, orderbook changes
This commit is contained in:
parent
3ba0507a79
commit
6f75af507a
@ -871,6 +871,16 @@
|
||||
The price of the entry
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:CryptoExchange.Net.Interfaces.ISymbolOrderSequencedBookEntry">
|
||||
<summary>
|
||||
Interface for order book entries
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.Interfaces.ISymbolOrderSequencedBookEntry.Sequence">
|
||||
<summary>
|
||||
Sequence of the update
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:CryptoExchange.Net.Interfaces.IWebsocket">
|
||||
<summary>
|
||||
Interface for websocket interaction
|
||||
@ -1649,16 +1659,6 @@
|
||||
Buffer entry for order book
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferEntry.FirstSequence">
|
||||
<summary>
|
||||
The first sequence number of the entries
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferEntry.LastSequence">
|
||||
<summary>
|
||||
The last sequence number of the entries
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferEntry.Asks">
|
||||
<summary>
|
||||
List of asks
|
||||
@ -1669,6 +1669,41 @@
|
||||
List of bids
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferSingleSequenceEntry.UpdateId">
|
||||
<summary>
|
||||
First update id
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferSingleSequenceEntry.Asks">
|
||||
<summary>
|
||||
List of asks
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferSingleSequenceEntry.Bids">
|
||||
<summary>
|
||||
List of bids
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferRangeSequenceEntry.FirstUpdateId">
|
||||
<summary>
|
||||
First update id
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferRangeSequenceEntry.LastUpdateId">
|
||||
<summary>
|
||||
Last update id
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferRangeSequenceEntry.Asks">
|
||||
<summary>
|
||||
List of asks
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.OrderBook.ProcessBufferRangeSequenceEntry.Bids">
|
||||
<summary>
|
||||
List of bids
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:CryptoExchange.Net.OrderBook.SymbolOrderBook">
|
||||
<summary>
|
||||
Base for order book implementations
|
||||
@ -1699,6 +1734,11 @@
|
||||
The log
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:CryptoExchange.Net.OrderBook.SymbolOrderBook.bookSet">
|
||||
<summary>
|
||||
If order book is set
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.OrderBook.SymbolOrderBook.Status">
|
||||
<summary>
|
||||
The status of the order book. Order book is up to date when the status is `Synced`
|
||||
@ -1722,7 +1762,7 @@
|
||||
<member name="E:CryptoExchange.Net.OrderBook.SymbolOrderBook.OnOrderBookUpdate">
|
||||
<summary>
|
||||
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
|
||||
</summary>
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:CryptoExchange.Net.OrderBook.SymbolOrderBook.LastOrderBookUpdate">
|
||||
<summary>
|
||||
@ -1815,12 +1855,27 @@
|
||||
<param name="askList">List of asks</param>
|
||||
<param name="bidList">List of bids</param>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.OrderBook.SymbolOrderBook.UpdateOrderBook(System.Int64,System.Collections.Generic.IEnumerable{CryptoExchange.Net.Interfaces.ISymbolOrderBookEntry},System.Collections.Generic.IEnumerable{CryptoExchange.Net.Interfaces.ISymbolOrderBookEntry})">
|
||||
<summary>
|
||||
Update the order book using a single id for an update
|
||||
</summary>
|
||||
<param name="rangeUpdateId"></param>
|
||||
<param name="bids"></param>
|
||||
<param name="asks"></param>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.OrderBook.SymbolOrderBook.UpdateOrderBook(System.Int64,System.Int64,System.Collections.Generic.IEnumerable{CryptoExchange.Net.Interfaces.ISymbolOrderBookEntry},System.Collections.Generic.IEnumerable{CryptoExchange.Net.Interfaces.ISymbolOrderBookEntry})">
|
||||
<summary>
|
||||
Update the order book with entries
|
||||
Update the order book using a first/last update id
|
||||
</summary>
|
||||
<param name="firstUpdateId"></param>
|
||||
<param name="lastUpdateId"></param>
|
||||
<param name="bids"></param>
|
||||
<param name="asks"></param>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.OrderBook.SymbolOrderBook.UpdateOrderBook(System.Collections.Generic.IEnumerable{CryptoExchange.Net.Interfaces.ISymbolOrderSequencedBookEntry},System.Collections.Generic.IEnumerable{CryptoExchange.Net.Interfaces.ISymbolOrderSequencedBookEntry})">
|
||||
<summary>
|
||||
Update the order book using sequenced entries
|
||||
</summary>
|
||||
<param name="firstSequenceNumber">First sequence number</param>
|
||||
<param name="lastSequenceNumber">Last sequence number</param>
|
||||
<param name="bids">List of bids</param>
|
||||
<param name="asks">List of asks</param>
|
||||
</member>
|
||||
@ -1829,13 +1884,21 @@
|
||||
Check and empty the process buffer; see what entries to update the book with
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.OrderBook.SymbolOrderBook.ProcessUpdate(CryptoExchange.Net.Objects.OrderBookEntryType,CryptoExchange.Net.Interfaces.ISymbolOrderBookEntry)">
|
||||
<member name="M:CryptoExchange.Net.OrderBook.SymbolOrderBook.ProcessUpdate(System.Int64,CryptoExchange.Net.Objects.OrderBookEntryType,CryptoExchange.Net.Interfaces.ISymbolOrderBookEntry)">
|
||||
<summary>
|
||||
Update order book with an entry
|
||||
</summary>
|
||||
<param name="sequence">Sequence number of the update</param>
|
||||
<param name="type">Type of entry</param>
|
||||
<param name="entry">The entry</param>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.OrderBook.SymbolOrderBook.WaitForSetOrderBook(System.Int32)">
|
||||
<summary>
|
||||
Wait until the order book has been set
|
||||
</summary>
|
||||
<param name="timeout">Max wait time</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:CryptoExchange.Net.OrderBook.SymbolOrderBook.Dispose">
|
||||
<summary>
|
||||
Dispose the order book
|
||||
@ -2815,148 +2878,5 @@
|
||||
<member name="M:CryptoExchange.Net.Sockets.WebsocketFactory.CreateWebsocket(CryptoExchange.Net.Logging.Log,System.String,System.Collections.Generic.IDictionary{System.String,System.String},System.Collections.Generic.IDictionary{System.String,System.String})">
|
||||
<inheritdoc />
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.AllowNullAttribute">
|
||||
<summary>
|
||||
Specifies that <see langword="null"/> is allowed as an input even if the
|
||||
corresponding type disallows it.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.AllowNullAttribute.#ctor">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.AllowNullAttribute"/> class.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.DisallowNullAttribute">
|
||||
<summary>
|
||||
Specifies that <see langword="null"/> is disallowed as an input even if the
|
||||
corresponding type allows it.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.DisallowNullAttribute.#ctor">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.DisallowNullAttribute"/> class.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute">
|
||||
<summary>
|
||||
Specifies that a method that will never return under any circumstance.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute.#ctor">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute"/> class.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute">
|
||||
<summary>
|
||||
Specifies that the method will not return if the associated <see cref="T:System.Boolean"/>
|
||||
parameter is passed the specified value.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute.ParameterValue">
|
||||
<summary>
|
||||
Gets the condition parameter value.
|
||||
Code after the method is considered unreachable by diagnostics if the argument
|
||||
to the associated parameter matches this value.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute.#ctor(System.Boolean)">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute"/>
|
||||
class with the specified parameter value.
|
||||
</summary>
|
||||
<param name="parameterValue">
|
||||
The condition parameter value.
|
||||
Code after the method is considered unreachable by diagnostics if the argument
|
||||
to the associated parameter matches this value.
|
||||
</param>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute">
|
||||
<summary>
|
||||
Specifies that an output may be <see langword="null"/> even if the
|
||||
corresponding type disallows it.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.MaybeNullAttribute.#ctor">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute"/> class.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute">
|
||||
<summary>
|
||||
Specifies that when a method returns <see cref="P:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute.ReturnValue"/>,
|
||||
the parameter may be <see langword="null"/> even if the corresponding type disallows it.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute.ReturnValue">
|
||||
<summary>
|
||||
Gets the return value condition.
|
||||
If the method returns this value, the associated parameter may be <see langword="null"/>.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute.#ctor(System.Boolean)">
|
||||
<summary>
|
||||
Initializes the attribute with the specified return value condition.
|
||||
</summary>
|
||||
<param name="returnValue">
|
||||
The return value condition.
|
||||
If the method returns this value, the associated parameter may be <see langword="null"/>.
|
||||
</param>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.NotNullAttribute">
|
||||
<summary>
|
||||
Specifies that an output is not <see langword="null"/> even if the
|
||||
corresponding type allows it.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.NotNullAttribute.#ctor">
|
||||
<summary>
|
||||
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.NotNullAttribute"/> class.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute">
|
||||
<summary>
|
||||
Specifies that the output will be non-<see langword="null"/> if the
|
||||
named parameter is non-<see langword="null"/>.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute.ParameterName">
|
||||
<summary>
|
||||
Gets the associated parameter name.
|
||||
The output will be non-<see langword="null"/> if the argument to the
|
||||
parameter specified is non-<see langword="null"/>.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute.#ctor(System.String)">
|
||||
<summary>
|
||||
Initializes the attribute with the associated parameter name.
|
||||
</summary>
|
||||
<param name="parameterName">
|
||||
The associated parameter name.
|
||||
The output will be non-<see langword="null"/> if the argument to the
|
||||
parameter specified is non-<see langword="null"/>.
|
||||
</param>
|
||||
</member>
|
||||
<member name="T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute">
|
||||
<summary>
|
||||
Specifies that when a method returns <see cref="P:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.ReturnValue"/>,
|
||||
the parameter will not be <see langword="null"/> even if the corresponding type allows it.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.ReturnValue">
|
||||
<summary>
|
||||
Gets the return value condition.
|
||||
If the method returns this value, the associated parameter will not be <see langword="null"/>.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.#ctor(System.Boolean)">
|
||||
<summary>
|
||||
Initializes the attribute with the specified return value condition.
|
||||
</summary>
|
||||
<param name="returnValue">
|
||||
The return value condition.
|
||||
If the method returns this value, the associated parameter will not be <see langword="null"/>.
|
||||
</param>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
||||
|
@ -14,4 +14,15 @@
|
||||
/// </summary>
|
||||
decimal Price { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for order book entries
|
||||
/// </summary>
|
||||
public interface ISymbolOrderSequencedBookEntry: ISymbolOrderBookEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Sequence of the update
|
||||
/// </summary>
|
||||
long Sequence { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -9,13 +9,41 @@ namespace CryptoExchange.Net.OrderBook
|
||||
public class ProcessBufferEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The first sequence number of the entries
|
||||
/// List of asks
|
||||
/// </summary>
|
||||
public long FirstSequence { get; set; }
|
||||
public IEnumerable<ISymbolOrderSequencedBookEntry> Asks { get; set; } = new List<ISymbolOrderSequencedBookEntry>();
|
||||
/// <summary>
|
||||
/// The last sequence number of the entries
|
||||
/// List of bids
|
||||
/// </summary>
|
||||
public long LastSequence { get; set; }
|
||||
public IEnumerable<ISymbolOrderSequencedBookEntry> Bids { get; set; } = new List<ISymbolOrderSequencedBookEntry>();
|
||||
}
|
||||
|
||||
public class ProcessBufferSingleSequenceEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// First update id
|
||||
/// </summary>
|
||||
public long UpdateId { get; set; }
|
||||
/// <summary>
|
||||
/// List of asks
|
||||
/// </summary>
|
||||
public IEnumerable<ISymbolOrderBookEntry> Asks { get; set; } = new List<ISymbolOrderBookEntry>();
|
||||
/// <summary>
|
||||
/// List of bids
|
||||
/// </summary>
|
||||
public IEnumerable<ISymbolOrderBookEntry> Bids { get; set; } = new List<ISymbolOrderBookEntry>();
|
||||
}
|
||||
|
||||
public class ProcessBufferRangeSequenceEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// First update id
|
||||
/// </summary>
|
||||
public long FirstUpdateId { get; set; }
|
||||
/// <summary>
|
||||
/// Last update id
|
||||
/// </summary>
|
||||
public long LastUpdateId { get; set; }
|
||||
/// <summary>
|
||||
/// List of asks
|
||||
/// </summary>
|
||||
|
@ -19,7 +19,7 @@ namespace CryptoExchange.Net.OrderBook
|
||||
/// <summary>
|
||||
/// The process buffer, used while syncing
|
||||
/// </summary>
|
||||
protected readonly List<ProcessBufferEntry> processBuffer;
|
||||
protected readonly List<object> processBuffer;
|
||||
private readonly object bookLock = new object();
|
||||
/// <summary>
|
||||
/// The ask list
|
||||
@ -43,7 +43,10 @@ namespace CryptoExchange.Net.OrderBook
|
||||
/// </summary>
|
||||
protected Log log;
|
||||
|
||||
private bool bookSet;
|
||||
/// <summary>
|
||||
/// If order book is set
|
||||
/// </summary>
|
||||
protected bool bookSet;
|
||||
|
||||
/// <summary>
|
||||
/// The status of the order book. Order book is up to date when the status is `Synced`
|
||||
@ -78,7 +81,7 @@ namespace CryptoExchange.Net.OrderBook
|
||||
public event Action<OrderBookStatus, OrderBookStatus>? OnStatusChange;
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// </summary>
|
||||
public event Action<IEnumerable<ISymbolOrderBookEntry>, IEnumerable<ISymbolOrderBookEntry>>? OnOrderBookUpdate;
|
||||
/// <summary>
|
||||
/// Timestamp of the last update
|
||||
@ -156,7 +159,7 @@ namespace CryptoExchange.Net.OrderBook
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
|
||||
Id = options.OrderBookName;
|
||||
processBuffer = new List<ProcessBufferEntry>();
|
||||
processBuffer = new List<object>();
|
||||
sequencesAreConsecutive = options.SequenceNumbersAreConsecutive;
|
||||
Symbol = symbol;
|
||||
Status = OrderBookStatus.Disconnected;
|
||||
@ -259,11 +262,11 @@ namespace CryptoExchange.Net.OrderBook
|
||||
/// <param name="orderBookSequenceNumber">The last update sequence number</param>
|
||||
/// <param name="askList">List of asks</param>
|
||||
/// <param name="bidList">List of bids</param>
|
||||
protected void SetInitialOrderBook(long orderBookSequenceNumber, IEnumerable<ISymbolOrderBookEntry> askList, IEnumerable<ISymbolOrderBookEntry> bidList)
|
||||
protected void SetInitialOrderBook(long orderBookSequenceNumber, IEnumerable<ISymbolOrderBookEntry> bidList, IEnumerable<ISymbolOrderBookEntry> askList)
|
||||
{
|
||||
lock (bookLock)
|
||||
{
|
||||
if (Status == OrderBookStatus.Connecting)
|
||||
if (Status == OrderBookStatus.Connecting || Status == OrderBookStatus.Disconnected)
|
||||
return;
|
||||
|
||||
asks.Clear();
|
||||
@ -278,99 +281,213 @@ namespace CryptoExchange.Net.OrderBook
|
||||
AskCount = asks.Count;
|
||||
BidCount = asks.Count;
|
||||
|
||||
CheckProcessBuffer();
|
||||
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);
|
||||
log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} data set: {BidCount} bids, {AskCount} asks");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the order book with entries
|
||||
/// Update the order book using a single id for an update
|
||||
/// </summary>
|
||||
/// <param name="firstSequenceNumber">First sequence number</param>
|
||||
/// <param name="lastSequenceNumber">Last sequence number</param>
|
||||
/// <param name="bids">List of bids</param>
|
||||
/// <param name="asks">List of asks</param>
|
||||
protected void UpdateOrderBook(long firstSequenceNumber, long lastSequenceNumber, IEnumerable<ISymbolOrderBookEntry> bids, IEnumerable<ISymbolOrderBookEntry> asks)
|
||||
/// <param name="rangeUpdateId"></param>
|
||||
/// <param name="bids"></param>
|
||||
/// <param name="asks"></param>
|
||||
protected void UpdateOrderBook(long rangeUpdateId, IEnumerable<ISymbolOrderBookEntry> bids, IEnumerable<ISymbolOrderBookEntry> asks)
|
||||
{
|
||||
lock (bookLock)
|
||||
{
|
||||
if (lastSequenceNumber < LastSequenceNumber)
|
||||
if (Status == OrderBookStatus.Connecting || Status == OrderBookStatus.Disconnected)
|
||||
return;
|
||||
|
||||
if (!bookSet)
|
||||
{
|
||||
var entry = new ProcessBufferEntry
|
||||
processBuffer.Add(new ProcessBufferSingleSequenceEntry()
|
||||
{
|
||||
FirstSequence = firstSequenceNumber,
|
||||
LastSequence = lastSequenceNumber,
|
||||
UpdateId = rangeUpdateId,
|
||||
Asks = asks,
|
||||
Bids = bids
|
||||
};
|
||||
processBuffer.Add(entry);
|
||||
log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} update before synced; buffering");
|
||||
}
|
||||
else if (sequencesAreConsecutive && firstSequenceNumber > LastSequenceNumber + 1)
|
||||
{
|
||||
// Out of sync
|
||||
log.Write(LogVerbosity.Warning, $"{Id} order book {Symbol} out of sync, reconnecting");
|
||||
subscription!.Reconnect().Wait();
|
||||
});
|
||||
log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} update buffered #{rangeUpdateId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var entry in asks)
|
||||
ProcessUpdate(OrderBookEntryType.Ask, entry);
|
||||
foreach (var entry in bids)
|
||||
ProcessUpdate(OrderBookEntryType.Bid, entry);
|
||||
LastSequenceNumber = lastSequenceNumber;
|
||||
CheckProcessBuffer();
|
||||
LastOrderBookUpdate = DateTime.UtcNow;
|
||||
ProcessSingleSequenceUpdates(rangeUpdateId, bids, asks);
|
||||
OnOrderBookUpdate?.Invoke(bids, asks);
|
||||
log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} update: {asks.Count()} asks, {bids.Count()} bids processed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the order book using a first/last update id
|
||||
/// </summary>
|
||||
/// <param name="firstUpdateId"></param>
|
||||
/// <param name="lastUpdateId"></param>
|
||||
/// <param name="bids"></param>
|
||||
/// <param name="asks"></param>
|
||||
protected void UpdateOrderBook(long firstUpdateId, long lastUpdateId, IEnumerable<ISymbolOrderBookEntry> bids, IEnumerable<ISymbolOrderBookEntry> asks)
|
||||
{
|
||||
lock (bookLock)
|
||||
{
|
||||
if (Status == OrderBookStatus.Connecting || Status == OrderBookStatus.Disconnected)
|
||||
return;
|
||||
|
||||
if (!bookSet)
|
||||
{
|
||||
processBuffer.Add(new ProcessBufferRangeSequenceEntry()
|
||||
{
|
||||
Asks = asks,
|
||||
Bids = bids,
|
||||
FirstUpdateId = firstUpdateId,
|
||||
LastUpdateId = lastUpdateId
|
||||
});
|
||||
log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} update buffered #{firstUpdateId}-{lastUpdateId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckProcessBuffer();
|
||||
ProcessRangeUpdates(firstUpdateId, lastUpdateId, bids, asks);
|
||||
OnOrderBookUpdate?.Invoke(bids, asks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the order book using sequenced entries
|
||||
/// </summary>
|
||||
/// <param name="bids">List of bids</param>
|
||||
/// <param name="asks">List of asks</param>
|
||||
protected void UpdateOrderBook(IEnumerable<ISymbolOrderSequencedBookEntry> bids, IEnumerable<ISymbolOrderSequencedBookEntry> asks)
|
||||
{
|
||||
lock (bookLock)
|
||||
{
|
||||
if (!bookSet)
|
||||
{
|
||||
processBuffer.Add(new ProcessBufferEntry
|
||||
{
|
||||
Asks = asks,
|
||||
Bids = bids
|
||||
});
|
||||
log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} update buffered #{Math.Min(bids.Min(b => b.Sequence), asks.Min(a => a.Sequence))}-{Math.Max(bids.Max(b => b.Sequence), asks.Max(a => a.Sequence))}");
|
||||
}
|
||||
else
|
||||
{
|
||||
CheckProcessBuffer();
|
||||
ProcessUpdates(bids, asks);
|
||||
OnOrderBookUpdate?.Invoke(bids, asks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessUpdates(IEnumerable<ISymbolOrderSequencedBookEntry> bids, IEnumerable<ISymbolOrderSequencedBookEntry> asks)
|
||||
{
|
||||
var entries = new Dictionary<ISymbolOrderSequencedBookEntry, OrderBookEntryType>();
|
||||
foreach (var entry in asks.OrderBy(a => a.Sequence))
|
||||
entries.Add(entry, OrderBookEntryType.Ask);
|
||||
foreach (var entry in bids.OrderBy(a => a.Sequence))
|
||||
entries.Add(entry, OrderBookEntryType.Bid);
|
||||
|
||||
foreach (var entry in entries.OrderBy(e => e.Key.Sequence))
|
||||
{
|
||||
if(ProcessUpdate(entry.Key.Sequence, entry.Value, entry.Key))
|
||||
LastSequenceNumber = entry.Key.Sequence;
|
||||
log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} update #{LastSequenceNumber}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessRangeUpdates(long firstUpdateId, long lastUpdateId, IEnumerable<ISymbolOrderBookEntry> bids, IEnumerable<ISymbolOrderBookEntry> asks)
|
||||
{
|
||||
if (lastUpdateId < LastSequenceNumber)
|
||||
{
|
||||
log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} update skipped #{firstUpdateId}-{lastUpdateId}");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var entry in bids)
|
||||
ProcessUpdate(LastSequenceNumber + 1, OrderBookEntryType.Bid, entry);
|
||||
|
||||
foreach (var entry in asks)
|
||||
ProcessUpdate(LastSequenceNumber + 1, OrderBookEntryType.Ask, entry);
|
||||
|
||||
LastSequenceNumber = lastUpdateId;
|
||||
log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} update processed #{firstUpdateId}-{lastUpdateId}");
|
||||
}
|
||||
|
||||
private void ProcessSingleSequenceUpdates(long updateId, IEnumerable<ISymbolOrderBookEntry> bids, IEnumerable<ISymbolOrderBookEntry> asks)
|
||||
{
|
||||
foreach (var entry in bids)
|
||||
{
|
||||
if (!ProcessUpdate(updateId, OrderBookEntryType.Bid, entry))
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var entry in asks)
|
||||
{
|
||||
if (!ProcessUpdate(updateId, OrderBookEntryType.Ask, entry))
|
||||
return;
|
||||
}
|
||||
|
||||
LastSequenceNumber = updateId;
|
||||
log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} update processed #{LastSequenceNumber}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check and empty the process buffer; see what entries to update the book with
|
||||
/// </summary>
|
||||
protected void CheckProcessBuffer()
|
||||
{
|
||||
foreach (var bufferEntry in processBuffer.OrderBy(b => b.FirstSequence).ToList())
|
||||
var pbList = processBuffer.ToList();
|
||||
if(pbList.Count > 0)
|
||||
log.Write(LogVerbosity.Debug, "Processing buffered updates");
|
||||
|
||||
foreach (var bufferEntry in pbList)
|
||||
{
|
||||
if (bufferEntry.LastSequence < LastSequenceNumber)
|
||||
{
|
||||
processBuffer.Remove(bufferEntry);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bufferEntry.FirstSequence > LastSequenceNumber + 1)
|
||||
break;
|
||||
|
||||
foreach (var entry in bufferEntry.Asks)
|
||||
ProcessUpdate(OrderBookEntryType.Ask, entry);
|
||||
foreach (var entry in bufferEntry.Bids)
|
||||
ProcessUpdate(OrderBookEntryType.Bid, entry);
|
||||
if (bufferEntry is ProcessBufferEntry pbe)
|
||||
ProcessUpdates(pbe.Bids, pbe.Asks);
|
||||
else if(bufferEntry is ProcessBufferRangeSequenceEntry pbrse)
|
||||
ProcessRangeUpdates(pbrse.FirstUpdateId, pbrse.LastUpdateId, pbrse.Bids, pbrse.Asks);
|
||||
else if (bufferEntry is ProcessBufferSingleSequenceEntry pbsse)
|
||||
ProcessSingleSequenceUpdates(pbsse.UpdateId, pbsse.Bids, pbsse.Asks);
|
||||
|
||||
processBuffer.Remove(bufferEntry);
|
||||
LastSequenceNumber = bufferEntry.LastSequence;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update order book with an entry
|
||||
/// </summary>
|
||||
/// <param name="sequence">Sequence number of the update</param>
|
||||
/// <param name="type">Type of entry</param>
|
||||
/// <param name="entry">The entry</param>
|
||||
protected virtual void ProcessUpdate(OrderBookEntryType type, ISymbolOrderBookEntry entry)
|
||||
protected virtual bool ProcessUpdate(long sequence, OrderBookEntryType type, ISymbolOrderBookEntry entry)
|
||||
{
|
||||
if (Status != OrderBookStatus.Syncing && Status != OrderBookStatus.Synced)
|
||||
return false;
|
||||
|
||||
if (sequence <= LastSequenceNumber)
|
||||
{
|
||||
log.Write(LogVerbosity.Debug, $"{Id} order book {Symbol} update skipped #{sequence}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sequencesAreConsecutive && sequence > LastSequenceNumber + 1)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
LastOrderBookUpdate = DateTime.UtcNow;
|
||||
var listToChange = type == OrderBookEntryType.Ask ? asks : bids;
|
||||
if (entry.Quantity == 0)
|
||||
{
|
||||
if (!listToChange.ContainsKey(entry.Price))
|
||||
return;
|
||||
return true;
|
||||
|
||||
listToChange.Remove(entry.Price);
|
||||
if (type == OrderBookEntryType.Ask) AskCount--;
|
||||
@ -389,6 +506,27 @@ namespace CryptoExchange.Net.OrderBook
|
||||
listToChange[entry.Price].Quantity = entry.Quantity;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait until the order book has been set
|
||||
/// </summary>
|
||||
/// <param name="timeout">Max wait time</param>
|
||||
/// <returns></returns>
|
||||
protected async Task<CallResult<bool>> WaitForSetOrderBook(int timeout)
|
||||
{
|
||||
var startWait = DateTime.UtcNow;
|
||||
while (!bookSet && Status == OrderBookStatus.Syncing)
|
||||
{
|
||||
if ((DateTime.UtcNow - startWait).TotalMilliseconds > timeout)
|
||||
return new CallResult<bool>(false, new ServerError("Timeout while waiting for data"));
|
||||
|
||||
await Task.Delay(10).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return new CallResult<bool>(true, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
Loading…
x
Reference in New Issue
Block a user