From a0764a88fc3c01eda1d94d6086264c3d9b95ffab Mon Sep 17 00:00:00 2001
From: Vlad Dumitru <vlad-dumitru@outlook.com>
Date: Mon, 20 Jan 2025 16:14:10 +0200
Subject: [PATCH] Fix cap problem

---
 .../API/Exchanges/KuCoin/ExchangeKuCoinAPI.cs | 25 +++++-----
 .../API/Exchanges/MEXC/ExchangeMEXCAPI.cs     | 43 +++++++++-------
 .../Exchanges/_Base/ExchangeAPIExtensions.cs  | 50 +++++++++++++------
 3 files changed, 72 insertions(+), 46 deletions(-)

diff --git a/src/ExchangeSharp/API/Exchanges/KuCoin/ExchangeKuCoinAPI.cs b/src/ExchangeSharp/API/Exchanges/KuCoin/ExchangeKuCoinAPI.cs
index 5b79ad70..a0735912 100644
--- a/src/ExchangeSharp/API/Exchanges/KuCoin/ExchangeKuCoinAPI.cs
+++ b/src/ExchangeSharp/API/Exchanges/KuCoin/ExchangeKuCoinAPI.cs
@@ -40,7 +40,7 @@ private ExchangeKuCoinAPI()
 			NonceEndPointField = "data";
 			NonceEndPointStyle = NonceStyle.UnixMilliseconds;
 			MarketSymbolSeparator = "-";
-			RateLimit = new RateGate(20, TimeSpan.FromSeconds(60.0));
+			RateLimit = new RateGate(60, TimeSpan.FromSeconds(1)); // https://www.kucoin.com/docs/basic-info/request-rate-limit/rest-api
 			WebSocketOrderBookType = WebSocketOrderBookType.FullBookFirstThenDeltas;
 		}
 
@@ -875,6 +875,16 @@ params string[] marketSymbols
 
 			var initialSequenceIds = new Dictionary<string, long>();
 
