@@ -20,6 +20,8 @@ The above copyright notice and this permission notice shall be included in all c
2020using System . Security . Cryptography ;
2121using System . Text ;
2222using System . Threading . Tasks ;
23+ using ExchangeSharp . API . Exchanges . Kraken . Models . Request ;
24+ using ExchangeSharp . API . Exchanges . Kraken . Models . Types ;
2325using Newtonsoft . Json ;
2426using Newtonsoft . Json . Linq ;
2527
@@ -39,6 +41,7 @@ private ExchangeKuCoinAPI()
3941 NonceEndPointStyle = NonceStyle . UnixMilliseconds ;
4042 MarketSymbolSeparator = "-" ;
4143 RateLimit = new RateGate ( 20 , TimeSpan . FromSeconds ( 60.0 ) ) ;
44+ WebSocketOrderBookType = WebSocketOrderBookType . FullBookFirstThenDeltas ;
4245 }
4346
4447 public override string PeriodSecondsToString ( int seconds )
@@ -233,7 +236,9 @@ protected override async Task<ExchangeOrderBook> OnGetOrderBookAsync(
233236 JToken token = await MakeJsonRequestAsync < JToken > (
234237 "/market/orderbook/level2_" + maxCount + "?symbol=" + marketSymbol
235238 ) ;
236- return token . ParseOrderBookFromJTokenArrays ( asks : "asks" , bids : "bids" ) ;
239+ var book = token . ParseOrderBookFromJTokenArrays ( asks : "asks" , bids : "bids" , sequence : "sequence" ) ;
240+ book . MarketSymbol = marketSymbol ;
241+ return book ;
237242 }
238243
239244 protected override async Task < ExchangeTicker > OnGetTickerAsync ( string marketSymbol )
@@ -804,6 +809,127 @@ await _socket.SendMessageAsync(
804809 ) ;
805810 }
806811
812+ protected override async Task < IWebSocket > OnGetDeltaOrderBookWebSocketAsync (
813+ Action < ExchangeOrderBook > callback ,
814+ int maxCount = 20 ,
815+ params string [ ] marketSymbols
816+ )
817+ {
818+ if ( marketSymbols == null || marketSymbols . Length == 0 )
819+ {
820+ marketSymbols = ( await GetMarketSymbolsAsync ( true ) ) . ToArray ( ) ;
821+ }
822+
823+ var initialSequenceIds = new Dictionary < string , long > ( ) ;
824+
825+ var websocketUrlToken = GetWebsocketBulletToken ( ) ;
826+
827+ return await ConnectPublicWebSocketAsync (
828+ $ "?token={ websocketUrlToken } &acceptUserMessage=true",
829+ messageCallback : ( _socket , msg ) =>
830+ {
831+ var message = msg . ToStringFromUTF8 ( ) ;
832+ var deserializedMessage = JsonConvert . DeserializeObject ( message ) as JObject ;
833+
834+ var data = deserializedMessage [ "data" ] ;
835+ if ( data is null )
836+ {
837+ return Task . CompletedTask ;
838+ }
839+
840+ var changes = data [ "changes" ] ;
841+ var time = data [ "time" ] ;
842+ var symbol = data [ "symbol" ] ;
843+ if ( changes is null || time is null || symbol is null )
844+ {
845+ return Task . CompletedTask ;
846+ }
847+
848+ var parsedTime = time . ConvertInvariant < long > ( ) ;
849+ var lastUpdatedDateTime = DateTimeOffset
850+ . FromUnixTimeMilliseconds ( parsedTime )
851+ . DateTime ;
852+
853+ var deltaBook = new ExchangeOrderBook
854+ {
855+ IsFromSnapshot = false ,
856+ ExchangeName = ExchangeName . Kucoin ,
857+ SequenceId = parsedTime ,
858+ MarketSymbol = symbol . ToString ( ) ,
859+ LastUpdatedUtc = lastUpdatedDateTime ,
860+ } ;
861+
862+ var rawAsks = changes [ "asks" ] as JArray ;
863+ foreach ( var rawAsk in rawAsks )
864+ {
865+ var sequence = rawAsk [ 2 ] . ConvertInvariant < long > ( ) ;
866+ if ( sequence <= initialSequenceIds [ deltaBook . MarketSymbol ] )
867+ {
868+ // A deprecated update should be ignored
869+ continue ;
870+ }
871+
872+ var price = rawAsk [ 0 ] . ConvertInvariant < decimal > ( ) ;
873+ var quantity = rawAsk [ 1 ] . ConvertInvariant < decimal > ( ) ;
874+
875+ deltaBook . Asks [ price ] = new ExchangeOrderPrice
876+ {
877+ Price = price ,
878+ Amount = quantity ,
879+ } ;
880+ }
881+
882+ var rawBids = changes [ "bids" ] as JArray ;
883+ foreach ( var rawBid in rawBids )
884+ {
885+ var sequence = rawBid [ 2 ] . ConvertInvariant < long > ( ) ;
886+ if ( sequence <= initialSequenceIds [ deltaBook . MarketSymbol ] )
887+ {
888+ // A deprecated update should be ignored
889+ continue ;
890+ }
891+
892+ var price = rawBid [ 0 ] . ConvertInvariant < decimal > ( ) ;
893+ var quantity = rawBid [ 1 ] . ConvertInvariant < decimal > ( ) ;
894+
895+ deltaBook . Bids [ price ] = new ExchangeOrderPrice
896+ {
897+ Price = price ,
898+ Amount = quantity ,
899+ } ;
900+ }
901+
902+ callback ( deltaBook ) ;
903+
904+ return Task . CompletedTask ;
905+ } ,
906+ connectCallback : async ( _socket ) =>
907+ {
908+ // Get full order book snapshot when connecting
909+ foreach ( var marketSymbol in marketSymbols )
910+ {
911+ var initialBook = await OnGetOrderBookAsync ( marketSymbol , maxCount ) ;
912+ initialBook . IsFromSnapshot = true ;
913+
914+ callback ( initialBook ) ;
915+
916+ initialSequenceIds [ marketSymbol ] = initialBook . SequenceId ;
917+ }
918+
919+ var id = CryptoUtility . UtcNow . Ticks ;
920+ var topic = $ "/market/level2:{ string . Join ( "," , marketSymbols ) } ";
921+ await _socket . SendMessageAsync (
922+ new
923+ {
924+ id = id ++ ,
925+ type = "subscribe" ,
926+ topic = topic ,
927+ }
928+ ) ;
929+ }
930+ ) ;
931+ }
932+
807933 #endregion Websockets
808934
809935 #region Private Functions
0 commit comments