# Routing Configuration System Design ## Overview This document details the implementation of the routing configuration system for the Order Management System (OMS), which allows users to configure and customize the smart order routing behavior through a flexible and extensible configuration system. ## Configuration Architecture The routing configuration system provides a hierarchical approach to configuration management: 1. **Global Configuration**: System-wide default settings 2. **Venue-Specific Configuration**: Settings that apply to specific execution venues 3. **Symbol-Specific Configuration**: Settings that apply to specific trading symbols 4. **Strategy-Specific Configuration**: Settings that apply to specific trading strategies 5. **Runtime Configuration**: Dynamic configuration that can be updated during operation ## Configuration Models ### Base Configuration Interface ```csharp /// /// Base interface for all configuration objects /// public interface IConfiguration { /// /// Unique identifier for this configuration /// string Id { get; } /// /// Name of this configuration /// string Name { get; } /// /// Description of this configuration /// string Description { get; } /// /// Whether this configuration is active /// bool IsActive { get; set; } /// /// When this configuration was created /// DateTime CreatedAt { get; } /// /// When this configuration was last updated /// DateTime UpdatedAt { get; set; } /// /// Version of this configuration /// int Version { get; } } ``` ### Routing Configuration Model ```csharp /// /// Main routing configuration parameters /// public record RoutingConfig : IConfiguration { public string Id { get; set; } = Guid.NewGuid().ToString(); public string Name { get; set; } = "Default Routing Configuration"; public string Description { get; set; } = "Default routing configuration for the OMS"; 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; // Core routing settings public bool SmartRoutingEnabled { get; set; } = true; public string DefaultVenue { get; set; } = "primary-broker"; public Dictionary VenuePreferences { get; set; } = new Dictionary { ["primary-broker"] = 1.0, ["secondary-broker"] = 0.8, ["dark-pool"] = 0.6 }; // Routing criteria weights public double CostWeight { get; set; } = 0.4; // 40% weight to cost public double SpeedWeight { get; set; } = 0.3; // 30% weight to speed public double ReliabilityWeight { get; set; } = 0.3; // 30% weight to reliability // Risk controls public double MaxSlippagePercent { get; set; } = 0.5; public TimeSpan MaxRoutingTime { get; set; } = TimeSpan.FromSeconds(30); public bool EnableSlippageControl { get; set; } = true; public bool EnableTimeoutControl { get; set; } = true; // Advanced routing features public bool EnableTimeBasedRouting { get; set; } = true; public bool EnableLiquidityBasedRouting { get; set; } = true; public bool EnableSizeBasedRouting { get; set; } = true; // Performance thresholds public double MinFillRateThreshold { get; set; } = 0.95; // 95% minimum fill rate public double MaxLatencyThresholdMs { get; set; } = 500; // 500ms maximum latency public int MaxConsecutiveFailures { get; set; } = 5; // Max consecutive failures before deactivating venue // Algorithmic order routing public Dictionary AlgorithmVenuePreferences { get; set; } = new Dictionary { ["TWAP"] = "primary-broker", ["VWAP"] = "primary-broker", ["Iceberg"] = "dark-pool" }; // Symbol-specific routing public Dictionary SymbolVenuePreferences { get; set; } = new Dictionary(); // Time-based routing public Dictionary TimeBasedVenuePreferences { get; set; } = new Dictionary(); public static RoutingConfig Default => new RoutingConfig(); } ``` ### Time of Day Range Model ```csharp /// /// Represents a time range during the day /// public record TimeOfDayRange( TimeSpan StartTime, TimeSpan EndTime ) { public bool Contains(DateTime time) { var timeOfDay = time.TimeOfDay; return timeOfDay >= StartTime && timeOfDay <= EndTime; } public bool Overlaps(TimeOfDayRange other) { return StartTime <= other.EndTime && EndTime >= other.StartTime; } } ``` ### Venue Configuration Model ```csharp /// /// Configuration for a specific execution venue /// public record VenueConfig : IConfiguration { public string Id { get; set; } public string Name { get; set; } public string Description { get; set; } 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; // Venue-specific routing settings public double CostFactor { get; set; } = 1.0; public double SpeedFactor { get; set; } = 1.0; public double ReliabilityFactor { get; set; } = 1.0; // Venue capabilities public List SupportedOrderTypes { get; set; } = new List { VenueOrderType.Market, VenueOrderType.Limit, VenueOrderType.StopMarket, VenueOrderType.StopLimit }; public List SupportedSymbols { get; set; } = new List(); public Dictionary MinimumOrderSizes { get; set; } = new Dictionary(); public Dictionary MaximumOrderSizes { get; set; } = new Dictionary(); // Connection settings public string ApiKey { get; set; } public string ApiSecret { get; set; } public string BaseUrl { get; set; } public int RateLimit { get; set; } = 100; // Requests per minute // Performance settings public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30); public int MaxRetries { get; set; } = 3; public TimeSpan RetryDelay { get; set; } = TimeSpan.FromSeconds(1); // Market data settings public bool EnableMarketData { get; set; } = true; public int MarketDataDepth { get; set; } = 10; // Levels of market depth public TimeSpan MarketDataRefreshInterval { get; set; } = TimeSpan.FromMilliseconds(100); } ``` ### Symbol Configuration Model ```csharp /// /// Configuration for routing specific symbols /// public record SymbolRoutingConfig : IConfiguration { public string Id { get; set; } public string Name { get; set; } public string Description { get; set; } 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; // Symbol-specific routing settings public string Symbol { get; set; } public string PreferredVenue { get; set; } public List BackupVenues { get; set; } = new List(); // Symbol characteristics public LiquidityLevel Liquidity { get; set; } = LiquidityLevel.Medium; public VolatilityLevel Volatility { get; set; } = VolatilityLevel.Medium; public string AssetClass { get; set; } = "Equity"; // Order size thresholds public int LargeOrderThreshold { get; set; } = 100; public int BlockOrderThreshold { get; set; } = 1000; // Pricing settings public int TickSize { get; set; } = 1; // In cents or appropriate units public decimal MinPriceIncrement { get; set; } = 0.01m; // Venue preferences for this symbol public Dictionary VenuePreferences { get; set; } = new Dictionary(); } ``` ## Configuration Management System ### Configuration Manager ```csharp /// /// Manages routing configuration for the OMS /// public class RoutingConfigurationManager { private readonly ILogger _logger; private readonly IConfigurationRepository _configRepository; private readonly Dictionary _configurations; private readonly object _lock = new object(); public RoutingConfigurationManager( ILogger logger, IConfigurationRepository configRepository) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _configRepository = configRepository ?? throw new ArgumentNullException(nameof(configRepository)); _configurations = new Dictionary(); // Load initial configurations LoadConfigurationsAsync().Wait(); } /// /// Get the current routing configuration /// public async Task GetRoutingConfigAsync() { var config = await GetConfigurationAsync("routing-config"); return config ?? RoutingConfig.Default; } /// /// Update the routing configuration /// public async Task UpdateRoutingConfigAsync(RoutingConfig config) { if (config == null) throw new ArgumentNullException(nameof(config)); await UpdateConfigurationAsync(config); _logger.LogInformation("Routing configuration updated"); } /// /// Get configuration for a specific venue /// public async Task GetVenueConfigAsync(string venueId) { if (string.IsNullOrEmpty(venueId)) throw new ArgumentException("Venue ID required", nameof(venueId)); var config = await GetConfigurationAsync($"venue-{venueId}"); return config; } /// /// Update configuration for a specific venue /// public async Task UpdateVenueConfigAsync(VenueConfig config) { if (config == null) throw new ArgumentNullException(nameof(config)); if (string.IsNullOrEmpty(config.Id)) throw new ArgumentException("Venue config ID required", nameof(config)); await UpdateConfigurationAsync(config); _logger.LogInformation("Venue configuration updated for {VenueId}", config.Id); } /// /// Get routing configuration for a specific symbol /// public async Task GetSymbolRoutingConfigAsync(string symbol) { if (string.IsNullOrEmpty(symbol)) throw new ArgumentException("Symbol required", nameof(symbol)); var config = await GetConfigurationAsync($"symbol-{symbol}"); return config; } /// /// Update routing configuration for a specific symbol /// public async Task UpdateSymbolRoutingConfigAsync(SymbolRoutingConfig config) { if (config == null) throw new ArgumentNullException(nameof(config)); if (string.IsNullOrEmpty(config.Symbol)) throw new ArgumentException("Symbol required", nameof(config)); var configId = $"symbol-{config.Symbol}"; config.Id = configId; config.Name = $"Routing config for {config.Symbol}"; config.Description = $"Routing configuration for symbol {config.Symbol}"; await UpdateConfigurationAsync(config); _logger.LogInformation("Symbol routing configuration updated for {Symbol}", config.Symbol); } /// /// Get configuration by ID /// public async Task GetConfigurationAsync(string configId) where T : class, IConfiguration { if (string.IsNullOrEmpty(configId)) throw new ArgumentException("Config ID required", nameof(configId)); lock (_lock) { if (_configurations.ContainsKey(configId)) { return _configurations[configId] as T; } } // Load from repository if not in memory var config = await _configRepository.GetConfigurationAsync(configId); if (config != null) { lock (_lock) { _configurations[configId] = config; } } return config; } /// /// Update a configuration /// public async Task UpdateConfigurationAsync(T config) where T : class, IConfiguration { if (config == null) throw new ArgumentNullException(nameof(config)); if (string.IsNullOrEmpty(config.Id)) throw new ArgumentException("Config ID required", nameof(config)); // Validate configuration if (!ValidateConfiguration(config)) { throw new ArgumentException("Invalid configuration", nameof(config)); } // Increment version var versionProperty = typeof(T).GetProperty("Version"); if (versionProperty != null && versionProperty.CanWrite) { var currentVersion = (int)versionProperty.GetValue(config); versionProperty.SetValue(config, currentVersion + 1); } // Update timestamp var updatedAtProperty = typeof(T).GetProperty("UpdatedAt"); if (updatedAtProperty != null && updatedAtProperty.CanWrite) { updatedAtProperty.SetValue(config, DateTime.UtcNow); } // Save to repository await _configRepository.SaveConfigurationAsync(config); // Update in memory cache lock (_lock) { _configurations[config.Id] = config; } } /// /// Validate a configuration /// public bool ValidateConfiguration(T config) where T : class, IConfiguration { if (config == null) return false; // Basic validation if (string.IsNullOrEmpty(config.Id)) return false; if (string.IsNullOrEmpty(config.Name)) return false; if (config.CreatedAt > DateTime.UtcNow) return false; if (config.UpdatedAt < config.CreatedAt) return false; if (config.Version < 1) return false; // Type-specific validation switch (config) { case RoutingConfig routingConfig: return ValidateRoutingConfig(routingConfig); case VenueConfig venueConfig: return ValidateVenueConfig(venueConfig); case SymbolRoutingConfig symbolConfig: return ValidateSymbolRoutingConfig(symbolConfig); default: return true; // Unknown config type, assume valid } } private bool ValidateRoutingConfig(RoutingConfig config) { // Validate weights sum to 1.0 (approximately) var totalWeight = config.CostWeight + config.SpeedWeight + config.ReliabilityWeight; if (Math.Abs(totalWeight - 1.0) > 0.01) return false; // Validate thresholds if (config.MaxSlippagePercent < 0 || config.MaxSlippagePercent > 100) return false; if (config.MaxRoutingTime.TotalMilliseconds <= 0) return false; if (config.MinFillRateThreshold < 0 || config.MinFillRateThreshold > 1) return false; if (config.MaxLatencyThresholdMs <= 0) return false; if (config.MaxConsecutiveFailures <= 0) return false; return true; } private bool ValidateVenueConfig(VenueConfig config) { // Validate factors are positive if (config.CostFactor <= 0) return false; if (config.SpeedFactor <= 0) return false; if (config.ReliabilityFactor <= 0) return false; // Validate connection settings if (string.IsNullOrEmpty(config.BaseUrl)) return false; if (config.RateLimit <= 0) return false; if (config.Timeout.TotalMilliseconds <= 0) return false; if (config.MaxRetries < 0) return false; return true; } private bool ValidateSymbolRoutingConfig(SymbolRoutingConfig config) { // Validate symbol if (string.IsNullOrEmpty(config.Symbol)) return false; // Validate thresholds if (config.LargeOrderThreshold <= 0) return false; if (config.BlockOrderThreshold <= 0) return false; if (config.LargeOrderThreshold > config.BlockOrderThreshold) return false; // Validate pricing settings if (config.TickSize <= 0) return false; if (config.MinPriceIncrement <= 0) return false; return true; } /// /// Load all configurations from repository /// private async Task LoadConfigurationsAsync() { try { var configs = await _configRepository.GetAllConfigurationsAsync(); lock (_lock) { _configurations.Clear(); foreach (var config in configs) { _configurations[config.Id] = config; } } _logger.LogInformation("Loaded {Count} configurations", configs.Count); } catch (Exception ex) { _logger.LogError(ex, "Error loading configurations"); throw; } } /// /// Reload configurations from repository /// public async Task ReloadConfigurationsAsync() { await LoadConfigurationsAsync(); _logger.LogInformation("Configurations reloaded"); } } ``` ### Configuration Repository Interface ```csharp /// /// Repository for configuration storage and retrieval /// public interface IConfigurationRepository { /// /// Get a configuration by ID /// Task GetConfigurationAsync(string configId) where T : class, IConfiguration; /// /// Save a configuration /// Task SaveConfigurationAsync(T config) where T : class, IConfiguration; /// /// Delete a configuration /// Task DeleteConfigurationAsync(string configId); /// /// Get all configurations /// Task> GetAllConfigurationsAsync(); /// /// Get configurations by type /// Task> GetConfigurationsByTypeAsync() where T : class, IConfiguration; } ``` ### File-Based Configuration Repository ```csharp /// /// File-based implementation of configuration repository /// public class FileConfigurationRepository : IConfigurationRepository { private readonly string _configDirectory; private readonly ILogger _logger; private readonly JsonSerializerOptions _jsonOptions; public FileConfigurationRepository( string configDirectory, ILogger logger) { _configDirectory = configDirectory ?? throw new ArgumentNullException(nameof(configDirectory)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); // Ensure directory exists if (!Directory.Exists(_configDirectory)) { Directory.CreateDirectory(_configDirectory); } _jsonOptions = new JsonSerializerOptions { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; } public async Task GetConfigurationAsync(string configId) where T : class, IConfiguration { if (string.IsNullOrEmpty(configId)) throw new ArgumentException("Config ID required", nameof(configId)); var filePath = Path.Combine(_configDirectory, $"{configId}.json"); if (!File.Exists(filePath)) { return null; } try { var json = await File.ReadAllTextAsync(filePath); return JsonSerializer.Deserialize(json, _jsonOptions); } catch (Exception ex) { _logger.LogError(ex, "Error reading configuration from {FilePath}", filePath); throw; } } public async Task SaveConfigurationAsync(T config) where T : class, IConfiguration { if (config == null) throw new ArgumentNullException(nameof(config)); if (string.IsNullOrEmpty(config.Id)) throw new ArgumentException("Config ID required", nameof(config)); var filePath = Path.Combine(_configDirectory, $"{config.Id}.json"); try { var json = JsonSerializer.Serialize(config, _jsonOptions); await File.WriteAllTextAsync(filePath, json); } catch (Exception ex) { _logger.LogError(ex, "Error saving configuration to {FilePath}", filePath); throw; } } public async Task DeleteConfigurationAsync(string configId) { if (string.IsNullOrEmpty(configId)) throw new ArgumentException("Config ID required", nameof(configId)); var filePath = Path.Combine(_configDirectory, $"{configId}.json"); if (File.Exists(filePath)) { try { File.Delete(filePath); } catch (Exception ex) { _logger.LogError(ex, "Error deleting configuration file {FilePath}", filePath); throw; } } } public async Task> GetAllConfigurationsAsync() { var configs = new List(); try { var files = Directory.GetFiles(_configDirectory, "*.json"); foreach (var file in files) { try { var json = await File.ReadAllTextAsync(file); // Try to deserialize as different config types // In a real implementation, you might store type information in the file var config = TryDeserializeConfig(json); if (config != null) { configs.Add(config); } } catch (Exception ex) { _logger.LogWarning(ex, "Error reading configuration file {FilePath}", file); } } } catch (Exception ex) { _logger.LogError(ex, "Error reading configuration directory {ConfigDirectory}", _configDirectory); throw; } return configs; } public async Task> GetConfigurationsByTypeAsync() where T : class, IConfiguration { var configs = new List(); try { var files = Directory.GetFiles(_configDirectory, "*.json"); foreach (var file in files) { try { var json = await File.ReadAllTextAsync(file); var config = JsonSerializer.Deserialize(json, _jsonOptions); if (config != null) { configs.Add(config); } } catch (Exception ex) { _logger.LogWarning(ex, "Error reading configuration file {FilePath}", file); } } } catch (Exception ex) { _logger.LogError(ex, "Error reading configuration directory {ConfigDirectory}", _configDirectory); throw; } return configs; } private IConfiguration TryDeserializeConfig(string json) { try { // Try different configuration types var doc = JsonDocument.Parse(json); var root = doc.RootElement; if (root.TryGetProperty("costWeight", out _)) { return JsonSerializer.Deserialize(json, _jsonOptions); } else if (root.TryGetProperty("costFactor", out _)) { return JsonSerializer.Deserialize(json, _jsonOptions); } else if (root.TryGetProperty("symbol", out _)) { return JsonSerializer.Deserialize(json, _jsonOptions); } return null; } catch { return null; } } } ``` ## Integration with OrderManager ### Configuration Integration in OrderManager ```csharp public partial class OrderManager : IOrderManager { private readonly RoutingConfigurationManager _configManager; // Enhanced constructor with configuration manager public OrderManager( IRiskManager riskManager, IPositionSizer positionSizer, ILogger logger, RoutingConfigurationManager configManager) : base(riskManager, positionSizer, logger) { _configManager = configManager ?? throw new ArgumentNullException(nameof(configManager)); _venueManager = new VenueManager(logger); _omsToVenueOrderIdMap = new Dictionary(); _venueToOmsOrderIdMap = new Dictionary(); // Initialize with configurations InitializeWithConfigurationsAsync().Wait(); } private async Task InitializeWithConfigurationsAsync() { try { // Get routing configuration var routingConfig = await _configManager.GetRoutingConfigAsync(); // Initialize venues based on configuration await InitializeVenuesFromConfigAsync(routingConfig); _logger.LogInformation("OrderManager initialized with configurations"); } catch (Exception ex) { _logger.LogError(ex, "Error initializing OrderManager with configurations"); throw; } } private async Task InitializeVenuesFromConfigAsync(RoutingConfig routingConfig) { // Get all venue configurations var venueConfigs = await _configManager.GetConfigurationsByTypeAsync(); foreach (var venueConfig in venueConfigs) { if (!venueConfig.IsActive) continue; try { // Create venue based on configuration var venue = CreateVenueFromConfig(venueConfig); if (venue != null) { _venueManager.AddVenue(venue); _logger.LogInformation("Venue {VenueId} initialized from configuration", venueConfig.Id); } } catch (Exception ex) { _logger.LogError(ex, "Error initializing venue {VenueId} from configuration", venueConfig.Id); } } } private IExecutionVenue CreateVenueFromConfig(VenueConfig config) { // Create venue based on type and configuration // This is a simplified implementation - in reality, you would have // specific venue implementations for different broker/exchange APIs return new BrokerExecutionVenue( id: config.Id, name: config.Name, description: config.Description, type: VenueType.Broker, config: config, brokerApi: CreateBrokerApiFromConfig(config), logger: _logger ); } private IBrokerApi CreateBrokerApiFromConfig(VenueConfig config) { // Create broker API client based on configuration // This would be specific to each broker's API return new GenericBrokerApi( apiKey: config.ApiKey, apiSecret: config.ApiSecret, baseUrl: config.BaseUrl, rateLimit: config.RateLimit, timeout: config.Timeout, maxRetries: config.MaxRetries, retryDelay: config.RetryDelay ); } // Enhanced routing with configuration public async Task RouteOrderAsync(OrderRequest request, StrategyContext context) { try { // Get current routing configuration var routingConfig = await _configManager.GetRoutingConfigAsync(); // Check if smart routing is enabled if (!routingConfig.SmartRoutingEnabled) { var defaultVenue = _venueManager.GetVenue(routingConfig.DefaultVenue); if (defaultVenue == null) { return new RoutingResult(false, null, null, "Default venue not found", new Dictionary { ["error"] = "Default venue not found" }); } _logger.LogInformation("Smart routing disabled, using default venue: {Venue}", defaultVenue.Name); return new RoutingResult(true, null, defaultVenue, "Routing disabled, using default venue", new Dictionary { ["venue"] = defaultVenue.Name }); } // Get symbol-specific configuration if available SymbolRoutingConfig symbolConfig = null; if (!string.IsNullOrEmpty(request.Symbol)) { symbolConfig = await _configManager.GetSymbolRoutingConfigAsync(request.Symbol); } // Select best venue based on configuration var selectedVenue = await SelectBestVenueAsync(request, context, routingConfig, symbolConfig); if (selectedVenue == null) { return new RoutingResult(false, null, null, "No suitable venue found", new Dictionary { ["error"] = "No suitable venue found" }); } // Validate venue is active if (!selectedVenue.IsActive) { return new RoutingResult(false, null, null, $"Venue {selectedVenue.Name} is not active", new Dictionary { ["error"] = "Venue inactive" }); } // Update routing metrics UpdateRoutingMetrics(selectedVenue); _logger.LogInformation("Order routed to venue: {Venue} (Cost: {Cost}, Speed: {Speed}, Reliability: {Reliability})", selectedVenue.Name, selectedVenue.CostFactor, selectedVenue.SpeedFactor, selectedVenue.ReliabilityFactor); return new RoutingResult(true, null, selectedVenue, "Order routed successfully", new Dictionary { ["venue"] = selectedVenue.Name, ["cost_factor"] = selectedVenue.CostFactor, ["speed_factor"] = selectedVenue.SpeedFactor, ["reliability_factor"] = selectedVenue.ReliabilityFactor }); } catch (Exception ex) { _logger.LogError(ex, "Error routing order for {Symbol}", request.Symbol); return new RoutingResult(false, null, null, $"Error routing order: {ex.Message}", new Dictionary { ["error"] = ex.Message }); } } private async Task SelectBestVenueAsync( OrderRequest request, StrategyContext context, RoutingConfig routingConfig, SymbolRoutingConfig symbolConfig) { // Get all active venues var venues = _venueManager.GetActiveVenues(); if (venues.Count == 0) { return null; } if (venues.Count == 1) { return venues[0]; } // Apply symbol-specific venue preference if available if (symbolConfig != null && !string.IsNullOrEmpty(symbolConfig.PreferredVenue)) { var preferredVenue = venues.FirstOrDefault(v => v.Id == symbolConfig.PreferredVenue); if (preferredVenue != null) { _logger.LogInformation("Using symbol-preferred venue: {Venue}", preferredVenue.Name); return preferredVenue; } } // Apply algorithm-specific venue preference if available if (!string.IsNullOrEmpty(request.Algorithm)) { if (routingConfig.AlgorithmVenuePreferences.ContainsKey(request.Algorithm)) { var algorithmVenue = routingConfig.AlgorithmVenuePreferences[request.Algorithm]; var preferredVenue = venues.FirstOrDefault(v => v.Id == algorithmVenue); if (preferredVenue != null) { _logger.LogInformation("Using algorithm-preferred venue: {Venue} for {Algorithm}", preferredVenue.Name, request.Algorithm); return preferredVenue; } } } // Apply time-based routing if enabled if (routingConfig.EnableTimeBasedRouting) { var timeBasedVenue = SelectVenueBasedOnTime(venues, routingConfig); if (timeBasedVenue != null) { _logger.LogInformation("Using time-based venue: {Venue}", timeBasedVenue.Name); return timeBasedVenue; } } // Calculate scores for all venues var venueScores = new Dictionary(); foreach (var venue in venues) { double score = 0; // Get venue-specific configuration var venueConfig = await _configManager.GetVenueConfigAsync(venue.Id); // Factor in venue preferences from routing config if (routingConfig.VenuePreferences.ContainsKey(venue.Id)) { score += routingConfig.VenuePreferences[venue.Id] * 100; } // Factor in cost var costFactor = venueConfig?.CostFactor ?? venue.CostFactor; score -= costFactor * (routingConfig.CostWeight * 100); // Factor in speed var speedFactor = venueConfig?.SpeedFactor ?? venue.SpeedFactor; score += speedFactor * (routingConfig.SpeedWeight * 100); // Factor in reliability var reliabilityFactor = venueConfig?.ReliabilityFactor ?? venue.ReliabilityFactor; score += reliabilityFactor * (routingConfig.ReliabilityWeight * 100); // Adjust for order characteristics score = AdjustScoreForOrderCharacteristics(score, venue, request, context, routingConfig, symbolConfig); venueScores[venue] = score; } // Select venue with highest score return venueScores.OrderByDescending(kvp => kvp.Value).First().Key; } private double AdjustScoreForOrderCharacteristics( double score, IExecutionVenue venue, OrderRequest request, StrategyContext context, RoutingConfig routingConfig, SymbolRoutingConfig symbolConfig) { // Adjust for order size if (request.Quantity > (symbolConfig?.LargeOrderThreshold ?? 100)) { // Prefer venues that handle large orders well if (venue.Type == VenueType.DarkPool) { score += 20; // Bonus for dark pool handling of large orders } else { score -= 10; // Penalty for regular venues handling large orders } } // Adjust for algorithmic orders if (!string.IsNullOrEmpty(request.Algorithm)) { // Some venues may be better optimized for algorithmic orders if (venue.Id == routingConfig.DefaultVenue) { score += 5; // Small bonus for default venue handling algorithms } } // Adjust for symbol-specific venue preferences if (symbolConfig?.VenuePreferences?.ContainsKey(venue.Id) == true) { score += symbolConfig.VenuePreferences[venue.Id] * 10; } return score; } private IExecutionVenue SelectVenueBasedOnTime(List venues, RoutingConfig routingConfig) { var currentTime = DateTime.UtcNow.TimeOfDay; // Check time-based venue preferences foreach (var kvp in routingConfig.TimeBasedVenuePreferences) { if (kvp.Key.Contains(DateTime.UtcNow)) { var venue = venues.FirstOrDefault(v => v.Id == kvp.Value); if (venue != null) { return venue; } } } return null; } } ``` ## Configuration Validation and Monitoring ### Configuration Validation ```csharp /// /// Validates routing configurations /// public class RoutingConfigurationValidator { private readonly ILogger _logger; public RoutingConfigurationValidator(ILogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task ValidateConfigurationAsync(RoutingConfig config) { var result = new ConfigurationValidationResult { IsValid = true, Errors = new List(), Warnings = new List() }; if (config == null) { result.IsValid = false; result.Errors.Add("Configuration is null"); return result; } // Validate weights var totalWeight = config.CostWeight + config.SpeedWeight + config.ReliabilityWeight; if (Math.Abs(totalWeight - 1.0) > 0.01) { result.Warnings.Add($"Routing weights do not sum to 1.0 (current sum: {totalWeight:F2})"); } // Validate venue preferences foreach (var venueId in config.VenuePreferences.Keys) { if (config.VenuePreferences[venueId] < 0 || config.VenuePreferences[venueId] > 1) { result.Errors.Add($"Invalid venue preference for {venueId}: {config.VenuePreferences[venueId]} (must be between 0 and 1)"); } } // Validate algorithm venue preferences foreach (var algorithm in config.AlgorithmVenuePreferences.Keys) { var venueId = config.AlgorithmVenuePreferences[algorithm]; if (!config.VenuePreferences.ContainsKey(venueId)) { result.Warnings.Add($"Algorithm {algorithm} prefers venue {venueId} which is not in venue preferences"); } } // Validate thresholds if (config.MaxSlippagePercent < 0.01) { result.Warnings.Add("Max slippage percent is very low, may reject valid orders"); } if (config.MaxRoutingTime.TotalMilliseconds < 100) { result.Warnings.Add("Max routing time is very short, may cause timeouts"); } result.IsValid = result.Errors.Count == 0; return result; } } /// /// Result of configuration validation /// public record ConfigurationValidationResult { public bool IsValid { get; set; } public List Errors { get; set; } = new List(); public List Warnings { get; set; } = new List(); } ``` ## Testing Considerations ### Unit Tests for Configuration System 1. **Configuration Validation**: Test validation of different configuration types 2. **Configuration Loading/Saving**: Test persistence of configurations 3. **Configuration Updates**: Test updating configurations with versioning 4. **Venue Selection**: Test venue selection based on different configurations 5. **Symbol-Specific Routing**: Test routing based on symbol configurations ### Integration Tests 1. **Configuration Repository**: Test file-based configuration storage 2. **Dynamic Configuration Updates**: Test updating configurations at runtime 3. **Performance Impact**: Test performance with large configurations 4. **Error Handling**: Test error handling for invalid configurations ## Performance Considerations ### Configuration Caching ```csharp /// /// Caching layer for configurations /// public class ConfigurationCache { private readonly MemoryCache _cache; private readonly ILogger _logger; public ConfigurationCache(ILogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); var options = new MemoryCacheOptions { SizeLimit = 1000, // Maximum number of entries ExpirationScanFrequency = TimeSpan.FromMinutes(5) }; _cache = new MemoryCache(options); } public T Get(string key) where T : class { return _cache.Get(key); } public void Set(string key, T value, TimeSpan expiration) where T : class { var cacheEntryOptions = new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = expiration, Size = 1 }; _cache.Set(key, value, cacheEntryOptions); } public void Remove(string key) { _cache.Remove(key); } public void Clear() { _cache.Clear(); } } ``` ## Monitoring and Alerting ### Configuration Change Tracking ```csharp /// /// Tracks configuration changes for monitoring and auditing /// public class ConfigurationChangeTracker { private readonly ILogger _logger; public ConfigurationChangeTracker(ILogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public void TrackConfigurationChange(T oldConfig, T newConfig, string userId) where T : class, IConfiguration { if (oldConfig == null || newConfig == null) return; var changes = CompareConfigurations(oldConfig, newConfig); if (changes.Any()) { _logger.LogInformation("Configuration {ConfigId} changed by {UserId}: {Changes}", newConfig.Id, userId, string.Join(", ", changes)); } } private List CompareConfigurations(T oldConfig, T newConfig) where T : class, IConfiguration { var changes = new List(); // Use reflection to compare properties var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.CanRead); foreach (var property in properties) { var oldValue = property.GetValue(oldConfig); var newValue = property.GetValue(newConfig); if (!Equals(oldValue, newValue)) { changes.Add($"{property.Name}: {oldValue} -> {newValue}"); } } return changes; } } ``` ## Future Enhancements 1. **Database Configuration Storage**: Store configurations in a database for enterprise deployments 2. **Configuration Versioning**: Full version history and rollback capabilities 3. **Configuration Templates**: Predefined configuration templates for different trading scenarios 4. **A/B Testing**: Test different configurations with subsets of orders 5. **Machine Learning**: Use ML to optimize configuration parameters based on performance 6. **Real-time Configuration Updates**: Push configuration updates to running instances 7. **Configuration Auditing**: Full audit trail of all configuration changes 8. **Multi-tenancy**: Support for multiple tenants with separate configurations