+			foreach (var marketSymbol in marketSymbols)
+			{
+				var initialBook = await OnGetOrderBookAsync(marketSymbol, maxCount);
+				initialBook.IsFromSnapshot = true;
+
+				callback(initialBook);
+
+				initialSequenceIds[marketSymbol] = initialBook.SequenceId;
+			}
+
 			var websocketUrlToken = GetWebsocketBulletToken();
 
 			return await ConnectPublicWebSocketAsync(
@@ -958,19 +968,10 @@ params string[] marketSymbols
 					},
 					connectCallback: async (_socket) =>
 					{
-						// Get full order book snapshot when connecting
-						foreach (var marketSymbol in marketSymbols)
-						{
-							var initialBook = await OnGetOrderBookAsync(marketSymbol, maxCount);
-							initialBook.IsFromSnapshot = true;
-
-							callback(initialBook);
-
-							initialSequenceIds[marketSymbol] = initialBook.SequenceId;
-						}
+						var marketSymbolsForSubscriptionString = string.Join(",", marketSymbols);
 
 						var id = CryptoUtility.UtcNow.Ticks;
-						var topic = $"/market/level2:{string.Join(",", marketSymbols)}";
+						var topic = $"/market/level2:{marketSymbolsForSubscriptionString}";
 						await _socket.SendMessageAsync(
 								new
 								{
diff --git a/src/ExchangeSharp/API/Exchanges/MEXC/ExchangeMEXCAPI.cs b/src/ExchangeSharp/API/Exchanges/MEXC/ExchangeMEXCAPI.cs
index c7f6280b..de30ec78 100644
--- a/src/ExchangeSharp/API/Exchanges/MEXC/ExchangeMEXCAPI.cs
+++ b/src/ExchangeSharp/API/Exchanges/MEXC/ExchangeMEXCAPI.cs
@@ -384,12 +384,28 @@ params string[] marketSymbols
 
 			var initialSequenceIds = new Dictionary<string, long>();
 
+			foreach (var marketSymbol in marketSymbols)
+			{
+				var initialBook = await OnGetOrderBookAsync(marketSymbol, maxCount);
+				initialBook.IsFromSnapshot = true;
+
+				callback(initialBook);
+
+				initialSequenceIds[marketSymbol] = initialBook.SequenceId;
+			}
+
 			return await ConnectPublicWebSocketAsync(
 					string.Empty,
 					(_socket, msg) =>
 					{
 						var json = msg.ToStringFromUTF8();
 
+						if (json.Contains("invalid") || json.Contains("Not Subscribed"))
+						{
+							Logger.Warn(json);
+							return Task.CompletedTask;
+						}
+
 						MarketDepthDiffUpdate update = null;
 						try
 						{
@@ -451,26 +467,15 @@ params string[] marketSymbols
 					},
 			    async (_socket) =>
 					{
-						foreach (var marketSymbol in marketSymbols) // "Every websocket connection maximum support 30 subscriptions at one time." - API docs
-						{
-							var initialBook = await OnGetOrderBookAsync(marketSymbol, maxCount);
-							initialBook.IsFromSnapshot = true;
-
-							callback(initialBook);
-
-							initialSequenceIds[marketSymbol] = initialBook.SequenceId;
-
-							var subscriptionParams = new List<string>
-							{
-								$"spot@public.increase.depth.v3.api@{marketSymbol}"
-							};
+						var subscriptionParams = marketSymbols
+							.Select(ms => $"spot@public.increase.depth.v3.api@{ms}")
+							.ToList();
 
-							await _socket.SendMessageAsync(new WebSocketSubscription
-							{
-								Method = "SUBSCRIPTION",
-								Params = subscriptionParams,
-							});
-						}
+						await _socket.SendMessageAsync(new WebSocketSubscription
+						{
+							Method = "SUBSCRIPTION",
+							Params = subscriptionParams,
+						});
 					}
 			);
 		}
diff --git a/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPIExtensions.cs b/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPIExtensions.cs
index 30606150..11016076 100644
--- a/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPIExtensions.cs
+++ b/src/ExchangeSharp/API/Exchanges/_Base/ExchangeAPIExtensions.cs
@@ -474,28 +474,48 @@ internal static ExchangeOrderBook ParseOrderBookFromJTokenArrays(
 				string sequence = "ts"
 		)
 		{
-			var book = new ExchangeOrderBook
+			var book = new ExchangeOrderBook();
+
+			if (token == null)
 			{
-				SequenceId = token[sequence].ConvertInvariant<long>()
-			};
-			foreach (var array in token[asks])
+				Logger.Warn($"Null token in {nameof(ParseOrderBookFromJTokenArrays)}");
+				return book;
+			}
+
+			book.SequenceId = token[sequence].ConvertInvariant<long>();
+
+			if (token[asks] != null)
 			{
-				var depth = new ExchangeOrderPrice
+				foreach (var array in token[asks])
 				{
-					Price = array[0].ConvertInvariant<decimal>(),
-					Amount = array[1].ConvertInvariant<decimal>()
-				};
-				book.Asks[depth.Price] = depth;
+					var depth = new ExchangeOrderPrice
+					{
+						Price = array[0].ConvertInvariant<decimal>(),
+						Amount = array[1].ConvertInvariant<decimal>()
+					};
+					book.Asks[depth.Price] = depth;
+				}
+			}
+			else
+			{
+				Logger.Warn($"No asks in {nameof(ParseOrderBookFromJTokenArrays)}");
 			}
 
-			foreach (var array in token[bids])
+			if (token[bids] != null)
 			{
-				var depth = new ExchangeOrderPrice
+				foreach (var array in token[bids])
 				{
-					Price = array[0].ConvertInvariant<decimal>(),
-					Amount = array[1].ConvertInvariant<decimal>()
-				};
-				book.Bids[depth.Price] = depth;
+					var depth = new ExchangeOrderPrice
+					{
+						Price = array[0].ConvertInvariant<decimal>(),
+						Amount = array[1].ConvertInvariant<decimal>()
+					};
+					book.Bids[depth.Price] = depth;
+				}
+			}
+			else
+			{
+				Logger.Error($"No bids in {nameof(ParseOrderBookFromJTokenArrays)}");
 			}
 
 			return book;