From b555aafe7707df4fa259b6abf0e2f2446203e6f9 Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Wed, 25 Mar 2026 20:38:56 +0100 Subject: [PATCH] feat: wire MBP-10 + trades data into hyperopt campaign config Adds mbp10_data_dir and trades_data_dir to CampaignConfig, passed to DQNTrainer via with_ofi_data_dirs(). Enables OFI features (VPIN, Kyle's Lambda, etc.) in hyperopt campaigns. dqn_full() now defaults to 50 epochs/trial for baseline runs. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/ml/src/hyperopt/campaign.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/crates/ml/src/hyperopt/campaign.rs b/crates/ml/src/hyperopt/campaign.rs index dff38cd67..a644165c7 100644 --- a/crates/ml/src/hyperopt/campaign.rs +++ b/crates/ml/src/hyperopt/campaign.rs @@ -49,6 +49,13 @@ pub struct CampaignConfig { pub results_base_dir: PathBuf, /// Campaign execution mode. pub mode: CampaignMode, + /// Optional MBP-10 order book data directory for OFI features. + /// When set, enables 8 additional OFI features (VPIN, Kyle's Lambda, etc.) + /// producing state_dim=80 instead of 72. + pub mbp10_data_dir: Option, + /// Optional trades data directory for VPIN/Kyle's Lambda computation. + /// Required alongside mbp10_data_dir for full OFI feature extraction. + pub trades_data_dir: Option, } impl CampaignConfig { @@ -65,11 +72,13 @@ impl CampaignConfig { max_epochs_per_trial: 81, results_base_dir: PathBuf::from("ml/hyperopt_results"), mode: CampaignMode::Standard, + mbp10_data_dir: None, + trades_data_dir: None, } } /// DQN full pipeline: all 3 training phases (BC → RL → Refinement). - /// 20 trials × 100 epochs — covers Phase 1 (warmup), Phase 2 (full RL), + /// 20 trials × 50 epochs — covers Phase 1 (warmup), Phase 2 (full RL), /// and Phase 3 (refinement with shrink-and-perturb). pub fn dqn_full() -> Self { let caps = cached_capabilities(); @@ -80,9 +89,11 @@ impl CampaignConfig { data_dir: PathBuf::from("test_data/real/databento/ml_training"), max_batch_size: max_batch, early_stopping_eta: 3, - max_epochs_per_trial: 100, + max_epochs_per_trial: 50, results_base_dir: PathBuf::from("ml/hyperopt_results"), mode: CampaignMode::Full, + mbp10_data_dir: None, + trades_data_dir: None, } } @@ -99,6 +110,8 @@ impl CampaignConfig { max_epochs_per_trial: 81, results_base_dir: PathBuf::from("ml/hyperopt_results"), mode: CampaignMode::Standard, + mbp10_data_dir: None, + trades_data_dir: None, } } @@ -189,8 +202,12 @@ fn run_dqn_campaign( .seed(42) .build(); - // Create DQN adapter - let adapter = DQNTrainer::new(&config.data_dir, config.max_epochs_per_trial)?; + // Create DQN adapter with optional MBP-10 + trades data for OFI features + let adapter = DQNTrainer::new(&config.data_dir, config.max_epochs_per_trial)? + .with_ofi_data_dirs( + config.mbp10_data_dir.as_ref().map(|p| p.to_string_lossy().into_owned()), + config.trades_data_dir.as_ref().map(|p| p.to_string_lossy().into_owned()), + ); // Run optimization let result = optimizer.optimize(adapter)?; @@ -318,6 +335,8 @@ mod tests { max_epochs_per_trial: 10, // Quick validation results_base_dir: PathBuf::from("ml/hyperopt_results"), mode: CampaignMode::Full, + mbp10_data_dir: None, + trades_data_dir: None, }; println!("Starting local DQN hyperopt: {} trials × {} epochs ({:?} mode)",