Files
foxhunt/ml/examples/validate_regime_features.rs
jgrusewski f946dcd952 feat: Wave 2 - Update MEDIUM RISK files (225→54 features)
WAVE 22: All examples, benchmarks, and data loaders updated

Files Modified (41 files):
- DQN examples: 7 files (train_dqn, evaluate_dqn, validate_dqn, etc.)
- PPO examples: 6 files (train_ppo, continuous_ppo, benchmark_ppo, etc.)
- TFT examples: 9 files (train_tft, validate_tft, benchmark_tft, etc.)
- MAMBA-2 examples: 3 files (train_mamba2, verify_dimensions, etc.)
- Benchmarks: 5 files (cuda_speedup, weight_caching, future_decoder, etc.)
- Data loaders: 7 files (parquet_utils, dbn_sequence_loader, tlob_loader, etc.)
- Integration: 4 files (load_parquet_data, streaming loaders, etc.)

Key Changes:
- state_dim: 225 → 54 (DQN, PPO)
- input_dim: 225 → 54 (TFT)
- d_model: 225 → 54 (MAMBA-2)
- Memory: 1.8KB → 0.43KB per vector (76% reduction)
- All tensor shapes updated: (batch, 225) → (batch, 54)

Agents Deployed: 5 parallel agents
Validation: cargo check PASSING

Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 00:57:17 +01:00

363 lines
10 KiB
Rust

