//! Data types for market data and broker integration use serde::{Deserialize, Serialize}; /// Time range for historical data queries #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct TimeRange { /// Start time pub start: chrono::DateTime, /// End time pub end: chrono::DateTime, } /// Market data type enumeration #[derive(Debug, Clone, Serialize, Deserialize)] pub enum MarketDataType { /// Real-time quotes Quotes, /// Trade data Trades, /// Aggregate/OHLC data Aggregates, /// Level 2 order book Level2, /// Market status Status, } // Use canonical MarketDataEvent from common crate pub use common::types::MarketDataEvent; /// Extended market data event types with provider-specific events #[derive(Debug, Clone, Serialize, Deserialize)] pub enum ExtendedMarketDataEvent { /// Core market data event Core(MarketDataEvent), /// News alerts (Benzinga) NewsAlert(crate::providers::common::NewsEvent), /// Sentiment updates (Benzinga) SentimentUpdate(crate::providers::common::SentimentEvent), /// Analyst ratings (Benzinga) AnalystRating(crate::providers::common::AnalystRatingEvent), /// Unusual options activity (Benzinga) UnusualOptions(crate::providers::common::UnusualOptionsEvent), } // Use canonical event types from common crate pub use common::types::QuoteEvent; use common::types::TradeEvent; use common::types::Aggregate; use common::types::BarEvent; use common::types::Level2Update; use common::types::MarketStatus; use common::types::ConnectionEvent; use common::types::ErrorEvent; use common::types::OrderBookEvent; use common::types::DataType; use common::types::Subscription; use common::types::PriceLevel; use common::types::ConnectionStatus; use common::error::ErrorCategory; /// Quote data structure (legacy compatibility) #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Quote { /// Symbol pub symbol: String, /// Bid price pub bid: Decimal, /// Ask price pub ask: Decimal, /// Bid size pub bid_size: Decimal, /// Ask size pub ask_size: Decimal, /// Exchange pub exchange: Option, /// Timestamp pub timestamp: chrono::DateTime, } /// Trade data structure (legacy compatibility) #[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "database", derive(sqlx::FromRow))] pub struct Trade { /// Symbol pub symbol: String, /// Trade price pub price: Decimal, /// Trade size pub size: Decimal, /// Exchange pub exchange: Option, /// Trade conditions pub conditions: Vec, /// Timestamp pub timestamp: chrono::DateTime, } // Aggregate moved to common::types::Aggregate // Level2Update, PriceLevel, and MarketStatus moved to common::types // Subscription and DataType moved to common::types // ConnectionEvent, ConnectionStatus, and ErrorEvent moved to common::types // OrderEvent is imported from common::prelude as part of the canonical event system // See: common::types::events::OrderEvent // OrderStatus is imported from common::prelude as part of the canonical type system // See: common::types::basic::OrderStatus /// Position information #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Position { /// Symbol pub symbol: String, /// Position size (positive for long, negative for short) pub size: Decimal, /// Average entry price pub avg_price: Decimal, /// Unrealized P&L pub unrealized_pnl: Decimal, /// Realized P&L pub realized_pnl: Decimal, /// Market value pub market_value: Decimal, /// Last update timestamp pub timestamp: chrono::DateTime, } /// Account information #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Account { /// Account ID pub account_id: String, /// Total equity pub total_equity: Decimal, /// Available cash pub available_cash: Decimal, /// Buying power pub buying_power: Decimal, /// Day trading buying power pub day_trading_buying_power: Decimal, /// Maintenance margin pub maintenance_margin: Decimal, /// Initial margin pub initial_margin: Decimal, /// Last update timestamp pub timestamp: chrono::DateTime, } impl ExtendedMarketDataEvent { /// Get the symbol from the extended market data event pub fn symbol(&self) -> &str { match self { ExtendedMarketDataEvent::Core(event) => event.symbol(), ExtendedMarketDataEvent::NewsAlert(n) => { // For news events, return first symbol if available, otherwise empty string n.symbols.first().map(|s| s.as_str()).unwrap_or("") }, ExtendedMarketDataEvent::SentimentUpdate(s) => s.symbol.as_str(), ExtendedMarketDataEvent::AnalystRating(a) => a.symbol.as_str(), ExtendedMarketDataEvent::UnusualOptions(u) => u.symbol.as_str(), } } /// Get the timestamp from the extended market data event pub fn timestamp(&self) -> Option> { match self { ExtendedMarketDataEvent::Core(event) => event.timestamp(), ExtendedMarketDataEvent::NewsAlert(n) => Some(n.timestamp), ExtendedMarketDataEvent::SentimentUpdate(s) => Some(s.timestamp), ExtendedMarketDataEvent::AnalystRating(a) => Some(a.timestamp), ExtendedMarketDataEvent::UnusualOptions(u) => Some(u.timestamp), } } /// Convert ExtendedMarketDataEvent to MarketDataEvent /// /// For provider-specific events (NewsAlert, SentimentUpdate, etc.), /// returns None since they don't have equivalents in the core MarketDataEvent enum. /// For Core events, returns the wrapped MarketDataEvent. pub fn into_core_event(self) -> Option { match self { ExtendedMarketDataEvent::Core(event) => Some(event), _ => None, // Provider-specific events don't have core equivalents } } } /// Helper function to convert a Vec to Vec /// by extracting only the core events and filtering out provider-specific ones pub fn extract_core_events(extended_events: Vec) -> Vec { extended_events .into_iter() .filter_map(|event| event.into_core_event()) .collect() } /// Helper function to get timestamp from MarketDataEvent /// Since we can't implement methods on MarketDataEvent from common crate pub fn get_event_timestamp(event: &MarketDataEvent) -> Option> { match event { MarketDataEvent::Quote(q) => Some(q.timestamp), MarketDataEvent::Trade(t) => Some(t.timestamp), MarketDataEvent::Aggregate(a) => Some(a.end_timestamp), MarketDataEvent::Bar(b) => Some(b.end_timestamp), MarketDataEvent::Level2(l) => Some(l.timestamp), MarketDataEvent::Status(s) => Some(s.timestamp), MarketDataEvent::ConnectionStatus(c) => Some(c.timestamp), MarketDataEvent::Error(e) => Some(e.timestamp), MarketDataEvent::OrderBook(o) => Some(o.timestamp), MarketDataEvent::OrderBookL2Snapshot(s) => Some(s.timestamp), MarketDataEvent::OrderBookL2Update(u) => Some(u.timestamp), } } // Note: Subscription implementation moved to common crate // Use common::types::Subscription methods #[cfg(test)] mod tests { use super::*; #[test] fn test_subscription_creation() { let sub = Subscription::quotes(vec!["AAPL".to_string(), "GOOGL".to_string()]); assert_eq!(sub.symbols.len(), 2); assert_eq!(sub.data_types.len(), 1); assert!(matches!(sub.data_types[0], DataType::Quotes)); } #[test] fn test_market_data_event_symbol() { let quote = MarketDataEvent::Quote(QuoteEvent { symbol: "AAPL".to_string(), bid: Some(Decimal::new(15000, 2)), // 150.00 ask: Some(Decimal::new(15001, 2)), // 150.01 bid_size: Some(Decimal::new(100, 0)), ask_size: Some(Decimal::new(200, 0)), exchange: Some("NASDAQ".to_string()), timestamp: chrono::Utc::now(), }); assert_eq!(quote.symbol(), "AAPL"); } #[test] fn test_order_status_display() { // OrderStatus tests removed - use canonical types from common::prelude } }