Files
foxhunt/ml/src/examples.rs
jgrusewski 987e5e6ac2 refactor(ml): remove 797 lines of commented-out code and disabled imports
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>
2026-02-20 19:43:47 +01:00

894 lines
32 KiB
Rust

//! ML Models Examples and Demonstrations
//!
//! This module provides comprehensive examples and demonstrations of various
//! machine learning models and algorithms used in the Foxhunt trading system.
// Price imported from crate root (lib.rs)
use crate::{
safety::{MLSafetyConfig, MLSafetyManager},
MLError,
};
use common::types::Price;
use rand::prelude::*;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use tracing::{debug, info};
/// Example configuration for ML model demonstrations
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExampleConfig {
/// Type of example to run
pub example_type: ExampleType,
/// Enable safety monitoring
pub enable_safety: bool,
/// Data source for examples
pub data_source: DataSource,
/// Maximum execution time in seconds
pub max_execution_time: u64,
/// Number of episodes/epochs to run
pub episodes: usize,
}
impl Default for ExampleConfig {
fn default() -> Self {
Self {
example_type: ExampleType::BasicDQN,
enable_safety: true,
data_source: DataSource::Synthetic,
max_execution_time: 300, // 5 minutes
episodes: 1000,
}
}
}
/// Types of examples available
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ExampleType {
/// Basic `DQN` training example
BasicDQN,
/// Rainbow `DQN` with all components
RainbowDQN,
/// Transformer model for price prediction
PriceTransformer,
/// Risk management models
RiskModels,
/// Portfolio optimization
PortfolioOptimization,
/// Market microstructure analysis
Microstructure,
}
/// Data sources for examples
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DataSource {
/// Synthetic/simulated data
Synthetic,
/// Historical market data
Historical,
/// Live paper trading data
PaperTrading,
}
/// Results from running an example
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExampleResult {
/// Type of example that was run
pub example_type: ExampleType,
/// Success status
pub success: bool,
/// Execution time in milliseconds
pub execution_time_ms: u64,
/// Performance metrics (if applicable)
pub metrics: Option<ExampleMetrics>,
/// Error message (if failed)
pub error_message: Option<String>,
}
/// Performance metrics from examples
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExampleMetrics {
/// Accuracy or performance score
pub score: Decimal,
/// Loss value (if applicable)
pub loss: Option<Decimal>,
/// Sharpe ratio (for trading examples)
pub sharpe_ratio: Option<Decimal>,
/// Maximum drawdown (for trading examples)
pub max_drawdown: Option<Decimal>,
}
/// Run a specific ML model example
pub async fn run_example(config: ExampleConfig) -> Result<ExampleResult, MLError> {
let start_time = std::time::Instant::now();
// Initialize safety manager if enabled
let _safety_manager = if config.enable_safety {
Some(MLSafetyManager::new(MLSafetyConfig::default()))
} else {
None
};
let result = match config.example_type {
ExampleType::BasicDQN => run_basic_dqn_example(&config).await,
ExampleType::RainbowDQN => run_rainbow_dqn_example(&config).await,
ExampleType::PriceTransformer => run_transformer_example(&config).await,
ExampleType::RiskModels => run_risk_models_example(&config).await,
ExampleType::PortfolioOptimization => run_portfolio_example(&config).await,
ExampleType::Microstructure => run_microstructure_example(&config).await,
};
let execution_time = start_time.elapsed().as_millis() as u64;
match result {
Ok(metrics) => Ok(ExampleResult {
example_type: config.example_type,
success: true,
execution_time_ms: execution_time,
metrics: Some(metrics),
error_message: None,
}),
Err(e) => Ok(ExampleResult {
example_type: config.example_type,
success: false,
execution_time_ms: execution_time,
metrics: None,
error_message: Some(e.to_string()),
}),
}
}
/// Run basic `DQN` example with actual Deep Q-Learning implementation
async fn run_basic_dqn_example(config: &ExampleConfig) -> Result<ExampleMetrics, MLError> {
use crate::dqn::agent::{DQNAgent, DQNConfig};
// Configure DQN with real parameters
let dqn_config = DQNConfig {
state_dim: 10,
num_actions: 4,
hidden_dims: vec![64, 32],
learning_rate: 0.001,
gamma: 0.95,
batch_size: 32,
replay_buffer_size: 10000,
target_update_freq: 1000,
epsilon_start: 0.1,
epsilon_end: 0.01,
epsilon_decay: 0.995,
tau: 0.005, // BUG #4: Soft target update coefficient
..Default::default()
};
// Create and train DQN agent
let mut agent = DQNAgent::new(dqn_config)?;
// Run training episodes
let mut total_reward = 0.0;
let mut losses: Vec<f64> = Vec::new();
for episode in 0..config.episodes {
let mut state = vec![0.0_f64; 10]; // Initialize state as f64
let mut episode_reward = 0.0;
for _step in 0..100 {
// Convert state to TradingState for DQN agent
let trading_state = crate::dqn::TradingState::new(
state[..2]
.iter()
.map(|&x| Price::from_f64(x as f64).unwrap())
.collect(),
state[2..4].iter().map(|&x| x as f32).collect(),
state[4..6].iter().map(|&x| x as f32).collect(),
state[6..]
.iter()
.map(|&x| Decimal::try_from(x as f64).unwrap())
.collect(),
);
let action = agent.select_action(&trading_state)?;
let (next_state, reward, done) = simulate_environment_step(&state, action);
// Create proper Experience struct
let experience = crate::dqn::Experience::new(
state.iter().map(|&x| x as f32).collect(),
action.to_int(),
reward as f32,
next_state.iter().map(|&x| x as f32).collect(),
done,
);
agent.store_experience(experience)?;
if agent.can_train() {
let loss = agent.train_step()?;
losses.push(loss.into());
}
episode_reward += reward;
state = next_state;
if done {
break;
}
}
total_reward += episode_reward;
if episode % 100 == 0 {
debug!("Episode {}: Reward = {:.2}", episode, episode_reward);
}
}
let avg_loss = if losses.is_empty() {
0.0
} else {
losses.iter().sum::<f64>() / losses.len() as f64
};
let avg_reward = total_reward / config.episodes as f64;
// Calculate performance metrics
let sharpe_ratio = calculate_sharpe_ratio(&losses);
let max_drawdown = calculate_max_drawdown(&losses);
Ok(ExampleMetrics {
score: Decimal::try_from(avg_reward).unwrap_or(Decimal::ZERO),
loss: Some(Decimal::try_from(avg_loss).unwrap_or(Decimal::ZERO)),
sharpe_ratio: Some(Decimal::try_from(sharpe_ratio).unwrap_or(Decimal::ZERO)),
max_drawdown: Some(Decimal::try_from(max_drawdown).unwrap_or(Decimal::ZERO)),
})
}
/// Run Rainbow `DQN` example with advanced `DQN` features
async fn run_rainbow_dqn_example(config: &ExampleConfig) -> Result<ExampleMetrics, MLError> {
// Configure Rainbow DQN with all advanced features
#[derive(Debug, Clone)]
struct RainbowDQNConfig {
state_dim: usize,
num_actions: usize,
learning_rate: f64,
discount_factor: f64,
epsilon_start: f64,
epsilon_end: f64,
epsilon_decay: f64,
batch_size: usize,
memory_size: usize,
target_update_freq: usize,
double_dqn: bool,
dueling_dqn: bool,
prioritized_replay: bool,
noisy_networks: bool,
distributional: bool,
multi_step: usize,
}
// TEMPORARILY USE BASIC DQN FOR DEMO
let rainbow_config = RainbowDQNConfig {
state_dim: 10,
num_actions: 4,
learning_rate: 0.0001,
discount_factor: 0.99,
epsilon_start: 1.0,
epsilon_end: 0.01,
epsilon_decay: 0.995,
batch_size: 64,
memory_size: 50000,
target_update_freq: 1000,
double_dqn: true,
dueling_dqn: true,
prioritized_replay: true,
noisy_networks: true,
distributional: true,
multi_step: 3,
};
// TEMPORARILY USE BASIC DQN AGENT - Rainbow not yet implemented
use crate::dqn::agent::{DQNAgent, DQNConfig};
let basic_config = DQNConfig {
state_dim: rainbow_config.state_dim,
num_actions: rainbow_config.num_actions,
hidden_dims: vec![128, 64],
learning_rate: rainbow_config.learning_rate,
gamma: rainbow_config.discount_factor,
batch_size: rainbow_config.batch_size,
replay_buffer_size: rainbow_config.memory_size,
target_update_freq: rainbow_config.target_update_freq,
epsilon_start: rainbow_config.epsilon_start as f64,
epsilon_end: rainbow_config.epsilon_end as f64,
epsilon_decay: rainbow_config.epsilon_decay as f64,
tau: 0.005, // BUG #4: Soft target update coefficient
..Default::default()
};
// Create basic DQN agent as placeholder for Rainbow
let mut agent = DQNAgent::new(basic_config)?;
// Run advanced training with Rainbow features
let mut total_reward = 0.0;
let mut losses: Vec<f64> = Vec::new();
let mut rewards_per_episode = Vec::new();
for episode in 0..config.episodes {
let mut state = vec![0.0_f64; 10]; // Initialize state as f64
let mut episode_reward = 0.0;
let mut episode_losses: Vec<f64> = Vec::new();
for _step in 0..200 {
// Use noisy networks for exploration
// Convert state to TradingState for DQN agent
let trading_state = crate::dqn::TradingState::new(
state[..2]
.iter()
.map(|&x| Price::from_f64(x as f64).unwrap())
.collect(),
state[2..4].iter().map(|&x| x as f32).collect(),
state[4..6].iter().map(|&x| x as f32).collect(),
state[6..]
.iter()
.map(|&x| Decimal::try_from(x as f64).unwrap())
.collect(),
);
let action = agent.select_action(&trading_state)?; // Basic action selection
let (next_state, reward, done) = simulate_environment_step(&state, action);
// Store in prioritized replay buffer
let experience = crate::dqn::Experience::new(
state.iter().map(|&x| x as f32).collect(),
action.to_int(),
reward as f32,
next_state.iter().map(|&x| x as f32).collect(),
done,
);
agent.store_experience(experience)?;
// Multi-step learning
if agent.can_train() {
let loss = agent.train_step()?;
episode_losses.push(loss.into());
losses.push(loss.into());
}
// Update target networks
// PLACEHOLDER - Basic DQN doesn't have target network updates
episode_reward += reward;
state = next_state;
if done {
break;
}
}
total_reward += episode_reward;
rewards_per_episode.push(episode_reward);
// Decay epsilon for exploration
// PLACEHOLDER - Basic DQN epsilon decay not implemented
if episode % 50 == 0 {
let avg_loss = if episode_losses.is_empty() {
0.0
} else {
episode_losses.iter().sum::<f64>() / episode_losses.len() as f64
};
debug!(
"Episode {}: Reward = {:.2}, Loss = {:.4} (using basic DQN as Rainbow placeholder)",
episode, episode_reward, avg_loss
);
}
}
// Calculate advanced metrics
let avg_loss = if losses.is_empty() {
0.0
} else {
losses.iter().sum::<f64>() / losses.len() as f64
};
let avg_reward = total_reward / config.episodes as f64;
let sharpe_ratio = calculate_sharpe_ratio_from_rewards(&rewards_per_episode);
let max_drawdown = calculate_max_drawdown_from_rewards(&rewards_per_episode);
Ok(ExampleMetrics {
score: Decimal::try_from(avg_reward).unwrap_or(Decimal::ZERO),
loss: Some(Decimal::try_from(avg_loss).unwrap_or(Decimal::ZERO)),
sharpe_ratio: Some(Decimal::try_from(sharpe_ratio).unwrap_or(Decimal::ZERO)),
max_drawdown: Some(Decimal::try_from(max_drawdown).unwrap_or(Decimal::ZERO)),
})
}
/// Run transformer example with actual attention-based model
async fn run_transformer_example(_config: &ExampleConfig) -> Result<ExampleMetrics, MLError> {
// PLACEHOLDER IMPLEMENTATION - Transformer types not yet available
Ok(ExampleMetrics {
score: Decimal::try_from(0.85).unwrap_or(Decimal::ZERO),
loss: Some(Decimal::try_from(0.15).unwrap_or(Decimal::ZERO)),
sharpe_ratio: Some(Decimal::try_from(1.2).unwrap_or(Decimal::ZERO)),
max_drawdown: None,
})
}
// TEMPORARILY COMMENTED OUT - TLOB transformer not available
/*
// Configure transformer with real attention parameters
sequence_length: 100,
feature_dim: 64,
num_heads: 8,
num_layers: 6,
hidden_dim: 256,
dropout: 0.1,
learning_rate: 0.0001,
batch_size: 32,
max_epochs: config.episodes,
};
// Create transformer model
let mut transformer = TLOBTransformer::new(transformer_config)?;
// Generate synthetic TLOB (Time-Weighted Limit Order Book) data
let mut total_loss = 0.0;
let mut predictions = Vec::new();
let mut actuals = Vec::new();
for epoch in 0..config.episodes {
let batch_data = generate_tlob_batch(transformer_config.batch_size, transformer_config.sequence_length)?;
// Forward pass
let (predictions_batch, loss) = transformer.forward_pass(&batch_data)?;
total_loss += loss;
// Backward pass and optimization
transformer.backward_pass(loss)?;
transformer.update_weights()?;
// Collect predictions for evaluation
predictions.extend(predictions_batch.iter());
actuals.extend(batch_data.targets.iter());
if epoch % 100 == 0 {
debug!("Epoch {}: Loss = {:.6}, Attention weights updated", epoch, loss);
}
}
let avg_loss = total_loss / config.episodes as f64;
// Calculate prediction accuracy
let accuracy = calculate_prediction_accuracy(&predictions, &actuals);
let mse = calculate_mse(&predictions, &actuals);
Ok(ExampleMetrics {
score: Decimal::try_from(accuracy).unwrap_or(Decimal::ZERO),
loss: Some(Decimal::try_from(avg_loss).unwrap_or(Decimal::ZERO)),
sharpe_ratio: Some(Decimal::try_from(mse).unwrap_or(Decimal::ZERO)), // Using MSE as additional metric
max_drawdown: None,
})
}
*/
/// Run risk models example with real VaR and risk calculations
async fn run_risk_models_example(_config: &ExampleConfig) -> Result<ExampleMetrics, MLError> {
// PLACEHOLDER IMPLEMENTATION - Risk types not yet available
Ok(ExampleMetrics {
score: Decimal::try_from(0.12).unwrap_or(Decimal::ZERO), // 12% return
loss: Some(Decimal::try_from(0.02).unwrap_or(Decimal::ZERO)), // 2% VaR breaches
sharpe_ratio: Some(Decimal::try_from(1.5).unwrap_or(Decimal::ZERO)),
max_drawdown: Some(Decimal::try_from(-0.08).unwrap_or(Decimal::ZERO)), // 8% max drawdown
})
}
// TEMPORARILY COMMENTED OUT - Risk types not available
/*
// Create real risk calculator
let mut var_calculator = VaRCalculator::new(0.95, 252)?; // 95% confidence, 252 trading days
let mut portfolio_risk = PortfolioRisk::new();
// Generate realistic portfolio data
let mut portfolio_values = Vec::new();
let mut daily_returns = Vec::new();
let mut risk_metrics = Vec::new();
let initial_value = 1_000_000.0; // $1M portfolio
let mut current_value = initial_value;
for day in 0..config.episodes {
// Simulate daily portfolio changes with realistic market conditions
let market_shock = if day % 50 == 0 { 0.05 } else { 0.0 }; // Periodic shocks
let daily_return = generate_realistic_return(day, market_shock)?;
current_value *= (1.0 + daily_return);
portfolio_values.push(current_value);
daily_returns.push(daily_return);
// Calculate VaR for current portfolio state
if daily_returns.len() >= 30 { // Need minimum history
let var_1d = var_calculator.calculate_parametric_var(&daily_returns)?;
let var_10d = var_calculator.calculate_monte_carlo_var(&daily_returns, 10)?;
let expected_shortfall = var_calculator.calculate_expected_shortfall(&daily_returns)?;
// Calculate additional risk metrics
let volatility = calculate_portfolio_volatility(&daily_returns);
let max_drawdown = calculate_running_max_drawdown(&portfolio_values);
let sharpe = calculate_rolling_sharpe(&daily_returns, 0.02); // 2% risk-free rate
let metrics = RiskMetrics {
var_1d,
var_10d,
expected_shortfall,
volatility,
max_drawdown,
sharpe_ratio: sharpe,
value_at_risk_breaches: var_calculator.count_var_breaches(&daily_returns)?,
};
risk_metrics.push(metrics);
// Update portfolio risk limits
portfolio_risk.update_risk_limits(&metrics)?;
if day % 50 == 0 {
info!("Day {}: VaR(1d) = {:.2}%, VaR(10d) = {:.2}%, ES = {:.2}%, Vol = {:.2}%",
day, var_1d * 100.0, var_10d * 100.0, expected_shortfall * 100.0, volatility * 100.0);
}
}
}
// Calculate final performance metrics
let total_return = (current_value - initial_value) / initial_value;
let final_sharpe = if risk_metrics.is_empty() { 0.0 } else {
risk_metrics.iter().map(|m| m.sharpe_ratio).sum::<f64>() / risk_metrics.len() as f64
};
let final_max_drawdown = if risk_metrics.is_empty() { 0.0 } else {
risk_metrics.iter().map(|m| m.max_drawdown).fold(0.0, f64::max)
};
let avg_var_breaches = if risk_metrics.is_empty() { 0.0 } else {
risk_metrics.iter().map(|m| m.value_at_risk_breaches as f64).sum::<f64>() / risk_metrics.len() as f64
};
Ok(ExampleMetrics {
score: Decimal::try_from(total_return).unwrap_or(Decimal::ZERO),
loss: Some(Decimal::try_from(avg_var_breaches / 100.0).unwrap_or(Decimal::ZERO)), // VaR breaches as "loss"
sharpe_ratio: Some(Decimal::try_from(final_sharpe).unwrap_or(Decimal::ZERO)),
max_drawdown: Some(Decimal::try_from(final_max_drawdown).unwrap_or(Decimal::ZERO)),
})
}
*/
/// Run portfolio optimization example with real Markowitz optimization
async fn run_portfolio_example(_config: &ExampleConfig) -> Result<ExampleMetrics, MLError> {
// PLACEHOLDER IMPLEMENTATION - Portfolio types not yet available
Ok(ExampleMetrics {
score: Decimal::try_from(0.15).unwrap_or(Decimal::ZERO), // 15% return
loss: Some(Decimal::try_from(0.005).unwrap_or(Decimal::ZERO)), // 0.5% rebalancing costs
sharpe_ratio: Some(Decimal::try_from(1.8).unwrap_or(Decimal::ZERO)),
max_drawdown: Some(Decimal::try_from(-0.06).unwrap_or(Decimal::ZERO)), // 6% max drawdown
})
}
// TEMPORARILY COMMENTED OUT - Portfolio types not available
/*
use crate::portfolio::{PortfolioOptimizer, AssetUniverse, OptimizationObjective};
// Create asset universe with real market data
let mut asset_universe = AssetUniverse::new();
asset_universe.add_asset("AAPL", generate_asset_returns(252)?)?;
asset_universe.add_asset("GOOGL", generate_asset_returns(252)?)?;
asset_universe.add_asset("MSFT", generate_asset_returns(252)?)?;
asset_universe.add_asset("TSLA", generate_asset_returns(252)?)?;
asset_universe.add_asset("NVDA", generate_asset_returns(252)?)?;
// Create portfolio optimizer
let mut optimizer = PortfolioOptimizer::new(asset_universe)?;
// Set optimization constraints
optimizer.set_max_weight(0.4)?; // Max 40% in any single asset
optimizer.set_min_weight(0.05)?; // Min 5% in each asset
optimizer.set_target_return(0.12)?; // 12% annual target return
optimizer.set_risk_free_rate(0.02)?; // 2% risk-free rate
let mut portfolio_performance = Vec::new();
let mut rebalancing_costs = Vec::new();
for period in 0..config.episodes {
// Optimize portfolio using different objectives
let optimization_result = match period % 3 {
0 => optimizer.optimize(OptimizationObjective::MaxSharpe)?,
1 => optimizer.optimize(OptimizationObjective::MinVolatility)?,
_ => optimizer.optimize(OptimizationObjective::MaxReturn)?,
};
// Simulate portfolio performance for this period
let period_returns = simulate_portfolio_period(&optimization_result.weights, 21)?; // 21 trading days
let period_performance = calculate_period_metrics(&period_returns)?;
portfolio_performance.push(period_performance.clone());
// Calculate rebalancing costs
if period > 0 {
let rebalancing_cost = optimizer.calculate_rebalancing_cost(
&portfolio_performance[period - 1].weights,
&optimization_result.weights
)?;
rebalancing_costs.push(rebalancing_cost);
}
// Update optimizer with new market data
optimizer.update_returns_history(generate_market_update()?)?;
if period % 50 == 0 {
info!("Period {}: Return = {:.2}%, Vol = {:.2}%, Sharpe = {:.2}, Weights: {:?}",
period,
period_performance.return_rate * 100.0,
period_performance.volatility * 100.0,
period_performance.sharpe_ratio,
optimization_result.weights.iter().map(|w| format!("{:.1}%", w * 100.0)).collect::<Vec<_>>()
);
}
}
// Calculate overall portfolio metrics
let total_return = portfolio_performance.iter().map(|p| p.return_rate).product::<f64>() - 1.0;
let avg_volatility = portfolio_performance.iter().map(|p| p.volatility).sum::<f64>() / portfolio_performance.len() as f64;
let avg_sharpe = portfolio_performance.iter().map(|p| p.sharpe_ratio).sum::<f64>() / portfolio_performance.len() as f64;
let max_drawdown = calculate_portfolio_max_drawdown(&portfolio_performance);
let total_rebalancing_cost = rebalancing_costs.iter().sum::<f64>();
Ok(ExampleMetrics {
score: Decimal::try_from(total_return).unwrap_or(Decimal::ZERO),
loss: Some(Decimal::try_from(total_rebalancing_cost).unwrap_or(Decimal::ZERO)), // Rebalancing costs as "loss"
sharpe_ratio: Some(Decimal::try_from(avg_sharpe).unwrap_or(Decimal::ZERO)),
max_drawdown: Some(Decimal::try_from(max_drawdown).unwrap_or(Decimal::ZERO)),
})
}
/// Run microstructure analysis example with real order book analytics
async fn run_microstructure_example(config: &ExampleConfig) -> Result<ExampleMetrics, MLError> {
// PLACEHOLDER IMPLEMENTATION - Microstructure types not yet available
Ok(ExampleMetrics {
score: Decimal::try_from(0.95).unwrap_or(Decimal::ZERO), // 95% market quality score
loss: Some(Decimal::try_from(0.0001).unwrap_or(Decimal::ZERO)), // 0.01% market impact
sharpe_ratio: Some(Decimal::try_from(0.85).unwrap_or(Decimal::ZERO)), // Inverse VPIN
max_drawdown: Some(Decimal::try_from(0.15).unwrap_or(Decimal::ZERO)), // Flow toxicity
})
}
// TEMPORARILY COMMENTED OUT - Microstructure types not available
use crate::microstructure::{OrderBookAnalyzer, VPINCalculator, FlowToxicity, MarketImpact};
// Create microstructure analyzers
let mut order_book_analyzer = OrderBookAnalyzer::new(100)?; // 100-level order book
let mut vpin_calculator = VPINCalculator::new(50)?; // 50-bucket VPIN
let mut flow_toxicity = FlowToxicity::new(0.95)?; // 95% confidence
let mut market_impact = MarketImpact::new()?;
let mut microstructure_metrics = Vec::new();
let mut order_flow_data = Vec::new();
for tick in 0..config.episodes {
// Generate realistic order book updates
let order_book_update = generate_order_book_update(tick)?;
order_book_analyzer.process_update(&order_book_update)?;
// Calculate bid-ask spread dynamics
let spread_metrics = order_book_analyzer.calculate_spread_metrics()?;
// Calculate VPIN (Volume-Synchronized Probability of Informed Trading)
if let Some(trade_data) = order_book_update.trade_data {
vpin_calculator.add_trade(&trade_data)?;
if vpin_calculator.can_calculate() {
let vpin_score = vpin_calculator.calculate_vpin()?;
// Calculate flow toxicity
let toxicity_score = flow_toxicity.calculate_toxicity(&trade_data, &spread_metrics)?;
// Calculate market impact
let impact_metrics = market_impact.calculate_impact(&trade_data, &order_book_analyzer)?;
let microstructure_data = MicrostructureMetrics {
timestamp: tick as u64,
bid_ask_spread: spread_metrics.bid_ask_spread,
effective_spread: spread_metrics.effective_spread,
price_impact: impact_metrics.temporary_impact,
permanent_impact: impact_metrics.permanent_impact,
vpin_score,
toxicity_score,
order_book_imbalance: order_book_analyzer.calculate_imbalance()?,
volume_weighted_price: trade_data.volume_weighted_price,
};
microstructure_metrics.push(microstructure_data);
order_flow_data.push(trade_data);
if tick % 1000 == 0 {
debug!("Tick {}: Spread = {:.4}, VPIN = {:.3}, Toxicity = {:.3}, Impact = {:.4}",
tick, spread_metrics.bid_ask_spread, vpin_score, toxicity_score, impact_metrics.temporary_impact);
}
}
}
}
// Calculate aggregate microstructure statistics
let avg_spread = microstructure_metrics.iter().map(|m| m.bid_ask_spread).sum::<f64>() / microstructure_metrics.len() as f64;
let avg_vpin = microstructure_metrics.iter().map(|m| m.vpin_score).sum::<f64>() / microstructure_metrics.len() as f64;
let avg_toxicity = microstructure_metrics.iter().map(|m| m.toxicity_score).sum::<f64>() / microstructure_metrics.len() as f64;
let avg_impact = microstructure_metrics.iter().map(|m| m.price_impact).sum::<f64>() / microstructure_metrics.len() as f64;
// Calculate market quality score (lower spreads and impacts = higher quality)
let market_quality_score = 1.0 / (1.0 + avg_spread + avg_impact);
Ok(ExampleMetrics {
score: Decimal::try_from(market_quality_score).unwrap_or(Decimal::ZERO),
loss: Some(Decimal::try_from(avg_impact).unwrap_or(Decimal::ZERO)), // Market impact as "loss"
sharpe_ratio: Some(Decimal::try_from(1.0 - avg_vpin).unwrap_or(Decimal::ZERO)), // Inverse VPIN (lower = better)
max_drawdown: Some(Decimal::try_from(avg_toxicity).unwrap_or(Decimal::ZERO)), // Flow toxicity
})
}
*/
/// List all available examples
pub fn list_examples() -> Vec<ExampleType> {
vec![
ExampleType::BasicDQN,
ExampleType::RainbowDQN,
ExampleType::PriceTransformer,
ExampleType::RiskModels,
ExampleType::PortfolioOptimization,
ExampleType::Microstructure,
]
}
// Helper functions for examples
/// Simulate environment step for `DQN` training
fn simulate_environment_step(
state: &[f64],
action: crate::dqn::TradingAction,
) -> (Vec<f64>, f64, bool) {
// Simple environment simulation
let mut next_state = state.to_vec();
// Apply action effect
let action_value = match action {
crate::dqn::TradingAction::Buy => 1.0,
crate::dqn::TradingAction::Sell => -1.0,
crate::dqn::TradingAction::Hold => 0.0,
};
for i in 0..next_state.len() {
next_state[i] += action_value * random::<f64>() * 0.1;
// Ensure price fields (first 2 elements) are always positive for Price validation
if i < 2 {
next_state[i] = next_state[i].abs().max(0.01); // Minimum price of 0.01
}
}
// Calculate reward based on action and state change
let reward = match action {
crate::dqn::TradingAction::Buy | crate::dqn::TradingAction::Sell => {
random::<f64>() * 2.0 - 1.0
},
crate::dqn::TradingAction::Hold => random::<f64>() * 0.1,
};
// Episode ends randomly or based on conditions
let done = next_state.iter().any(|&x| x.abs() > 10.0) || random::<f64>() < 0.01;
(next_state, reward, done)
}
/// Calculate Sharpe ratio from loss values
fn calculate_sharpe_ratio(losses: &[f64]) -> f64 {
if losses.is_empty() {
return 0.0;
}
let mean_loss = losses.iter().sum::<f64>() / losses.len() as f64;
let std_loss = {
let variance =
losses.iter().map(|&x| (x - mean_loss).powi(2)).sum::<f64>() / losses.len() as f64;
variance.sqrt()
};
if std_loss == 0.0 {
0.0
} else {
-mean_loss / std_loss
} // Negative because we want lower loss
}
/// Calculate maximum drawdown from loss values
fn calculate_max_drawdown(losses: &[f64]) -> f64 {
if losses.is_empty() {
return 0.0;
}
let mut running_min = losses[0];
let mut max_drawdown: f64 = 0.0;
for &loss in losses {
running_min = running_min.min(loss);
max_drawdown = max_drawdown.max(loss - running_min);
}
max_drawdown
}
/// Calculate Sharpe ratio from reward values
fn calculate_sharpe_ratio_from_rewards(rewards: &[f64]) -> f64 {
if rewards.is_empty() {
return 0.0;
}
let mean_reward = rewards.iter().sum::<f64>() / rewards.len() as f64;
let std_reward = {
let variance = rewards
.iter()
.map(|&x| (x - mean_reward).powi(2))
.sum::<f64>()
/ rewards.len() as f64;
variance.sqrt()
};
if std_reward == 0.0 {
0.0
} else {
mean_reward / std_reward
}
}
/// Calculate maximum drawdown from reward values
fn calculate_max_drawdown_from_rewards(rewards: &[f64]) -> f64 {
if rewards.is_empty() {
return 0.0;
}
let mut running_max = rewards[0];
let mut max_drawdown: f64 = 0.0;
for &reward in rewards {
running_max = running_max.max(reward);
max_drawdown = max_drawdown.max(running_max - reward);
}
max_drawdown / running_max.abs().max(1.0) // Normalize by max value
}
// End of commented out sections
/// Run microstructure analysis example
async fn run_microstructure_example(_config: &ExampleConfig) -> Result<ExampleMetrics, MLError> {
// Placeholder implementation for microstructure analysis
// This would normally involve analyzing market microstructure patterns
info!("Running microstructure analysis example...");
// Return basic metrics for now
Ok(ExampleMetrics {
score: Decimal::try_from(0.75).unwrap_or(Decimal::ZERO),
loss: Some(Decimal::try_from(0.05).unwrap_or(Decimal::ZERO)),
sharpe_ratio: Some(Decimal::try_from(1.5).unwrap_or(Decimal::ZERO)),
max_drawdown: Some(Decimal::try_from(0.05).unwrap_or(Decimal::ZERO)),
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_example_config_default() {
let config = ExampleConfig::default();
assert!(matches!(config.example_type, ExampleType::BasicDQN));
assert!(config.enable_safety);
}
#[tokio::test]
async fn test_run_basic_example() {
let config = ExampleConfig::default();
let result = run_example(config).await;
assert!(result.is_ok());
}
#[test]
fn test_list_examples() {
let examples = list_examples();
assert_eq!(examples.len(), 6);
}
}