Phase 0 completion: NT8 SDK core framework with risk management and position sizing
Some checks failed
Build and Test / build (push) Has been cancelled

This commit is contained in:
Billy Valentine
2025-09-09 17:06:37 -04:00
parent 97e5050d3e
commit 92f3732b3d
109 changed files with 38593 additions and 380 deletions

View File

@@ -0,0 +1,708 @@
# OrderManager Class Implementation Design
## Overview
The OrderManager class is the primary implementation of the IOrderManager interface, providing core order management functionality, integration with risk management, and support for algorithmic execution strategies.
## Class Structure
```csharp
using NT8.Core.Common.Models;
using NT8.Core.Risk;
using NT8.Core.Sizing;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace NT8.Core.Orders
{
/// <summary>
/// Order manager implementation with smart routing and algorithmic execution
/// </summary>
public class OrderManager : IOrderManager
{
private readonly IRiskManager _riskManager;
private readonly IPositionSizer _positionSizer;
private readonly ILogger<OrderManager> _logger;
private readonly object _lock = new object();
// Configuration
private RoutingConfig _routingConfig;
private AlgorithmParameters _algorithmParameters;
// State
private readonly Dictionary<string, OrderStatus> _orders;
private readonly Dictionary<string, ExecutionVenue> _venues;
private readonly RoutingMetrics _routingMetrics;
private readonly OmsMetrics _omsMetrics;
public OrderManager(
IRiskManager riskManager,
IPositionSizer positionSizer,
ILogger<OrderManager> logger)
{
_riskManager = riskManager ?? throw new ArgumentNullException(nameof(riskManager));
_positionSizer = positionSizer ?? throw new ArgumentNullException(nameof(positionSizer));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_orders = new Dictionary<string, OrderStatus>();
_venues = new Dictionary<string, ExecutionVenue>();
_routingMetrics = new RoutingMetrics(new Dictionary<string, VenueMetrics>(), 0, 0.0, DateTime.UtcNow);
_omsMetrics = new OmsMetrics(0, 0, 0.0, 0.0, 0.0, 0, DateTime.UtcNow);
InitializeDefaultConfig();
InitializeVenues();
}
private void InitializeDefaultConfig()
{
_routingConfig = new RoutingConfig(
SmartRoutingEnabled: true,
DefaultVenue: "Primary",
VenuePreferences: new Dictionary<string, double> { ["Primary"] = 1.0, ["Secondary"] = 0.8 },
MaxSlippagePercent: 0.5,
MaxRoutingTime: TimeSpan.FromSeconds(30),
RouteByCost: true,
RouteBySpeed: true,
RouteByReliability: true
);
_algorithmParameters = new AlgorithmParameters(
new TwapConfig(TimeSpan.FromMinutes(15), 30, true),
new VwapConfig(0.1, true),
new IcebergConfig(0.1, true)
);
}
private void InitializeVenues()
{
_venues.Add("Primary", new ExecutionVenue(
"Primary", "Primary execution venue", true, 1.0, 1.0, 0.99));
_venues.Add("Secondary", new ExecutionVenue(
"Secondary", "Backup execution venue", true, 1.2, 0.9, 0.95));
}
}
}
```
## Core Order Management Implementation
### Order Submission
```csharp
public async Task<OrderResult> SubmitOrderAsync(OrderRequest request, StrategyContext context)
{
if (request == null) throw new ArgumentNullException(nameof(request));
if (context == null) throw new ArgumentNullException(nameof(context));
try
{
_logger.LogInformation("Submitting order for {Symbol} {Side} {Quantity}",
request.Symbol, request.Side, request.Quantity);
// 1. Validate order 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, riskDecision.RejectReason, null);
}
// 2. Route order based on smart routing logic
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);
}
// 3. Submit to selected venue (simulated)
var orderId = Guid.NewGuid().ToString();
var orderStatus = new OrderStatus(
orderId, request.Symbol, request.Side, request.Type, request.Quantity, 0,
request.LimitPrice, request.StopPrice, OrderState.Submitted, DateTime.UtcNow, null,
new List<OrderFill>());
lock (_lock)
{
_orders[orderId] = orderStatus;
UpdateOmsMetrics();
}
_logger.LogInformation("Order {OrderId} submitted to {Venue}", orderId, routingResult.SelectedVenue.Name);
return new OrderResult(true, orderId, "Order submitted successfully", orderStatus);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error submitting order for {Symbol}", request.Symbol);
return new OrderResult(false, null, $"Error submitting order: {ex.Message}", null);
}
}
```
### Order Cancellation
```csharp
public async Task<bool> CancelOrderAsync(string orderId)
{
if (string.IsNullOrEmpty(orderId)) throw new ArgumentException("Order ID required", nameof(orderId));
try
{
lock (_lock)
{
if (!_orders.ContainsKey(orderId))
{
_logger.LogWarning("Cannot cancel order {OrderId} - not found", orderId);
return false;
}
var order = _orders[orderId];
if (order.State == OrderState.Filled || order.State == OrderState.Cancelled)
{
_logger.LogWarning("Cannot cancel order {OrderId} - already {State}", orderId, order.State);
return false;
}
// Update order state to cancelled
var updatedOrder = order with { State = OrderState.Cancelled, FilledTime = DateTime.UtcNow };
_orders[orderId] = updatedOrder;
UpdateOmsMetrics();
}
_logger.LogInformation("Order {OrderId} cancelled successfully", orderId);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error cancelling order {OrderId}", orderId);
return false;
}
}
```
## Risk Integration Implementation
```csharp
public async Task<RiskDecision> ValidateOrderAsync(OrderRequest request, StrategyContext context)
{
if (request == null) throw new ArgumentNullException(nameof(request));
if (context == null) throw new ArgumentNullException(nameof(context));
// Convert OrderRequest to StrategyIntent for risk validation
var intent = new StrategyIntent(
request.Symbol,
request.Side,
ConvertOrderType(request.Type),
(double?)request.LimitPrice,
GetStopTicks(request),
null, // TargetTicks
1.0, // Confidence
"OMS Order Submission",
new Dictionary<string, object>()
);
// Create a mock risk config for validation
var riskConfig = new RiskConfig(1000, 200, 10, true);
return _riskManager.ValidateOrder(intent, context, riskConfig);
}
private OrderType ConvertOrderType(NT8.Core.Orders.OrderType orderType)
{
return orderType switch
{
NT8.Core.Orders.OrderType.Market => OrderType.Market,
NT8.Core.Orders.OrderType.Limit => OrderType.Limit,
NT8.Core.Orders.OrderType.StopMarket => OrderType.StopMarket,
NT8.Core.Orders.OrderType.StopLimit => OrderType.StopLimit,
_ => OrderType.Market
};
}
private int GetStopTicks(OrderRequest request)
{
// Simplified stop ticks calculation
return 10;
}
```
## Smart Order Routing Implementation
```csharp
public async Task<RoutingResult> RouteOrderAsync(OrderRequest request, StrategyContext context)
{
if (request == null) throw new ArgumentNullException(nameof(request));
if (context == null) throw new ArgumentNullException(nameof(context));
if (!_routingConfig.SmartRoutingEnabled)
{
var defaultVenue = _venues[_routingConfig.DefaultVenue];
return new RoutingResult(true, null, defaultVenue, "Routing disabled, using default venue",
new Dictionary<string, object> { ["venue"] = defaultVenue.Name });
}
// Select best venue based on configuration
var selectedVenue = SelectBestVenue(request, context);
// Update routing metrics
UpdateRoutingMetrics(selectedVenue);
return new RoutingResult(true, null, selectedVenue, "Order routed successfully",
new Dictionary<string, object>
{
["venue"] = selectedVenue.Name,
["cost_factor"] = selectedVenue.CostFactor,
["speed_factor"] = selectedVenue.SpeedFactor
});
}
private ExecutionVenue SelectBestVenue(OrderRequest request, StrategyContext context)
{
ExecutionVenue bestVenue = null;
double bestScore = double.MinValue;
foreach (var venue in _venues.Values)
{
if (!venue.IsActive) continue;
double score = 0;
// Factor in venue preferences
if (_routingConfig.VenuePreferences.ContainsKey(venue.Name))
{
score += _routingConfig.VenuePreferences[venue.Name] * 100;
}
// Factor in cost if enabled
if (_routingConfig.RouteByCost)
{
score -= venue.CostFactor * 50; // Lower cost is better
}
// Factor in speed if enabled
if (_routingConfig.RouteBySpeed)
{
score += venue.SpeedFactor * 30; // Higher speed is better
}
// Factor in reliability
if (_routingConfig.RouteByReliability)
{
score += venue.ReliabilityFactor * 20; // Higher reliability is better
}
if (score > bestScore)
{
bestScore = score;
bestVenue = venue;
}
return bestVenue ?? _venues[_routingConfig.DefaultVenue];
}
private void UpdateRoutingMetrics(ExecutionVenue venue)
{
lock (_lock)
{
var venueMetrics = _routingMetrics.VenuePerformance.ContainsKey(venue.Name) ?
_routingMetrics.VenuePerformance[venue.Name] :
new VenueMetrics(venue.Name, 0, 0.0, 0.0, 0.0, 0);
var updatedMetrics = venueMetrics with
{
OrdersRouted = venueMetrics.OrdersRouted + 1
};
_routingMetrics.VenuePerformance[venue.Name] = updatedMetrics;
_routingMetrics.TotalRoutedOrders++;
_routingMetrics.LastUpdated = DateTime.UtcNow;
}
}
```
## Algorithmic Execution Support
### TWAP Algorithm Integration
```csharp
public async Task<OrderResult> ExecuteTwapAsync(TwapParameters parameters, StrategyContext context)
{
if (parameters == null) throw new ArgumentNullException(nameof(parameters));
if (context == null) throw new ArgumentNullException(nameof(context));
_logger.LogInformation("Executing TWAP order for {Symbol} {Side} {Quantity} over {Duration}",
parameters.Symbol, parameters.Side, parameters.TotalQuantity, parameters.Duration);
// Create a parent order for tracking
var parentOrderId = Guid.NewGuid().ToString();
// Calculate slice parameters
var sliceCount = (int)(parameters.Duration.TotalSeconds / parameters.IntervalSeconds);
var sliceQuantity = parameters.TotalQuantity / sliceCount;
// Execute slices
for (int i = 0; i < sliceCount; i++)
{
// Create slice order
var sliceRequest = new OrderRequest(
parameters.Symbol,
parameters.Side,
OrderType.Market, // Simplified to market orders
sliceQuantity,
parameters.LimitPrice,
null, // StopPrice
TimeInForce.Day,
null, // No algorithm for slices
new Dictionary<string, object>()
);
// Submit slice order
var result = await SubmitOrderAsync(sliceRequest, context);
if (!result.Success)
{
_logger.LogWarning("TWAP slice {Slice}/{Total} failed: {Message}",
i + 1, sliceCount, result.Message);
}
else
{
_logger.LogInformation("TWAP slice {Slice}/{Total} submitted: {OrderId}",
i + 1, sliceCount, result.OrderId);
}
// Wait for next interval (except for last slice)
if (i < sliceCount - 1)
{
await Task.Delay(TimeSpan.FromSeconds(parameters.IntervalSeconds));
}
}
return new OrderResult(true, parentOrderId, "TWAP execution completed", null);
}
```
### VWAP Algorithm Integration
```csharp
public async Task<OrderResult> ExecuteVwapAsync(VwapParameters parameters, StrategyContext context)
{
if (parameters == null) throw new ArgumentNullException(nameof(parameters));
if (context == null) throw new ArgumentNullException(nameof(context));
_logger.LogInformation("Executing VWAP order for {Symbol} {Side} {Quantity} from {Start} to {End}",
parameters.Symbol, parameters.Side, parameters.TotalQuantity, parameters.StartTime, parameters.EndTime);
// Create a parent order for tracking
var parentOrderId = Guid.NewGuid().ToString();
// Simplified VWAP implementation - in a real system, this would:
// 1. Monitor market volume throughout the execution period
// 2. Calculate participation rate based on target participation
// 3. Execute orders in proportion to volume
// For now, we'll execute the order as a single market order
var request = new OrderRequest(
parameters.Symbol,
parameters.Side,
OrderType.Market,
parameters.TotalQuantity,
parameters.LimitPrice,
null, // StopPrice
TimeInForce.Day,
null, // No algorithm for this simplified version
new Dictionary<string, object>()
);
var result = await SubmitOrderAsync(request, context);
return new OrderResult(result.Success, parentOrderId,
result.Success ? "VWAP execution completed" : $"VWAP execution failed: {result.Message}",
result.Status);
}
```
### Iceberg Algorithm Integration
```csharp
public async Task<OrderResult> ExecuteIcebergAsync(IcebergParameters parameters, StrategyContext context)
{
if (parameters == null) throw new ArgumentNullException(nameof(parameters));
if (context == null) throw new ArgumentNullException(nameof(context));
_logger.LogInformation("Executing Iceberg order for {Symbol} {Side} {TotalQuantity} (visible: {VisibleQuantity})",
parameters.Symbol, parameters.Side, parameters.TotalQuantity, parameters.VisibleQuantity);
// Create a parent order for tracking
var parentOrderId = Guid.NewGuid().ToString();
var remainingQuantity = parameters.TotalQuantity;
while (remainingQuantity > 0)
{
// Determine visible quantity for this slice
var visibleQuantity = Math.Min(parameters.VisibleQuantity, remainingQuantity);
// Create slice order
var sliceRequest = new OrderRequest(
parameters.Symbol,
parameters.Side,
parameters.LimitPrice.HasValue ? OrderType.Limit : OrderType.Market,
visibleQuantity,
parameters.LimitPrice,
null, // StopPrice
TimeInForce.Day,
null, // No algorithm for slices
new Dictionary<string, object>()
);
// Submit slice order
var result = await SubmitOrderAsync(sliceRequest, context);
if (!result.Success)
{
_logger.LogWarning("Iceberg slice failed with {Remaining} qty remaining: {Message}",
remainingQuantity, result.Message);
break;
}
// Update remaining quantity
remainingQuantity -= visibleQuantity;
_logger.LogInformation("Iceberg slice submitted, {Remaining} qty remaining", remainingQuantity);
// In a real implementation, we would wait for the order to fill
// before submitting the next slice. For this design, we'll add a delay.
if (remainingQuantity > 0)
{
await Task.Delay(TimeSpan.FromSeconds(5)); // Simulate time between slices
}
return new OrderResult(true, parentOrderId, "Iceberg execution completed", null);
}
```
## Configuration Management
```csharp
public void UpdateRoutingConfig(RoutingConfig config)
{
if (config == null) throw new ArgumentNullException(nameof(config));
lock (_lock)
{
_routingConfig = config;
}
_logger.LogInformation("Routing configuration updated");
}
public RoutingConfig GetRoutingConfig()
{
lock (_lock)
{
return _routingConfig;
}
}
public void UpdateAlgorithmParameters(AlgorithmParameters parameters)
{
if (parameters == null) throw new ArgumentNullException(nameof(parameters));
lock (_lock)
{
_algorithmParameters = parameters;
}
_logger.LogInformation("Algorithm parameters updated");
}
public AlgorithmParameters GetAlgorithmParameters()
{
lock (_lock)
{
return _algorithmParameters;
}
}
```
## Metrics and Monitoring
```csharp
private void UpdateOmsMetrics()
{
lock (_lock)
{
var activeOrders = 0;
var filledOrders = 0;
var failedOrders = 0;
var totalQuantity = 0;
foreach (var order in _orders.Values)
{
switch (order.State)
{
case OrderState.Submitted:
case OrderState.Accepted:
case OrderState.PartiallyFilled:
activeOrders++;
break;
case OrderState.Filled:
filledOrders++;
break;
case OrderState.Rejected:
case OrderState.Expired:
case OrderState.Cancelled:
failedOrders++;
break;
}
totalQuantity += order.Quantity;
}
var totalOrders = _orders.Count;
var fillRate = totalOrders > 0 ? (double)filledOrders / totalOrders : 0.0;
_omsMetrics = _omsMetrics with
{
TotalOrders = totalOrders,
ActiveOrders = activeOrders,
FailedOrders = failedOrders,
FillRate = fillRate,
TotalValueTraded = totalQuantity, // Simplified
LastUpdated = DateTime.UtcNow
};
}
}
public OmsMetrics GetMetrics()
{
lock (_lock)
{
return _omsMetrics;
}
}
public RoutingMetrics GetRoutingMetrics()
{
lock (_lock)
{
return _routingMetrics;
}
}
public bool IsHealthy()
{
// Simple health check - in a real implementation, this would check:
// - Connection to execution venues
// - Risk management system availability
// - Position sizing system availability
// - Internal state consistency
return true;
}
```
## Order Status Management
```csharp
public async Task<OrderStatus> GetOrderStatusAsync(string orderId)
{
if (string.IsNullOrEmpty(orderId)) throw new ArgumentException("Order ID required", nameof(orderId));
lock (_lock)
{
return _orders.ContainsKey(orderId) ? _orders[orderId] : null;
}
}
public async Task<List<OrderStatus>> GetOrdersBySymbolAsync(string symbol)
{
if (string.IsNullOrEmpty(symbol)) throw new ArgumentException("Symbol required", nameof(symbol));
var result = new List<OrderStatus>();
lock (_lock)
{
foreach (var order in _orders.Values)
{
if (order.Symbol.Equals(symbol, StringComparison.OrdinalIgnoreCase))
{
result.Add(order);
}
}
}
return result;
}
public async Task<List<OrderStatus>> GetActiveOrdersAsync()
{
var result = new List<OrderStatus>();
lock (_lock)
{
foreach (var order in _orders.Values)
{
if (order.State == OrderState.Submitted ||
order.State == OrderState.Accepted ||
order.State == OrderState.PartiallyFilled)
{
result.Add(order);
}
}
}
return result;
}
```
## Error Handling and Validation
The OrderManager implements comprehensive error handling:
1. **Input Validation**: All public methods validate their parameters
2. **Exception Handling**: Try-catch blocks around critical operations
3. **Logging**: Detailed logging of all operations and errors
4. **Graceful Degradation**: When possible, the system continues operating even when some components fail
## Thread Safety
The OrderManager uses a lock-based approach to ensure thread safety:
1. **State Protection**: All shared state is protected by a single lock
2. **Atomic Operations**: Complex state updates are performed atomically
3. **Immutable Data**: Where possible, immutable data structures are used
## Integration Points
### Risk Management
- All orders pass through the IRiskManager.ValidateOrder method
- Risk decisions are respected before order submission
### Position Sizing
- Future enhancement could integrate with IPositionSizer for dynamic quantity adjustments
### Execution Venues
- Orders are routed to configured execution venues
- Routing decisions are based on configurable criteria
## Testing Considerations
The OrderManager is designed to be testable:
1. **Dependency Injection**: All dependencies are injected through the constructor
2. **Interface-Based**: Depends on interfaces rather than concrete implementations
3. **State Access**: Provides methods to access internal state for verification
4. **Configuration**: All behavior can be controlled through configuration
## Performance Considerations
1. **Lock Contention**: The single lock could become a bottleneck under high load
2. **Memory Usage**: Order state is maintained in memory
3. **Latency**: Order routing adds minimal latency
4. **Scalability**: Design supports horizontal scaling through instance isolation