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

563 lines
17 KiB
Markdown

# Risk Validation Logic Implementation Design
## Overview
This document details the implementation of risk validation logic in the Order Management System (OMS), which integrates with the existing risk management system to ensure all orders comply with defined risk parameters before submission.
## Integration Approach
The OMS integrates with the existing IRiskManager interface to validate all orders before submission. This ensures consistency with the risk management system already implemented in the NT8 SDK.
## Risk Validation Flow
### 1. Order Request Conversion
Before validation, OMS order requests are converted to StrategyIntent objects that the risk manager can understand:
```csharp
private StrategyIntent ConvertToStrategyIntent(OrderRequest request)
{
return new StrategyIntent(
request.Symbol,
ConvertOrderSide(request.Side),
ConvertOrderType(request.Type),
(double?)request.LimitPrice,
GetStopTicks(request),
GetTargetTicks(request),
1.0, // Confidence - maximum for OMS orders
$"OMS {request.Type} Order", // Reason
new Dictionary<string, object>
{
["OrderId"] = Guid.NewGuid().ToString(),
["Algorithm"] = request.Algorithm,
["TimeInForce"] = request.TimeInForce.ToString()
}
);
}
private OrderSide ConvertOrderSide(NT8.Core.Orders.OrderSide side)
{
return side switch
{
NT8.Core.Orders.OrderSide.Buy => OrderSide.Buy,
NT8.Core.Orders.OrderSide.Sell => OrderSide.Sell,
_ => OrderSide.Flat
};
}
private NT8.Core.Common.Models.OrderType ConvertOrderType(NT8.Core.Orders.OrderType type)
{
return type switch
{
NT8.Core.Orders.OrderType.Market => NT8.Core.Common.Models.OrderType.Market,
NT8.Core.Orders.OrderType.Limit => NT8.Core.Common.Models.OrderType.Limit,
NT8.Core.Orders.OrderType.StopMarket => NT8.Core.Common.Models.OrderType.StopMarket,
NT8.Core.Orders.OrderType.StopLimit => NT8.Core.Common.Models.OrderType.StopLimit,
_ => NT8.Core.Common.Models.OrderType.Market
};
}
private int GetStopTicks(OrderRequest request)
{
// Calculate stop ticks based on stop price and current market price
// This is a simplified implementation
if (request.StopPrice.HasValue)
{
// In a real implementation, this would calculate the actual tick difference
return 10; // Placeholder value
}
return 0;
}
private int? GetTargetTicks(OrderRequest request)
{
// Calculate target ticks for profit targets if applicable
// This would be used for strategies with predefined profit targets
return null; // Not applicable for OMS orders
}
```
### 2. Risk Configuration
The risk validation uses a configuration that aligns with the existing risk management system:
```csharp
private RiskConfig GetRiskConfig()
{
// In a real implementation, this would be configurable
// For now, using values consistent with the existing risk management system
return new RiskConfig(
DailyLossLimit: 1000, // $1000 daily loss limit
MaxTradeRisk: 200, // $200 maximum per-trade risk
MaxOpenPositions: 10, // Maximum 10 open positions
EmergencyFlattenEnabled: true // Emergency flatten functionality enabled
);
}
```
### 3. Risk Validation Implementation
The core risk validation logic that integrates with the existing risk manager:
```csharp
public async Task<RiskDecision> ValidateOrderAsync(OrderRequest request, StrategyContext context)
{
if (request == null) throw new ArgumentNullException(nameof(request));
try
{
_logger.LogInformation("Validating order for {Symbol} {Side} {Quantity}",
request.Symbol, request.Side, request.Quantity);
// Convert order request to strategy intent
var intent = ConvertToStrategyIntent(request);
// Validate intent
if (!intent.IsValid())
{
_logger.LogWarning("Invalid strategy intent generated from order request");
return new RiskDecision(
Allow: false,
RejectReason: "Invalid order parameters",
ModifiedIntent: null,
RiskLevel: RiskLevel.Critical,
RiskMetrics: new Dictionary<string, object> { ["error"] = "Invalid intent" }
);
}
// Get risk configuration
var config = GetRiskConfig();
// Validate with risk manager
var decision = _riskManager.ValidateOrder(intent, context, config);
if (decision.Allow)
{
_logger.LogInformation("Order validation passed for {Symbol}", request.Symbol);
}
else
{
_logger.LogWarning("Order validation failed for {Symbol}: {Reason}",
request.Symbol, decision.RejectReason);
}
return decision;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error validating order for {Symbol}", request.Symbol);
return new RiskDecision(
Allow: false,
RejectReason: $"Risk validation error: {ex.Message}",
ModifiedIntent: null,
RiskLevel: RiskLevel.Critical,
RiskMetrics: new Dictionary<string, object> { ["error"] = ex.Message }
);
}
}
```
### 4. Enhanced Validation for Algorithmic Orders
Special validation logic for algorithmic orders (TWAP, VWAP, Iceberg):
```csharp
private RiskDecision ValidateAlgorithmicOrder(OrderRequest request, StrategyContext context, RiskConfig config)
{
// For algorithmic orders, we need to validate the total risk of the entire order
// rather than just individual slices
var intent = ConvertToStrategyIntent(request);
// Adjust risk calculation for algorithmic orders
if (!string.IsNullOrEmpty(request.Algorithm))
{
// Modify intent to reflect total order size for risk calculation
var modifiedIntent = ModifyIntentForAlgorithmicRisk(intent, request);
return _riskManager.ValidateOrder(modifiedIntent, context, config);
}
// Standard validation for regular orders
return _riskManager.ValidateOrder(intent, context, config);
}
private StrategyIntent ModifyIntentForAlgorithmicRisk(StrategyIntent intent, OrderRequest request)
{
// For algorithmic orders, the risk manager needs to understand that this is
// part of a larger strategy and may need to adjust risk calculations
var metadata = new Dictionary<string, object>(intent.Metadata ?? new Dictionary<string, object>())
{
["IsAlgorithmic"] = true,
["AlgorithmType"] = request.Algorithm,
["TotalQuantity"] = request.Quantity
};
// If this is a slice of a larger order, we might need to adjust the risk calculation
// to account for the total order size rather than just this slice
return new StrategyIntent(
intent.Symbol,
intent.Side,
intent.EntryType,
intent.LimitPrice,
intent.StopTicks,
intent.TargetTicks,
intent.Confidence,
intent.Reason,
metadata
);
}
```
## Risk Integration with Order Processing
### Pre-Submission Validation
All order submission methods validate orders through the risk management system:
```csharp
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);
}
// Continue with order processing if validation passes
// ... (rest of order processing logic)
}
```
### Real-time Risk Updates
The OMS also updates the risk manager with real-time information about order fills:
```csharp
private async Task NotifyRiskManagerOfFillAsync(OrderFill fill)
{
try
{
_riskManager.OnFill(fill);
_logger.LogInformation("Risk manager notified of fill for order {OrderId}", fill.OrderId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error notifying risk manager of fill for order {OrderId}", fill.OrderId);
}
}
private async Task NotifyRiskManagerOfPnLAsync(double netPnL, double dayPnL)
{
try
{
_riskManager.OnPnLUpdate(netPnL, dayPnL);
_logger.LogInformation("Risk manager updated with P&L: Net={NetPnL}, Day={DayPnL}", netPnL, dayPnL);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error updating risk manager with P&L");
}
}
```
## Emergency Handling
### Emergency Flatten Integration
The OMS can trigger emergency flattening through the risk management system:
```csharp
public async Task<bool> EmergencyFlattenAsync(string reason)
{
if (string.IsNullOrEmpty(reason)) throw new ArgumentException("Reason required", nameof(reason));
try
{
_logger.LogCritical("Emergency flatten triggered: {Reason}", reason);
// Cancel all active orders
var activeOrders = await GetActiveOrdersAsync();
foreach (var order in activeOrders)
{
await CancelOrderAsync(order.OrderId);
}
// Trigger emergency flatten in risk manager
var result = await _riskManager.EmergencyFlatten(reason);
_logger.LogInformation("Emergency flatten completed: {Result}", result);
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during emergency flatten: {Reason}", reason);
return false;
}
}
```
### Circuit Breaker Integration
The OMS respects circuit breaker status from the risk management system:
```csharp
private bool IsTradingHalted()
{
try
{
var riskStatus = _riskManager.GetRiskStatus();
return !riskStatus.TradingEnabled;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error checking trading halt status");
// Err on the side of caution - assume trading is halted if we can't determine status
return true;
}
}
public async Task<OrderResult> SubmitOrderAsync(OrderRequest request, StrategyContext context)
{
// Check if trading is halted
if (IsTradingHalted())
{
_logger.LogWarning("Order submission blocked - trading is currently halted");
return new OrderResult(false, null, "Trading is currently halted by risk management", null);
}
// Continue with normal order processing
// ... (rest of order processing logic)
}
```
## Risk Metrics Collection
### Order-Level Risk Metrics
Collect risk metrics for each order:
```csharp
private Dictionary<string, object> CollectRiskMetrics(OrderRequest request, RiskDecision decision)
{
var metrics = new Dictionary<string, object>
{
["Symbol"] = request.Symbol,
["OrderType"] = request.Type.ToString(),
["Quantity"] = request.Quantity,
["RiskLevel"] = decision.RiskLevel.ToString(),
["ValidationTime"] = DateTime.UtcNow
};
if (decision.RiskMetrics != null)
{
foreach (var kvp in decision.RiskMetrics)
{
metrics[$"Risk_{kvp.Key}"] = kvp.Value;
}
}
return metrics;
}
```
### Aggregated Risk Metrics
Maintain aggregated risk metrics for monitoring:
```csharp
private void UpdateAggregatedRiskMetrics(RiskDecision decision)
{
lock (_lock)
{
// Update counters based on risk decision
switch (decision.RiskLevel)
{
case RiskLevel.Low:
_riskMetrics.LowRiskOrders++;
break;
case RiskLevel.Medium:
_riskMetrics.MediumRiskOrders++;
break;
case RiskLevel.High:
_riskMetrics.HighRiskOrders++;
break;
case RiskLevel.Critical:
_riskMetrics.CriticalRiskOrders++;
break;
}
// Track rejected orders
if (!decision.Allow)
{
_riskMetrics.RejectedOrders++;
}
_riskMetrics.LastUpdated = DateTime.UtcNow;
}
}
```
## Error Handling and Fallbacks
### Graceful Degradation
If the risk manager is unavailable, the system can implement fallback behavior:
```csharp
public async Task<RiskDecision> ValidateOrderAsync(OrderRequest request, StrategyContext context)
{
try
{
// Normal risk validation
var intent = ConvertToStrategyIntent(request);
var config = GetRiskConfig();
return _riskManager.ValidateOrder(intent, context, config);
}
catch (Exception ex)
{
_logger.LogError(ex, "Risk manager unavailable, applying fallback validation for {Symbol}", request.Symbol);
// Fallback validation - more conservative approach
return ApplyFallbackValidation(request, context);
}
}
private RiskDecision ApplyFallbackValidation(OrderRequest request, StrategyContext context)
{
// Conservative fallback validation
// Reject orders that are clearly problematic
// Reject if order size is too large
if (request.Quantity > 100) // Arbitrary large size
{
return new RiskDecision(
Allow: false,
RejectReason: "Order size exceeds fallback limit",
ModifiedIntent: null,
RiskLevel: RiskLevel.Critical,
RiskMetrics: new Dictionary<string, object> { ["fallback_reject"] = "size" }
);
}
// Allow other orders with high risk level
return new RiskDecision(
Allow: true,
RejectReason: null,
ModifiedIntent: null,
RiskLevel: RiskLevel.High, // Conservative risk level
RiskMetrics: new Dictionary<string, object> { ["fallback_allow"] = true }
);
}
```
## Testing Considerations
### Unit Tests for Risk Validation
1. **Valid Orders**: Orders that should pass risk validation
2. **Invalid Orders**: Orders that should be rejected by risk management
3. **Edge Cases**: Boundary conditions for risk limits
4. **Algorithmic Orders**: Special handling for TWAP, VWAP, Iceberg orders
5. **Emergency Scenarios**: Trading halt and emergency flatten scenarios
### Integration Tests
1. **Risk Manager Integration**: Verify proper integration with IRiskManager
2. **Configuration Changes**: Test behavior with different risk configurations
3. **State Management**: Verify risk state is properly maintained
4. **Error Handling**: Test fallback behavior when risk manager is unavailable
## Performance Considerations
### Validation Caching
Cache risk validation results for identical orders within a short time window:
```csharp
private readonly Dictionary<string, (RiskDecision decision, DateTime timestamp)> _validationCache
= new Dictionary<string, (RiskDecision, DateTime)>();
private string GenerateValidationCacheKey(OrderRequest request, StrategyContext context)
{
// Generate a cache key based on order parameters and context
return $"{request.Symbol}_{request.Side}_{request.Quantity}_{request.Type}_{context?.CurrentTime:yyyyMMdd}";
}
private RiskDecision GetCachedValidationResult(string cacheKey)
{
if (_validationCache.ContainsKey(cacheKey))
{
var (decision, timestamp) = _validationCache[cacheKey];
// Expire cache after 1 second
if (DateTime.UtcNow.Subtract(timestamp).TotalSeconds < 1)
{
return decision;
}
else
{
_validationCache.Remove(cacheKey);
}
}
return null;
}
```
### Asynchronous Validation
Perform risk validation asynchronously to avoid blocking order submission:
```csharp
public async Task<OrderResult> SubmitOrderAsync(OrderRequest request, StrategyContext context)
{
// Start risk validation in background
var validationTask = ValidateOrderAsync(request, context);
// Continue with other order processing steps
// Wait for validation result
var riskDecision = await validationTask;
// Process validation result
// ... (rest of logic)
}
```
## Monitoring and Alerting
### Risk Alerts
Generate alerts for high-risk orders or risk limit breaches:
```csharp
private void GenerateRiskAlerts(RiskDecision decision, OrderRequest request)
{
if (decision.RiskLevel == RiskLevel.High || decision.RiskLevel == RiskLevel.Critical)
{
_logger.LogWarning("High-risk order detected: {Symbol} {Side} {Quantity} - Risk Level: {RiskLevel}",
request.Symbol, request.Side, request.Quantity, decision.RiskLevel);
// In a real implementation, this might trigger:
// - Email alerts to risk managers
// - Slack notifications
// - Dashboard warnings
}
}
```
### Risk Dashboard Integration
Provide metrics for risk dashboard integration:
```csharp
public RiskMetrics GetRiskMetrics()
{
lock (_lock)
{
return _riskMetrics;
}
}
```
## Future Enhancements
1. **Dynamic Risk Limits**: Adjust risk limits based on market conditions
2. **Machine Learning**: Use ML models to predict risk levels
3. **Real-time Market Data**: Integrate real-time volatility data into risk calculations
4. **Cross-Asset Risk**: Calculate risk across multiple asset classes
5. **Scenario Analysis**: Simulate risk under different market conditions