Files
foxhunt/crates/ml-ensemble/src/aggregator.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

107 lines
2.6 KiB
Rust

//! High-performance signal aggregation with SIMD optimization
//!
//! Ensemble aggregation for ML model predictions.
use std::collections::HashMap;
use std::sync::Arc;
use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use crate::confidence::ConfidenceCalculator;
use crate::weights::ModelWeights;
use crate::MLError;
/// Model signal structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelSignal {
pub model_id: String,
pub signal: f32,
pub confidence: f32,
pub timestamp: u64,
pub metadata: SignalMetadata,
}
/// Signal metadata
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SignalMetadata {
pub model_version: String,
pub features_used: Vec<String>,
pub prediction_horizon: u32,
}
/// Signal statistics
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SignalStatistics {
pub total_signals: u64,
pub avg_confidence: f32,
pub accuracy_rate: f32,
pub last_updated: u64,
}
/// Signal aggregator configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AggregatorConfig {
pub max_signals: usize,
pub confidence_threshold: f32,
pub enable_simd: bool,
}
impl Default for AggregatorConfig {
fn default() -> Self {
Self {
max_signals: 100,
confidence_threshold: 0.5,
enable_simd: true,
}
}
}
/// Main signal aggregator
#[derive(Debug)]
pub struct SignalAggregator {
config: AggregatorConfig,
weights: ModelWeights,
confidence_calc: ConfidenceCalculator,
stats: Arc<RwLock<HashMap<String, SignalStatistics>>>,
}
impl SignalAggregator {
pub fn new(
config: AggregatorConfig,
weights: ModelWeights,
confidence_calc: ConfidenceCalculator,
) -> Self {
Self {
config,
weights,
confidence_calc,
stats: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn aggregate_signals(&self, signals: Vec<ModelSignal>) -> Result<f32, MLError> {
if signals.is_empty() {
return Ok(0.0);
}
// Simple weighted average for now
let mut weighted_sum = 0.0;
let mut total_weight = 0.0;
for signal in &signals {
if signal.confidence >= self.config.confidence_threshold {
let weight = 1.0; // Simplified - should use actual weights
weighted_sum += signal.signal * weight;
total_weight += weight;
}
}
if total_weight > 0.0 {
Ok(weighted_sum / total_weight)
} else {
Ok(0.0)
}
}
}