Files
nt8-sdk/docs/architecture/multiple_execution_venues_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

40 KiB

Multiple Execution Venues Implementation Design

Overview

This document details the implementation of support for multiple execution venues in the Order Management System (OMS), allowing orders to be routed to different execution destinations based on routing logic and configuration.

Venue Architecture

The multiple execution venues system is designed to be extensible and configurable, supporting various types of execution destinations including brokers, exchanges, dark pools, and alternative trading systems.

Execution Venue Types

Base Venue Interface

/// <summary>
/// Base interface for all execution venues
/// </summary>
public interface IExecutionVenue
{
    /// <summary>
    /// Unique identifier for the venue
    /// </summary>
    string Id { get; }
    
    /// <summary>
    /// Human-readable name of the venue
    /// </summary>
    string Name { get; }
    
    /// <summary>
    /// Description of the venue
    /// </summary>
    string Description { get; }
    
    /// <summary>
    /// Whether the venue is currently active and available for routing
    /// </summary>
    bool IsActive { get; }
    
    /// <summary>
    /// Type of venue (broker, exchange, dark pool, etc.)
    /// </summary>
    VenueType Type { get; }
    
    /// <summary>
    /// Submit an order to this venue
    /// </summary>
    Task<VenueOrderResult> SubmitOrderAsync(VenueOrderRequest request);
    
    /// <summary>
    /// Cancel an order at this venue
    /// </summary>
    Task<bool> CancelOrderAsync(string venueOrderId);
    
    /// <summary>
    /// Modify an order at this venue
    /// </summary>
    Task<VenueOrderResult> ModifyOrderAsync(string venueOrderId, VenueOrderModification modification);
    
    /// <summary>
    /// Get the status of an order at this venue
    /// </summary>
    Task<VenueOrderStatus> GetOrderStatusAsync(string venueOrderId);
    
    /// <summary>
    /// Get real-time market data from this venue
    /// </summary>
    Task<MarketDataSnapshot> GetMarketDataAsync(string symbol);
    
    /// <summary>
    /// Check if this venue is healthy and responsive
    /// </summary>
    Task<bool> IsHealthyAsync();
}

Venue Order Models

/// <summary>
/// Order request specific to a venue
/// </summary>
public record VenueOrderRequest(
    string Symbol,
    OrderSide Side,
    VenueOrderType Type,
    int Quantity,
    decimal? LimitPrice,
    decimal? StopPrice,
    TimeInForce TimeInForce,
    string OmsOrderId, // Reference to OMS order ID
    Dictionary<string, object> Metadata
);

/// <summary>
/// Result from venue order submission
/// </summary>
public record VenueOrderResult(
    bool Success,
    string VenueOrderId, // Order ID assigned by the venue
    string Message,
    VenueOrderStatus Status,
    Dictionary<string, object> Metadata
);

/// <summary>
/// Status of an order at a venue
/// </summary>
public record VenueOrderStatus(
    string VenueOrderId,
    string Symbol,
    OrderSide Side,
    VenueOrderType Type,
    int Quantity,
    int FilledQuantity,
    decimal? LimitPrice,
    decimal? StopPrice,
    VenueOrderState State,
    DateTime CreatedTime,
    DateTime? FilledTime,
    List<VenueOrderFill> Fills,
    Dictionary<string, object> Metadata
);

/// <summary>
/// Fill information from a venue
/// </summary>
public record VenueOrderFill(
    string VenueOrderId,
    string Symbol,
    int Quantity,
    decimal FillPrice,
    DateTime FillTime,
    decimal Commission,
    string ExecutionId,
    Dictionary<string, object> Metadata
);

Venue Types

/// <summary>
/// Types of execution venues
/// </summary>
public enum VenueType
{
    Broker,
    Exchange,
    DarkPool,
    AlternativeTradingSystem,
    Internalizer,
    MarketMaker
}

/// <summary>
/// Types of orders supported by venues
/// </summary>
public enum VenueOrderType
{
    Market,
    Limit,
    StopMarket,
    StopLimit,
    MarketToLimit,
    Pegged
}

/// <summary>
/// States of orders at venues
/// </summary>
public enum VenueOrderState
{
    New,
    Submitted,
    Accepted,
    PartiallyFilled,
    Filled,
    Cancelled,
    Rejected,
    Expired,
    Suspended
}

Venue Implementations

Base Venue Implementation

/// <summary>
/// Base implementation for execution venues
/// </summary>
public abstract class BaseExecutionVenue : IExecutionVenue
{
    protected readonly ILogger _logger;
    protected readonly VenueConfig _config;
    
    public string Id { get; }
    public string Name { get; }
    public string Description { get; }
    public bool IsActive { get; protected set; }
    public VenueType Type { get; }
    
