Removed across 66 files:
- 49 instances of "// use crate::safe_operations; // DISABLED"
- 11 instances of "// use error_handling::{...}; // crate doesn't exist"
- 2 instances of "// use crate::Optimizer; // not available"
- 5 disabled test placeholder blocks (/* ... */) in ensemble/
- 1 disabled From impl in lib.rs (38 lines)
- 1 disabled test module in model.rs (113 lines)
- 1 disabled code block in integration/distillation.rs (41 lines)
- Various other disabled imports with explanation comments
All of this code references modules/crates that were removed during
prior refactoring waves and is preserved in git history. Removing it
reduces noise and makes the codebase easier to navigate.
1922 lib tests passing, compilation clean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2248 lines
73 KiB
Rust
2248 lines
73 KiB
Rust
#![allow(missing_docs)] // Internal implementation details don't require documentation
|
|
#![allow(missing_debug_implementations)] // Not all types need Debug
|
|
#![allow(dead_code)] // Many utility functions are defined for future use
|
|
#![allow(unused_crate_dependencies)] // Dev dependencies not used in lib.rs
|
|
#![allow(clippy::float_arithmetic)] // ML operations require float arithmetic
|
|
#![recursion_limit = "256"] // Required for complex TFT quantile operations
|
|
//! Machine Learning Models for Foxhunt
|
|
//!
|
|
//! This crate provides comprehensive machine learning models and algorithms
|
|
//! for the Foxhunt high-frequency trading system. All ML operations use
|
|
//! enterprise-grade safety controls to prevent system failures.
|
|
//!
|
|
//! ## Safety Features
|
|
//!
|
|
//! - **Comprehensive mathematical safety**: All operations handle NaN/Infinity gracefully
|
|
//! - **Tensor bounds checking**: Prevents buffer overflows and memory issues
|
|
//! - **Model drift detection**: Automatic monitoring of model performance degradation
|
|
//! - **Financial validation**: Ensures all predictions use unified financial types
|
|
//! - **Memory management**: Prevents OOM conditions and memory leaks
|
|
//! - **Timeout handling**: Prevents hanging operations
|
|
//!
|
|
//! ## Usage
|
|
//!
|
|
//! ```no_run
|
|
//! // ML safety manager usage example
|
|
//! // Note: This is a conceptual example - actual implementation may vary
|
|
//! use ml::safety::MLSafetyConfig;
|
|
//!
|
|
//! #[tokio::main]
|
|
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
//! // Initialize safety with custom configuration
|
|
//! let _config = MLSafetyConfig::default();
|
|
//!
|
|
//! // ML operations would use the safety manager
|
|
//! // (actual implementation details depend on the safety module structure)
|
|
//! Ok(())
|
|
//! }
|
|
//! ```
|
|
|
|
#![warn(missing_debug_implementations)]
|
|
#![warn(rust_2018_idioms)]
|
|
#![deny(
|
|
clippy::unwrap_used,
|
|
clippy::expect_used,
|
|
clippy::panic,
|
|
clippy::unimplemented,
|
|
clippy::unreachable,
|
|
clippy::indexing_slicing
|
|
)]
|
|
|
|
// Import common types properly - NO ALIASES THAT CONFLICT!
|
|
use candle_core::Tensor;
|
|
use candle_core::Var;
|
|
use candle_nn::Optimizer; // For Adam optimizer support
|
|
use serde::{Deserialize, Serialize}; // For tensor variables
|
|
|
|
// Silence unused crate warnings for dependencies used in tests or feature-gated code
|
|
use approx as _;
|
|
use bincode as _;
|
|
use half as _;
|
|
use memmap2 as _;
|
|
use num as _;
|
|
use num_traits as _;
|
|
use semver as _;
|
|
use tempfile as _;
|
|
use trading_engine as _;
|
|
|
|
/// Wrapper for Adam optimizer to provide required methods
|
|
///
|
|
/// This wrapper provides a unified interface around the candle_optimisers Adam optimizer,
|
|
/// ensuring consistent behavior across the ML crate and providing additional convenience methods.
|
|
/// Adam is an adaptive learning rate optimization algorithm that computes individual learning
|
|
/// rates for different parameters from estimates of first and second moments of the gradients.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust,no_run
|
|
/// use ml::Adam;
|
|
/// use candle_core::Var;
|
|
/// use candle_optimisers::adam::ParamsAdam;
|
|
///
|
|
/// let vars = vec![]; // Your model variables
|
|
/// let params = ParamsAdam::default();
|
|
/// let optimizer = Adam::new(vars, params)?;
|
|
/// # Ok::<(), ml::MLError>(())
|
|
/// ```
|
|
#[derive(Debug)]
|
|
pub struct Adam {
|
|
optimizer: candle_optimisers::adam::Adam,
|
|
learning_rate: f64,
|
|
vars: Vec<Var>,
|
|
}
|
|
|
|
impl Adam {
|
|
/// Create a new Adam optimizer with the given variables and parameters
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `vars` - Vector of model variables to optimize
|
|
/// * `params` - Adam optimizer parameters including learning rate, betas, and epsilon
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Returns `Ok(Adam)` on success, or `Err(MLError::TrainingError)` if optimizer creation fails
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// This function will return an error if the underlying candle Adam optimizer fails to initialize
|
|
pub fn new(
|
|
vars: Vec<Var>,
|
|
params: candle_optimisers::adam::ParamsAdam,
|
|
) -> Result<Self, MLError> {
|
|
let learning_rate = params.lr;
|
|
let optimizer = candle_optimisers::adam::Adam::new(vars.clone(), params).map_err(|e| {
|
|
MLError::TrainingError(format!("Failed to create Adam optimizer: {}", e))
|
|
})?;
|
|
|
|
Ok(Self {
|
|
optimizer,
|
|
learning_rate,
|
|
vars,
|
|
})
|
|
}
|
|
|
|
/// Perform a backward pass and optimizer step
|
|
///
|
|
/// This method computes gradients via backpropagation and then applies the Adam
|
|
/// optimization update to all registered variables.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `loss` - The loss tensor to compute gradients from
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Returns `Ok(())` on successful optimization step, or `Err(MLError::TrainingError)` on failure
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// This function will return an error if:
|
|
/// - The backward pass fails to compute gradients
|
|
/// - The optimizer step fails to apply updates
|
|
pub fn backward_step(&mut self, loss: &Tensor) -> Result<(), MLError> {
|
|
// Calculate gradients
|
|
let grads = loss
|
|
.backward()
|
|
.map_err(|e| MLError::TrainingError(format!("Backward pass failed: {}", e)))?;
|
|
|
|
// Apply optimizer step using trait method
|
|
Optimizer::step(&mut self.optimizer, &grads)
|
|
.map_err(|e| MLError::TrainingError(format!("Optimizer step failed: {}", e)))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Get the learning rate used by this optimizer
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Returns the learning rate as a 64-bit floating point number
|
|
pub fn learning_rate(&self) -> f64 {
|
|
self.learning_rate
|
|
}
|
|
|
|
/// Get a reference to the variables tracked by this optimizer
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Returns a slice reference to the vector of variables
|
|
pub fn vars(&self) -> &[Var] {
|
|
&self.vars
|
|
}
|
|
|
|
/// Perform backward pass with gradient clipping
|
|
///
|
|
/// Implements proper gradient clipping by norm to prevent gradient explosions.
|
|
/// Uses a two-pass approach: first pass computes gradient norm, second pass
|
|
/// (if needed) computes clipped gradients by scaling the loss.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `loss` - The loss tensor to compute gradients from
|
|
/// * `max_norm` - Maximum allowed gradient norm (gradients will be clipped to this value)
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Returns `Ok(gradient_norm)` on success with the actual gradient norm (before clipping),
|
|
/// or `Err(MLError::TrainingError)` on failure
|
|
pub fn backward_step_with_monitoring(
|
|
&mut self,
|
|
loss: &Tensor,
|
|
max_norm: f64,
|
|
) -> Result<f64, MLError> {
|
|
// Bug fix: Single backward pass to prevent gradient accumulation
|
|
// Root cause: Two backward passes caused 1.5x gradient amplification
|
|
// Solution: Compute gradients once, then scale loss before optimizer step if needed
|
|
|
|
// 1. Compute gradients via backward pass
|
|
let mut grads = loss
|
|
.backward()
|
|
.map_err(|e| MLError::TrainingError(format!("Backward pass failed: {}", e)))?;
|
|
|
|
// 2. Apply gradient clipping IN-PLACE (Bug #32 fix - gradient explosion)
|
|
// This modifies the grads directly before optimizer step
|
|
let (actual_grad_norm, clipped_grad_norm) = crate::gradient_utils::clip_grad_norm(&self.vars, &mut grads, max_norm)
|
|
.map_err(|e| MLError::TrainingError(format!("Gradient clipping failed: {}", e)))?;
|
|
|
|
// BUG #14 FIX: Log actual gradient norms to detect if threshold is too low
|
|
// Temporary diagnostic logging to investigate gradient clipping issue
|
|
static GRAD_LOG_COUNTER: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
|
|
let count = GRAD_LOG_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
|
if count < 20 || count % 100 == 0 {
|
|
tracing::info!(
|
|
"BUG #14 DIAGNOSTIC: Gradient norm BEFORE clipping: {:.4}, AFTER clipping: {:.4}, max_norm: {:.4}",
|
|
actual_grad_norm,
|
|
clipped_grad_norm,
|
|
max_norm
|
|
);
|
|
}
|
|
|
|
// 3. Apply optimizer step with clipped gradients
|
|
Optimizer::step(&mut self.optimizer, &grads)
|
|
.map_err(|e| MLError::TrainingError(format!("Optimizer step failed: {}", e)))?;
|
|
|
|
if actual_grad_norm > max_norm {
|
|
tracing::warn!(
|
|
"Gradient clipping: {:.4} -> {:.4} - this is expected occasionally but should be rare. \
|
|
If frequent, consider reducing learning rate.",
|
|
actual_grad_norm,
|
|
clipped_grad_norm
|
|
);
|
|
}
|
|
|
|
Ok(clipped_grad_norm)
|
|
}
|
|
|
|
/// Compute the L2 norm of all gradients
|
|
fn compute_gradient_norm(
|
|
&self,
|
|
grads: &candle_core::backprop::GradStore,
|
|
) -> Result<f64, MLError> {
|
|
let mut total_norm_sq = 0.0f64;
|
|
|
|
// Get all variables from the optimizer
|
|
for var in &self.vars {
|
|
if let Some(grad) = grads.get(var) {
|
|
// Compute L2 norm squared for this gradient
|
|
let grad_norm_sq = grad
|
|
.sqr()
|
|
.map_err(|e| {
|
|
MLError::TrainingError(format!("Failed to square gradient: {}", e))
|
|
})?
|
|
.sum_all()
|
|
.map_err(|e| MLError::TrainingError(format!("Failed to sum gradient: {}", e)))?
|
|
.to_vec0::<f32>()
|
|
.map_err(|e| {
|
|
MLError::TrainingError(format!("Failed to extract gradient norm: {}", e))
|
|
})? as f64;
|
|
|
|
total_norm_sq += grad_norm_sq;
|
|
}
|
|
}
|
|
|
|
Ok(total_norm_sq.sqrt())
|
|
}
|
|
}
|
|
|
|
// Direct type imports - no compatibility aliases
|
|
use rust_decimal::Decimal;
|
|
|
|
/// Common type errors that can occur during ML operations
|
|
///
|
|
/// This enum represents various type-related errors that can occur when working
|
|
/// with different data types across the ML pipeline, including type conversions,
|
|
/// validation errors, and compatibility issues.
|
|
#[derive(Debug, Clone, thiserror::Error, serde::Serialize, serde::Deserialize)]
|
|
pub enum CommonTypeError {
|
|
/// Generic type error with descriptive message
|
|
#[error("Type error: {0}")]
|
|
Error(String),
|
|
}
|
|
|
|
/// Market regime classification for algorithmic trading strategies
|
|
///
|
|
/// This enum represents different market conditions that can be detected through
|
|
/// statistical analysis and machine learning models. Market regime detection is
|
|
/// crucial for adaptive trading strategies that adjust their behavior based on
|
|
/// current market conditions.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// use ml::MarketRegime;
|
|
///
|
|
/// let regime = MarketRegime::Trending;
|
|
/// match regime {
|
|
/// MarketRegime::Bull => println!("Use momentum strategies"),
|
|
/// MarketRegime::Bear => println!("Use defensive strategies"),
|
|
/// MarketRegime::Crisis => println!("Implement risk controls"),
|
|
/// _ => println!("Use balanced approach"),
|
|
/// }
|
|
/// ```
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub enum MarketRegime {
|
|
/// Normal market conditions with typical volatility and volume
|
|
Normal,
|
|
/// Strong directional movement with clear trends
|
|
Trending,
|
|
/// Range-bound market with limited directional movement
|
|
Sideways,
|
|
/// Bullish market with rising prices and positive sentiment
|
|
Bull,
|
|
/// Bearish market with falling prices and negative sentiment
|
|
Bear,
|
|
/// Crisis conditions with extreme volatility and risk
|
|
Crisis,
|
|
}
|
|
|
|
/// Common errors that can occur across the ML system
|
|
///
|
|
/// This enum provides a unified error type that can be used throughout the ML
|
|
/// pipeline to ensure consistent error handling and reporting. It serves as a
|
|
/// bridge between different subsystems and provides appropriate error categorization.
|
|
#[derive(Debug, Clone, thiserror::Error, serde::Serialize, serde::Deserialize)]
|
|
pub enum CommonError {
|
|
/// General error with descriptive message
|
|
#[error("Error: {0}")]
|
|
General(String),
|
|
}
|
|
|
|
impl CommonError {
|
|
/// Create a validation error with a descriptive message
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `msg` - A descriptive message explaining the validation failure
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Returns a `CommonError::General` variant with the validation message
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// use ml::CommonError;
|
|
///
|
|
/// let error = CommonError::validation("Invalid input range");
|
|
/// assert!(error.to_string().contains("Invalid input range"));
|
|
/// ```
|
|
pub fn validation(msg: impl Into<String>) -> Self {
|
|
Self::General(msg.into())
|
|
}
|
|
|
|
/// Create a configuration error with a descriptive message
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `msg` - A descriptive message explaining the configuration issue
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Returns a `CommonError::General` variant with the configuration message
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// use ml::CommonError;
|
|
///
|
|
/// let error = CommonError::config("Missing required parameter");
|
|
/// assert!(error.to_string().contains("Missing required parameter"));
|
|
/// ```
|
|
pub fn config(msg: impl Into<String>) -> Self {
|
|
Self::General(msg.into())
|
|
}
|
|
|
|
/// Create a service error with category and descriptive message
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `category` - The error category to classify the service error
|
|
/// * `msg` - A descriptive message explaining the service issue
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Returns a `CommonError::General` variant with the categorized service message
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// use ml::{CommonError, ErrorCategory};
|
|
///
|
|
/// let error = CommonError::service(ErrorCategory::System, "Database connection failed");
|
|
/// assert!(error.to_string().contains("System"));
|
|
/// assert!(error.to_string().contains("Database connection failed"));
|
|
/// ```
|
|
pub fn service(category: ErrorCategory, msg: impl Into<String>) -> Self {
|
|
Self::General(format!("{:?}: {}", category, msg.into()))
|
|
}
|
|
}
|
|
|
|
/// Error categories for system-wide error classification
|
|
///
|
|
/// This enum provides a way to categorize errors across the entire system,
|
|
/// enabling better error handling, logging, and monitoring strategies.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
pub enum ErrorCategory {
|
|
/// System-level errors including hardware, network, and infrastructure issues
|
|
System,
|
|
}
|
|
|
|
// Now using real types from common crate
|
|
|
|
// Core ML types
|
|
/// Represents a financial trade for ML model training and analysis
|
|
///
|
|
/// This structure contains the essential information about a trade that is used
|
|
/// by ML models for pattern recognition, market analysis, and strategy optimization.
|
|
/// The structure is optimized for both in-memory processing and database storage.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// use ml::Trade;
|
|
/// use rust_decimal::Decimal;
|
|
///
|
|
/// let trade = Trade {
|
|
/// symbol: "AAPL".to_string(),
|
|
/// price: Decimal::new(15000, 2), // $150.00
|
|
/// quantity: Decimal::new(100, 0), // 100 shares
|
|
/// timestamp: 1640995200000000, // microseconds since epoch
|
|
/// side: "buy".to_string(),
|
|
/// };
|
|
/// ```
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Trade {
|
|
/// Trading symbol (e.g., "AAPL", "MSFT")
|
|
pub symbol: String,
|
|
/// Trade price in decimal format for precision
|
|
pub price: Decimal,
|
|
/// Trade quantity in decimal format for precision
|
|
pub quantity: Decimal,
|
|
/// Timestamp in microseconds since Unix epoch
|
|
pub timestamp: u64,
|
|
/// Trade side: "buy" or "sell"
|
|
pub side: String,
|
|
}
|
|
|
|
/// Health status for ensemble models and ML system components
|
|
///
|
|
/// This enum tracks the operational status of ML models and system components,
|
|
/// enabling automated health monitoring, alerting, and failover mechanisms.
|
|
/// Health status is crucial for maintaining system reliability in production.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// use ml::HealthStatus;
|
|
///
|
|
/// let status = HealthStatus::Healthy;
|
|
/// match status {
|
|
/// HealthStatus::Healthy => println!("System operating normally"),
|
|
/// HealthStatus::Degraded => println!("Performance below optimal"),
|
|
/// HealthStatus::Unhealthy => println!("System requires intervention"),
|
|
/// }
|
|
/// ```
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
pub enum HealthStatus {
|
|
/// Component is operating within normal parameters
|
|
Healthy,
|
|
/// Component is operational but performance is below optimal
|
|
Degraded,
|
|
/// Component is not functioning properly and requires intervention
|
|
Unhealthy,
|
|
}
|
|
|
|
// Import specific types from trading_engine that we need
|
|
// (removed wildcard prelude to avoid conflicts)
|
|
|
|
// Using Decimal for financial types
|
|
|
|
/// Market data snapshot for ML model input
|
|
///
|
|
/// Represents a point-in-time snapshot of market data that serves as input
|
|
/// for ML models. This structure contains the essential market information
|
|
/// needed for real-time trading decisions and model inference.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// use ml::MarketDataSnapshot;
|
|
/// use rust_decimal::Decimal;
|
|
/// use chrono::Utc;
|
|
///
|
|
/// let snapshot = MarketDataSnapshot {
|
|
/// timestamp: Utc::now(),
|
|
/// symbol: "AAPL".to_string(),
|
|
/// price: Decimal::new(15000, 2), // $150.00
|
|
/// volume: Decimal::new(1000000, 0), // 1M shares
|
|
/// };
|
|
/// ```
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct MarketDataSnapshot {
|
|
/// Timestamp of the market data snapshot
|
|
pub timestamp: DateTime<Utc>,
|
|
/// Trading symbol (e.g., "AAPL", "MSFT")
|
|
pub symbol: String,
|
|
/// Current market price
|
|
pub price: Decimal,
|
|
/// Trading volume at this timestamp
|
|
pub volume: Decimal,
|
|
}
|
|
|
|
/// Feature vector for ML model input
|
|
///
|
|
/// A wrapper around a vector of f64 values that represents extracted features
|
|
/// for machine learning models. Features are numerical representations of
|
|
/// market data, indicators, and other relevant information.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// use ml::FeatureVector;
|
|
///
|
|
/// let features = FeatureVector(vec![1.0, 2.5, -0.3, 4.2]);
|
|
/// assert_eq!(features.0.len(), 4);
|
|
/// ```
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct FeatureVector(pub Vec<f64>);
|
|
|
|
impl FeatureVector {
|
|
/// Get the length of the feature vector
|
|
pub fn len(&self) -> usize {
|
|
self.0.len()
|
|
}
|
|
|
|
/// Check if the feature vector is empty
|
|
pub fn is_empty(&self) -> bool {
|
|
self.0.is_empty()
|
|
}
|
|
}
|
|
|
|
/// Integer tensor for discrete ML model operations
|
|
///
|
|
/// A wrapper around a vector of i64 values used for discrete operations
|
|
/// such as classification labels, indices, and categorical data in ML models.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// use ml::IntegerTensor;
|
|
///
|
|
/// let tensor = IntegerTensor(vec![0, 1, 2, 1, 0]);
|
|
/// assert_eq!(tensor.0.len(), 5);
|
|
/// ```
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct IntegerTensor(pub Vec<i64>);
|
|
|
|
/// Summary of model update operations
|
|
///
|
|
/// Provides information about batch update operations on ML models,
|
|
/// including success counts and overall statistics. Used for monitoring
|
|
/// and logging model maintenance operations.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust
|
|
/// use ml::UpdateSummary;
|
|
///
|
|
/// let summary = UpdateSummary {
|
|
/// updated_models: 5,
|
|
/// total_models: 10,
|
|
/// };
|
|
///
|
|
/// let success_rate = summary.updated_models as f64 / summary.total_models as f64;
|
|
/// println!("Update success rate: {:.1}%", success_rate * 100.0);
|
|
/// ```
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct UpdateSummary {
|
|
/// Number of models successfully updated
|
|
pub updated_models: usize,
|
|
/// Total number of models in the update operation
|
|
pub total_models: usize,
|
|
}
|
|
|
|
use thiserror::Error;
|
|
|
|
/// Machine Learning specific errors
|
|
#[derive(Debug, Clone, Error, Serialize, Deserialize)]
|
|
pub enum MLError {
|
|
/// Configuration error
|
|
#[error("Configuration error: {reason}")]
|
|
ConfigError { reason: String },
|
|
|
|
/// Configuration error (alternative naming)
|
|
#[error("Configuration error: {0}")]
|
|
ConfigurationError(String),
|
|
|
|
/// Dimension mismatch error
|
|
#[error("Dimension mismatch: expected {expected}, got {actual}")]
|
|
DimensionMismatch { expected: usize, actual: usize },
|
|
|
|
/// Graph-related error
|
|
#[error("Graph error: {message}")]
|
|
GraphError { message: String },
|
|
|
|
/// Resource limit exceeded
|
|
#[error("Resource limit exceeded: {resource} limit {limit}")]
|
|
ResourceLimit { resource: String, limit: usize },
|
|
|
|
/// Serialization error
|
|
#[error("Serialization error: {reason}")]
|
|
SerializationError { reason: String },
|
|
|
|
/// Validation error
|
|
#[error("Validation error: {message}")]
|
|
ValidationError { message: String },
|
|
|
|
/// Concurrency error
|
|
#[error("Concurrency error in operation: {operation}")]
|
|
ConcurrencyError { operation: String },
|
|
|
|
/// Invalid input error
|
|
#[error("Invalid input: {0}")]
|
|
InvalidInput(String),
|
|
|
|
/// Initialization error
|
|
#[error("Initialization error in {component}: {message}")]
|
|
InitializationError { component: String, message: String },
|
|
|
|
/// Training error
|
|
#[error("Training error: {0}")]
|
|
TrainingError(String),
|
|
|
|
/// Inference error
|
|
#[error("Inference error: {0}")]
|
|
InferenceError(String),
|
|
|
|
/// Model error
|
|
#[error("Model error: {0}")]
|
|
ModelError(String),
|
|
|
|
/// Model not trained error
|
|
#[error("Model not trained: {0}")]
|
|
NotTrained(String),
|
|
|
|
/// Anyhow error wrapping
|
|
#[error("General error: {0}")]
|
|
AnyhowError(String),
|
|
|
|
/// Tensor creation error
|
|
#[error("Tensor creation error in {operation}: {reason}")]
|
|
TensorCreationError { operation: String, reason: String },
|
|
|
|
/// Tensor operation error
|
|
#[error("Tensor operation error: {0}")]
|
|
TensorOperationError(String),
|
|
|
|
/// Lock error
|
|
#[error("Lock error: {0}")]
|
|
LockError(String),
|
|
|
|
/// Model not found error
|
|
#[error("Model not found: {0}")]
|
|
ModelNotFound(String),
|
|
|
|
/// Insufficient data error
|
|
#[error("Insufficient data: {0}")]
|
|
InsufficientData(String),
|
|
|
|
/// Checkpoint error
|
|
#[error("Checkpoint error: {0}")]
|
|
CheckpointError(String),
|
|
}
|
|
|
|
// Implement From trait for candle_core::Error
|
|
impl From<candle_core::Error> for MLError {
|
|
fn from(err: candle_core::Error) -> Self {
|
|
MLError::ModelError(format!("Candle error: {}", err))
|
|
}
|
|
}
|
|
|
|
// Implement From trait for LabelingError
|
|
impl From<labeling::gpu_acceleration::LabelingError> for MLError {
|
|
fn from(err: labeling::gpu_acceleration::LabelingError) -> Self {
|
|
MLError::InferenceError(err.to_string())
|
|
}
|
|
}
|
|
|
|
// Implement From trait for anyhow::Error
|
|
impl From<anyhow::Error> for MLError {
|
|
fn from(err: anyhow::Error) -> Self {
|
|
MLError::AnyhowError(err.to_string())
|
|
}
|
|
}
|
|
|
|
// Implement From trait for std::io::Error
|
|
impl From<std::io::Error> for MLError {
|
|
fn from(err: std::io::Error) -> Self {
|
|
MLError::ModelError(format!("IO error: {}", err))
|
|
}
|
|
}
|
|
|
|
// UNIFIED ERROR HANDLING: Convert all ML errors to CommonError for workspace consistency
|
|
impl From<MLError> for CommonError {
|
|
fn from(err: MLError) -> Self {
|
|
match err {
|
|
MLError::ConfigError { reason } => {
|
|
CommonError::config(format!("ML configuration error: {}", reason))
|
|
},
|
|
MLError::ConfigurationError(msg) => {
|
|
CommonError::config(format!("ML configuration error: {}", msg))
|
|
},
|
|
MLError::InitializationError { component, message } => CommonError::service(
|
|
ErrorCategory::System,
|
|
format!("ML initialization error in {}: {}", component, message),
|
|
),
|
|
MLError::DimensionMismatch { expected, actual } => CommonError::validation(format!(
|
|
"ML dimension mismatch: expected {}, got {}",
|
|
expected, actual
|
|
)),
|
|
MLError::GraphError { message } => CommonError::service(
|
|
ErrorCategory::System,
|
|
format!("ML graph error: {}", message),
|
|
),
|
|
MLError::ResourceLimit { resource, limit } => CommonError::service(
|
|
ErrorCategory::System,
|
|
format!("ML resource limit exceeded: {} limit {}", resource, limit),
|
|
),
|
|
MLError::SerializationError { reason } => CommonError::service(
|
|
ErrorCategory::System,
|
|
format!("ML serialization error: {}", reason),
|
|
),
|
|
MLError::ValidationError { message } => {
|
|
CommonError::validation(format!("ML validation error: {}", message))
|
|
},
|
|
MLError::ConcurrencyError { operation } => CommonError::service(
|
|
ErrorCategory::System,
|
|
format!("ML concurrency error in operation: {}", operation),
|
|
),
|
|
MLError::InvalidInput(msg) => {
|
|
CommonError::validation(format!("ML invalid input: {}", msg))
|
|
},
|
|
MLError::TrainingError(msg) => {
|
|
CommonError::service(ErrorCategory::System, format!("ML training error: {}", msg))
|
|
},
|
|
MLError::InferenceError(msg) => CommonError::service(
|
|
ErrorCategory::System,
|
|
format!("ML inference error: {}", msg),
|
|
),
|
|
MLError::ModelError(msg) => {
|
|
CommonError::service(ErrorCategory::System, format!("ML model error: {}", msg))
|
|
},
|
|
MLError::CheckpointError(msg) => CommonError::service(
|
|
ErrorCategory::System,
|
|
format!("ML checkpoint error: {}", msg),
|
|
),
|
|
MLError::NotTrained(msg) => CommonError::service(
|
|
ErrorCategory::System,
|
|
format!("ML model not trained: {}", msg),
|
|
),
|
|
MLError::AnyhowError(msg) => {
|
|
CommonError::service(ErrorCategory::System, format!("ML error: {}", msg))
|
|
},
|
|
MLError::TensorCreationError { operation, reason } => CommonError::service(
|
|
ErrorCategory::System,
|
|
format!("ML tensor creation error in {}: {}", operation, reason),
|
|
),
|
|
MLError::TensorOperationError(msg) => CommonError::service(
|
|
ErrorCategory::System,
|
|
format!("ML tensor operation error: {}", msg),
|
|
),
|
|
MLError::LockError(msg) => {
|
|
CommonError::service(ErrorCategory::System, format!("ML lock error: {}", msg))
|
|
},
|
|
MLError::ModelNotFound(msg) => CommonError::service(
|
|
ErrorCategory::System,
|
|
format!("ML model not found: {}", msg),
|
|
),
|
|
MLError::InsufficientData(msg) => {
|
|
CommonError::validation(format!("ML insufficient data: {}", msg))
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convert common type errors to MLError
|
|
impl From<CommonTypeError> for MLError {
|
|
fn from(err: CommonTypeError) -> Self {
|
|
MLError::ModelError(format!("Common type error: {}", err))
|
|
}
|
|
}
|
|
|
|
impl From<serde_json::Error> for MLError {
|
|
fn from(err: serde_json::Error) -> Self {
|
|
MLError::SerializationError {
|
|
reason: err.to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<inference::RealInferenceError> for MLError {
|
|
fn from(err: inference::RealInferenceError) -> Self {
|
|
match err {
|
|
inference::RealInferenceError::GpuRequired { reason } => {
|
|
MLError::ModelError(format!("GPU required: {}", reason))
|
|
},
|
|
inference::RealInferenceError::ComputationFailed { reason } => {
|
|
MLError::InferenceError(reason)
|
|
},
|
|
inference::RealInferenceError::FeatureMismatch { expected, actual } => {
|
|
MLError::DimensionMismatch { expected, actual }
|
|
},
|
|
inference::RealInferenceError::PredictionValidation { reason } => {
|
|
MLError::ValidationError { message: reason }
|
|
},
|
|
inference::RealInferenceError::HardwareError { reason } => {
|
|
MLError::ModelError(format!("Hardware error: {}", reason))
|
|
},
|
|
other => MLError::InferenceError(other.to_string()),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Implement From<ProductionTrainingError> for MLError
|
|
impl From<training_pipeline::ProductionTrainingError> for MLError {
|
|
fn from(err: training_pipeline::ProductionTrainingError) -> Self {
|
|
match err {
|
|
training_pipeline::ProductionTrainingError::ConfigError { reason } => {
|
|
MLError::ConfigError { reason }
|
|
},
|
|
training_pipeline::ProductionTrainingError::ArchitectureError { reason } => {
|
|
MLError::ModelError(format!("Architecture error: {}", reason))
|
|
},
|
|
training_pipeline::ProductionTrainingError::DataError { reason } => {
|
|
MLError::ValidationError {
|
|
message: format!("Data error: {}", reason),
|
|
}
|
|
},
|
|
training_pipeline::ProductionTrainingError::OptimizationError { reason } => {
|
|
MLError::TrainingError(format!("Optimization error: {}", reason))
|
|
},
|
|
training_pipeline::ProductionTrainingError::FinancialError { reason } => {
|
|
MLError::ValidationError {
|
|
message: format!("Financial error: {}", reason),
|
|
}
|
|
},
|
|
training_pipeline::ProductionTrainingError::SafetyViolation { reason } => {
|
|
MLError::ValidationError {
|
|
message: format!("Safety violation: {}", reason),
|
|
}
|
|
},
|
|
training_pipeline::ProductionTrainingError::ConvergenceError { reason } => {
|
|
MLError::TrainingError(format!("Convergence error: {}", reason))
|
|
},
|
|
training_pipeline::ProductionTrainingError::ResourceError { reason } => {
|
|
MLError::ModelError(format!("Resource error: {}", reason))
|
|
},
|
|
training_pipeline::ProductionTrainingError::GpuRequired { reason } => {
|
|
MLError::ModelError(format!("GPU required: {}", reason))
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
// Note: From trait for liquid::LiquidError is implemented in the liquid module to avoid conflicts
|
|
|
|
/// Result type for ML operations
|
|
pub type MLResult<T> = Result<T, MLError>;
|
|
|
|
/// New unified result type using CommonError for better integration
|
|
pub type UnifiedMLResult<T> = Result<T, CommonError>;
|
|
|
|
/// Precision factor for fixed-point arithmetic
|
|
pub const PRECISION_FACTOR: i64 = 100_000_000;
|
|
|
|
/// Maximum inference latency target in microseconds
|
|
pub const MAX_INFERENCE_LATENCY_US: u64 = 100;
|
|
|
|
// ========== CORE ML MODULES ==========
|
|
// Core ML modules
|
|
pub mod backtesting; // Backtesting framework for barrier optimization
|
|
pub mod checkpoint;
|
|
pub mod config; // Configuration module for feature extraction
|
|
pub mod cuda_compat; // CUDA-compatible operations (manual sigmoid, etc.)
|
|
pub mod data_loaders; // Data loaders for ML training
|
|
pub mod dqn;
|
|
pub mod gradient_utils; // Gradient utilities (clipping, monitoring)
|
|
pub mod ensemble;
|
|
pub mod evaluation; // DQN evaluation engine (backtest metrics, Sharpe ratio)
|
|
pub mod flash_attention;
|
|
pub mod hyperopt; // Bayesian hyperparameter optimization (egobox)
|
|
pub mod integration;
|
|
pub mod labeling;
|
|
pub mod liquid;
|
|
pub mod mamba;
|
|
pub mod memory_optimization; // Memory optimization utilities (lazy loading, quantization, precision)
|
|
pub mod microstructure;
|
|
pub mod ppo;
|
|
pub mod preprocessing; // Data preprocessing (log returns, normalization, outlier clipping)
|
|
pub mod risk;
|
|
pub mod safety;
|
|
pub mod security; // ML security (prediction validation, anomaly detection)
|
|
pub mod tft;
|
|
pub mod tgnn;
|
|
pub mod tlob;
|
|
|
|
// Re-export quantized TFT types (Wave 9.12)
|
|
pub use tft::{
|
|
QuantizedGatedResidualNetwork, QuantizedLSTMEncoder, QuantizedTemporalAttention,
|
|
QuantizedTemporalFusionTransformer, QuantizedVariableSelectionNetwork,
|
|
};
|
|
pub mod trainers; // ML model trainers with gRPC integration
|
|
pub mod types; // Canonical shared types (OHLCVBar, etc.)
|
|
pub mod transformers;
|
|
pub mod universe;
|
|
|
|
// ============================================================================
|
|
// MANDATORY CUDA TRAINING DEVICE
|
|
// ============================================================================
|
|
//
|
|
// ALL ML training requires CUDA GPU acceleration. CPU fallback wastes time.
|
|
//
|
|
// This module provides a fail-fast device initialization function that:
|
|
// 1. Requires CUDA GPU (no silent CPU fallback)
|
|
// 2. Provides helpful error messages for common setup issues
|
|
// 3. Ensures consistent device initialization across all trainers
|
|
//
|
|
// Training scripts MUST use this function instead of Device::cuda_if_available()
|
|
|
|
/// Get mandatory CUDA device for training
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if CUDA GPU is not available with detailed error message
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```no_run
|
|
/// use ml::get_training_device;
|
|
///
|
|
/// let device = get_training_device(); // Panics if no GPU
|
|
/// ```
|
|
pub fn get_training_device() -> candle_core::Device {
|
|
match candle_core::Device::new_cuda(0) {
|
|
Ok(device) => device,
|
|
Err(e) => {
|
|
panic!(
|
|
"\n\n\
|
|
╔═══════════════════════════════════════════════════════════════════╗\n\
|
|
║ CUDA GPU REQUIRED FOR TRAINING ║\n\
|
|
╚═══════════════════════════════════════════════════════════════════╝\n\
|
|
\n\
|
|
Training requires CUDA GPU acceleration. CPU fallback is disabled.\n\
|
|
\n\
|
|
Error: {}\n\
|
|
\n\
|
|
Troubleshooting:\n\
|
|
\n\
|
|
1. Check GPU availability:\n\
|
|
nvidia-smi\n\
|
|
\n\
|
|
2. Verify CUDA toolkit installation:\n\
|
|
nvcc --version\n\
|
|
\n\
|
|
3. Check CUDA libraries are in LD_LIBRARY_PATH:\n\
|
|
echo $LD_LIBRARY_PATH | grep cuda\n\
|
|
\n\
|
|
4. Ensure project built with CUDA feature:\n\
|
|
cargo build --release --features cuda\n\
|
|
\n\
|
|
5. Check CUDA environment variables:\n\
|
|
echo $CUDA_HOME\n\
|
|
ls $CUDA_HOME/lib64/\n\
|
|
\n\
|
|
If GPU is unavailable, training cannot proceed.\n\
|
|
\n",
|
|
e
|
|
);
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Get CUDA device with index (for multi-GPU setups)
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if CUDA GPU at specified index is not available
|
|
pub fn get_training_device_at(device_id: usize) -> candle_core::Device {
|
|
match candle_core::Device::new_cuda(device_id) {
|
|
Ok(device) => device,
|
|
Err(e) => {
|
|
panic!(
|
|
"\n\n\
|
|
╔═══════════════════════════════════════════════════════════════════╗\n\
|
|
║ CUDA GPU {} NOT AVAILABLE ║\n\
|
|
╚═══════════════════════════════════════════════════════════════════╝\n\
|
|
\n\
|
|
Error: {}\n\
|
|
\n\
|
|
Check available GPUs with: nvidia-smi\n\
|
|
\n",
|
|
device_id, e
|
|
);
|
|
},
|
|
}
|
|
}
|
|
|
|
// ========== INFRASTRUCTURE MODULES ==========
|
|
// Infrastructure
|
|
pub mod benchmark;
|
|
pub mod benchmarks;
|
|
pub mod common;
|
|
pub mod metrics; // Performance metrics (Sharpe ratio, etc.)
|
|
pub mod training;
|
|
|
|
// ========== MODEL DEPLOYMENT AND FACTORY ==========
|
|
pub mod model_factory;
|
|
|
|
// ========== CORE EXPORTS ==========
|
|
// Core exports
|
|
pub mod error;
|
|
pub mod features; // Feature cache and extraction (Parquet + MinIO)
|
|
pub mod feature_cache; // MBP-10 OFI feature caching for hyperopt speedup
|
|
|
|
pub mod inference;
|
|
pub mod model;
|
|
pub mod performance;
|
|
pub mod production;
|
|
pub mod validation;
|
|
|
|
// ========== ADDITIONAL MODULES ==========
|
|
// Additional ML processing modules
|
|
pub mod batch_processing; // Batch processing for ML operations
|
|
pub mod bridge; // Type system bridge for ML-Financial integration
|
|
pub mod portfolio_transformer; // Portfolio-specific transformer
|
|
pub mod regime; // Wave D: Structural breaks and regime classification
|
|
pub mod regime_detection; // Market regime detection
|
|
pub mod tensor_ops;
|
|
pub mod examples;
|
|
pub mod models_demo;
|
|
pub mod observability;
|
|
pub mod qat_metrics_exporter; // QAT Prometheus metrics export
|
|
pub mod stress_testing; // Stress testing framework
|
|
pub mod training_pipeline; // Complete training pipeline system
|
|
pub mod traits; // Common traits for ML models // Production observability and monitoring // Integration with model_loader crate
|
|
|
|
pub mod real_data_loader;
|
|
pub mod data_validation;
|
|
pub mod random_model;
|
|
pub mod model_registry;
|
|
|
|
// ========== MISSING TYPES STUBS ==========
|
|
|
|
/// Application result wrapper for ML operations
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct MLAppResult<T> {
|
|
pub data: T,
|
|
pub success: bool,
|
|
pub message: Option<String>,
|
|
pub execution_time_ms: u64,
|
|
pub metadata: HashMap<String, String>,
|
|
}
|
|
|
|
impl<T> MLAppResult<T> {
|
|
/// Create a successful result
|
|
pub fn success(data: T) -> Self {
|
|
Self {
|
|
data,
|
|
success: true,
|
|
message: None,
|
|
execution_time_ms: 0,
|
|
metadata: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Create a failed result with message
|
|
pub fn error(data: T, message: String) -> Self {
|
|
Self {
|
|
data,
|
|
success: false,
|
|
message: Some(message),
|
|
execution_time_ms: 0,
|
|
metadata: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Set execution time
|
|
pub fn with_timing(mut self, execution_time_ms: u64) -> Self {
|
|
self.execution_time_ms = execution_time_ms;
|
|
self
|
|
}
|
|
|
|
/// Add metadata
|
|
pub fn with_metadata(mut self, key: String, value: String) -> Self {
|
|
self.metadata.insert(key, value);
|
|
self
|
|
}
|
|
}
|
|
|
|
/// Performance profile configuration for HFT models
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct HFTPerformanceProfile {
|
|
pub max_latency_us: u64,
|
|
pub target_throughput: u32,
|
|
pub memory_limit_mb: u64,
|
|
pub cpu_affinity: Option<Vec<usize>>,
|
|
pub gpu_enabled: bool,
|
|
pub batch_size: u32,
|
|
pub optimization_level: OptimizationLevel,
|
|
}
|
|
|
|
/// Optimization levels for HFT performance
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
|
pub enum OptimizationLevel {
|
|
/// Maximum speed, minimal safety checks
|
|
UltraLow,
|
|
/// Balanced speed and safety
|
|
Low,
|
|
/// Standard optimization
|
|
Medium,
|
|
/// Conservative with full validation
|
|
High,
|
|
}
|
|
|
|
impl Default for HFTPerformanceProfile {
|
|
fn default() -> Self {
|
|
Self {
|
|
max_latency_us: 100, // 100 microseconds target
|
|
target_throughput: 10000, // 10k operations per second
|
|
memory_limit_mb: 1024, // 1GB memory limit
|
|
cpu_affinity: None,
|
|
gpu_enabled: false,
|
|
batch_size: 1,
|
|
optimization_level: OptimizationLevel::Medium,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Create HFT performance profile with default settings
|
|
pub fn create_hft_performance_profile() -> HFTPerformanceProfile {
|
|
HFTPerformanceProfile::default()
|
|
}
|
|
|
|
/// Create HFT performance profile with custom latency target
|
|
pub fn create_hft_performance_profile_with_latency(max_latency_us: u64) -> HFTPerformanceProfile {
|
|
HFTPerformanceProfile {
|
|
max_latency_us,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
/// Create HFT performance profile optimized for ultra-low latency
|
|
pub fn create_ultra_low_latency_profile() -> HFTPerformanceProfile {
|
|
HFTPerformanceProfile {
|
|
max_latency_us: 10, // 10 microseconds target
|
|
target_throughput: 50000, // 50k operations per second
|
|
memory_limit_mb: 512, // Reduced memory for cache efficiency
|
|
gpu_enabled: true, // Enable GPU acceleration
|
|
batch_size: 1, // No batching for minimal latency
|
|
optimization_level: OptimizationLevel::UltraLow,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
// ========== UNIFIED ML MODEL INTERFACE ==========
|
|
|
|
use async_trait::async_trait;
|
|
use chrono::{DateTime, Utc};
|
|
use futures::future::join_all;
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
use tokio::sync::RwLock;
|
|
|
|
/// Features vector for ML model input
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Features {
|
|
/// Raw feature values
|
|
pub values: Vec<f64>,
|
|
/// Feature names for debugging
|
|
pub names: Vec<String>,
|
|
/// Timestamp of features
|
|
pub timestamp: u64,
|
|
/// Symbol these features are for
|
|
pub symbol: Option<String>,
|
|
}
|
|
|
|
impl Features {
|
|
pub fn new(values: Vec<f64>, names: Vec<String>) -> Self {
|
|
Self {
|
|
values,
|
|
names,
|
|
timestamp: std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.unwrap_or_default()
|
|
.as_micros() as u64,
|
|
symbol: None,
|
|
}
|
|
}
|
|
|
|
/// Set the symbol for this market data point
|
|
///
|
|
/// # Arguments
|
|
/// * `symbol` - The trading symbol (e.g., "AAPL", "MSFT")
|
|
///
|
|
/// # Returns
|
|
/// Modified MarketData instance with symbol set
|
|
pub fn with_symbol(mut self, symbol: String) -> Self {
|
|
self.symbol = Some(symbol);
|
|
self
|
|
}
|
|
}
|
|
|
|
/// Model prediction result
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ModelPrediction {
|
|
/// Predicted value (price direction, probability, etc.)
|
|
pub value: f64,
|
|
/// Model confidence (0.0 to 1.0)
|
|
pub confidence: f64,
|
|
/// Additional model-specific metadata
|
|
pub metadata: HashMap<String, serde_json::Value>,
|
|
/// Prediction timestamp
|
|
pub timestamp: u64,
|
|
/// Model identifier
|
|
pub model_id: String,
|
|
}
|
|
|
|
impl ModelPrediction {
|
|
pub fn new(model_id: String, value: f64, confidence: f64) -> Self {
|
|
Self {
|
|
value,
|
|
confidence,
|
|
metadata: HashMap::new(),
|
|
timestamp: std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.unwrap_or_default()
|
|
.as_micros() as u64,
|
|
model_id,
|
|
}
|
|
}
|
|
|
|
/// Add metadata to the model prediction
|
|
///
|
|
/// # Arguments
|
|
/// * `key` - Metadata key identifier
|
|
/// * `value` - JSON value containing metadata
|
|
///
|
|
/// # Returns
|
|
/// Modified ModelPrediction with additional metadata
|
|
pub fn with_metadata(mut self, key: String, value: serde_json::Value) -> Self {
|
|
self.metadata.insert(key, value);
|
|
self
|
|
}
|
|
}
|
|
|
|
/// Feedback for model weight updates
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Feedback {
|
|
/// Actual outcome (for supervised learning)
|
|
pub actual_value: Option<f64>,
|
|
/// Reward signal (for reinforcement learning)
|
|
pub reward: Option<f64>,
|
|
/// Trading performance metrics
|
|
pub performance_metrics: HashMap<String, f64>,
|
|
/// Timestamp of feedback
|
|
pub timestamp: u64,
|
|
}
|
|
|
|
impl Feedback {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
actual_value: None,
|
|
reward: None,
|
|
performance_metrics: HashMap::new(),
|
|
timestamp: std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.unwrap_or_default()
|
|
.as_micros() as u64,
|
|
}
|
|
}
|
|
|
|
/// Set the actual outcome value for supervised learning feedback
|
|
///
|
|
/// # Arguments
|
|
/// * `actual` - The actual observed value
|
|
///
|
|
/// # Returns
|
|
/// Modified Feedback with actual value set
|
|
pub fn with_actual(mut self, actual: f64) -> Self {
|
|
self.actual_value = Some(actual);
|
|
self
|
|
}
|
|
|
|
/// Set the reward signal for reinforcement learning feedback
|
|
///
|
|
/// # Arguments
|
|
/// * `reward` - The reward value (positive for good outcomes, negative for bad)
|
|
///
|
|
/// # Returns
|
|
/// Modified Feedback with reward signal set
|
|
pub fn with_reward(mut self, reward: f64) -> Self {
|
|
self.reward = Some(reward);
|
|
self
|
|
}
|
|
}
|
|
|
|
/// Unified interface for all ML models in the system
|
|
#[async_trait]
|
|
pub trait MLModel: Send + Sync + std::fmt::Debug {
|
|
/// Get unique model identifier
|
|
fn name(&self) -> &str;
|
|
|
|
/// Get model type
|
|
fn model_type(&self) -> ModelType;
|
|
|
|
/// Make prediction based on features
|
|
async fn predict(&self, features: &Features) -> MLResult<ModelPrediction>;
|
|
|
|
/// Get current model confidence score (0.0 to 1.0)
|
|
fn get_confidence(&self) -> f64;
|
|
|
|
/// Update model weights based on feedback (optional - not all models support online learning)
|
|
async fn update_weights(&mut self, _feedback: &Feedback) -> MLResult<()> {
|
|
// Default implementation does nothing (for immutable models)
|
|
Ok(())
|
|
}
|
|
|
|
/// Check if model is ready for predictions
|
|
fn is_ready(&self) -> bool {
|
|
true // Default to ready
|
|
}
|
|
|
|
/// Get model metadata
|
|
fn get_metadata(&self) -> ModelMetadata;
|
|
|
|
/// Validate input features
|
|
fn validate_features(&self, features: &Features) -> MLResult<()> {
|
|
// Default validation - check for empty features
|
|
if features.values.is_empty() {
|
|
return Err(MLError::ValidationError {
|
|
message: "Empty feature vector".to_string(),
|
|
});
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Thread-safe model registry using DashMap for high-performance concurrent access
|
|
pub struct ModelRegistry {
|
|
/// Models stored by name
|
|
models: dashmap::DashMap<String, Arc<dyn MLModel>>,
|
|
/// Registry metadata
|
|
metadata: Arc<RwLock<RegistryMetadata>>,
|
|
}
|
|
|
|
impl std::fmt::Debug for ModelRegistry {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("ModelRegistry")
|
|
.field(
|
|
"models",
|
|
&format_args!("<DashMap with {} models>", self.models.len()),
|
|
)
|
|
.field("metadata", &self.metadata)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct RegistryMetadata {
|
|
created_at: std::time::SystemTime,
|
|
total_registrations: u64,
|
|
last_access: std::time::SystemTime,
|
|
}
|
|
|
|
impl ModelRegistry {
|
|
/// Create new model registry
|
|
pub fn new() -> Self {
|
|
Self {
|
|
models: dashmap::DashMap::new(),
|
|
metadata: Arc::new(RwLock::new(RegistryMetadata {
|
|
created_at: std::time::SystemTime::now(),
|
|
total_registrations: 0,
|
|
last_access: std::time::SystemTime::now(),
|
|
})),
|
|
}
|
|
}
|
|
|
|
/// Register a model in the registry
|
|
pub async fn register(&self, model: Arc<dyn MLModel>) -> MLResult<()> {
|
|
let name = model.name().to_string();
|
|
|
|
// Check if model is ready
|
|
if !model.is_ready() {
|
|
return Err(MLError::ModelError(format!("Model {} is not ready", name)));
|
|
}
|
|
|
|
self.models.insert(name.clone(), model);
|
|
|
|
// Update metadata
|
|
{
|
|
let mut meta = self.metadata.write().await;
|
|
meta.total_registrations += 1;
|
|
meta.last_access = std::time::SystemTime::now();
|
|
}
|
|
|
|
tracing::info!("Registered ML model: {}", name);
|
|
Ok(())
|
|
}
|
|
|
|
/// Get model by name
|
|
pub async fn get(&self, name: &str) -> Option<Arc<dyn MLModel>> {
|
|
// Update last access time
|
|
{
|
|
let mut meta = self.metadata.write().await;
|
|
meta.last_access = std::time::SystemTime::now();
|
|
}
|
|
|
|
self.models.get(name).map(|entry| entry.value().clone())
|
|
}
|
|
|
|
/// Get all registered models
|
|
pub fn get_all(&self) -> Vec<Arc<dyn MLModel>> {
|
|
self.models
|
|
.iter()
|
|
.map(|entry| entry.value().clone())
|
|
.collect()
|
|
}
|
|
|
|
/// Get model names
|
|
pub fn get_model_names(&self) -> Vec<String> {
|
|
self.models
|
|
.iter()
|
|
.map(|entry| entry.key().clone())
|
|
.collect()
|
|
}
|
|
|
|
/// Remove model from registry
|
|
pub async fn remove(&self, name: &str) -> Option<Arc<dyn MLModel>> {
|
|
let result = self.models.remove(name).map(|(_, model)| model);
|
|
|
|
if result.is_some() {
|
|
tracing::info!("Removed ML model: {}", name);
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
/// Get registry statistics
|
|
pub async fn get_stats(&self) -> RegistryStats {
|
|
let meta = self.metadata.read().await;
|
|
RegistryStats {
|
|
total_models: self.models.len(),
|
|
total_registrations: meta.total_registrations,
|
|
created_at: meta.created_at,
|
|
last_access: meta.last_access,
|
|
}
|
|
}
|
|
|
|
/// Parallel prediction across all models
|
|
pub async fn predict_all(&self, features: &Features) -> Vec<MLResult<ModelPrediction>> {
|
|
let models = self.get_all();
|
|
let futures = models.iter().map(|model| {
|
|
let features = features.clone();
|
|
async move { model.predict(&features).await }
|
|
});
|
|
|
|
join_all(futures).await
|
|
}
|
|
|
|
/// Parallel prediction across specific models
|
|
pub async fn predict_selected(
|
|
&self,
|
|
model_names: &[String],
|
|
features: &Features,
|
|
) -> Vec<MLResult<ModelPrediction>> {
|
|
let futures = model_names.iter().map(|name| {
|
|
let name = name.clone();
|
|
let features = features.clone();
|
|
async move {
|
|
if let Some(model) = self.get(&name).await {
|
|
model.predict(&features).await
|
|
} else {
|
|
Err(MLError::ModelNotFound(name))
|
|
}
|
|
}
|
|
});
|
|
|
|
join_all(futures).await
|
|
}
|
|
}
|
|
|
|
impl Default for ModelRegistry {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
/// Registry statistics
|
|
#[derive(Debug, Clone)]
|
|
pub struct RegistryStats {
|
|
pub total_models: usize,
|
|
pub total_registrations: u64,
|
|
pub created_at: std::time::SystemTime,
|
|
pub last_access: std::time::SystemTime,
|
|
}
|
|
|
|
/// Global model registry instance (singleton pattern)
|
|
static GLOBAL_REGISTRY: once_cell::sync::Lazy<Arc<ModelRegistry>> =
|
|
once_cell::sync::Lazy::new(|| Arc::new(ModelRegistry::new()));
|
|
|
|
/// Get global model registry
|
|
pub fn get_global_registry() -> Arc<ModelRegistry> {
|
|
GLOBAL_REGISTRY.clone()
|
|
}
|
|
|
|
// ========== PARALLEL EXECUTION OPTIMIZATIONS ==========
|
|
|
|
/// High-performance parallel executor for ML models optimized for sub-50μs latency
|
|
#[derive(Debug)]
|
|
pub struct ParallelExecutor {
|
|
/// Performance profile
|
|
profile: HFTPerformanceProfile,
|
|
/// CPU affinity settings
|
|
cpu_affinity: Option<Vec<usize>>,
|
|
/// Thread pool for CPU-bound operations
|
|
cpu_pool: Arc<rayon::ThreadPool>,
|
|
/// Async runtime handle
|
|
#[allow(dead_code)]
|
|
runtime_handle: tokio::runtime::Handle,
|
|
}
|
|
|
|
impl ParallelExecutor {
|
|
/// Create new parallel executor with HFT performance profile
|
|
pub fn new(profile: HFTPerformanceProfile) -> Result<Self, MLError> {
|
|
// Create dedicated thread pool based on profile
|
|
let cpu_pool = rayon::ThreadPoolBuilder::new()
|
|
.num_threads(
|
|
profile
|
|
.cpu_affinity
|
|
.as_ref()
|
|
.map(|v| v.len())
|
|
.unwrap_or(num_cpus::get()),
|
|
)
|
|
.thread_name(|i| format!("ml-cpu-{}", i))
|
|
.build()
|
|
.map_err(|e| MLError::ModelError(format!("Failed to create thread pool: {}", e)))?;
|
|
|
|
let runtime_handle = tokio::runtime::Handle::try_current()
|
|
.map_err(|e| MLError::ModelError(format!("No tokio runtime available: {}", e)))?;
|
|
|
|
let cpu_affinity = profile.cpu_affinity.clone();
|
|
|
|
Ok(Self {
|
|
profile,
|
|
cpu_affinity,
|
|
cpu_pool: Arc::new(cpu_pool),
|
|
runtime_handle,
|
|
})
|
|
}
|
|
|
|
/// Execute parallel predictions with latency optimization
|
|
pub async fn execute_parallel_predictions(
|
|
&self,
|
|
models: Vec<Arc<dyn MLModel>>,
|
|
features: Features,
|
|
) -> Vec<MLResult<ModelPrediction>> {
|
|
let start_time = std::time::Instant::now();
|
|
|
|
// Determine execution strategy based on performance profile
|
|
let results = match self.profile.optimization_level {
|
|
OptimizationLevel::UltraLow => {
|
|
// Ultra-low latency: parallel execution with minimal overhead
|
|
self.execute_ultra_low_latency(models, features).await
|
|
},
|
|
OptimizationLevel::Low => {
|
|
// Low latency: parallel with basic batching
|
|
self.execute_low_latency(models, features).await
|
|
},
|
|
OptimizationLevel::Medium => {
|
|
// Medium: balanced parallel execution
|
|
self.execute_balanced(models, features).await
|
|
},
|
|
OptimizationLevel::High => {
|
|
// High: conservative with full validation
|
|
self.execute_conservative(models, features).await
|
|
},
|
|
};
|
|
|
|
let execution_time = start_time.elapsed();
|
|
|
|
// Log performance if exceeding target latency
|
|
if execution_time.as_micros() > self.profile.max_latency_us as u128 {
|
|
tracing::warn!(
|
|
"Parallel execution exceeded target latency: {}μs > {}μs",
|
|
execution_time.as_micros(),
|
|
self.profile.max_latency_us
|
|
);
|
|
}
|
|
|
|
results
|
|
}
|
|
|
|
/// Ultra-low latency execution (<10μs target)
|
|
async fn execute_ultra_low_latency(
|
|
&self,
|
|
models: Vec<Arc<dyn MLModel>>,
|
|
features: Features,
|
|
) -> Vec<MLResult<ModelPrediction>> {
|
|
// Use futures::future::join_all for minimal overhead
|
|
let futures = models.into_iter().map(|model| {
|
|
let features = features.clone();
|
|
async move { model.predict(&features).await }
|
|
});
|
|
|
|
join_all(futures).await
|
|
}
|
|
|
|
/// Low latency execution with basic optimizations
|
|
async fn execute_low_latency(
|
|
&self,
|
|
models: Vec<Arc<dyn MLModel>>,
|
|
features: Features,
|
|
) -> Vec<MLResult<ModelPrediction>> {
|
|
// Group models by type for potential batching
|
|
let mut model_groups: HashMap<ModelType, Vec<Arc<dyn MLModel>>> = HashMap::new();
|
|
|
|
for model in models {
|
|
let model_type = model.model_type();
|
|
model_groups.entry(model_type).or_default().push(model);
|
|
}
|
|
|
|
let mut all_futures = Vec::new();
|
|
|
|
for (_, group_models) in model_groups {
|
|
for model in group_models {
|
|
let features = features.clone();
|
|
all_futures.push(async move { model.predict(&features).await });
|
|
}
|
|
}
|
|
|
|
join_all(all_futures).await
|
|
}
|
|
|
|
/// Balanced execution with moderate optimizations
|
|
async fn execute_balanced(
|
|
&self,
|
|
models: Vec<Arc<dyn MLModel>>,
|
|
features: Features,
|
|
) -> Vec<MLResult<ModelPrediction>> {
|
|
// Validate features once for all models
|
|
for model in &models {
|
|
if let Err(e) = model.validate_features(&features) {
|
|
tracing::debug!(
|
|
"Feature validation failed for model {}: {}",
|
|
model.name(),
|
|
e
|
|
);
|
|
}
|
|
}
|
|
|
|
let futures = models.into_iter().map(|model| {
|
|
let features = features.clone();
|
|
async move {
|
|
if model.is_ready() {
|
|
model.predict(&features).await
|
|
} else {
|
|
Err(MLError::ModelError(format!(
|
|
"Model {} not ready",
|
|
model.name()
|
|
)))
|
|
}
|
|
}
|
|
});
|
|
|
|
join_all(futures).await
|
|
}
|
|
|
|
/// Conservative execution with full validation
|
|
async fn execute_conservative(
|
|
&self,
|
|
models: Vec<Arc<dyn MLModel>>,
|
|
features: Features,
|
|
) -> Vec<MLResult<ModelPrediction>> {
|
|
let mut results = Vec::new();
|
|
|
|
for model in models {
|
|
// Comprehensive validation
|
|
if !model.is_ready() {
|
|
results.push(Err(MLError::ModelError(format!(
|
|
"Model {} not ready",
|
|
model.name()
|
|
))));
|
|
continue;
|
|
}
|
|
|
|
if let Err(e) = model.validate_features(&features) {
|
|
results.push(Err(e));
|
|
continue;
|
|
}
|
|
|
|
// Execute with timeout
|
|
let prediction_future = model.predict(&features);
|
|
let timeout_duration = std::time::Duration::from_micros(self.profile.max_latency_us);
|
|
|
|
match tokio::time::timeout(timeout_duration, prediction_future).await {
|
|
Ok(result) => results.push(result),
|
|
Err(_) => results.push(Err(MLError::ModelError(format!(
|
|
"Model {} prediction timed out after {}μs",
|
|
model.name(),
|
|
self.profile.max_latency_us
|
|
)))),
|
|
}
|
|
}
|
|
|
|
results
|
|
}
|
|
|
|
/// Get execution statistics
|
|
pub fn get_stats(&self) -> ExecutorStats {
|
|
ExecutorStats {
|
|
optimization_level: self.profile.optimization_level,
|
|
target_latency_us: self.profile.max_latency_us,
|
|
cpu_threads: self.cpu_pool.current_num_threads(),
|
|
cpu_affinity: self.cpu_affinity.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Executor performance statistics
|
|
#[derive(Debug, Clone)]
|
|
pub struct ExecutorStats {
|
|
pub optimization_level: OptimizationLevel,
|
|
pub target_latency_us: u64,
|
|
pub cpu_threads: usize,
|
|
pub cpu_affinity: Option<Vec<usize>>,
|
|
}
|
|
|
|
/// Latency optimizer for ML inference pipelines
|
|
#[derive(Debug)]
|
|
pub struct LatencyOptimizer {
|
|
/// Target latency in microseconds
|
|
target_latency_us: u64,
|
|
/// Performance history
|
|
performance_history: Arc<RwLock<Vec<PerformancePoint>>>,
|
|
/// Optimization parameters
|
|
#[allow(dead_code)]
|
|
optimization_params: OptimizationParams,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct PerformancePoint {
|
|
timestamp: std::time::Instant,
|
|
latency_us: u64,
|
|
model_count: usize,
|
|
batch_size: u32,
|
|
success: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct OptimizationParams {
|
|
max_batch_size: u32,
|
|
adaptive_batching: bool,
|
|
prefetch_enabled: bool,
|
|
cache_predictions: bool,
|
|
}
|
|
|
|
impl Default for OptimizationParams {
|
|
fn default() -> Self {
|
|
Self {
|
|
max_batch_size: 8,
|
|
adaptive_batching: true,
|
|
prefetch_enabled: true,
|
|
cache_predictions: false, // Disabled for real-time trading
|
|
}
|
|
}
|
|
}
|
|
|
|
impl LatencyOptimizer {
|
|
/// Create new latency optimizer
|
|
pub fn new(target_latency_us: u64) -> Self {
|
|
Self {
|
|
target_latency_us,
|
|
performance_history: Arc::new(RwLock::new(Vec::new())),
|
|
optimization_params: OptimizationParams::default(),
|
|
}
|
|
}
|
|
|
|
/// Record performance measurement
|
|
pub async fn record_performance(
|
|
&self,
|
|
latency_us: u64,
|
|
model_count: usize,
|
|
batch_size: u32,
|
|
success: bool,
|
|
) {
|
|
let point = PerformancePoint {
|
|
timestamp: std::time::Instant::now(),
|
|
latency_us,
|
|
model_count,
|
|
batch_size,
|
|
success,
|
|
};
|
|
|
|
{
|
|
let mut history = self.performance_history.write().await;
|
|
history.push(point);
|
|
|
|
// Keep only recent history (last 1000 measurements)
|
|
if history.len() > 1000 {
|
|
let excess = history.len() - 1000;
|
|
history.drain(0..excess);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get optimization recommendations
|
|
pub async fn get_recommendations(&self) -> OptimizationRecommendations {
|
|
let history = self.performance_history.read().await;
|
|
|
|
if history.is_empty() {
|
|
return OptimizationRecommendations::default();
|
|
}
|
|
|
|
let recent_points: Vec<&PerformancePoint> = history.iter().rev().take(100).collect();
|
|
|
|
let avg_latency =
|
|
recent_points.iter().map(|p| p.latency_us).sum::<u64>() / recent_points.len() as u64;
|
|
|
|
let success_rate =
|
|
recent_points.iter().filter(|p| p.success).count() as f64 / recent_points.len() as f64;
|
|
|
|
OptimizationRecommendations {
|
|
current_avg_latency_us: avg_latency,
|
|
target_latency_us: self.target_latency_us,
|
|
success_rate,
|
|
meets_target: avg_latency <= self.target_latency_us,
|
|
recommended_batch_size: self.calculate_optimal_batch_size(&recent_points),
|
|
recommended_model_limit: self.calculate_optimal_model_limit(&recent_points),
|
|
}
|
|
}
|
|
|
|
fn calculate_optimal_batch_size(&self, points: &[&PerformancePoint]) -> u32 {
|
|
// Simple heuristic: find batch size with best latency/success ratio
|
|
let mut batch_performance: HashMap<u32, (u64, f64)> = HashMap::new();
|
|
|
|
for point in points {
|
|
let entry = batch_performance
|
|
.entry(point.batch_size)
|
|
.or_insert((0, 0.0));
|
|
entry.0 += point.latency_us;
|
|
entry.1 += if point.success { 1.0 } else { 0.0 };
|
|
}
|
|
|
|
batch_performance
|
|
.into_iter()
|
|
.filter(|(_, (_, success_count))| *success_count > 0.0)
|
|
.min_by_key(|(_, (latency, success_count))| {
|
|
// Optimize for latency with success rate weighting
|
|
((*latency as f64) / success_count) as u64
|
|
})
|
|
.map(|(batch_size, _)| batch_size)
|
|
.unwrap_or(1)
|
|
}
|
|
|
|
fn calculate_optimal_model_limit(&self, points: &[&PerformancePoint]) -> usize {
|
|
// Find the sweet spot where adding more models doesn't improve latency
|
|
let mut model_performance: HashMap<usize, u64> = HashMap::new();
|
|
|
|
for point in points {
|
|
if point.success {
|
|
let entry = model_performance.entry(point.model_count).or_insert(0);
|
|
*entry += point.latency_us;
|
|
}
|
|
}
|
|
|
|
model_performance
|
|
.into_iter()
|
|
.filter(|(_, avg_latency)| *avg_latency <= self.target_latency_us)
|
|
.max_by_key(|(model_count, _)| *model_count)
|
|
.map(|(model_count, _)| model_count)
|
|
.unwrap_or(1)
|
|
}
|
|
}
|
|
|
|
/// Optimization recommendations from latency analysis
|
|
#[derive(Debug, Clone)]
|
|
pub struct OptimizationRecommendations {
|
|
pub current_avg_latency_us: u64,
|
|
pub target_latency_us: u64,
|
|
pub success_rate: f64,
|
|
pub meets_target: bool,
|
|
pub recommended_batch_size: u32,
|
|
pub recommended_model_limit: usize,
|
|
}
|
|
|
|
impl Default for OptimizationRecommendations {
|
|
fn default() -> Self {
|
|
Self {
|
|
current_avg_latency_us: 0,
|
|
target_latency_us: 50,
|
|
success_rate: 0.0,
|
|
meets_target: false,
|
|
recommended_batch_size: 1,
|
|
recommended_model_limit: 1,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Create optimized parallel executor for HFT scenarios
|
|
pub fn create_hft_parallel_executor() -> Result<ParallelExecutor, MLError> {
|
|
let profile = create_ultra_low_latency_profile();
|
|
ParallelExecutor::new(profile)
|
|
}
|
|
|
|
/// Create latency optimizer with HFT targets
|
|
pub fn create_hft_latency_optimizer() -> LatencyOptimizer {
|
|
LatencyOptimizer::new(50) // 50 microsecond target
|
|
}
|
|
|
|
// ========== CANONICAL TRAINING AND VALIDATION METRICS ==========
|
|
// These are the unified types that all ML modules must use to prevent type conflicts
|
|
|
|
/// Canonical training metrics used throughout ML module
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TrainingMetrics {
|
|
/// Training loss value
|
|
pub loss: f64,
|
|
/// Training accuracy (0.0 to 1.0)
|
|
pub accuracy: f64,
|
|
/// Training precision (0.0 to 1.0)
|
|
pub precision: f64,
|
|
/// Training recall (0.0 to 1.0)
|
|
pub recall: f64,
|
|
/// Training F1 score (0.0 to 1.0)
|
|
pub f1_score: f64,
|
|
/// Total training time in seconds
|
|
pub training_time_seconds: f64,
|
|
/// Number of epochs trained
|
|
pub epochs_trained: u32,
|
|
/// Whether convergence was achieved
|
|
pub convergence_achieved: bool,
|
|
/// Additional model-specific metrics
|
|
pub additional_metrics: HashMap<String, f64>,
|
|
}
|
|
|
|
impl TrainingMetrics {
|
|
/// Create new training metrics
|
|
pub fn new() -> Self {
|
|
Self {
|
|
loss: 0.0,
|
|
accuracy: 0.0,
|
|
precision: 0.0,
|
|
recall: 0.0,
|
|
f1_score: 0.0,
|
|
training_time_seconds: 0.0,
|
|
epochs_trained: 0,
|
|
convergence_achieved: false,
|
|
additional_metrics: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Add an additional metric
|
|
pub fn add_metric(&mut self, name: &str, value: f64) {
|
|
self.additional_metrics.insert(name.to_string(), value);
|
|
}
|
|
|
|
/// Check if training was successful
|
|
pub fn is_successful(&self) -> bool {
|
|
self.convergence_achieved && self.accuracy > 0.5
|
|
}
|
|
}
|
|
|
|
impl Default for TrainingMetrics {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
/// Canonical validation metrics used throughout ML module
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ValidationMetrics {
|
|
/// Validation loss value
|
|
pub validation_loss: f64,
|
|
/// Validation accuracy (0.0 to 1.0)
|
|
pub validation_accuracy: f64,
|
|
/// Validation precision (0.0 to 1.0)
|
|
pub validation_precision: f64,
|
|
/// Validation recall (0.0 to 1.0)
|
|
pub validation_recall: f64,
|
|
/// Validation F1 score (0.0 to 1.0)
|
|
pub validation_f1_score: f64,
|
|
/// Number of samples validated
|
|
pub samples_validated: usize,
|
|
/// Additional model-specific validation metrics
|
|
pub additional_metrics: HashMap<String, f64>,
|
|
}
|
|
|
|
impl ValidationMetrics {
|
|
/// Create new validation metrics
|
|
pub fn new() -> Self {
|
|
Self {
|
|
validation_loss: 0.0,
|
|
validation_accuracy: 0.0,
|
|
validation_precision: 0.0,
|
|
validation_recall: 0.0,
|
|
validation_f1_score: 0.0,
|
|
samples_validated: 0,
|
|
additional_metrics: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Add an additional validation metric
|
|
pub fn add_metric(&mut self, name: &str, value: f64) {
|
|
self.additional_metrics.insert(name.to_string(), value);
|
|
}
|
|
|
|
/// Check if validation was successful
|
|
pub fn is_successful(&self) -> bool {
|
|
self.validation_accuracy > 0.5 && self.samples_validated > 0
|
|
}
|
|
}
|
|
|
|
impl Default for ValidationMetrics {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
// Public exports moved to end of file after all type definitions
|
|
|
|
// ========== CANONICAL ML TYPES ==========
|
|
// These are the unified types that all ML modules must use to prevent type conflicts
|
|
|
|
/// Canonical inference result used throughout ML module
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct InferenceResult {
|
|
/// Model identifier
|
|
pub model_id: String,
|
|
/// Prediction value (primary prediction)
|
|
pub prediction_value: f64,
|
|
/// Confidence score (0.0 to 1.0)
|
|
pub confidence: f64,
|
|
/// Latency in microseconds
|
|
pub latency_us: u64,
|
|
/// Timestamp in microseconds since UNIX epoch
|
|
pub timestamp: u64,
|
|
/// Model metadata
|
|
pub metadata: ModelMetadata,
|
|
}
|
|
|
|
impl InferenceResult {
|
|
/// Create new inference result
|
|
pub fn new(
|
|
model_id: String,
|
|
prediction_value: f64,
|
|
confidence: f64,
|
|
latency_us: u64,
|
|
timestamp: u64,
|
|
metadata: ModelMetadata,
|
|
) -> Self {
|
|
Self {
|
|
model_id,
|
|
prediction_value,
|
|
confidence,
|
|
latency_us,
|
|
timestamp,
|
|
metadata,
|
|
}
|
|
}
|
|
|
|
/// Extract prediction as float value
|
|
pub fn prediction_as_float(&self) -> f64 {
|
|
self.prediction_value
|
|
}
|
|
|
|
/// Get the model identifier
|
|
pub fn model_id(&self) -> &str {
|
|
&self.model_id
|
|
}
|
|
}
|
|
|
|
/// Canonical model metadata used throughout ML module
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ModelMetadata {
|
|
/// Type of the model
|
|
pub model_type: ModelType,
|
|
/// Model version
|
|
pub version: String,
|
|
/// Number of features used for inference
|
|
pub features_used: usize,
|
|
/// Memory usage in megabytes
|
|
pub memory_usage_mb: f64,
|
|
/// Additional metadata key-value pairs
|
|
pub additional_metadata: HashMap<String, String>,
|
|
}
|
|
|
|
impl ModelMetadata {
|
|
/// Create new model metadata
|
|
pub fn new(
|
|
model_type: ModelType,
|
|
version: String,
|
|
features_used: usize,
|
|
memory_usage_mb: f64,
|
|
) -> Self {
|
|
Self {
|
|
model_type,
|
|
version,
|
|
features_used,
|
|
memory_usage_mb,
|
|
additional_metadata: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Add additional metadata
|
|
pub fn add_metadata(&mut self, key: &str, value: String) {
|
|
self.additional_metadata.insert(key.to_string(), value);
|
|
}
|
|
|
|
/// Mark the model as trained
|
|
pub fn mark_trained(&mut self) {
|
|
self.add_metadata("training_status", "trained".to_string());
|
|
self.add_metadata(
|
|
"training_timestamp",
|
|
std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.unwrap_or_default()
|
|
.as_secs()
|
|
.to_string(),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Canonical model type enum used throughout ML module
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
pub enum ModelType {
|
|
/// Compact Deep Q-Network
|
|
CompactDQN,
|
|
/// Distilled micro network for ultra-low latency
|
|
DistilledMicroNet,
|
|
/// Standard Deep Q-Network
|
|
DQN,
|
|
/// Rainbow `DQN` with all enhancements
|
|
RainbowDQN,
|
|
/// `MAMBA` model (SSM)
|
|
MAMBA,
|
|
/// Temporal Fusion Transformer
|
|
TFT,
|
|
/// Temporal Graph Neural Network
|
|
TGGN,
|
|
/// Liquid Neural Network
|
|
LNN,
|
|
/// Temporal Limit Order Book transformer
|
|
TLOB,
|
|
/// Proximal Policy Optimization
|
|
PPO,
|
|
/// Transformer for sequence modeling
|
|
Transformer,
|
|
/// Mamba state space model (alias for `MAMBA`)
|
|
Mamba,
|
|
/// Liquid time constant networks (alias for LNN)
|
|
LiquidNet,
|
|
/// Temporal Graph Neural Network (alias for TGGN)
|
|
TGNN,
|
|
/// Ensemble methods
|
|
Ensemble,
|
|
}
|
|
|
|
impl ModelType {
|
|
/// Get file extension for model type
|
|
pub fn file_extension(&self) -> &'static str {
|
|
match self {
|
|
ModelType::DQN => "dqn",
|
|
ModelType::MAMBA | ModelType::Mamba => "mamba",
|
|
ModelType::TFT => "tft",
|
|
ModelType::TGGN | ModelType::TGNN => "tggn",
|
|
ModelType::LNN | ModelType::LiquidNet => "lnn",
|
|
ModelType::CompactDQN => "compact_dqn",
|
|
ModelType::DistilledMicroNet => "distilled",
|
|
ModelType::RainbowDQN => "rainbow_dqn",
|
|
ModelType::TLOB => "tlob",
|
|
ModelType::PPO => "ppo",
|
|
ModelType::Transformer => "transformer",
|
|
ModelType::Ensemble => "ensemble",
|
|
}
|
|
}
|
|
|
|
/// Get model type from string
|
|
pub fn from_str(s: &str) -> Option<Self> {
|
|
match s.to_lowercase().as_str() {
|
|
"dqn" => Some(ModelType::DQN),
|
|
"mamba" => Some(ModelType::MAMBA),
|
|
"tft" => Some(ModelType::TFT),
|
|
"tggn" | "tgnn" => Some(ModelType::TGGN),
|
|
"lnn" | "liquidnet" => Some(ModelType::LNN),
|
|
"compact_dqn" | "compactdqn" => Some(ModelType::CompactDQN),
|
|
"distilled" | "distilledmicronet" => Some(ModelType::DistilledMicroNet),
|
|
"rainbow_dqn" | "rainbowdqn" => Some(ModelType::RainbowDQN),
|
|
"tlob" => Some(ModelType::TLOB),
|
|
"ppo" => Some(ModelType::PPO),
|
|
"transformer" => Some(ModelType::Transformer),
|
|
"ensemble" => Some(ModelType::Ensemble),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Prelude module for convenient imports of commonly used ML types
|
|
///
|
|
/// This module re-exports the most commonly used types and traits from the ML crate
|
|
/// to allow users to import everything they need with a single `use ml::prelude::*;`
|
|
pub mod prelude {
|
|
// Core ML types
|
|
pub use crate::{
|
|
CommonError, CommonTypeError, ErrorCategory, FeatureVector, Features, Feedback,
|
|
HealthStatus, InferenceResult, IntegerTensor, MarketDataSnapshot, MarketRegime,
|
|
ModelMetadata, ModelPrediction, ModelType, Trade, TrainingMetrics, UpdateSummary,
|
|
ValidationMetrics,
|
|
};
|
|
|
|
// Error types
|
|
pub use crate::{MLError, MLResult, UnifiedMLResult};
|
|
|
|
// ML Model trait
|
|
pub use crate::MLModel;
|
|
|
|
// Model registry
|
|
pub use crate::{get_global_registry, ModelRegistry, RegistryStats};
|
|
|
|
// Performance types
|
|
pub use crate::{
|
|
create_hft_latency_optimizer, create_hft_parallel_executor, create_hft_performance_profile,
|
|
create_hft_performance_profile_with_latency, create_ultra_low_latency_profile,
|
|
ExecutorStats, HFTPerformanceProfile, LatencyOptimizer, OptimizationLevel,
|
|
OptimizationRecommendations, ParallelExecutor,
|
|
};
|
|
|
|
// Constants
|
|
pub use crate::{MAX_INFERENCE_LATENCY_US, PRECISION_FACTOR};
|
|
|
|
// Tensor types from candle
|
|
pub use candle_core::{Device, Tensor};
|
|
pub use candle_nn::{Module, VarBuilder, VarMap};
|
|
|
|
// Common external types
|
|
pub use rust_decimal::Decimal;
|
|
pub use serde::{Deserialize, Serialize};
|
|
}
|