Files
nt8-sdk/docs/architecture/order_rate_limiting_implementation.md
Billy Valentine 92f3732b3d
Some checks failed
Build and Test / build (push) Has been cancelled
Phase 0 completion: NT8 SDK core framework with risk management and position sizing
2025-09-09 17:06:37 -04:00

1442 lines
50 KiB
Markdown

# Order Rate Limiting Implementation Design
## Overview
This document details the implementation of order rate limiting functionality in the Order Management System (OMS), which prevents excessive order submission rates that could overwhelm execution venues, violate exchange rules, or trigger risk management alerts.
## Rate Limiting Architecture
### Core Components
1. **Rate Limiter**: Core rate limiting logic
2. **Rate Limit Configuration**: Configuration for different rate limits
3. **Rate Limit Tracker**: Tracks order submission rates
4. **Rate Limit Enforcer**: Enforces rate limits on order submissions
5. **Rate Limit Monitor**: Monitors rate limit violations and generates alerts
## Rate Limiting Models
### Rate Limit Configuration
```csharp
/// <summary>
/// Configuration for rate limiting
/// </summary>
public record RateLimitConfig : IConfiguration
{
public string Id { get; set; } = "rate-limit-config";
public string Name { get; set; } = "Rate Limit Configuration";
public string Description { get; set; } = "Configuration for order rate limiting";
public bool IsActive { get; set; } = true;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public int Version { get; set; } = 1;
/// <summary>
/// Global maximum orders per second
/// </summary>
public int MaxOrdersPerSecond { get; set; } = 100;
/// <summary>
/// Global maximum orders per minute
/// </summary>
public int MaxOrdersPerMinute { get; set; } = 5000;
/// <summary>
/// Global maximum orders per hour
/// </summary>
public int MaxOrdersPerHour { get; set; } = 200000;
/// <summary>
/// Per-user maximum orders per second
/// </summary>
public int MaxOrdersPerSecondPerUser { get; set; } = 10;
/// <summary>
/// Per-user maximum orders per minute
/// </summary>
public int MaxOrdersPerMinutePerUser { get; set; } = 500;
/// <summary>
/// Per-user maximum orders per hour
/// </summary>
public int MaxOrdersPerHourPerUser { get; set; } = 20000;
/// <summary>
/// Per-symbol maximum orders per second
/// </summary>
public int MaxOrdersPerSecondPerSymbol { get; set; } = 20;
/// <summary>
/// Per-symbol maximum orders per minute
/// </summary>
public int MaxOrdersPerMinutePerSymbol { get; set; } = 1000;
/// <summary>
/// Per-symbol maximum orders per hour
/// </summary>
public int MaxOrdersPerHourPerSymbol { get; set; } = 50000;
/// <summary>
/// Per-venue maximum orders per second
/// </summary>
public int MaxOrdersPerSecondPerVenue { get; set; } = 50;
/// <summary>
/// Per-venue maximum orders per minute
/// </summary>
public int MaxOrdersPerMinutePerVenue { get; set; } = 2500;
/// <summary>
/// Per-venue maximum orders per hour
/// </summary>
public int MaxOrdersPerHourPerVenue { get; set; } = 100000;
/// <summary>
/// Burst allowance (additional orders allowed in short bursts)
/// </summary>
public int BurstAllowance { get; set; } = 10;
/// <summary>
/// Burst window (in seconds)
/// </summary>
public int BurstWindowSeconds { get; set; } = 10;
/// <summary>
/// Whether to enable adaptive rate limiting based on system load
/// </summary>
public bool EnableAdaptiveRateLimiting { get; set; } = false;
/// <summary>
/// Threshold for system load to trigger adaptive rate limiting (0.0 to 1.0)
/// </summary>
public double AdaptiveRateLimitingThreshold { get; set; } = 0.8;
/// <summary>
/// Percentage reduction in rate limits when adaptive limiting is triggered
/// </summary>
public double AdaptiveRateLimitingReduction { get; set; } = 0.5; // 50% reduction
/// <summary>
/// Whether to log rate limit violations
/// </summary>
public bool LogViolations { get; set; } = true;
/// <summary>
/// Whether to generate alerts for rate limit violations
/// </summary>
public bool GenerateViolationAlerts { get; set; } = true;
/// <summary>
/// Violation alert threshold (number of violations before alerting)
/// </summary>
public int ViolationAlertThreshold { get; set; } = 5;
/// <summary>
/// Violation window (in minutes)
/// </summary>
public int ViolationWindowMinutes { get; set; } = 60;
/// <summary>
/// Whether to temporarily ban users after repeated violations
/// </summary>
public bool EnableTemporaryBans { get; set; } = true;
/// <summary>
/// Ban duration (in minutes) after violation threshold is exceeded
/// </summary>
public int BanDurationMinutes { get; set; } = 30;
/// <summary>
/// Ban violation threshold
/// </summary>
public int BanViolationThreshold { get; set; } = 20;
public static RateLimitConfig Default => new RateLimitConfig();
}
```
### Rate Limit State
```csharp
/// <summary>
/// Tracks rate limit state for different dimensions
/// </summary>
public class RateLimitState
{
/// <summary>
/// Order timestamps for sliding window calculations
/// </summary>
private readonly Queue<DateTime> _orderTimestamps;
private readonly object _lock = new object();
public RateLimitState()
{
_orderTimestamps = new Queue<DateTime>();
}
/// <summary>
/// Record an order submission
/// </summary>
public void RecordOrder(DateTime timestamp)
{
lock (_lock)
{
_orderTimestamps.Enqueue(timestamp);
}
}
/// <summary>
/// Get order count in the specified time window
/// </summary>
public int GetOrderCount(TimeSpan timeWindow)
{
lock (_lock)
{
var cutoffTime = DateTime.UtcNow.Subtract(timeWindow);
return _orderTimestamps.Count(t => t >= cutoffTime);
}
}
/// <summary>
/// Get order rate per second in the specified time window
/// </summary>
public double GetOrderRate(TimeSpan timeWindow)
{
lock (_lock)
{
var cutoffTime = DateTime.UtcNow.Subtract(timeWindow);
var count = _orderTimestamps.Count(t => t >= cutoffTime);
return count / timeWindow.TotalSeconds;
}
}
/// <summary>
/// Prune old timestamps to prevent memory leaks
/// </summary>
public void PruneOldTimestamps(TimeSpan maxAge)
{
lock (_lock)
{
var cutoffTime = DateTime.UtcNow.Subtract(maxAge);
while (_orderTimestamps.Count > 0 && _orderTimestamps.Peek() < cutoffTime)
{
_orderTimestamps.Dequeue();
}
}
}
/// <summary>
/// Clear all timestamps
/// </summary>
public void Clear()
{
lock (_lock)
{
_orderTimestamps.Clear();
}
}
}
```
### Rate Limit Violation
```csharp
/// <summary>
/// Represents a rate limit violation
/// </summary>
public record RateLimitViolation
{
/// <summary>
/// Unique identifier for this violation
/// </summary>
public string Id { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// Timestamp of violation
/// </summary>
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
/// <summary>
/// User that triggered the violation (if applicable)
/// </summary>
public string UserId { get; set; }
/// <summary>
/// Symbol involved in the violation (if applicable)
/// </summary>
public string Symbol { get; set; }
/// <summary>
/// Venue involved in the violation (if applicable)
/// </summary>
public string VenueId { get; set; }
/// <summary>
/// Type of rate limit violated
/// </summary>
public RateLimitType LimitType { get; set; }
/// <summary>
/// Current rate
/// </summary>
public double CurrentRate { get; set; }
/// <summary>
/// Maximum allowed rate
/// </summary>
public double MaxRate { get; set; }
/// <summary>
/// Order that triggered the violation
/// </summary>
public OrderRequest Order { get; set; }
/// <summary>
/// Error message
/// </summary>
public string ErrorMessage { get; set; }
/// <summary>
/// Whether this violation resulted in a temporary ban
/// </summary>
public bool ResultedInBan { get; set; }
/// <summary>
/// Ban expiration time (if applicable)
/// </summary>
public DateTime? BanExpiration { get; set; }
}
```
### Rate Limit Enums
```csharp
/// <summary>
/// Types of rate limits
/// </summary>
public enum RateLimitType
{
GlobalPerSecond,
GlobalPerMinute,
GlobalPerHour,
UserPerSecond,
UserPerMinute,
UserPerHour,
SymbolPerSecond,
SymbolPerMinute,
SymbolPerHour,
VenuePerSecond,
VenuePerMinute,
VenuePerHour
}
/// <summary>
/// Rate limit enforcement action
/// </summary>
public enum RateLimitAction
{
Allow,
Delay,
Reject,
Ban
}
```
## Rate Limiting Implementation
### Rate Limiter
```csharp
/// <summary>
/// Implements rate limiting for order submissions
/// </summary>
public class RateLimiter
{
private readonly ILogger<RateLimiter> _logger;
private readonly RateLimitConfig _config;
private readonly Dictionary<string, RateLimitState> _userStates;
private readonly Dictionary<string, RateLimitState> _symbolStates;
private readonly Dictionary<string, RateLimitState> _venueStates;
private readonly RateLimitState _globalState;
private readonly Dictionary<string, List<DateTime>> _violations;
private readonly Dictionary<string, DateTime> _bans;
private readonly object _lock = new object();
private readonly Timer _cleanupTimer;
public RateLimiter(ILogger<RateLimiter> logger, RateLimitConfig config = null)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_config = config ?? RateLimitConfig.Default;
_userStates = new Dictionary<string, RateLimitState>();
_symbolStates = new Dictionary<string, RateLimitState>();
_venueStates = new Dictionary<string, RateLimitState>();
_globalState = new RateLimitState();
_violations = new Dictionary<string, List<DateTime>>();
_bans = new Dictionary<string, DateTime>();
// Set up cleanup timer to prune old data
_cleanupTimer = new Timer(CleanupOldData, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
}
/// <summary>
/// Check if an order submission is allowed based on rate limits
/// </summary>
public RateLimitResult CheckRateLimit(OrderRequest order, StrategyContext context)
{
if (order == null) throw new ArgumentNullException(nameof(order));
if (context == null) throw new ArgumentNullException(nameof(context));
var now = DateTime.UtcNow;
var violations = new List<RateLimitViolation>();
// Check if user is banned
if (_config.EnableTemporaryBans && IsUserBanned(context.UserId))
{
return new RateLimitResult(RateLimitAction.Ban,
new List<RateLimitViolation>
{
new RateLimitViolation
{
UserId = context.UserId,
ErrorMessage = "User is temporarily banned due to rate limit violations",
BanExpiration = _bans[context.UserId]
}
});
}
// Check global limits
CheckGlobalLimits(now, order, violations);
// Check user limits
CheckUserLimits(now, order, context.UserId, violations);
// Check symbol limits
CheckSymbolLimits(now, order, violations);
// Check venue limits
CheckVenueLimits(now, order, violations);
// If there are violations, handle them
if (violations.Any())
{
HandleViolations(violations, context.UserId);
// Return appropriate action based on violation severity
var action = DetermineAction(violations);
return new RateLimitResult(action, violations);
}
// Record the order if allowed
RecordOrder(now, order, context.UserId);
return new RateLimitResult(RateLimitAction.Allow, new List<RateLimitViolation>());
}
private void CheckGlobalLimits(DateTime now, OrderRequest order, List<RateLimitViolation> violations)
{
// Check per-second limit
var perSecondRate = _globalState.GetOrderRate(TimeSpan.FromSeconds(1));
if (perSecondRate >= _config.MaxOrdersPerSecond)
{
violations.Add(new RateLimitViolation
{
Timestamp = now,
LimitType = RateLimitType.GlobalPerSecond,
CurrentRate = perSecondRate,
MaxRate = _config.MaxOrdersPerSecond,
Order = order,
ErrorMessage = $"Global rate limit exceeded: {perSecondRate:F2} orders/sec > {_config.MaxOrdersPerSecond}"
});
}
// Check per-minute limit
var perMinuteRate = _globalState.GetOrderRate(TimeSpan.FromMinutes(1));
if (perMinuteRate >= _config.MaxOrdersPerMinute / 60.0)
{
violations.Add(new RateLimitViolation
{
Timestamp = now,
LimitType = RateLimitType.GlobalPerMinute,
CurrentRate = perMinuteRate * 60,
MaxRate = _config.MaxOrdersPerMinute,
Order = order,
ErrorMessage = $"Global rate limit exceeded: {perMinuteRate * 60:F0} orders/min > {_config.MaxOrdersPerMinute}"
});
}
// Check per-hour limit
var perHourRate = _globalState.GetOrderRate(TimeSpan.FromHours(1));
if (perHourRate >= _config.MaxOrdersPerHour / 3600.0)
{
violations.Add(new RateLimitViolation
{
Timestamp = now,
LimitType = RateLimitType.GlobalPerHour,
CurrentRate = perHourRate * 3600,
MaxRate = _config.MaxOrdersPerHour,
Order = order,
ErrorMessage = $"Global rate limit exceeded: {perHourRate * 3600:F0} orders/hr > {_config.MaxOrdersPerHour}"
});
}
}
private void CheckUserLimits(DateTime now, OrderRequest order, string userId, List<RateLimitViolation> violations)
{
if (string.IsNullOrEmpty(userId)) return;
var userState = GetUserState(userId);
// Check per-second limit
var perSecondRate = userState.GetOrderRate(TimeSpan.FromSeconds(1));
if (perSecondRate >= _config.MaxOrdersPerSecondPerUser)
{
violations.Add(new RateLimitViolation
{
Timestamp = now,
UserId = userId,
LimitType = RateLimitType.UserPerSecond,
CurrentRate = perSecondRate,
MaxRate = _config.MaxOrdersPerSecondPerUser,
Order = order,
ErrorMessage = $"User rate limit exceeded: {perSecondRate:F2} orders/sec > {_config.MaxOrdersPerSecondPerUser}"
});
}
// Check per-minute limit
var perMinuteRate = userState.GetOrderRate(TimeSpan.FromMinutes(1));
if (perMinuteRate >= _config.MaxOrdersPerMinutePerUser / 60.0)
{
violations.Add(new RateLimitViolation
{
Timestamp = now,
UserId = userId,
LimitType = RateLimitType.UserPerMinute,
CurrentRate = perMinuteRate * 60,
MaxRate = _config.MaxOrdersPerMinutePerUser,
Order = order,
ErrorMessage = $"User rate limit exceeded: {perMinuteRate * 60:F0} orders/min > {_config.MaxOrdersPerMinutePerUser}"
});
}
// Check per-hour limit
var perHourRate = userState.GetOrderRate(TimeSpan.FromHours(1));
if (perHourRate >= _config.MaxOrdersPerHourPerUser / 3600.0)
{
violations.Add(new RateLimitViolation
{
Timestamp = now,
UserId = userId,
LimitType = RateLimitType.UserPerHour,
CurrentRate = perHourRate * 3600,
MaxRate = _config.MaxOrdersPerHourPerUser,
Order = order,
ErrorMessage = $"User rate limit exceeded: {perHourRate * 3600:F0} orders/hr > {_config.MaxOrdersPerHourPerUser}"
});
}
}
private void CheckSymbolLimits(DateTime now, OrderRequest order, List<RateLimitViolation> violations)
{
if (string.IsNullOrEmpty(order.Symbol)) return;
var symbolState = GetSymbolState(order.Symbol);
// Check per-second limit
var perSecondRate = symbolState.GetOrderRate(TimeSpan.FromSeconds(1));
if (perSecondRate >= _config.MaxOrdersPerSecondPerSymbol)
{
violations.Add(new RateLimitViolation
{
Timestamp = now,
Symbol = order.Symbol,
LimitType = RateLimitType.SymbolPerSecond,
CurrentRate = perSecondRate,
MaxRate = _config.MaxOrdersPerSecondPerSymbol,
Order = order,
ErrorMessage = $"Symbol rate limit exceeded: {perSecondRate:F2} orders/sec > {_config.MaxOrdersPerSecondPerSymbol}"
});
}
// Check per-minute limit
var perMinuteRate = symbolState.GetOrderRate(TimeSpan.FromMinutes(1));
if (perMinuteRate >= _config.MaxOrdersPerMinutePerSymbol / 60.0)
{
violations.Add(new RateLimitViolation
{
Timestamp = now,
Symbol = order.Symbol,
LimitType = RateLimitType.SymbolPerMinute,
CurrentRate = perMinuteRate * 60,
MaxRate = _config.MaxOrdersPerMinutePerSymbol,
Order = order,
ErrorMessage = $"Symbol rate limit exceeded: {perMinuteRate * 60:F0} orders/min > {_config.MaxOrdersPerMinutePerSymbol}"
});
}
// Check per-hour limit
var perHourRate = symbolState.GetOrderRate(TimeSpan.FromHours(1));
if (perHourRate >= _config.MaxOrdersPerHourPerSymbol / 3600.0)
{
violations.Add(new RateLimitViolation
{
Timestamp = now,
Symbol = order.Symbol,
LimitType = RateLimitType.SymbolPerHour,
CurrentRate = perHourRate * 3600,
MaxRate = _config.MaxOrdersPerHourPerSymbol,
Order = order,
ErrorMessage = $"Symbol rate limit exceeded: {perHourRate * 3600:F0} orders/hr > {_config.MaxOrdersPerHourPerSymbol}"
});
}
}
private void CheckVenueLimits(DateTime now, OrderRequest order, List<RateLimitViolation> violations)
{
// In a real implementation, we would determine the venue for the order
// For now, we'll use a placeholder
var venueId = "primary-venue"; // Placeholder
var venueState = GetVenueState(venueId);
// Check per-second limit
var perSecondRate = venueState.GetOrderRate(TimeSpan.FromSeconds(1));
if (perSecondRate >= _config.MaxOrdersPerSecondPerVenue)
{
violations.Add(new RateLimitViolation
{
Timestamp = now,
VenueId = venueId,
LimitType = RateLimitType.VenuePerSecond,
CurrentRate = perSecondRate,
MaxRate = _config.MaxOrdersPerSecondPerVenue,
Order = order,
ErrorMessage = $"Venue rate limit exceeded: {perSecondRate:F2} orders/sec > {_config.MaxOrdersPerSecondPerVenue}"
});
}
// Check per-minute limit
var perMinuteRate = venueState.GetOrderRate(TimeSpan.FromMinutes(1));
if (perMinuteRate >= _config.MaxOrdersPerMinutePerVenue / 60.0)
{
violations.Add(new RateLimitViolation
{
Timestamp = now,
VenueId = venueId,
LimitType = RateLimitType.VenuePerMinute,
CurrentRate = perMinuteRate * 60,
MaxRate = _config.MaxOrdersPerMinutePerVenue,
Order = order,
ErrorMessage = $"Venue rate limit exceeded: {perMinuteRate * 60:F0} orders/min > {_config.MaxOrdersPerMinutePerVenue}"
});
}
// Check per-hour limit
var perHourRate = venueState.GetOrderRate(TimeSpan.FromHours(1));
if (perHourRate >= _config.MaxOrdersPerHourPerVenue / 3600.0)
{
violations.Add(new RateLimitViolation
{
Timestamp = now,
VenueId = venueId,
LimitType = RateLimitType.VenuePerHour,
CurrentRate = perHourRate * 3600,
MaxRate = _config.MaxOrdersPerHourPerVenue,
Order = order,
ErrorMessage = $"Venue rate limit exceeded: {perHourRate * 3600:F0} orders/hr > {_config.MaxOrdersPerHourPerVenue}"
});
}
}
private void RecordOrder(DateTime timestamp, OrderRequest order, string userId)
{
// Record in global state
_globalState.RecordOrder(timestamp);
// Record in user state
if (!string.IsNullOrEmpty(userId))
{
var userState = GetUserState(userId);
userState.RecordOrder(timestamp);
}
// Record in symbol state
if (!string.IsNullOrEmpty(order.Symbol))
{
var symbolState = GetSymbolState(order.Symbol);
symbolState.RecordOrder(timestamp);
}
// Record in venue state
// In a real implementation, we would determine the venue for the order
var venueId = "primary-venue"; // Placeholder
var venueState = GetVenueState(venueId);
venueState.RecordOrder(timestamp);
}
private RateLimitState GetUserState(string userId)
{
lock (_lock)
{
if (!_userStates.ContainsKey(userId))
{
_userStates[userId] = new RateLimitState();
}
return _userStates[userId];
}
}
private RateLimitState GetSymbolState(string symbol)
{
lock (_lock)
{
if (!_symbolStates.ContainsKey(symbol))
{
_symbolStates[symbol] = new RateLimitState();
}
return _symbolStates[symbol];
}
}
private RateLimitState GetVenueState(string venueId)
{
lock (_lock)
{
if (!_venueStates.ContainsKey(venueId))
{
_venueStates[venueId] = new RateLimitState();
}
return _venueStates[venueId];
}
}
private void HandleViolations(List<RateLimitViolation> violations, string userId)
{
if (_config.LogViolations)
{
foreach (var violation in violations)
{
_logger.LogWarning("Rate limit violation: {ErrorMessage}", violation.ErrorMessage);
}
}
if (_config.GenerateViolationAlerts)
{
// Track violations for alerting
lock (_lock)
{
if (!_violations.ContainsKey(userId))
{
_violations[userId] = new List<DateTime>();
}
_violations[userId].AddRange(violations.Select(v => v.Timestamp));
// Prune old violations
var cutoffTime = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(_config.ViolationWindowMinutes));
_violations[userId] = _violations[userId].Where(t => t >= cutoffTime).ToList();
// Check if ban threshold is exceeded
if (_config.EnableTemporaryBans &&
_violations[userId].Count >= _config.BanViolationThreshold)
{
_bans[userId] = DateTime.UtcNow.Add(TimeSpan.FromMinutes(_config.BanDurationMinutes));
// Update violations to mark ban
foreach (var violation in violations)
{
violation.ResultedInBan = true;
violation.BanExpiration = _bans[userId];
}
_logger.LogWarning("User {UserId} temporarily banned due to {ViolationCount} violations in {WindowMinutes} minutes",
userId, _violations[userId].Count, _config.ViolationWindowMinutes);
}
}
}
}
private RateLimitAction DetermineAction(List<RateLimitViolation> violations)
{
// If any violation resulted in a ban, return ban action
if (violations.Any(v => v.ResultedInBan))
{
return RateLimitAction.Ban;
}
// If we have burst allowance and violations are within burst window, allow with delay
if (_config.BurstAllowance > 0)
{
var burstWindow = TimeSpan.FromSeconds(_config.BurstWindowSeconds);
var recentViolations = violations.Count(v =>
DateTime.UtcNow.Subtract(v.Timestamp) <= burstWindow);
if (recentViolations <= _config.BurstAllowance)
{
return RateLimitAction.Delay;
}
}
// Otherwise, reject the order
return RateLimitAction.Reject;
}
private bool IsUserBanned(string userId)
{
if (string.IsNullOrEmpty(userId)) return false;
lock (_lock)
{
if (_bans.ContainsKey(userId))
{
// Check if ban has expired
if (_bans[userId] <= DateTime.UtcNow)
{
_bans.Remove(userId);
return false;
}
return true;
}
}
return false;
}
private void CleanupOldData(object state)
{
try
{
var maxAge = TimeSpan.FromHours(1);
// Prune global state
_globalState.PruneOldTimestamps(maxAge);
// Prune user states
lock (_lock)
{
var usersToRemove = new List<string>();
foreach (var kvp in _userStates)
{
kvp.Value.PruneOldTimestamps(maxAge);
// Remove empty states to prevent memory leaks
if (kvp.Value.GetOrderCount(TimeSpan.FromHours(24)) == 0)
{
usersToRemove.Add(kvp.Key);
}
}
foreach (var user in usersToRemove)
{
_userStates.Remove(user);
}
// Prune symbol states
var symbolsToRemove = new List<string>();
foreach (var kvp in _symbolStates)
{
kvp.Value.PruneOldTimestamps(maxAge);
// Remove empty states to prevent memory leaks
if (kvp.Value.GetOrderCount(TimeSpan.FromHours(24)) == 0)
{
symbolsToRemove.Add(kvp.Key);
}
}
foreach (var symbol in symbolsToRemove)
{
_symbolStates.Remove(symbol);
}
// Prune venue states
var venuesToRemove = new List<string>();
foreach (var kvp in _venueStates)
{
kvp.Value.PruneOldTimestamps(maxAge);
// Remove empty states to prevent memory leaks
if (kvp.Value.GetOrderCount(TimeSpan.FromHours(24)) == 0)
{
venuesToRemove.Add(kvp.Key);
}
}
foreach (var venue in venuesToRemove)
{
_venueStates.Remove(venue);
}
// Prune violations
var cutoffTime = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(_config.ViolationWindowMinutes * 2));
var usersToRemoveFromViolations = new List<string>();
foreach (var kvp in _violations)
{
kvp.Value.RemoveAll(t => t < cutoffTime);
if (kvp.Value.Count == 0)
{
usersToRemoveFromViolations.Add(kvp.Key);
}
}
foreach (var user in usersToRemoveFromViolations)
{
_violations.Remove(user);
}
// Prune expired bans
var usersToRemoveFromBans = new List<string>();
foreach (var kvp in _bans)
{
if (kvp.Value <= DateTime.UtcNow)
{
usersToRemoveFromBans.Add(kvp.Key);
}
}
foreach (var user in usersToRemoveFromBans)
{
_bans.Remove(user);
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error cleaning up rate limit data");
}
}
/// <summary>
/// Get current rate limit metrics
/// </summary>
public RateLimitMetrics GetMetrics()
{
lock (_lock)
{
return new RateLimitMetrics
{
GlobalPerSecondRate = _globalState.GetOrderRate(TimeSpan.FromSeconds(1)),
GlobalPerMinuteRate = _globalState.GetOrderRate(TimeSpan.FromMinutes(1)) * 60,
GlobalPerHourRate = _globalState.GetOrderRate(TimeSpan.FromHours(1)) * 3600,
UserRates = _userStates.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.GetOrderRate(TimeSpan.FromMinutes(1)) * 60),
SymbolRates = _symbolStates.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.GetOrderRate(TimeSpan.FromMinutes(1)) * 60),
VenueRates = _venueStates.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.GetOrderRate(TimeSpan.FromMinutes(1)) * 60),
ViolationCount = _violations.Sum(kvp => kvp.Value.Count),
BanCount = _bans.Count
};
}
}
/// <summary>
/// Reset rate limit state for a user
/// </summary>
public void ResetUserState(string userId)
{
if (string.IsNullOrEmpty(userId)) return;
lock (_lock)
{
if (_userStates.ContainsKey(userId))
{
_userStates[userId].Clear();
}
if (_violations.ContainsKey(userId))
{
_violations[userId].Clear();
}
if (_bans.ContainsKey(userId))
{
_bans.Remove(userId);
}
}
_logger.LogInformation("Rate limit state reset for user {UserId}", userId);
}
/// <summary>
/// Reset rate limit state for a symbol
/// </summary>
public void ResetSymbolState(string symbol)
{
if (string.IsNullOrEmpty(symbol)) return;
lock (_lock)
{
if (_symbolStates.ContainsKey(symbol))
{
_symbolStates[symbol].Clear();
}
}
_logger.LogInformation("Rate limit state reset for symbol {Symbol}", symbol);
}
/// <summary>
/// Reset all rate limit state
/// </summary>
public void ResetAllState()
{
lock (_lock)
{
_globalState.Clear();
_userStates.Clear();
_symbolStates.Clear();
_venueStates.Clear();
_violations.Clear();
_bans.Clear();
}
_logger.LogInformation("All rate limit state reset");
}
public void Dispose()
{
_cleanupTimer?.Dispose();
}
}
```
### Rate Limit Results
```csharp
/// <summary>
/// Result of rate limit check
/// </summary>
public record RateLimitResult
{
/// <summary>
/// Action to take for the order
/// </summary>
public RateLimitAction Action { get; set; }
/// <summary>
/// Violations that occurred (if any)
/// </summary>
public List<RateLimitViolation> Violations { get; set; } = new List<RateLimitViolation>();
/// <summary>
/// Delay required (if action is Delay)
/// </summary>
public TimeSpan Delay { get; set; } = TimeSpan.Zero;
public RateLimitResult(RateLimitAction action, List<RateLimitViolation> violations = null)
{
Action = action;
Violations = violations ?? new List<RateLimitViolation>();
// Calculate delay if needed
if (action == RateLimitAction.Delay)
{
// Simple delay calculation - in a real implementation, this would be more sophisticated
Delay = TimeSpan.FromMilliseconds(100 * Violations.Count);
}
}
}
/// <summary>
/// Rate limit metrics
/// </summary>
public record RateLimitMetrics
{
public double GlobalPerSecondRate { get; set; }
public double GlobalPerMinuteRate { get; set; }
public double GlobalPerHourRate { get; set; }
public Dictionary<string, double> UserRates { get; set; } = new Dictionary<string, double>();
public Dictionary<string, double> SymbolRates { get; set; } = new Dictionary<string, double>();
public Dictionary<string, double> VenueRates { get; set; } = new Dictionary<string, double>();
public int ViolationCount { get; set; }
public int BanCount { get; set; }
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
}
```
## Integration with OrderManager
### Rate Limiting Integration
```csharp
public partial class OrderManager : IOrderManager
{
private readonly RateLimiter _rateLimiter;
// Enhanced constructor with rate limiter
public OrderManager(
IRiskManager riskManager,
IPositionSizer positionSizer,
ILogger<OrderManager> logger,
RoutingConfigurationManager configManager,
RoutingMetricsCollector metricsCollector,
TwapExecutor twapExecutor,
VwapExecutor vwapExecutor,
IcebergExecutor icebergExecutor,
AlgorithmParameterProvider parameterProvider,
RateLimiter rateLimiter) : base(riskManager, positionSizer, logger, configManager, metricsCollector, twapExecutor, vwapExecutor, icebergExecutor, parameterProvider)
{
_rateLimiter = rateLimiter ?? throw new ArgumentNullException(nameof(rateLimiter));
_venueManager = new VenueManager(logger);
_omsToVenueOrderIdMap = new Dictionary<string, string>();
_venueToOmsOrderIdMap = new Dictionary<string, string>();
// Initialize with configurations
InitializeWithConfigurationsAsync().Wait();
}
/// <summary>
/// Submit an order with rate limiting
/// </summary>
public async Task<OrderResult> SubmitOrderAsync(OrderRequest request, StrategyContext context)
{
if (request == null) throw new ArgumentNullException(nameof(request));
if (context == null) throw new ArgumentNullException(nameof(context));
try
{
// Check rate limits
var rateLimitResult = _rateLimiter.CheckRateLimit(request, context);
switch (rateLimitResult.Action)
{
case RateLimitAction.Ban:
_logger.LogWarning("Order submission rejected due to temporary ban for user {UserId}", context.UserId);
return new OrderResult(false, null, "Order submission rejected due to temporary ban", null);
case RateLimitAction.Reject:
_logger.LogWarning("Order submission rejected due to rate limit violation");
return new OrderResult(false, null, "Order submission rejected due to rate limit violation", null);
case RateLimitAction.Delay:
_logger.LogInformation("Delaying order submission due to rate limit");
await Task.Delay(rateLimitResult.Delay);
break;
case RateLimitAction.Allow:
// Proceed with normal order submission
break;
}
// Continue with normal order submission process
return await base.SubmitOrderAsync(request, context);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error checking rate limits for order submission");
// In case of rate limiter error, we'll proceed with the order but log the error
return await base.SubmitOrderAsync(request, context);
}
}
/// <summary>
/// Get rate limit metrics
/// </summary>
public RateLimitMetrics GetRateLimitMetrics()
{
return _rateLimiter.GetMetrics();
}
/// <summary>
/// Reset rate limit state for a user
/// </summary>
public void ResetUserRateLimitState(string userId)
{
_rateLimiter.ResetUserState(userId);
}
/// <summary>
/// Reset rate limit state for a symbol
/// </summary>
public void ResetSymbolRateLimitState(string symbol)
{
_rateLimiter.ResetSymbolState(symbol);
}
/// <summary>
/// Reset all rate limit state
/// </summary>
public void ResetAllRateLimitState()
{
_rateLimiter.ResetAllState();
}
}
```
## Rate Limit Configuration Management
### Rate Limit Configuration Integration
```csharp
public partial class RoutingConfigurationManager
{
/// <summary>
/// Get rate limit configuration
/// </summary>
public async Task<RateLimitConfig> GetRateLimitConfigAsync()
{
var config = await GetConfigurationAsync<RateLimitConfig>("rate-limit-config");
return config ?? RateLimitConfig.Default;
}
/// <summary>
/// Update rate limit configuration
/// </summary>
public async Task UpdateRateLimitConfigAsync(RateLimitConfig config)
{
if (config == null) throw new ArgumentNullException(nameof(config));
config.Id = "rate-limit-config";
config.Name = "Rate Limit Configuration";
config.Description = "Configuration for order rate limiting";
await UpdateConfigurationAsync(config);
_logger.LogInformation("Rate limit configuration updated");
}
}
```
## Testing Considerations
### Unit Tests for Rate Limiting
1. **Rate Limit Checking**: Test rate limit checks for different dimensions
2. **Rate Limit Violations**: Test handling of rate limit violations
3. **Temporary Bans**: Test temporary ban functionality
4. **Burst Allowance**: Test burst allowance behavior
5. **State Management**: Test rate limit state tracking and pruning
6. **Configuration Validation**: Test validation of rate limit configurations
7. **Metrics Collection**: Test collection of rate limit metrics
8. **Reset Functionality**: Test resetting of rate limit state
### Integration Tests
1. **End-to-End Rate Limiting**: Test complete rate limiting flow
2. **Order Manager Integration**: Test integration with OrderManager
3. **Performance Testing**: Test performance with high order volumes
4. **Concurrent Access**: Test concurrent access to rate limiter
5. **Error Handling**: Test error handling in rate limiting
6. **Configuration Updates**: Test dynamic configuration updates
## Performance Considerations
### Memory Management
```csharp
/// <summary>
/// Manages memory usage for rate limiting
/// </summary>
public class RateLimitMemoryManager
{
private readonly int _maxUsers;
private readonly int _maxSymbols;
private readonly int _maxVenues;
private readonly TimeSpan _stateRetentionTime;
private readonly object _lock = new object();
public RateLimitMemoryManager(
int maxUsers = 10000,
int maxSymbols = 1000,
int maxVenues = 100,
TimeSpan retentionTime = default)
{
_maxUsers = maxUsers;
_maxSymbols = maxSymbols;
_maxVenues = maxVenues;
_stateRetentionTime = retentionTime == default ?
TimeSpan.FromHours(24) : retentionTime;
}
public bool IsMemoryPressureHigh(
int currentUserCount,
int currentSymbolCount,
int currentVenueCount)
{
return currentUserCount > (_maxUsers * 0.8) || // 80% threshold
currentSymbolCount > (_maxSymbols * 0.8) ||
currentVenueCount > (_maxVenues * 0.8);
}
public TimeSpan GetStateRetentionTime()
{
return _stateRetentionTime;
}
}
```
### Adaptive Rate Limiting
```csharp
/// <summary>
/// Adaptive rate limiting that adjusts based on system conditions
/// </summary>
public class AdaptiveRateLimiter : RateLimiter
{
private readonly ISystemMonitor _systemMonitor;
private readonly RateLimitConfig _baseConfig;
public AdaptiveRateLimiter(
ILogger<RateLimiter> logger,
ISystemMonitor systemMonitor,
RateLimitConfig config = null) : base(logger, config)
{
_systemMonitor = systemMonitor ?? throw new ArgumentNullException(nameof(systemMonitor));
_baseConfig = config ?? RateLimitConfig.Default;
}
protected override RateLimitConfig GetEffectiveConfig()
{
if (!_baseConfig.EnableAdaptiveRateLimiting)
return _baseConfig;
var systemLoad = _systemMonitor.GetCurrentSystemLoad();
if (systemLoad.LoadAverage > _baseConfig.AdaptiveRateLimitingThreshold)
{
// Reduce rate limits based on system load
var reductionFactor = 1.0 - (_baseConfig.AdaptiveRateLimitingReduction *
(systemLoad.LoadAverage - _baseConfig.AdaptiveRateLimitingThreshold) /
(1.0 - _baseConfig.AdaptiveRateLimitingThreshold));
return new RateLimitConfig
{
Id = _baseConfig.Id,
Name = _baseConfig.Name,
Description = _baseConfig.Description,
IsActive = _baseConfig.IsActive,
CreatedAt = _baseConfig.CreatedAt,
UpdatedAt = _baseConfig.UpdatedAt,
Version = _baseConfig.Version,
MaxOrdersPerSecond = (int)(_baseConfig.MaxOrdersPerSecond * reductionFactor),
MaxOrdersPerMinute = (int)(_baseConfig.MaxOrdersPerMinute * reductionFactor),
MaxOrdersPerHour = (int)(_baseConfig.MaxOrdersPerHour * reductionFactor),
MaxOrdersPerSecondPerUser = (int)(_baseConfig.MaxOrdersPerSecondPerUser * reductionFactor),
MaxOrdersPerMinutePerUser = (int)(_baseConfig.MaxOrdersPerMinutePerUser * reductionFactor),
MaxOrdersPerHourPerUser = (int)(_baseConfig.MaxOrdersPerHourPerUser * reductionFactor),
MaxOrdersPerSecondPerSymbol = (int)(_baseConfig.MaxOrdersPerSecondPerSymbol * reductionFactor),
MaxOrdersPerMinutePerSymbol = (int)(_baseConfig.MaxOrdersPerMinutePerSymbol * reductionFactor),
MaxOrdersPerHourPerSymbol = (int)(_baseConfig.MaxOrdersPerHourPerSymbol * reductionFactor),
MaxOrdersPerSecondPerVenue = (int)(_baseConfig.MaxOrdersPerSecondPerVenue * reductionFactor),
MaxOrdersPerMinutePerVenue = (int)(_baseConfig.MaxOrdersPerMinutePerVenue * reductionFactor),
MaxOrdersPerHourPerVenue = (int)(_baseConfig.MaxOrdersPerHourPerVenue * reductionFactor),
BurstAllowance = _baseConfig.BurstAllowance,
BurstWindowSeconds = _baseConfig.BurstWindowSeconds,
EnableAdaptiveRateLimiting = _baseConfig.EnableAdaptiveRateLimiting,
AdaptiveRateLimitingThreshold = _baseConfig.AdaptiveRateLimitingThreshold,
AdaptiveRateLimitingReduction = _baseConfig.AdaptiveRateLimitingReduction,
LogViolations = _baseConfig.LogViolations,
GenerateViolationAlerts = _baseConfig.GenerateViolationAlerts,
ViolationAlertThreshold = _baseConfig.ViolationAlertThreshold,
ViolationWindowMinutes = _baseConfig.ViolationWindowMinutes,
EnableTemporaryBans = _baseConfig.EnableTemporaryBans,
BanDurationMinutes = _baseConfig.BanDurationMinutes,
BanViolationThreshold = _baseConfig.BanViolationThreshold
};
}
return _baseConfig;
}
}
/// <summary>
/// System monitor for adaptive rate limiting
/// </summary>
public interface ISystemMonitor
{
/// <summary>
/// Get current system load metrics
/// </summary>
SystemLoadMetrics GetCurrentSystemLoad();
}
/// <summary>
/// System load metrics
/// </summary>
public record SystemLoadMetrics
{
public double CpuUsage { get; set; } // 0.0 to 1.0
public double MemoryUsage { get; set; } // 0.0 to 1.0
public double DiskIo { get; set; } // 0.0 to 1.0
public double NetworkIo { get; set; } // 0.0 to 1.0
public double LoadAverage { get; set; } // 0.0 to 1.0 (normalized)
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
}
```
## Monitoring and Alerting
### Rate Limit Metrics Export
```csharp
/// <summary>
/// Exports rate limit metrics for monitoring
/// </summary>
public class RateLimitMetricsExporter
{
private readonly RateLimiter _rateLimiter;
public RateLimitMetricsExporter(RateLimiter rateLimiter)
{
_rateLimiter = rateLimiter ?? throw new ArgumentNullException(nameof(rateLimiter));
}
public string ExportToPrometheus()
{
var metrics = _rateLimiter.GetMetrics();
var sb = new StringBuilder();
// Global rate metrics
sb.AppendLine($"# HELP rate_limit_global_per_second Current global orders per second");
sb.AppendLine($"# TYPE rate_limit_global_per_second gauge");
sb.AppendLine($"rate_limit_global_per_second {metrics.GlobalPerSecondRate:F2}");
sb.AppendLine($"# HELP rate_limit_global_per_minute Current global orders per minute");
sb.AppendLine($"# TYPE rate_limit_global_per_minute gauge");
sb.AppendLine($"rate_limit_global_per_minute {metrics.GlobalPerMinuteRate:F0}");
sb.AppendLine($"# HELP rate_limit_global_per_hour Current global orders per hour");
sb.AppendLine($"# TYPE rate_limit_global_per_hour gauge");
sb.AppendLine($"rate_limit_global_per_hour {metrics.GlobalPerHourRate:F0}");
// User rate metrics (top 10 users)
var topUsers = metrics.UserRates
.OrderByDescending(kvp => kvp.Value)
.Take(10);
foreach (var kvp in topUsers)
{
sb.AppendLine($"# HELP rate_limit_user_per_minute Current orders per minute for user {kvp.Key}");
sb.AppendLine($"# TYPE rate_limit_user_per_minute gauge");
sb.AppendLine($"rate_limit_user_per_minute{{user=\"{kvp.Key}\"}} {kvp.Value:F0}");
}
// Symbol rate metrics (top 10 symbols)
var topSymbols = metrics.SymbolRates
.OrderByDescending(kvp => kvp.Value)
.Take(10);
foreach (var kvp in topSymbols)
{
sb.AppendLine($"# HELP rate_limit_symbol_per_minute Current orders per minute for symbol {kvp.Key}");
sb.AppendLine($"# TYPE rate_limit_symbol_per_minute gauge");
sb.AppendLine($"rate_limit_symbol_per_minute{{symbol=\"{kvp.Key}\"}} {kvp.Value:F0}");
}
// Violation metrics
sb.AppendLine($"# HELP rate_limit_violations_total Total rate limit violations");
sb.AppendLine($"# TYPE rate_limit_violations_total counter");
sb.AppendLine($"rate_limit_violations_total {metrics.ViolationCount}");
sb.AppendLine($"# HELP rate_limit_bans_total Total temporary bans");
sb.AppendLine($"# TYPE rate_limit_bans_total counter");
sb.AppendLine($"rate_limit_bans_total {metrics.BanCount}");
return sb.ToString();
}
}
```
## Future Enhancements
1. **Machine Learning Rate Limiting**: Use ML to predict optimal rate limits based on historical data
2. **Real-time Adaptive Rate Limiting**: Adjust rate limits in real-time based on market conditions
3. **Cross-Venue Rate Limiting**: Coordinate rate limits across multiple execution venues
4. **Rate Limiting Analytics**: Advanced analytics and reporting on rate limiting performance
5. **Rate Limiting Strategy Builder**: Visual tools for building and testing rate limiting strategies
6. **Rate Limiting Benchmarking**: Compare rate limiting performance against industry standards
7. **Rate Limiting Compliance**: Ensure rate limiting complies with exchange and regulatory requirements
8. **Distributed Rate Limiting**: Implement rate limiting across distributed systems
9. **Hierarchical Rate Limiting**: Implement hierarchical rate limits for organizations and teams
10. **Rate Limiting with Cost Awareness**: Adjust rate limits based on order costs and values