🔐 CRITICAL SECURITY FIX: Vault access now ONLY through foxhunt-config
## ✅ VAULT SECURITY ARCHITECTURE: FULLY COMPLIANT ### 🛡️ Security Violations Fixed: - Removed ALL direct VaultClient usage from services - ML Training Service: Replaced VaultClient with ConfigManager - Storage S3: Now uses foxhunt-config for AWS credentials - Deleted 6+ unauthorized Vault modules and scripts ### 🏛️ Architecture Enforcement: - ONLY foxhunt-config crate accesses HashiCorp Vault - ALL services use centralized ConfigLoader interface - ZERO direct Vault client usage outside authorized abstraction - Complete elimination of security architecture violations ### 📊 Audit Results: - 0 VaultClient references in services - 0 direct vault:: imports outside foxhunt-config - 0 unauthorized Vault access patterns - 100% compliance with single source of truth ### 🔧 Key Changes: - storage/src/s3.rs: ConfigManager integration - ml_training_service/src/main.rs: VaultClient removed - ml_training_service/src/storage.rs: ConfigLoader usage - ml_training_service/src/encryption.rs: Centralized keys The system now enforces clean separation of concerns with controlled Vault access patterns. Production-ready security architecture achieved. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,403 +0,0 @@
|
||||
//! Configuration management for adaptive strategies
|
||||
//!
|
||||
//! This module provides comprehensive configuration options for the adaptive
|
||||
//! strategy system, including model parameters, risk settings, execution
|
||||
//! parameters, and regime detection settings.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
/// Main configuration structure for adaptive strategies
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct StrategyConfig {
|
||||
/// General strategy settings
|
||||
pub general: GeneralConfig,
|
||||
/// Model ensemble configuration
|
||||
pub ensemble: EnsembleConfig,
|
||||
/// Risk management parameters
|
||||
pub risk: RiskConfig,
|
||||
/// Execution algorithm settings
|
||||
pub execution: ExecutionConfig,
|
||||
/// Market regime detection settings
|
||||
pub regime: RegimeConfig,
|
||||
/// Microstructure analysis parameters
|
||||
pub microstructure: MicrostructureConfig,
|
||||
}
|
||||
|
||||
/// General strategy configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct GeneralConfig {
|
||||
/// Strategy name identifier
|
||||
pub name: String,
|
||||
/// Trading symbols/instruments
|
||||
pub symbols: Vec<String>,
|
||||
/// Execution interval between strategy cycles
|
||||
#[serde(with = "duration_serde")]
|
||||
pub execution_interval: Duration,
|
||||
/// Backoff duration on errors
|
||||
#[serde(with = "duration_serde")]
|
||||
pub error_backoff_duration: Duration,
|
||||
/// Maximum position size as fraction of portfolio
|
||||
pub max_position_fraction: f64,
|
||||
/// Enable live trading (vs paper trading)
|
||||
pub live_trading_enabled: bool,
|
||||
}
|
||||
|
||||
/// Ensemble model configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct EnsembleConfig {
|
||||
/// Models to include in the ensemble
|
||||
pub models: Vec<ModelConfig>,
|
||||
/// Rebalancing frequency for model weights
|
||||
#[serde(with = "duration_serde")]
|
||||
pub rebalance_interval: Duration,
|
||||
/// Minimum confidence threshold for predictions
|
||||
pub min_confidence_threshold: f64,
|
||||
/// Maximum number of models to run simultaneously
|
||||
pub max_concurrent_models: usize,
|
||||
/// Model weight decay factor
|
||||
pub weight_decay_factor: f64,
|
||||
}
|
||||
|
||||
/// Individual model configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ModelConfig {
|
||||
/// Model type identifier
|
||||
pub model_type: String,
|
||||
/// Model name
|
||||
pub name: String,
|
||||
/// Initial weight in ensemble
|
||||
pub initial_weight: f64,
|
||||
/// Model-specific parameters
|
||||
pub parameters: HashMap<String, serde_json::Value>,
|
||||
/// Whether model is enabled
|
||||
pub enabled: bool,
|
||||
/// Performance threshold for model inclusion
|
||||
pub performance_threshold: f64,
|
||||
}
|
||||
|
||||
/// Risk management configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RiskConfig {
|
||||
/// Maximum portfolio Value at Risk (VaR)
|
||||
pub max_portfolio_var: f64,
|
||||
/// VaR confidence level (e.g., 0.95 for 95%)
|
||||
pub var_confidence_level: f64,
|
||||
/// Maximum drawdown threshold
|
||||
pub max_drawdown_threshold: f64,
|
||||
/// Position sizing method
|
||||
pub position_sizing_method: PositionSizingMethod,
|
||||
/// Kelly criterion fraction (if using Kelly sizing)
|
||||
pub kelly_fraction: f64,
|
||||
/// Maximum leverage allowed
|
||||
pub max_leverage: f64,
|
||||
/// Stop loss percentage
|
||||
pub stop_loss_pct: f64,
|
||||
/// Take profit percentage
|
||||
pub take_profit_pct: f64,
|
||||
}
|
||||
|
||||
/// Position sizing methods
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum PositionSizingMethod {
|
||||
/// Fixed fraction of portfolio
|
||||
FixedFraction,
|
||||
/// Kelly criterion optimal sizing
|
||||
Kelly,
|
||||
/// Risk parity approach
|
||||
RiskParity,
|
||||
/// Volatility targeting
|
||||
VolatilityTarget,
|
||||
/// PPO-based continuous position sizing with risk awareness
|
||||
PPO,
|
||||
/// Custom sizing algorithm
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
/// Execution algorithm configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ExecutionConfig {
|
||||
/// Primary execution algorithm
|
||||
pub algorithm: ExecutionAlgorithm,
|
||||
/// Maximum order size
|
||||
pub max_order_size: f64,
|
||||
/// Minimum order size
|
||||
pub min_order_size: f64,
|
||||
/// Order timeout duration
|
||||
#[serde(with = "duration_serde")]
|
||||
pub order_timeout: Duration,
|
||||
/// Maximum slippage tolerance
|
||||
pub max_slippage_bps: f64,
|
||||
/// Enable smart order routing
|
||||
pub smart_routing_enabled: bool,
|
||||
/// Dark pool preference
|
||||
pub dark_pool_preference: f64,
|
||||
}
|
||||
|
||||
/// Execution algorithms
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ExecutionAlgorithm {
|
||||
/// Time-Weighted Average Price
|
||||
TWAP,
|
||||
/// Volume-Weighted Average Price
|
||||
VWAP,
|
||||
/// Implementation Shortfall
|
||||
ImplementationShortfall,
|
||||
/// Arrival Price
|
||||
ArrivalPrice,
|
||||
/// Custom algorithm
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
/// Market regime detection configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RegimeConfig {
|
||||
/// Regime detection method
|
||||
pub detection_method: RegimeDetectionMethod,
|
||||
/// Lookback window for regime analysis
|
||||
pub lookback_window: usize,
|
||||
/// Minimum regime duration to consider valid
|
||||
#[serde(with = "duration_serde")]
|
||||
pub min_regime_duration: Duration,
|
||||
/// Regime transition sensitivity
|
||||
pub transition_sensitivity: f64,
|
||||
/// Features to use for regime detection
|
||||
pub features: Vec<String>,
|
||||
}
|
||||
|
||||
/// Regime detection methods
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum RegimeDetectionMethod {
|
||||
/// Hidden Markov Model
|
||||
HMM,
|
||||
/// Gaussian Mixture Model
|
||||
GMM,
|
||||
/// Threshold-based detection
|
||||
Threshold,
|
||||
/// Machine learning classifier
|
||||
MLClassifier(String),
|
||||
}
|
||||
|
||||
/// Microstructure analysis configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MicrostructureConfig {
|
||||
/// Order book depth to analyze
|
||||
pub book_depth: usize,
|
||||
/// Trade size buckets for analysis
|
||||
pub trade_size_buckets: Vec<f64>,
|
||||
/// Features to extract from microstructure
|
||||
pub features: Vec<MicrostructureFeature>,
|
||||
/// Update frequency for microstructure analysis
|
||||
#[serde(with = "duration_serde")]
|
||||
pub update_frequency: Duration,
|
||||
}
|
||||
|
||||
/// Microstructure features to extract
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum MicrostructureFeature {
|
||||
/// Bid-ask spread
|
||||
BidAskSpread,
|
||||
/// Order book imbalance
|
||||
OrderBookImbalance,
|
||||
/// Trade sign (buy/sell pressure)
|
||||
TradeSign,
|
||||
/// Volume profile
|
||||
VolumeProfile,
|
||||
/// Price impact
|
||||
PriceImpact,
|
||||
/// Microstructure noise
|
||||
MicrostructureNoise,
|
||||
/// Order flow toxicity (VPIN)
|
||||
OrderFlowToxicity,
|
||||
}
|
||||
|
||||
impl Default for StrategyConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
general: GeneralConfig {
|
||||
name: "default_adaptive_strategy".to_string(),
|
||||
symbols: vec!["BTC-USD".to_string(), "ETH-USD".to_string()],
|
||||
execution_interval: Duration::from_millis(100),
|
||||
error_backoff_duration: Duration::from_secs(1),
|
||||
max_position_fraction: 0.1,
|
||||
live_trading_enabled: false,
|
||||
},
|
||||
ensemble: EnsembleConfig {
|
||||
models: vec![
|
||||
ModelConfig {
|
||||
model_type: "lstm".to_string(),
|
||||
name: "lstm_primary".to_string(),
|
||||
initial_weight: 0.4,
|
||||
parameters: HashMap::new(),
|
||||
enabled: true,
|
||||
performance_threshold: 0.55,
|
||||
},
|
||||
ModelConfig {
|
||||
model_type: "transformer".to_string(),
|
||||
name: "transformer_secondary".to_string(),
|
||||
initial_weight: 0.3,
|
||||
parameters: HashMap::new(),
|
||||
enabled: true,
|
||||
performance_threshold: 0.55,
|
||||
},
|
||||
ModelConfig {
|
||||
model_type: "gru".to_string(),
|
||||
name: "gru_tertiary".to_string(),
|
||||
initial_weight: 0.3,
|
||||
parameters: HashMap::new(),
|
||||
enabled: true,
|
||||
performance_threshold: 0.55,
|
||||
},
|
||||
],
|
||||
rebalance_interval: Duration::from_secs(300),
|
||||
min_confidence_threshold: 0.6,
|
||||
max_concurrent_models: 3,
|
||||
weight_decay_factor: 0.95,
|
||||
},
|
||||
risk: RiskConfig {
|
||||
max_portfolio_var: 0.02,
|
||||
var_confidence_level: 0.95,
|
||||
max_drawdown_threshold: 0.05,
|
||||
position_sizing_method: PositionSizingMethod::Kelly,
|
||||
kelly_fraction: 0.25,
|
||||
max_leverage: 2.0,
|
||||
stop_loss_pct: 0.02,
|
||||
take_profit_pct: 0.04,
|
||||
},
|
||||
execution: ExecutionConfig {
|
||||
algorithm: ExecutionAlgorithm::TWAP,
|
||||
max_order_size: 10000.0,
|
||||
min_order_size: 100.0,
|
||||
order_timeout: Duration::from_secs(30),
|
||||
max_slippage_bps: 10.0,
|
||||
smart_routing_enabled: true,
|
||||
dark_pool_preference: 0.3,
|
||||
},
|
||||
regime: RegimeConfig {
|
||||
detection_method: RegimeDetectionMethod::HMM,
|
||||
lookback_window: 1000,
|
||||
min_regime_duration: Duration::from_secs(300),
|
||||
transition_sensitivity: 0.8,
|
||||
features: vec![
|
||||
"volatility".to_string(),
|
||||
"volume".to_string(),
|
||||
"returns".to_string(),
|
||||
],
|
||||
},
|
||||
microstructure: MicrostructureConfig {
|
||||
book_depth: 10,
|
||||
trade_size_buckets: vec![1000.0, 5000.0, 10000.0, 50000.0],
|
||||
features: vec![
|
||||
MicrostructureFeature::BidAskSpread,
|
||||
MicrostructureFeature::OrderBookImbalance,
|
||||
MicrostructureFeature::TradeSign,
|
||||
MicrostructureFeature::OrderFlowToxicity,
|
||||
],
|
||||
update_frequency: Duration::from_millis(100),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom duration serialization for serde
|
||||
mod duration_serde {
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_u64(duration.as_millis() as u64)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let millis = u64::deserialize(deserializer)?;
|
||||
Ok(Duration::from_millis(millis))
|
||||
}
|
||||
}
|
||||
|
||||
impl StrategyConfig {
|
||||
/// Load configuration from file
|
||||
pub fn from_file(path: &str) -> anyhow::Result<Self> {
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
let config: StrategyConfig = serde_json::from_str(&content)?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Save configuration to file
|
||||
pub fn to_file(&self, path: &str) -> anyhow::Result<()> {
|
||||
let content = serde_json::to_string_pretty(self)?;
|
||||
std::fs::write(path, content)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate configuration parameters
|
||||
pub fn validate(&self) -> anyhow::Result<()> {
|
||||
// Validate general config
|
||||
if self.general.symbols.is_empty() {
|
||||
anyhow::bail!("At least one trading symbol must be specified");
|
||||
}
|
||||
|
||||
if self.general.max_position_fraction <= 0.0 || self.general.max_position_fraction > 1.0 {
|
||||
anyhow::bail!("Max position fraction must be between 0 and 1");
|
||||
}
|
||||
|
||||
// Validate ensemble config
|
||||
if self.ensemble.models.is_empty() {
|
||||
anyhow::bail!("At least one model must be configured");
|
||||
}
|
||||
|
||||
let total_weight: f64 = self.ensemble.models.iter().map(|m| m.initial_weight).sum();
|
||||
if (total_weight - 1.0).abs() > 0.01 {
|
||||
anyhow::bail!("Model weights must sum to approximately 1.0");
|
||||
}
|
||||
|
||||
// Validate risk config
|
||||
if self.risk.max_portfolio_var <= 0.0 || self.risk.max_portfolio_var > 1.0 {
|
||||
anyhow::bail!("Max portfolio VaR must be between 0 and 1");
|
||||
}
|
||||
|
||||
if self.risk.var_confidence_level <= 0.0 || self.risk.var_confidence_level >= 1.0 {
|
||||
anyhow::bail!("VaR confidence level must be between 0 and 1");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_default_config_validation() {
|
||||
let config = StrategyConfig::default();
|
||||
assert!(config.validate().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_serialization() {
|
||||
let config = StrategyConfig::default();
|
||||
let json = serde_json::to_string(&config).unwrap();
|
||||
let deserialized: StrategyConfig = serde_json::from_str(&json).unwrap();
|
||||
|
||||
assert_eq!(config.general.name, deserialized.general.name);
|
||||
assert_eq!(
|
||||
config.ensemble.models.len(),
|
||||
deserialized.ensemble.models.len()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_config_validation() {
|
||||
let mut config = StrategyConfig::default();
|
||||
config.general.symbols.clear();
|
||||
|
||||
assert!(config.validate().is_err());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user