    protected BaseExecutionVenue(
        string id,
        string name,
        string description,
        VenueType type,
        VenueConfig config,
        ILogger logger)
    {
        Id = id ?? throw new ArgumentNullException(nameof(id));
        Name = name ?? throw new ArgumentNullException(nameof(name));
        Description = description ?? throw new ArgumentNullException(nameof(description));
        Type = type;
        _config = config ?? throw new ArgumentNullException(nameof(config));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        IsActive = true;
    }
    
    public abstract Task<VenueOrderResult> SubmitOrderAsync(VenueOrderRequest request);
    public abstract Task<bool> CancelOrderAsync(string venueOrderId);
    public abstract Task<VenueOrderResult> ModifyOrderAsync(string venueOrderId, VenueOrderModification modification);
    public abstract Task<VenueOrderStatus> GetOrderStatusAsync(string venueOrderId);
    public abstract Task<MarketDataSnapshot> GetMarketDataAsync(string symbol);
    public abstract Task<bool> IsHealthyAsync();
    
    protected virtual void ValidateOrderRequest(VenueOrderRequest request)
    {
        if (request == null)
            throw new ArgumentNullException(nameof(request));
            
        if (string.IsNullOrEmpty(request.Symbol))
            throw new ArgumentException("Symbol is required", nameof(request));
            
        if (request.Quantity <= 0)
            throw new ArgumentException("Quantity must be positive", nameof(request));
            
        if ((request.Type == VenueOrderType.Limit || request.Type == VenueOrderType.StopLimit) 
            && !request.LimitPrice.HasValue)
            throw new ArgumentException("Limit price is required for limit orders", nameof(request));
            
        if ((request.Type == VenueOrderType.StopMarket || request.Type == VenueOrderType.StopLimit) 
            && !request.StopPrice.HasValue)
            throw new ArgumentException("Stop price is required for stop orders", nameof(request));
    }
}

Broker Venue Implementation

/// <summary>
/// Implementation for broker execution venues
/// </summary>
public class BrokerExecutionVenue : BaseExecutionVenue
{
    private readonly IBrokerApi _brokerApi;
    private readonly Dictionary<string, string> _omsToVenueOrderIdMap;
    private readonly Dictionary<string, string> _venueToOmsOrderIdMap;
    
    public BrokerExecutionVenue(
        string id,
        string name,
        string description,
        VenueConfig config,
        IBrokerApi brokerApi,
        ILogger<BrokerExecutionVenue> logger) 
        : base(id, name, description, VenueType.Broker, config, logger)
    {
        _brokerApi = brokerApi ?? throw new ArgumentNullException(nameof(brokerApi));
        _omsToVenueOrderIdMap = new Dictionary<string, string>();
        _venueToOmsOrderIdMap = new Dictionary<string, string>();
    }
    
    public override async Task<VenueOrderResult> SubmitOrderAsync(VenueOrderRequest request)
    {
        try
        {
            ValidateOrderRequest(request);
            
            _logger.LogInformation("Submitting order to broker venue {Venue}: {Symbol} {Side} {Quantity}", 
                Name, request.Symbol, request.Side, request.Quantity);
            
            // Convert to broker-specific order format
            var brokerOrder = ConvertToBrokerOrder(request);
            
            // Submit to broker
            var brokerResult = await _brokerApi.SubmitOrderAsync(brokerOrder);
            
            if (brokerResult.Success)
            {
                // Map order IDs
                lock (_omsToVenueOrderIdMap)
                {
                    _omsToVenueOrderIdMap[request.OmsOrderId] = brokerResult.OrderId;
                    _venueToOmsOrderIdMap[brokerResult.OrderId] = request.OmsOrderId;
                }
                
                var status = ConvertToVenueOrderStatus(brokerResult.OrderStatus);
                
                _logger.LogInformation("Order submitted to broker venue {Venue}: {VenueOrderId}", 
                    Name, brokerResult.OrderId);
                
                return new VenueOrderResult(true, brokerResult.OrderId, "Order submitted successfully", status, 
                    new Dictionary<string, object> { ["broker_response"] = brokerResult.RawResponse });
            }
            else
            {
                _logger.LogWarning("Order submission failed at broker venue {Venue}: {Message}", 
                    Name, brokerResult.Message);
                
                return new VenueOrderResult(false, null, brokerResult.Message, null, 
                    new Dictionary<string, object> { ["broker_error"] = brokerResult.RawResponse });
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error submitting order to broker venue {Venue}", Name);
            return new VenueOrderResult(false, null, $"Error submitting order: {ex.Message}", null, 
                new Dictionary<string, object> { ["error"] = ex.Message });
        }
    }
    
    public override async Task<bool> CancelOrderAsync(string venueOrderId)
    {
        try
        {
            if (string.IsNullOrEmpty(venueOrderId))
                throw new ArgumentException("Venue order ID required", nameof(venueOrderId));
            
            _logger.LogInformation("Cancelling order at broker venue {Venue}: {VenueOrderId}", 
                Name, venueOrderId);
            
            var result = await _brokerApi.CancelOrderAsync(venueOrderId);
            
            if (result)
            {
                _logger.LogInformation("Order cancelled at broker venue {Venue}: {VenueOrderId}", 
                    Name, venueOrderId);
            }
            else
            {
                _logger.LogWarning("Order cancellation failed at broker venue {Venue}: {VenueOrderId}", 
                    Name, venueOrderId);
            }
            
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error cancelling order at broker venue {Venue}: {VenueOrderId}", 
                Name, venueOrderId);
            return false;
        }
    }
    
