@@ -20,6 +20,8 @@ The above copyright notice and this permission notice shall be included in all c
20
20
using System . Security . Cryptography ;
21
21
using System . Text ;
22
22
using System . Threading . Tasks ;
23
+ using ExchangeSharp . API . Exchanges . Kraken . Models . Request ;
24
+ using ExchangeSharp . API . Exchanges . Kraken . Models . Types ;
23
25
using Newtonsoft . Json ;
24
26
using Newtonsoft . Json . Linq ;
25
27
@@ -39,6 +41,7 @@ private ExchangeKuCoinAPI()
39
41
NonceEndPointStyle = NonceStyle . UnixMilliseconds ;
40
42
MarketSymbolSeparator = "-" ;
41
43
RateLimit = new RateGate ( 20 , TimeSpan . FromSeconds ( 60.0 ) ) ;
44
+ WebSocketOrderBookType = WebSocketOrderBookType . FullBookFirstThenDeltas ;
42
45
}
43
46
44
47
public override string PeriodSecondsToString ( int seconds )
@@ -233,7 +236,9 @@ protected override async Task<ExchangeOrderBook> OnGetOrderBookAsync(
233
236
JToken token = await MakeJsonRequestAsync < JToken > (
234
237
"/market/orderbook/level2_" + maxCount + "?symbol=" + marketSymbol
235
238
) ;
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 ;
237
242
}
238
243
239
244
protected override async Task < ExchangeTicker > OnGetTickerAsync ( string marketSymbol )
@@ -804,6 +809,127 @@ await _socket.SendMessageAsync(
804
809
) ;
805
810
}
806
811
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
+
807
933
#endregion Websockets
808
934
809
935
#region Private Functions
0 commit comments