From d8a75b0d9e57a37fc83137d04bbd82b8399f28a0 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 12 Aug 2021 22:07:00 +0100 Subject: [PATCH 1/2] Fix Kraken WS orderbook updates and add checksum support --- .../API/Exchanges/Kraken/ExchangeKrakenAPI.cs | 37 +++++++++++++------ src/ExchangeSharp/Model/ExchangeOrderBook.cs | 15 ++++++-- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/ExchangeSharp/API/Exchanges/Kraken/ExchangeKrakenAPI.cs b/src/ExchangeSharp/API/Exchanges/Kraken/ExchangeKrakenAPI.cs index b3ff7788..75add8ed 100644 --- a/src/ExchangeSharp/API/Exchanges/Kraken/ExchangeKrakenAPI.cs +++ b/src/ExchangeSharp/API/Exchanges/Kraken/ExchangeKrakenAPI.cs @@ -1142,6 +1142,15 @@ private async Task> GetMarketSymbolList(string[] marketSymbols) return marketSymbolList; } + /// + /// Handle Kraken "book" channel message: https://docs.kraken.com/websockets/#message-book + /// Note in the "update payload" case there may be varying number of update blocks for + /// bid/ask updates. The last such block has a checksum. The market symbol is always the last item. + /// + /// + /// + /// + /// protected override async Task OnGetDeltaOrderBookWebSocketAsync( Action callback, int maxCount = 20, @@ -1158,15 +1167,16 @@ params string[] marketSymbols string message = msg.ToStringFromUTF8(); var book = new ExchangeOrderBook(); + // SNAPSHOT payload if (message.Contains("\"as\"") || message.Contains("\"bs\"")) { // parse delta update - var delta = JsonConvert.DeserializeObject(message) as JArray; + var snapshot = JsonConvert.DeserializeObject(message) as JArray; - book.MarketSymbol = delta[3].ToString(); + book.MarketSymbol = snapshot[3].ToString(); - var asks = delta[1]["as"].ToList(); - var bids = delta[1]["bs"].ToList(); + var asks = snapshot[1]["as"].ToList(); + var bids = snapshot[1]["bs"].ToList(); var lastUpdatedTime = DateTime.MinValue; @@ -1205,16 +1215,16 @@ params string[] marketSymbols { // parse delta update var delta = JsonConvert.DeserializeObject(message) as JArray; + book.MarketSymbol = delta.Last.ToString(); - book.MarketSymbol = delta[3].ToString(); + var _a = delta.FirstOrDefault(token => token is JObject && token["a"] != null); + var _b = delta.FirstOrDefault(token => token is JObject && token["b"] != null); var lastUpdatedTime = DateTime.MinValue; - var updates = delta[1]; - - if (updates["a"] != null) + if (_a != null) { - var asks = updates["a"].ToList(); + var asks = _a["a"].ToList(); foreach (var ask in asks) { @@ -1230,9 +1240,9 @@ params string[] marketSymbols } } - if (updates["b"] != null) + if (_b != null) { - var bids = updates["b"].ToList(); + var bids = _b["b"].ToList(); foreach (var bid in bids) { @@ -1251,6 +1261,11 @@ params string[] marketSymbols book.LastUpdatedUtc = lastUpdatedTime; book.SequenceId = lastUpdatedTime.Ticks; + //https://docs.kraken.com/websockets/#book-checksum + //"c" belongs to the last update block + var checksum = _b?["c"] ?? _a?["c"]; + book.Checksum = (checksum as JValue)?.ToString(); + callback(book); } diff --git a/src/ExchangeSharp/Model/ExchangeOrderBook.cs b/src/ExchangeSharp/Model/ExchangeOrderBook.cs index d644a972..23a9f77f 100644 --- a/src/ExchangeSharp/Model/ExchangeOrderBook.cs +++ b/src/ExchangeSharp/Model/ExchangeOrderBook.cs @@ -93,10 +93,17 @@ public sealed class ExchangeOrderBook public SortedDictionary Bids { get; } = new SortedDictionary(new DescendingComparer()); /// - /// ToString - /// - /// String - public override string ToString() + /// If provided by the exchange, a checksum value that may be used to check orderbook integrity. + /// Otherwise it will be null. + /// This property is not serialized using the ToBinary and FromBinary methods. + /// + public string Checksum { get; set; } + + /// + /// ToString + /// + /// String + public override string ToString() { return string.Format("Book {0}, Asks: {1} ({2:0.00}), Bids: {3} ({4:0.00})", MarketSymbol, Asks.Count, From df2d1a026ea0de998cfc24bfddd977b9e94807ea Mon Sep 17 00:00:00 2001 From: John Date: Sun, 15 Aug 2021 18:10:56 +0100 Subject: [PATCH 2/2] Update ExchangeOrderBook.cs --- src/ExchangeSharp/Model/ExchangeOrderBook.cs | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/ExchangeSharp/Model/ExchangeOrderBook.cs b/src/ExchangeSharp/Model/ExchangeOrderBook.cs index 23a9f77f..4446c603 100644 --- a/src/ExchangeSharp/Model/ExchangeOrderBook.cs +++ b/src/ExchangeSharp/Model/ExchangeOrderBook.cs @@ -195,5 +195,30 @@ public decimal GetPriceToSell(decimal amount) return sellPrice; } + + /// + /// Updates this order book with a partial order book update. items with a price-level + /// of 0 are removed from the orderbook, all others are inserted/updated with the supplied value + /// + /// Set of changes to make + public void ApplyUpdates(ExchangeOrderBook partialUpdate) + { + MergeOrderBookDelta(partialUpdate.Asks, this.Asks); + MergeOrderBookDelta(partialUpdate.Bids, this.Bids); + + static void MergeOrderBookDelta( + SortedDictionary newData, + SortedDictionary bookData) + { + newData.ToList().ForEach(x => + { + if (x.Value.Amount == 0m) + bookData.Remove(x.Key); + else + bookData[x.Key] = x.Value; + }); + } + + } } }