- 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>
1636 lines
50 KiB
Rust
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>,
|
|
}
|