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

2703 lines
90 KiB
Markdown

# Unit Test Plan for OMS Components
## Overview
This document outlines a comprehensive plan for writing unit tests for all Order Management System (OMS) components, ensuring proper functionality, reliability, and maintainability of the system.
## Test Strategy
### Testing Principles
1. **Comprehensive Coverage**: Aim for >90% code coverage for all critical components
2. **Isolation**: Each test should be independent and not rely on external state
3. **Repeatability**: Tests should produce consistent results across runs
4. **Speed**: Tests should execute quickly to enable rapid development cycles
5. **Readability**: Tests should be clear and self-documenting
6. **Maintainability**: Tests should be easy to update when implementation changes
### Testing Framework
- **Framework**: xUnit.net
- **Mocking**: Moq
- **Assertion Library**: FluentAssertions
- **Test Runner**: dotnet test
### Test Categories
1. **Unit Tests**: Test individual components in isolation
2. **Integration Tests**: Test interactions between components
3. **Performance Tests**: Test system performance under load
4. **Regression Tests**: Ensure previously fixed bugs don't reappear
## Component Test Plans
### 1. OrderManager Tests
#### Core Functionality Tests
```csharp
/// <summary>
/// Tests for OrderManager core functionality
/// </summary>
public class OrderManagerTests
{
private Mock<IRiskManager> _mockRiskManager;
private Mock<IPositionSizer> _mockPositionSizer;
private Mock<ILogger<OrderManager>> _mockLogger;
private Mock<RoutingConfigurationManager> _mockConfigManager;
private Mock<RoutingMetricsCollector> _mockMetricsCollector;
private Mock<TwapExecutor> _mockTwapExecutor;
private Mock<VwapExecutor> _mockVwapExecutor;
private Mock<IcebergExecutor> _mockIcebergExecutor;
private Mock<AlgorithmParameterProvider> _mockParameterProvider;
private Mock<RateLimiter> _mockRateLimiter;
private Mock<ValueLimiter> _mockValueLimiter;
private Mock<CircuitBreaker> _mockCircuitBreaker;
private OrderManager _orderManager;
[Fact]
public void Constructor_WithValidDependencies_ShouldInitializeSuccessfully()
{
// Arrange
SetupMocks();
// Act
var orderManager = new OrderManager(
_mockRiskManager.Object,
_mockPositionSizer.Object,
_mockLogger.Object,
_mockConfigManager.Object,
_mockMetricsCollector.Object,
_mockTwapExecutor.Object,
_mockVwapExecutor.Object,
_mockIcebergExecutor.Object,
_mockParameterProvider.Object,
_mockRateLimiter.Object,
_mockValueLimiter.Object,
_mockCircuitBreaker.Object);
// Assert
orderManager.Should().NotBeNull();
}
[Fact]
public async Task SubmitOrderAsync_WithValidMarketOrder_ShouldSubmitSuccessfully()
{
// Arrange
SetupMocks();
_orderManager = CreateOrderManager();
var request = new OrderRequest(
Symbol: "ES",
Side: OrderSide.Buy,
Type: OrderType.Market,
Quantity: 1,
LimitPrice: null,
StopPrice: null,
TimeInForce: TimeInForce.Day,
Algorithm: null,
AlgorithmParameters: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var riskDecision = new RiskDecision(
Allow: true,
RejectReason: null,
ModifiedIntent: null,
RiskLevel: RiskLevel.Low,
RiskMetrics: new Dictionary<string, object>()
);
_mockRiskManager.Setup(rm => rm.ValidateOrder(It.IsAny<StrategyIntent>(), It.IsAny<StrategyContext>(), It.IsAny<RiskConfig>()))
.Returns(riskDecision);
// Act
var result = await _orderManager.SubmitOrderAsync(request, context);
// Assert
result.Should().NotBeNull();
result.Success.Should().BeTrue();
result.OrderId.Should().NotBeNullOrEmpty();
result.Message.Should().Contain("submitted successfully");
}
[Fact]
public async Task SubmitOrderAsync_WithRiskRejection_ShouldRejectOrder()
{
// Arrange
SetupMocks();
_orderManager = CreateOrderManager();
var request = new OrderRequest(
Symbol: "ES",
Side: OrderSide.Buy,
Type: OrderType.Market,
Quantity: 1,
LimitPrice: null,
StopPrice: null,
TimeInForce: TimeInForce.Day,
Algorithm: null,
AlgorithmParameters: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var riskDecision = new RiskDecision(
Allow: false,
RejectReason: "Daily loss limit exceeded",
ModifiedIntent: null,
RiskLevel: RiskLevel.Critical,
RiskMetrics: new Dictionary<string, object>()
);
_mockRiskManager.Setup(rm => rm.ValidateOrder(It.IsAny<StrategyIntent>(), It.IsAny<StrategyContext>(), It.IsAny<RiskConfig>()))
.Returns(riskDecision);
// Act
var result = await _orderManager.SubmitOrderAsync(request, context);
// Assert
result.Should().NotBeNull();
result.Success.Should().BeFalse();
result.Message.Should().Contain("Risk validation failed");
result.Message.Should().Contain("Daily loss limit exceeded");
}
[Fact]
public async Task SubmitOrderAsync_WithInvalidParameters_ShouldReturnValidationError()
{
// Arrange
SetupMocks();
_orderManager = CreateOrderManager();
var request = new OrderRequest(
Symbol: "", // Invalid - empty symbol
Side: OrderSide.Buy,
Type: OrderType.Market,
Quantity: 0, // Invalid - zero quantity
LimitPrice: null,
StopPrice: null,
TimeInForce: TimeInForce.Day,
Algorithm: null,
AlgorithmParameters: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
// Act
var result = await _orderManager.SubmitOrderAsync(request, context);
// Assert
result.Should().NotBeNull();
result.Success.Should().BeFalse();
result.Message.Should().Contain("validation");
}
[Fact]
public async Task CancelOrderAsync_WithValidOrderId_ShouldCancelSuccessfully()
{
// Arrange
SetupMocks();
_orderManager = CreateOrderManager();
var orderId = Guid.NewGuid().ToString();
// Act
var result = await _orderManager.CancelOrderAsync(orderId);
// Assert
result.Should().BeTrue();
// Note: In a real implementation, we would verify the order was actually cancelled
// This is a simplified test
}
[Fact]
public async Task CancelOrderAsync_WithInvalidOrderId_ShouldReturnFalse()
{
// Arrange
SetupMocks();
_orderManager = CreateOrderManager();
string orderId = null; // Invalid - null order ID
// Act
var result = await _orderManager.CancelOrderAsync(orderId);
// Assert
result.Should().BeFalse();
}
[Fact]
public async Task GetOrderStatusAsync_WithValidOrderId_ShouldReturnStatus()
{
// Arrange
SetupMocks();
_orderManager = CreateOrderManager();
var orderId = Guid.NewGuid().ToString();
// Act
var status = await _orderManager.GetOrderStatusAsync(orderId);
// Assert
// In a real implementation, we would verify the status was retrieved correctly
// This is a simplified test
}
[Fact]
public async Task GetActiveOrdersAsync_ShouldReturnActiveOrders()
{
// Arrange
SetupMocks();
_orderManager = CreateOrderManager();
// Act
var orders = await _orderManager.GetActiveOrdersAsync();
// Assert
orders.Should().NotBeNull();
// In a real implementation, we would verify the active orders were retrieved correctly
}
private void SetupMocks()
{
_mockRiskManager = new Mock<IRiskManager>();
_mockPositionSizer = new Mock<IPositionSizer>();
_mockLogger = new Mock<ILogger<OrderManager>>();
_mockConfigManager = new Mock<RoutingConfigurationManager>();
_mockMetricsCollector = new Mock<RoutingMetricsCollector>();
_mockTwapExecutor = new Mock<TwapExecutor>();
_mockVwapExecutor = new Mock<VwapExecutor>();
_mockIcebergExecutor = new Mock<IcebergExecutor>();
_mockParameterProvider = new Mock<AlgorithmParameterProvider>();
_mockRateLimiter = new Mock<RateLimiter>();
_mockValueLimiter = new Mock<ValueLimiter>();
_mockCircuitBreaker = new Mock<CircuitBreaker>();
}
private OrderManager CreateOrderManager()
{
return new OrderManager(
_mockRiskManager.Object,
_mockPositionSizer.Object,
_mockLogger.Object,
_mockConfigManager.Object,
_mockMetricsCollector.Object,
_mockTwapExecutor.Object,
_mockVwapExecutor.Object,
_mockIcebergExecutor.Object,
_mockParameterProvider.Object,
_mockRateLimiter.Object,
_mockValueLimiter.Object,
_mockCircuitBreaker.Object);
}
}
```
#### Algorithmic Order Tests
```csharp
/// <summary>
/// Tests for OrderManager algorithmic order functionality
/// </summary>
public class OrderManagerAlgorithmicTests
{
private Mock<IRiskManager> _mockRiskManager;
private Mock<IPositionSizer> _mockPositionSizer;
private Mock<ILogger<OrderManager>> _mockLogger;
private Mock<RoutingConfigurationManager> _mockConfigManager;
private Mock<RoutingMetricsCollector> _mockMetricsCollector;
private Mock<TwapExecutor> _mockTwapExecutor;
private Mock<VwapExecutor> _mockVwapExecutor;
private Mock<IcebergExecutor> _mockIcebergExecutor;
private Mock<AlgorithmParameterProvider> _mockParameterProvider;
private Mock<RateLimiter> _mockRateLimiter;
private Mock<ValueLimiter> _mockValueLimiter;
private Mock<CircuitBreaker> _mockCircuitBreaker;
private OrderManager _orderManager;
[Fact]
public async Task ExecuteTwapAsync_WithValidParameters_ShouldExecuteSuccessfully()
{
// Arrange
SetupMocks();
_orderManager = CreateOrderManager();
var parameters = new TwapParameters(
Symbol: "ES",
Side: OrderSide.Buy,
TotalQuantity: 10,
StartTime: DateTime.UtcNow,
EndTime: DateTime.UtcNow.AddMinutes(30),
IntervalSeconds: 60,
LimitPrice: null,
TimeInForce: TimeInForce.Day,
MinSliceSize: 1,
MaxSliceSize: null,
AdjustForRemainingTime: true,
CancelAtEndTime: true,
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var riskDecision = new RiskDecision(
Allow: true,
RejectReason: null,
ModifiedIntent: null,
RiskLevel: RiskLevel.Low,
RiskMetrics: new Dictionary<string, object>()
);
_mockRiskManager.Setup(rm => rm.ValidateOrder(It.IsAny<StrategyIntent>(), It.IsAny<StrategyContext>(), It.IsAny<RiskConfig>()))
.Returns(riskDecision);
var executionState = new TwapExecutionState
{
ExecutionId = Guid.NewGuid().ToString(),
Parameters = parameters,
Status = TwapExecutionStatus.Completed,
TotalQuantity = 10,
ExecutedQuantity = 10,
StartTime = DateTime.UtcNow,
EndTime = DateTime.UtcNow.AddMinutes(30),
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
_mockTwapExecutor.Setup(te => te.ExecuteTwapAsync(It.IsAny<TwapParameters>(), It.IsAny<StrategyContext>()))
.ReturnsAsync(executionState);
// Act
var result = await _orderManager.ExecuteTwapAsync(parameters, context);
// Assert
result.Should().NotBeNull();
result.Success.Should().BeTrue();
result.OrderId.Should().NotBeNullOrEmpty();
result.Message.Should().Contain("completed successfully");
}
[Fact]
public async Task ExecuteVwapAsync_WithValidParameters_ShouldExecuteSuccessfully()
{
// Arrange
SetupMocks();
_orderManager = CreateOrderManager();
var parameters = new VwapParameters(
Symbol: "ES",
Side: OrderSide.Buy,
TotalQuantity: 10,
StartTime: DateTime.UtcNow,
EndTime: DateTime.UtcNow.AddMinutes(30),
ParticipationRate: 0.1,
LimitPrice: null,
TimeInForce: TimeInForce.Day,
MinOrderSize: 1,
MaxOrderSize: null,
CheckIntervalSeconds: 30,
CancelAtEndTime: true,
Aggressiveness: 0.5,
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var riskDecision = new RiskDecision(
Allow: true,
RejectReason: null,
ModifiedIntent: null,
RiskLevel: RiskLevel.Low,
RiskMetrics: new Dictionary<string, object>()
);
_mockRiskManager.Setup(rm => rm.ValidateOrder(It.IsAny<StrategyIntent>(), It.IsAny<StrategyContext>(), It.IsAny<RiskConfig>()))
.Returns(riskDecision);
var executionState = new VwapExecutionState
{
ExecutionId = Guid.NewGuid().ToString(),
Parameters = parameters,
Status = VwapExecutionStatus.Completed,
TotalQuantity = 10,
ExecutedQuantity = 10,
StartTime = DateTime.UtcNow,
EndTime = DateTime.UtcNow.AddMinutes(30),
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
_mockVwapExecutor.Setup(ve => ve.ExecuteVwapAsync(It.IsAny<VwapParameters>(), It.IsAny<StrategyContext>()))
.ReturnsAsync(executionState);
// Act
var result = await _orderManager.ExecuteVwapAsync(parameters, context);
// Assert
result.Should().NotBeNull();
result.Success.Should().BeTrue();
result.OrderId.Should().NotBeNullOrEmpty();
result.Message.Should().Contain("completed successfully");
}
[Fact]
public async Task ExecuteIcebergAsync_WithValidParameters_ShouldExecuteSuccessfully()
{
// Arrange
SetupMocks();
_orderManager = CreateOrderManager();
var parameters = new IcebergParameters(
Symbol: "ES",
Side: OrderSide.Buy,
TotalQuantity: 100,
VisibleQuantity: 10,
LimitPrice: null,
TimeInForce: TimeInForce.Day,
AutoReplenish: true,
MinVisibleQuantity: 1,
MaxVisibleQuantity: null,
PlacementDelayMs: 1000,
CancelAtEnd: true,
Aggressiveness: 0.5,
AdaptiveVisibility: false,
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var riskDecision = new RiskDecision(
Allow: true,
RejectReason: null,
ModifiedIntent: null,
RiskLevel: RiskLevel.Low,
RiskMetrics: new Dictionary<string, object>()
);
_mockRiskManager.Setup(rm => rm.ValidateOrder(It.IsAny<StrategyIntent>(), It.IsAny<StrategyContext>(), It.IsAny<RiskConfig>()))
.Returns(riskDecision);
var executionState = new IcebergExecutionState
{
ExecutionId = Guid.NewGuid().ToString(),
Parameters = parameters,
Status = IcebergExecutionStatus.Completed,
TotalQuantity = 100,
ExecutedQuantity = 100,
VisibleQuantity = 10,
StartTime = DateTime.UtcNow,
EndTime = DateTime.UtcNow.AddMinutes(30),
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
};
_mockIcebergExecutor.Setup(ie => ie.ExecuteIcebergAsync(It.IsAny<IcebergParameters>(), It.IsAny<StrategyContext>()))
.ReturnsAsync(executionState);
// Act
var result = await _orderManager.ExecuteIcebergAsync(parameters, context);
// Assert
result.Should().NotBeNull();
result.Success.Should().BeTrue();
result.OrderId.Should().NotBeNullOrEmpty();
result.Message.Should().Contain("completed successfully");
}
private void SetupMocks()
{
_mockRiskManager = new Mock<IRiskManager>();
_mockPositionSizer = new Mock<IPositionSizer>();
_mockLogger = new Mock<ILogger<OrderManager>>();
_mockConfigManager = new Mock<RoutingConfigurationManager>();
_mockMetricsCollector = new Mock<RoutingMetricsCollector>();
_mockTwapExecutor = new Mock<TwapExecutor>();
_mockVwapExecutor = new Mock<VwapExecutor>();
_mockIcebergExecutor = new Mock<IcebergExecutor>();
_mockParameterProvider = new Mock<AlgorithmParameterProvider>();
_mockRateLimiter = new Mock<RateLimiter>();
_mockValueLimiter = new Mock<ValueLimiter>();
_mockCircuitBreaker = new Mock<CircuitBreaker>();
}
private OrderManager CreateOrderManager()
{
return new OrderManager(
_mockRiskManager.Object,
_mockPositionSizer.Object,
_mockLogger.Object,
_mockConfigManager.Object,
_mockMetricsCollector.Object,
_mockTwapExecutor.Object,
_mockVwapExecutor.Object,
_mockIcebergExecutor.Object,
_mockParameterProvider.Object,
_mockRateLimiter.Object,
_mockValueLimiter.Object,
_mockCircuitBreaker.Object);
}
}
```
### 2. RiskManager Tests
#### Core Risk Management Tests
```csharp
/// <summary>
/// Tests for RiskManager core functionality
/// </summary>
public class RiskManagerTests
{
private Mock<ILogger<BasicRiskManager>> _mockLogger;
private BasicRiskManager _riskManager;
[Fact]
public void Constructor_WithValidLogger_ShouldInitializeSuccessfully()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicRiskManager>>();
// Act
var riskManager = new BasicRiskManager(_mockLogger.Object);
// Assert
riskManager.Should().NotBeNull();
}
[Fact]
public void ValidateOrder_WithValidIntentAndWithinLimits_ShouldAllow()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicRiskManager>>();
_riskManager = new BasicRiskManager(_mockLogger.Object);
var intent = new StrategyIntent(
Symbol: "ES",
Side: OrderSide.Buy,
EntryType: OrderType.Market,
LimitPrice: null,
StopTicks: 10,
TargetTicks: null,
Confidence: 1.0,
Reason: "Test order",
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var config = new RiskConfig(
DailyLossLimit: 1000,
MaxTradeRisk: 200,
MaxOpenPositions: 5,
EmergencyFlattenEnabled: true
);
// Act
var decision = _riskManager.ValidateOrder(intent, context, config);
// Assert
decision.Should().NotBeNull();
decision.Allow.Should().BeTrue();
decision.RejectReason.Should().BeNull();
decision.RiskLevel.Should().Be(RiskLevel.Low);
}
[Fact]
public void ValidateOrder_WithNullIntent_ShouldThrowArgumentNullException()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicRiskManager>>();
_riskManager = new BasicRiskManager(_mockLogger.Object);
StrategyIntent intent = null;
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var config = new RiskConfig(
DailyLossLimit: 1000,
MaxTradeRisk: 200,
MaxOpenPositions: 5,
EmergencyFlattenEnabled: true
);
// Act & Assert
Assert.Throws<ArgumentNullException>(() => _riskManager.ValidateOrder(intent, context, config));
}
[Fact]
public void ValidateOrder_WithNullContext_ShouldThrowArgumentNullException()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicRiskManager>>();
_riskManager = new BasicRiskManager(_mockLogger.Object);
var intent = new StrategyIntent(
Symbol: "ES",
Side: OrderSide.Buy,
EntryType: OrderType.Market,
LimitPrice: null,
StopTicks: 10,
TargetTicks: null,
Confidence: 1.0,
Reason: "Test order",
Metadata: new Dictionary<string, object>()
);
StrategyContext context = null;
var config = new RiskConfig(
DailyLossLimit: 1000,
MaxTradeRisk: 200,
MaxOpenPositions: 5,
EmergencyFlattenEnabled: true
);
// Act & Assert
Assert.Throws<ArgumentNullException>(() => _riskManager.ValidateOrder(intent, context, config));
}
[Fact]
public void ValidateOrder_WithNullConfig_ShouldThrowArgumentNullException()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicRiskManager>>();
_riskManager = new BasicRiskManager(_mockLogger.Object);
var intent = new StrategyIntent(
Symbol: "ES",
Side: OrderSide.Buy,
EntryType: OrderType.Market,
LimitPrice: null,
StopTicks: 10,
TargetTicks: null,
Confidence: 1.0,
Reason: "Test order",
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
RiskConfig config = null;
// Act & Assert
Assert.Throws<ArgumentNullException>(() => _riskManager.ValidateOrder(intent, context, config));
}
[Fact]
public void ValidateOrder_WithDailyLossLimitExceeded_ShouldReject()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicRiskManager>>();
_riskManager = new BasicRiskManager(_mockLogger.Object);
// Simulate daily loss exceeding limit
_riskManager.OnPnLUpdate(-1500, -1500); // $1500 loss, exceeds $1000 limit
var intent = new StrategyIntent(
Symbol: "ES",
Side: OrderSide.Buy,
EntryType: OrderType.Market,
LimitPrice: null,
StopTicks: 10,
TargetTicks: null,
Confidence: 1.0,
Reason: "Test order",
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var config = new RiskConfig(
DailyLossLimit: 1000,
MaxTradeRisk: 200,
MaxOpenPositions: 5,
EmergencyFlattenEnabled: true
);
// Act
var decision = _riskManager.ValidateOrder(intent, context, config);
// Assert
decision.Should().NotBeNull();
decision.Allow.Should().BeFalse();
decision.RejectReason.Should().Contain("Daily loss limit breached");
decision.RiskLevel.Should().Be(RiskLevel.Critical);
}
[Fact]
public void OnFill_WithValidFill_ShouldUpdateRiskState()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicRiskManager>>();
_riskManager = new BasicRiskManager(_mockLogger.Object);
var fill = new OrderFill(
OrderId: Guid.NewGuid().ToString(),
Symbol: "ES",
Quantity: 2,
FillPrice: 4200,
FillTime: DateTime.UtcNow,
Commission: 4.50m,
ExecutionId: Guid.NewGuid().ToString()
);
// Act
_riskManager.OnFill(fill);
// Assert
// In a real implementation, we would verify the risk state was updated correctly
// This is a simplified test
}
[Fact]
public void OnPnLUpdate_WithValidPnL_ShouldUpdateRiskState()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicRiskManager>>();
_riskManager = new BasicRiskManager(_mockLogger.Object);
var netPnL = -500.0;
var dayPnL = -500.0;
// Act
_riskManager.OnPnLUpdate(netPnL, dayPnL);
// Assert
// In a real implementation, we would verify the risk state was updated correctly
// This is a simplified test
}
[Fact]
public async Task EmergencyFlattenAsync_WithValidReason_ShouldFlattenPositions()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicRiskManager>>();
_riskManager = new BasicRiskManager(_mockLogger.Object);
var reason = "Manual emergency flatten";
// Act
var result = await _riskManager.EmergencyFlatten(reason);
// Assert
result.Should().BeTrue();
// In a real implementation, we would verify positions were actually flattened
}
[Fact]
public void GetRiskStatus_ShouldReturnCurrentRiskStatus()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicRiskManager>>();
_riskManager = new BasicRiskManager(_mockLogger.Object);
// Act
var status = _riskManager.GetRiskStatus();
// Assert
status.Should().NotBeNull();
status.TradingEnabled.Should().BeTrue();
status.DailyPnL.Should().Be(0);
status.MaxDrawdown.Should().Be(0);
}
}
```
### 3. PositionSizer Tests
#### Core Position Sizing Tests
```csharp
/// <summary>
/// Tests for PositionSizer core functionality
/// </summary>
public class PositionSizerTests
{
private Mock<ILogger<BasicPositionSizer>> _mockLogger;
private BasicPositionSizer _positionSizer;
[Fact]
public void Constructor_WithValidLogger_ShouldInitializeSuccessfully()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicPositionSizer>>();
// Act
var positionSizer = new BasicPositionSizer(_mockLogger.Object);
// Assert
positionSizer.Should().NotBeNull();
}
[Fact]
public void CalculateSize_WithFixedContractsMethod_ShouldReturnCorrectSize()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicPositionSizer>>();
_positionSizer = new BasicPositionSizer(_mockLogger.Object);
var intent = new StrategyIntent(
Symbol: "ES",
Side: OrderSide.Buy,
EntryType: OrderType.Market,
LimitPrice: null,
StopTicks: 8,
TargetTicks: null,
Confidence: 1.0,
Reason: "Test order",
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var config = new SizingConfig(
Method: SizingMethod.FixedContracts,
MinContracts: 1,
MaxContracts: 10,
RiskPerTrade: 200,
MethodParameters: new Dictionary<string, object> { ["contracts"] = 3 }
);
// Act
var result = _positionSizer.CalculateSize(intent, context, config);
// Assert
result.Should().NotBeNull();
result.Contracts.Should().Be(3);
result.Method.Should().Be(SizingMethod.FixedContracts);
result.RiskAmount.Should().Be(300.0); // 3 contracts * 8 ticks * $12.50
result.Calculations.Should().ContainKey("target_contracts");
result.Calculations.Should().ContainKey("clamped_contracts");
}
[Fact]
public void CalculateSize_WithFixedDollarRiskMethod_ShouldReturnCorrectSize()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicPositionSizer>>();
_positionSizer = new BasicPositionSizer(_mockLogger.Object);
var intent = new StrategyIntent(
Symbol: "ES",
Side: OrderSide.Buy,
EntryType: OrderType.Market,
LimitPrice: null,
StopTicks: 10,
TargetTicks: null,
Confidence: 1.0,
Reason: "Test order",
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var config = new SizingConfig(
Method: SizingMethod.FixedDollarRisk,
MinContracts: 1,
MaxContracts: 10,
RiskPerTrade: 250.0, // Target $250 risk
MethodParameters: new Dictionary<string, object>()
);
// Act
var result = _positionSizer.CalculateSize(intent, context, config);
// Assert
result.Should().NotBeNull();
result.Contracts.Should().Be(2); // $250 / (10 ticks * $12.50) = 2 contracts
result.Method.Should().Be(SizingMethod.FixedDollarRisk);
result.RiskAmount.Should().Be(250.0); // 2 * 10 * $12.50
result.Calculations.Should().ContainKey("target_risk");
result.Calculations.Should().ContainKey("optimal_contracts");
result.Calculations.Should().ContainKey("actual_risk");
}
[Fact]
public void CalculateSize_WithNullIntent_ShouldThrowArgumentNullException()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicPositionSizer>>();
_positionSizer = new BasicPositionSizer(_mockLogger.Object);
StrategyIntent intent = null;
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var config = new SizingConfig(
Method: SizingMethod.FixedContracts,
MinContracts: 1,
MaxContracts: 10,
RiskPerTrade: 200,
MethodParameters: new Dictionary<string, object> { ["contracts"] = 3 }
);
// Act & Assert
Assert.Throws<ArgumentNullException>(() => _positionSizer.CalculateSize(intent, context, config));
}
[Fact]
public void CalculateSize_WithNullContext_ShouldThrowArgumentNullException()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicPositionSizer>>();
_positionSizer = new BasicPositionSizer(_mockLogger.Object);
var intent = new StrategyIntent(
Symbol: "ES",
Side: OrderSide.Buy,
EntryType: OrderType.Market,
LimitPrice: null,
StopTicks: 8,
TargetTicks: null,
Confidence: 1.0,
Reason: "Test order",
Metadata: new Dictionary<string, object>()
);
StrategyContext context = null;
var config = new SizingConfig(
Method: SizingMethod.FixedContracts,
MinContracts: 1,
MaxContracts: 10,
RiskPerTrade: 200,
MethodParameters: new Dictionary<string, object> { ["contracts"] = 3 }
);
// Act & Assert
Assert.Throws<ArgumentNullException>(() => _positionSizer.CalculateSize(intent, context, config));
}
[Fact]
public void CalculateSize_WithNullConfig_ShouldThrowArgumentNullException()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicPositionSizer>>();
_positionSizer = new BasicPositionSizer(_mockLogger.Object);
var intent = new StrategyIntent(
Symbol: "ES",
Side: OrderSide.Buy,
EntryType: OrderType.Market,
LimitPrice: null,
StopTicks: 8,
TargetTicks: null,
Confidence: 1.0,
Reason: "Test order",
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
SizingConfig config = null;
// Act & Assert
Assert.Throws<ArgumentNullException>(() => _positionSizer.CalculateSize(intent, context, config));
}
[Fact]
public void GetMetadata_ShouldReturnCorrectMetadata()
{
// Arrange
_mockLogger = new Mock<ILogger<BasicPositionSizer>>();
_positionSizer = new BasicPositionSizer(_mockLogger.Object);
// Act
var metadata = _positionSizer.GetMetadata();
// Assert
metadata.Should().NotBeNull();
metadata.Name.Should().Be("Basic Position Sizer");
metadata.Description.Should().Contain("Fixed contracts");
metadata.Description.Should().Contain("fixed dollar risk");
metadata.RequiredParameters.Should().Contain("method");
metadata.RequiredParameters.Should().Contain("risk_per_trade");
}
[Fact]
public void ValidateConfig_WithValidConfig_ShouldReturnTrue()
{
// Arrange
var config = new SizingConfig(
Method: SizingMethod.FixedContracts,
MinContracts: 1,
MaxContracts: 10,
RiskPerTrade: 200,
MethodParameters: new Dictionary<string, object> { ["contracts"] = 2 }
);
// Act
var isValid = BasicPositionSizer.ValidateConfig(config, out var errors);
// Assert
isValid.Should().BeTrue();
errors.Should().BeEmpty();
}
[Fact]
public void ValidateConfig_WithInvalidConfig_ShouldReturnFalse()
{
// Arrange
var config = new SizingConfig(
Method: SizingMethod.FixedContracts,
MinContracts: 5,
MaxContracts: 2, // Invalid: min > max
RiskPerTrade: -100, // Invalid: negative risk
MethodParameters: new Dictionary<string, object>() // Missing required parameter
);
// Act
var isValid = BasicPositionSizer.ValidateConfig(config, out var errors);
// Assert
isValid.Should().BeFalse();
errors.Should().Contain("MinContracts must be <= MaxContracts");
errors.Should().Contain("RiskPerTrade must be > 0");
errors.Should().Contain("FixedContracts method requires 'contracts' parameter");
}
}
```
### 4. TWAP Algorithm Tests
#### TWAP Execution Tests
```csharp
/// <summary>
/// Tests for TWAP algorithm execution
/// </summary>
public class TwapExecutorTests
{
private Mock<ILogger<TwapExecutor>> _mockLogger;
private Mock<IOrderManager> _mockOrderManager;
private Mock<IMarketDataProvider> _mockMarketDataProvider;
private TwapExecutor _twapExecutor;
[Fact]
public void Constructor_WithValidDependencies_ShouldInitializeSuccessfully()
{
// Arrange
SetupMocks();
// Act
var twapExecutor = new TwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
// Assert
twapExecutor.Should().NotBeNull();
}
[Fact]
public async Task ExecuteTwapAsync_WithValidParameters_ShouldExecuteSuccessfully()
{
// Arrange
SetupMocks();
_twapExecutor = new TwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
var parameters = new TwapParameters(
Symbol: "ES",
Side: OrderSide.Buy,
TotalQuantity: 100,
StartTime: DateTime.UtcNow,
EndTime: DateTime.UtcNow.AddMinutes(30),
IntervalSeconds: 60,
LimitPrice: null,
TimeInForce: TimeInForce.Day,
MinSliceSize: 1,
MaxSliceSize: null,
AdjustForRemainingTime: true,
CancelAtEndTime: true,
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var orderResult = new OrderResult(
Success: true,
OrderId: Guid.NewGuid().ToString(),
Message: "Order submitted successfully",
Status: new OrderStatus(
OrderId: Guid.NewGuid().ToString(),
Symbol: "ES",
Side: OrderSide.Buy,
Type: OrderType.Market,
Quantity: 10,
FilledQuantity: 10,
LimitPrice: null,
StopPrice: null,
State: OrderState.Filled,
CreatedTime: DateTime.UtcNow,
FilledTime: DateTime.UtcNow,
Fills: new List<OrderFill>
{
new OrderFill(
OrderId: Guid.NewGuid().ToString(),
Symbol: "ES",
Quantity: 10,
FillPrice: 4200,
FillTime: DateTime.UtcNow,
Commission: 4.50m,
ExecutionId: Guid.NewGuid().ToString()
)
}
)
);
_mockOrderManager.Setup(om => om.SubmitOrderAsync(It.IsAny<OrderRequest>(), It.IsAny<StrategyContext>()))
.ReturnsAsync(orderResult);
// Act
var executionState = await _twapExecutor.ExecuteTwapAsync(parameters, context);
// Assert
executionState.Should().NotBeNull();
executionState.Status.Should().Be(TwapExecutionStatus.Running); // Initially running
executionState.TotalQuantity.Should().Be(100);
executionState.ExecutedQuantity.Should().Be(0); // Initially 0
executionState.Parameters.Should().BeEquivalentTo(parameters);
}
[Fact]
public async Task ExecuteTwapAsync_WithInvalidParameters_ShouldThrowArgumentException()
{
// Arrange
SetupMocks();
_twapExecutor = new TwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
var parameters = new TwapParameters(
Symbol: "", // Invalid - empty symbol
Side: OrderSide.Buy,
TotalQuantity: 0, // Invalid - zero quantity
StartTime: DateTime.UtcNow,
EndTime: DateTime.UtcNow.AddMinutes(30),
IntervalSeconds: 60,
LimitPrice: null,
TimeInForce: TimeInForce.Day,
MinSliceSize: 1,
MaxSliceSize: null,
AdjustForRemainingTime: true,
CancelAtEndTime: true,
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
// Act & Assert
await Assert.ThrowsAsync<ArgumentException>(() => _twapExecutor.ExecuteTwapAsync(parameters, context));
}
[Fact]
public async Task ExecuteTwapAsync_WithNullParameters_ShouldThrowArgumentNullException()
{
// Arrange
SetupMocks();
_twapExecutor = new TwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
TwapParameters parameters = null;
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => _twapExecutor.ExecuteTwapAsync(parameters, context));
}
[Fact]
public async Task ExecuteTwapAsync_WithNullContext_ShouldThrowArgumentNullException()
{
// Arrange
SetupMocks();
_twapExecutor = new TwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
var parameters = new TwapParameters(
Symbol: "ES",
Side: OrderSide.Buy,
TotalQuantity: 100,
StartTime: DateTime.UtcNow,
EndTime: DateTime.UtcNow.AddMinutes(30),
IntervalSeconds: 60,
LimitPrice: null,
TimeInForce: TimeInForce.Day,
MinSliceSize: 1,
MaxSliceSize: null,
AdjustForRemainingTime: true,
CancelAtEndTime: true,
Metadata: new Dictionary<string, object>()
);
StrategyContext context = null;
// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => _twapExecutor.ExecuteTwapAsync(parameters, context));
}
[Fact]
public async Task CancelExecutionAsync_WithValidExecutionId_ShouldCancelSuccessfully()
{
// Arrange
SetupMocks();
_twapExecutor = new TwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
var executionId = Guid.NewGuid().ToString();
// Act
var result = await _twapExecutor.CancelExecutionAsync(executionId);
// Assert
result.Should().BeFalse(); // False because execution doesn't exist
// In a real implementation, we would test cancelling an actual execution
}
[Fact]
public async Task CancelExecutionAsync_WithInvalidExecutionId_ShouldReturnFalse()
{
// Arrange
SetupMocks();
_twapExecutor = new TwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
string executionId = null; // Invalid - null execution ID
// Act
var result = await _twapExecutor.CancelExecutionAsync(executionId);
// Assert
result.Should().BeFalse(); // False because execution ID is invalid
}
private void SetupMocks()
{
_mockLogger = new Mock<ILogger<TwapExecutor>>();
_mockOrderManager = new Mock<IOrderManager>();
_mockMarketDataProvider = new Mock<IMarketDataProvider>();
}
}
```
### 5. VWAP Algorithm Tests
#### VWAP Execution Tests
```csharp
/// <summary>
/// Tests for VWAP algorithm execution
/// </summary>
public class VwapExecutorTests
{
private Mock<ILogger<VwapExecutor>> _mockLogger;
private Mock<IOrderManager> _mockOrderManager;
private Mock<IMarketDataProvider> _mockMarketDataProvider;
private VwapExecutor _vwapExecutor;
[Fact]
public void Constructor_WithValidDependencies_ShouldInitializeSuccessfully()
{
// Arrange
SetupMocks();
// Act
var vwapExecutor = new VwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
// Assert
vwapExecutor.Should().NotBeNull();
}
[Fact]
public async Task ExecuteVwapAsync_WithValidParameters_ShouldExecuteSuccessfully()
{
// Arrange
SetupMocks();
_vwapExecutor = new VwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
var parameters = new VwapParameters(
Symbol: "ES",
Side: OrderSide.Buy,
TotalQuantity: 100,
StartTime: DateTime.UtcNow,
EndTime: DateTime.UtcNow.AddMinutes(30),
ParticipationRate: 0.1,
LimitPrice: null,
TimeInForce: TimeInForce.Day,
MinOrderSize: 1,
MaxOrderSize: null,
CheckIntervalSeconds: 30,
CancelAtEndTime: true,
Aggressiveness: 0.5,
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var orderResult = new OrderResult(
Success: true,
OrderId: Guid.NewGuid().ToString(),
Message: "Order submitted successfully",
Status: new OrderStatus(
OrderId: Guid.NewGuid().ToString(),
Symbol: "ES",
Side: OrderSide.Buy,
Type: OrderType.Market,
Quantity: 10,
FilledQuantity: 10,
LimitPrice: null,
StopPrice: null,
State: OrderState.Filled,
CreatedTime: DateTime.UtcNow,
FilledTime: DateTime.UtcNow,
Fills: new List<OrderFill>
{
new OrderFill(
OrderId: Guid.NewGuid().ToString(),
Symbol: "ES",
Quantity: 10,
FillPrice: 4200,
FillTime: DateTime.UtcNow,
Commission: 4.50m,
ExecutionId: Guid.NewGuid().ToString()
)
}
)
);
_mockOrderManager.Setup(om => om.SubmitOrderAsync(It.IsAny<OrderRequest>(), It.IsAny<StrategyContext>()))
.ReturnsAsync(orderResult);
// Act
var executionState = await _vwapExecutor.ExecuteVwapAsync(parameters, context);
// Assert
executionState.Should().NotBeNull();
executionState.Status.Should().Be(VwapExecutionStatus.Running); // Initially running
executionState.TotalQuantity.Should().Be(100);
executionState.ExecutedQuantity.Should().Be(0); // Initially 0
executionState.Parameters.Should().BeEquivalentTo(parameters);
}
[Fact]
public async Task ExecuteVwapAsync_WithInvalidParameters_ShouldThrowArgumentException()
{
// Arrange
SetupMocks();
_vwapExecutor = new VwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
var parameters = new VwapParameters(
Symbol: "", // Invalid - empty symbol
Side: OrderSide.Buy,
TotalQuantity: 0, // Invalid - zero quantity
StartTime: DateTime.UtcNow,
EndTime: DateTime.UtcNow.AddMinutes(30),
ParticipationRate: 0.1,
LimitPrice: null,
TimeInForce: TimeInForce.Day,
MinOrderSize: 1,
MaxOrderSize: null,
CheckIntervalSeconds: 30,
CancelAtEndTime: true,
Aggressiveness: 0.5,
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
// Act & Assert
await Assert.ThrowsAsync<ArgumentException>(() => _vwapExecutor.ExecuteVwapAsync(parameters, context));
}
[Fact]
public async Task ExecuteVwapAsync_WithNullParameters_ShouldThrowArgumentNullException()
{
// Arrange
SetupMocks();
_vwapExecutor = new VwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
VwapParameters parameters = null;
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => _vwapExecutor.ExecuteVwapAsync(parameters, context));
}
[Fact]
public async Task ExecuteVwapAsync_WithNullContext_ShouldThrowArgumentNullException()
{
// Arrange
SetupMocks();
_vwapExecutor = new VwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
var parameters = new VwapParameters(
Symbol: "ES",
Side: OrderSide.Buy,
TotalQuantity: 100,
StartTime: DateTime.UtcNow,
EndTime: DateTime.UtcNow.AddMinutes(30),
ParticipationRate: 0.1,
LimitPrice: null,
TimeInForce: TimeInForce.Day,
MinOrderSize: 1,
MaxOrderSize: null,
CheckIntervalSeconds: 30,
CancelAtEndTime: true,
Aggressiveness: 0.5,
Metadata: new Dictionary<string, object>()
);
StrategyContext context = null;
// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => _vwapExecutor.ExecuteVwapAsync(parameters, context));
}
[Fact]
public async Task CancelExecutionAsync_WithValidExecutionId_ShouldCancelSuccessfully()
{
// Arrange
SetupMocks();
_vwapExecutor = new VwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
var executionId = Guid.NewGuid().ToString();
// Act
var result = await _vwapExecutor.CancelExecutionAsync(executionId);
// Assert
result.Should().BeFalse(); // False because execution doesn't exist
// In a real implementation, we would test cancelling an actual execution
}
[Fact]
public async Task CancelExecutionAsync_WithInvalidExecutionId_ShouldReturnFalse()
{
// Arrange
SetupMocks();
_vwapExecutor = new VwapExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
string executionId = null; // Invalid - null execution ID
// Act
var result = await _vwapExecutor.CancelExecutionAsync(executionId);
// Assert
result.Should().BeFalse(); // False because execution ID is invalid
}
private void SetupMocks()
{
_mockLogger = new Mock<ILogger<VwapExecutor>>();
_mockOrderManager = new Mock<IOrderManager>();
_mockMarketDataProvider = new Mock<IMarketDataProvider>();
}
}
```
### 6. Iceberg Algorithm Tests
#### Iceberg Execution Tests
```csharp
/// <summary>
/// Tests for Iceberg algorithm execution
/// </summary>
public class IcebergExecutorTests
{
private Mock<ILogger<IcebergExecutor>> _mockLogger;
private Mock<IOrderManager> _mockOrderManager;
private Mock<IMarketDataProvider> _mockMarketDataProvider;
private IcebergExecutor _icebergExecutor;
[Fact]
public void Constructor_WithValidDependencies_ShouldInitializeSuccessfully()
{
// Arrange
SetupMocks();
// Act
var icebergExecutor = new IcebergExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
// Assert
icebergExecutor.Should().NotBeNull();
}
[Fact]
public async Task ExecuteIcebergAsync_WithValidParameters_ShouldExecuteSuccessfully()
{
// Arrange
SetupMocks();
_icebergExecutor = new IcebergExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
var parameters = new IcebergParameters(
Symbol: "ES",
Side: OrderSide.Buy,
TotalQuantity: 100,
VisibleQuantity: 10,
LimitPrice: null,
TimeInForce: TimeInForce.Day,
AutoReplenish: true,
MinVisibleQuantity: 1,
MaxVisibleQuantity: null,
PlacementDelayMs: 1000,
CancelAtEnd: true,
Aggressiveness: 0.5,
AdaptiveVisibility: false,
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
var orderResult = new OrderResult(
Success: true,
OrderId: Guid.NewGuid().ToString(),
Message: "Order submitted successfully",
Status: new OrderStatus(
OrderId: Guid.NewGuid().ToString(),
Symbol: "ES",
Side: OrderSide.Buy,
Type: OrderType.Market,
Quantity: 10,
FilledQuantity: 10,
LimitPrice: null,
StopPrice: null,
State: OrderState.Filled,
CreatedTime: DateTime.UtcNow,
FilledTime: DateTime.UtcNow,
Fills: new List<OrderFill>
{
new OrderFill(
OrderId: Guid.NewGuid().ToString(),
Symbol: "ES",
Quantity: 10,
FillPrice: 4200,
FillTime: DateTime.UtcNow,
Commission: 4.50m,
ExecutionId: Guid.NewGuid().ToString()
)
}
)
);
_mockOrderManager.Setup(om => om.SubmitOrderAsync(It.IsAny<OrderRequest>(), It.IsAny<StrategyContext>()))
.ReturnsAsync(orderResult);
// Act
var executionState = await _icebergExecutor.ExecuteIcebergAsync(parameters, context);
// Assert
executionState.Should().NotBeNull();
executionState.Status.Should().Be(IcebergExecutionStatus.Running); // Initially running
executionState.TotalQuantity.Should().Be(100);
executionState.ExecutedQuantity.Should().Be(0); // Initially 0
executionState.VisibleQuantity.Should().Be(10);
executionState.Parameters.Should().BeEquivalentTo(parameters);
}
[Fact]
public async Task ExecuteIcebergAsync_WithInvalidParameters_ShouldThrowArgumentException()
{
// Arrange
SetupMocks();
_icebergExecutor = new IcebergExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
var parameters = new IcebergParameters(
Symbol: "", // Invalid - empty symbol
Side: OrderSide.Buy,
TotalQuantity: 0, // Invalid - zero quantity
VisibleQuantity: 0, // Invalid - zero visible quantity
LimitPrice: null,
TimeInForce: TimeInForce.Day,
AutoReplenish: true,
MinVisibleQuantity: 1,
MaxVisibleQuantity: null,
PlacementDelayMs: 1000,
CancelAtEnd: true,
Aggressiveness: 0.5,
AdaptiveVisibility: false,
Metadata: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
// Act & Assert
await Assert.ThrowsAsync<ArgumentException>(() => _icebergExecutor.ExecuteIcebergAsync(parameters, context));
}
[Fact]
public async Task ExecuteIcebergAsync_WithNullParameters_ShouldThrowArgumentNullException()
{
// Arrange
SetupMocks();
_icebergExecutor = new IcebergExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
IcebergParameters parameters = null;
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => _icebergExecutor.ExecuteIcebergAsync(parameters, context));
}
[Fact]
public async Task ExecuteIcebergAsync_WithNullContext_ShouldThrowArgumentNullException()
{
// Arrange
SetupMocks();
_icebergExecutor = new IcebergExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
var parameters = new IcebergParameters(
Symbol: "ES",
Side: OrderSide.Buy,
TotalQuantity: 100,
VisibleQuantity: 10,
LimitPrice: null,
TimeInForce: TimeInForce.Day,
AutoReplenish: true,
MinVisibleQuantity: 1,
MaxVisibleQuantity: null,
PlacementDelayMs: 1000,
CancelAtEnd: true,
Aggressiveness: 0.5,
AdaptiveVisibility: false,
Metadata: new Dictionary<string, object>()
);
StrategyContext context = null;
// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => _icebergExecutor.ExecuteIcebergAsync(parameters, context));
}
[Fact]
public async Task CancelExecutionAsync_WithValidExecutionId_ShouldCancelSuccessfully()
{
// Arrange
SetupMocks();
_icebergExecutor = new IcebergExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
var executionId = Guid.NewGuid().ToString();
// Act
var result = await _icebergExecutor.CancelExecutionAsync(executionId);
// Assert
result.Should().BeFalse(); // False because execution doesn't exist
// In a real implementation, we would test cancelling an actual execution
}
[Fact]
public async Task CancelExecutionAsync_WithInvalidExecutionId_ShouldReturnFalse()
{
// Arrange
SetupMocks();
_icebergExecutor = new IcebergExecutor(
_mockLogger.Object,
_mockOrderManager.Object,
_mockMarketDataProvider.Object);
string executionId = null; // Invalid - null execution ID
// Act
var result = await _icebergExecutor.CancelExecutionAsync(executionId);
// Assert
result.Should().BeFalse(); // False because execution ID is invalid
}
private void SetupMocks()
{
_mockLogger = new Mock<ILogger<IcebergExecutor>>();
_mockOrderManager = new Mock<IOrderManager>();
_mockMarketDataProvider = new Mock<IMarketDataProvider>();
}
}
```
### 7. Rate Limiter Tests
#### Rate Limiting Tests
```csharp
/// <summary>
/// Tests for RateLimiter functionality
/// </summary>
public class RateLimiterTests
{
private Mock<ILogger<RateLimiter>> _mockLogger;
private RateLimiter _rateLimiter;
[Fact]
public void Constructor_WithValidLogger_ShouldInitializeSuccessfully()
{
// Arrange
_mockLogger = new Mock<ILogger<RateLimiter>>();
// Act
var rateLimiter = new RateLimiter(_mockLogger.Object);
// Assert
rateLimiter.Should().NotBeNull();
}
[Fact]
public void CheckRateLimit_WithValidOrderAndWithinLimits_ShouldAllow()
{
// Arrange
_mockLogger = new Mock<ILogger<RateLimiter>>();
_rateLimiter = new RateLimiter(_mockLogger.Object);
var order = new OrderRequest(
Symbol: "ES",
Side: OrderSide.Buy,
Type: OrderType.Market,
Quantity: 1,
LimitPrice: null,
StopPrice: null,
TimeInForce: TimeInForce.Day,
Algorithm: null,
AlgorithmParameters: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
// Act
var result = _rateLimiter.CheckRateLimit(order, context);
// Assert
result.Should().NotBeNull();
result.Action.Should().Be(RateLimitAction.Allow);
result.Violations.Should().BeEmpty();
}
[Fact]
public void CheckRateLimit_WithNullOrder_ShouldThrowArgumentNullException()
{
// Arrange
_mockLogger = new Mock<ILogger<RateLimiter>>();
_rateLimiter = new RateLimiter(_mockLogger.Object);
OrderRequest order = null;
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
// Act & Assert
Assert.Throws<ArgumentNullException>(() => _rateLimiter.CheckRateLimit(order, context));
}
[Fact]
public void CheckRateLimit_WithNullContext_ShouldThrowArgumentNullException()
{
// Arrange
_mockLogger = new Mock<ILogger<RateLimiter>>();
_rateLimiter = new RateLimiter(_mockLogger.Object);
var order = new OrderRequest(
Symbol: "ES",
Side: OrderSide.Buy,
Type: OrderType.Market,
Quantity: 1,
LimitPrice: null,
StopPrice: null,
TimeInForce: TimeInForce.Day,
Algorithm: null,
AlgorithmParameters: new Dictionary<string, object>()
);
StrategyContext context = null;
// Act & Assert
Assert.Throws<ArgumentNullException>(() => _rateLimiter.CheckRateLimit(order, context));
}
[Fact]
public void GetMetrics_ShouldReturnCurrentMetrics()
{
// Arrange
_mockLogger = new Mock<ILogger<RateLimiter>>();
_rateLimiter = new RateLimiter(_mockLogger.Object);
// Act
var metrics = _rateLimiter.GetMetrics();
// Assert
metrics.Should().NotBeNull();
metrics.GlobalPerSecondRate.Should().BeGreaterOrEqualTo(0);
metrics.GlobalPerMinuteRate.Should().BeGreaterOrEqualTo(0);
metrics.GlobalPerHourRate.Should().BeGreaterOrEqualTo(0);
}
[Fact]
public void ResetUserState_WithValidUserId_ShouldResetUserState()
{
// Arrange
_mockLogger = new Mock<ILogger<RateLimiter>>();
_rateLimiter = new RateLimiter(_mockLogger.Object);
var userId = "test-user";
// Act
_rateLimiter.ResetUserState(userId);
// Assert
// In a real implementation, we would verify the user state was reset
// This is a simplified test
}
[Fact]
public void ResetSymbolState_WithValidSymbol_ShouldResetSymbolState()
{
// Arrange
_mockLogger = new Mock<ILogger<RateLimiter>>();
_rateLimiter = new RateLimiter(_mockLogger.Object);
var symbol = "ES";
// Act
_rateLimiter.ResetSymbolState(symbol);
// Assert
// In a real implementation, we would verify the symbol state was reset
// This is a simplified test
}
[Fact]
public void ResetAllState_ShouldResetAllState()
{
// Arrange
_mockLogger = new Mock<ILogger<RateLimiter>>();
_rateLimiter = new RateLimiter(_mockLogger.Object);
// Act
_rateLimiter.ResetAllState();
// Assert
// In a real implementation, we would verify all state was reset
// This is a simplified test
}
}
```
### 8. Value Limiter Tests
#### Value Limiting Tests
```csharp
/// <summary>
/// Tests for ValueLimiter functionality
/// </summary>
public class ValueLimiterTests
{
private Mock<ILogger<ValueLimiter>> _mockLogger;
private Mock<ICurrencyConverter> _mockCurrencyConverter;
private ValueLimiter _valueLimiter;
[Fact]
public void Constructor_WithValidDependencies_ShouldInitializeSuccessfully()
{
// Arrange
SetupMocks();
// Act
var valueLimiter = new ValueLimiter(
_mockLogger.Object,
_mockCurrencyConverter.Object);
// Assert
valueLimiter.Should().NotBeNull();
}
[Fact]
public async Task CheckValueLimitAsync_WithValidOrderAndWithinLimits_ShouldAllow()
{
// Arrange
SetupMocks();
_valueLimiter = new ValueLimiter(
_mockLogger.Object,
_mockCurrencyConverter.Object);
var order = new OrderRequest(
Symbol: "ES",
Side: OrderSide.Buy,
Type: OrderType.Market,
Quantity: 1,
LimitPrice: null,
StopPrice: null,
TimeInForce: TimeInForce.Day,
Algorithm: null,
AlgorithmParameters: new Dictionary<string, object>()
);
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
// Act
var result = await _valueLimiter.CheckValueLimitAsync(order, context);
// Assert
result.Should().NotBeNull();
result.Action.Should().Be(ValueLimitAction.Allow);
result.Violations.Should().BeEmpty();
}
[Fact]
public async Task CheckValueLimitAsync_WithNullOrder_ShouldThrowArgumentNullException()
{
// Arrange
SetupMocks();
_valueLimiter = new ValueLimiter(
_mockLogger.Object,
_mockCurrencyConverter.Object);
OrderRequest order = null;
var context = new StrategyContext(
Symbol: "ES",
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position("ES", 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => _valueLimiter.CheckValueLimitAsync(order, context));
}
[Fact]
public async Task CheckValueLimitAsync_WithNullContext_ShouldThrowArgumentNullException()
{
// Arrange
SetupMocks();
_valueLimiter = new ValueLimiter(
_mockLogger.Object,
_mockCurrencyConverter.Object);
var order = new OrderRequest(
Symbol: "ES",
Side: OrderSide.Buy,
Type: OrderType.Market,
Quantity: 1,
LimitPrice: null,
StopPrice: null,
TimeInForce: TimeInForce.Day,
Algorithm: null,
AlgorithmParameters: new Dictionary<string, object>()
);
StrategyContext context = null;
// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() => _valueLimiter.CheckValueLimitAsync(order, context));
}
[Fact]
public void GetMetrics_ShouldReturnCurrentMetrics()
{
// Arrange
SetupMocks();
_valueLimiter = new ValueLimiter(
_mockLogger.Object,
_mockCurrencyConverter.Object);
// Act
var metrics = _valueLimiter.GetMetrics();
// Assert
metrics.Should().NotBeNull();
metrics.GlobalTotalValueToday.Should().BeGreaterOrEqualTo(0);
metrics.GlobalAverageOrderValue.Should().BeGreaterOrEqualTo(0);
metrics.GlobalMaxOrderValue.Should().BeGreaterOrEqualTo(0);
}
[Fact]
public void ResetUserState_WithValidUserId_ShouldResetUserState()
{
// Arrange
SetupMocks();
_valueLimiter = new ValueLimiter(
_mockLogger.Object,
_mockCurrencyConverter.Object);
var userId = "test-user";
// Act
_valueLimiter.ResetUserState(userId);
// Assert
// In a real implementation, we would verify the user state was reset
// This is a simplified test
}
[Fact]
public void ResetSymbolState_WithValidSymbol_ShouldResetSymbolState()
{
// Arrange
SetupMocks();
_valueLimiter = new ValueLimiter(
_mockLogger.Object,
_mockCurrencyConverter.Object);
var symbol = "ES";
// Act
_valueLimiter.ResetSymbolState(symbol);
// Assert
// In a real implementation, we would verify the symbol state was reset
// This is a simplified test
}
[Fact]
public void ResetAllState_ShouldResetAllState()
{
// Arrange
SetupMocks();
_valueLimiter = new ValueLimiter(
_mockLogger.Object,
_mockCurrencyConverter.Object);
// Act
_valueLimiter.ResetAllState();
// Assert
// In a real implementation, we would verify all state was reset
// This is a simplified test
}
private void SetupMocks()
{
_mockLogger = new Mock<ILogger<ValueLimiter>>();
_mockCurrencyConverter = new Mock<ICurrencyConverter>();
}
}
```
### 9. Circuit Breaker Tests
#### Circuit Breaking Tests
```csharp
/// <summary>
/// Tests for CircuitBreaker functionality
/// </summary>
public class CircuitBreakerTests
{
private Mock<ILogger<CircuitBreaker>> _mockLogger;
private Mock<IHealthChecker> _mockHealthChecker;
private CircuitBreaker _circuitBreaker;
[Fact]
public void Constructor_WithValidDependencies_ShouldInitializeSuccessfully()
{
// Arrange
SetupMocks();
// Act
var circuitBreaker = new CircuitBreaker(
_mockLogger.Object,
_mockHealthChecker.Object);
// Assert
circuitBreaker.Should().NotBeNull();
}
[Fact]
public void CheckCircuit_WhenClosed_ShouldAllow()
{
// Arrange
SetupMocks();
_circuitBreaker = new CircuitBreaker(
_mockLogger.Object,
_mockHealthChecker.Object);
// Act
var result = _circuitBreaker.CheckCircuit();
// Assert
result.Should().NotBeNull();
result.Action.Should().Be(CircuitBreakerAction.Allow);
result.State.Should().Be(CircuitBreakerState.Closed);
}
[Fact]
public void CheckCircuit_WhenOpen_ShouldReject()
{
// Arrange
SetupMocks();
_circuitBreaker = new CircuitBreaker(
_mockLogger.Object,
_mockHealthChecker.Object);
// Manually open circuit
_circuitBreaker.OpenCircuit("Test open");
// Act
var result = _circuitBreaker.CheckCircuit();
// Assert
result.Should().NotBeNull();
result.Action.Should().Be(CircuitBreakerAction.Reject);
result.State.Should().Be(CircuitBreakerState.Open);
}
[Fact]
public void RecordFailure_WhenThresholdExceeded_ShouldOpenCircuit()
{
// Arrange
SetupMocks();
var config = new CircuitBreakerConfig
{
FailureThreshold = 2,
TimeoutSeconds = 60,
WindowSizeSeconds = 300
};
_circuitBreaker = new CircuitBreaker(
_mockLogger.Object,
_mockHealthChecker.Object,
config);
var failure1 = new FailureRecord
{
Timestamp = DateTime.UtcNow,
Type = FailureType.OrderRejection,
Message = "Test failure 1"
};
var failure2 = new FailureRecord
{
Timestamp = DateTime.UtcNow,
Type = FailureType.OrderRejection,
Message = "Test failure 2"
};
// Act
_circuitBreaker.RecordFailure(failure1);
_circuitBreaker.RecordFailure(failure2);
// Assert
_circuitBreaker.GetState().Should().Be(CircuitBreakerState.Open);
}
[Fact]
public void RecordSuccess_WhenInHalfOpenAndThresholdExceeded_ShouldCloseCircuit()
{
// Arrange
SetupMocks();
var config = new CircuitBreakerConfig
{
FailureThreshold = 1,
SuccessThreshold = 2,
TimeoutSeconds = 1,
WindowSizeSeconds = 300,
EnableHalfOpenState = true,
EnableAutomaticReset = true
};
_circuitBreaker = new CircuitBreaker(
_mockLogger.Object,
_mockHealthChecker.Object,
config);
// Open circuit
var failure = new FailureRecord
{
Timestamp = DateTime.UtcNow,
Type = FailureType.OrderRejection,
Message = "Test failure"
};
_circuitBreaker.RecordFailure(failure);
// Wait for timeout to transition to half-open
Thread.Sleep(TimeSpan.FromSeconds(2));
// Act
_circuitBreaker.RecordSuccess(DateTime.UtcNow);
_circuitBreaker.RecordSuccess(DateTime.UtcNow);
// Assert
_circuitBreaker.GetState().Should().Be(CircuitBreakerState.Closed);
}
[Fact]
public void OpenCircuit_WithValidReason_ShouldOpenCircuit()
{
// Arrange
SetupMocks();
_circuitBreaker = new CircuitBreaker(
_mockLogger.Object,
_mockHealthChecker.Object);
var reason = "Manual open";
// Act
_circuitBreaker.OpenCircuit(reason);
// Assert
_circuitBreaker.GetState().Should().Be(CircuitBreakerState.Open);
}
[Fact]
public void CloseCircuit_WithValidReason_ShouldCloseCircuit()
{
// Arrange
SetupMocks();
_circuitBreaker = new CircuitBreaker(
_mockLogger.Object,
_mockHealthChecker.Object);
// Open circuit first
_circuitBreaker.OpenCircuit("Test open");
var reason = "Manual close";
// Act
_circuitBreaker.CloseCircuit(reason);
// Assert
_circuitBreaker.GetState().Should().Be(CircuitBreakerState.Closed);
}
[Fact]
public void GetMetrics_ShouldReturnCurrentMetrics()
{
// Arrange
SetupMocks();
_circuitBreaker = new CircuitBreaker(
_mockLogger.Object,
_mockHealthChecker.Object);
// Act
var metrics = _circuitBreaker.GetMetrics();
// Assert
metrics.Should().NotBeNull();
metrics.State.Should().Be(CircuitBreakerState.Closed);
metrics.FailureCount.Should().Be(0);
metrics.SuccessCount.Should().Be(0);
}
[Fact]
public void Reset_ShouldResetCircuitState()
{
// Arrange
SetupMocks();
_circuitBreaker = new CircuitBreaker(
_mockLogger.Object,
_mockHealthChecker.Object);
// Open circuit
_circuitBreaker.OpenCircuit("Test open");
// Act
_circuitBreaker.Reset();
// Assert
_circuitBreaker.GetState().Should().Be(CircuitBreakerState.Closed);
}
private void SetupMocks()
{
_mockLogger = new Mock<ILogger<CircuitBreaker>>();
_mockHealthChecker = new Mock<IHealthChecker>();
}
}
```
## Test Coverage Goals
### Component Coverage Targets
| Component | Coverage Target | Notes |
|-----------|-----------------|-------|
| OrderManager | 95% | Critical component, extensive testing required |
| RiskManager | 95% | Core risk management, must be thoroughly tested |
| PositionSizer | 90% | Important for position sizing accuracy |
| TWAP Executor | 90% | Algorithmic execution, complex logic |
| VWAP Executor | 90% | Algorithmic execution, complex logic |
| Iceberg Executor | 90% | Algorithmic execution, complex logic |
| Rate Limiter | 85% | Important for system stability |
| Value Limiter | 85% | Important for risk management |
| Circuit Breaker | 90% | Critical for system resilience |
### Edge Case Testing
1. **Boundary Conditions**: Test at the limits of all numeric parameters
2. **Race Conditions**: Test concurrent access to shared resources
3. **Network Failures**: Test handling of network interruptions
4. **System Overload**: Test behavior under high load conditions
5. **Invalid Input**: Test handling of malformed or malicious input
6. **Time Zone Issues**: Test behavior across different time zones
7. **Memory Pressure**: Test behavior under memory constraints
8. **Disk Space**: Test behavior when disk space is low
## Test Data Management
### Test Data Generation
```csharp
/// <summary>
/// Helper class for generating test data
/// </summary>
public static class TestDataGenerator
{
/// <summary>
/// Generate a valid order request for testing
/// </summary>
public static OrderRequest GenerateValidOrderRequest(
string symbol = "ES",
OrderSide side = OrderSide.Buy,
OrderType type = OrderType.Market,
int quantity = 1)
{
return new OrderRequest(
Symbol: symbol,
Side: side,
Type: type,
Quantity: quantity,
LimitPrice: type == OrderType.Limit || type == OrderType.StopLimit ? 4200m : (decimal?)null,
StopPrice: type == OrderType.StopMarket || type == OrderType.StopLimit ? 4190m : (decimal?)null,
TimeInForce: TimeInForce.Day,
Algorithm: null,
AlgorithmParameters: new Dictionary<string, object>()
);
}
/// <summary>
/// Generate a valid strategy context for testing
/// </summary>
public static StrategyContext GenerateValidStrategyContext(string symbol = "ES")
{
return new StrategyContext(
Symbol: symbol,
CurrentTime: DateTime.UtcNow,
CurrentPosition: new Position(symbol, 0, 0, 0, 0, DateTime.UtcNow),
Account: new AccountInfo(100000, 100000, 0, 0, DateTime.UtcNow),
Session: new MarketSession(DateTime.UtcNow.Date, DateTime.UtcNow.Date.AddDays(1), true, "RTH"),
CustomData: new Dictionary<string, object>()
);
}
/// <summary>
/// Generate a valid risk config for testing
/// </summary>
public static RiskConfig GenerateValidRiskConfig()
{
return new RiskConfig(
DailyLossLimit: 1000,
MaxTradeRisk: 200,
MaxOpenPositions: 5,
EmergencyFlattenEnabled: true
);
}
/// <summary>
/// Generate a valid sizing config for testing
/// </summary>
public static SizingConfig GenerateValidSizingConfig()
{
return new SizingConfig(
Method: SizingMethod.FixedContracts,
MinContracts: 1,
MaxContracts: 10,
RiskPerTrade: 200,
MethodParameters: new Dictionary<string, object> { ["contracts"] = 2 }
);
}
/// <summary>
/// Generate a valid order result for testing
/// </summary>
public static OrderResult GenerateValidOrderResult(string orderId = null)
{
return new OrderResult(
Success: true,
OrderId: orderId ?? Guid.NewGuid().ToString(),
Message: "Order submitted successfully",
Status: new OrderStatus(
OrderId: orderId ?? Guid.NewGuid().ToString(),
Symbol: "ES",
Side: OrderSide.Buy,
Type: OrderType.Market,
Quantity: 1,
FilledQuantity: 1,
LimitPrice: null,
StopPrice: null,
State: OrderState.Filled,
CreatedTime: DateTime.UtcNow,
FilledTime: DateTime.UtcNow,
Fills: new List<OrderFill>
{
new OrderFill(
OrderId: orderId ?? Guid.NewGuid().ToString(),
Symbol: "ES",
Quantity: 1,
FillPrice: 4200,
FillTime: DateTime.UtcNow,
Commission: 4.50m,
ExecutionId: Guid.NewGuid().ToString()
)
}
)
);
}
}
```
## Test Execution Strategy
### Continuous Integration Testing
1. **Build Verification**: Run critical tests on every commit
2. **Nightly Builds**: Run full test suite nightly
3. **Release Testing**: Run extended test suite before releases
4. **Performance Testing**: Run performance tests weekly
### Test Parallelization
1. **Component Isolation**: Run component tests in parallel
2. **Database Isolation**: Use separate test databases for each test
3. **Resource Management**: Ensure tests don't interfere with each other
### Test Reporting
1. **Real-time Feedback**: Provide immediate feedback on test results
2. **Historical Trends**: Track test results over time
3. **Coverage Reports**: Generate code coverage reports
4. **Performance Metrics**: Track test execution performance
## Test Maintenance
### Test Refactoring
1. **Regular Reviews**: Review and refactor tests quarterly
2. **Dead Code Removal**: Remove obsolete tests
3. **Duplication Elimination**: Consolidate duplicate test logic
4. **Performance Optimization**: Optimize slow tests
### Test Documentation
1. **Inline Comments**: Document complex test logic
2. **Test Descriptions**: Provide clear test descriptions
3. **Failure Analysis**: Document common test failures and resolutions
4. **Best Practices**: Maintain test best practices documentation
## Conclusion
This comprehensive unit test plan ensures that all OMS components are thoroughly tested with >90% code coverage. The plan covers all critical functionality, edge cases, and integration points, providing confidence in the reliability and correctness of the system.
The test suite will be implemented incrementally, with priority given to the most critical components (OrderManager, RiskManager, PositionSizer) followed by algorithmic execution components (TWAP, VWAP, Iceberg) and finally auxiliary components (Rate Limiter, Value Limiter, Circuit Breaker).
Regular test reviews and maintenance will ensure the test suite remains effective as the system evolves.