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>
363 lines
10 KiB
Rust
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
|
|
}
|