Files
foxhunt/ml/examples/ofi_features_demo.rs
jgrusewski 28ee27b2bb 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>
2025-11-23 00:41:22 +01:00

158 lines
4.9 KiB
Rust

//! OFI Features Demo
//!
//! Demonstrates how to use the OFI (Order Flow Imbalance) calculator
//! with MBP-10 order book snapshots.
use data::providers::databento::mbp10::{BidAskPair, Mbp10Snapshot};
use ml::features::ofi_calculator::{OFICalculator, OFIFeatures};
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== OFI Features Demo ===\n");
// Create OFI calculator
let mut calculator = OFICalculator::new();
// Create sample MBP-10 snapshots (simulating real market data)
let snapshots = create_sample_snapshots();
println!("Processing {} snapshots...\n", snapshots.len());
for (i, snapshot) in snapshots.iter().enumerate() {
// Calculate OFI features
let features = calculator.calculate(snapshot)?;
// Display results
println!("Snapshot #{}", i + 1);
println!(" Mid Price: ${:.2}", snapshot.mid_price());
println!(" Spread: ${:.4}", snapshot.spread());
println!("\n OFI Features:");
println!(" OFI Level 1 (normalized): {:>8.3}", features.ofi_level1);
println!(" OFI Level 5 (weighted): {:>8.3}", features.ofi_level5);
println!(" Depth Imbalance: {:>8.3}", features.depth_imbalance);
println!(" VPIN: {:>8.3}", features.vpin);
println!(" Kyle's Lambda: {:>8.6}", features.kyle_lambda);
println!(" Bid Slope: {:>8.1}", features.bid_slope);
println!(" Ask Slope: {:>8.1}", features.ask_slope);
println!(" Trade Imbalance: {:>8.3}", features.trade_imbalance);
// Validate features
if !features.is_valid() {
println!(" ⚠️ WARNING: Invalid features detected!");
} else {
println!(" ✓ All features valid (finite)");
}
println!();
}
// Convert to array format (for ML model input)
let final_snapshot = snapshots.last().unwrap();
let final_features = calculator.calculate(final_snapshot)?;
let feature_array = final_features.to_array();
println!("Feature Array (for ML models):");
println!("{:?}", feature_array);
println!("\nArray length: {} (indices 226-233)", feature_array.len());
Ok(())
}
/// Create sample MBP-10 snapshots for demonstration
fn create_sample_snapshots() -> Vec<Mbp10Snapshot> {
vec![
// Snapshot 1: Balanced book
create_snapshot(
1640995200000000000,
5000_000_000_000_000, // $5000.00 bid
5000_100_000_000_000, // $5000.01 ask
100,
100,
),
// Snapshot 2: Bid rises (bullish signal)
create_snapshot(
1640995201000000000,
5000_050_000_000_000, // $5000.05 bid (rises)
5000_100_000_000_000, // $5000.01 ask
120,
90,
),
// Snapshot 3: Ask falls (bullish signal)
create_snapshot(
1640995202000000000,
5000_050_000_000_000,
5000_075_000_000_000, // $5000.075 ask (falls)
130,
80,
),
// Snapshot 4: Bid-heavy book
create_snapshot(
1640995203000000000,
5000_075_000_000_000, // Bid continues rising
5000_100_000_000_000,
200, // Large bid size
100,
),
// Snapshot 5: Reversal - Ask rises (bearish signal)
create_snapshot(
1640995204000000000,
5000_050_000_000_000, // Bid falls back
5000_150_000_000_000, // Ask jumps up
100,
150,
),
]
}
fn create_snapshot(
timestamp: u64,
bid_px: i64,
ask_px: i64,
bid_sz: u32,
ask_sz: u32,
) -> Mbp10Snapshot {
let levels = vec![
BidAskPair {
bid_px,
bid_sz,
bid_ct: 5,
ask_px,
ask_sz,
ask_ct: 6,
},
BidAskPair {
bid_px: bid_px - 10_000_000_000,
bid_sz: bid_sz * 2,
bid_ct: 8,
ask_px: ask_px + 10_000_000_000,
ask_sz: ask_sz * 2,
ask_ct: 7,
},
BidAskPair {
bid_px: bid_px - 20_000_000_000,
bid_sz: bid_sz * 3,
bid_ct: 10,
ask_px: ask_px + 20_000_000_000,
ask_sz: ask_sz * 3,
ask_ct: 9,
},
BidAskPair {
bid_px: bid_px - 30_000_000_000,
bid_sz: bid_sz * 4,
bid_ct: 12,
ask_px: ask_px + 30_000_000_000,
ask_sz: ask_sz * 4,
ask_ct: 11,
},
BidAskPair {
bid_px: bid_px - 40_000_000_000,
bid_sz: bid_sz * 5,
bid_ct: 15,
ask_px: ask_px + 40_000_000_000,
ask_sz: ask_sz * 5,
ask_ct: 13,
},
];
Mbp10Snapshot::new("ES.FUT".to_string(), timestamp, levels, 0, 100)
}