Files
foxhunt/ml/examples/train_ppo_es_fut.rs
jgrusewski 3853988af7 feat(hyperopt): Complete DQN hyperopt analysis and PSO optimizer fix
- Fixed PSO budget calculation bug in ml/src/hyperopt/optimizer.rs
  - Root cause: Division by n_particles in sequential execution
  - Now correctly calculates max_iters = remaining_trials (no division)
  - Result: 50 trials complete instead of 23 (100% vs 46%)

- Added comprehensive DQN hyperopt results analysis
  - 39/50 trials analyzed across 2 RunPod deployments
  - Best hyperparameters identified: LR 4.89e-5 (ultra-low)
  - Created DQN_HYPEROPT_RESULTS_SUMMARY.md with expert validation

- GitLab CI/CD pipeline operational (48 lines fixed)
  - Fixed YAML syntax errors (unquoted colons)
  - All 7 jobs validated and working

- Warning cleanup complete (136 → 0 warnings)
  - Removed 143 lines dead code
  - Fixed visibility, unused imports, Debug traits

- Archived Wave D reports to docs/archive/
  - 8 early stopping reports moved
  - Root directory cleaned up

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 21:49:07 +01:00

243 lines
8.7 KiB
Rust

//! Train PPO on ES.FUT Market Data
//!
//! This example demonstrates training a PPO (Proximal Policy Optimization) model
//! on real ES.FUT (E-mini S&P 500) futures data for trading strategy development.
//!
//! ## Usage
//!
//! ```bash
//! cargo run -p ml --example train_ppo_es_fut --release
//! ```
//!
//! ## Configuration
//!
//! - State dimension: 26 (OHLCV + technical indicators)
//! - Actions: 3 (Buy, Sell, Hold)
//! - Training epochs: 50 (configurable)
//! - GPU: Automatic (RTX 3050 Ti if available, else CPU)
//! - Checkpoints: Saved every 10 epochs to `ml/checkpoints/`
//!
//! ## Expected Results
//!
//! - Policy improvement > 20% over 50 epochs
//! - Value loss decreasing trend
//! - Checkpoint file ~10-20 KB per epoch
//! - Training time: ~5-10 minutes on CPU, ~2-3 minutes on GPU
//!
//! ## Output
//!
//! - Checkpoint: `ml/checkpoints/ppo_es_fut_v1_actor_epoch_50.safetensors`
//! - Checkpoint: `ml/checkpoints/ppo_es_fut_v1_critic_epoch_50.safetensors`
//! - Metrics: Epoch-by-epoch training progress
use anyhow::Result;
use ml::trainers::ppo::{PpoHyperparameters, PpoTrainer, PpoTrainingMetrics};
use std::f32::consts::PI;
/// Generate synthetic ES.FUT market data
///
/// In production, this would load from Parquet files or database.
/// For now, we generate realistic synthetic data with:
/// - OHLCV patterns (sine wave price movements)
/// - Technical indicators (RSI, MACD, Bollinger Bands, etc.)
/// - Realistic price ranges (~4000-4200 for ES.FUT)
fn generate_market_data(num_bars: usize, state_dim: usize) -> Vec<Vec<f32>> {
println!(
"🔄 Generating {} bars of synthetic ES.FUT data...",
num_bars
);
let mut data: Vec<Vec<f32>> = Vec::with_capacity(num_bars);
for i in 0..num_bars {
let t = i as f32 / num_bars as f32;
// Base price with trend and volatility
let base_price = 4100.0 + 50.0 * (t * 2.0 * PI).sin() + 20.0 * (t * 10.0 * PI).sin();
// OHLCV features
let close = base_price;
let high = close * 1.005; // 0.5% above close
let low = close * 0.995; // 0.5% below close
let open = close * (1.0 + 0.002 * (t * 5.0 * PI).sin());
let volume = 1000.0 + 200.0 * (t * 4.0 * PI).cos();
// Technical indicators
let rsi = 50.0 + 20.0 * (t * PI).sin(); // RSI oscillating around 50
let macd = (t * 2.0 * PI).sin(); // MACD signal
let signal = (t * 2.0 * PI - 0.5).sin(); // Signal line
let atr = 15.0 + 5.0 * (t * 3.0 * PI).cos(); // ATR
let bb_lower = close * 0.98; // Bollinger lower
let bb_upper = close * 1.02; // Bollinger upper
let ema = close * (1.0 + 0.001 * (t * PI).cos()); // EMA
// Build state vector
let mut state = vec![
close, high, low, open, volume, rsi, macd, signal, atr, bb_lower, bb_upper, ema,
];
// Add log return (used for reward calculation)
let log_return = if i > 0 {
let prev_close = data[i - 1][0]; // Previous close
(close / prev_close).ln()
} else {
0.0
};
state.push(log_return);
// Pad to state_dim with zeros
while state.len() < state_dim {
state.push(0.0);
}
data.push(state);
}
println!("✓ Generated {} bars (state_dim={})", data.len(), state_dim);
data
}
#[tokio::main]
async fn main() -> Result<()> {
// Initialize logging
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.init();
println!("\n🚀 PPO Training on ES.FUT Market Data");
println!("=====================================\n");
// Configuration
let state_dim = 26;
let num_bars = 5000; // 5000 bars for more robust training
let num_epochs = 50;
let checkpoint_dir = "ml/checkpoints";
// Generate synthetic market data (in production, load from Parquet)
let market_data = generate_market_data(num_bars, state_dim);
// Configure PPO hyperparameters
let mut hyperparams = PpoHyperparameters::conservative();
hyperparams.epochs = num_epochs;
hyperparams.learning_rate = 3e-4; // Standard PPO learning rate
hyperparams.batch_size = 128; // Larger batch for stability
hyperparams.rollout_steps = 2048; // Standard rollout length
hyperparams.minibatch_size = 64; // Mini-batch size
hyperparams.gamma = 0.99; // Discount factor
hyperparams.gae_lambda = 0.95; // GAE parameter
hyperparams.clip_epsilon = 0.2; // PPO clip range
hyperparams.vf_coef = 0.5; // Value loss coefficient
hyperparams.ent_coef = 0.01; // Entropy coefficient
hyperparams.early_stopping_enabled = true;
hyperparams.min_value_loss_improvement_pct = 2.0;
hyperparams.min_explained_variance = 0.4;
hyperparams.plateau_window = 30;
hyperparams.min_epochs_before_stopping = 50; // No early stopping for full training
println!("📋 Training Configuration:");
println!(" • State dimension: {}", state_dim);
println!(" • Market data: {} bars", num_bars);
println!(" • Training epochs: {}", num_epochs);
println!(" • Learning rate: {}", hyperparams.learning_rate);
println!(" • Batch size: {}", hyperparams.batch_size);
println!(" • Rollout steps: {}", hyperparams.rollout_steps);
println!(" • Checkpoint dir: {}", checkpoint_dir);
// Detect GPU availability
let use_gpu = candle_core::Device::cuda_if_available(0).is_ok();
println!(
" • Device: {}\n",
if use_gpu { "GPU (CUDA)" } else { "CPU" }
);
// Create PPO trainer
let trainer = PpoTrainer::new(hyperparams, state_dim, checkpoint_dir, use_gpu, None)?;
println!("✓ PPO trainer initialized\n");
println!("🏋️ Starting training...\n");
println!(
"{:<8} {:<12} {:<12} {:<12} {:<12}",
"Epoch", "Policy Loss", "Value Loss", "Expl. Var.", "Mean Reward"
);
println!("{}", "-".repeat(64));
// Track metrics for summary
let mut all_metrics = Vec::new();
// Train PPO model
let final_metrics = trainer
.train(market_data, |metrics: PpoTrainingMetrics| {
println!(
"{:<8} {:<12.4} {:<12.4} {:<12.4} {:<12.4}",
metrics.epoch,
metrics.policy_loss,
metrics.value_loss,
metrics.explained_variance,
metrics.mean_reward
);
all_metrics.push(metrics);
})
.await?;
println!("{}", "-".repeat(64));
println!("\n✅ Training complete!\n");
// Print summary statistics
println!("📊 Training Summary:");
println!(" • Final epoch: {}", final_metrics.epoch);
println!(" • Policy loss: {:.4}", final_metrics.policy_loss);
println!(" • Value loss: {:.4}", final_metrics.value_loss);
println!(" • KL divergence: {:.4}", final_metrics.kl_divergence);
println!(
" • Explained variance: {:.4}",
final_metrics.explained_variance
);
println!(" • Mean reward: {:.4}", final_metrics.mean_reward);
println!(" • Std reward: {:.4}", final_metrics.std_reward);
println!(" • Entropy: {:.4}\n", final_metrics.entropy);
// Compute improvement metrics
if let (Some(first), Some(last)) = (all_metrics.first(), all_metrics.last()) {
let policy_improvement =
((first.policy_loss - last.policy_loss) / first.policy_loss.abs()) * 100.0;
let value_improvement = ((first.value_loss - last.value_loss) / first.value_loss) * 100.0;
println!("📈 Improvement Over Training:");
println!(" • Policy loss: {:.2}%", policy_improvement);
println!(" • Value loss: {:.2}%\n", value_improvement);
// Check if target achieved
if policy_improvement > 20.0 {
println!(
"🎯 Target achieved: Policy improved by {:.2}% (target: >20%)",
policy_improvement
);
} else {
println!(
"⚠️ Target not met: Policy improved by {:.2}% (target: >20%)",
policy_improvement
);
println!(" Consider training for more epochs or tuning hyperparameters");
}
}
// Print checkpoint locations
println!("\n💾 Model Checkpoints:");
println!(
" • Actor: {}/ppo_es_fut_v1_actor_epoch_{}.safetensors",
checkpoint_dir, final_metrics.epoch
);
println!(
" • Critic: {}/ppo_es_fut_v1_critic_epoch_{}.safetensors",
checkpoint_dir, final_metrics.epoch
);
println!("\n🎉 PPO training pipeline complete!");
println!("\nNext steps:");
println!("1. Validate checkpoint loading: cargo test -p ml test_checkpoint_loading");
println!("2. Backtest strategy with trained model");
println!("3. Deploy to paper trading environment\n");
Ok(())
}