# Order Value Limits Implementation Design ## Overview This document details the implementation of order value limits functionality in the Order Management System (OMS), which prevents orders that exceed specified monetary values to mitigate financial risk and ensure compliance with trading policies. ## Value Limits Architecture ### Core Components 1. **Value Limiter**: Core value limiting logic 2. **Value Limit Configuration**: Configuration for different value limits 3. **Value Limit Tracker**: Tracks order values and cumulative exposures 4. **Value Limit Enforcer**: Enforces value limits on order submissions 5. **Value Limit Monitor**: Monitors value limit violations and generates alerts ## Value Limiting Models ### Value Limit Configuration ```csharp /// /// Configuration for value limiting /// public record ValueLimitConfig : IConfiguration { public string Id { get; set; } = "value-limit-config"; public string Name { get; set; } = "Value Limit Configuration"; public string Description { get; set; } = "Configuration for order value 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; /// /// Global maximum order value (in base currency) /// public decimal MaxOrderValue { get; set; } = 1000000m; // $1,000 /// /// Global maximum daily order value (in base currency) /// public decimal MaxDailyOrderValue { get; set; } = 10000000m; // $10,000 /// /// Per-user maximum order value (in base currency) /// public decimal MaxOrderValuePerUser { get; set; } = 100000m; // $100,000 /// /// Per-user maximum daily order value (in base currency) /// public decimal MaxDailyOrderValuePerUser { get; set; } = 1000000m; // $1,000,000 /// /// Per-symbol maximum order value (in base currency) /// public decimal MaxOrderValuePerSymbol { get; set; } = 500000m; // $500,000 /// /// Per-symbol maximum daily order value (in base currency) /// public decimal MaxDailyOrderValuePerSymbol { get; set; } = 5000000m; // $5,000,000 /// /// Per-venue maximum order value (in base currency) /// public decimal MaxOrderValuePerVenue { get; set; } = 750000m; // $750,000 /// /// Per-venue maximum daily order value (in base currency) /// public decimal MaxDailyOrderValuePerVenue { get; set; } = 7500000m; // $7,500,000 /// /// Per-sector maximum order value (in base currency) /// public Dictionary MaxOrderValuePerSector { get; set; } = new Dictionary { ["Equities"] = 500000m, ["Futures"] = 750000m, ["Forex"] = 250000m, ["Options"] = 100000m, ["Cryptocurrency"] = 50000m }; /// /// Per-sector maximum daily order value (in base currency) /// public Dictionary MaxDailyOrderValuePerSector { get; set; } = new Dictionary { ["Equities"] = 5000000m, ["Futures"] = 7500000m, ["Forex"] = 2500000m, ["Options"] = 1000000m, ["Cryptocurrency"] = 500000m }; /// /// Currency conversion settings /// public CurrencyConversionSettings CurrencyConversion { get; set; } = new CurrencyConversionSettings(); /// /// Whether to enable adaptive value limiting based on market conditions /// public bool EnableAdaptiveValueLimiting { get; set; } = false; /// /// Threshold for market volatility to trigger adaptive value limiting (0.0 to 1.0) /// public double AdaptiveValueLimitingThreshold { get; set; } = 0.02; // 2% volatility /// /// Percentage reduction in value limits when adaptive limiting is triggered /// public double AdaptiveValueLimitingReduction { get; set; } = 0.5; // 50% reduction /// /// Whether to log value limit violations /// public bool LogViolations { get; set; } = true; /// /// Whether to generate alerts for value limit violations /// public bool GenerateViolationAlerts { get; set; } = true; /// /// Violation alert threshold (number of violations before alerting) /// public int ViolationAlertThreshold { get; set; } = 3; /// /// Violation window (in minutes) /// public int ViolationWindowMinutes { get; set; } = 60; /// /// Whether to temporarily ban users after repeated violations /// public bool EnableTemporaryBans { get; set; } = true; /// /// Ban duration (in minutes) after violation threshold is exceeded /// public int BanDurationMinutes { get; set; } = 60; /// /// Ban violation threshold /// public int BanViolationThreshold { get; set; } = 10; /// /// Whether to enable soft limits (warn but allow) vs hard limits (reject) /// public bool EnableSoftLimits { get; set; } = false; /// /// Soft limit warning threshold (percentage of hard limit) /// public double SoftLimitWarningThreshold { get; set; } = 0.8; // 80% of hard limit public static ValueLimitConfig Default => new ValueLimitConfig(); } ``` ### Currency Conversion Settings ```csharp /// /// Settings for currency conversion /// public record CurrencyConversionSettings { /// /// Base currency for all value calculations /// public string BaseCurrency { get; set; } = "USD"; /// /// Whether to enable real-time currency conversion /// public bool EnableRealTimeConversion { get; set; } = true; /// /// Currency conversion provider /// public string ConversionProvider { get; set; } = "ECB"; // European Central Bank /// /// Cache expiration for currency rates (in minutes) /// public int CacheExpirationMinutes { get; set; } = 60; /// /// Fallback exchange rates (currency to base currency) /// public Dictionary FallbackRates { get; set; } = new Dictionary { ["EUR"] = 1.18m, ["GBP"] = 1.38m, ["JPY"] = 0.0091m, ["CAD"] = 0.79m, ["AUD"] = 0.73m, ["CHF"] = 1.09m }; /// /// Whether to use historical rates for past-dated orders /// public bool EnableHistoricalRates { get; set; } = true; } ``` ### Value Limit State ```csharp /// /// Tracks value limit state for different dimensions /// public class ValueLimitState { /// /// Order values for sliding window calculations /// private readonly Queue _orderValues; private readonly object _lock = new object(); public ValueLimitState() { _orderValues = new Queue(); } /// /// Record an order value /// public void RecordOrderValue(decimal value, DateTime timestamp, string orderId) { lock (_lock) { _orderValues.Enqueue(new OrderValueEntry { Value = value, Timestamp = timestamp, OrderId = orderId }); } } /// /// Get total value in the specified time window /// public decimal GetTotalValue(TimeSpan timeWindow) { lock (_lock) { var cutoffTime = DateTime.UtcNow.Subtract(timeWindow); return _orderValues .Where(entry => entry.Timestamp >= cutoffTime) .Sum(entry => entry.Value); } } /// /// Get average order value in the specified time window /// public decimal GetAverageValue(TimeSpan timeWindow) { lock (_lock) { var cutoffTime = DateTime.UtcNow.Subtract(timeWindow); var entries = _orderValues.Where(entry => entry.Timestamp >= cutoffTime).ToList(); return entries.Any() ? entries.Average(entry => entry.Value) : 0; } } /// /// Get largest order value in the specified time window /// public decimal GetMaxValue(TimeSpan timeWindow) { lock (_lock) { var cutoffTime = DateTime.UtcNow.Subtract(timeWindow); var entries = _orderValues.Where(entry => entry.Timestamp >= cutoffTime).ToList(); return entries.Any() ? entries.Max(entry => entry.Value) : 0; } } /// /// Get order count in the specified time window /// public int GetOrderCount(TimeSpan timeWindow) { lock (_lock) { var cutoffTime = DateTime.UtcNow.Subtract(timeWindow); return _orderValues.Count(entry => entry.Timestamp >= cutoffTime); } } /// /// Prune old values to prevent memory leaks /// public void PruneOldValues(TimeSpan maxAge) { lock (_lock) { var cutoffTime = DateTime.UtcNow.Subtract(maxAge); while (_orderValues.Count > 0 && _orderValues.Peek().Timestamp < cutoffTime) { _orderValues.Dequeue(); } } } /// /// Clear all values /// public void Clear() { lock (_lock) { _orderValues.Clear(); } } } /// /// Order value entry for tracking /// public record OrderValueEntry { public decimal Value { get; set; } public DateTime Timestamp { get; set; } public string OrderId { get; set; } } ``` ### Value Limit Violation ```csharp /// /// Represents a value limit violation /// public record ValueLimitViolation { /// /// Unique identifier for this violation /// public string Id { get; set; } = Guid.NewGuid().ToString(); /// /// Timestamp of violation /// public DateTime Timestamp { get; set; } = DateTime.UtcNow; /// /// User that triggered the violation (if applicable) /// public string UserId { get; set; } /// /// Symbol involved in the violation (if applicable) /// public string Symbol { get; set; } /// /// Venue involved in the violation (if applicable) /// public string VenueId { get; set; } /// /// Sector involved in the violation (if applicable) /// public string Sector { get; set; } /// /// Type of value limit violated /// public ValueLimitType LimitType { get; set; } /// /// Current value /// public decimal CurrentValue { get; set; } /// /// Maximum allowed value /// public decimal MaxValue { get; set; } /// /// Order that triggered the violation /// public OrderRequest Order { get; set; } /// /// Error message /// public string ErrorMessage { get; set; } /// /// Whether this violation resulted in a temporary ban /// public bool ResultedInBan { get; set; } /// /// Ban expiration time (if applicable) /// public DateTime? BanExpiration { get; set; } /// /// Whether this was a soft limit warning /// public bool IsSoftLimitWarning { get; set; } } ``` ### Value Limit Enums ```csharp /// /// Types of value limits /// public enum ValueLimitType { GlobalOrder, GlobalDaily, UserOrder, UserDaily, SymbolOrder, SymbolDaily, VenueOrder, VenueDaily, SectorOrder, SectorDaily } /// /// Value limit enforcement action /// public enum ValueLimitAction { Allow, Warn, Reject, Ban } ``` ## Value Limiting Implementation ### Value Limiter ```csharp /// /// Implements value limiting for order submissions /// public class ValueLimiter { private readonly ILogger _logger; private readonly ValueLimitConfig _config; private readonly ICurrencyConverter _currencyConverter; private readonly Dictionary _userStates; private readonly Dictionary _symbolStates; private readonly Dictionary _venueStates; private readonly Dictionary _sectorStates; private readonly ValueLimitState _globalState; private readonly Dictionary> _violations; private readonly Dictionary _bans; private readonly object _lock = new object(); private readonly Timer _cleanupTimer; public ValueLimiter( ILogger logger, ICurrencyConverter currencyConverter, ValueLimitConfig config = null) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _currencyConverter = currencyConverter ?? throw new ArgumentNullException(nameof(currencyConverter)); _config = config ?? ValueLimitConfig.Default; _userStates = new Dictionary(); _symbolStates = new Dictionary(); _venueStates = new Dictionary(); _sectorStates = new Dictionary(); _globalState = new ValueLimitState(); _violations = new Dictionary>(); _bans = new Dictionary(); // Set up cleanup timer to prune old data _cleanupTimer = new Timer(CleanupOldData, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5)); } /// /// Check if an order submission is allowed based on value limits /// public async Task CheckValueLimitAsync(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(); // Check if user is banned if (_config.EnableTemporaryBans && IsUserBanned(context.UserId)) { return new ValueLimitResult(ValueLimitAction.Ban, new List { new ValueLimitViolation { UserId = context.UserId, ErrorMessage = "User is temporarily banned due to value limit violations", BanExpiration = _bans[context.UserId] } }); } // Calculate order value var orderValue = await CalculateOrderValueAsync(order, context); // Check global limits CheckGlobalLimits(now, order, orderValue, violations); // Check user limits CheckUserLimits(now, order, context.UserId, orderValue, violations); // Check symbol limits CheckSymbolLimits(now, order, orderValue, violations); // Check venue limits CheckVenueLimits(now, order, orderValue, violations); // Check sector limits CheckSectorLimits(now, order, orderValue, 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 ValueLimitResult(action, violations); } // Record the order if allowed RecordOrderValue(now, order, orderValue, context.UserId); return new ValueLimitResult(ValueLimitAction.Allow, new List()); } private async Task CalculateOrderValueAsync(OrderRequest order, StrategyContext context) { // Get current market price for the symbol var marketPrice = await GetCurrentMarketPriceAsync(order.Symbol); if (!marketPrice.HasValue) { // Fallback to limit price or a default value marketPrice = order.LimitPrice ?? 100m; // Default fallback } // Calculate order value var orderValue = order.Quantity * marketPrice.Value; // Convert to base currency if needed if (context.AccountCurrency != _config.CurrencyConversion.BaseCurrency) { orderValue = await _currencyConverter.ConvertAsync( orderValue, context.AccountCurrency, _config.CurrencyConversion.BaseCurrency); } return orderValue; } private async Task GetCurrentMarketPriceAsync(string symbol) { // In a real implementation, this would get real-time market data // For now, we'll simulate market prices var random = new Random(); return symbol switch { "ES" => 4200m + (decimal)(random.NextDouble() * 100 - 50), // S&P 500 E-mini "NQ" => 15000m + (decimal)(random.NextDouble() * 200 - 100), // NASDAQ-100 E-mini "CL" => 75m + (decimal)(random.NextDouble() * 10 - 5), // Crude Oil _ => 100m + (decimal)(random.NextDouble() * 50 - 25) // Default }; } private void CheckGlobalLimits(DateTime now, OrderRequest order, decimal orderValue, List violations) { // Check per-order limit if (orderValue > _config.MaxOrderValue) { violations.Add(new ValueLimitViolation { Timestamp = now, LimitType = ValueLimitType.GlobalOrder, CurrentValue = orderValue, MaxValue = _config.MaxOrderValue, Order = order, ErrorMessage = $"Global order value limit exceeded: {orderValue:C} > {_config.MaxOrderValue:C}" }); } // Check daily limit var dailyValue = _globalState.GetTotalValue(TimeSpan.FromDays(1)) + orderValue; if (dailyValue > _config.MaxDailyOrderValue) { violations.Add(new ValueLimitViolation { Timestamp = now, LimitType = ValueLimitType.GlobalDaily, CurrentValue = dailyValue, MaxValue = _config.MaxDailyOrderValue, Order = order, ErrorMessage = $"Global daily order value limit exceeded: {dailyValue:C} > {_config.MaxDailyOrderValue:C}" }); } // Check soft limit warning if (_config.EnableSoftLimits && orderValue > _config.MaxOrderValue * (decimal)_config.SoftLimitWarningThreshold) { violations.Add(new ValueLimitViolation { Timestamp = now, LimitType = ValueLimitType.GlobalOrder, CurrentValue = orderValue, MaxValue = _config.MaxOrderValue, Order = order, ErrorMessage = $"Global order value approaching limit: {orderValue:C} > {_config.MaxOrderValue * (decimal)_config.SoftLimitWarningThreshold:C}", IsSoftLimitWarning = true }); } } private void CheckUserLimits(DateTime now, OrderRequest order, string userId, decimal orderValue, List violations) { if (string.IsNullOrEmpty(userId)) return; var userState = GetUserState(userId); // Check per-order limit if (orderValue > _config.MaxOrderValuePerUser) { violations.Add(new ValueLimitViolation { Timestamp = now, UserId = userId, LimitType = ValueLimitType.UserOrder, CurrentValue = orderValue, MaxValue = _config.MaxOrderValuePerUser, Order = order, ErrorMessage = $"User order value limit exceeded: {orderValue:C} > {_config.MaxOrderValuePerUser:C}" }); } // Check daily limit var dailyValue = userState.GetTotalValue(TimeSpan.FromDays(1)) + orderValue; if (dailyValue > _config.MaxDailyOrderValuePerUser) { violations.Add(new ValueLimitViolation { Timestamp = now, UserId = userId, LimitType = ValueLimitType.UserDaily, CurrentValue = dailyValue, MaxValue = _config.MaxDailyOrderValuePerUser, Order = order, ErrorMessage = $"User daily order value limit exceeded: {dailyValue:C} > {_config.MaxDailyOrderValuePerUser:C}" }); } // Check soft limit warning if (_config.EnableSoftLimits && orderValue > _config.MaxOrderValuePerUser * (decimal)_config.SoftLimitWarningThreshold) { violations.Add(new ValueLimitViolation { Timestamp = now, UserId = userId, LimitType = ValueLimitType.UserOrder, CurrentValue = orderValue, MaxValue = _config.MaxOrderValuePerUser, Order = order, ErrorMessage = $"User order value approaching limit: {orderValue:C} > {_config.MaxOrderValuePerUser * (decimal)_config.SoftLimitWarningThreshold:C}", IsSoftLimitWarning = true }); } } private void CheckSymbolLimits(DateTime now, OrderRequest order, decimal orderValue, List violations) { if (string.IsNullOrEmpty(order.Symbol)) return; var symbolState = GetSymbolState(order.Symbol); // Check per-order limit if (orderValue > _config.MaxOrderValuePerSymbol) { violations.Add(new ValueLimitViolation { Timestamp = now, Symbol = order.Symbol, LimitType = ValueLimitType.SymbolOrder, CurrentValue = orderValue, MaxValue = _config.MaxOrderValuePerSymbol, Order = order, ErrorMessage = $"Symbol order value limit exceeded: {orderValue:C} > {_config.MaxOrderValuePerSymbol:C}" }); } // Check daily limit var dailyValue = symbolState.GetTotalValue(TimeSpan.FromDays(1)) + orderValue; if (dailyValue > _config.MaxDailyOrderValuePerSymbol) { violations.Add(new ValueLimitViolation { Timestamp = now, Symbol = order.Symbol, LimitType = ValueLimitType.SymbolDaily, CurrentValue = dailyValue, MaxValue = _config.MaxDailyOrderValuePerSymbol, Order = order, ErrorMessage = $"Symbol daily order value limit exceeded: {dailyValue:C} > {_config.MaxDailyOrderValuePerSymbol:C}" }); } // Check soft limit warning if (_config.EnableSoftLimits && orderValue > _config.MaxOrderValuePerSymbol * (decimal)_config.SoftLimitWarningThreshold) { violations.Add(new ValueLimitViolation { Timestamp = now, Symbol = order.Symbol, LimitType = ValueLimitType.SymbolOrder, CurrentValue = orderValue, MaxValue = _config.MaxOrderValuePerSymbol, Order = order, ErrorMessage = $"Symbol order value approaching limit: {orderValue:C} > {_config.MaxOrderValuePerSymbol * (decimal)_config.SoftLimitWarningThreshold:C}", IsSoftLimitWarning = true }); } } private void CheckVenueLimits(DateTime now, OrderRequest order, decimal orderValue, List 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-order limit if (orderValue > _config.MaxOrderValuePerVenue) { violations.Add(new ValueLimitViolation { Timestamp = now, VenueId = venueId, LimitType = ValueLimitType.VenueOrder, CurrentValue = orderValue, MaxValue = _config.MaxOrderValuePerVenue, Order = order, ErrorMessage = $"Venue order value limit exceeded: {orderValue:C} > {_config.MaxOrderValuePerVenue:C}" }); } // Check daily limit var dailyValue = venueState.GetTotalValue(TimeSpan.FromDays(1)) + orderValue; if (dailyValue > _config.MaxDailyOrderValuePerVenue) { violations.Add(new ValueLimitViolation { Timestamp = now, VenueId = venueId, LimitType = ValueLimitType.VenueDaily, CurrentValue = dailyValue, MaxValue = _config.MaxDailyOrderValuePerVenue, Order = order, ErrorMessage = $"Venue daily order value limit exceeded: {dailyValue:C} > {_config.MaxDailyOrderValuePerVenue:C}" }); } // Check soft limit warning if (_config.EnableSoftLimits && orderValue > _config.MaxOrderValuePerVenue * (decimal)_config.SoftLimitWarningThreshold) { violations.Add(new ValueLimitViolation { Timestamp = now, VenueId = venueId, LimitType = ValueLimitType.VenueOrder, CurrentValue = orderValue, MaxValue = _config.MaxOrderValuePerVenue, Order = order, ErrorMessage = $"Venue order value approaching limit: {orderValue:C} > {_config.MaxOrderValuePerVenue * (decimal)_config.SoftLimitWarningThreshold:C}", IsSoftLimitWarning = true }); } } private void CheckSectorLimits(DateTime now, OrderRequest order, decimal orderValue, List violations) { // Determine sector for the symbol var sector = DetermineSector(order.Symbol); if (string.IsNullOrEmpty(sector)) return; var sectorState = GetSectorState(sector); // Check if sector has specific limits if (_config.MaxOrderValuePerSector.ContainsKey(sector)) { var maxOrderValue = _config.MaxOrderValuePerSector[sector]; // Check per-order limit if (orderValue > maxOrderValue) { violations.Add(new ValueLimitViolation { Timestamp = now, Sector = sector, LimitType = ValueLimitType.SectorOrder, CurrentValue = orderValue, MaxValue = maxOrderValue, Order = order, ErrorMessage = $"Sector order value limit exceeded: {orderValue:C} > {maxOrderValue:C}" }); } } if (_config.MaxDailyOrderValuePerSector.ContainsKey(sector)) { var maxDailyValue = _config.MaxDailyOrderValuePerSector[sector]; // Check daily limit var dailyValue = sectorState.GetTotalValue(TimeSpan.FromDays(1)) + orderValue; if (dailyValue > maxDailyValue) { violations.Add(new ValueLimitViolation { Timestamp = now, Sector = sector, LimitType = ValueLimitType.SectorDaily, CurrentValue = dailyValue, MaxValue = maxDailyValue, Order = order, ErrorMessage = $"Sector daily order value limit exceeded: {dailyValue:C} > {maxDailyValue:C}" }); } } // Check soft limit warning if (_config.EnableSoftLimits && _config.MaxOrderValuePerSector.ContainsKey(sector) && orderValue > _config.MaxOrderValuePerSector[sector] * (decimal)_config.SoftLimitWarningThreshold) { violations.Add(new ValueLimitViolation { Timestamp = now, Sector = sector, LimitType = ValueLimitType.SectorOrder, CurrentValue = orderValue, MaxValue = _config.MaxOrderValuePerSector[sector], Order = order, ErrorMessage = $"Sector order value approaching limit: {orderValue:C} > {_config.MaxOrderValuePerSector[sector] * (decimal)_config.SoftLimitWarningThreshold:C}", IsSoftLimitWarning = true }); } } private string DetermineSector(string symbol) { // Simple sector determination based on symbol return symbol switch { string s when s.StartsWith("ES") || s.StartsWith("NQ") || s.StartsWith("YM") => "Equities", string s when s.StartsWith("CL") || s.StartsWith("NG") || s.StartsWith("RB") => "Futures", string s when s.StartsWith("EUR") || s.StartsWith("GBP") || s.StartsWith("JPY") => "Forex", string s when s.StartsWith("SPY") || s.StartsWith("QQQ") || s.StartsWith("IWM") => "Options", string s when s.StartsWith("BTC") || s.StartsWith("ETH") => "Cryptocurrency", _ => "Other" }; } private void RecordOrderValue(DateTime timestamp, OrderRequest order, decimal orderValue, string userId) { // Record in global state _globalState.RecordOrderValue(orderValue, timestamp, order.OrderId); // Record in user state if (!string.IsNullOrEmpty(userId)) { var userState = GetUserState(userId); userState.RecordOrderValue(orderValue, timestamp, order.OrderId); } // Record in symbol state if (!string.IsNullOrEmpty(order.Symbol)) { var symbolState = GetSymbolState(order.Symbol); symbolState.RecordOrderValue(orderValue, timestamp, order.OrderId); } // 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.RecordOrderValue(orderValue, timestamp, order.OrderId); // Record in sector state var sector = DetermineSector(order.Symbol); if (!string.IsNullOrEmpty(sector)) { var sectorState = GetSectorState(sector); sectorState.RecordOrderValue(orderValue, timestamp, order.OrderId); } } private ValueLimitState GetUserState(string userId) { lock (_lock) { if (!_userStates.ContainsKey(userId)) { _userStates[userId] = new ValueLimitState(); } return _userStates[userId]; } } private ValueLimitState GetSymbolState(string symbol) { lock (_lock) { if (!_symbolStates.ContainsKey(symbol)) { _symbolStates[symbol] = new ValueLimitState(); } return _symbolStates[symbol]; } } private ValueLimitState GetVenueState(string venueId) { lock (_lock) { if (!_venueStates.ContainsKey(venueId)) { _venueStates[venueId] = new ValueLimitState(); } return _venueStates[venueId]; } } private ValueLimitState GetSectorState(string sector) { lock (_lock) { if (!_sectorStates.ContainsKey(sector)) { _sectorStates[sector] = new ValueLimitState(); } return _sectorStates[sector]; } } private void HandleViolations(List violations, string userId) { if (_config.LogViolations) { foreach (var violation in violations) { if (violation.IsSoftLimitWarning) { _logger.LogWarning("Value limit warning: {ErrorMessage}", violation.ErrorMessage); } else { _logger.LogError("Value limit violation: {ErrorMessage}", violation.ErrorMessage); } } } if (_config.GenerateViolationAlerts) { // Track violations for alerting lock (_lock) { if (!_violations.ContainsKey(userId)) { _violations[userId] = new List(); } _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 ValueLimitAction DetermineAction(List violations) { // If any violation resulted in a ban, return ban action if (violations.Any(v => v.ResultedInBan)) { return ValueLimitAction.Ban; } // If any violation is a hard limit (not soft limit warning), reject if (violations.Any(v => !v.IsSoftLimitWarning)) { return ValueLimitAction.Reject; } // If all violations are soft limit warnings, warn if (violations.All(v => v.IsSoftLimitWarning)) { return ValueLimitAction.Warn; } // Otherwise, allow the order return ValueLimitAction.Allow; } 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(24); // Prune global state _globalState.PruneOldValues(maxAge); // Prune user states lock (_lock) { var usersToRemove = new List(); foreach (var kvp in _userStates) { kvp.Value.PruneOldValues(maxAge); // Remove empty states to prevent memory leaks if (kvp.Value.GetOrderCount(TimeSpan.FromHours(48)) == 0) { usersToRemove.Add(kvp.Key); } } foreach (var user in usersToRemove) { _userStates.Remove(user); } // Prune symbol states var symbolsToRemove = new List(); foreach (var kvp in _symbolStates) { kvp.Value.PruneOldValues(maxAge); // Remove empty states to prevent memory leaks if (kvp.Value.GetOrderCount(TimeSpan.FromHours(48)) == 0) { symbolsToRemove.Add(kvp.Key); } } foreach (var symbol in symbolsToRemove) { _symbolStates.Remove(symbol); } // Prune venue states var venuesToRemove = new List(); foreach (var kvp in _venueStates) { kvp.Value.PruneOldValues(maxAge); // Remove empty states to prevent memory leaks if (kvp.Value.GetOrderCount(TimeSpan.FromHours(48)) == 0) { venuesToRemove.Add(kvp.Key); } } foreach (var venue in venuesToRemove) { _venueStates.Remove(venue); } // Prune sector states var sectorsToRemove = new List(); foreach (var kvp in _sectorStates) { kvp.Value.PruneOldValues(maxAge); // Remove empty states to prevent memory leaks if (kvp.Value.GetOrderCount(TimeSpan.FromHours(48)) == 0) { sectorsToRemove.Add(kvp.Key); } } foreach (var sector in sectorsToRemove) { _sectorStates.Remove(sector); } // Prune violations var cutoffTime = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(_config.ViolationWindowMinutes * 2)); var usersToRemoveFromViolations = new List(); 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(); 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 value limit data"); } } /// /// Get current value limit metrics /// public ValueLimitMetrics GetMetrics() { lock (_lock) { return new ValueLimitMetrics { GlobalTotalValueToday = _globalState.GetTotalValue(TimeSpan.FromDays(1)), GlobalAverageOrderValue = _globalState.GetAverageValue(TimeSpan.FromDays(1)), GlobalMaxOrderValue = _globalState.GetMaxValue(TimeSpan.FromDays(1)), GlobalOrderCountToday = _globalState.GetOrderCount(TimeSpan.FromDays(1)), UserValues = _userStates.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.GetTotalValue(TimeSpan.FromDays(1))), SymbolValues = _symbolStates.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.GetTotalValue(TimeSpan.FromDays(1))), VenueValues = _venueStates.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.GetTotalValue(TimeSpan.FromDays(1))), SectorValues = _sectorStates.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.GetTotalValue(TimeSpan.FromDays(1))), ViolationCount = _violations.Sum(kvp => kvp.Value.Count), BanCount = _bans.Count }; } } /// /// Reset value limit state for a user /// 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("Value limit state reset for user {UserId}", userId); } /// /// Reset value limit state for a symbol /// public void ResetSymbolState(string symbol) { if (string.IsNullOrEmpty(symbol)) return; lock (_lock) { if (_symbolStates.ContainsKey(symbol)) { _symbolStates[symbol].Clear(); } } _logger.LogInformation("Value limit state reset for symbol {Symbol}", symbol); } /// /// Reset all value limit state /// public void ResetAllState() { lock (_lock) { _globalState.Clear(); _userStates.Clear(); _symbolStates.Clear(); _venueStates.Clear(); _sectorStates.Clear(); _violations.Clear(); _bans.Clear(); } _logger.LogInformation("All value limit state reset"); } public void Dispose() { _cleanupTimer?.Dispose(); } } ``` ### Value Limit Results ```csharp /// /// Result of value limit check /// public record ValueLimitResult { /// /// Action to take for the order /// public ValueLimitAction Action { get; set; } /// /// Violations that occurred (if any) /// public List Violations { get; set; } = new List(); public ValueLimitResult(ValueLimitAction action, List violations = null) { Action = action; Violations = violations ?? new List(); } } /// /// Value limit metrics /// public record ValueLimitMetrics { public decimal GlobalTotalValueToday { get; set; } public decimal GlobalAverageOrderValue { get; set; } public decimal GlobalMaxOrderValue { get; set; } public int GlobalOrderCountToday { get; set; } public Dictionary UserValues { get; set; } = new Dictionary(); public Dictionary SymbolValues { get; set; } = new Dictionary(); public Dictionary VenueValues { get; set; } = new Dictionary(); public Dictionary SectorValues { get; set; } = new Dictionary(); public int ViolationCount { get; set; } public int BanCount { get; set; } public DateTime Timestamp { get; set; } = DateTime.UtcNow; } ``` ### Currency Converter Interface ```csharp /// /// Interface for currency conversion /// public interface ICurrencyConverter { /// /// Convert amount from one currency to another /// Task ConvertAsync(decimal amount, string fromCurrency, string toCurrency); /// /// Get current exchange rate /// Task GetExchangeRateAsync(string fromCurrency, string toCurrency); /// /// Get historical exchange rate /// Task GetHistoricalExchangeRateAsync(string fromCurrency, string toCurrency, DateTime date); } ``` ## Integration with OrderManager ### Value Limiting Integration ```csharp public partial class OrderManager : IOrderManager { private readonly ValueLimiter _valueLimiter; // Enhanced constructor with value limiter public OrderManager( IRiskManager riskManager, IPositionSizer positionSizer, ILogger logger, RoutingConfigurationManager configManager, RoutingMetricsCollector metricsCollector, TwapExecutor twapExecutor, VwapExecutor vwapExecutor, IcebergExecutor icebergExecutor, AlgorithmParameterProvider parameterProvider, RateLimiter rateLimiter, ValueLimiter valueLimiter) : base(riskManager, positionSizer, logger, configManager, metricsCollector, twapExecutor, vwapExecutor, icebergExecutor, parameterProvider, rateLimiter) { _valueLimiter = valueLimiter ?? throw new ArgumentNullException(nameof(valueLimiter)); _venueManager = new VenueManager(logger); _omsToVenueOrderIdMap = new Dictionary(); _venueToOmsOrderIdMap = new Dictionary(); // Initialize with configurations InitializeWithConfigurationsAsync().Wait(); } /// /// Submit an order with value limiting /// public async Task SubmitOrderAsync(OrderRequest request, StrategyContext context) { if (request == null) throw new ArgumentNullException(nameof(request)); if (context == null) throw new ArgumentNullException(nameof(context)); try { // Check value limits var valueLimitResult = await _valueLimiter.CheckValueLimitAsync(request, context); switch (valueLimitResult.Action) { case ValueLimitAction.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 ValueLimitAction.Reject: _logger.LogWarning("Order submission rejected due to value limit violation"); return new OrderResult(false, null, "Order submission rejected due to value limit violation", null); case ValueLimitAction.Warn: _logger.LogWarning("Order submission warning due to approaching value limit"); // Continue with order submission but log warning break; case ValueLimitAction.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 value limits for order submission"); // In case of value limiter error, we'll proceed with the order but log the error return await base.SubmitOrderAsync(request, context); } } /// /// Get value limit metrics /// public ValueLimitMetrics GetValueLimitMetrics() { return _valueLimiter.GetMetrics(); } /// /// Reset value limit state for a user /// public void ResetUserValueLimitState(string userId) { _valueLimiter.ResetUserState(userId); } /// /// Reset value limit state for a symbol /// public void ResetSymbolValueLimitState(string symbol) { _valueLimiter.ResetSymbolState(symbol); } /// /// Reset all value limit state /// public void ResetAllValueLimitState() { _valueLimiter.ResetAllState(); } } ``` ## Value Limit Configuration Management ### Value Limit Configuration Integration ```csharp public partial class RoutingConfigurationManager { /// /// Get value limit configuration /// public async Task GetValueLimitConfigAsync() { var config = await GetConfigurationAsync("value-limit-config"); return config ?? ValueLimitConfig.Default; } /// /// Update value limit configuration /// public async Task UpdateValueLimitConfigAsync(ValueLimitConfig config) { if (config == null) throw new ArgumentNullException(nameof(config)); config.Id = "value-limit-config"; config.Name = "Value Limit Configuration"; config.Description = "Configuration for order value limiting"; await UpdateConfigurationAsync(config); _logger.LogInformation("Value limit configuration updated"); } } ``` ## Testing Considerations ### Unit Tests for Value Limiting 1. **Value Calculation**: Test calculation of order values in different currencies 2. **Value Limit Checking**: Test value limit checks for different dimensions 3. **Value Limit Violations**: Test handling of value limit violations 4. **Temporary Bans**: Test temporary ban functionality 5. **Soft Limits**: Test soft limit warnings 6. **State Management**: Test value limit state tracking and pruning 7. **Configuration Validation**: Test validation of value limit configurations 8. **Metrics Collection**: Test collection of value limit metrics 9. **Reset Functionality**: Test resetting of value limit state 10. **Currency Conversion**: Test currency conversion functionality ### Integration Tests 1. **End-to-End Value Limiting**: Test complete value 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 value limiter 5. **Error Handling**: Test error handling in value limiting 6. **Configuration Updates**: Test dynamic configuration updates ## Performance Considerations ### Memory Management ```csharp /// /// Manages memory usage for value limiting /// public class ValueLimitMemoryManager { private readonly int _maxUsers; private readonly int _maxSymbols; private readonly int _maxVenues; private readonly int _maxSectors; private readonly TimeSpan _stateRetentionTime; private readonly object _lock = new object(); public ValueLimitMemoryManager( int maxUsers = 10000, int maxSymbols = 1000, int maxVenues = 100, int maxSectors = 10, TimeSpan retentionTime = default) { _maxUsers = maxUsers; _maxSymbols = maxSymbols; _maxVenues = maxVenues; _maxSectors = maxSectors; _stateRetentionTime = retentionTime == default ? TimeSpan.FromHours(24) : retentionTime; } public bool IsMemoryPressureHigh( int currentUserCount, int currentSymbolCount, int currentVenueCount, int currentSectorCount) { return currentUserCount > (_maxUsers * 0.8) || // 80% threshold currentSymbolCount > (_maxSymbols * 0.8) || currentVenueCount > (_maxVenues * 0.8) || currentSectorCount > (_maxSectors * 0.8); } public TimeSpan GetStateRetentionTime() { return _stateRetentionTime; } } ``` ### Adaptive Value Limiting ```csharp /// /// Adaptive value limiting that adjusts based on market conditions /// public class AdaptiveValueLimiter : ValueLimiter { private readonly IMarketAnalyzer _marketAnalyzer; private readonly ValueLimitConfig _baseConfig; public AdaptiveValueLimiter( ILogger logger, ICurrencyConverter currencyConverter, IMarketAnalyzer marketAnalyzer, ValueLimitConfig config = null) : base(logger, currencyConverter, config) { _marketAnalyzer = marketAnalyzer ?? throw new ArgumentNullException(nameof(marketAnalyzer)); _baseConfig = config ?? ValueLimitConfig.Default; } protected override ValueLimitConfig GetEffectiveConfig() { if (!_baseConfig.EnableAdaptiveValueLimiting) return _baseConfig; var marketConditions = _marketAnalyzer.GetMarketConditions(); if (marketConditions.Volatility > _baseConfig.AdaptiveValueLimitingThreshold) { // Reduce value limits based on market volatility var reductionFactor = 1.0 - (_baseConfig.AdaptiveValueLimitingReduction * (marketConditions.Volatility - _baseConfig.AdaptiveValueLimitingThreshold) / (1.0 - _baseConfig.AdaptiveValueLimitingThreshold)); return new ValueLimitConfig { Id = _baseConfig.Id, Name = _baseConfig.Name, Description = _baseConfig.Description, IsActive = _baseConfig.IsActive, CreatedAt = _baseConfig.CreatedAt, UpdatedAt = _baseConfig.UpdatedAt, Version = _baseConfig.Version, MaxOrderValue = (decimal)(_baseConfig.MaxOrderValue * reductionFactor), MaxDailyOrderValue = (decimal)(_baseConfig.MaxDailyOrderValue * reductionFactor), MaxOrderValuePerUser = (decimal)(_baseConfig.MaxOrderValuePerUser * reductionFactor), MaxDailyOrderValuePerUser = (decimal)(_baseConfig.MaxDailyOrderValuePerUser * reductionFactor), MaxOrderValuePerSymbol = (decimal)(_baseConfig.MaxOrderValuePerSymbol * reductionFactor), MaxDailyOrderValuePerSymbol = (decimal)(_baseConfig.MaxDailyOrderValuePerSymbol * reductionFactor), MaxOrderValuePerVenue = (decimal)(_baseConfig.MaxOrderValuePerVenue * reductionFactor), MaxDailyOrderValuePerVenue = (decimal)(_baseConfig.MaxDailyOrderValuePerVenue * reductionFactor), MaxOrderValuePerSector = _baseConfig.MaxOrderValuePerSector.ToDictionary( kvp => kvp.Key, kvp => (decimal)(kvp.Value * reductionFactor)), MaxDailyOrderValuePerSector = _baseConfig.MaxDailyOrderValuePerSector.ToDictionary( kvp => kvp.Key, kvp => (decimal)(kvp.Value * reductionFactor)), CurrencyConversion = _baseConfig.CurrencyConversion, EnableAdaptiveValueLimiting = _baseConfig.EnableAdaptiveValueLimiting, AdaptiveValueLimitingThreshold = _baseConfig.AdaptiveValueLimitingThreshold, AdaptiveValueLimitingReduction = _baseConfig.AdaptiveValueLimitingReduction, LogViolations = _baseConfig.LogViolations, GenerateViolationAlerts = _baseConfig.GenerateViolationAlerts, ViolationAlertThreshold = _baseConfig.ViolationAlertThreshold, ViolationWindowMinutes = _baseConfig.ViolationWindowMinutes, EnableTemporaryBans = _baseConfig.EnableTemporaryBans, BanDurationMinutes = _baseConfig.BanDurationMinutes, BanViolationThreshold = _baseConfig.BanViolationThreshold, EnableSoftLimits = _baseConfig.EnableSoftLimits, SoftLimitWarningThreshold = _baseConfig.SoftLimitWarningThreshold }; } return _baseConfig; } } /// /// Market analyzer for adaptive value limiting /// public interface IMarketAnalyzer { /// /// Get current market conditions /// MarketConditions GetMarketConditions(); } /// /// Market conditions /// public record MarketConditions { public double Volatility { get; set; } // 0.0 to 1.0 public double Liquidity { get; set; } // 0.0 to 1.0 public MarketTrend Trend { get; set; } public DateTime Timestamp { get; set; } = DateTime.UtcNow; } /// /// Market trend enumeration /// public enum MarketTrend { Bullish, Bearish, Sideways } ``` ## Monitoring and Alerting ### Value Limit Metrics Export ```csharp /// /// Exports value limit metrics for monitoring /// public class ValueLimitMetricsExporter { private readonly ValueLimiter _valueLimiter; public ValueLimitMetricsExporter(ValueLimiter valueLimiter) { _valueLimiter = valueLimiter ?? throw new ArgumentNullException(nameof(valueLimiter)); } public string ExportToPrometheus() { var metrics = _valueLimiter.GetMetrics(); var sb = new StringBuilder(); // Global value metrics sb.AppendLine($"# HELP value_limit_global_total_today Current global total value today"); sb.AppendLine($"# TYPE value_limit_global_total_today gauge"); sb.AppendLine($"value_limit_global_total_today {metrics.GlobalTotalValueToday:F2}"); sb.AppendLine($"# HELP value_limit_global_average_order Current global average order value"); sb.AppendLine($"# TYPE value_limit_global_average_order gauge"); sb.AppendLine($"value_limit_global_average_order {metrics.GlobalAverageOrderValue:F2}"); sb.AppendLine($"# HELP value_limit_global_max_order Current global maximum order value"); sb.AppendLine($"# TYPE value_limit_global_max_order gauge"); sb.AppendLine($"value_limit_global_max_order {metrics.GlobalMaxOrderValue:F2}"); sb.AppendLine($"# HELP value_limit_global_order_count_today Current global order count today"); sb.AppendLine($"# TYPE value_limit_global_order_count_today gauge"); sb.AppendLine($"value_limit_global_order_count_today {metrics.GlobalOrderCountToday}"); // User value metrics (top 10 users) var topUsers = metrics.UserValues .OrderByDescending(kvp => kvp.Value) .Take(10); foreach (var kvp in topUsers) { sb.AppendLine($"# HELP value_limit_user_total_today Current total value today for user {kvp.Key}"); sb.AppendLine($"# TYPE value_limit_user_total_today gauge"); sb.AppendLine($"value_limit_user_total_today{{user=\"{kvp.Key}\"}} {kvp.Value:F2}"); } // Symbol value metrics (top 10 symbols) var topSymbols = metrics.SymbolValues .OrderByDescending(kvp => kvp.Value) .Take(10); foreach (var kvp in topSymbols) { sb.AppendLine($"# HELP value_limit_symbol_total_today Current total value today for symbol {kvp.Key}"); sb.AppendLine($"# TYPE value_limit_symbol_total_today gauge"); sb.AppendLine($"value_limit_symbol_total_today{{symbol=\"{kvp.Key}\"}} {kvp.Value:F2}"); } // Violation metrics sb.AppendLine($"# HELP value_limit_violations_total Total value limit violations"); sb.AppendLine($"# TYPE value_limit_violations_total counter"); sb.AppendLine($"value_limit_violations_total {metrics.ViolationCount}"); sb.AppendLine($"# HELP value_limit_bans_total Total temporary bans"); sb.AppendLine($"# TYPE value_limit_bans_total counter"); sb.AppendLine($"value_limit_bans_total {metrics.BanCount}"); return sb.ToString(); } } ``` ## Future Enhancements 1. **Machine Learning Value Limiting**: Use ML to predict optimal value limits based on historical data 2. **Real-time Adaptive Value Limiting**: Adjust value limits in real-time based on market conditions 3. **Cross-Venue Value Limiting**: Coordinate value limits across multiple execution venues 4. **Value Limiting Analytics**: Advanced analytics and reporting on value limiting performance 5. **Value Limiting Strategy Builder**: Visual tools for building and testing value limiting strategies 6. **Value Limiting Benchmarking**: Compare value limiting performance against industry standards 7. **Value Limiting Compliance**: Ensure value limiting complies with exchange and regulatory requirements 8. **Distributed Value Limiting**: Implement value limiting across distributed systems 9. **Hierarchical Value Limiting**: Implement hierarchical value limits for organizations and teams 10. **Value-at-Risk Based Value Limiting**: Adjust value limits based on Value-at-Risk calculations