    public override async Task<VenueOrderResult> ModifyOrderAsync(string venueOrderId, VenueOrderModification modification)
    {
        try
        {
            if (string.IsNullOrEmpty(venueOrderId))
                throw new ArgumentException("Venue order ID required", nameof(venueOrderId));
            
            if (modification == null)
                throw new ArgumentNullException(nameof(modification));
            
            _logger.LogInformation("Modifying order at broker venue {Venue}: {VenueOrderId}", 
                Name, venueOrderId);
            
            var brokerModification = ConvertToBrokerModification(modification);
            var brokerResult = await _brokerApi.ModifyOrderAsync(venueOrderId, brokerModification);
            
            if (brokerResult.Success)
            {
                var status = ConvertToVenueOrderStatus(brokerResult.OrderStatus);
                
                _logger.LogInformation("Order modified at broker venue {Venue}: {VenueOrderId}", 
                    Name, venueOrderId);
                
                return new VenueOrderResult(true, venueOrderId, "Order modified successfully", status, 
                    new Dictionary<string, object> { ["broker_response"] = brokerResult.RawResponse });
            }
            else
            {
                _logger.LogWarning("Order modification failed at broker venue {Venue}: {VenueOrderId}: {Message}", 
                    Name, venueOrderId, brokerResult.Message);
                
                return new VenueOrderResult(false, venueOrderId, brokerResult.Message, null, 
                    new Dictionary<string, object> { ["broker_error"] = brokerResult.RawResponse });
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error modifying order at broker venue {Venue}: {VenueOrderId}", 
                Name, venueOrderId);
            return new VenueOrderResult(false, venueOrderId, $"Error modifying order: {ex.Message}", null, 
                new Dictionary<string, object> { ["error"] = ex.Message });
        }
    }
    