//! Agent F4: Wave D Features 201-224 Validation Script
//!
//! This script validates all regime detection features using synthetic data
//! and measures their latency performance.
use chrono::Utc;
use ml::ensemble::MarketRegime;
use ml::features::extraction::OHLCVBar as ExtBar;
use ml::features::regime_adaptive::RegimeAdaptiveFeatures;
use ml::features::regime_adx::{OHLCVBar as AdxBar, RegimeADXFeatures};
use ml::features::regime_cusum::RegimeCUSUMFeatures;
use std::time::Instant;
fn main() {
println!("=== Agent F4: Wave D Features 201-224 Validation ===\n");
// Validate CUSUM features (201-210)
validate_cusum_features();
// Validate ADX features (211-215)
validate_adx_features();
// Validate Adaptive features (221-224)
validate_adaptive_features();
// Note: Transition features (216-220) are stubs, skip validation
println!("\n=== Validation Complete ===");
}
fn validate_cusum_features() {
println!("## Validating CUSUM Features (201-210)");
let mut features = RegimeCUSUMFeatures::new(0.0, 1.0, 0.5, 5.0);
let mut passing = 0;
let mut total = 0;
// Test 1: Initialization
total += 1;
let result = features.update(0.0);
if result.len() == 10 && result.iter().all(|&x| x.is_finite()) {
passing += 1;
println!("✓ Test 1: Initialization - PASS");
} else {
println!("✗ Test 1: Initialization - FAIL");
}
// Test 2: Positive break detection
total += 1;
let mut detected_break = false;
for _ in 0..10 {
let result = features.update(3.0);
if result[2] == 1.0 {
// Feature 203: break indicator
detected_break = true;
break;
}
}
if detected_break {
passing += 1;
println!("✓ Test 2: Positive break detection - PASS");
} else {
println!("✗ Test 2: Positive break detection - FAIL");
}
// Test 3: Normalization bounds
total += 1;
features = RegimeCUSUMFeatures::new(0.0, 1.0, 0.5, 5.0);
for _ in 0..20 {
features.update(5.0);
}
let result = features.update(5.0);
if result[0] >= 0.0 && result[0] <= 1.5 && result[1] >= 0.0 && result[1] <= 1.5 {
passing += 1;
println!("✓ Test 3: Normalization bounds - PASS");
} else {
println!(
"✗ Test 3: Normalization bounds - FAIL (S+={}, S-={})",
result[0], result[1]
);
}
// Test 4: Latency benchmark
total += 1;
let mut features = RegimeCUSUMFeatures::new(0.0, 1.0, 0.5, 5.0);
let iterations = 10000;
let start = Instant::now();
for i in 0..iterations {
let value = ((i as f64) * 0.1).sin() * 2.0;
features.update(value);
}
let elapsed = start.elapsed();
let avg_latency_ns = elapsed.as_nanos() / iterations as u128;
let avg_latency_us = avg_latency_ns as f64 / 1000.0;
if avg_latency_us < 50.0 {
passing += 1;
println!(
"✓ Test 4: Latency - PASS ({:.2}μs < 50μs target)",
avg_latency_us
);
} else {
println!(
"✗ Test 4: Latency - FAIL ({:.2}μs >= 50μs target)",
avg_latency_us
);
}
println!("CUSUM Results: {}/{} passing\n", passing, total);
}
fn validate_adx_features() {
println!("## Validating ADX Features (211-215)");
let mut features = RegimeADXFeatures::new(14);
let mut passing = 0;
let mut total = 0;
// Create test bars
let bars = create_test_bars(50);
// Test 1: Initialization
total += 1;
let result = features.update(&bars[0]);
if result.len() == 5 && result == [0.0; 5] {
passing += 1;
println!("✓ Test 1: Initialization - PASS");
} else {
println!("✗ Test 1: Initialization - FAIL");
}
// Test 2: Valid range after warmup
total += 1;
for bar in &bars[1..30] {
features.update(bar);
}
let result = features.update(&bars[30]);
if result[0] >= 0.0 && result[0] <= 100.0 && // ADX
result[1] >= 0.0 && result[1] <= 100.0 && // +DI
result[2] >= 0.0 && result[2] <= 100.0 && // -DI
result[3] >= 0.0 && result[3] <= 100.0 && // DX
result[4] >= 0.0
{
// ATR
passing += 1;
println!("✓ Test 2: Valid range after warmup - PASS");
} else {
println!("✗ Test 2: Valid range after warmup - FAIL");
}
// Test 3: Trend detection
total += 1;
let mut features = RegimeADXFeatures::new(14);
let trend_bars = create_trend_bars(50);
for bar in &trend_bars {
features.update(bar);
}
let result = features.update(&trend_bars[49]);
if result[0] > 15.0 && result[1] > result[2] {
// ADX > 15 and +DI > -DI
passing += 1;
println!(
"✓ Test 3: Trend detection - PASS (ADX={:.2}, +DI={:.2}, -DI={:.2})",
result[0], result[1], result[2]
);
} else {
println!(
"✗ Test 3: Trend detection - FAIL (ADX={:.2}, +DI={:.2}, -DI={:.2})",
result[0], result[1], result[2]
);
}
// Test 4: Latency benchmark
total += 1;
let mut features = RegimeADXFeatures::new(14);
let test_bars = create_test_bars(100);
// Warmup
for bar in &test_bars[0..30] {
features.update(bar);
}
let iterations = 1000;
let start = Instant::now();
for bar in test_bars.iter().cycle().take(iterations) {
features.update(bar);
}
let elapsed = start.elapsed();
let avg_latency_us = elapsed.as_micros() as f64 / iterations as f64;
if avg_latency_us < 50.0 {
passing += 1;
println!(
"✓ Test 4: Latency - PASS ({:.2}μs < 50μs target)",
avg_latency_us
);
} else {
println!(
"✗ Test 4: Latency - FAIL ({:.2}μs >= 50μs target)",
avg_latency_us
);
}
println!("ADX Results: {}/{} passing\n", passing, total);
}
fn validate_adaptive_features() {
println!("## Validating Adaptive Features (221-224)");
let mut features = RegimeAdaptiveFeatures::new(20, 100_000.0, 14);
let mut passing = 0;
let mut total = 0;
let bars = create_ext_bars(20);
// Test 1: Position multiplier validation
total += 1;
let result = features.update(MarketRegime::Normal, 0.01, 50_000.0, &bars);
if result[0] == 1.0 {
// Normal regime = 1.0x
passing += 1;
println!("✓ Test 1: Position multiplier (Normal) - PASS");
} else {
println!(
"✗ Test 1: Position multiplier (Normal) - FAIL (expected 1.0, got {})",
result[0]
);
}
// Test 2: All regimes
total += 1;
let regimes = vec![
(MarketRegime::Trending, 1.5),
(MarketRegime::Sideways, 0.8),
(MarketRegime::Bull, 1.2),
(MarketRegime::Bear, 0.7),
(MarketRegime::HighVolatility, 0.5),
(MarketRegime::Crisis, 0.2),
];
let mut all_correct = true;
for (regime, expected_mult) in regimes {
let result = features.update(regime, 0.01, 50_000.0, &bars);
if result[0] != expected_mult {
all_correct = false;
println!(
"{:?}: expected {}, got {}",
regime, expected_mult, result[0]
);
}
}
if all_correct {
passing += 1;
println!("✓ Test 2: All regime multipliers - PASS");
} else {
println!("✗ Test 2: All regime multipliers - FAIL");
}
// Test 3: Risk budget bounds
total += 1;
let result = features.update(MarketRegime::Normal, 0.01, 50_000.0, &bars);
if result[3] >= 0.0 && result[3] <= 1.0 {
passing += 1;
println!("✓ Test 3: Risk budget bounds - PASS");
} else {
println!("✗ Test 3: Risk budget bounds - FAIL (got {})", result[3]);
}
// Test 4: Latency benchmark
total += 1;
let mut features = RegimeAdaptiveFeatures::new(20, 100_000.0, 14);
let test_bars = create_ext_bars(30);
let iterations = 10000;
let start = Instant::now();
for i in 0..iterations {
let regime = if i % 2 == 0 {
MarketRegime::Normal
} else {
MarketRegime::Trending
};
features.update(regime, 0.01, 50_000.0, &test_bars);
}
let elapsed = start.elapsed();
let avg_latency_us = elapsed.as_micros() as f64 / iterations as f64;
if avg_latency_us < 50.0 {
passing += 1;
println!(
"✓ Test 4: Latency - PASS ({:.2}μs < 50μs target)",
avg_latency_us
);
} else {
println!(
"✗ Test 4: Latency - FAIL ({:.2}μs >= 50μs target)",
avg_latency_us
);
}
println!("Adaptive Results: {}/{} passing\n", passing, total);
}
// Helper functions
fn create_test_bars(count: usize) -> Vec<AdxBar> {
let mut bars = Vec::new();
let mut price = 100.0;
for i in 0..count {
price += ((i as f64) * 0.3).sin() * 0.5;
bars.push(AdxBar {
timestamp: i as i64,
open: price,
high: price + 0.5,
low: price - 0.5,
close: price,
volume: 1000.0,
});
}
bars
}
fn create_trend_bars(count: usize) -> Vec<AdxBar> {
let mut bars = Vec::new();
let mut price = 100.0;
for i in 0..count {
price += 1.0; // Strong uptrend
bars.push(AdxBar {
timestamp: i as i64,
open: price - 0.3,
high: price + 0.5,
low: price - 0.6,
close: price,
volume: 1000.0,
});
}
bars
}
fn create_ext_bars(count: usize) -> Vec<ExtBar> {
let mut bars = Vec::new();
let base_time = Utc::now();
let mut price = 100.0;
for i in 0..count {
price += ((i as f64) * 0.3).sin() * 0.5;
bars.push(ExtBar {
timestamp: base_time + chrono::Duration::seconds(i as i64 * 60),
open: price,
high: price + 0.5,
low: price - 0.5,
close: price,
volume: 1000.0,
});
}
bars
}