From b68e227a8365c3d6132e2908f14ce9c381903341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20=C3=96ner?= Date: Wed, 6 Jan 2021 00:24:54 +0300 Subject: [PATCH 1/6] Update RestClient.cs --- CryptoExchange.Net/RestClient.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CryptoExchange.Net/RestClient.cs b/CryptoExchange.Net/RestClient.cs index d3372fa..bc8bdd5 100644 --- a/CryptoExchange.Net/RestClient.cs +++ b/CryptoExchange.Net/RestClient.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -172,7 +172,7 @@ namespace CryptoExchange.Net /// [return: NotNull] protected virtual async Task> SendRequest(Uri uri, HttpMethod method, CancellationToken cancellationToken, - Dictionary? parameters = null, bool signed = false, bool checkResult = true, PostParameters? postPosition = null, ArrayParametersSerialization? arraySerialization = null) where T : class + Dictionary? parameters = null, bool signed = false, bool checkResult = true, PostParameters? postPosition = null, ArrayParametersSerialization? arraySerialization = null, int credits=1) where T : class { var requestId = NextId(); log.Write(LogVerbosity.Debug, $"[{requestId}] Creating request for " + uri); @@ -185,7 +185,7 @@ namespace CryptoExchange.Net var request = ConstructRequest(uri, method, parameters, signed, postPosition ?? postParametersPosition, arraySerialization ?? this.arraySerialization, requestId); foreach (var limiter in RateLimiters) { - var limitResult = limiter.LimitRequest(this, uri.AbsolutePath, RateLimitBehaviour); + var limitResult = limiter.LimitRequest(this, uri.AbsolutePath, RateLimitBehaviour, credits); if (!limitResult.Success) { log.Write(LogVerbosity.Debug, $"[{requestId}] Request {uri.AbsolutePath} failed because of rate limit"); From 3b6666031a6479ec8925da968cf94c4610114909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20=C3=96ner?= Date: Wed, 6 Jan 2021 00:26:17 +0300 Subject: [PATCH 2/6] Update IRateLimiter.cs --- CryptoExchange.Net/Interfaces/IRateLimiter.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CryptoExchange.Net/Interfaces/IRateLimiter.cs b/CryptoExchange.Net/Interfaces/IRateLimiter.cs index 735f3ec..df6b494 100644 --- a/CryptoExchange.Net/Interfaces/IRateLimiter.cs +++ b/CryptoExchange.Net/Interfaces/IRateLimiter.cs @@ -1,4 +1,4 @@ -using CryptoExchange.Net.Objects; +using CryptoExchange.Net.Objects; namespace CryptoExchange.Net.Interfaces { @@ -13,7 +13,8 @@ namespace CryptoExchange.Net.Interfaces /// /// /// + /// /// - CallResult LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour); + CallResult LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour, int credits=1); } } From d3cb3583289bda7fcdcf7ff0487eed47e8e32cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20=C3=96ner?= Date: Wed, 6 Jan 2021 00:27:31 +0300 Subject: [PATCH 3/6] Update RateLimiterAPIKey.cs --- CryptoExchange.Net/RateLimiter/RateLimiterAPIKey.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptoExchange.Net/RateLimiter/RateLimiterAPIKey.cs b/CryptoExchange.Net/RateLimiter/RateLimiterAPIKey.cs index 0b2dddf..8bfe2f6 100644 --- a/CryptoExchange.Net/RateLimiter/RateLimiterAPIKey.cs +++ b/CryptoExchange.Net/RateLimiter/RateLimiterAPIKey.cs @@ -34,7 +34,7 @@ namespace CryptoExchange.Net.RateLimiter } /// - public CallResult LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour) + public CallResult LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour, int credits = 1) { if(client.authProvider?.Credentials?.Key == null) return new CallResult(0, null); From ad4c6e6a207c43c4dd4ac18a606ba70f82251b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20=C3=96ner?= Date: Wed, 6 Jan 2021 00:27:47 +0300 Subject: [PATCH 4/6] Update RateLimiterPerEndpoint.cs --- CryptoExchange.Net/RateLimiter/RateLimiterPerEndpoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptoExchange.Net/RateLimiter/RateLimiterPerEndpoint.cs b/CryptoExchange.Net/RateLimiter/RateLimiterPerEndpoint.cs index fb62900..f137ae3 100644 --- a/CryptoExchange.Net/RateLimiter/RateLimiterPerEndpoint.cs +++ b/CryptoExchange.Net/RateLimiter/RateLimiterPerEndpoint.cs @@ -30,7 +30,7 @@ namespace CryptoExchange.Net.RateLimiter } /// - public CallResult LimitRequest(RestClient client, string url, RateLimitingBehaviour limitingBehaviour) + public CallResult LimitRequest(RestClient client, string url, RateLimitingBehaviour limitingBehaviour, int credits = 1) { int waitTime; RateLimitObject rlo; From 6d2b75ed4ce1e873f13716846350adfc18ae6d1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20=C3=96ner?= Date: Wed, 6 Jan 2021 00:28:01 +0300 Subject: [PATCH 5/6] Update RateLimiterTotal.cs --- CryptoExchange.Net/RateLimiter/RateLimiterTotal.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptoExchange.Net/RateLimiter/RateLimiterTotal.cs b/CryptoExchange.Net/RateLimiter/RateLimiterTotal.cs index 85919e6..a5d1792 100644 --- a/CryptoExchange.Net/RateLimiter/RateLimiterTotal.cs +++ b/CryptoExchange.Net/RateLimiter/RateLimiterTotal.cs @@ -31,7 +31,7 @@ namespace CryptoExchange.Net.RateLimiter } /// - public CallResult LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour) + public CallResult LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour, int credits = 1) { var sw = Stopwatch.StartNew(); lock (requestLock) From 36a03587b93fe658d4a7a15af1c213171b91d13e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20=C3=96ner?= Date: Wed, 6 Jan 2021 00:29:25 +0300 Subject: [PATCH 6/6] Create RateLimiterCredit.cs --- .../RateLimiter/RateLimiterCredit.cs | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 CryptoExchange.Net/RateLimiter/RateLimiterCredit.cs diff --git a/CryptoExchange.Net/RateLimiter/RateLimiterCredit.cs b/CryptoExchange.Net/RateLimiter/RateLimiterCredit.cs new file mode 100644 index 0000000..ef6cc2b --- /dev/null +++ b/CryptoExchange.Net/RateLimiter/RateLimiterCredit.cs @@ -0,0 +1,65 @@ +using CryptoExchange.Net.Interfaces; +using CryptoExchange.Net.Objects; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; + +namespace CryptoExchange.Net.RateLimiter +{ + /// + /// Limits the amount of requests per time period to a certain limit, counts the total amount of requests. + /// + public class RateLimiterCredit : IRateLimiter + { + internal List history = new List(); + + private readonly int limit; + private readonly TimeSpan perTimePeriod; + private readonly object requestLock = new object(); + + /// + /// Create a new RateLimiterTotal. This rate limiter limits the amount of requests per time period to a certain limit, counts the total amount of requests. + /// + /// The amount to limit to + /// The time period over which the limit counts + public RateLimiterCredit(int limit, TimeSpan perTimePeriod) + { + this.limit = limit; + this.perTimePeriod = perTimePeriod; + } + + /// + public CallResult LimitRequest(RestClient client, string url, RateLimitingBehaviour limitBehaviour, int credits = 1) + { + var sw = Stopwatch.StartNew(); + lock (requestLock) + { + sw.Stop(); + double waitTime = 0; + var checkTime = DateTime.UtcNow; + history.RemoveAll(d => d < checkTime - perTimePeriod); + + if (history.Count >= limit) + { + waitTime = (history.First() - (checkTime - perTimePeriod)).TotalMilliseconds; + if (waitTime > 0) + { + if (limitBehaviour == RateLimitingBehaviour.Fail) + return new CallResult(waitTime, new RateLimitError($"total limit of {limit} reached")); + + Thread.Sleep(Convert.ToInt32(waitTime)); + waitTime += sw.ElapsedMilliseconds; + } + } + + for (int i = 1; i <= credits; i++) + history.Add(DateTime.UtcNow); + + history.Sort(); + return new CallResult(waitTime, null); + } + } + } +}