    public override async Task<VenueOrderStatus> GetOrderStatusAsync(string venueOrderId)
    {
        try
        {
            if (string.IsNullOrEmpty(venueOrderId))
                throw new ArgumentException("Venue order ID required", nameof(venueOrderId));
            
            var brokerStatus = await _brokerApi.GetOrderStatusAsync(venueOrderId);
            return ConvertToVenueOrderStatus(brokerStatus);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting order status from broker venue {Venue}: {VenueOrderId}", 
                Name, venueOrderId);
            throw;
        }
    }
    
    public override async Task<MarketDataSnapshot> GetMarketDataAsync(string symbol)
    {
        try
        {
            if (string.IsNullOrEmpty(symbol))
                throw new ArgumentException("Symbol required", nameof(symbol));
            
            var brokerData = await _brokerApi.GetMarketDataAsync(symbol);
            return ConvertToMarketDataSnapshot(brokerData);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting market data from broker venue {Venue}: {Symbol}", 
                Name, symbol);
            throw;
        }
    }
    
    public override async Task<bool> IsHealthyAsync()
    {
        try
        {
            return await _brokerApi.IsHealthyAsync();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error checking health of broker venue {Venue}", Name);
            return false;
        }
    }
    
    private BrokerOrder ConvertToBrokerOrder(VenueOrderRequest request)
    {
        return new BrokerOrder(
            Symbol: request.Symbol,
            Side: ConvertOrderSide(request.Side),
            Type: ConvertOrderType(request.Type),
            Quantity: request.Quantity,
            LimitPrice: request.LimitPrice,
            StopPrice: request.StopPrice,
            TimeInForce: ConvertTimeInForce(request.TimeInForce),
            Metadata: request.Metadata
        );
    }
    
    private BrokerOrderSide ConvertOrderSide(OrderSide side)
    {
        return side switch
        {
            OrderSide.Buy => BrokerOrderSide.Buy,
            OrderSide.Sell => BrokerOrderSide.Sell,
            _ => throw new ArgumentException($"Unsupported order side: {side}")
        };
    }
    
    private BrokerOrderType ConvertOrderType(VenueOrderType type)
    {
        return type switch
        {
            VenueOrderType.Market => BrokerOrderType.Market,
            VenueOrderType.Limit => BrokerOrderType.Limit,
            VenueOrderType.StopMarket => BrokerOrderType.StopMarket,
            VenueOrderType.StopLimit => BrokerOrderType.StopLimit,
            VenueOrderType.MarketToLimit => BrokerOrderType.MarketToLimit,
            VenueOrderType.Pegged => BrokerOrderType.Pegged,
            _ => throw new ArgumentException($"Unsupported order type: {type}")
        };
    }
    
    private BrokerTimeInForce ConvertTimeInForce(TimeInForce tif)
    {
        return tif switch
        {
            TimeInForce.Day => BrokerTimeInForce.Day,
            TimeInForce.Gtc => BrokerTimeInForce.Gtc,
            TimeInForce.Ioc => BrokerTimeInForce.Ioc,
            TimeInForce.Fok => BrokerTimeInForce.Fok,
            _ => throw new ArgumentException($"Unsupported time in force: {tif}")
        };
    }
    
    private VenueOrderStatus ConvertToVenueOrderStatus(BrokerOrderStatus brokerStatus)
    {
        if (brokerStatus == null) return null;
        
        return new VenueOrderStatus(
            VenueOrderId: brokerStatus.OrderId,
            Symbol: brokerStatus.Symbol,
            Side: ConvertBrokerOrderSide(brokerStatus.Side),
            Type: ConvertBrokerOrderType(brokerStatus.Type),
            Quantity: brokerStatus.Quantity,
            FilledQuantity: brokerStatus.FilledQuantity,
            LimitPrice: brokerStatus.LimitPrice,
            StopPrice: brokerStatus.StopPrice,
            State: ConvertBrokerOrderState(brokerStatus.State),
            CreatedTime: brokerStatus.CreatedTime,
            FilledTime: brokerStatus.FilledTime,
            Fills: brokerStatus.Fills?.Select(ConvertToVenueOrderFill).ToList() ?? new List<VenueOrderFill>(),
            Metadata: brokerStatus.Metadata
        );
    }
    
    private OrderSide ConvertBrokerOrderSide(BrokerOrderSide side)
    {
        return side switch
        {
            BrokerOrderSide.Buy => OrderSide.Buy,
            BrokerOrderSide.Sell => OrderSide.Sell,
            _ => throw new ArgumentException($"Unsupported broker order side: {side}")
        };
    }
    
    private VenueOrderType ConvertBrokerOrderType(BrokerOrderType type)
    {
        return type switch
        {
            BrokerOrderType.Market => VenueOrderType.Market,
            BrokerOrderType.Limit => VenueOrderType.Limit,
            BrokerOrderType.StopMarket => VenueOrderType.StopMarket,
            BrokerOrderType.StopLimit => VenueOrderType.StopLimit,
            BrokerOrderType.MarketToLimit => VenueOrderType.MarketToLimit,
            BrokerOrderType.Pegged => VenueOrderType.Pegged,
            _ => throw new ArgumentException($"Unsupported broker order type: {type}")
        };
    }
    
    private VenueOrderState ConvertBrokerOrderState(BrokerOrderState state)
    {
        return state switch
        {
            BrokerOrderState.New => VenueOrderState.New,
            BrokerOrderState.Submitted => VenueOrderState.Submitted,
            BrokerOrderState.Accepted => VenueOrderState.Accepted,
            BrokerOrderState.PartiallyFilled => VenueOrderState.PartiallyFilled,
            BrokerOrderState.Filled => VenueOrderState.Filled,
            BrokerOrderState.Cancelled => VenueOrderState.Cancelled,
            BrokerOrderState.Rejected => VenueOrderState.Rejected,
            BrokerOrderState.Expired => VenueOrderState.Expired,
            BrokerOrderState.Suspended => VenueOrderState.Suspended,
            _ => throw new ArgumentException($"Unsupported broker order state: {state}")
        };
    }
    
    private VenueOrderFill ConvertToVenueOrderFill(BrokerOrderFill brokerFill)
    {
        if (brokerFill == null) return null;
        
        return new VenueOrderFill(
            VenueOrderId: brokerFill.OrderId,
            Symbol: brokerFill.Symbol,
            Quantity: brokerFill.Quantity,
            FillPrice: brokerFill.FillPrice,
            FillTime: brokerFill.FillTime,
            Commission: brokerFill.Commission,
            ExecutionId: brokerFill.ExecutionId,
            Metadata: brokerFill.Metadata
        );
    }
    
    private MarketDataSnapshot ConvertToMarketDataSnapshot(BrokerMarketData brokerData)
    {
        if (brokerData == null) return null;
        
        return new MarketDataSnapshot(
            Symbol: brokerData.Symbol,
            BidPrice: brokerData.BidPrice,
            BidSize: brokerData.BidSize,
            AskPrice: brokerData.AskPrice,
            AskSize: brokerData.AskSize,
            LastPrice: brokerData.LastPrice,
            LastSize: brokerData.LastSize,
            Volume: brokerData.Volume,
            Timestamp: brokerData.Timestamp,
            Metadata: brokerData.Metadata
        );
    }
    
    private BrokerOrderModification ConvertToBrokerModification(VenueOrderModification modification)
    {
        return new BrokerOrderModification(
            Quantity: modification.Quantity,
            LimitPrice: modification.LimitPrice,
            StopPrice: modification.StopPrice,
            TimeInForce: ConvertTimeInForce(modification.TimeInForce),
            Metadata: modification.Metadata
        );
    }
}

