feat: add Phase Risk (P4) to four-phase hyperopt — 8D risk parameter search

Add HyperoptPhase::Risk variant that fixes dynamics + architecture from
Phase 2 best params and searches only risk parameters (8D):
kelly_fractional, dd_threshold, loss_aversion, time_decay_rate,
q_gap_threshold, w_dsr, w_pnl, w_dd.

CLI: --phase risk (alongside fast, full, reward)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-03-23 12:51:49 +01:00
parent 33d91cce5b
commit d1b183c6dc
3 changed files with 32 additions and 5 deletions

View File

@@ -75,9 +75,9 @@
//! Hyperopt RL Runner for DQN/PPO on Real Databento Market Data
//!
//! Runs hyperparameter optimization using PSO/TPE for DQN, PPO, or both models
//! on downloaded Databento futures data. Supports two-phase optimization:
//! on downloaded Databento futures data. Supports four-phase optimization:
//!
//! ## Two-Phase Workflow (recommended)
//! ## Four-Phase Workflow (recommended)
//!
//! ```bash
//! # Phase 1 (default): fix small architecture, search learning dynamics (~15D, ~30 min)
@@ -195,10 +195,11 @@ struct Args {
#[arg(long)]
trades_data_dir: Option<PathBuf>,
/// Three-phase hyperopt phase selection:
/// Four-phase hyperopt phase selection:
/// fast — Phase 1: fix architecture + reward, search dynamics (~17D). DEFAULT.
/// full — Phase 2: fix dynamics, search architecture (~5D).
/// reward — Phase 3: fix dynamics + architecture, search reward weights only (7D, fastest).
/// risk — Phase 4: fix dynamics + architecture, search risk params (8D).
#[arg(long, default_value = "fast")]
phase: String,
@@ -538,7 +539,8 @@ fn main() -> Result<()> {
"fast" => HyperoptPhase::Fast,
"full" => HyperoptPhase::Full(load_best_vec("full")),
"reward" => HyperoptPhase::Reward(load_best_vec("reward")),
other => panic!("Invalid --phase '{}'. Must be 'fast', 'full', or 'reward'.", other),
"risk" => HyperoptPhase::Risk(load_best_vec("risk")),
other => panic!("Invalid --phase '{}'. Must be 'fast', 'full', 'reward', or 'risk'.", other),
};
info!("Hyperopt phase: {:?}", args.phase);
set_hyperopt_phase(phase);

View File

@@ -615,6 +615,14 @@ impl ParameterSpace for DQNParams {
tracing::info!("Phase REWARD: fixing {} dynamics+architecture params, searching 7 reward dims", best_vec.len());
// Bounds will be applied after vec construction below.
}
HyperoptPhase::Risk(ref best_vec) => {
// Phase 4: fix dynamics + architecture from Phase 2 best params.
// Search ONLY risk params (8D):
// kelly_fractional (17), dd_threshold (35), loss_aversion (36),
// time_decay_rate (37), q_gap_threshold (38), w_dsr (31), w_pnl (32), w_dd (33).
tracing::info!("Phase RISK: fixing {} dynamics+architecture params, searching 8 risk dims", best_vec.len());
// Bounds will be applied after vec construction below.
}
}
let mut bounds = vec![
@@ -683,6 +691,19 @@ impl ParameterSpace for DQNParams {
}
}
// Phase RISK: fix ALL dims EXCEPT risk params (8D).
// Risk dims: kelly_fractional (17), w_dsr (31), w_pnl (32), w_dd (33),
// dd_threshold (35), loss_aversion (36), time_decay_rate (37),
// q_gap_threshold (38).
if let HyperoptPhase::Risk(ref best_vec) = phase {
let risk_dims: &[usize] = &[17, 31, 32, 33, 35, 36, 37, 38];
for (i, bv) in best_vec.iter().enumerate() {
if i < bounds.len() && !risk_dims.contains(&i) {
bounds[i] = (*bv, *bv);
}
}
}
bounds
}
@@ -2626,7 +2647,7 @@ impl HyperparameterOptimizable for DQNTrainer {
// penalize VRAM estimate for replay buffer capacity.
let vram_buffer = if budget.gpu_memory_mb <= 8192 { 0 } else { clamped_buffer_size };
// Aligned state_dim: 64 with OFI (58→64), 56 without (50→56)
let aligned_state_dim: usize = if self.mbp10_data_dir.is_some() { 64 } else { 56 };
let aligned_state_dim: usize = if self.mbp10_data_dir.is_some() { 80 } else { 72 };
let vram_check = budget.trial_fits_vram(
params.hidden_dim_base,
params.batch_size,

View File

@@ -294,6 +294,10 @@ pub enum HyperoptPhase {
/// Phase 3: fix dynamics + architecture from Phase 2 JSON, search only reward weights (7D).
/// Fastest phase (~10s/trial) — only experience collection changes, not network or training.
Reward(Vec<f64>),
/// Phase 4: fix dynamics + architecture from Phase 2, search risk params (8D).
/// Risk dims: kelly_fractional, dd_threshold, loss_aversion, time_decay_rate,
/// q_gap_threshold, w_dsr, w_pnl, w_dd.
Risk(Vec<f64>),
}
impl Default for HyperoptPhase {