Files
foxhunt/crates/ml-risk/src/lib.rs
jgrusewski d313486dc2 refactor(ml): split monolith into 9 sub-crates + delete dead code
Extract 9 new sub-crates from the ml monolith to enable parallel
compilation across the workspace:

New crates (this commit):
- ml-features (282 tests): feature engineering, 21 modules
- ml-labeling (45 tests): triple barrier, meta-labeling, fractional diff
- ml-ensemble (116 tests): ensemble coordination, voting, confidence
- ml-hyperopt (47 tests): core PSO/TPE optimizer, parameter space
- ml-checkpoint (41 tests): checkpoint persistence, compression, signing
- ml-regime (68 tests): CUSUM, Bayesian changepoint, regime classification
- ml-data-validation (67 tests): FDR correction, CPCV, data quality
- ml-risk (33 tests): neural VaR, Kelly criterion, circuit breakers
- ml-validation (43 tests): statistical validation, walk-forward, DSR

Extended existing crates:
- ml-dqn: added evaluation/ (backtesting engine, metrics, reports)
  and checkpoint implementation
- ml-supervised: added checkpoint implementations
- ml-core: added shared types needed by new sub-crates

Pattern: each module in ml/ becomes a thin facade (pub use subcrate::*)
with bridge modules staying in ml for cross-model adapter code.

Dead code deleted (~7K lines):
- 13 undeclared files in microstructure/ (never compiled)
- 7 undeclared files + tests/ in risk/ (never compiled)
- parquet_io, cache_service, cache_storage, minio_integration (unused)
- extraction_wave_d_impl.rs (bare fn outside impl block)

All 2,746 sub-crate tests + 951 ml tests pass.
Full workspace builds clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 15:17:22 +01:00

286 lines
10 KiB
Rust