Venue Management System

Venue Manager

/// <summary>
/// Manages multiple execution venues
/// </summary>
public class VenueManager
{
    private readonly Dictionary<string, IExecutionVenue> _venues;
    private readonly ILogger<VenueManager> _logger;
    private readonly object _lock = new object();
    
    public VenueManager(ILogger<VenueManager> logger)
    {
        _venues = new Dictionary<string, IExecutionVenue>();
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }
    
    /// <summary>
    /// Add a new execution venue
    /// </summary>
    public void AddVenue(IExecutionVenue venue)
    {
        if (venue == null) throw new ArgumentNullException(nameof(venue));
        
        lock (_lock)
        {
            _venues[venue.Id] = venue;
        }
        
        _logger.LogInformation("Venue added: {VenueId} ({VenueName})", venue.Id, venue.Name);
    }
    
    /// <summary>
    /// Remove an execution venue
    /// </summary>
    public void RemoveVenue(string venueId)
    {
        if (string.IsNullOrEmpty(venueId)) throw new ArgumentException("Venue ID required", nameof(venueId));
        
        lock (_lock)
        {
            if (_venues.ContainsKey(venueId))
            {
                _venues.Remove(venueId);
            }
        }
        
        _logger.LogInformation("Venue removed: {VenueId}", venueId);
    }
    
    /// <summary>
    /// Get a specific venue by ID
    /// </summary>
    public IExecutionVenue GetVenue(string venueId)
    {
        if (string.IsNullOrEmpty(venueId)) return null;
        
        lock (_lock)
        {
            return _venues.ContainsKey(venueId) ? _venues[venueId] : null;
        }
    }
    
    /// <summary>
    /// Get all active venues
    /// </summary>
    public List<IExecutionVenue> GetActiveVenues()
    {
        lock (_lock)
        {
            return _venues.Values.Where(v => v.IsActive).ToList();
        }
    }
    
    /// <summary>
    /// Get venues by type
    /// </summary>
    public List<IExecutionVenue> GetVenuesByType(VenueType type)
    {
        lock (_lock)
        {
            return _venues.Values.Where(v => v.Type == type && v.IsActive).ToList();
        }
    }
    
    /// <summary>
    /// Update venue status
    /// </summary>
    public void UpdateVenueStatus(string venueId, bool isActive)
    {
        var venue = GetVenue(venueId);
        if (venue != null)
        {
            // Note: This would require the venue to have a mutable IsActive property
            // or we would need to replace the venue instance
            _logger.LogInformation("Venue {VenueId} status updated to {IsActive}", venueId, isActive);
        }
    }
    
    /// <summary>
    /// Check health of all venues
    /// </summary>
    public async Task<Dictionary<string, bool>> CheckVenueHealthAsync()
    {
        var healthStatus = new Dictionary<string, bool>();
        
        var venues = GetActiveVenues();
        var healthTasks = venues.Select(async venue =>
        {
            try
            {
                var isHealthy = await venue.IsHealthyAsync();
                return new { VenueId = venue.Id, IsHealthy = isHealthy };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error checking health of venue {VenueId}", venue.Id);
                return new { VenueId = venue.Id, IsHealthy = false };
            }
        }).ToList();
        
        var results = await Task.WhenAll(healthTasks);
        
        foreach (var result in results)
        {
            healthStatus[result.VenueId] = result.IsHealthy;
        }
        
        return healthStatus;
    }
}

Integration with OrderManager

Venue Integration in OrderManager

public partial class OrderManager : IOrderManager
{
    private readonly VenueManager _venueManager;
    private readonly Dictionary<string, string> _omsToVenueOrderIdMap;
    private readonly Dictionary<string, string> _venueToOmsOrderIdMap;
    
    // Initialize venue manager in constructor
    public OrderManager(
        IRiskManager riskManager,
        IPositionSizer positionSizer,
        ILogger<OrderManager> logger) : base(riskManager, positionSizer, logger)
    {
        _venueManager = new VenueManager(logger);
        _omsToVenueOrderIdMap = new Dictionary<string, string>();
        _venueToOmsOrderIdMap = new Dictionary<string, string>();
        
        // Initialize default venues
        InitializeDefaultVenues();
    }
    
