Files
foxhunt/trading_engine/src/features/unified_extractor.rs
jgrusewski 1ae419d88e feat(trading_engine): re-enable features module, clean stale comments
- Remove 6 stale "TEMPORARILY COMMENTED OUT" comments (modules already active)
- Fix features/mod.rs: repair corrupted use block, fix test_utils type mismatches
- Fix features/unified_extractor.rs: correct MarketTick import, safe indexing
- Add Debug derives, suppress dead_code on partially-implemented fields

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 03:08:30 +01:00

1636 lines
50 KiB
Rust

//! `UnifiedFeatureExtractor` - Single Source of Truth for Feature Engineering
//!
//! This module provides the CRITICAL `UnifiedFeatureExtractor` that ensures zero
//! training/serving skew by extracting features identically across all stages:
//! - Training: Historical data processing
//! - Backtesting: Strategy validation
//! - Live Trading: Real-time inference
//!
//! KEY PRINCIPLES:
//! 1. Single source of truth for all feature calculations
//! 2. Identical processing for Databento market data and Benzinga news
//! 3. Model-specific feature sets with unified base features
//! 4. High-performance implementation with SIMD optimizations
//! 5. Type-safe feature engineering with compile-time guarantees
use std::collections::HashMap;
use std::time::{Duration, Instant};
use chrono::{DateTime, Datelike, Timelike, Utc};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tracing::{debug, error};
use crate::simd::SimdMarketDataOps;
use common::types::MarketTick;
use common::{Price, Quantity, QuoteEvent, Symbol, TradeEvent, Volume};
/// Feature extraction errors
#[derive(Error, Debug)]
/// FeatureError
///
/// Auto-generated documentation placeholder - enhance with specifics
pub enum FeatureError {
#[error("Insufficient data: {feature} needs {required} points, got {available}")]
InsufficientData {
feature: String,
required: usize,
available: usize,
},
#[error("Invalid parameters for {feature}: {reason}")]
InvalidParameters { feature: String, reason: String },
#[error("Mathematical error in {feature}: {reason}")]
MathematicalError { feature: String, reason: String },
#[error("Data alignment error: {reason}")]
AlignmentError { reason: String },
#[error("Missing required data: {data_type}")]
MissingData { data_type: String },
}
/// Databento market data features
#[derive(Debug, Clone, Serialize, Deserialize)]
/// DatabentoBuFeatures
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct DatabentoBuFeatures {
// Order Book Microstructure (MBO/MBP data)
/// Bid Ask Spread Bps
pub bid_ask_spread_bps: f64,
/// Order Book Imbalance
pub order_book_imbalance: f64, // -1 (ask heavy) to +1 (bid heavy)
/// Depth Weighted Mid
pub depth_weighted_mid: Price,
/// Effective Spread Bps
pub effective_spread_bps: f64,
/// Price Impact Bps
pub price_impact_bps: f64,
// Level 2/3 Order Book Features
/// L2 Slope
pub l2_slope: f64, // Order book slope regression
/// L2 Curvature
pub l2_curvature: f64, // Order book curvature
/// L3 Order Intensity
pub l3_order_intensity: f64, // Orders per second
/// L3 Cancellation Ratio
pub l3_cancellation_ratio: f64, // Cancel/Submit ratio
// Trade Classification (Lee-Ready, Tick Rule)
/// Trade Sign
pub trade_sign: i8, // -1 (sell), 0 (unknown), +1 (buy)
/// Trade Size Category
pub trade_size_category: u8, // 1 (small), 2 (medium), 3 (large), 4 (block)
/// Trade Urgency
pub trade_urgency: f64, // Aggressive vs passive flow
// Microstructure Noise and Information
/// Realized Spread Bps
pub realized_spread_bps: f64,
/// Information Share
pub information_share: f64, // Price discovery contribution
/// Microstructure Noise
pub microstructure_noise: f64, // Bid-ask bounce component
// High-Frequency Patterns
/// Tick Direction Streak
pub tick_direction_streak: i8, // Consecutive upticks/downticks
/// Quote Update Frequency
pub quote_update_frequency: f64, // Updates per second
/// Order Arrival Intensity
pub order_arrival_intensity: f64, // Poisson lambda estimate
}
/// Benzinga news sentiment features
#[derive(Debug, Clone, Serialize, Deserialize)]
/// BenzingaNewsFeatures
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct BenzingaNewsFeatures {
// Real-time News Sentiment
/// Sentiment Score
pub sentiment_score: f64, // -1.0 (very negative) to +1.0 (very positive)
/// Sentiment Confidence
pub sentiment_confidence: f64, // 0.0 to 1.0 confidence in sentiment
/// News Velocity
pub news_velocity: f64, // News articles per hour
/// Breaking News Flag
pub breaking_news_flag: bool, // Breaking news indicator
// Weighted Sentiment (by source credibility)
/// Weighted Sentiment 1H
pub weighted_sentiment_1h: f64,
/// Weighted Sentiment 4H
pub weighted_sentiment_4h: f64,
/// Weighted Sentiment 24H
pub weighted_sentiment_24h: f64,
// News Categories and Impact
/// Earnings Related
pub earnings_related: bool,
/// Analyst Rating
pub analyst_rating: Option<f64>, // Analyst rating change
/// Unusual Options Activity
pub unusual_options_activity: bool,
/// Sec Filing Type
pub sec_filing_type: Option<String>,
// Sentiment Momentum
/// Sentiment Acceleration
pub sentiment_acceleration: f64, // Rate of sentiment change
/// Sentiment Divergence
pub sentiment_divergence: f64, // News vs price action divergence
/// Contrarian Signal
pub contrarian_signal: f64, // Contrarian opportunity score
// Source Diversity and Volume
/// Source Count
pub source_count: u32, // Number of distinct sources
/// Mention Volume
pub mention_volume: u32, // Total mentions/references
/// Social Amplification
pub social_amplification: f64, // Social media pickup ratio
}
/// Base market features (common across all models)
#[derive(Debug, Clone, Serialize, Deserialize)]
/// BaseMarketFeatures
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct BaseMarketFeatures {
// Price Action
/// Price
pub price: Price,
/// Returns 1M
pub returns_1m: f64,
/// Returns 5M
pub returns_5m: f64,
/// Returns 15M
pub returns_15m: f64,
/// Returns 1H
pub returns_1h: f64,
/// Volatility 1H
pub volatility_1h: f64,
/// Volatility 4H
pub volatility_4h: f64,
// Volume Profile
/// Volume
pub volume: Volume,
/// Volume Ratio 1H
pub volume_ratio_1h: f64, // Current vs 1h average
/// Vwap Deviation
pub vwap_deviation: f64, // Distance from VWAP
/// Volume Imbalance
pub volume_imbalance: f64, // Buy vs sell volume
// Technical Indicators
/// Rsi 14
pub rsi_14: f64,
/// Macd Signal
pub macd_signal: f64,
/// Bollinger Position
pub bollinger_position: f64, // Position within bands
/// Momentum Score
pub momentum_score: f64,
// Market Context
/// Time Of Day
pub time_of_day: f64, // Normalized 0-1
/// Day Of Week
pub day_of_week: u8, // 1-7
/// Market Session
pub market_session: u8, // 1 (pre), 2 (regular), 3 (after)
/// Is Opex
pub is_opex: bool, // Options expiration
/// Is Earnings Week
pub is_earnings_week: bool,
}
/// TLOB (Temporal Limit Order Book) specific features
#[derive(Debug, Clone, Serialize, Deserialize)]
/// TLOBFeatures
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct TLOBFeatures {
/// Base
pub base: BaseMarketFeatures,
/// Databento
pub databento: DatabentoBuFeatures,
/// Benzinga
pub benzinga: BenzingaNewsFeatures,
// TLOB-specific order book sequences
/// Order Book Sequence
pub order_book_sequence: Vec<f64>, // 50-point sequence
/// Trade Flow Sequence
pub trade_flow_sequence: Vec<f64>, // Trade intensity sequence
/// Spread Sequence
pub spread_sequence: Vec<f64>, // Spread evolution
/// Depth Sequence
pub depth_sequence: Vec<f64>, // Market depth changes
}
/// MAMBA (State Space Model) features for sequential processing
#[derive(Debug, Clone, Serialize, Deserialize)]
/// MAMBAFeatures
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct MAMBAFeatures {
/// Base
pub base: BaseMarketFeatures,
/// Databento
pub databento: DatabentoBuFeatures,
/// Benzinga
pub benzinga: BenzingaNewsFeatures,
// Long-term sequences for state space modeling
/// Price Sequence
pub price_sequence: Vec<f64>, // 200-point price sequence
/// Volume Sequence
pub volume_sequence: Vec<f64>, // Volume evolution
/// Sentiment Sequence
pub sentiment_sequence: Vec<f64>, // News sentiment over time
/// Volatility Regime
pub volatility_regime: f64, // Current volatility state
/// Trend Persistence
pub trend_persistence: f64, // Trend stability measure
}
/// DQN (Deep Q-Network) features for reinforcement learning
#[derive(Debug, Clone, Serialize, Deserialize)]
/// DQNFeatures
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct DQNFeatures {
/// Base
pub base: BaseMarketFeatures,
/// Databento
pub databento: DatabentoBuFeatures,
/// Benzinga
pub benzinga: BenzingaNewsFeatures,
// State representation for RL
/// Position
pub position: f64, // Normalized position size
/// Unrealized Pnl
pub unrealized_pnl: f64, // Current P&L
/// Time In Position
pub time_in_position: f64, // Holding period
/// Market Impact Estimate
pub market_impact_estimate: f64, // Expected slippage
/// Opportunity Cost
pub opportunity_cost: f64, // Missed opportunities
// Action space context
/// Available Liquidity
pub available_liquidity: f64, // Market depth available
/// Transaction Cost Estimate
pub transaction_cost_estimate: f64, // Estimated costs
/// Risk Budget Remaining
pub risk_budget_remaining: f64, // Available risk capacity
}
/// PPO (Proximal Policy Optimization) features
#[derive(Debug, Clone, Serialize, Deserialize)]
/// PPOFeatures
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct PPOFeatures {
/// Base
pub base: BaseMarketFeatures,
/// Databento
pub databento: DatabentoBuFeatures,
/// Benzinga
pub benzinga: BenzingaNewsFeatures,
// Policy-specific features
/// Action History
pub action_history: Vec<f64>, // Last 10 actions
/// Reward History
pub reward_history: Vec<f64>, // Last 10 rewards
/// Advantage Estimate
pub advantage_estimate: f64, // GAE advantage
/// Value Estimate
pub value_estimate: f64, // State value estimate
/// Policy Entropy
pub policy_entropy: f64, // Action distribution entropy
// Exploration features
/// Exploration Bonus
pub exploration_bonus: f64, // Curiosity-driven exploration
/// Uncertainty Estimate
pub uncertainty_estimate: f64, // Model uncertainty
}
/// Liquid Networks features for adaptive processing
#[derive(Debug, Clone, Serialize, Deserialize)]
/// LiquidFeatures
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct LiquidFeatures {
/// Base
pub base: BaseMarketFeatures,
/// Databento
pub databento: DatabentoBuFeatures,
/// Benzinga
pub benzinga: BenzingaNewsFeatures,
// Adaptive time constants
/// Fast Adaptation Signal
pub fast_adaptation_signal: f64, // High-frequency adaptation
/// Slow Adaptation Signal
pub slow_adaptation_signal: f64, // Low-frequency trends
/// Regime Change Signal
pub regime_change_signal: f64, // Market regime shifts
/// Adaptation Rate
pub adaptation_rate: f64, // Current learning rate
// Causal discovery features
/// Causal Strength
pub causal_strength: f64, // Causal relationship strength
/// Information Flow
pub information_flow: f64, // Directional information transfer
/// Network Centrality
pub network_centrality: f64, // Node importance in causal graph
}
/// TFT (Temporal Fusion Transformer) features
#[derive(Debug, Clone, Serialize, Deserialize)]
/// TFTFeatures
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct TFTFeatures {
/// Base
pub base: BaseMarketFeatures,
/// Databento
pub databento: DatabentoBuFeatures,
/// Benzinga
pub benzinga: BenzingaNewsFeatures,
// Multi-horizon sequences
/// Observed Sequence
pub observed_sequence: Vec<f64>, // Historical observations
/// Known Future
pub known_future: Vec<f64>, // Known future inputs
/// Static Metadata
pub static_metadata: Vec<f64>, // Time-invariant features
// Attention mechanism inputs
/// Temporal Patterns
pub temporal_patterns: Vec<f64>, // Recurring temporal patterns
/// Seasonal Components
pub seasonal_components: Vec<f64>, // Seasonal decomposition
/// Forecast Horizon
pub forecast_horizon: usize, // Prediction steps ahead
}
/// Unified feature extraction configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
/// UnifiedConfig
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct UnifiedConfig {
// Data requirements
/// Min Data Points
pub min_data_points: usize,
/// Max Missing Ratio
pub max_missing_ratio: f64,
/// Outlier Threshold
pub outlier_threshold: f64,
// Time windows
/// Short Window
pub short_window: Duration,
/// Medium Window
pub medium_window: Duration,
/// Long Window
pub long_window: Duration,
// Model-specific configurations
/// Tlob Sequence Length
pub tlob_sequence_length: usize,
/// Mamba Sequence Length
pub mamba_sequence_length: usize,
/// Dqn History Length
pub dqn_history_length: usize,
/// Ppo History Length
pub ppo_history_length: usize,
/// Tft Encoder Length
pub tft_encoder_length: usize,
/// Tft Decoder Length
pub tft_decoder_length: usize,
// Performance settings
/// Enable Simd
pub enable_simd: bool,
/// Parallel Processing
pub parallel_processing: bool,
/// Cache Intermediate Results
pub cache_intermediate_results: bool,
}
impl Default for UnifiedConfig {
fn default() -> Self {
Self {
min_data_points: 100,
max_missing_ratio: 0.1,
outlier_threshold: 3.0,
short_window: Duration::from_secs(300), // 5 minutes
medium_window: Duration::from_secs(3600), // 1 hour
long_window: Duration::from_secs(14400), // 4 hours
tlob_sequence_length: 50,
mamba_sequence_length: 200,
dqn_history_length: 10,
ppo_history_length: 10,
tft_encoder_length: 192,
tft_decoder_length: 24,
enable_simd: true,
parallel_processing: true,
cache_intermediate_results: true,
}
}
}
/// The unified feature extractor - SINGLE SOURCE OF TRUTH
#[derive(Debug)]
#[allow(dead_code)]
pub struct UnifiedFeatureExtractor {
config: UnifiedConfig,
simd_processor: Option<SimdMarketDataOps>,
feature_cache: HashMap<String, (Instant, Vec<f64>)>,
}
impl UnifiedFeatureExtractor {
/// Create new unified feature extractor
pub fn new(config: UnifiedConfig) -> Self {
Self {
simd_processor: crate::simd::SafeSimdDispatcher::new()
.create_market_data_ops()
.ok(),
config,
feature_cache: HashMap::new(),
}
}
/// Extract features for TLOB Transformer model
pub async fn extract_tlob_features(
&mut self,
symbol: &Symbol,
databento_data: &DatabentoBuData,
benzinga_data: &BenzingaNewsData,
historical_data: &[MarketTick],
) -> Result<TLOBFeatures, FeatureError> {
let start_time = Instant::now();
// Extract base features
let base = self.extract_base_features(historical_data).await?;
// Extract Databento order book features
let databento = self.extract_databento_features(databento_data).await?;
// Extract Benzinga sentiment features
let benzinga = self.extract_benzinga_features(benzinga_data).await?;
// Generate TLOB-specific sequences
let order_book_sequence =
self.generate_order_book_sequence(databento_data, self.config.tlob_sequence_length)?;
let trade_flow_sequence =
self.generate_trade_flow_sequence(databento_data, self.config.tlob_sequence_length)?;
let spread_sequence =
self.generate_spread_sequence(databento_data, self.config.tlob_sequence_length)?;
let depth_sequence =
self.generate_depth_sequence(databento_data, self.config.tlob_sequence_length)?;
debug!(
"TLOB feature extraction for {} completed in {:.2}ms",
symbol,
start_time.elapsed().as_millis()
);
Ok(TLOBFeatures {
base,
databento,
benzinga,
order_book_sequence,
trade_flow_sequence,
spread_sequence,
depth_sequence,
})
}
/// Extract features for MAMBA State Space Model
pub async fn extract_mamba_features(
&mut self,
symbol: &Symbol,
databento_data: &DatabentoBuData,
benzinga_data: &BenzingaNewsData,
historical_data: &[MarketTick],
) -> Result<MAMBAFeatures, FeatureError> {
let start_time = Instant::now();
let base = self.extract_base_features(historical_data).await?;
let databento = self.extract_databento_features(databento_data).await?;
let benzinga = self.extract_benzinga_features(benzinga_data).await?;
// Generate long-term sequences for state space modeling
let price_sequence =
self.generate_price_sequence(historical_data, self.config.mamba_sequence_length)?;
let volume_sequence =
self.generate_volume_sequence(historical_data, self.config.mamba_sequence_length)?;
let sentiment_sequence =
self.generate_sentiment_sequence(benzinga_data, self.config.mamba_sequence_length)?;
let volatility_regime = self.calculate_volatility_regime(historical_data)?;
let trend_persistence = self.calculate_trend_persistence(historical_data)?;
debug!(
"MAMBA feature extraction for {} completed in {:.2}ms",
symbol,
start_time.elapsed().as_millis()
);
Ok(MAMBAFeatures {
base,
databento,
benzinga,
price_sequence,
volume_sequence,
sentiment_sequence,
volatility_regime,
trend_persistence,
})
}
/// Extract features for DQN reinforcement learning
pub async fn extract_dqn_features(
&mut self,
symbol: &Symbol,
databento_data: &DatabentoBuData,
benzinga_data: &BenzingaNewsData,
historical_data: &[MarketTick],
current_position: f64,
unrealized_pnl: f64,
) -> Result<DQNFeatures, FeatureError> {
let start_time = Instant::now();
let base = self.extract_base_features(historical_data).await?;
let databento = self.extract_databento_features(databento_data).await?;
let benzinga = self.extract_benzinga_features(benzinga_data).await?;
// Calculate RL-specific features
let time_in_position = self.calculate_time_in_position(current_position)?;
let market_impact_estimate =
self.estimate_market_impact(databento_data, current_position.abs())?;
let opportunity_cost = self.calculate_opportunity_cost(historical_data)?;
let available_liquidity = self.calculate_available_liquidity(databento_data)?;
let transaction_cost_estimate = self.estimate_transaction_costs(databento_data)?;
let risk_budget_remaining =
self.calculate_risk_budget_remaining(current_position, unrealized_pnl)?;
debug!(
"DQN feature extraction for {} completed in {:.2}ms",
symbol,
start_time.elapsed().as_millis()
);
Ok(DQNFeatures {
base,
databento,
benzinga,
position: current_position,
unrealized_pnl,
time_in_position,
market_impact_estimate,
opportunity_cost,
available_liquidity,
transaction_cost_estimate,
risk_budget_remaining,
})
}
/// Extract features for PPO policy optimization
pub async fn extract_ppo_features(
&mut self,
symbol: &Symbol,
databento_data: &DatabentoBuData,
benzinga_data: &BenzingaNewsData,
historical_data: &[MarketTick],
action_history: &[f64],
reward_history: &[f64],
) -> Result<PPOFeatures, FeatureError> {
let start_time = Instant::now();
let base = self.extract_base_features(historical_data).await?;
let databento = self.extract_databento_features(databento_data).await?;
let benzinga = self.extract_benzinga_features(benzinga_data).await?;
// Calculate PPO-specific features
let advantage_estimate = self.calculate_gae_advantage(reward_history)?;
let value_estimate = self.estimate_state_value(historical_data)?;
let policy_entropy = self.calculate_policy_entropy(action_history)?;
let exploration_bonus = self.calculate_exploration_bonus(action_history)?;
let uncertainty_estimate = self.estimate_model_uncertainty(historical_data)?;
debug!(
"PPO feature extraction for {} completed in {:.2}ms",
symbol,
start_time.elapsed().as_millis()
);
Ok(PPOFeatures {
base,
databento,
benzinga,
action_history: action_history.to_vec(),
reward_history: reward_history.to_vec(),
advantage_estimate,
value_estimate,
policy_entropy,
exploration_bonus,
uncertainty_estimate,
})
}
/// Extract features for Liquid Networks
pub async fn extract_liquid_features(
&mut self,
symbol: &Symbol,
databento_data: &DatabentoBuData,
benzinga_data: &BenzingaNewsData,
historical_data: &[MarketTick],
) -> Result<LiquidFeatures, FeatureError> {
let start_time = Instant::now();
let base = self.extract_base_features(historical_data).await?;
let databento = self.extract_databento_features(databento_data).await?;
let benzinga = self.extract_benzinga_features(benzinga_data).await?;
// Calculate adaptive features
let fast_adaptation_signal = self.calculate_fast_adaptation(historical_data)?;
let slow_adaptation_signal = self.calculate_slow_adaptation(historical_data)?;
let regime_change_signal = self.detect_regime_change(historical_data)?;
let adaptation_rate = self.calculate_adaptation_rate(historical_data)?;
// Causal discovery features
let causal_strength = self.measure_causal_strength(databento_data, benzinga_data)?;
let information_flow = self.calculate_information_flow(historical_data)?;
let network_centrality = self.calculate_network_centrality(symbol)?;
debug!(
"Liquid feature extraction for {} completed in {:.2}ms",
symbol,
start_time.elapsed().as_millis()
);
Ok(LiquidFeatures {
base,
databento,
benzinga,
fast_adaptation_signal,
slow_adaptation_signal,
regime_change_signal,
adaptation_rate,
causal_strength,
information_flow,
network_centrality,
})
}
/// Extract features for Temporal Fusion Transformer
pub async fn extract_tft_features(
&mut self,
symbol: &Symbol,
databento_data: &DatabentoBuData,
benzinga_data: &BenzingaNewsData,
historical_data: &[MarketTick],
forecast_horizon: usize,
) -> Result<TFTFeatures, FeatureError> {
let start_time = Instant::now();
let base = self.extract_base_features(historical_data).await?;
let databento = self.extract_databento_features(databento_data).await?;
let benzinga = self.extract_benzinga_features(benzinga_data).await?;
// Generate TFT-specific sequences
let observed_sequence =
self.generate_observed_sequence(historical_data, self.config.tft_encoder_length)?;
let known_future = self.generate_known_future_sequence(forecast_horizon)?;
let static_metadata = self.generate_static_metadata(symbol)?;
// Temporal pattern analysis
let temporal_patterns = self.extract_temporal_patterns(historical_data)?;
let seasonal_components = self.decompose_seasonal_components(historical_data)?;
debug!(
"TFT feature extraction for {} completed in {:.2}ms",
symbol,
start_time.elapsed().as_millis()
);
Ok(TFTFeatures {
base,
databento,
benzinga,
observed_sequence,
known_future,
static_metadata,
temporal_patterns,
seasonal_components,
forecast_horizon,
})
}
// PRIVATE HELPER METHODS FOR FEATURE EXTRACTION
async fn extract_base_features(
&mut self,
historical_data: &[MarketTick],
) -> Result<BaseMarketFeatures, FeatureError> {
if historical_data.len() < self.config.min_data_points {
return Err(FeatureError::InsufficientData {
feature: "base_features".to_owned(),
required: self.config.min_data_points,
available: historical_data.len(),
});
}
let latest = historical_data.last().ok_or_else(|| FeatureError::InsufficientData {
feature: "base_features".to_owned(),
required: 1,
available: 0,
})?;
// Calculate returns using SIMD optimization
let returns_1m = self.calculate_returns(historical_data, Duration::from_secs(60))?;
let returns_5m = self.calculate_returns(historical_data, Duration::from_secs(300))?;
let returns_15m = self.calculate_returns(historical_data, Duration::from_secs(900))?;
let returns_1h = self.calculate_returns(historical_data, Duration::from_secs(3600))?;
// Calculate volatility
let volatility_1h =
self.calculate_volatility(historical_data, Duration::from_secs(3600))?;
let volatility_4h =
self.calculate_volatility(historical_data, Duration::from_secs(14400))?;
// Volume analysis
let volume_ratio_1h =
self.calculate_volume_ratio(historical_data, Duration::from_secs(3600))?;
let vwap_deviation = self.calculate_vwap_deviation(historical_data)?;
let volume_imbalance = self.calculate_volume_imbalance(historical_data)?;
// Technical indicators
let rsi_14 = self.calculate_rsi(historical_data, 14)?;
let macd_signal = self.calculate_macd_signal(historical_data)?;
let bollinger_position = self.calculate_bollinger_position(historical_data)?;
let momentum_score = self.calculate_momentum_score(historical_data)?;
// Market context
let now = Utc::now();
let time_of_day = self.normalize_time_of_day(now);
let day_of_week = now.weekday().number_from_monday() as u8;
let market_session = self.determine_market_session(now);
let is_opex = self.is_options_expiration(now);
let is_earnings_week = self.is_earnings_week(now);
Ok(BaseMarketFeatures {
price: latest.price,
returns_1m,
returns_5m,
returns_15m,
returns_1h,
volatility_1h,
volatility_4h,
volume: Volume::from_f64(latest.size.to_f64()).unwrap_or(Volume::ZERO),
volume_ratio_1h,
vwap_deviation,
volume_imbalance,
rsi_14,
macd_signal,
bollinger_position,
momentum_score,
time_of_day,
day_of_week,
market_session,
is_opex,
is_earnings_week,
})
}
async fn extract_databento_features(
&mut self,
databento_data: &DatabentoBuData,
) -> Result<DatabentoBuFeatures, FeatureError> {
// Extract order book microstructure features
let bid_ask_spread_bps = self.calculate_spread_bps(databento_data)?;
let order_book_imbalance = self.calculate_order_book_imbalance(databento_data)?;
let depth_weighted_mid = self.calculate_depth_weighted_mid(databento_data)?;
let effective_spread_bps = self.calculate_effective_spread_bps(databento_data)?;
let price_impact_bps = self.calculate_price_impact_bps(databento_data)?;
// Level 2/3 analysis
let l2_slope = self.calculate_order_book_slope(databento_data)?;
let l2_curvature = self.calculate_order_book_curvature(databento_data)?;
let l3_order_intensity = self.calculate_order_intensity(databento_data)?;
let l3_cancellation_ratio = self.calculate_cancellation_ratio(databento_data)?;
// Trade classification
let trade_sign = self.classify_trade_direction(databento_data)?;
let trade_size_category = self.classify_trade_size(databento_data)?;
let trade_urgency = self.calculate_trade_urgency(databento_data)?;
// Microstructure noise analysis
let realized_spread_bps = self.calculate_realized_spread_bps(databento_data)?;
let information_share = self.calculate_information_share(databento_data)?;
let microstructure_noise = self.estimate_microstructure_noise(databento_data)?;
// High-frequency patterns
let tick_direction_streak = self.calculate_tick_streak(databento_data)?;
let quote_update_frequency = self.calculate_quote_frequency(databento_data)?;
let order_arrival_intensity = self.estimate_arrival_intensity(databento_data)?;
Ok(DatabentoBuFeatures {
bid_ask_spread_bps,
order_book_imbalance,
depth_weighted_mid,
effective_spread_bps,
price_impact_bps,
l2_slope,
l2_curvature,
l3_order_intensity,
l3_cancellation_ratio,
trade_sign,
trade_size_category,
trade_urgency,
realized_spread_bps,
information_share,
microstructure_noise,
tick_direction_streak,
quote_update_frequency,
order_arrival_intensity,
})
}
async fn extract_benzinga_features(
&mut self,
benzinga_data: &BenzingaNewsData,
) -> Result<BenzingaNewsFeatures, FeatureError> {
// Sentiment analysis
let sentiment_score = self.calculate_news_sentiment(benzinga_data)?;
let sentiment_confidence = self.calculate_sentiment_confidence(benzinga_data)?;
let news_velocity = self.calculate_news_velocity(benzinga_data)?;
let breaking_news_flag = self.detect_breaking_news(benzinga_data)?;
// Time-weighted sentiment
let weighted_sentiment_1h =
self.calculate_weighted_sentiment(benzinga_data, Duration::from_secs(3600))?;
let weighted_sentiment_4h =
self.calculate_weighted_sentiment(benzinga_data, Duration::from_secs(14400))?;
let weighted_sentiment_24h =
self.calculate_weighted_sentiment(benzinga_data, Duration::from_secs(86400))?;
// News categorization
let earnings_related = self.is_earnings_related(benzinga_data)?;
let analyst_rating = self.extract_analyst_rating(benzinga_data)?;
let unusual_options_activity = self.detect_unusual_options(benzinga_data)?;
let sec_filing_type = self.extract_sec_filing_type(benzinga_data)?;
// Sentiment dynamics
let sentiment_acceleration = self.calculate_sentiment_acceleration(benzinga_data)?;
let sentiment_divergence = self.calculate_sentiment_divergence(benzinga_data)?;
let contrarian_signal = self.calculate_contrarian_signal(benzinga_data)?;
// Source analysis
let source_count = self.count_unique_sources(benzinga_data)?;
let mention_volume = self.calculate_mention_volume(benzinga_data)?;
let social_amplification = self.calculate_social_amplification(benzinga_data)?;
Ok(BenzingaNewsFeatures {
sentiment_score,
sentiment_confidence,
news_velocity,
breaking_news_flag,
weighted_sentiment_1h,
weighted_sentiment_4h,
weighted_sentiment_24h,
earnings_related,
analyst_rating,
unusual_options_activity,
sec_filing_type,
sentiment_acceleration,
sentiment_divergence,
contrarian_signal,
source_count,
mention_volume,
social_amplification,
})
}
// Additional helper methods would be implemented here...
// This is a comprehensive structure showing the key methods needed
fn calculate_returns(
&self,
data: &[MarketTick],
_window: Duration,
) -> Result<f64, FeatureError> {
// Implementation using SIMD for performance
if data.len() < 2 {
return Ok(0.0);
}
let first_price = data.first().ok_or(FeatureError::InsufficientData {
feature: "price_change".to_string(),
required: 2,
available: data.len(),
})?.price;
let last_price = data.last().ok_or(FeatureError::InsufficientData {
feature: "price_change".to_string(),
required: 2,
available: data.len(),
})?.price;
if first_price.to_f64() == 0.0 {
return Ok(0.0);
}
Ok((last_price.to_f64() - first_price.to_f64()) / first_price.to_f64())
}
fn calculate_volatility(
&self,
data: &[MarketTick],
_window: Duration,
) -> Result<f64, FeatureError> {
if data.len() < 2 {
return Ok(0.0);
}
// Calculate log returns
let mut returns = Vec::with_capacity(data.len() - 1);
for window in data.windows(2) {
let prev_price = window.first().map(|t| t.price).unwrap_or(Price::ZERO);
let curr_price = window.last().map(|t| t.price).unwrap_or(Price::ZERO);
if prev_price.to_f64() > 0.0 && curr_price.to_f64() > 0.0 {
returns.push((curr_price.to_f64() / prev_price.to_f64()).ln());
}
}
if returns.is_empty() {
return Ok(0.0);
}
// Calculate sample standard deviation
let mean = returns.iter().sum::<f64>() / returns.len() as f64;
let variance = returns.iter().map(|r| (r - mean).powi(2)).sum::<f64>()
/ (returns.len() - 1).max(1) as f64;
Ok(variance.sqrt())
}
// TLOB-specific sequence generation methods
fn generate_order_book_sequence(
&self,
_data: &DatabentoBuData,
length: usize,
) -> Result<Vec<f64>, FeatureError> {
// Ok variant
Ok(vec![0.0; length])
}
fn generate_trade_flow_sequence(
&self,
_data: &DatabentoBuData,
length: usize,
) -> Result<Vec<f64>, FeatureError> {
// Ok variant
Ok(vec![0.0; length])
}
fn generate_spread_sequence(
&self,
_data: &DatabentoBuData,
length: usize,
) -> Result<Vec<f64>, FeatureError> {
// Ok variant
Ok(vec![0.0; length])
}
fn generate_depth_sequence(
&self,
_data: &DatabentoBuData,
length: usize,
) -> Result<Vec<f64>, FeatureError> {
// Ok variant
Ok(vec![0.0; length])
}
// MAMBA-specific sequence generation methods
fn generate_price_sequence(
&self,
data: &[MarketTick],
length: usize,
) -> Result<Vec<f64>, FeatureError> {
let mut sequence = Vec::with_capacity(length);
for i in 0..length {
if let Some(tick) = data.get(i) {
sequence.push(tick.price.to_f64());
} else {
sequence.push(0.0);
}
}
Ok(sequence)
}
fn generate_volume_sequence(
&self,
data: &[MarketTick],
length: usize,
) -> Result<Vec<f64>, FeatureError> {
let mut sequence = Vec::with_capacity(length);
for i in 0..length {
if let Some(tick) = data.get(i) {
sequence.push(tick.size.to_f64());
} else {
sequence.push(0.0);
}
}
Ok(sequence)
}
fn generate_sentiment_sequence(
&self,
_data: &BenzingaNewsData,
length: usize,
) -> Result<Vec<f64>, FeatureError> {
// Ok variant
Ok(vec![0.0; length])
}
const fn calculate_volatility_regime(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_trend_persistence(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
// DQN-specific methods
const fn calculate_time_in_position(&self, _position: f64) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn estimate_market_impact(
&self,
_data: &DatabentoBuData,
_position_size: f64,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_opportunity_cost(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_available_liquidity(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn estimate_transaction_costs(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_risk_budget_remaining(
&self,
_position: f64,
_pnl: f64,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
// PPO-specific methods
const fn calculate_gae_advantage(&self, _rewards: &[f64]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn estimate_state_value(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_policy_entropy(&self, _actions: &[f64]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_exploration_bonus(&self, _actions: &[f64]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn estimate_model_uncertainty(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
// Liquid Networks methods
const fn calculate_fast_adaptation(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_slow_adaptation(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn detect_regime_change(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_adaptation_rate(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn measure_causal_strength(
&self,
_databento: &DatabentoBuData,
_benzinga: &BenzingaNewsData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_information_flow(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_network_centrality(&self, _symbol: &Symbol) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
// TFT methods
fn generate_observed_sequence(
&self,
data: &[MarketTick],
length: usize,
) -> Result<Vec<f64>, FeatureError> {
self.generate_price_sequence(data, length)
}
fn generate_known_future_sequence(&self, horizon: usize) -> Result<Vec<f64>, FeatureError> {
// Ok variant
Ok(vec![0.0; horizon])
}
fn generate_static_metadata(&self, _symbol: &Symbol) -> Result<Vec<f64>, FeatureError> {
Ok(vec![0.0; 10]) // Static metadata vector
}
fn extract_temporal_patterns(&self, _data: &[MarketTick]) -> Result<Vec<f64>, FeatureError> {
Ok(vec![0.0; 24]) // Hourly patterns
}
fn decompose_seasonal_components(
&self,
_data: &[MarketTick],
) -> Result<Vec<f64>, FeatureError> {
Ok(vec![0.0; 12]) // Monthly seasonality
}
// Volume analysis methods
const fn calculate_volume_ratio(
&self,
_data: &[MarketTick],
_window: Duration,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(1.0)
}
const fn calculate_vwap_deviation(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_volume_imbalance(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
// Technical indicators
const fn calculate_rsi(
&self,
_data: &[MarketTick],
_period: usize,
) -> Result<f64, FeatureError> {
Ok(50.0) // Neutral RSI
}
const fn calculate_macd_signal(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_bollinger_position(
&self,
_data: &[MarketTick],
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.5)
}
const fn calculate_momentum_score(&self, _data: &[MarketTick]) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
// Market context methods
fn normalize_time_of_day(&self, time: DateTime<Utc>) -> f64 {
let hour = time.hour() as f64;
let minute = time.minute() as f64;
(hour * 60.0 + minute) / (24.0 * 60.0)
}
const fn determine_market_session(&self, _time: DateTime<Utc>) -> u8 {
2 // Regular session
}
const fn is_options_expiration(&self, _time: DateTime<Utc>) -> bool {
false
}
const fn is_earnings_week(&self, _time: DateTime<Utc>) -> bool {
false
}
// Databento features
const fn calculate_spread_bps(&self, _data: &DatabentoBuData) -> Result<f64, FeatureError> {
Ok(5.0) // 5 basis points default spread
}
const fn calculate_order_book_imbalance(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
fn calculate_depth_weighted_mid(&self, _data: &DatabentoBuData) -> Result<Price, FeatureError> {
Price::from_f64(100.0).map_err(|e| FeatureError::MathematicalError {
feature: "depth_weighted_mid".to_owned(),
reason: e.to_string(),
})
}
const fn calculate_effective_spread_bps(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(3.0)
}
const fn calculate_price_impact_bps(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(2.0)
}
const fn calculate_order_book_slope(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_order_book_curvature(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_order_intensity(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
Ok(10.0) // 10 orders per second
}
const fn calculate_cancellation_ratio(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
Ok(0.3) // 30% cancellation ratio
}
const fn classify_trade_direction(&self, _data: &DatabentoBuData) -> Result<i8, FeatureError> {
Ok(0) // Unknown direction
}
const fn classify_trade_size(&self, _data: &DatabentoBuData) -> Result<u8, FeatureError> {
Ok(2) // Medium size
}
const fn calculate_trade_urgency(&self, _data: &DatabentoBuData) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.5)
}
const fn calculate_realized_spread_bps(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(4.0)
}
const fn calculate_information_share(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.5)
}
const fn estimate_microstructure_noise(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.1)
}
const fn calculate_tick_streak(&self, _data: &DatabentoBuData) -> Result<i8, FeatureError> {
// Ok variant
Ok(0)
}
const fn calculate_quote_frequency(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
Ok(100.0) // 100 quotes per second
}
const fn estimate_arrival_intensity(
&self,
_data: &DatabentoBuData,
) -> Result<f64, FeatureError> {
Ok(50.0) // 50 arrivals per second
}
// Benzinga news features
const fn calculate_news_sentiment(
&self,
_data: &BenzingaNewsData,
) -> Result<f64, FeatureError> {
Ok(0.0) // Neutral sentiment
}
const fn calculate_sentiment_confidence(
&self,
_data: &BenzingaNewsData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.5)
}
const fn calculate_news_velocity(&self, _data: &BenzingaNewsData) -> Result<f64, FeatureError> {
Ok(1.0) // 1 article per hour
}
const fn detect_breaking_news(&self, _data: &BenzingaNewsData) -> Result<bool, FeatureError> {
// Ok variant
Ok(false)
}
const fn calculate_weighted_sentiment(
&self,
_data: &BenzingaNewsData,
_window: Duration,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn is_earnings_related(&self, _data: &BenzingaNewsData) -> Result<bool, FeatureError> {
// Ok variant
Ok(false)
}
const fn extract_analyst_rating(
&self,
_data: &BenzingaNewsData,
) -> Result<Option<f64>, FeatureError> {
// Ok variant
Ok(None)
}
const fn detect_unusual_options(&self, _data: &BenzingaNewsData) -> Result<bool, FeatureError> {
// Ok variant
Ok(false)
}
const fn extract_sec_filing_type(
&self,
_data: &BenzingaNewsData,
) -> Result<Option<String>, FeatureError> {
// Ok variant
Ok(None)
}
const fn calculate_sentiment_acceleration(
&self,
_data: &BenzingaNewsData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_sentiment_divergence(
&self,
_data: &BenzingaNewsData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn calculate_contrarian_signal(
&self,
_data: &BenzingaNewsData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(0.0)
}
const fn count_unique_sources(&self, _data: &BenzingaNewsData) -> Result<u32, FeatureError> {
// Ok variant
Ok(5)
}
const fn calculate_mention_volume(
&self,
_data: &BenzingaNewsData,
) -> Result<u32, FeatureError> {
// Ok variant
Ok(10)
}
const fn calculate_social_amplification(
&self,
_data: &BenzingaNewsData,
) -> Result<f64, FeatureError> {
// Ok variant
Ok(1.0)
}
// ... dozens more helper methods for each specific feature
}
// Type aliases for common market data structures (must be defined before use)
type OrderBookLevel = (Price, Quantity); // (price, quantity) tuple
type Trade = TradeEvent; // Use canonical trade event
// Data structures for Databento and Benzinga integration
#[derive(Debug, Clone)]
pub struct DatabentoBuData {
/// Order Book
pub order_book: Vec<OrderBookLevel>,
/// Trades
pub trades: Vec<Trade>,
/// Quotes
pub quotes: Vec<QuoteEvent>,
/// Timestamp
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone)]
/// BenzingaNewsData
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct BenzingaNewsData {
/// Articles
pub articles: Vec<NewsArticle>,
/// Sentiment Scores
pub sentiment_scores: Vec<SentimentScore>,
/// Analyst Ratings
pub analyst_ratings: Vec<AnalystRating>,
/// Unusual Options
pub unusual_options: Vec<UnusualOptionsActivity>,
/// Timestamp
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone)]
/// NewsArticle
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct NewsArticle {
/// Title
pub title: String,
/// Content
pub content: String,
/// Source
pub source: String,
/// Timestamp
pub timestamp: DateTime<Utc>,
/// Symbols
pub symbols: Vec<Symbol>,
/// Category
pub category: String,
/// Importance
pub importance: f64,
}
#[derive(Debug, Clone)]
/// SentimentScore
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct SentimentScore {
/// Symbol
pub symbol: Symbol,
/// Score
pub score: f64,
/// Confidence
pub confidence: f64,
/// Timestamp
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone)]
/// AnalystRating
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct AnalystRating {
/// Symbol
pub symbol: Symbol,
/// Rating
pub rating: String,
/// Price Target
pub price_target: Option<f64>,
/// Analyst
pub analyst: String,
/// Firm
pub firm: String,
/// Timestamp
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone)]
/// UnusualOptionsActivity
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct UnusualOptionsActivity {
/// Symbol
pub symbol: Symbol,
/// `Option` Type
pub option_type: String,
/// Strike
pub strike: f64,
/// Expiration
pub expiration: DateTime<Utc>,
/// Volume
pub volume: i64,
/// Unusual Score
pub unusual_score: f64,
/// Timestamp
pub timestamp: DateTime<Utc>,
}