//! 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> { 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 { 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) }