    private void InitializeDefaultVenues()
    {
        // Add primary broker venue
        var primaryBrokerConfig = new VenueConfig(
            ApiKey: "primary-broker-key",
            ApiSecret: "primary-broker-secret",
            BaseUrl: "https://api.primary-broker.com",
            RateLimit: 100 // requests per minute
        );
        
        var primaryBrokerApi = new PrimaryBrokerApi(primaryBrokerConfig);
        var primaryVenue = new BrokerExecutionVenue(
            id: "primary-broker",
            name: "Primary Broker",
            description: "Primary execution broker",
            config: primaryBrokerConfig,
            brokerApi: primaryBrokerApi,
            logger: _logger
        );
        
        _venueManager.AddVenue(primaryVenue);
        
        // Add secondary broker venue
        var secondaryBrokerConfig = new VenueConfig(
            ApiKey: "secondary-broker-key",
            ApiSecret: "secondary-broker-secret",
            BaseUrl: "https://api.secondary-broker.com",
            RateLimit: 50 // requests per minute
        );
        
        var secondaryBrokerApi = new SecondaryBrokerApi(secondaryBrokerConfig);
        var secondaryVenue = new BrokerExecutionVenue(
            id: "secondary-broker",
            name: "Secondary Broker",
            description: "Backup execution broker",
            config: secondaryBrokerConfig,
            brokerApi: secondaryBrokerApi,
            logger: _logger
        );
        
        _venueManager.AddVenue(secondaryVenue);
    }
    
