feat: Wave 1 - Update HIGH RISK files (225→54 features)

WAVE 21: Core type definitions and trainer configs updated

Files Modified (13 files):
- ml/src/features/extraction.rs: FeatureVector = [f64; 54]
- common/src/features/types.rs: Added FeatureVector54
- ml/src/trainers/dqn.rs: state_dim 225→54
- ml/src/trainers/ppo.rs: state_dim 225→54
- ml/src/dqn/dqn.rs, config.rs, replay_buffer.rs: Updated configs
- ml/src/hyperopt/adapters/: All adapters updated to 54-dim
- ml/src/features/unified.rs: Struct fields updated
- ml/src/trainers/tft_parquet.rs: Return types updated

Agents Deployed: 5 parallel agents
Test Results: cargo check --package ml --lib PASSING

Next: Wave 2 (examples), Wave 3 (tests), Wave 4 (OFI integration)

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2025-11-23 00:41:22 +01:00
parent 3a880bae61
commit 28ee27b2bb
30 changed files with 5359 additions and 143 deletions

View File

@@ -445,7 +445,7 @@ impl HyperparameterOptimizable for ContinuousPPOTrainer {
// Create continuous PPO config
let policy_config = ContinuousPolicyConfig {
state_dim: 225,
state_dim: 54,
hidden_dims: vec![128, 64],
action_min: params.action_min as f32,
action_max: params.action_max as f32,
@@ -454,7 +454,7 @@ impl HyperparameterOptimizable for ContinuousPPOTrainer {
};
let ppo_config = ContinuousPPOConfig {
state_dim: 225,
state_dim: 54,
policy_config,
value_hidden_dims: vec![128, 64],
policy_learning_rate: params.policy_lr,

View File

@@ -526,7 +526,7 @@ pub struct DQNMetrics {
/// - **DBN data dir**: Market data source (OHLCV bars from Databento)
/// - **Epochs**: Number of training epochs per trial
/// - **Device**: CUDA GPU (falls back to CPU if unavailable)
/// - **Features**: 125 features (Wave 16D: Reduced from 225 by Agent 37)
/// - **Features**: 54 features (Wave 1 Agent 5: Reduced from 225→54)
///
/// ## Fixed Architecture
///
@@ -784,7 +784,7 @@ impl DQNTrainer {
/// - No Parquet or DBN files found
/// - File format is invalid
/// - Feature extraction fails
fn load_training_data(&self) -> anyhow::Result<Vec<([f32; 225], f64)>> {
fn load_training_data(&self) -> anyhow::Result<Vec<([f32; 54], f64)>> {
use std::path::Path;
let dir_path = Path::new(&self.dbn_data_dir);
@@ -803,10 +803,10 @@ impl DQNTrainer {
}
}
/// Load training data from Parquet file
/// Load training data from Parquet file (54-feature vectors)
///
/// Reads OHLCV bars from Parquet file, extracts 225-feature vectors,
/// and creates (state, reward) tuples for DQN training.
/// Reads OHLCV bars from Parquet file, extracts 54-feature vectors,
/// and creates (state, reward) tuples for DQN training
///
/// # Returns
///
@@ -818,7 +818,7 @@ impl DQNTrainer {
/// - No Parquet file found in directory
/// - Parquet file is malformed
/// - Feature extraction fails
fn load_from_parquet(&self) -> anyhow::Result<Vec<([f32; 225], f64)>> {
fn load_from_parquet(&self) -> anyhow::Result<Vec<([f32; 54], f64)>> {
use crate::features::extraction::OHLCVBar;
use arrow::array::{Array, Float64Array, PrimitiveArray, UInt64Array};
use arrow::datatypes::TimestampNanosecondType;
@@ -925,16 +925,16 @@ impl DQNTrainer {
/// # Returns
///
/// Error indicating DBN loading not yet implemented
fn load_from_dbn(&self) -> anyhow::Result<Vec<([f32; 225], f64)>> {
fn load_from_dbn(&self) -> anyhow::Result<Vec<([f32; 54], f64)>> {
Err(anyhow::anyhow!(
"DBN file loading not yet implemented for DQN. Please use Parquet files instead."
))
}
/// Extract 225-feature vectors from OHLCV bars and create training data
/// Extract 54-feature vectors from OHLCV bars and create training data
///
/// Uses the production feature extraction API (extract_ml_features) to
/// generate 225-feature vectors from OHLCV bars. Creates dummy rewards
/// generate 54-feature vectors from OHLCV bars. Creates dummy rewards
/// for DQN training (actual rewards are computed during training).
///
/// # Arguments
@@ -943,8 +943,8 @@ impl DQNTrainer {
///
/// # Returns
///
/// Vector of (state, reward) tuples where:
/// - state: [f32; 225] feature vector
/// Vector of (state, reward) tuples where
/// - state: [f32; 54] feature vector
/// - reward: f64 dummy reward (0.0)
///
/// # Errors
@@ -955,11 +955,11 @@ impl DQNTrainer {
fn extract_features_and_targets(
&self,
ohlcv_bars: &[crate::features::extraction::OHLCVBar],
) -> anyhow::Result<Vec<([f32; 225], f64)>> {
) -> anyhow::Result<Vec<([f32; 54], f64)>> {
use crate::features::extraction::extract_ml_features;
info!(
"Extracting 225-feature vectors from {} OHLCV bars...",
"Extracting 54-feature vectors from {} OHLCV bars...",
ohlcv_bars.len()
);
@@ -971,18 +971,18 @@ impl DQNTrainer {
));
}
// Extract features using production API (returns Vec<[f64; 225]>)
// Extract features using production API (returns Vec<[f64; 54]>)
let feature_vectors = extract_ml_features(ohlcv_bars)
.map_err(|e| anyhow::anyhow!("Feature extraction failed: {}", e))?;
info!("Extracted {} feature vectors", feature_vectors.len());
// Convert to [f32; 225] and create dummy rewards (actual rewards computed during training)
let training_data: Vec<([f32; 225], f64)> = feature_vectors
// Convert to [f32; 54] and create dummy rewards (actual rewards computed during training)
let training_data: Vec<([f32; 54], f64)> = feature_vectors
.into_iter()
.map(|vec_f64| {
// Convert [f64; 225] to [f32; 225]
let mut vec_f32 = [0.0_f32; 225];
// Convert [f64; 54] to [f32; 54]
let mut vec_f32 = [0.0_f32; 54];
for (i, &val) in vec_f64.iter().enumerate() {
vec_f32[i] = val as f32;
}

View File

@@ -360,7 +360,7 @@ impl HyperparameterOptimizable for PPOTrainer {
// Create PPO config with trial hyperparameters
let ppo_config = PPOConfig {
state_dim: 225, // Wave D features
state_dim: 54, // Wave D features
num_actions: 45, // 5×3×3 factored action space (size × order type × duration)
policy_hidden_dims: vec![128, 64],
value_hidden_dims: vec![256, 128, 64],
@@ -585,8 +585,8 @@ impl HyperparameterOptimizable for PPOTrainer {
impl PPOTrainer {
/// Load training data from Parquet/DBN files
///
/// Returns feature vectors (225 dims) with target close prices for trajectory generation
fn load_training_data(&self) -> anyhow::Result<Vec<([f32; 225], f64)>> {
/// Returns feature vectors (54 dims) with target close prices for trajectory generation
fn load_training_data(&self) -> anyhow::Result<Vec<([f32; 54], f64)>> {
use std::path::Path;
let dir_path = Path::new(&self.dbn_data_dir);
@@ -606,7 +606,7 @@ impl PPOTrainer {
}
/// Load training data from Parquet file (optimized path)
fn load_from_parquet(&self) -> anyhow::Result<Vec<([f32; 225], f64)>> {
fn load_from_parquet(&self) -> anyhow::Result<Vec<([f32; 54], f64)>> {
use crate::features::extraction::OHLCVBar;
use arrow::array::{Array, Float64Array, PrimitiveArray, UInt64Array};
use arrow::datatypes::TimestampNanosecondType;
@@ -704,7 +704,7 @@ impl PPOTrainer {
}
/// Load training data from DBN files
fn load_from_dbn(&self) -> anyhow::Result<Vec<([f32; 225], f64)>> {
fn load_from_dbn(&self) -> anyhow::Result<Vec<([f32; 54], f64)>> {
let dbn_files: Vec<_> = std::fs::read_dir(&self.dbn_data_dir)?
.filter_map(|entry| entry.ok())
.filter(|entry| entry.path().extension().and_then(|s| s.to_str()) == Some("dbn"))
@@ -724,15 +724,15 @@ impl PPOTrainer {
));
}
/// Extract 225-feature vectors and targets from OHLCV bars
/// Extract 54-feature vectors and targets from OHLCV bars
fn extract_features_and_targets(
&self,
ohlcv_bars: &[crate::features::extraction::OHLCVBar],
) -> anyhow::Result<Vec<([f32; 225], f64)>> {
) -> anyhow::Result<Vec<([f32; 54], f64)>> {
use crate::features::extraction::extract_ml_features;
info!(
"Extracting 225-feature vectors from {} OHLCV bars...",
"Extracting 54-feature vectors from {} OHLCV bars...",
ohlcv_bars.len()
);
@@ -744,12 +744,12 @@ impl PPOTrainer {
));
}
// Extract features using production API (returns Vec<[f64; 225]>)
// Extract features using production API (returns Vec<[f64; 54]>)
let feature_vectors = extract_ml_features(ohlcv_bars)
.map_err(|e| anyhow::anyhow!("Feature extraction failed: {}", e))?;
info!(
"Extracted {} feature vectors (225 dimensions each)",
"Extracted {} feature vectors (54 dimensions each)",
feature_vectors.len()
);
@@ -767,13 +767,13 @@ impl PPOTrainer {
let features_f32: Vec<f32> = feature_vectors[i].iter().map(|&f| f as f32).collect();
// Convert Vec to fixed-size array
if features_f32.len() == 225 {
let mut feature_array = [0.0f32; 225];
if features_f32.len() == 54 {
let mut feature_array = [0.0f32; 54];
feature_array.copy_from_slice(&features_f32);
training_data.push((feature_array, next_close));
} else {
warn!(
"Feature vector has wrong size: {} != 225",
"Feature vector has wrong size: {} != 54",
features_f32.len()
);
}
@@ -787,15 +787,15 @@ impl PPOTrainer {
.iter()
.map(|&f| f as f32)
.collect();
if features_f32.len() == 225 {
let mut feature_array = [0.0f32; 225];
if features_f32.len() == 54 {
let mut feature_array = [0.0f32; 54];
feature_array.copy_from_slice(&features_f32);
training_data.push((feature_array, last_close));
}
}
info!(
"Created {} training samples with 225-dim features",
"Created {} training samples with 54-dim features",
training_data.len()
);
@@ -808,7 +808,7 @@ impl PPOTrainer {
/// to select actions and computing rewards based on price movements.
fn generate_trajectories_from_data(
&self,
data: &[([f32; 225], f64)],
data: &[([f32; 54], f64)],
num_episodes: usize,
) -> anyhow::Result<TrajectoryBatch> {
use crate::ppo::trajectories::{Trajectory, TrajectoryStep};