chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:50 +08:00
commit 0fc60fdcb1
5008 changed files with 910633 additions and 0 deletions
@@ -0,0 +1,36 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Threading;
namespace QuantConnect.Util.RateLimit
{
/// <summary>
/// Provides a CPU intensive means of waiting for more tokens to be available in <see cref="ITokenBucket"/>.
/// This strategy is only viable when the requested number of tokens is expected to become available in an
/// extremely short period of time. This implementation aims to keep the current thread executing to prevent
/// potential content switches arising from a thread yielding or sleeping strategy.
/// </summary>
public class BusyWaitSleepStrategy : ISleepStrategy
{
/// <summary>
/// Provides a CPU intensive sleep by executing <see cref="Thread.SpinWait"/> for a single spin.
/// </summary>
public void Sleep()
{
Thread.SpinWait(1);
}
}
}
@@ -0,0 +1,79 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace QuantConnect.Util.RateLimit
{
/// <summary>
/// Provides a refill strategy that has a constant, quantized refill rate.
/// For example, after 1 minute passes add 5 units. If 59 seconds has passed, it will add zero unit,
/// but if 2 minutes have passed, then 10 units would be added.
/// </summary>
public class FixedIntervalRefillStrategy : IRefillStrategy
{
private readonly object _sync = new object();
private long _nextRefillTimeTicks;
private readonly long _refillAmount;
private readonly long _refillIntervalTicks;
private readonly ITimeProvider _timeProvider;
/// <summary>
/// Initializes a new instance of the <see cref="FixedIntervalRefillStrategy"/> class.
/// </summary>
/// <param name="timeProvider">Provides the current time used for determining how much time has elapsed
/// between invocations of the refill method</param>
/// <param name="refillAmount">Defines the constant number of tokens to be made available for consumption
/// each time the provided <paramref name="refillInterval"/> has passed</param>
/// <param name="refillInterval">The amount of time that must pass before adding the specified <paramref name="refillAmount"/>
/// back to the bucket</param>
public FixedIntervalRefillStrategy(ITimeProvider timeProvider, long refillAmount, TimeSpan refillInterval)
{
_timeProvider = timeProvider;
_refillAmount = refillAmount;
_refillIntervalTicks = refillInterval.Ticks;
_nextRefillTimeTicks = _timeProvider.GetUtcNow().Ticks + _refillIntervalTicks;
}
/// <summary>
/// Computes the number of new tokens made available to the bucket for consumption by determining the
/// number of time intervals that have passed and multiplying by the number of tokens to refill for
/// each time interval.
/// </summary>
public long Refill()
{
lock (_sync)
{
var currentTimeTicks = _timeProvider.GetUtcNow().Ticks;
if (currentTimeTicks < _nextRefillTimeTicks)
{
return 0L;
}
// determine number of time increments that have passed
var deltaTimeTicks = currentTimeTicks - _nextRefillTimeTicks;
var intervalsElapsed = 1 + Math.Max(deltaTimeTicks / _refillIntervalTicks, 0);
// update next refill time as quantized via the number of passed intervals
_nextRefillTimeTicks += _refillIntervalTicks * intervalsElapsed;
// refill by the tokens per interval times the number of intervals elapsed
return _refillAmount * intervalsElapsed;
}
}
}
}
+28
View File
@@ -0,0 +1,28 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Util.RateLimit
{
/// <summary>
/// Provides a strategy for making tokens available for consumption in the <see cref="ITokenBucket"/>
/// </summary>
public interface IRefillStrategy
{
/// <summary>
/// Computes the number of new tokens made available, typically via the passing of time.
/// </summary>
long Refill();
}
}
+30
View File
@@ -0,0 +1,30 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Util.RateLimit
{
/// <summary>
/// Defines a strategy for sleeping the current thread of execution. This is currently used via the
/// <see cref="ITokenBucket.Consume"/> in order to wait for new tokens to become available for consumption.
/// </summary>
public interface ISleepStrategy
{
/// <summary>
/// Sleeps the current thread in an implementation specific way
/// and for an implementation specific amount of time
/// </summary>
void Sleep();
}
}
+59
View File
@@ -0,0 +1,59 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Threading;
namespace QuantConnect.Util.RateLimit
{
/// <summary>
/// Defines a token bucket for rate limiting
/// See: https://en.wikipedia.org/wiki/Token_bucket
/// </summary>
/// <remarks>
/// This code is ported from https://github.com/mxplusb/TokenBucket - since it's a dotnet core
/// project, there were issued importing the nuget package directly. The referenced repository
/// is provided under the Apache V2 license.
/// </remarks>
public interface ITokenBucket
{
/// <summary>
/// Gets the maximum capacity of tokens this bucket can hold.
/// </summary>
long Capacity { get; }
/// <summary>
/// Gets the total number of currently available tokens for consumption
/// </summary>
long AvailableTokens { get; }
/// <summary>
/// Blocks until the specified number of tokens are available for consumption
/// and then consumes that number of tokens.
/// </summary>
/// <param name="tokens">The number of tokens to consume</param>
/// <param name="timeout">The maximum amount of time, in milliseconds, to block. A <see cref="TimeoutException"/>
/// is throw in the event it takes longer than the stated timeout to consume the requested number of tokens.
/// The default timeout is set to infinite, which will block forever.</param>
void Consume(long tokens, long timeout = Timeout.Infinite);
/// <summary>
/// Attempts to consume the specified number of tokens from the bucket. If the
/// requested number of tokens are not immediately available, then this method
/// will return false to indicate that zero tokens have been consumed.
/// </summary>
bool TryConsume(long tokens);
}
}
+172
View File
@@ -0,0 +1,172 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Threading;
namespace QuantConnect.Util.RateLimit
{
/// <summary>
/// Provides an implementation of <see cref="ITokenBucket"/> that implements the leaky bucket algorithm
/// See: https://en.wikipedia.org/wiki/Leaky_bucket
/// </summary>
public class LeakyBucket : ITokenBucket
{
private readonly object _sync = new object();
private long _available;
private readonly ISleepStrategy _sleep;
private readonly IRefillStrategy _refill;
private readonly ITimeProvider _timeProvider;
/// <summary>
/// Gets the maximum capacity of tokens this bucket can hold.
/// </summary>
public long Capacity { get; }
/// <summary>
/// Gets the total number of currently available tokens for consumption
/// </summary>
public long AvailableTokens
{
// synchronized read w/ the modification of available tokens in TryConsume
get { lock (_sync) return _available; }
}
/// <summary>
/// Initializes a new instance of the <see cref="LeakyBucket"/> class.
/// This constructor initializes the bucket using the <see cref="ThreadSleepStrategy.Sleep"/> with a 1 millisecond
/// sleep to prevent being CPU intensive and uses the <see cref="FixedIntervalRefillStrategy"/> to refill bucket
/// tokens according to the <paramref name="refillAmount"/> and <paramref name="refillInterval"/> parameters.
/// </summary>
/// <param name="capacity">The maximum number of tokens this bucket can hold</param>
/// <param name="refillAmount">The number of tokens to add to the bucket each <paramref name="refillInterval"/></param>
/// <param name="refillInterval">The interval which after passing more tokens are added to the bucket</param>
public LeakyBucket(long capacity, long refillAmount, TimeSpan refillInterval)
: this(capacity, ThreadSleepStrategy.Sleeping(1),
new FixedIntervalRefillStrategy(RealTimeProvider.Instance, refillAmount, refillInterval)
)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="LeakyBucket"/> class
/// </summary>
/// <param name="capacity">The maximum number of tokens this bucket can hold</param>
/// <param name="sleep">Defines the <see cref="ISleepStrategy"/> used when <see cref="Consume"/> is invoked
/// but the bucket does not have enough tokens yet</param>
/// <param name="refill">Defines the <see cref="IRefillStrategy"/> that computes how many tokens to add
/// back to the bucket each time consumption is attempted</param>
/// <param name="timeProvider">Defines the <see cref="ITimeProvider"/> used to enforce timeouts when
/// invoking <see cref="Consume"/></param>
public LeakyBucket(long capacity, ISleepStrategy sleep, IRefillStrategy refill, ITimeProvider timeProvider = null)
{
_sleep = sleep;
_refill = refill;
Capacity = capacity;
_available = capacity;
_timeProvider = timeProvider ?? RealTimeProvider.Instance;
}
/// <summary>
/// Blocks until the specified number of tokens are available for consumption
/// and then consumes that number of tokens.
/// </summary>
/// <param name="tokens">The number of tokens to consume</param>
/// <param name="timeout">The maximum amount of time, in milliseconds, to block. An exception is
/// throw in the event it takes longer than the stated timeout to consume the requested number
/// of tokens</param>
public void Consume(long tokens, long timeout = Timeout.Infinite)
{
if (timeout < Timeout.Infinite)
{
throw new ArgumentOutOfRangeException(nameof(timeout),
"Invalid timeout. Use -1 for no timeout, 0 for immediate timeout and a positive number " +
"of milliseconds to indicate a timeout. All other values are out of range."
);
}
var startTime = _timeProvider.GetUtcNow();
while (true)
{
if (TryConsume(tokens))
{
break;
}
if (timeout != Timeout.Infinite)
{
// determine if the requested timeout has elapsed
var currentTime = _timeProvider.GetUtcNow();
var elapsedMilliseconds = (currentTime - startTime).TotalMilliseconds;
if (elapsedMilliseconds > timeout)
{
throw new TimeoutException("The operation timed out while waiting for the rate limit to be lifted.");
}
}
_sleep.Sleep();
}
}
/// <summary>
/// Attempts to consume the specified number of tokens from the bucket. If the
/// requested number of tokens are not immediately available, then this method
/// will return false to indicate that zero tokens have been consumed.
/// </summary>
public bool TryConsume(long tokens)
{
if (tokens <= 0)
{
throw new ArgumentOutOfRangeException(nameof(tokens),
"Number of tokens to consume must be positive"
);
}
if (tokens > Capacity)
{
throw new ArgumentOutOfRangeException(nameof(tokens),
"Number of tokens to consume must be less than or equal to the capacity"
);
}
lock (_sync)
{
// determine how many units have become available since last invocation
var refilled = Math.Max(0, _refill.Refill());
// the number of tokens to add, the max of which is the difference between capacity and currently available
var deltaTokens = Math.Min(Capacity - _available, refilled);
// update the available number of units with the new tokens
_available += deltaTokens;
if (tokens > _available)
{
// we don't have enough tokens yet
Logging.Log.Trace($"LeakyBucket.TryConsume({tokens}): Failed to consumed tokens. Available: {_available}");
return false;
}
// subtract the number of tokens consumed
_available = _available - tokens;
Logging.Log.Trace($"LeakyBucket.TryConsume({tokens}): Successfully consumed tokens. Available: {_available}");
return true;
}
}
}
}
@@ -0,0 +1,60 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Threading;
namespace QuantConnect.Util.RateLimit
{
/// <summary>
/// Provides a CPU non-intensive means of waiting for more tokens to be available in <see cref="ITokenBucket"/>.
/// This strategy should be the most commonly used as it either sleeps or yields the currently executing thread,
/// allowing for other threads to execute while the current thread is blocked and waiting for new tokens to become
/// available in the bucket for consumption.
/// </summary>
public class ThreadSleepStrategy : ISleepStrategy
{
/// <summary>
/// Gets an instance of <see cref="ISleepStrategy"/> that yields the current thread
/// </summary>
public static readonly ISleepStrategy Yielding = new ThreadSleepStrategy(0);
/// <summary>
/// Gets an instance of <see cref="ISleepStrategy"/> that sleeps the current thread for
/// the specified number of milliseconds
/// </summary>
/// <param name="milliseconds">The duration of time to sleep, in milliseconds</param>
public static ISleepStrategy Sleeping(int milliseconds) => new ThreadSleepStrategy(milliseconds);
private readonly int _milliseconds;
/// <summary>
/// Initializes a new instance of the <see cref="ThreadSleepStrategy"/> using the specified
/// number of <paramref name="milliseconds"/> for each <see cref="Sleep"/> invocation.
/// </summary>
/// <param name="milliseconds">The duration of time to sleep, in milliseconds</param>
public ThreadSleepStrategy(int milliseconds)
{
_milliseconds = milliseconds;
}
/// <summary>
/// Sleeps the current thread using the initialized number of milliseconds
/// </summary>
public void Sleep()
{
Thread.Sleep(_milliseconds);
}
}
}
+51
View File
@@ -0,0 +1,51 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Threading;
namespace QuantConnect.Util.RateLimit
{
/// <summary>
/// Provides extension methods for interacting with <see cref="ITokenBucket"/> instances as well
/// as access to the <see cref="NullTokenBucket"/> via <see cref="TokenBucket.Null"/>
/// </summary>
public static class TokenBucket
{
/// <summary>
/// Gets an <see cref="ITokenBucket"/> that always permits consumption
/// </summary>
public static ITokenBucket Null = new NullTokenBucket();
/// <summary>
/// Provides an overload of <see cref="ITokenBucket.Consume"/> that accepts a <see cref="TimeSpan"/> timeout
/// </summary>
public static void Consume(this ITokenBucket bucket, long tokens, TimeSpan timeout)
{
bucket.Consume(tokens, (long) timeout.TotalMilliseconds);
}
/// <summary>
/// Provides an implementation of <see cref="ITokenBucket"/> that does not enforce rate limiting
/// </summary>
private class NullTokenBucket : ITokenBucket
{
public long Capacity => long.MaxValue;
public long AvailableTokens => long.MaxValue;
public bool TryConsume(long tokens) { return true; }
public void Consume(long tokens, long timeout = Timeout.Infinite) { }
}
}
}