    // Enhanced order submission with venue routing
    public async Task<OrderResult> SubmitOrderAsync(OrderRequest request, StrategyContext context)
    {
        // Validate request parameters
        if (!request.IsValid(out var errors))
        {
            return new OrderResult(false, null, string.Join("; ", errors), null);
        }
        
        // Validate through risk management
        var riskDecision = await ValidateOrderAsync(request, context);
        if (!riskDecision.Allow)
        {
            _logger.LogWarning("Order rejected by risk management: {Reason}", riskDecision.RejectReason);
            return new OrderResult(false, null, $"Risk validation failed: {riskDecision.RejectReason}", null);
        }
        
        try
        {
            // Route order to appropriate venue
            var routingResult = await RouteOrderAsync(request, context);
            if (!routingResult.Success)
            {
                _logger.LogError("Order routing failed: {Message}", routingResult.Message);
                return new OrderResult(false, null, routingResult.Message, null);
            }
            
            // Submit to selected venue
            var venueOrderRequest = ConvertToVenueOrderRequest(request);
            var venueResult = await routingResult.SelectedVenue.SubmitOrderAsync(venueOrderRequest);
            
            if (venueResult.Success)
            {
                // Map order IDs
                lock (_lock)
                {
                    _omsToVenueOrderIdMap[venueResult.VenueOrderId] = venueResult.VenueOrderId;
                    _venueToOmsOrderIdMap[venueResult.VenueOrderId] = venueResult.VenueOrderId;
                }
                
                // Create order status
                var orderStatus = ConvertToOrderStatus(venueResult.Status, request);
                
                // Store order status
                lock (_lock)
                {
                    _orders[venueResult.VenueOrderId] = orderStatus; // Using venue order ID as key
                }
                
                _logger.LogInformation("Order {OrderId} submitted to venue {Venue}", 
                    venueResult.VenueOrderId, routingResult.SelectedVenue.Name);
                
                return new OrderResult(true, venueResult.VenueOrderId, "Order submitted successfully", orderStatus);
            }
            else
            {
                _logger.LogError("Order submission failed at venue {Venue}: {Message}", 
                    routingResult.SelectedVenue.Name, venueResult.Message);
                
                return new OrderResult(false, null, $"Venue submission failed: {venueResult.Message}", null);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error submitting order for {Symbol}", request.Symbol);
            return new OrderResult(false, null, $"Error submitting order: {ex.Message}", null);
        }
    }
    
    // Enhanced order cancellation with venue integration
    public async Task<bool> CancelOrderAsync(string orderId)
    {
        if (string.IsNullOrEmpty(orderId)) throw new ArgumentException("Order ID required", nameof(orderId));
        
        try
        {
            // Get venue order ID
            string venueOrderId;
            lock (_lock)
            {
                if (!_omsToVenueOrderIdMap.ContainsKey(orderId))
                {
                    _logger.LogWarning("Cannot cancel order {OrderId} - not found", orderId);
                    return false;
                }
                
                venueOrderId = _omsToVenueOrderIdMap[orderId];
            }
            
            // Get venue for this order
            var venue = await GetVenueForOrderAsync(orderId);
            if (venue == null)
            {
                _logger.LogWarning("Cannot cancel order {OrderId} - venue not found", orderId);
                return false;
            }
            
            // Cancel at venue
            var result = await venue.CancelOrderAsync(venueOrderId);
            
            if (result)
            {
                // Update order status
                lock (_lock)
                {
                    if (_orders.ContainsKey(orderId))
                    {
                        var order = _orders[orderId];
                        var updatedOrder = order with { State = OrderState.Cancelled, FilledTime = DateTime.UtcNow };
                        _orders[orderId] = updatedOrder;
                    }
                }
                
                _logger.LogInformation("Order {OrderId} cancelled successfully at venue {Venue}", 
                    orderId, venue.Name);
            }
            else
            {
                _logger.LogWarning("Order {OrderId} cancellation failed at venue {Venue}", 
                    orderId, venue.Name);
            }
            
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error cancelling order {OrderId}", orderId);
            return false;
        }
    }
    
    private async Task<IExecutionVenue> GetVenueForOrderAsync(string orderId)
    {
        // In a real implementation, this would lookup which venue was used for the order
        // For now, we'll return the primary venue
        return _venueManager.GetVenue("primary-broker");
    }
    
    private VenueOrderRequest ConvertToVenueOrderRequest(OrderRequest request)
    {
        return new VenueOrderRequest(
            Symbol: request.Symbol,
            Side: ConvertOrderSide(request.Side),
            Type: ConvertOrderType(request.Type),
            Quantity: request.Quantity,
            LimitPrice: request.LimitPrice,
            StopPrice: request.StopPrice,
            TimeInForce: request.TimeInForce,
            OmsOrderId: Guid.NewGuid().ToString(), // This would be the actual OMS order ID
            Metadata: new Dictionary<string, object>
            {
                ["Algorithm"] = request.Algorithm,
                ["AlgorithmParameters"] = request.AlgorithmParameters
            }
        );
    }
    
    private OrderStatus ConvertToOrderStatus(VenueOrderStatus venueStatus, OrderRequest request)
    {
        if (venueStatus == null) return null;
        
        return new OrderStatus(
            OrderId: venueStatus.VenueOrderId,
            Symbol: venueStatus.Symbol,
            Side: ConvertVenueOrderSide(venueStatus.Side),
            Type: ConvertVenueOrderType(venueStatus.Type),
            Quantity: venueStatus.Quantity,
            FilledQuantity: venueStatus.FilledQuantity,
            LimitPrice: venueStatus.LimitPrice,
            StopPrice: venueStatus.StopPrice,
            State: ConvertVenueOrderState(venueStatus.State),
            CreatedTime: venueStatus.CreatedTime,
            FilledTime: venueStatus.FilledTime,
            Fills: venueStatus.Fills?.Select(ConvertToOrderFill).ToList() ?? new List<OrderFill>()
        );
    }
    
    private OrderSide ConvertVenueOrderSide(VenueOrderSide side)
    {
        return side switch
        {
            VenueOrderSide.Buy => OrderSide.Buy,
            VenueOrderSide.Sell => OrderSide.Sell,
            _ => throw new ArgumentException($"Unsupported venue order side: {side}")
        };
    }
    
    private OrderType ConvertVenueOrderType(VenueOrderType type)
    {
        return type switch
        {
            VenueOrderType.Market => OrderType.Market,
            VenueOrderType.Limit => OrderType.Limit,
            VenueOrderType.StopMarket => OrderType.StopMarket,
            VenueOrderType.StopLimit => OrderType.StopLimit,
            _ => throw new ArgumentException($"Unsupported venue order type: {type}")
        };
    }
    
    private OrderState ConvertVenueOrderState(VenueOrderState state)
    {
        return state switch
        {
            VenueOrderState.New => OrderState.New,
            VenueOrderState.Submitted => OrderState.Submitted,
            VenueOrderState.Accepted => OrderState.Accepted,
            VenueOrderState.PartiallyFilled => OrderState.PartiallyFilled,
            VenueOrderState.Filled => OrderState.Filled,
            VenueOrderState.Cancelled => OrderState.Cancelled,
            VenueOrderState.Rejected => OrderState.Rejected,
            VenueOrderState.Expired => OrderState.Expired,
            VenueOrderState.Suspended => OrderState.Suspended,
            _ => throw new ArgumentException($"Unsupported venue order state: {state}")
        };
    }
    
    private OrderFill ConvertToOrderFill(VenueOrderFill venueFill)
    {
        if (venueFill == null) return null;
        
        return new OrderFill(
            OrderId: venueFill.VenueOrderId,
            Symbol: venueFill.Symbol,
            Quantity: venueFill.Quantity,
            FillPrice: venueFill.FillPrice,
            FillTime: venueFill.FillTime,
            Commission: venueFill.Commission,
            ExecutionId: venueFill.ExecutionId
        );
    }
}

Venue Configuration

Venue Configuration Model

/// <summary>
/// Configuration for execution venues
/// </summary>
public record VenueConfig(
    string ApiKey,
    string ApiSecret,
    string BaseUrl,
    int RateLimit, // Requests per minute
    Dictionary<string, object> AdditionalSettings = null
);

Testing Considerations

Unit Tests for Venue Management

