Files
foxhunt/crates/ml/src/transformers/features.rs
jgrusewski 0d62bdba2e refactor(ml): eliminate candle from all 104 src files
Zero candle_core/candle_nn imports in ml/src/. Three-agent parallel migration:

- cuda_pipeline/ (19 files): Tensor→CudaSlice, Device→Arc<CudaStream>,
  VarMap→GpuVarStore, cudarc import path fixed
- trainers/ + adapters (45 files): DQN/PPO/TFT trainers, 10 ensemble
  adapters, 11 hyperopt adapters — all migrated to MlDevice, GpuTensor,
  GpuVarStore, GpuAdamW
- model dirs + infra (40 files): 10 trainable adapters, preprocessing,
  inference, transformers, validation, benchmarks

61 test/example files still reference candle — next commit.
candle-nn still in Cargo.toml (needed by tests until migrated).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 00:24:35 +01:00

127 lines
4.5 KiB
Rust

//! # Financial Feature Engineering for HFT Transformers
//!
//! This module implements state-of-the-art feature extraction for financial
//! market microstructure data, optimized for transformer model input.
//!
//! ## Key Features
//!
//! - **Order Book Imbalance**: Bid/ask volume imbalances and pressure
//! - **Trade Flow Analysis**: Aggressive vs passive order flow patterns
//! - **Microstructure Signals**: Spread, volatility, intensity measures
//! - **Temporal Features**: Time-of-day, volume clocks, event sequences
//! - **Cross-Asset Signals**: Correlation and cointegration features
//!
//! ## Performance Optimizations
//!
//! - Pre-allocated feature vectors for zero-allocation extraction
//! - SIMD-optimized mathematical operations
//! - Incremental updates for streaming data
//! - <20μs feature extraction from raw market data
use common::types::{Price, Quantity, Symbol};
use std::collections::VecDeque;
use ml_core::native_types::{NativeDevice, NativeTensor};
use ml_core::cuda_autograd::GpuTensor;
use chrono::{DateTime, Datelike, Timelike, Utc};
use serde::{Deserialize, Serialize};
use super::*;
#[test]
fn test_feature_config_default() {
let config = FeatureConfig::default();
assert_eq!(config.lookback_window, 100);
assert_eq!(config.book_levels, 5);
assert!(config.use_order_book_features);
assert!(config.use_trade_flow_features);
}
#[test]
fn test_market_microstructure_from_tick() {
let tick = MarketTick::new(
Symbol::new("EURUSD")?,
Price::from_f64(1.1000).unwrap(), // bid_price
Price::from_f64(1.1002).unwrap(), // ask_price
Price::from_f64(1.1001).unwrap(), // last_price
Volume::new(1000.0), // volume
Quantity::from(500), // bid_size
Quantity::from(300), // ask_size
1234567890, // timestamp_us
);
let micro = MarketMicrostructure::from_tick(&tick);
assert_eq!(micro.bid_price, tick.bid_price);
assert_eq!(micro.ask_price, tick.ask_price);
assert!(micro.book_imbalance > 0.0); // More bid volume than ask
}
#[test]
fn test_trade_flow_features() {
let mut trades = Vec::new();
// Create sample trades
for i in 0..10 {
let mut micro = MarketMicrostructure {
timestamp: 1234567890 + i as u64 * 1000,
bid_price: Price::from_f64(1.1000).unwrap(),
ask_price: Price::from_f64(1.1002).unwrap(),
bid_volume: Volume::new(100.0),
ask_volume: Volume::new(100.0),
mid_price: Price::from_f64(1.1001).unwrap(),
spread: Price::from_f64(0.0002).unwrap(),
last_price: Some(Price::from_f64(1.1001).unwrap()),
last_volume: Some(Volume::new(100 + i as u64 * 10)),
trade_direction: if i % 2 == 0 { 1 } else { -1 },
book_imbalance: 0.0,
vwap: None,
trade_count: 1,
};
trades.push(micro);
}
let features = TradeFlowFeatures::extract(&trades, 1.0);
let vector = features.to_vector();
assert_eq!(vector.len(), 8);
assert!(features.trade_intensity > 0.0);
assert!(features.avg_trade_size > 0.0);
}
#[test]
fn test_percentile_calculation() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
assert_eq!(percentile(&data, 0.0), 1.0);
assert_eq!(percentile(&data, 0.5), 3.0);
assert_eq!(percentile(&data, 1.0), 5.0);
let empty_data = vec![];
assert_eq!(percentile(&empty_data, 0.5), 0.0);
}
#[tokio::test]
async fn test_feature_extractor() {
let config = FeatureConfig::default();
let device = NativeDevice::Cuda(0);
let mut extractor = FinancialFeatureExtractor::new(config, device);
let tick = MarketTick::new(
Symbol::new("EURUSD")?,
Price::from_f64(1.1000).unwrap(), // bid_price
Price::from_f64(1.1002).unwrap(), // ask_price
Price::from_f64(1.1001).unwrap(), // last_price
Volume::new(1000.0), // volume
Quantity::from(500), // bid_size
Quantity::from(300), // ask_size
1234567890, // timestamp_us
);
let features_tensor = extractor.extract_features(&tick)?;
let shape = features_tensor.shape();
assert_eq!(shape.dims(), &[1, 32]); // Default output dimension
assert!(extractor.average_extraction_time_us() > 0.0);
}
}