diff --git a/CryptoExchange.Net/Clients/RestApiClient.cs b/CryptoExchange.Net/Clients/RestApiClient.cs
index d27ba38..e04748f 100644
--- a/CryptoExchange.Net/Clients/RestApiClient.cs
+++ b/CryptoExchange.Net/Clients/RestApiClient.cs
@@ -223,7 +223,7 @@ namespace CryptoExchange.Net.Clients
if (definition.RateLimitGate == null)
throw new Exception("Ratelimit gate not set when request weight is not 0");
- if (ClientOptions.RatelimiterEnabled)
+ if (ClientOptions.RateLimiterEnabled)
{
var limitResult = await definition.RateLimitGate.ProcessAsync(_logger, requestId, RateLimitItemType.Request, definition, baseAddress, ApiOptions.ApiCredentials?.Key ?? ClientOptions.ApiCredentials?.Key, requestWeight, ClientOptions.RateLimitingBehaviour, cancellationToken).ConfigureAwait(false);
if (!limitResult)
@@ -237,7 +237,7 @@ namespace CryptoExchange.Net.Clients
if (definition.RateLimitGate == null)
throw new Exception("Ratelimit gate not set when endpoint limit is specified");
- if (ClientOptions.RatelimiterEnabled)
+ if (ClientOptions.RateLimiterEnabled)
{
var limitResult = await definition.RateLimitGate.ProcessSingleAsync(_logger, requestId, RateLimitItemType.Request, definition, baseAddress, ApiOptions.ApiCredentials?.Key ?? ClientOptions.ApiCredentials?.Key, requestWeight, ClientOptions.RateLimitingBehaviour, cancellationToken).ConfigureAwait(false);
if (!limitResult)
@@ -515,7 +515,7 @@ namespace CryptoExchange.Net.Clients
if (gate == null)
throw new Exception("Ratelimit gate not set when request weight is not 0");
- if (ClientOptions.RatelimiterEnabled)
+ if (ClientOptions.RateLimiterEnabled)
{
var limitResult = await gate.ProcessAsync(_logger, requestId, RateLimitItemType.Request, new RequestDefinition(uri.AbsolutePath.TrimStart('/'), method) { Authenticated = signed }, uri.Host, ApiOptions.ApiCredentials?.Key ?? ClientOptions.ApiCredentials?.Key, requestWeight, ClientOptions.RateLimitingBehaviour, cancellationToken).ConfigureAwait(false);
if (!limitResult)
@@ -576,7 +576,7 @@ namespace CryptoExchange.Net.Clients
if (response.StatusCode == (HttpStatusCode)418 || response.StatusCode == (HttpStatusCode)429)
{
var rateError = ParseRateLimitResponse((int)response.StatusCode, response.ResponseHeaders, accessor);
- if (rateError.RetryAfter != null && gate != null && ClientOptions.RatelimiterEnabled)
+ if (rateError.RetryAfter != null && gate != null && ClientOptions.RateLimiterEnabled)
{
_logger.RestApiRateLimitPauseUntil(request.RequestId, rateError.RetryAfter.Value);
await gate.SetRetryAfterGuardAsync(rateError.RetryAfter.Value).ConfigureAwait(false);
@@ -666,7 +666,7 @@ namespace CryptoExchange.Net.Clients
return false;
if ((int?)callResult.ResponseStatusCode == 429
- && ClientOptions.RatelimiterEnabled
+ && ClientOptions.RateLimiterEnabled
&& ClientOptions.RateLimitingBehaviour != RateLimitingBehaviour.Fail
&& gate != null)
{
diff --git a/CryptoExchange.Net/Clients/SocketApiClient.cs b/CryptoExchange.Net/Clients/SocketApiClient.cs
index 356aeb8..7f29850 100644
--- a/CryptoExchange.Net/Clients/SocketApiClient.cs
+++ b/CryptoExchange.Net/Clients/SocketApiClient.cs
@@ -519,7 +519,7 @@ namespace CryptoExchange.Net.Clients
{
KeepAliveInterval = KeepAliveInterval,
ReconnectInterval = ClientOptions.ReconnectInterval,
- RateLimiter = ClientOptions.RatelimiterEnabled ? RateLimiter : null,
+ RateLimiter = ClientOptions.RateLimiterEnabled ? RateLimiter : null,
RateLimitingBehaviour = ClientOptions.RateLimitingBehaviour,
Proxy = ClientOptions.Proxy,
Timeout = ApiOptions.SocketNoDataTimeout ?? ClientOptions.SocketNoDataTimeout
diff --git a/CryptoExchange.Net/Objects/Options/ExchangeOptions.cs b/CryptoExchange.Net/Objects/Options/ExchangeOptions.cs
index 332af8c..2d55808 100644
--- a/CryptoExchange.Net/Objects/Options/ExchangeOptions.cs
+++ b/CryptoExchange.Net/Objects/Options/ExchangeOptions.cs
@@ -31,7 +31,7 @@ namespace CryptoExchange.Net.Objects.Options
///
var client = new CoinExRestClient();
-var tickersResult = await client.SpotApi.ExchangeData.GetTickersAsync();
+var tickersResult = await client.SpotApiV2.ExchangeData.GetTickersAsync();
if (!tickersResult.Success)
{
// Handle error, tickersResult.Error contains more information
@@ -1115,12 +1115,12 @@ if (!subscribeResult.Success)
var client = new CoinExSocketClient();
-var subscribeResult = await client.SpotApi.SubscribeToTickerUpdatesAsync("ETHUSDT", update => {
- // Handle the data update, update.Data will contain the actual data
+var subscribeResult = await sclient.SpotApiV2.SubscribeToTickerUpdatesAsync(new[] { "ETHUSDT" }, update => {
+ // Handle the data update, update.Data will contain the actual data
});
if (!subscribeResult.Success)
{
- // Handle error, subscribeResult.Error contains more information on why the subscription failed
+ // Handle error, subscribeResult.Error contains more information on why the subscription failed
}
// Subscribing was successfull, the data will now be streamed into the data handler
WebCallResult.OriginalData
property, for Websocket API client subscriptions the data will be available in the DataEvent.OriginalData
property when receiving an update.false
true
RateLimitingBehaviour.Wait
TimeSpan.FromHours(1)
IRateLimiter
s to useDependent on the library
RateLimitingBehaviour.Wait
null
IRateLimiter
s to useDependent on the library
- The client libraries have build in rate limiting. These rate limits can be configured per client. Some client implementations where the exchange has clear rate limits will also have a default rate limiter already set up.
-Rate limiting is configured in the client options, and can be set on a specific client or for all clients by either providing it in the constructor for a client, or by using the SetDefaultOptions
on a client.
-
-
RateLimitingBehaviour
client options, either Fail
or Wait
.RateLimitingBehaviour
client options, either Fail
for returning an error or Wait
to wait until the request can safely be send.
- Ratelimit configuration
- A rate limiter can be configured in the options like so:
-
new ClientOptions
-{
- RateLimitingBehaviour = RateLimitingBehaviour.Wait,
- RateLimiters = new List
- {
- new RateLimiter()
- .AddTotalRateLimit(50, TimeSpan.FromSeconds(10))
- }
-}
-
-This will add a rate limiter for 50 requests per 10 seconds.
-A rate limiter can have multiple limits:
-new RateLimiter()
- .AddTotalRateLimit(50, TimeSpan.FromSeconds(10))
- .AddEndpointLimit("/api/order", 10, TimeSpan.FromSeconds(2))
-This adds another limit of 10 requests per 2 seconds for the order endpoint in addition to the 50 requests per 10 seconds limit.
+ Client side rate limiting can only correctly work if there is only a single program talking to the exchange. When multiple different application send requests at the same time it's impossible for the client side to keep track of the rate limits. When using multiple concurrent applications it is advised to turn off rate limiting.
+
+ Client side rate limiting is currently implemented for the following libraries:
+services.AddBinance(x =>
+ x.RatelimiterEnabled = true;
+ x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
+}, x =>
+{
+ x.RatelimiterEnabled = true;
+ x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
+});
+ To be notified of when a rate limit is hit the static BinanceExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
BinanceExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
+
+// Output: Limit triggered: RateLimitEvent { ApiLimit = Spot Socket, LimitDescription = Limit of 6000 per 00:01:00, RequestDefinition = GET 1, Host = wss://ws-api.binance.com, Current = 5752, RequestWeight = 250, Limit = 6000, TimePeriod = 00:01:00, DelayTime = 00:00:38.7784145, Behaviour = Wait }
+
+ services.AddKraken(x =>
+ x.RatelimiterEnabled = true;
+ x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
+}, x =>
+{
+ x.RatelimiterEnabled = true;
+ x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
+});
+ To be notified of when a rate limit is hit the static KrakenExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
KrakenExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
+
+// Output: Limit triggered: RateLimitEvent { ApiLimit = Spot Rest, LimitDescription = Limit of 15 with a decay rate of 0,33, RequestDefinition = POST 0/private/TradesHistory authenticated, Host = api.kraken.com, Current = 14, RequestWeight = 2, Limit = 15, TimePeriod = 00:00:01, DelayTime = 00:00:04, Behaviour = Wait }
+
+ Kraken applies different rate limits based on the account verification tier. By default the rate limit is set to the most conservative Starter
tier. To change the rate limit tier call the Configure
method
KrakenExchange.RateLimiter.Configure(Kraken.Net.Enums.RateLimitTier.Pro);
+ services.AddKucoin(x =>
+ x.RatelimiterEnabled = true;
+ x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
+}, x =>
+{
+ x.RatelimiterEnabled = true;
+ x.RateLimitingBehaviour = RateLimitingBehaviour.Wait;
+});
+ To be notified of when a rate limit is hit the static KucoinExchange.RateLimiter
exposes an event which triggers when a rate limit is reached
KucoinExchange.RateLimiter.RateLimitTriggered += (rateLimitEvent) => Console.WriteLine("Limit triggered: " + rateLimitEvent);
+
+// Output: Limit triggered: RateLimitEvent { ApiLimit = Public Rest, LimitDescription = Limit of 2000 per 00:00:30, RequestDefinition = GET api/v1/market/stats, Host = https://api.kucoin.com/, Current = 1995, RequestWeight = 15, Limit = 2000, TimePeriod = 00:00:30, DelayTime = 00:00:19.8111238, Behaviour = Wait }
+
+ Kucoin applies different rate limits based on the account VIP level. By default the rate limit is set to the most conservative VIP0
tier. To change the rate limit tier call the Configure
method
KucoinExchange.RateLimiter.Configure(Kucoin.Net.Enums.VipLevel.Vip5);
+ await bybitClient.V5Api.ExchangeData.GetSpotSymbolsAsync();
await coinExClient.SpotApi.ExchangeData.GetSymbolsAsync();
+ await coinExClient.SpotApiV2.ExchangeData.GetSymbolsAsync();
await huobiClient.SpotApi.ExchangeData.GetSymbolsAsync();
@@ -2476,7 +2520,7 @@ await spotClient.GetTickerAsync(spotClient.GetSymbolName("BTC", "USDT"));
await bybitClient.V5Api.ExchangeData.GetSpotTickersAsync("BTCUSDT");
await coinExClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
+ await coinExClient.SpotApiV2.ExchangeData.GetTickersAsync(new[] { "BTCUSDT" });
await huobiClient.SpotApi.ExchangeData.GetTickerAsync("BTCUSDT");
@@ -2570,7 +2614,7 @@ await spotClient.GetBalancesAsync();
await bybitClient.V5Api.Account.GetBalancesAsync(AccountType.Spot);
await coinExClient.SpotApi.Account.GetBalancesAsync();
+ await coinExClient.SpotApiV2.Account.GetBalancesAsync();
// Need an account id, you probably want to already have done this before placing the order
@@ -2668,7 +2712,7 @@ await spotClient.PlaceOrderAsync(spotClient.GetSymbolName("BTC", "USDT"), Common
await bybitClient.V5Api.Trading.PlaceOrderAsync(Category.Spot, "BTCUSDT", OrderSide.Buy, NewOrderType.Limit, 0.1m, price: 50000);
await coinExClient.SpotApi.Trading.PlaceOrderAsync("BTCUSDT", OrderSide.Buy, OrderType.Limit, 0.1m, 50000);
+ await coinExClient.SpotApiV2.Trading.PlaceOrderAsync("BTCUSDT", AccountType.Spot, OrderSide.Buy, OrderTypeV2.Limit, 0.1m, 50000);
// Need an account id, you probably want to already have done this before placing the order
@@ -2768,7 +2812,7 @@ var result = await huobiClient.SpotApi.Trading.PlaceOrderAsync(account.Id, "BTCU
});
await coinExSocketClient.SpotApi.SubscribeToTickerUpdatesAsync("ETHUSDT", data => {
+ await coinExSocketClient.SpotApiV2.SubscribeToTickerUpdatesAsync(new[] { "ETHUSDT" }, data => {
// Handle update
});
await coinExSocketClient.SpotApi.SubscribeToOrderUpdatesAsync(data => {
+ await coinExSocketClient.SpotApiV2.SubscribeToOrderUpdatesAsync(data => {
// Handle update
});