  1. Venue Addition/Removal: Test adding and removing venues
  2. Venue Selection: Test getting venues by ID and type
  3. Health Checks: Test venue health monitoring
  4. Order Mapping: Test mapping between OMS and venue order IDs

Integration Tests

  1. Venue Integration: Test integration with different venue types
  2. Order Lifecycle: Test complete order lifecycle across venues
  3. Error Handling: Test error handling for venue connectivity issues
  4. Performance: Test performance with multiple venues

Performance Considerations

Connection Management

/// <summary>
/// Connection pool for venue connections
/// </summary>
public class VenueConnectionPool
{
    private readonly Dictionary<string, SemaphoreSlim> _rateLimiters;
    private readonly Dictionary<string, HttpClient> _httpClients;
    
    public VenueConnectionPool()
    {
        _rateLimiters = new Dictionary<string, SemaphoreSlim>();
        _httpClients = new Dictionary<string, HttpClient>();
    }
    
    public SemaphoreSlim GetRateLimiter(string venueId, int maxRequestsPerMinute)
    {
        if (!_rateLimiters.ContainsKey(venueId))
        {
            _rateLimiters[venueId] = new SemaphoreSlim(maxRequestsPerMinute, maxRequestsPerMinute);
        }
        
        return _rateLimiters[venueId];
    }
    
    public HttpClient GetHttpClient(string venueId, string baseUrl)
    {
        if (!_httpClients.ContainsKey(venueId))
        {
            var handler = new HttpClientHandler
            {
                AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
            };
            
            var client = new HttpClient(handler)
            {
                BaseAddress = new Uri(baseUrl),
                Timeout = TimeSpan.FromSeconds(30)
            };
            
            client.DefaultRequestHeaders.Add("User-Agent", "NT8-OMS/1.0");
            
            _httpClients[venueId] = client;
        }
        
        return _httpClients[venueId];
    }
}

Caching and Rate Limiting

/// <summary>
/// Rate limiter for venue API calls
/// </summary>
public class ApiRateLimiter
{
    private readonly SemaphoreSlim _semaphore;
    private readonly TimeSpan _timeWindow;
    private readonly int _maxRequests;
    private readonly Queue<DateTime> _requestTimes;
    
    public ApiRateLimiter(int maxRequests, TimeSpan timeWindow)
    {
        _semaphore = new SemaphoreSlim(maxRequests, maxRequests);
        _timeWindow = timeWindow;
        _maxRequests = maxRequests;
        _requestTimes = new Queue<DateTime>();
    }
    
    public async Task<IDisposable> AcquireAsync(CancellationToken cancellationToken = default)
    {
        await _semaphore.WaitAsync(cancellationToken);
        
        lock (_requestTimes)
        {
            var now = DateTime.UtcNow;
            _requestTimes.Enqueue(now);
            
            // Remove old requests outside the time window
            while (_requestTimes.Count > 0 && _requestTimes.Peek() < now.Subtract(_timeWindow))
            {
                _requestTimes.Dequeue();
            }
        }
        
        return new RateLimitLease(_semaphore);
    }
    
    private class RateLimitLease : IDisposable
    {
        private readonly SemaphoreSlim _semaphore;
        
        public RateLimitLease(SemaphoreSlim semaphore)
        {
            _semaphore = semaphore;
        }
        
        public void Dispose()
        {
            _semaphore.Release();
        }
    }
}

Monitoring and Alerting

Venue Metrics

/// <summary>
/// Metrics for execution venues
/// </summary>
public class VenueMetrics
{
    public string VenueId { get; set; }
    public string VenueName { get; set; }
    public int TotalOrders { get; set; }
    public int SuccessfulOrders { get; set; }
    public int FailedOrders { get; set; }
    public double SuccessRate => TotalOrders > 0 ? (double)SuccessfulOrders / TotalOrders : 0;
    public double AverageLatencyMs { get; set; }
    public double AverageSlippage { get; set; }
    public DateTime LastUpdated { get; set; }
}

Future Enhancements

  1. Cross-Venue Order Management: Coordinate orders across multiple venues
  2. Smart Order Routing: Advanced routing based on real-time venue performance
  3. Venue Failover: Automatic failover to backup venues when primary venues fail
  4. Regulatory Compliance: Ensure venue selection complies with regulatory requirements
  5. Cost Analysis: Detailed cost analysis including rebates and fees across venues
  6. Liquidity Aggregation: Aggregate liquidity from multiple venues for better execution