diff --git a/Cargo.lock b/Cargo.lock index 3aa374f10..e2fdba8f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1347,6 +1347,7 @@ dependencies = [ "toml", "tracing", "tracing-subscriber", + "trading_engine", "uuid 1.18.1", ] diff --git a/adaptive-strategy/src/execution/mod.rs b/adaptive-strategy/src/execution/mod.rs index c77a4c7e1..06e7ffa5b 100644 --- a/adaptive-strategy/src/execution/mod.rs +++ b/adaptive-strategy/src/execution/mod.rs @@ -82,7 +82,7 @@ pub struct Order { // OrderSide now imported from canonical source use common::prelude::OrderSide; -// OrderType and OrderStatus now imported from canonical source via foxhunt_common_types +// OrderType and OrderStatus imported from canonical source in common::prelude /// Time in force #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/backtesting/src/replay_engine.rs b/backtesting/src/replay_engine.rs index 761d8a628..ff28b8bd7 100644 --- a/backtesting/src/replay_engine.rs +++ b/backtesting/src/replay_engine.rs @@ -13,6 +13,7 @@ use std::{ use anyhow::{Context, Result}; use chrono::{DateTime, Utc}; use common::types::Timestamp; +use trading_engine::types::prelude::{Symbol, Decimal, Quantity, MarketEvent, Price}; use crossbeam_channel::{bounded, Receiver, Sender}; use dashmap::DashMap; use serde::{Deserialize, Serialize}; diff --git a/backtesting/src/strategy_tester.rs b/backtesting/src/strategy_tester.rs index 0986f3c97..97ff9026e 100644 --- a/backtesting/src/strategy_tester.rs +++ b/backtesting/src/strategy_tester.rs @@ -20,9 +20,10 @@ use serde::{Deserialize, Serialize}; use tokio::sync::{mpsc, RwLock}; use tracing::{debug, error, info, warn}; use trading_engine::types::basic::{ - Order, OrderId, OrderStatus, OrderType, Position, Price, Quantity, Side as OrderSide, Symbol, + Order, OrderId, Position, Price, Quantity, Side as OrderSide, Symbol, TimeInForce, }; +use common::types::{OrderStatus, OrderType}; use trading_engine::types::events::MarketEvent; use trading_engine::types::prelude::*; use uuid::Uuid; @@ -447,10 +448,11 @@ impl StrategyTester { for (symbol, position) in positions { if let Some(market_event) = self.market_data.get(&symbol) { let current_price = self.extract_price_from_event(&market_event)?; - let position_value = position.quantity.to_decimal().unwrap_or_default() - * Price::from(current_price).to_decimal().unwrap_or_default(); + let position_decimal = position.quantity.to_decimal().unwrap_or(Decimal::ZERO); + let price_decimal = Price::from(current_price).to_decimal().unwrap_or(Decimal::ZERO); + let position_value = position_decimal * price_decimal; - if position.quantity.to_decimal().unwrap_or_default() >= Decimal::ZERO { + if position_decimal >= Decimal::ZERO { total_value += position_value; } else { // Short position @@ -525,6 +527,25 @@ impl StrategyTester { false } } + OrderType::TrailingStop => { + // For backtesting, treat as stop order + if let Some(order_price) = order.price { + match order.side { + OrderSide::Buy => current_price >= order_price, + OrderSide::Sell => current_price <= order_price, + } + } else { + false + } + } + OrderType::Hidden => { + // For backtesting, treat as market order + true + } + _ => { + // Default case for any other order types + false + } } } @@ -552,8 +573,8 @@ impl StrategyTester { // Update account let mut account = self.account.write().await; - let trade_value = order.quantity.to_decimal().unwrap_or_default() - * execution_price.to_decimal().unwrap_or_default(); + let trade_value = order.quantity.to_decimal().unwrap_or(Decimal::ZERO) + * execution_price.to_decimal().unwrap_or(Decimal::ZERO); match order.side { OrderSide::Buy => { @@ -634,16 +655,16 @@ impl StrategyTester { /// Apply slippage to execution price fn apply_slippage(&self, price: Price, order: &Order) -> Price { - let slippage = price.to_decimal().unwrap_or_default() * self.config.slippage_factor; + let slippage = price.to_decimal().unwrap_or(Decimal::ZERO) * self.config.slippage_factor; match order.side { OrderSide::Buy => Price::from_f64( - (price.to_decimal().unwrap_or_default() + slippage) + (price.to_decimal().unwrap_or(Decimal::ZERO) + slippage) .try_into() .unwrap_or(0.0), ) .unwrap_or(Price::zero()), OrderSide::Sell => Price::from_f64( - (price.to_decimal().unwrap_or_default() - slippage) + (price.to_decimal().unwrap_or(Decimal::ZERO) - slippage) .try_into() .unwrap_or(0.0), ) @@ -653,8 +674,8 @@ impl StrategyTester { /// Calculate commission for trade fn calculate_commission(&self, order: &Order, price: Price) -> Decimal { - let trade_value = order.quantity.to_decimal().unwrap_or_default() - * price.to_decimal().unwrap_or_default(); + let trade_value = order.quantity.to_decimal().unwrap_or(Decimal::ZERO) + * price.to_decimal().unwrap_or(Decimal::ZERO); trade_value * self.config.commission_rate } @@ -667,8 +688,8 @@ impl StrategyTester { ask_price, .. } => { - let avg_price = (bid_price.to_decimal().unwrap_or_default() - + ask_price.to_decimal().unwrap_or_default()) + let avg_price = (bid_price.to_decimal().unwrap_or(Decimal::ZERO) + + ask_price.to_decimal().unwrap_or(Decimal::ZERO)) / Decimal::from(2); Ok(Price::from_f64(avg_price.try_into().unwrap_or(0.0)).unwrap_or(Price::ZERO)) } diff --git a/common/Cargo.toml b/common/Cargo.toml index 9f2e83127..092b0a20f 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -44,8 +44,9 @@ tracing-subscriber.workspace = true # Configuration toml.workspace = true config = { path = "../crates/config" } -# Common types (migrated to common/src/types.rs) -# foxhunt-common-types.workspace = true # REMOVED - types migrated to common/ + +# Trading engine for canonical types +trading_engine = { path = "../trading_engine" } # Utilities uuid = { workspace = true, features = ["v4", "serde"] } diff --git a/common/src/trading.rs b/common/src/trading.rs index 5883e2c56..9ba757cd4 100644 --- a/common/src/trading.rs +++ b/common/src/trading.rs @@ -7,142 +7,8 @@ use serde::{Deserialize, Serialize}; use std::fmt; -/// Order side - whether the order is a buy or sell -/// -/// This is the canonical definition used across all services. -/// Note: Some legacy code may use "OrderSide" - this is the same enum. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum OrderSide { - /// Buy order - Buy, - /// Sell order - Sell, -} - -impl fmt::Display for OrderSide { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Buy => write!(f, "BUY"), - Self::Sell => write!(f, "SELL"), - } - } -} - -impl Default for OrderSide { - fn default() -> Self { - Self::Buy - } -} - -// Alias for backward compatibility with existing code -pub use OrderSide as Side; - -/// Order status throughout its lifecycle -/// -/// This is the canonical definition with all possible states -/// an order can be in across different brokers and systems. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[non_exhaustive] -pub enum OrderStatus { - /// Order created but not yet submitted - Created, - /// Order submitted to broker/exchange - Submitted, - /// Order partially filled - PartiallyFilled, - /// Order completely filled - Filled, - /// Order rejected by broker/exchange - Rejected, - /// Order cancelled - Cancelled, - /// New order (broker-specific) - New, - /// Order expired - Expired, - /// Order pending (awaiting processing) - Pending, - /// Order working in the market - Working, - /// Unknown status - Unknown, - /// Order suspended - Suspended, - /// Order pending cancellation - PendingCancel, - /// Order pending replacement/modification - PendingReplace, -} - -impl fmt::Display for OrderStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Created => write!(f, "CREATED"), - Self::Submitted => write!(f, "SUBMITTED"), - Self::PartiallyFilled => write!(f, "PARTIALLY_FILLED"), - Self::Filled => write!(f, "FILLED"), - Self::Rejected => write!(f, "REJECTED"), - Self::Cancelled => write!(f, "CANCELLED"), - Self::New => write!(f, "NEW"), - Self::Expired => write!(f, "EXPIRED"), - Self::Pending => write!(f, "PENDING"), - Self::Working => write!(f, "WORKING"), - Self::Unknown => write!(f, "UNKNOWN"), - Self::Suspended => write!(f, "SUSPENDED"), - Self::PendingCancel => write!(f, "PENDING_CANCEL"), - Self::PendingReplace => write!(f, "PENDING_REPLACE"), - } - } -} - -impl Default for OrderStatus { - fn default() -> Self { - Self::Created - } -} - -/// Order type specifying execution behavior -/// -/// This is the canonical definition supporting all common -/// order types used in HFT systems. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[non_exhaustive] -pub enum OrderType { - /// Market order - execute immediately at best available price - Market, - /// Limit order - execute only at specified price or better - Limit, - /// Stop order - becomes market order when stop price reached - Stop, - /// Stop limit order - becomes limit order when stop price reached - StopLimit, - /// Iceberg order - only shows small portion of total size - Iceberg, - /// Trailing stop - stop price follows market by specified amount - TrailingStop, - /// Hidden order - not displayed in order book - Hidden, -} - -impl fmt::Display for OrderType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Market => write!(f, "MARKET"), - Self::Limit => write!(f, "LIMIT"), - Self::Stop => write!(f, "STOP"), - Self::StopLimit => write!(f, "STOP_LIMIT"), - Self::Iceberg => write!(f, "ICEBERG"), - Self::TrailingStop => write!(f, "TRAILING_STOP"), - Self::Hidden => write!(f, "HIDDEN"), - } - } -} - -impl Default for OrderType { - fn default() -> Self { - Self::Market - } -} +// Re-export canonical types from trading_engine (via common::types) +pub use crate::types::{OrderSide, OrderStatus, OrderType, Side}; /// Time in force specification for orders #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] diff --git a/common/src/types.rs b/common/src/types.rs index 3669bbcc3..3c1cafd8b 100644 --- a/common/src/types.rs +++ b/common/src/types.rs @@ -268,300 +268,23 @@ pub enum CommonTypeError { }, } -/// Order side - whether the order is a buy or sell -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum OrderSide { - /// Buy order - Buy, - /// Sell order - Sell, -} +// Re-export ALL canonical trading types from trading_engine +pub use trading_engine::types::{ + Currency, OrderId, OrderSide, OrderStatus, OrderType, Price, Quantity, Side, Symbol, TradeId, + TimeInForce, +}; -impl fmt::Display for OrderSide { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Buy => write!(f, "BUY"), - Self::Sell => write!(f, "SELL"), - } - } -} +// Re-export volume as alias +pub use trading_engine::types::Quantity as Volume; -impl Default for OrderSide { - fn default() -> Self { - Self::Buy - } -} +// TimeInForce moved to canonical source: trading_engine::types::TimeInForce -/// Alias for backward compatibility -pub use OrderSide as Side; +// Currency moved to canonical source: trading_engine::types::Currency -/// Order status throughout its lifecycle -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[non_exhaustive] -pub enum OrderStatus { - Created, - Submitted, - PartiallyFilled, - Filled, - Rejected, - Cancelled, - New, - Expired, - Pending, - Working, - Unknown, - Suspended, - PendingCancel, - PendingReplace, -} +// Price moved to canonical source: trading_engine::types::Price -impl fmt::Display for OrderStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Created => write!(f, "CREATED"), - Self::Submitted => write!(f, "SUBMITTED"), - Self::PartiallyFilled => write!(f, "PARTIALLY_FILLED"), - Self::Filled => write!(f, "FILLED"), - Self::Rejected => write!(f, "REJECTED"), - Self::Cancelled => write!(f, "CANCELLED"), - Self::New => write!(f, "NEW"), - Self::Expired => write!(f, "EXPIRED"), - Self::Pending => write!(f, "PENDING"), - Self::Working => write!(f, "WORKING"), - Self::Unknown => write!(f, "UNKNOWN"), - Self::Suspended => write!(f, "SUSPENDED"), - Self::PendingCancel => write!(f, "PENDING_CANCEL"), - Self::PendingReplace => write!(f, "PENDING_REPLACE"), - } - } -} - -impl Default for OrderStatus { - fn default() -> Self { - Self::Created - } -} - -/// Order type specifying execution behavior -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[non_exhaustive] -pub enum OrderType { - Market, - Limit, - Stop, - StopLimit, - Iceberg, - TrailingStop, - Hidden, -} - -impl fmt::Display for OrderType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Market => write!(f, "MARKET"), - Self::Limit => write!(f, "LIMIT"), - Self::Stop => write!(f, "STOP"), - Self::StopLimit => write!(f, "STOP_LIMIT"), - Self::Iceberg => write!(f, "ICEBERG"), - Self::TrailingStop => write!(f, "TRAILING_STOP"), - Self::Hidden => write!(f, "HIDDEN"), - } - } -} - -impl Default for OrderType { - fn default() -> Self { - Self::Market - } -} - -/// Time in force specification for orders -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum TimeInForce { - Day, - GoodTillCancel, - GTC, - ImmediateOrCancel, - IOC, - FillOrKill, - FOK, -} - -impl fmt::Display for TimeInForce { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Day => write!(f, "DAY"), - Self::GoodTillCancel | Self::GTC => write!(f, "GTC"), - Self::ImmediateOrCancel | Self::IOC => write!(f, "IOC"), - Self::FillOrKill | Self::FOK => write!(f, "FOK"), - } - } -} - -impl Default for TimeInForce { - fn default() -> Self { - Self::Day - } -} - -/// Currency enumeration for financial instruments -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] -pub enum Currency { - USD, - EUR, - GBP, - JPY, - CHF, - CAD, - AUD, - NZD, - BTC, -} - -impl fmt::Display for Currency { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::USD => write!(f, "USD"), - Self::EUR => write!(f, "EUR"), - Self::GBP => write!(f, "GBP"), - Self::JPY => write!(f, "JPY"), - Self::CHF => write!(f, "CHF"), - Self::CAD => write!(f, "CAD"), - Self::AUD => write!(f, "AUD"), - Self::NZD => write!(f, "NZD"), - Self::BTC => write!(f, "BTC"), - } - } -} - -impl Default for Currency { - fn default() -> Self { - Self::USD - } -} - -/// Type-safe price with validation -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct Price(Decimal); - -impl Price { - pub const ZERO: Self = Self(Decimal::ZERO); - - /// Create a new price with validation - pub fn new(value: Decimal) -> Result { - if value < Decimal::ZERO { - return Err(CommonTypeError::InvalidPrice { - value: value.to_string(), - reason: "Price cannot be negative".to_string(), - }); - } - Ok(Self(value)) - } - - /// Create price from f64 with validation - pub fn from_f64(value: f64) -> Result { - if !value.is_finite() || value < 0.0 { - return Err(CommonTypeError::InvalidPrice { - value: value.to_string(), - reason: "Price must be a finite positive number".to_string(), - }); - } - - let decimal = Decimal::from_f64_retain(value) - .ok_or_else(|| CommonTypeError::InvalidPrice { - value: value.to_string(), - reason: "Cannot convert to decimal".to_string(), - })?; - - Ok(Self(decimal)) - } - - /// Get the underlying decimal value - pub fn value(&self) -> Decimal { - self.0 - } - - /// Convert to f64 - pub fn to_f64(&self) -> f64 { - use rust_decimal::prelude::ToPrimitive; - self.0.to_f64().unwrap_or(0.0) - } -} - -impl fmt::Display for Price { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl Default for Price { - fn default() -> Self { - Self::ZERO - } -} - -/// Type-safe quantity with validation -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct Quantity(Decimal); - -impl Quantity { - pub const ZERO: Self = Self(Decimal::ZERO); - - /// Create a new quantity with validation - pub fn new(value: Decimal) -> Result { - if value < Decimal::ZERO { - return Err(CommonTypeError::InvalidQuantity { - value: value.to_string(), - reason: "Quantity cannot be negative".to_string(), - }); - } - Ok(Self(value)) - } - - /// Create quantity from f64 with validation - pub fn from_f64(value: f64) -> Result { - if !value.is_finite() || value < 0.0 { - return Err(CommonTypeError::InvalidQuantity { - value: value.to_string(), - reason: "Quantity must be a finite positive number".to_string(), - }); - } - - let decimal = Decimal::from_f64_retain(value) - .ok_or_else(|| CommonTypeError::InvalidQuantity { - value: value.to_string(), - reason: "Cannot convert to decimal".to_string(), - })?; - - Ok(Self(decimal)) - } - - /// Get the underlying decimal value - pub fn value(&self) -> Decimal { - self.0 - } - - /// Convert to f64 - pub fn to_f64(&self) -> f64 { - use rust_decimal::prelude::ToPrimitive; - self.0.to_f64().unwrap_or(0.0) - } -} - -impl fmt::Display for Quantity { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl Default for Quantity { - fn default() -> Self { - Self::ZERO - } -} - -/// Type-safe volume (alias for Quantity for backward compatibility) -pub type Volume = Quantity; +// Quantity moved to canonical source: trading_engine::types::Quantity +// Volume moved to canonical source: trading_engine::types::Quantity (as Volume alias) /// Money amount with currency #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -583,112 +306,11 @@ impl fmt::Display for Money { } } -/// Type-safe order identifier -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct OrderId(u64); +// OrderId moved to canonical source: trading_engine::types::OrderId -impl OrderId { - /// Generate a new unique order ID - pub fn new() -> Self { - static COUNTER: AtomicU64 = AtomicU64::new(1); - Self(COUNTER.fetch_add(1, Ordering::Relaxed)) - } +// TradeId moved to canonical source: trading_engine::types::TradeId - /// Create from existing u64 - pub const fn from_u64(id: u64) -> Self { - Self(id) - } - - /// Get the underlying u64 value - pub const fn value(&self) -> u64 { - self.0 - } -} - -impl fmt::Display for OrderId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl Default for OrderId { - fn default() -> Self { - Self::new() - } -} - -/// Type-safe trade identifier -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct TradeId(String); - -impl TradeId { - /// Create a new trade ID with validation - pub fn new>(id: S) -> Result { - let id = id.into(); - if id.trim().is_empty() { - return Err(CommonTypeError::InvalidIdentifier { - field: "trade_id".to_string(), - reason: "Trade ID cannot be empty".to_string(), - }); - } - Ok(Self(id)) - } - - /// Generate a new UUID-based trade ID - pub fn generate() -> Self { - Self(Uuid::new_v4().to_string()) - } - - /// Get the ID as a string slice - pub fn as_str(&self) -> &str { - &self.0 - } - - /// Convert to owned String - pub fn into_string(self) -> String { - self.0 - } -} - -impl fmt::Display for TradeId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -/// Type-safe symbol identifier -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub struct Symbol(String); - -impl Symbol { - /// Create a new symbol with validation - pub fn new>(symbol: S) -> Result { - let symbol = symbol.into().to_uppercase(); - if symbol.trim().is_empty() { - return Err(CommonTypeError::InvalidIdentifier { - field: "symbol".to_string(), - reason: "Symbol cannot be empty".to_string(), - }); - } - Ok(Self(symbol)) - } - - /// Get the symbol as a string slice - pub fn as_str(&self) -> &str { - &self.0 - } - - /// Convert to owned String - pub fn into_string(self) -> String { - self.0 - } -} - -impl fmt::Display for Symbol { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} +// Symbol moved to canonical source: trading_engine::types::Symbol /// Type-safe account identifier #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] diff --git a/data/src/lib.rs b/data/src/lib.rs index 3682dffd9..b6593fb7e 100644 --- a/data/src/lib.rs +++ b/data/src/lib.rs @@ -222,6 +222,7 @@ pub use crate::utils::{ use tokio::sync::broadcast; // Import canonical types from trading_engine prelude per TYPE_GOVERNANCE.md use common::prelude::*; +use trading_engine::types::prelude::OrderEvent; // Add missing OrderEvent import // Import shared configuration from foxhunt-config-crate use config::{DataModuleConfig, DataModuleSettings}; @@ -260,7 +261,17 @@ impl DataManager { // REMOVED: Polygon client initialization let ib_client = if let Some(ib_config) = &config.interactive_brokers { - Some(brokers::InteractiveBrokersAdapter::new(ib_config.clone())) + let broker_config = brokers::IBConfig { + host: ib_config.host.clone(), + port: ib_config.port, + client_id: ib_config.client_id as i32, + account_id: std::env::var("IB_ACCOUNT_ID").unwrap_or_else(|_| "DU123456".to_string()), + connection_timeout: ib_config.timeout_seconds, + heartbeat_interval: 30, + max_reconnect_attempts: 5, + request_timeout: 30, + }; + Some(brokers::InteractiveBrokersAdapter::new(broker_config)) } else { None }; diff --git a/data/src/providers/benzinga/integration.rs b/data/src/providers/benzinga/integration.rs index 3f45a6d07..90c8d6765 100644 --- a/data/src/providers/benzinga/integration.rs +++ b/data/src/providers/benzinga/integration.rs @@ -550,7 +550,7 @@ impl BenzingaHFTIntegration { signal_config: &SignalConfig, rate_limiter: &Arc>>>>, ) -> Option { - let symbol = event.symbol()?.clone(); + let symbol = event.symbol().clone(); let now = Utc::now(); // Check rate limiting diff --git a/data/src/providers/benzinga/mod.rs b/data/src/providers/benzinga/mod.rs index abe0fd6e5..1006e7b99 100644 --- a/data/src/providers/benzinga/mod.rs +++ b/data/src/providers/benzinga/mod.rs @@ -352,7 +352,7 @@ impl BenzingaProviderFactory { config: BenzingaStreamingConfig, ) -> crate::error::Result { // Create a default config manager for now - this needs proper implementation - let config_manager = config::ConfigManager::new_in_memory()?; + let config_manager = config::ConfigManager::new(None, None, None).await?; BenzingaHFTIntegration::new(config_manager).await } diff --git a/data/src/providers/benzinga/production_streaming.rs b/data/src/providers/benzinga/production_streaming.rs index 873e8f711..047729e94 100644 --- a/data/src/providers/benzinga/production_streaming.rs +++ b/data/src/providers/benzinga/production_streaming.rs @@ -1037,8 +1037,7 @@ impl RealTimeProvider for ProductionBenzingaProvider { *status = ConnectionStatus::connected(); } - self.metrics.record_connection_success(); - self.connected.store(true, Ordering::Relaxed); + self.metrics.successful_connections.fetch_add(1, Ordering::Relaxed); // Start background tasks self.start_background_tasks().await?; @@ -1069,7 +1068,10 @@ impl RealTimeProvider for ProductionBenzingaProvider { *status = ConnectionStatus::disconnected(); } - self.connection_status.write().await.is_connected = false; + { + let mut status = self.connection_status.write().await; + status.state = ConnectionState::Disconnected; + } info!("Disconnected from Benzinga WebSocket stream"); Ok(()) } diff --git a/data/src/providers/benzinga/streaming.rs b/data/src/providers/benzinga/streaming.rs index 6223f7842..61f21c46d 100644 --- a/data/src/providers/benzinga/streaming.rs +++ b/data/src/providers/benzinga/streaming.rs @@ -1037,7 +1037,7 @@ impl RealTimeProvider for BenzingaStreamingProvider { if let Some(tx) = self.event_tx.lock().await.as_ref() { let status_event = MarketDataEvent::ConnectionStatus(ConnectionEvent { provider: "benzinga".to_string(), - status: ConnectionState::Connected, + status: ConnectionStatus::connected(), message: Some("Connected to Benzinga streaming API".to_string()), timestamp: Utc::now(), }); @@ -1081,7 +1081,7 @@ impl RealTimeProvider for BenzingaStreamingProvider { if let Some(tx) = self.event_tx.lock().await.as_ref() { let status_event = MarketDataEvent::ConnectionStatus(ConnectionEvent { provider: "benzinga".to_string(), - status: ConnectionState::Disconnected, + status: ConnectionStatus::disconnected(), message: Some("Disconnected from Benzinga streaming API".to_string()), timestamp: Utc::now(), }); diff --git a/data/src/training_pipeline.rs b/data/src/training_pipeline.rs index 901153e59..d971b2e28 100644 --- a/data/src/training_pipeline.rs +++ b/data/src/training_pipeline.rs @@ -427,7 +427,14 @@ impl TrainingDataPipeline { Arc::new(RwLock::new(FeatureProcessor::new(config.features.clone())?)); // Initialize data validator - let validator = Arc::new(DataValidator::new(config.validation.clone())?); + let data_validation_config = DataValidationConfig { + enabled: config.validation.enabled, + max_missing_percentage: config.validation.max_missing_percentage, + outlier_detection: config.validation.outlier_detection.clone(), + min_data_points: config.validation.min_data_points, + quality_threshold: config.validation.quality_threshold, + }; + let validator = Arc::new(DataValidator::new(data_validation_config)?); // Initialize storage manager with default config let storage_config = TrainingStorageConfig::default(); diff --git a/data/src/types.rs b/data/src/types.rs index e5ce36ed5..d6b642d90 100644 --- a/data/src/types.rs +++ b/data/src/types.rs @@ -57,24 +57,8 @@ pub enum MarketDataEvent { UnusualOptions(crate::providers::common::UnusualOptionsEvent), } -/// Quote event structure -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct QuoteEvent { - /// Symbol - pub symbol: String, - /// Bid price - pub bid: Option, - /// Ask price - pub ask: Option, - /// Bid size - pub bid_size: Option, - /// Ask size - pub ask_size: Option, - /// Exchange - pub exchange: Option, - /// Timestamp - pub timestamp: chrono::DateTime, -} +// Use canonical QuoteEvent from trading_engine +pub use trading_engine::types::QuoteEvent; // TradeEvent removed - use canonical version from trading_engine::types::TradeEvent pub use trading_engine::types::TradeEvent; diff --git a/risk-data/src/limits.rs b/risk-data/src/limits.rs index a471c5e60..aa2a9849b 100644 --- a/risk-data/src/limits.rs +++ b/risk-data/src/limits.rs @@ -11,6 +11,7 @@ use sqlx::{PgPool, Row}; use std::collections::HashMap; use tracing::{error, info, warn}; use common::prelude::*; +use trading_engine::types::prelude::Decimal; use uuid::Uuid; use crate::{RiskDataError, RiskDataResult}; diff --git a/risk/src/compliance.rs b/risk/src/compliance.rs index 0428f65fb..e86521881 100644 --- a/risk/src/compliance.rs +++ b/risk/src/compliance.rs @@ -28,6 +28,7 @@ use crate::risk_types::{ }; // CANONICAL TYPE IMPORTS - ENFORCED BY TYPE SYSTEM AGENT use trading_engine::types::prelude::*; +use common::types::OrderType; /// Comprehensive compliance validation result #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/tests/unit/core/unified_extractor_tests.rs b/tests/unit/core/unified_extractor_tests.rs index 22e000f47..b08366e78 100644 --- a/tests/unit/core/unified_extractor_tests.rs +++ b/tests/unit/core/unified_extractor_tests.rs @@ -19,7 +19,7 @@ use trading_engine::features::unified_extractor::{ AnalystRating, UnusualOptionsActivity, }; use trading_engine::types::prelude::*; -use data::types::QuoteEvent; +use trading_engine::types::QuoteEvent; // Test fixtures and mock data generators diff --git a/trading_engine/src/features/unified_extractor.rs b/trading_engine/src/features/unified_extractor.rs index 5e3cf735c..2e884ed1a 100644 --- a/trading_engine/src/features/unified_extractor.rs +++ b/trading_engine/src/features/unified_extractor.rs @@ -23,7 +23,7 @@ use tracing::{debug, error}; use crate::simd::SimdMarketDataOps; use crate::types::prelude::*; -use crate::trading::data_interface::QuoteEvent; +use crate::types::QuoteEvent; /// Feature extraction errors #[derive(Error, Debug)] diff --git a/trading_engine/src/trading/data_interface.rs b/trading_engine/src/trading/data_interface.rs index e929e17ca..f3000be03 100644 --- a/trading_engine/src/trading/data_interface.rs +++ b/trading_engine/src/trading/data_interface.rs @@ -11,24 +11,8 @@ use std::fmt::Debug; use tokio::sync::broadcast; use serde::{Deserialize, Serialize}; -/// Quote event structure - local definition to avoid cyclic dependencies -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct QuoteEvent { - /// Symbol - pub symbol: String, - /// Bid price - pub bid: Option, - /// Ask price - pub ask: Option, - /// Bid size - pub bid_size: Option, - /// Ask size - pub ask_size: Option, - /// Exchange - pub exchange: Option, - /// Timestamp - pub timestamp: chrono::DateTime, -} +// Use canonical QuoteEvent from crate::types::basic +use crate::types::basic::QuoteEvent; // TradeEvent functionality removed - no longer available without data crate dependency diff --git a/trading_engine/src/trading_operations.rs b/trading_engine/src/trading_operations.rs index eb91b5306..831cf2b67 100644 --- a/trading_engine/src/trading_operations.rs +++ b/trading_engine/src/trading_operations.rs @@ -292,26 +292,7 @@ pub use crate::types::prelude::OrderStatus; // TimeInForce ELIMINATED - Use canonical from types::prelude pub use crate::types::prelude::TimeInForce; -impl fmt::Display for OrderStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - OrderStatus::Created => write!(f, "CREATED"), - OrderStatus::Submitted => write!(f, "SUBMITTED"), - OrderStatus::PartiallyFilled => write!(f, "PARTIALLY_FILLED"), - OrderStatus::Filled => write!(f, "FILLED"), - OrderStatus::Rejected => write!(f, "REJECTED"), - OrderStatus::Cancelled => write!(f, "CANCELLED"), - OrderStatus::New => write!(f, "NEW"), - OrderStatus::Expired => write!(f, "EXPIRED"), - OrderStatus::Pending => write!(f, "PENDING"), - OrderStatus::Working => write!(f, "WORKING"), - OrderStatus::Unknown => write!(f, "UNKNOWN"), - OrderStatus::Suspended => write!(f, "SUSPENDED"), - OrderStatus::PendingCancel => write!(f, "PENDING_CANCEL"), - OrderStatus::PendingReplace => write!(f, "PENDING_REPLACE"), - } - } -} +// Display implementation removed - use canonical from types::basic::OrderStatus // Default implementation ELIMINATED - Use canonical from types::basic diff --git a/trading_engine/src/types/basic.rs b/trading_engine/src/types/basic.rs index 84a0de21f..a8dc050c0 100644 --- a/trading_engine/src/types/basic.rs +++ b/trading_engine/src/types/basic.rs @@ -1435,7 +1435,9 @@ impl Default for Symbol { } } +/// Order type specifying execution behavior - CANONICAL DEFINITION #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[non_exhaustive] pub enum OrderType { Market, Limit, @@ -1446,12 +1448,6 @@ pub enum OrderType { Hidden, } -impl Default for OrderType { - fn default() -> Self { - Self::Market - } -} - impl fmt::Display for OrderType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -1466,31 +1462,15 @@ impl fmt::Display for OrderType { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum Side { - Buy, - Sell, -} - -impl fmt::Display for Side { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Buy => write!(f, "BUY"), - Self::Sell => write!(f, "SELL"), - } - } -} - -// CANONICAL OrderStatus - NO DUPLICATES -// OrderStatus import FIXED - OrderStatus is defined in this file (line 662) - -impl Default for Side { +impl Default for OrderType { fn default() -> Self { - Self::Buy + Self::Market } } +/// Order status throughout its lifecycle - CANONICAL DEFINITION #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[non_exhaustive] pub enum OrderStatus { Created, Submitted, @@ -1508,12 +1488,58 @@ pub enum OrderStatus { PendingReplace, } +impl fmt::Display for OrderStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Created => write!(f, "CREATED"), + Self::Submitted => write!(f, "SUBMITTED"), + Self::PartiallyFilled => write!(f, "PARTIALLY_FILLED"), + Self::Filled => write!(f, "FILLED"), + Self::Rejected => write!(f, "REJECTED"), + Self::Cancelled => write!(f, "CANCELLED"), + Self::New => write!(f, "NEW"), + Self::Expired => write!(f, "EXPIRED"), + Self::Pending => write!(f, "PENDING"), + Self::Working => write!(f, "WORKING"), + Self::Unknown => write!(f, "UNKNOWN"), + Self::Suspended => write!(f, "SUSPENDED"), + Self::PendingCancel => write!(f, "PENDING_CANCEL"), + Self::PendingReplace => write!(f, "PENDING_REPLACE"), + } + } +} + impl Default for OrderStatus { fn default() -> Self { Self::Created } } +/// Order side - whether the order is a buy or sell - CANONICAL DEFINITION +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum OrderSide { + Buy, + Sell, +} + +impl fmt::Display for OrderSide { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Buy => write!(f, "BUY"), + Self::Sell => write!(f, "SELL"), + } + } +} + +impl Default for OrderSide { + fn default() -> Self { + Self::Buy + } +} + +/// Alias for backward compatibility +pub use OrderSide as Side; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum TimeInForce { Day, @@ -3546,7 +3572,58 @@ impl ExecutionReport { } } -// QuoteEvent implementation moved to trading::data_interface +/// Quote event structure - CANONICAL DEFINITION +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct QuoteEvent { + /// Symbol + pub symbol: String, + /// Bid price + pub bid: Option, + /// Ask price + pub ask: Option, + /// Bid size + pub bid_size: Option, + /// Ask size + pub ask_size: Option, + /// Exchange + pub exchange: Option, + /// Timestamp + pub timestamp: DateTime, +} + +impl QuoteEvent { + /// Create a new quote event + #[must_use] + pub fn new(symbol: String, timestamp: DateTime) -> Self { + Self { + symbol, + bid: None, + ask: None, + bid_size: None, + ask_size: None, + exchange: None, + timestamp, + } + } + + /// Get the spread (ask - bid) + #[must_use] + pub fn spread(&self) -> Option { + match (self.bid, self.ask) { + (Some(bid), Some(ask)) => Some(ask - bid), + _ => None, + } + } + + /// Get the mid price + #[must_use] + pub fn mid_price(&self) -> Option { + match (self.bid, self.ask) { + (Some(bid), Some(ask)) => Some((bid + ask) / Decimal::from(2)), + _ => None, + } + } +} // TradeEvent removed - no longer available without data crate dependency diff --git a/trading_engine/src/types/mod.rs b/trading_engine/src/types/mod.rs index fa7eeb06c..d14002d19 100644 --- a/trading_engine/src/types/mod.rs +++ b/trading_engine/src/types/mod.rs @@ -243,7 +243,7 @@ pub use basic::MarketRegime::{self, Bear, Bull, Crisis, Custom, Normal, Sideways // Re-export new data compatibility types explicitly pub use basic::{ConnectionEvent, ConnectionStatus, ErrorEvent, TradeEvent}; -// QuoteEvent removed - use data::types::QuoteEvent instead +// QuoteEvent now available from basic::QuoteEvent (canonical definition) // Re-export event types pub use events::SystemStatus; diff --git a/trading_engine/src/types/prelude.rs b/trading_engine/src/types/prelude.rs index 59f1b8e42..b32238722 100644 --- a/trading_engine/src/types/prelude.rs +++ b/trading_engine/src/types/prelude.rs @@ -309,7 +309,8 @@ pub use crate::types::basic::{ // Infrastructure monitoring and management MonitoringConfig, NodeId, - Side as OrderSide, + OrderSide, + Side, OrderType, // Performance and scaling PerformanceProfile, @@ -323,7 +324,6 @@ pub use crate::types::basic::{ ScalingConfig, ServiceId, ServiceLevelObjectives, - Side, // Slippage models for backtesting SlippageModel, Symbol,