#![deny(clippy::unwrap_used, clippy::expect_used)]
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(missing_debug_implementations)]
#![allow(unused_crate_dependencies)]
#![allow(clippy::float_arithmetic)]
#![allow(clippy::non_ascii_literal)]
#![allow(clippy::str_to_string)]
#![allow(clippy::partial_pub_fields)]
#![allow(clippy::multiple_inherent_impl)]
#![allow(clippy::same_name_method)]
#![allow(clippy::shadow_reuse)]
#![allow(clippy::shadow_unrelated)]
#![allow(clippy::shadow_same)]
#![allow(clippy::doc_markdown)]
#![allow(clippy::indexing_slicing)]
#![allow(clippy::missing_const_for_fn)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::integer_division)]
#![allow(clippy::cognitive_complexity)]
#![allow(clippy::similar_names)]
#![allow(clippy::clone_on_ref_ptr)]
#![allow(clippy::too_many_lines)]
#![allow(clippy::as_conversions)]
#![allow(clippy::cast_precision_loss)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::default_numeric_fallback)]
#![allow(clippy::arithmetic_side_effects)]
#![allow(clippy::needless_range_loop)]
#![allow(clippy::into_iter_on_ref)]
#![allow(clippy::new_without_default)]
#![allow(clippy::manual_let_else)]
#![allow(clippy::unnecessary_wraps)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::cast_sign_loss)]
#![allow(clippy::cast_possible_wrap)]
#![allow(clippy::cast_lossless)]
#![allow(clippy::unused_async)]
#![allow(clippy::match_same_arms)]
#![allow(clippy::unused_self)]
#![allow(clippy::map_err_ignore)]
#![allow(clippy::single_match_else)]
#![allow(clippy::wildcard_imports)]
#![allow(clippy::unnecessary_cast)]
#![allow(clippy::undocumented_unsafe_blocks)]
#![allow(clippy::redundant_clone)]
#![allow(clippy::redundant_closure)]
#![allow(clippy::type_complexity)]
#![allow(clippy::manual_clamp)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::should_implement_trait)]
#![allow(clippy::derivable_impls)]
#![allow(clippy::useless_conversion)]
#![allow(clippy::get_first)]
#![allow(clippy::len_zero)]
#![allow(clippy::assign_op_pattern)]
#![allow(clippy::if_same_then_else)]
#![allow(clippy::unused_enumerate_index)]
#![allow(clippy::doc_lazy_continuation)]
#![allow(clippy::doc_overindented_list_items)]
#![allow(clippy::single_char_add_str)]
#![allow(clippy::let_and_return)]
#![allow(clippy::useless_format)]
#![allow(clippy::manual_div_ceil)]
#![allow(clippy::io_other_error)]
#![allow(clippy::manual_range_contains)]
#![allow(clippy::unwrap_or_default)]
#![allow(clippy::used_underscore_binding)]
#![allow(clippy::trivially_copy_pass_by_ref)]
#![allow(clippy::needless_borrows_for_generic_args)]
#![allow(clippy::needless_borrow)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::module_inception)]
#![allow(clippy::if_not_else)]
//! # Neural Risk Management System
//!
//! Advanced ML-driven risk management with neural VaR models, Kelly criterion optimization,
//! and real-time regime detection for production HFT systems using canonical types.
pub mod circuit_breakers;
pub mod graph_risk_model;
pub mod kelly_optimizer;
pub mod kelly_position_sizing_service;
pub mod position_sizing;
pub mod slippage;
pub mod var_models;
// Re-export key types from submodules
pub use circuit_breakers::MLCircuitBreaker;
pub use kelly_optimizer::{
KellyCriterionOptimizer, KellyOptimizerConfig, KellyPositionRecommendation,
};
pub use position_sizing::PositionSizingNetwork;
pub use slippage::{FixedCostModel, LinearImpactConfig, LinearImpactModel, MarketImpactModel};
pub use var_models::{NeuralVarConfig, NeuralVarModel};
// Re-export core ML types used throughout risk modules
pub use ml_core::{MLError, MLResult, MarketDataSnapshot};
use std::collections::HashMap;
use chrono::{DateTime, Utc};
use ndarray::Array2;
use serde::{Deserialize, Serialize};
use common::{Price, Volume};
// AssetId type for risk management
#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
pub struct AssetId(String);
/// Risk assessment levels
/// RiskLevel component.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd)]
pub enum RiskLevel {
VeryLow,
Low,
Medium,
High,
VeryHigh,
Critical,
}
/// Portfolio risk profile
/// RiskProfile component.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskProfile {
pub total_var_95: f64, // 1-day 95% VaR
pub total_var_99: f64, // 1-day 99% VaR
pub expected_shortfall: f64, // Expected tail loss
pub maximum_drawdown: f64, // Historical maximum drawdown
pub current_drawdown: f64, // Current drawdown from peak
pub sharpe_ratio: f64, // Risk-adjusted return
pub sortino_ratio: f64, // Downside risk-adjusted return
pub calmar_ratio: f64, // Return over maximum drawdown
pub beta: f64, // Market beta
pub tracking_error: f64, // Volatility vs benchmark
pub concentration_risk: f64, // Single position concentration
pub correlation_risk: f64, // Average correlation risk
pub liquidity_risk: f64, // Liquidity-adjusted risk
pub regime_risk: f64, // Market regime risk factor
pub overall_risk_score: f64, // Combined ML risk score (0-1)
pub risk_level: RiskLevel, // Categorical risk assessment
pub timestamp: DateTime<Utc>,
}
/// Position-level risk metrics
/// PositionRisk component.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PositionRisk {
pub asset_id: AssetId,
pub position_size: f64, // Current position size
pub market_value: f64, // Current market value
pub var_contribution: f64, // Contribution to portfolio VaR
pub marginal_var: f64, // Marginal VaR (change in portfolio VaR)
pub component_var: f64, // Component VaR (allocation of portfolio VaR)
pub standalone_var: f64, // Position VaR in isolation
pub beta_to_portfolio: f64, // Beta relative to portfolio
pub correlation_risk: f64, // Correlation with other positions
pub liquidity_horizon: f64, // Days to liquidate position
pub concentration_weight: f64, // Weight in portfolio
pub stress_loss: f64, // Loss under stress scenarios
pub kelly_optimal_size: f64, // Kelly optimal position size
pub recommended_size: f64, // ML recommended position size
pub risk_score: f64, // Individual risk score (0-1)
pub timestamp: DateTime<Utc>,
}
/// `Market` data for risk calculations
/// MarketData component.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketData {
pub timestamp: DateTime<Utc>,
pub prices: HashMap<AssetId, Price>,
pub volumes: HashMap<AssetId, Volume>,
pub volatilities: HashMap<AssetId, f64>,
pub correlations: Array2<f64>,
pub market_cap_weights: HashMap<AssetId, f64>,
pub sector_exposures: HashMap<String, f64>,
}
/// Risk limits and constraints
/// RiskLimits component.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskLimits {
pub max_portfolio_var: f64, // Maximum portfolio VaR
pub max_position_size: f64, // Maximum single position size
pub max_sector_exposure: f64, // Maximum sector exposure
pub max_correlation: f64, // Maximum position correlation
pub max_drawdown: f64, // Maximum allowed drawdown
pub min_liquidity_days: f64, // Minimum liquidity (days to exit)
pub max_leverage: f64, // Maximum portfolio leverage
pub var_limit_buffer: f64, // VaR limit buffer (e.g., 0.8 of limit)
}
impl Default for RiskLimits {
fn default() -> Self {
Self {
max_portfolio_var: 0.02, // 2% daily VaR limit
max_position_size: 0.05, // 5% maximum position size
max_sector_exposure: 0.20, // 20% maximum sector exposure
max_correlation: 0.70, // 70% maximum correlation
max_drawdown: 0.10, // 10% maximum drawdown
min_liquidity_days: 2.0, // 2 days maximum liquidation time
max_leverage: 3.0, // 3x maximum leverage
var_limit_buffer: 0.80, // Use 80% of VaR limit
}
}
}
/// Comprehensive risk configuration
/// RiskConfig component.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskConfig {
pub var_confidence_levels: Vec<f64>,
pub lookback_days: usize,
pub monte_carlo_simulations: usize,
pub stress_scenarios: usize,
pub regime_detection_window: usize,
pub update_frequency_seconds: u32,
pub enable_neural_var: bool,
pub enable_regime_detection: bool,
pub enable_ml_position_sizing: bool,
pub enable_dynamic_hedging: bool,
pub risk_limits: RiskLimits,
}
impl Default for RiskConfig {
fn default() -> Self {
Self {
var_confidence_levels: vec![0.95, 0.99],
lookback_days: 252,
monte_carlo_simulations: 10_000,
stress_scenarios: 1_000,
regime_detection_window: 60,
update_frequency_seconds: 60,
enable_neural_var: true,
enable_regime_detection: true,
enable_ml_position_sizing: true,
enable_dynamic_hedging: true,
risk_limits: RiskLimits::default(),
}
}
}
/// Main neural risk management system
#[derive(Debug)]
pub struct NeuralRiskManager {
config: RiskConfig,
var_model: NeuralVarModel,
kelly_optimizer: KellyCriterionOptimizer,
position_sizer: PositionSizingNetwork,
circuit_breaker: MLCircuitBreaker,
}
impl NeuralRiskManager {
/// Create new neural risk management system
pub fn new(config: RiskConfig) -> MLResult<Self> {
let var_config = NeuralVarConfig {
confidence_levels: config.var_confidence_levels.clone(),
lookback_days: config.lookback_days,
lookback_period: config.lookback_days,
monte_carlo_simulations: config.monte_carlo_simulations,
enable_stress_testing: true,
hidden_layers: vec![128, 64, 32],
};
let var_model = NeuralVarModel::new(var_config)?;
let kelly_optimizer = KellyCriterionOptimizer::new(Default::default())?;
let position_sizer = PositionSizingNetwork::new(Default::default())?;
let circuit_breaker = MLCircuitBreaker::new(Default::default())?;
Ok(Self {
config,
var_model,
kelly_optimizer,
position_sizer,
circuit_breaker,
})
}
}