//! Common data types used across services //! //! This module provides shared data types that are used throughout //! the Foxhunt HFT trading system. This includes both infrastructure types //! and core trading types migrated from foxhunt-common-types. use chrono::{DateTime, Utc}; use crate::error::ErrorCategory; // Re-export Decimal for public use pub use rust_decimal::Decimal; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; use std::sync::{Arc, RwLock, Mutex}; use std::convert::TryFrom; use std::fmt; use std::iter::Sum; use std::num::ParseIntError; use std::ops::{Add, Sub, Mul, Div, AddAssign, SubAssign, MulAssign, DivAssign}; use std::str::FromStr; use uuid::Uuid; use crate::error::{CommonError, ErrorCategory as CommonErrorCategory}; use rust_decimal::prelude::FromPrimitive; // ============================================================================= // Type Aliases for Complex Types // ============================================================================= /// Common error type for async operations pub type AsyncResult = Result>; /// Thread-safe hash map for shared state pub type SharedHashMap = Arc>>; /// Thread-safe hash map with Mutex for shared state pub type MutexHashMap = Arc>>; /// Thread-safe container for any value pub type SharedValue = Arc>; /// Thread-safe container with Mutex for any value pub type MutexValue = Arc>; // Trading-specific type aliases /// Map of positions by symbol pub type PositionMap = SharedHashMap; /// Map of orders by order ID pub type OrderMap = SharedHashMap; /// Map of accounts by account ID pub type AccountMap = SharedHashMap; /// Map of instruments by instrument ID pub type InstrumentMap = SharedHashMap; /// Map of market data by symbol pub type MarketDataMap = SharedHashMap; /// Cache entry with timestamp pub type CacheEntry = (T, DateTime); /// Cache map with timestamped entries pub type CacheMap = SharedHashMap>; /// Risk factor loadings by instrument pub type RiskFactorMap = SharedHashMap>; /// Performance metrics history pub type PerformanceHistory = SharedHashMap>; /// Model registry for ML models pub type ModelRegistry = SharedHashMap; /// Generic configuration cache pub type ConfigCache = SharedHashMap; // ============================================================================= // Core Data Types // ============================================================================= /// Unique identifier for services #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ServiceId(pub String); impl ServiceId { /// Create a new service ID pub fn new>(id: S) -> Self { Self(id.into()) } /// Get the inner string value pub fn as_str(&self) -> &str { &self.0 } } impl fmt::Display for ServiceId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } impl From<&str> for ServiceId { fn from(s: &str) -> Self { Self(s.to_owned()) } } impl From for ServiceId { fn from(s: String) -> Self { Self(s) } } /// Service status enumeration #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum ServiceStatus { /// Service is starting up Starting, /// Service is running normally Running, /// Service is degraded but functional Degraded, /// Service is stopping Stopping, /// Service is stopped Stopped, /// Service has encountered an error Error, /// Service is in maintenance mode Maintenance, } impl fmt::Display for ServiceStatus { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Starting => write!(f, "STARTING"), Self::Running => write!(f, "RUNNING"), Self::Degraded => write!(f, "DEGRADED"), Self::Stopping => write!(f, "STOPPING"), Self::Stopped => write!(f, "STOPPED"), Self::Error => write!(f, "ERROR"), Self::Maintenance => write!(f, "MAINTENANCE"), } } } impl ServiceStatus { /// Check if the service is healthy pub fn is_healthy(&self) -> bool { matches!(self, Self::Running | Self::Starting) } /// Check if the service is available for requests pub fn is_available(&self) -> bool { matches!(self, Self::Running | Self::Degraded) } } /// Configuration version for tracking changes #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ConfigVersion { /// Version number pub version: u64, /// Timestamp when version was created pub timestamp: DateTime, /// Optional description of changes pub description: Option, } impl ConfigVersion { /// Create a new config version pub fn new(version: u64) -> Self { Self { version, timestamp: Utc::now(), description: None, } } /// Create a new config version with description pub fn with_description>(version: u64, description: S) -> Self { Self { version, timestamp: Utc::now(), description: Some(description.into()), } } } // TECHNICAL DEBT ELIMINATED - Use DateTime directly instead of Timestamp alias /// Timestamp type alias for consistency across the system pub type Timestamp = DateTime; /// Request ID for tracing and correlation #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct RequestId(pub Uuid); impl Default for RequestId { fn default() -> Self { Self::new() } } impl RequestId { /// Generate a new random request ID pub fn new() -> Self { Self(Uuid::new_v4()) } /// Create from UUID pub fn from_uuid(uuid: Uuid) -> Self { Self(uuid) } /// Get the inner UUID pub fn as_uuid(&self) -> Uuid { self.0 } } // Default implementation is now in the derive macro above // ============================================================================= // MARKET DATA EVENT TYPES (Consolidated from data and trading_engine crates) // ============================================================================= /// Market data event types - CANONICAL DEFINITION #[derive(Debug, Clone, Serialize, Deserialize)] pub enum MarketDataEvent { /// Quote update (bid/ask) Quote(QuoteEvent), /// Trade execution Trade(TradeEvent), /// Aggregate trade data Aggregate(Aggregate), /// Bar/candle data Bar(BarEvent), /// Level 2 market data update Level2(Level2Update), /// Market status update Status(MarketStatus), /// Connection status updates ConnectionStatus(ConnectionEvent), /// Error events with details Error(ErrorEvent), /// Order book update OrderBook(OrderBookEvent), /// Level 2 order book snapshot OrderBookL2Snapshot(OrderBookSnapshot), /// Level 2 order book incremental update OrderBookL2Update(OrderBookUpdate), } /// 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, /// Bid exchange pub bid_exchange: Option, /// Ask exchange pub ask_exchange: Option, /// Quote conditions pub conditions: Vec, /// Timestamp pub timestamp: DateTime, /// Sequence number pub sequence: u64, } 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, bid_exchange: None, ask_exchange: None, conditions: Vec::new(), timestamp, sequence: 0, } } /// Set bid price and size pub fn with_bid(mut self, price: Decimal, size: Decimal) -> Self { self.bid = Some(price); self.bid_size = Some(size); self } /// Set ask price and size pub fn with_ask(mut self, price: Decimal, size: Decimal) -> Self { self.ask = Some(price); self.ask_size = Some(size); self } /// Set exchange pub fn with_exchange>(mut self, exchange: S) -> Self { self.exchange = Some(exchange.into()); self } /// Set bid exchange pub fn with_bid_exchange>(mut self, exchange: S) -> Self { self.bid_exchange = Some(exchange.into()); self } /// Set ask exchange pub fn with_ask_exchange>(mut self, exchange: S) -> Self { self.ask_exchange = Some(exchange.into()); self } /// Add quote condition pub fn with_condition>(mut self, condition: S) -> Self { self.conditions.push(condition.into()); self } /// Set sequence number pub fn with_sequence(mut self, sequence: u64) -> Self { self.sequence = sequence; self } /// Get mid price pub fn mid_price(&self) -> Option { match (self.bid, self.ask) { (Some(bid), Some(ask)) => Some((bid + ask) / Decimal::from(2)), _ => None, } } /// Get spread pub fn spread(&self) -> Option { match (self.bid, self.ask) { (Some(bid), Some(ask)) => Some(ask - bid), _ => None, } } } /// Trade event structure - CANONICAL DEFINITION #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TradeEvent { /// Symbol pub symbol: String, /// Trade price pub price: Decimal, /// Trade size pub size: Decimal, /// Trade ID pub trade_id: Option, /// Exchange pub exchange: Option, /// Trade conditions pub conditions: Vec, /// Timestamp pub timestamp: DateTime, /// Sequence number pub sequence: u64, } impl TradeEvent { /// Create a new trade event #[must_use] pub fn new(symbol: String, price: Decimal, size: Decimal, timestamp: DateTime) -> Self { Self { symbol, price, size, trade_id: None, exchange: None, conditions: Vec::new(), timestamp, sequence: 0, } } /// Set trade ID pub fn with_trade_id>(mut self, trade_id: S) -> Self { self.trade_id = Some(trade_id.into()); self } /// Set exchange pub fn with_exchange>(mut self, exchange: S) -> Self { self.exchange = Some(exchange.into()); self } /// Add trade condition pub fn with_condition>(mut self, condition: S) -> Self { self.conditions.push(condition.into()); self } /// Set sequence number pub fn with_sequence(mut self, sequence: u64) -> Self { self.sequence = sequence; self } /// Get notional value pub fn notional_value(&self) -> Decimal { self.price * self.size } } /// Aggregate trade data #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Aggregate { /// Symbol pub symbol: String, /// Open price pub open: Decimal, /// High price pub high: Decimal, /// Low price pub low: Decimal, /// Close price pub close: Decimal, /// Volume pub volume: Decimal, /// Volume weighted average price pub vwap: Option, /// Start timestamp pub start_timestamp: DateTime, /// End timestamp pub end_timestamp: DateTime, } /// Bar/candle event structure #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BarEvent { /// Symbol pub symbol: String, /// Open price pub open: Decimal, /// High price pub high: Decimal, /// Low price pub low: Decimal, /// Close price pub close: Decimal, /// Volume pub volume: Decimal, /// Volume weighted average price pub vwap: Option, /// Start timestamp pub start_timestamp: DateTime, /// End timestamp pub end_timestamp: DateTime, /// Timeframe (e.g., "1m", "5m", "1h") pub timeframe: String, } /// Level 2 market data update #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Level2Update { /// Symbol pub symbol: String, /// Bid levels pub bids: Vec, /// Ask levels pub asks: Vec, /// Timestamp pub timestamp: DateTime, } /// Price level for order book #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PriceLevel { /// Price pub price: Decimal, /// Size at this price level pub size: Decimal, } /// Order book snapshot from providers #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OrderBookSnapshot { /// Symbol pub symbol: String, /// Bid levels (price, size) sorted by price descending pub bids: Vec, /// Ask levels (price, size) sorted by price ascending pub asks: Vec, /// Exchange pub exchange: String, /// Timestamp of snapshot pub timestamp: DateTime, /// Sequence number pub sequence: u64, } /// Incremental order book update from providers #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OrderBookUpdate { /// Symbol pub symbol: String, /// Changes to bid levels pub bid_changes: Vec, /// Changes to ask levels pub ask_changes: Vec, /// Exchange pub exchange: String, /// Timestamp of update pub timestamp: DateTime, /// Sequence number pub sequence: u64, } /// Change to a price level #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PriceLevelChange { /// Price level being modified pub price: Decimal, /// New size (0 = remove level) pub size: Decimal, /// Type of change pub change_type: PriceLevelChangeType, /// Side (bid or ask) pub side: OrderBookSide, } /// Type of price level change #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub enum PriceLevelChangeType { /// Add new price level Add, /// Update existing price level Update, /// Remove price level Delete, } /// Order book side #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub enum OrderBookSide { /// Bid side Bid, /// Ask side Ask, } /// Market status information #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MarketStatus { /// Market pub market: String, /// Status (open, closed, early_hours, etc.) pub status: String, /// Timestamp pub timestamp: DateTime, } /// Connection event for status updates #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ConnectionEvent { /// Provider name pub provider: String, /// Connection status pub status: ConnectionStatus, /// Optional message pub message: Option, /// Timestamp pub timestamp: DateTime, } /// Connection status enumeration /// Connection status for data providers and brokers #[derive(Debug, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "database", derive(sqlx::Type))] #[cfg_attr(feature = "database", sqlx(type_name = "connection_status", rename_all = "snake_case"))] pub enum ConnectionStatus { /// Successfully connected and operational Connected, /// Disconnected from the service Disconnected, /// Currently attempting to reconnect Reconnecting, } /// Error event structure #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ErrorEvent { /// Provider name pub provider: String, /// Error message pub message: String, /// Error category pub category: ErrorCategory, /// Timestamp pub timestamp: DateTime, } // ErrorCategory is imported from crate::error as CommonErrorCategory /// Order book event #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OrderBookEvent { /// Symbol pub symbol: String, /// Timestamp pub timestamp: DateTime, /// Bid levels pub bids: Vec<(Price, Quantity)>, /// Ask levels pub asks: Vec<(Price, Quantity)>, } /// Data types for subscription #[derive(Debug, Clone, Serialize, Deserialize)] pub enum DataType { /// Real-time quotes Quotes, /// Real-time trades Trades, /// Aggregate/minute bars Aggregates, /// Level 2 order book Level2, /// Market status Status, /// Historical bars/aggregates Bars, /// Order book data OrderBook, /// Volume data Volume, } /// Market data subscription request #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Subscription { /// Symbols to subscribe to pub symbols: Vec, /// Data types to subscribe to pub data_types: Vec, /// Exchange filter (optional) pub exchanges: Vec, } impl MarketDataEvent { /// Get the symbol for any market data event pub fn symbol(&self) -> &str { match self { MarketDataEvent::Quote(q) => &q.symbol, MarketDataEvent::Trade(t) => &t.symbol, MarketDataEvent::Aggregate(a) => &a.symbol, MarketDataEvent::Bar(b) => &b.symbol, MarketDataEvent::Level2(l) => &l.symbol, MarketDataEvent::Status(s) => &s.market, MarketDataEvent::ConnectionStatus(_) => "", MarketDataEvent::Error(_) => "", MarketDataEvent::OrderBook(o) => &o.symbol, MarketDataEvent::OrderBookL2Snapshot(s) => &s.symbol, MarketDataEvent::OrderBookL2Update(u) => &u.symbol, } } /// Get the timestamp for any market data event pub fn timestamp(&self) -> Option> { match self { 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), } } } impl fmt::Display for RequestId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } /// Connection information for services #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ConnectionInfo { /// Host address pub host: String, /// Port number pub port: u16, /// Whether TLS is enabled pub tls: bool, /// Connection timeout in milliseconds pub timeout_ms: u64, } impl ConnectionInfo { /// Create new connection info pub fn new>(host: S, port: u16) -> Self { Self { host: host.into(), port, tls: false, timeout_ms: 5000, } } /// Enable TLS pub fn with_tls(mut self) -> Self { self.tls = true; self } /// Set timeout pub fn with_timeout(mut self, timeout_ms: u64) -> Self { self.timeout_ms = timeout_ms; self } /// Get connection URL pub fn url(&self) -> String { let scheme = if self.tls { "https" } else { "http" }; format!("{}://{}:{}", scheme, self.host, self.port) } } /// Resource limits for services #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ResourceLimits { /// Maximum memory usage in bytes pub max_memory_bytes: Option, /// Maximum CPU usage as percentage (0-100) pub max_cpu_percent: Option, /// Maximum number of open file descriptors pub max_file_descriptors: Option, /// Maximum number of network connections pub max_connections: Option, } // ============================================================================= // TRADING TYPES (Migrated from foxhunt-common-types) // ============================================================================= /// Common error types for trading operations /// /// This error type implements Send + Sync for use in async contexts #[derive(thiserror::Error, Debug)] pub enum CommonTypeError { /// Invalid price value #[error("Invalid price: {value} - {reason}")] InvalidPrice { /// The invalid price value as string value: String, /// Reason why the price is invalid reason: String, }, /// Invalid quantity value #[error("Invalid quantity: {value} - {reason}")] InvalidQuantity { /// The invalid quantity value as string value: String, /// Reason why the quantity is invalid reason: String, }, /// Invalid identifier #[error("Invalid {field}: {reason}")] InvalidIdentifier { /// The field name that contains the invalid identifier field: String, /// Reason why the identifier is invalid reason: String, }, /// Validation error #[error("Validation error for {field}: {reason}")] ValidationError { /// The field name that failed validation field: String, /// Reason why the validation failed reason: String, }, /// Conversion error #[error("Conversion error: {message}")] ConversionError { /// Detailed error message describing the conversion failure message: String, }, /// I/O error #[error("I/O error: {0}")] IoError(#[from] std::io::Error), /// JSON serialization/deserialization error #[error("JSON error: {0}")] JsonError(#[from] serde_json::Error), /// Float parsing error #[error("Float parsing error: {0}")] ParseFloatError(#[from] std::num::ParseFloatError), /// Integer parsing error #[error("Integer parsing error: {0}")] ParseIntError(#[from] std::num::ParseIntError), } // Manual trait implementations for CommonTypeError // (Cannot derive Clone, PartialEq, Eq, Serialize due to std::io::Error and serde_json::Error) impl Clone for CommonTypeError { fn clone(&self) -> Self { match self { Self::InvalidPrice { value, reason } => Self::InvalidPrice { value: value.clone(), reason: reason.clone(), }, Self::InvalidQuantity { value, reason } => Self::InvalidQuantity { value: value.clone(), reason: reason.clone(), }, Self::InvalidIdentifier { field, reason } => Self::InvalidIdentifier { field: field.clone(), reason: reason.clone(), }, Self::ValidationError { field, reason } => Self::ValidationError { field: field.clone(), reason: reason.clone(), }, Self::ConversionError { message } => Self::ConversionError { message: message.clone(), }, // Cannot clone std::io::Error or serde_json::Error, so create new instances Self::IoError(e) => Self::ConversionError { message: format!("I/O error: {}", e), }, Self::JsonError(e) => Self::ConversionError { message: format!("JSON error: {}", e), }, Self::ParseFloatError(e) => Self::ParseFloatError(e.clone()), Self::ParseIntError(e) => Self::ParseIntError(e.clone()), } } } impl PartialEq for CommonTypeError { fn eq(&self, other: &Self) -> bool { match (self, other) { (Self::InvalidPrice { value: v1, reason: r1 }, Self::InvalidPrice { value: v2, reason: r2 }) => v1 == v2 && r1 == r2, (Self::InvalidQuantity { value: v1, reason: r1 }, Self::InvalidQuantity { value: v2, reason: r2 }) => v1 == v2 && r1 == r2, (Self::InvalidIdentifier { field: f1, reason: r1 }, Self::InvalidIdentifier { field: f2, reason: r2 }) => f1 == f2 && r1 == r2, (Self::ValidationError { field: f1, reason: r1 }, Self::ValidationError { field: f2, reason: r2 }) => f1 == f2 && r1 == r2, (Self::ConversionError { message: m1 }, Self::ConversionError { message: m2 }) => m1 == m2, (Self::ParseFloatError(e1), Self::ParseFloatError(e2)) => e1 == e2, (Self::ParseIntError(e1), Self::ParseIntError(e2)) => e1 == e2, // std::io::Error and serde_json::Error don't implement PartialEq, so they're never equal (Self::IoError(_), Self::IoError(_)) => false, (Self::JsonError(_), Self::JsonError(_)) => false, _ => false, } } } impl Eq for CommonTypeError {} // Note: Display is automatically implemented by thiserror::Error derive // based on the #[error("...")] attributes on each variant impl Serialize for CommonTypeError { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { use serde::ser::SerializeStruct; match self { Self::InvalidPrice { value, reason } => { let mut state = serializer.serialize_struct("InvalidPrice", 2)?; state.serialize_field("value", value)?; state.serialize_field("reason", reason)?; state.end() } Self::InvalidQuantity { value, reason } => { let mut state = serializer.serialize_struct("InvalidQuantity", 2)?; state.serialize_field("value", value)?; state.serialize_field("reason", reason)?; state.end() } Self::InvalidIdentifier { field, reason } => { let mut state = serializer.serialize_struct("InvalidIdentifier", 2)?; state.serialize_field("field", field)?; state.serialize_field("reason", reason)?; state.end() } Self::ValidationError { field, reason } => { let mut state = serializer.serialize_struct("ValidationError", 2)?; state.serialize_field("field", field)?; state.serialize_field("reason", reason)?; state.end() } Self::ConversionError { message } => { let mut state = serializer.serialize_struct("ConversionError", 1)?; state.serialize_field("message", message)?; state.end() } Self::IoError(e) => { let mut state = serializer.serialize_struct("IoError", 1)?; state.serialize_field("message", &format!("I/O error: {}", e))?; state.end() } Self::JsonError(e) => { let mut state = serializer.serialize_struct("JsonError", 1)?; state.serialize_field("message", &format!("JSON error: {}", e))?; state.end() } Self::ParseFloatError(e) => { let mut state = serializer.serialize_struct("ParseFloatError", 1)?; state.serialize_field("message", &format!("Float parsing error: {}", e))?; state.end() } Self::ParseIntError(e) => { let mut state = serializer.serialize_struct("ParseIntError", 1)?; state.serialize_field("message", &format!("Integer parsing error: {}", e))?; state.end() } } } } impl<'de> Deserialize<'de> for CommonTypeError { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { // For deserialization, we'll convert everything to ConversionError since // we can't reconstruct std::io::Error or serde_json::Error from serialized form use serde::de::{MapAccess, Visitor}; use std::fmt; struct CommonTypeErrorVisitor; impl<'de> Visitor<'de> for CommonTypeErrorVisitor { type Value = CommonTypeError; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a CommonTypeError") } fn visit_map(self, mut map: V) -> Result where V: MapAccess<'de>, { // For simplicity, deserialize everything as ConversionError let mut message = String::new(); while let Some(key) = map.next_key::()? { let value: serde_json::Value = map.next_value()?; if key == "message" { if let Some(msg) = value.as_str() { message = msg.to_string(); } } else { message = format!("Deserialized error: {}: {}", key, value); } } if message.is_empty() { message = "Unknown deserialized error".to_string(); } Ok(CommonTypeError::ConversionError { message }) } } deserializer.deserialize_struct( "CommonTypeError", &["value", "reason", "field", "message"], CommonTypeErrorVisitor, ) } } // ============================================================================= // ORDER TYPES (Moved from trading_engine) // ============================================================================= /// Order type specifying execution behavior - CANONICAL DEFINITION #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[non_exhaustive] pub enum OrderType { /// Market order - executes immediately at current market price Market, /// Limit order - executes only at specified price or better Limit, /// Stop order - becomes market order when stop price is reached Stop, /// Stop-limit order - becomes limit order when stop price is reached StopLimit, /// Iceberg order - large order split into smaller visible portions Iceberg, /// Trailing stop order - stop price adjusts with favorable price movement 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 } } /// Supported broker types - CANONICAL DEFINITION #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum BrokerType { /// Interactive Brokers TWS/API InteractiveBrokers, /// IC Markets FIX API ICMarkets, /// Paper trading simulation PaperTrading, /// Demo/Test broker Demo, } impl Default for BrokerType { fn default() -> Self { Self::InteractiveBrokers } } /// Order status throughout its lifecycle - CANONICAL DEFINITION #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[non_exhaustive] pub enum OrderStatus { /// Order has been created but not yet submitted to broker Created, /// Order has been submitted to broker for execution Submitted, /// Order has been partially executed with remaining quantity PartiallyFilled, /// Order has been completely executed Filled, /// Order was rejected by broker or exchange Rejected, /// Order was cancelled by user or system Cancelled, /// New order accepted by broker New, /// Order expired due to time restrictions Expired, /// Order is pending broker acceptance Pending, /// Order is actively working in the market Working, /// Order status is unknown or not yet determined Unknown, /// Order is temporarily suspended Suspended, /// Order cancellation is pending PendingCancel, /// Order modification is pending 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 order - purchasing securities Buy, /// Sell order - selling securities 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 } } // REMOVED: Side alias - use OrderSide directly /// Currency enumeration - CANONICAL DEFINITION #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] #[cfg_attr(feature = "database", derive(sqlx::Type))] pub enum Currency { /// US Dollar USD, /// Euro EUR, /// British Pound Sterling GBP, /// Japanese Yen JPY, /// Swiss Franc CHF, /// Canadian Dollar CAD, /// Australian Dollar AUD, /// New Zealand Dollar NZD, /// Bitcoin BTC, /// Ethereum ETH, } 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"), Self::ETH => write!(f, "ETH"), } } } impl Default for Currency { fn default() -> Self { Self::USD } } /// Time in force enumeration - CANONICAL DEFINITION #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum TimeInForce { /// Order is valid for the current trading day only Day, /// Order remains active until explicitly cancelled GoodTillCancel, /// Order must be executed immediately or cancelled ImmediateOrCancel, /// Order must be executed completely or cancelled FillOrKill, } impl fmt::Display for TimeInForce { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Day => write!(f, "DAY"), Self::GoodTillCancel => write!(f, "GTC"), Self::ImmediateOrCancel => write!(f, "IOC"), Self::FillOrKill => write!(f, "FOK"), } } } impl Default for TimeInForce { fn default() -> Self { Self::Day } } // ============================================================================= // CORE ID TYPES (MIGRATED FROM TRADING_ENGINE) // ============================================================================= // Duplicate TradeId removed - using definition from line 1008 /// Event identifier for tracking system events #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct EventId(String); impl EventId { /// Create a new random event ID pub fn new() -> Self { use uuid::Uuid; Self(Uuid::new_v4().to_string()) } /// Create an event ID from a string, generating new if empty pub fn from_string>(id: S) -> Self { let id = id.into(); if id.is_empty() { Self::new() // Generate new ID if empty } else { Self(id) } } /// Get the string value of the event ID pub fn value(&self) -> &str { &self.0 } } impl fmt::Display for EventId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } impl From for EventId { fn from(s: String) -> Self { Self(s) } } impl Default for EventId { fn default() -> Self { Self::new() } } /// Fill identifier with validation #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct FillId(String); impl FillId { /// Create a new fill ID with validation pub fn new>(id: S) -> Result { let id = id.into(); if id.is_empty() { return Err(CommonTypeError::ValidationError { field: "fill_id".to_owned(), reason: "Fill ID cannot be empty".to_owned(), }); } Ok(Self(id)) } /// Get the fill ID as a string slice pub fn as_str(&self) -> &str { &self.0 } /// Convert the fill ID into an owned string pub fn into_string(self) -> String { self.0 } } impl fmt::Display for FillId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } /// Aggregate identifier with validation #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct AggregateId(String); impl AggregateId { /// Create a new aggregate ID with validation pub fn new>(id: S) -> Result { let id = id.into(); if id.is_empty() { return Err(CommonTypeError::ValidationError { field: "aggregate_id".to_owned(), reason: "Aggregate ID cannot be empty".to_owned(), }); } Ok(Self(id)) } /// Get the aggregate ID as a string slice pub fn as_str(&self) -> &str { &self.0 } /// Convert the aggregate ID into an owned string pub fn into_string(self) -> String { self.0 } } impl fmt::Display for AggregateId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } /// Asset identifier with validation #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct AssetId(String); impl AssetId { /// Create a new asset ID with validation pub fn new>(id: S) -> Result { let id = id.into(); if id.is_empty() { return Err(CommonTypeError::ValidationError { field: "asset_id".to_owned(), reason: "Asset ID cannot be empty".to_owned(), }); } Ok(Self(id)) } /// Get the asset ID as a string slice pub fn as_str(&self) -> &str { &self.0 } /// Convert the asset ID into an owned string pub fn into_string(self) -> String { self.0 } } impl fmt::Display for AssetId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } /// Client identifier with validation #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ClientId(String); impl ClientId { /// Create a new client ID with validation pub fn new>(id: S) -> Result { let id = id.into(); if id.is_empty() { return Err(CommonTypeError::ValidationError { field: "client_id".to_owned(), reason: "Client ID cannot be empty".to_owned(), }); } Ok(Self(id)) } /// Get the client ID as a string slice pub fn as_str(&self) -> &str { &self.0 } /// Convert the client ID into an owned string pub fn into_string(self) -> String { self.0 } } impl fmt::Display for ClientId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } // ============================================================================= // CORE TRADING TYPES - MIGRATED FROM TRADING_ENGINE // ============================================================================= /// Canonical Order struct - UNIFIED DEFINITION based on Agent 1's comprehensive analysis /// This represents the single source of truth for Order across all services #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[cfg_attr(feature = "database", derive(sqlx::FromRow))] pub struct Order { // Core Identity /// Unique order identifier pub id: OrderId, /// Client-provided order identifier pub client_order_id: Option, /// Broker-assigned order identifier pub broker_order_id: Option, /// Account identifier for the order pub account_id: Option, // Trading Details /// Trading symbol for the order pub symbol: Symbol, /// Order side (buy or sell) pub side: OrderSide, /// Type of order (market, limit, etc.) pub order_type: OrderType, /// Current status of the order pub status: OrderStatus, /// Time in force policy pub time_in_force: TimeInForce, // Quantities & Pricing /// Total order quantity pub quantity: Quantity, /// Limit price for the order pub price: Option, /// Stop price for stop orders pub stop_price: Option, /// Quantity that has been filled pub filled_quantity: Quantity, /// Remaining quantity to be filled pub remaining_quantity: Quantity, /// Average execution price pub average_price: Option, /// Alias for average_price for database compatibility pub avg_fill_price: Option, // Strategy Fields (from Agent 1) /// Parent order ID for iceberg/algo orders pub parent_id: Option, /// Execution algorithm name pub execution_algorithm: Option, /// Execution algorithm parameters stored as JSON pub execution_params: Value, // Risk Management (from Agent 1) /// Stop loss price for risk management pub stop_loss: Option, /// Take profit price for profit taking pub take_profit: Option, // Timestamps /// Order creation timestamp pub created_at: HftTimestamp, /// Last update timestamp pub updated_at: Option, /// Order expiration timestamp pub expires_at: Option, // Extensibility /// Additional order metadata stored as JSON pub metadata: Value, } impl Order { /// Create a new order with canonical fields pub fn new( symbol: Symbol, side: OrderSide, quantity: Quantity, price: Option, order_type: OrderType, ) -> Self { let now = HftTimestamp::now_or_zero(); Self { // Core Identity id: OrderId::new(), client_order_id: None, broker_order_id: None, account_id: None, // Trading Details symbol, side, order_type, status: OrderStatus::Created, time_in_force: TimeInForce::default(), // Quantities & Pricing quantity, price, stop_price: None, filled_quantity: Quantity::ZERO, remaining_quantity: quantity, average_price: None, avg_fill_price: None, // Database compatibility alias // Strategy Fields parent_id: None, execution_algorithm: None, execution_params: serde_json::json!({}), // Risk Management stop_loss: None, take_profit: None, // Timestamps created_at: now, updated_at: None, expires_at: None, // Extensibility metadata: serde_json::json!({}), } } /// Check if the order is fully filled pub fn is_filled(&self) -> bool { self.filled_quantity == self.quantity } /// Check if the order is partially filled pub fn is_partially_filled(&self) -> bool { self.filled_quantity > Quantity::ZERO && self.filled_quantity < self.quantity } /// Calculate fill percentage pub fn fill_percentage(&self) -> f64 { if self.quantity.is_zero() { 0.0 } else { (self.filled_quantity.to_f64() / self.quantity.to_f64()) * 100.0 } } /// Set client order ID for tracking pub fn with_client_order_id(mut self, client_order_id: String) -> Self { self.client_order_id = Some(client_order_id); self } /// Set account ID pub fn with_account_id(mut self, account_id: String) -> Self { self.account_id = Some(account_id); self } /// Set time in force pub fn with_time_in_force(mut self, time_in_force: TimeInForce) -> Self { self.time_in_force = time_in_force; self } /// Set stop price pub fn with_stop_price(mut self, stop_price: Price) -> Self { self.stop_price = Some(stop_price); self } /// Set execution algorithm pub fn with_execution_algorithm(mut self, algorithm: String) -> Self { self.execution_algorithm = Some(algorithm); self } /// Add execution parameter pub fn with_execution_param(mut self, key: String, value: f64) -> Self { if let Some(obj) = self.execution_params.as_object_mut() { obj.insert(key, serde_json::to_value(value).unwrap_or(Value::Null)); } else { let mut map = serde_json::Map::new(); map.insert(key, serde_json::to_value(value).unwrap_or(Value::Null)); self.execution_params = Value::Object(map); } self } /// Set stop loss pub fn with_stop_loss(mut self, stop_loss: Price) -> Self { self.stop_loss = Some(stop_loss); self } /// Set take profit pub fn with_take_profit(mut self, take_profit: Price) -> Self { self.take_profit = Some(take_profit); self } /// Add metadata pub fn with_metadata(mut self, key: String, value: String) -> Self { if let Some(obj) = self.metadata.as_object_mut() { obj.insert(key, Value::String(value)); } else { let mut map = serde_json::Map::new(); map.insert(key, Value::String(value)); self.metadata = Value::Object(map); } self } /// Update order status and timestamp pub fn update_status(&mut self, status: OrderStatus) { self.status = status; self.updated_at = Some(HftTimestamp::now_or_zero()); } /// Fill order with given quantity and price pub fn fill(&mut self, fill_quantity: Quantity, fill_price: Price) -> Result<(), CommonTypeError> { if self.filled_quantity + fill_quantity > self.quantity { return Err(CommonTypeError::ValidationError { field: "fill_quantity".to_string(), reason: "Fill quantity exceeds remaining quantity".to_string(), }); } // Update filled quantity let previous_filled = self.filled_quantity; self.filled_quantity = self.filled_quantity + fill_quantity; self.remaining_quantity = self.quantity - self.filled_quantity; // Update average price if let Some(avg_price) = self.average_price { let total_value = avg_price.to_f64() * previous_filled.to_f64() + fill_price.to_f64() * fill_quantity.to_f64(); let new_avg = Some(Price::from_f64(total_value / self.filled_quantity.to_f64()).unwrap_or(fill_price)); self.average_price = new_avg; self.avg_fill_price = new_avg; // Keep in sync } else { self.average_price = Some(fill_price); self.avg_fill_price = Some(fill_price); // Keep in sync } // Update status if self.is_filled() { self.update_status(OrderStatus::Filled); } else { self.update_status(OrderStatus::PartiallyFilled); } Ok(()) } /// Create a limit order - convenience constructor pub fn limit(symbol: Symbol, side: OrderSide, quantity: Quantity, price: Price) -> Self { Self::new(symbol, side, quantity, Some(price), OrderType::Limit) } /// Create a market order - convenience constructor pub fn market(symbol: Symbol, side: OrderSide, quantity: Quantity) -> Self { Self::new(symbol, side, quantity, None, OrderType::Market) } /// Get symbol hash for performance-critical operations pub fn symbol_hash(&self) -> i64 { use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; let mut hasher = DefaultHasher::new(); self.symbol.as_str().hash(&mut hasher); hasher.finish() as i64 } /// Get order timestamp pub fn timestamp(&self) -> HftTimestamp { self.created_at } } impl Default for Order { fn default() -> Self { Self { id: OrderId::new(), client_order_id: None, broker_order_id: None, account_id: None, symbol: Symbol::from("DEFAULT"), side: OrderSide::Buy, order_type: OrderType::Market, status: OrderStatus::Created, time_in_force: TimeInForce::Day, quantity: Quantity::ONE, price: None, stop_price: None, filled_quantity: Quantity::ZERO, remaining_quantity: Quantity::ONE, average_price: None, avg_fill_price: None, parent_id: None, execution_algorithm: None, execution_params: serde_json::json!({}), stop_loss: None, take_profit: None, created_at: HftTimestamp::now().unwrap_or(HftTimestamp { nanos: 0 }), updated_at: None, expires_at: None, metadata: serde_json::json!({}), } } } /// Represents a trading position - CANONICAL DEFINITION #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "database", derive(sqlx::FromRow))] pub struct Position { /// Unique position identifier pub id: Uuid, /// Trading symbol pub symbol: String, /// Position quantity (positive for long, negative for short) pub quantity: Decimal, /// Average entry price pub avg_price: Decimal, /// Average cost (alias for avg_price for compatibility) pub avg_cost: Decimal, /// Cost basis for tax calculations pub basis: Decimal, /// Average entry price (alias for avg_price for compatibility) pub average_price: Decimal, /// Market value of position pub market_value: Decimal, /// Unrealized P&L pub unrealized_pnl: Decimal, /// Realized P&L pub realized_pnl: Decimal, /// Position creation timestamp pub created_at: DateTime, /// Last update timestamp pub updated_at: DateTime, /// Last updated timestamp (alias for updated_at for compatibility) pub last_updated: DateTime, /// Current market price (for P&L calculation) pub current_price: Option, /// Position size in base currency pub notional_value: Decimal, /// Margin requirement pub margin_requirement: Decimal, } impl Position { /// Create a new position pub fn new(symbol: String, quantity: Decimal, avg_price: Decimal) -> Self { let now = Utc::now(); let notional_value = quantity.abs() * avg_price; Self { id: Uuid::new_v4(), symbol, quantity, avg_price, avg_cost: avg_price, // Keep avg_cost synchronized with avg_price basis: quantity * avg_price, // Cost basis calculation average_price: avg_price, // Same as avg_price for compatibility market_value: notional_value, // Initialize market value to notional value unrealized_pnl: Decimal::ZERO, realized_pnl: Decimal::ZERO, created_at: now, updated_at: now, last_updated: now, // Same as updated_at for compatibility current_price: None, notional_value, margin_requirement: notional_value * Decimal::from_str_exact("0.02").unwrap_or(Decimal::ZERO), // 2% margin } } /// Check if position is long pub fn is_long(&self) -> bool { self.quantity > Decimal::ZERO } /// Check if position is short pub fn is_short(&self) -> bool { self.quantity < Decimal::ZERO } /// Calculate unrealized P&L based on current price pub fn calculate_unrealized_pnl(&mut self, current_price: Decimal) { self.current_price = Some(current_price); self.market_value = self.quantity.abs() * current_price; self.unrealized_pnl = if self.is_long() { self.quantity * (current_price - self.avg_price) } else { self.quantity * (self.avg_price - current_price) }; let now = Utc::now(); self.updated_at = now; self.last_updated = now; // Keep alias synchronized } /// Get total P&L (realized + unrealized) pub fn total_pnl(&self) -> Decimal { self.realized_pnl + self.unrealized_pnl } /// Calculate return on investment percentage pub fn roi_percentage(&self) -> Decimal { if self.notional_value.is_zero() { Decimal::ZERO } else { self.total_pnl() / self.notional_value * Decimal::from(100) } } } /// Represents a trade execution - CANONICAL DEFINITION #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[cfg_attr(feature = "database", derive(sqlx::FromRow))] pub struct Execution { /// Unique execution identifier pub id: Uuid, /// Related order ID pub order_id: Uuid, /// Trading symbol pub symbol: String, /// Executed quantity pub quantity: Decimal, /// Execution price pub price: Decimal, /// Execution side pub side: OrderSide, /// Trading fees pub fees: Decimal, /// Fee currency pub fee_currency: String, /// Execution timestamp pub executed_at: DateTime, /// Execution timestamp (alias for executed_at for compatibility) pub timestamp: DateTime, /// Symbol hash for performance pub symbol_hash: i64, /// Broker execution ID pub broker_execution_id: Option, /// Counterparty information pub counterparty: Option, /// Trade venue pub venue: Option, /// Gross trade value pub gross_value: Decimal, /// Net trade value (after fees) pub net_value: Decimal, } impl Execution { /// Create a new execution pub fn new( order_id: Uuid, symbol: String, quantity: Decimal, price: Decimal, side: OrderSide, fees: Decimal, ) -> Self { let gross_value = quantity * price; let net_value = if side == OrderSide::Buy { gross_value + fees } else { gross_value - fees }; let now = Utc::now(); let symbol_hash = Self::hash_symbol(&symbol); Self { id: Uuid::new_v4(), order_id, symbol, quantity, price, side, fees, fee_currency: "USD".to_string(), // Default to USD executed_at: now, timestamp: now, // Same as executed_at for compatibility symbol_hash, broker_execution_id: None, counterparty: None, venue: None, gross_value, net_value, } } /// Calculate effective price including fees pub fn effective_price(&self) -> Decimal { if self.quantity.is_zero() { self.price } else { self.net_value / self.quantity } } /// Hash symbol for performance fn hash_symbol(symbol: &str) -> i64 { use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; let mut hasher = DefaultHasher::new(); symbol.hash(&mut hasher); hasher.finish() as i64 } } /// Core Price type using fixed-point arithmetic for precision #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct Price { value: u64, } impl Price { /// Zero price constant pub const ZERO: Self = Self { value: 0 }; /// One unit price constant (1.0) pub const ONE: Self = Self { value: 100_000_000 }; /// One cent constant (0.01) pub const CENT: Self = Self { value: 1_000_000 }; /// Maximum price value pub const MAX: Self = Self { value: u64::MAX }; /// Create a Price from a floating-point value pub fn from_f64(value: f64) -> Result { if value < 0.0 || !value.is_finite() { return Err(CommonTypeError::InvalidPrice { value: value.to_string(), reason: "Price validation failed".to_owned(), }); } Ok(Self { value: (value * 100_000_000.0).round() as u64, }) } /// Convert to floating-point representation #[must_use] pub fn to_f64(&self) -> f64 { self.value as f64 / 100_000_000.0 } /// Get floating-point representation (alias for to_f64) #[must_use] pub fn as_f64(&self) -> f64 { self.to_f64() } /// Create a zero price #[must_use] pub const fn zero() -> Self { Self::ZERO } /// Convert to Decimal type for precise calculations pub fn to_decimal(&self) -> Result { Decimal::from_f64(self.to_f64()).ok_or_else(|| CommonTypeError::InvalidPrice { value: "0.0".to_owned(), reason: "Price to Decimal conversion failed".to_owned(), }) } /// Create a Price from a Decimal value #[must_use] pub fn from_decimal(decimal: Decimal) -> Self { Self::from(decimal) } /// Create a new Price (alias for from_f64) pub fn new(value: f64) -> Result { Self::from_f64(value) } #[must_use] pub const fn raw_value(&self) -> u64 { self.value } #[must_use] pub const fn as_u64(&self) -> u64 { self.value } #[must_use] pub const fn from_raw(value: u64) -> Self { Self { value } } #[must_use] pub const fn to_cents(&self) -> u64 { self.value / 1_000_000 } #[must_use] pub const fn from_cents(cents: u64) -> Self { Self { value: cents * 1_000_000, } } #[must_use] pub const fn is_zero(&self) -> bool { self.value == 0 } #[must_use] pub const fn is_some(&self) -> bool { !self.is_zero() } #[must_use] pub const fn is_none(&self) -> bool { self.is_zero() } #[must_use] pub const fn as_ref(&self) -> &Self { self } #[must_use] pub const fn abs(&self) -> Self { *self } pub fn multiply(&self, other: Self) -> Result { *self * other } #[must_use] pub fn subtract(&self, other: Self) -> Self { *self - other } pub fn divide(&self, divisor: f64) -> Result { *self / divisor } } impl fmt::Display for Price { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:.8}", self.to_f64()) } } impl Default for Price { fn default() -> Self { Self::ZERO } } impl FromStr for Price { type Err = CommonTypeError; fn from_str(s: &str) -> Result { let parsed_value = s.parse::().map_err(|_| CommonTypeError::InvalidPrice { value: s.to_owned(), reason: format!("Cannot parse '{}' as price", s), })?; Self::from_f64(parsed_value) } } impl Add for Price { type Output = Self; fn add(self, rhs: Self) -> Self::Output { Self { value: self.value.saturating_add(rhs.value), } } } impl Sub for Price { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { Self { value: self.value.saturating_sub(rhs.value), } } } impl Mul for Price { type Output = Result; fn mul(self, rhs: f64) -> Self::Output { Self::from_f64(self.to_f64() * rhs) } } impl Div for Price { type Output = Result; fn div(self, rhs: f64) -> Self::Output { if rhs == 0.0 { return Err(CommonTypeError::ConversionError { message: "Cannot divide price by zero".to_owned(), }); } Self::from_f64(self.to_f64() / rhs) } } impl From for Price { fn from(decimal: Decimal) -> Self { let f64_val: f64 = TryInto::::try_into(decimal).unwrap_or_else(|_| { eprintln!("Failed to convert Decimal to f64, using 0.0 as fallback"); 0.0_f64 }); Self::from_f64(f64_val).unwrap_or_else(|_| { eprintln!("Failed to create Price from f64 value {}, using ZERO", f64_val); Self::ZERO }) } } impl From for Decimal { fn from(price: Price) -> Self { price.to_decimal().unwrap_or(Decimal::ZERO) } } // TryFrom for Decimal removed due to conflicting blanket implementation // Use qty.to_decimal() directly instead impl From for Decimal { fn from(qty: Quantity) -> Self { qty.to_decimal().unwrap_or(Decimal::ZERO) } } // TryFrom for Decimal removed due to conflict with From implementation // Use the From implementation instead which handles errors by returning ZERO impl Mul for Price { type Output = Result; fn mul(self, rhs: Self) -> Self::Output { Self::from_f64(self.to_f64() * rhs.to_f64()) } } impl TryFrom for Price { type Error = CommonTypeError; fn try_from(s: String) -> Result { Self::from_str(&s) } } impl TryFrom<&str> for Price { type Error = CommonTypeError; fn try_from(s: &str) -> Result { Self::from_str(s) } } impl PartialEq for Price { fn eq(&self, other: &f64) -> bool { (self.to_f64() - other).abs() < f64::EPSILON } } impl PartialEq for f64 { fn eq(&self, other: &Price) -> bool { (self - other.to_f64()).abs() < f64::EPSILON } } impl AddAssign for Price { fn add_assign(&mut self, rhs: Self) { self.value = self.value.saturating_add(rhs.value); } } impl SubAssign for Price { fn sub_assign(&mut self, rhs: Self) { self.value = self.value.saturating_sub(rhs.value); } } impl MulAssign for Price { fn mul_assign(&mut self, rhs: f64) { if let Ok(result) = self.mul(rhs) { *self = result; } // If multiplication fails, self remains unchanged } } impl DivAssign for Price { fn div_assign(&mut self, rhs: f64) { if let Ok(result) = self.div(rhs) { *self = result; } // If division fails, self remains unchanged } } impl PartialOrd for Price { fn partial_cmp(&self, other: &f64) -> Option { self.to_f64().partial_cmp(other) } } impl PartialOrd for f64 { fn partial_cmp(&self, other: &Price) -> Option { self.partial_cmp(&other.to_f64()) } } /// Core Quantity type using fixed-point arithmetic #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct Quantity { value: u64, } impl Quantity { pub const ZERO: Self = Self { value: 0 }; pub const ONE: Self = Self { value: 100_000_000 }; pub const MAX: Self = Self { value: u64::MAX }; pub fn from_f64(value: f64) -> Result { if value < 0.0 || !value.is_finite() { return Err(CommonTypeError::InvalidQuantity { value: value.to_string(), reason: "Quantity validation failed".to_owned(), }); } Ok(Self { value: (value * 100_000_000.0).round() as u64, }) } #[must_use] pub fn to_f64(&self) -> f64 { self.value as f64 / 100_000_000.0 } pub fn to_decimal(&self) -> Result { Decimal::from_f64(self.to_f64()).ok_or_else(|| CommonTypeError::InvalidQuantity { value: "0.0".to_owned(), reason: "Quantity to Decimal conversion failed".to_owned(), }) } #[must_use] pub const fn value(&self) -> u64 { self.value } #[must_use] pub const fn raw_value(&self) -> u64 { self.value } #[must_use] pub const fn as_u64(&self) -> u64 { self.value } #[must_use] pub const fn from_raw(value: u64) -> Self { Self { value } } pub fn new(value: f64) -> Result { Self::from_f64(value) } #[must_use] pub const fn zero() -> Self { Self::ZERO } pub fn from_i64(value: i64) -> Result { Self::from_f64(value as f64) } pub fn from_decimal(decimal: Decimal) -> Result { use std::convert::TryFrom; Self::try_from(decimal).map_err(|_| CommonTypeError::InvalidQuantity { value: decimal.to_string(), reason: "Failed to convert Decimal to Quantity".to_owned(), }) } #[must_use] pub const fn is_zero(&self) -> bool { self.value == 0 } #[must_use] pub const fn is_some(&self) -> bool { !self.is_zero() } #[must_use] pub const fn is_none(&self) -> bool { self.is_zero() } #[must_use] pub const fn as_ref(&self) -> &Self { self } #[must_use] pub const fn abs(&self) -> Self { *self } #[must_use] pub const fn signum(&self) -> f64 { if self.value > 0 { 1.0 } else { 0.0 } } #[must_use] pub const fn is_positive(&self) -> bool { self.value > 0 } #[must_use] pub const fn is_negative(&self) -> bool { false } #[must_use] pub fn as_f64(&self) -> f64 { self.to_f64() } #[must_use] pub const fn from_shares(shares: u64) -> Self { Self { value: shares * 100_000_000, } } #[must_use] pub const fn to_shares(&self) -> u64 { self.value / 100_000_000 } pub fn multiply(&self, other: Self) -> Result { Self::from_f64(self.to_f64() * other.to_f64()) } #[must_use] pub fn subtract(&self, other: Self) -> Self { *self - other } } impl Default for Quantity { fn default() -> Self { Self::ZERO } } impl FromStr for Quantity { type Err = CommonTypeError; fn from_str(s: &str) -> Result { let parsed_value = s.parse::().map_err(|_| CommonTypeError::InvalidQuantity { value: s.to_owned(), reason: format!("Cannot parse '{}' as quantity", s), })?; Self::from_f64(parsed_value) } } impl fmt::Display for Quantity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:.8}", self.to_f64()) } } impl TryFrom for Quantity { type Error = CommonTypeError; fn try_from(value: i32) -> Result { Self::new(f64::from(value)) } } impl TryFrom for Quantity { type Error = CommonTypeError; fn try_from(value: u64) -> Result { Self::new(value as f64) } } impl TryFrom for Quantity { type Error = CommonTypeError; fn try_from(value: f64) -> Result { Self::new(value) } } impl TryFrom for Quantity { type Error = CommonTypeError; fn try_from(decimal: Decimal) -> Result { let f64_val: f64 = TryInto::::try_into(decimal).map_err(|_| { CommonTypeError::ConversionError { message: "Failed to convert Decimal to f64".to_owned(), } })? ; Self::from_f64(f64_val) } } impl TryFrom for Quantity { type Error = CommonTypeError; fn try_from(s: String) -> Result { Self::from_str(&s) } } impl TryFrom<&str> for Quantity { type Error = CommonTypeError; fn try_from(s: &str) -> Result { Self::from_str(s) } } impl PartialEq for Quantity { fn eq(&self, other: &f64) -> bool { (self.to_f64() - other).abs() < f64::EPSILON } } impl PartialEq for f64 { fn eq(&self, other: &Quantity) -> bool { (self - other.to_f64()).abs() < f64::EPSILON } } impl PartialOrd for Quantity { fn partial_cmp(&self, other: &f64) -> Option { self.to_f64().partial_cmp(other) } } impl PartialOrd for f64 { fn partial_cmp(&self, other: &Quantity) -> Option { self.partial_cmp(&other.to_f64()) } } impl Add for Quantity { type Output = Self; fn add(self, rhs: Self) -> Self::Output { Self { value: self.value.saturating_add(rhs.value), } } } impl Sub for Quantity { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { Self { value: self.value.saturating_sub(rhs.value), } } } impl Mul for Quantity { type Output = Result; fn mul(self, rhs: f64) -> Self::Output { Self::from_f64(self.to_f64() * rhs) } } impl Div for Quantity { type Output = Result; fn div(self, rhs: f64) -> Self::Output { if rhs == 0.0 { return Err(CommonTypeError::ConversionError { message: "Cannot divide quantity by zero".to_owned(), }); } Self::from_f64(self.to_f64() / rhs) } } impl Sum for Quantity { fn sum>(iter: I) -> Self { iter.fold(Self::ZERO, |acc, x| acc + x) } } impl<'quantity> Sum<&'quantity Self> for Quantity { fn sum>(iter: I) -> Self { iter.fold(Self::ZERO, |acc, x| acc + *x) } } // ============================================================================= // SQLX IMPLEMENTATIONS FOR FINANCIAL TYPES // ============================================================================= #[cfg(feature = "database")] mod sqlx_impls { use super::{Price, Quantity, OrderStatus, OrderSide, OrderType, MarketRegime, HftTimestamp}; use rust_decimal::Decimal as RustDecimal; use sqlx::{ decode::Decode, encode::{Encode, IsNull}, error::BoxDynError, postgres::{PgArgumentBuffer, PgTypeInfo, PgValueRef, Postgres}, Type, }; // SQLx implementations for Price impl Type for Price { fn type_info() -> PgTypeInfo { PgTypeInfo::with_name("NUMERIC") } } impl<'q> Encode<'q, Postgres> for Price { fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { // Convert our fixed-point u64 to rust_decimal::Decimal with 8 decimal places let decimal_value = RustDecimal::new(self.raw_value() as i64, 8); decimal_value.encode_by_ref(buf) } } impl<'r> Decode<'r, Postgres> for Price { fn decode(value: PgValueRef<'r>) -> Result { // Decode from NUMERIC to rust_decimal::Decimal let decimal_value = >::decode(value)?; // Validate scale matches our fixed-point precision (8 decimal places) if decimal_value.scale() != 8 { return Err(format!( "Invalid scale for Price: expected 8, got {}", decimal_value.scale() ) .into()); } // Extract mantissa and convert to our u64 representation let mantissa = decimal_value.mantissa(); let inner_val = u64::try_from(mantissa) .map_err(|_| "Failed to convert negative or overflowing NUMERIC to Price")?; Ok(Price::from_raw(inner_val)) } } // SQLx implementations for Quantity impl Type for Quantity { fn type_info() -> PgTypeInfo { PgTypeInfo::with_name("NUMERIC") } } impl<'q> Encode<'q, Postgres> for Quantity { fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { // Convert our fixed-point u64 to rust_decimal::Decimal with 8 decimal places let decimal_value = RustDecimal::new(self.raw_value() as i64, 8); decimal_value.encode_by_ref(buf) } } impl<'r> Decode<'r, Postgres> for Quantity { fn decode(value: PgValueRef<'r>) -> Result { // Decode from NUMERIC to rust_decimal::Decimal let decimal_value = >::decode(value)?; // Validate scale matches our fixed-point precision (8 decimal places) if decimal_value.scale() != 8 { return Err(format!( "Invalid scale for Quantity: expected 8, got {}", decimal_value.scale() ) .into()); } // Extract mantissa and convert to our u64 representation let mantissa = decimal_value.mantissa(); let inner_val = u64::try_from(mantissa) .map_err(|_| "Failed to convert negative or overflowing NUMERIC to Quantity")?; Ok(Quantity::from_raw(inner_val)) } } // SQLx implementations for TimeInForce impl Type for super::TimeInForce { fn type_info() -> PgTypeInfo { PgTypeInfo::with_name("TEXT") } } impl<'q> Encode<'q, Postgres> for super::TimeInForce { fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { // Use the Display trait to convert enum to string representation <&str as Encode>::encode(self.to_string().as_str(), buf) } } impl<'r> Decode<'r, Postgres> for super::TimeInForce { fn decode(value: PgValueRef<'r>) -> Result { // Decode from TEXT to string, then parse to enum let s = <&str as Decode>::decode(value)?; match s { "DAY" => Ok(super::TimeInForce::Day), "GTC" => Ok(super::TimeInForce::GoodTillCancel), "IOC" => Ok(super::TimeInForce::ImmediateOrCancel), "FOK" => Ok(super::TimeInForce::FillOrKill), _ => Err(format!("Invalid TimeInForce value: {}", s).into()), } } } // SQLx implementations for OrderStatus impl Type for OrderStatus { fn type_info() -> PgTypeInfo { PgTypeInfo::with_name("TEXT") } } impl<'q> Encode<'q, Postgres> for OrderStatus { fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { let value = match self { OrderStatus::Created => "CREATED", OrderStatus::Submitted => "SUBMITTED", OrderStatus::PartiallyFilled => "PARTIALLY_FILLED", OrderStatus::Filled => "FILLED", OrderStatus::Rejected => "REJECTED", OrderStatus::Cancelled => "CANCELLED", OrderStatus::New => "NEW", OrderStatus::Expired => "EXPIRED", OrderStatus::Pending => "PENDING", OrderStatus::Working => "WORKING", OrderStatus::Unknown => "UNKNOWN", OrderStatus::Suspended => "SUSPENDED", OrderStatus::PendingCancel => "PENDING_CANCEL", OrderStatus::PendingReplace => "PENDING_REPLACE", }; <&str as Encode>::encode_by_ref(&value, buf) } } impl<'r> Decode<'r, Postgres> for OrderStatus { fn decode(value: PgValueRef<'r>) -> Result { let s = >::decode(value)?; match s.as_str() { "CREATED" => Ok(OrderStatus::Created), "SUBMITTED" => Ok(OrderStatus::Submitted), "PARTIALLY_FILLED" => Ok(OrderStatus::PartiallyFilled), "FILLED" => Ok(OrderStatus::Filled), "REJECTED" => Ok(OrderStatus::Rejected), "CANCELLED" => Ok(OrderStatus::Cancelled), "NEW" => Ok(OrderStatus::New), "EXPIRED" => Ok(OrderStatus::Expired), "PENDING" => Ok(OrderStatus::Pending), "WORKING" => Ok(OrderStatus::Working), "UNKNOWN" => Ok(OrderStatus::Unknown), "SUSPENDED" => Ok(OrderStatus::Suspended), "PENDING_CANCEL" => Ok(OrderStatus::PendingCancel), "PENDING_REPLACE" => Ok(OrderStatus::PendingReplace), _ => Err(format!("Invalid OrderStatus value: {}", s).into()), } } } // SQLx implementations for OrderSide impl Type for OrderSide { fn type_info() -> PgTypeInfo { PgTypeInfo::with_name("TEXT") } } impl<'q> Encode<'q, Postgres> for OrderSide { fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { let value = match self { OrderSide::Buy => "BUY", OrderSide::Sell => "SELL", }; <&str as Encode>::encode_by_ref(&value, buf) } } impl<'r> Decode<'r, Postgres> for OrderSide { fn decode(value: PgValueRef<'r>) -> Result { let s = >::decode(value)?; match s.as_str() { "BUY" => Ok(OrderSide::Buy), "SELL" => Ok(OrderSide::Sell), _ => Err(format!("Invalid OrderSide value: {}", s).into()), } } } // SQLx implementations for OrderType impl Type for OrderType { fn type_info() -> PgTypeInfo { PgTypeInfo::with_name("TEXT") } } impl<'q> Encode<'q, Postgres> for OrderType { fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { let value = match self { OrderType::Market => "MARKET", OrderType::Limit => "LIMIT", OrderType::Stop => "STOP", OrderType::StopLimit => "STOP_LIMIT", OrderType::Iceberg => "ICEBERG", OrderType::TrailingStop => "TRAILING_STOP", OrderType::Hidden => "HIDDEN", }; <&str as Encode>::encode_by_ref(&value, buf) } } impl<'r> Decode<'r, Postgres> for OrderType { fn decode(value: PgValueRef<'r>) -> Result { let s = >::decode(value)?; match s.as_str() { "MARKET" => Ok(OrderType::Market), "LIMIT" => Ok(OrderType::Limit), "STOP" => Ok(OrderType::Stop), "STOP_LIMIT" => Ok(OrderType::StopLimit), "ICEBERG" => Ok(OrderType::Iceberg), "TRAILING_STOP" => Ok(OrderType::TrailingStop), "HIDDEN" => Ok(OrderType::Hidden), _ => Err(format!("Invalid OrderType value: {}", s).into()), } } } // SQLx implementations for MarketRegime impl Type for MarketRegime { fn type_info() -> PgTypeInfo { PgTypeInfo::with_name("TEXT") } } impl<'q> Encode<'q, Postgres> for MarketRegime { fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { let value = match self { MarketRegime::Normal => "NORMAL", MarketRegime::Crisis => "CRISIS", MarketRegime::Trending => "TRENDING", MarketRegime::Sideways => "SIDEWAYS", MarketRegime::Bull => "BULL", MarketRegime::Bear => "BEAR", MarketRegime::HighVolatility => "HIGH_VOLATILITY", MarketRegime::LowVolatility => "LOW_VOLATILITY", MarketRegime::Volatile => "VOLATILE", MarketRegime::Calm => "CALM", MarketRegime::Unknown => "UNKNOWN", MarketRegime::Recovery => "RECOVERY", MarketRegime::Bubble => "BUBBLE", MarketRegime::Correction => "CORRECTION", MarketRegime::Custom(id) => return >::encode_by_ref(&format!("CUSTOM_{}", id), buf), }; <&str as Encode>::encode_by_ref(&value, buf) } } impl<'r> Decode<'r, Postgres> for MarketRegime { fn decode(value: PgValueRef<'r>) -> Result { let s = >::decode(value)?; match s.as_str() { "NORMAL" => Ok(MarketRegime::Normal), "CRISIS" => Ok(MarketRegime::Crisis), "TRENDING" => Ok(MarketRegime::Trending), "SIDEWAYS" => Ok(MarketRegime::Sideways), "BULL" => Ok(MarketRegime::Bull), "BEAR" => Ok(MarketRegime::Bear), "HIGH_VOLATILITY" => Ok(MarketRegime::HighVolatility), "LOW_VOLATILITY" => Ok(MarketRegime::LowVolatility), "VOLATILE" => Ok(MarketRegime::Volatile), "CALM" => Ok(MarketRegime::Calm), "UNKNOWN" => Ok(MarketRegime::Unknown), "RECOVERY" => Ok(MarketRegime::Recovery), "BUBBLE" => Ok(MarketRegime::Bubble), "CORRECTION" => Ok(MarketRegime::Correction), _ => { // Handle Custom(id) format if let Some(id_str) = s.strip_prefix("CUSTOM_") { if let Ok(id) = id_str.parse::() { Ok(MarketRegime::Custom(id)) } else { Err(format!("Invalid MarketRegime Custom ID: {}", id_str).into()) } } else { Err(format!("Invalid MarketRegime value: {}", s).into()) } } } } } // SQLx implementations for HftTimestamp // Maps to PostgreSQL BIGINT (stores nanoseconds since Unix epoch) // Note: Limited to i64::MAX nanoseconds (year 2262) due to PostgreSQL BIGINT constraints impl<'q> Encode<'q, Postgres> for HftTimestamp { fn encode_by_ref( &self, buf: &mut PgArgumentBuffer, ) -> Result { // Cast u64 to i64 for PostgreSQL BIGINT compatibility >::encode(self.nanos() as i64, buf) } } impl<'r> Decode<'r, Postgres> for HftTimestamp { fn decode( value: PgValueRef<'r>, ) -> Result { let val = >::decode(value)?; // Cast i64 back to u64 for internal representation Ok(HftTimestamp::from_nanos(val as u64)) } } impl Type for HftTimestamp { fn type_info() -> ::TypeInfo { >::type_info() } fn compatible(ty: &::TypeInfo) -> bool { >::compatible(ty) } } // SQLx implementations for OrderId (uses BIGINT for u64) impl Type for super::OrderId { fn type_info() -> PgTypeInfo { PgTypeInfo::with_name("BIGINT") } } impl<'q> Encode<'q, Postgres> for super::OrderId { fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result { >::encode_by_ref(&(self.value() as i64), buf) } } impl<'r> Decode<'r, Postgres> for super::OrderId { fn decode(value: PgValueRef<'r>) -> Result { let id = >::decode(value)?; Ok(super::OrderId::from_u64(id as u64)) } } } /// Volume type - alias for Quantity with the same fixed-point arithmetic /// SQLx traits are automatically inherited from Quantity pub type Volume = Quantity; // ORDER TYPES ALREADY DEFINED ABOVE - No need to re-export from trading_engine // ============================================================================= // CORE ID TYPES (MOVED FROM TRADING_ENGINE) // ============================================================================= /// Order identifier with ultra-fast atomic generation /// Replaces slow UUID generation (1ms+) with atomic increment (~5ns) #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct OrderId(u64); impl Default for OrderId { fn default() -> Self { Self::new() } } impl OrderId { /// Generate next `OrderId` using atomic counter - <50ns performance pub fn new() -> Self { use std::sync::atomic::{AtomicU64, Ordering}; static COUNTER: AtomicU64 = AtomicU64::new(1); Self(COUNTER.fetch_add(1, Ordering::Relaxed)) } /// Create `OrderId` from u64 value #[must_use] pub const fn from_u64(value: u64) -> Self { Self(value) } /// Get u64 value #[must_use] pub const fn value(&self) -> u64 { self.0 } /// Get u64 value for performance-critical code (alias for value) #[must_use] pub const fn as_u64(&self) -> u64 { self.0 } /// Get as string for compatibility #[must_use] pub fn as_str(&self) -> String { self.0.to_string() } } impl fmt::Display for OrderId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } impl From for OrderId { fn from(value: u64) -> Self { Self(value) } } impl From for u64 { fn from(order_id: OrderId) -> Self { order_id.0 } } impl FromStr for OrderId { type Err = ParseIntError; fn from_str(s: &str) -> Result { s.parse::().map(OrderId) } } impl From for OrderId { fn from(s: String) -> Self { s.parse().unwrap_or_else(|_| Self::new()) } } impl From<&str> for OrderId { fn from(s: &str) -> Self { s.parse().unwrap_or_else(|_| Self::new()) } } /// Trading symbol with validation #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[cfg_attr(feature = "database", derive(sqlx::Type))]pub struct ExecutionId(String); impl ExecutionId { pub fn new>(id: S) -> Result { let id = id.into(); if id.is_empty() { return Err(CommonTypeError::ValidationError { field: "execution_id".to_owned(), reason: "Execution ID cannot be empty".to_owned(), }); } Ok(Self(id)) } pub fn generate() -> Self { Self(uuid::Uuid::new_v4().to_string()) } pub fn as_str(&self) -> &str { &self.0 } pub fn into_string(self) -> String { self.0 } } impl fmt::Display for ExecutionId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } impl FromStr for ExecutionId { type Err = CommonTypeError; fn from_str(s: &str) -> Result { Self::new(s) } } /// Trade identifier with validation #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct TradeId(String); impl TradeId { pub fn new>(id: S) -> Result { let id = id.into(); if id.is_empty() { return Err(CommonTypeError::ValidationError { field: "trade_id".to_owned(), reason: "Trade ID cannot be empty".to_owned(), }); } Ok(Self(id)) } pub fn as_str(&self) -> &str { &self.0 } 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) } } /// Trading symbol with validation #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[cfg_attr(feature = "database", derive(sqlx::Type))] pub struct Symbol { value: String, } impl Symbol { #[must_use] pub const fn new(s: String) -> Self { Self { value: s } } /// Create a new Symbol with validation pub fn new_validated(s: String) -> Result { if s.trim().is_empty() { return Err(CommonTypeError::ValidationError { field: "symbol".to_string(), reason: "Symbol cannot be empty".to_string(), }); } Ok(Self { value: s }) } /// Create a Symbol from &str with validation pub fn from_str_validated(s: &str) -> Result { Self::new_validated(s.to_owned()) } #[must_use] pub fn as_str(&self) -> &str { &self.value } #[must_use] pub fn value(&self) -> &str { &self.value } #[must_use] pub fn as_bytes(&self) -> &[u8] { self.value.as_bytes() } #[must_use] pub fn is_empty(&self) -> bool { self.value.is_empty() } #[must_use] pub fn to_uppercase(&self) -> String { self.value.to_uppercase() } #[must_use] pub fn replace(&self, from: &str, to: &str) -> String { self.value.replace(from, to) } // Helper for risk management #[must_use] pub fn none() -> Self { "NONE".parse().unwrap() } // Missing methods needed by services #[must_use] pub fn contains(&self, pattern: &str) -> bool { self.value.contains(pattern) } } impl FromStr for Symbol { type Err = std::convert::Infallible; fn from_str(s: &str) -> Result { Ok(Self { value: s.to_owned(), }) } } // Additional implementation to support conversion from &Symbol to &str impl AsRef for Symbol { fn as_ref(&self) -> &str { &self.value } } impl fmt::Display for Symbol { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.value) } } impl From for Symbol { fn from(s: String) -> Self { Self::new(s) } } impl From<&str> for Symbol { fn from(s: &str) -> Self { Self::new(s.to_owned()) } } // TryFrom implementations removed due to conflicting blanket implementations // Use Symbol::new_validated() or Symbol::from_validated() directly instead impl Default for Symbol { fn default() -> Self { Self::new(String::new()) } } impl PartialEq for Symbol { fn eq(&self, other: &str) -> bool { self.value == other } } impl PartialEq for Symbol { fn eq(&self, other: &String) -> bool { &self.value == other } } impl PartialEq for &str { fn eq(&self, other: &Symbol) -> bool { *self == other.value } } impl PartialEq for String { fn eq(&self, other: &Symbol) -> bool { self == &other.value } } // TimeInForce moved to canonical source: common::types::TimeInForce // Currency moved to canonical source: common::types::Currency // Price moved to canonical source: common::types::Price // Quantity moved to canonical source: common::types::Quantity // Volume moved to canonical source: common::types::Quantity (as Volume alias) /// Money amount with currency #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Money { pub amount: Decimal, pub currency: Currency, } impl Money { /// Create new money amount pub const fn new(amount: Decimal, currency: Currency) -> Self { Self { amount, currency } } } impl fmt::Display for Money { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{} {}", self.amount, self.currency) } } // OrderId moved to canonical source: common::types::OrderId // TradeId moved to canonical source: common::types::TradeId // Symbol moved to canonical source: common::types::Symbol /// Type-safe account identifier #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct AccountId(String); impl AccountId { /// Create a new account ID with validation pub fn new>(id: S) -> Result { let id = id.into(); if id.trim().is_empty() { return Err(CommonTypeError::InvalidIdentifier { field: "account_id".to_string(), reason: "Account ID cannot be empty".to_string(), }); } Ok(Self(id)) } /// 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 AccountId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } /// High-precision timestamp for HFT applications - CANONICAL DEFINITION /// Robust implementation with error handling for financial safety #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default)] pub struct HftTimestamp { nanos: u64, } impl HftTimestamp { /// Get current timestamp with error handling for financial safety pub fn now() -> Result { use std::time::{SystemTime, UNIX_EPOCH}; let nanos = SystemTime::now() .duration_since(UNIX_EPOCH) .map_err(|e| CommonError::Service { category: CommonErrorCategory::System, message: format!("System time before UNIX epoch: {e}"), })? .as_nanos() as u64; Ok(Self { nanos }) } /// Get current timestamp with error handling for financial safety (CommonTypeError version) pub fn now_common() -> Result { use std::time::{SystemTime, UNIX_EPOCH}; let nanos = SystemTime::now() .duration_since(UNIX_EPOCH) .map_err(|e| CommonTypeError::ConversionError { message: format!("System time before UNIX epoch: {e}"), })? .as_nanos() as u64; Ok(Self { nanos }) } /// Get current timestamp or zero if system time is invalid #[must_use] pub fn now_or_zero() -> Self { Self::now().unwrap_or(Self { nanos: 0 }) } /// Get nanoseconds since epoch #[must_use] pub const fn nanos(self) -> u64 { self.nanos } /// Create from nanoseconds since epoch #[must_use] pub const fn from_nanos(nanos: u64) -> Self { Self { nanos } } /// Create from signed nanoseconds (cast to unsigned) #[must_use] pub const fn from_nanos_i64(nanos: i64) -> Self { Self { nanos: nanos as u64, } } /// Get nanoseconds since epoch (legacy method for compatibility) pub const fn as_nanos(&self) -> u64 { self.nanos } /// Convert to DateTime pub fn to_datetime(&self) -> DateTime { let secs = self.nanos / 1_000_000_000; let nsecs = (self.nanos % 1_000_000_000) as u32; DateTime::from_timestamp(secs as i64, nsecs).unwrap_or_default() } } impl fmt::Display for HftTimestamp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.to_datetime()) } } /// Generic timestamp for general use cases #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct GenericTimestamp { nanos: u64, } impl GenericTimestamp { /// Create from nanoseconds since epoch #[must_use] pub const fn from_nanos(nanos: u64) -> Self { Self { nanos } } /// Get nanoseconds since epoch #[must_use] pub const fn nanos(&self) -> u64 { self.nanos } } // ============================================================================= // MARKET TYPES (MIGRATED FROM TRADING_ENGINE) // ============================================================================= /// Market regime enumeration for position sizing scaling and risk management #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum MarketRegime { /// Normal market conditions Normal, /// Crisis/stress market conditions Crisis, /// Trending market (strong directional movement) Trending, /// Sideways/ranging market (low volatility) Sideways, /// Bull market (sustained upward trend) Bull, /// Bear market (sustained downward trend) Bear, /// High volatility market conditions HighVolatility, /// Low volatility market conditions LowVolatility, /// Volatile market conditions (alias for `HighVolatility`) Volatile, /// Calm market conditions (alias for `LowVolatility`) Calm, /// Unknown/unclassified regime Unknown, /// Recovery regime - transitioning from crisis Recovery, /// Bubble regime - unsustainable upward movement Bubble, /// Correction regime - temporary downward adjustment Correction, /// Custom regime with numeric identifier Custom(usize), } impl Default for MarketRegime { fn default() -> Self { Self::Normal } } impl fmt::Display for MarketRegime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Normal => write!(f, "Normal"), Self::Crisis => write!(f, "Crisis"), Self::Trending => write!(f, "Trending"), Self::Sideways => write!(f, "Sideways"), Self::Bull => write!(f, "Bull"), Self::Bear => write!(f, "Bear"), Self::HighVolatility => write!(f, "HighVolatility"), Self::LowVolatility => write!(f, "LowVolatility"), Self::Volatile => write!(f, "Volatile"), Self::Calm => write!(f, "Calm"), Self::Unknown => write!(f, "Unknown"), Self::Recovery => write!(f, "Recovery"), Self::Bubble => write!(f, "Bubble"), Self::Correction => write!(f, "Correction"), Self::Custom(id) => write!(f, "Custom({id})"), } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[cfg_attr(feature = "database", derive(sqlx::Type))] #[cfg_attr(feature = "database", sqlx(type_name = "tick_type", rename_all = "snake_case"))] pub enum TickType { Trade, Bid, Ask, Quote, } /// Exchange enumeration for trading venues #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum Exchange { /// New York Stock Exchange NYSE, /// NASDAQ NASDAQ, /// Chicago Mercantile Exchange CME, /// Intercontinental Exchange ICE, /// London Stock Exchange LSE, /// Tokyo Stock Exchange TSE, /// Hong Kong Stock Exchange HKEX, /// Shanghai Stock Exchange SSE, /// Shenzhen Stock Exchange SZSE, /// Euronext EURONEXT, /// Deutsche Börse XETRA, /// Chicago Board of Trade CBOT, /// Chicago Board Options Exchange CBOE, /// BATS Global Markets BATS, /// IEX Exchange IEX, /// Interactive Brokers IBKR, /// IC Markets ICMARKETS, /// Forex.com FOREX, /// Binance BINANCE, /// Coinbase COINBASE, /// Kraken KRAKEN, /// Unknown or unrecognized exchange UNKNOWN, } impl Default for Exchange { fn default() -> Self { Self::UNKNOWN } } impl fmt::Display for Exchange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::NYSE => write!(f, "NYSE"), Self::NASDAQ => write!(f, "NASDAQ"), Self::CME => write!(f, "CME"), Self::ICE => write!(f, "ICE"), Self::LSE => write!(f, "LSE"), Self::TSE => write!(f, "TSE"), Self::HKEX => write!(f, "HKEX"), Self::SSE => write!(f, "SSE"), Self::SZSE => write!(f, "SZSE"), Self::EURONEXT => write!(f, "EURONEXT"), Self::XETRA => write!(f, "XETRA"), Self::CBOT => write!(f, "CBOT"), Self::CBOE => write!(f, "CBOE"), Self::BATS => write!(f, "BATS"), Self::IEX => write!(f, "IEX"), Self::IBKR => write!(f, "IBKR"), Self::ICMARKETS => write!(f, "ICMARKETS"), Self::FOREX => write!(f, "FOREX"), Self::BINANCE => write!(f, "BINANCE"), Self::COINBASE => write!(f, "COINBASE"), Self::KRAKEN => write!(f, "KRAKEN"), Self::UNKNOWN => write!(f, "UNKNOWN"), } } } impl FromStr for Exchange { type Err = CommonTypeError; fn from_str(s: &str) -> Result { match s.to_uppercase().as_str() { "NYSE" => Ok(Self::NYSE), "NASDAQ" => Ok(Self::NASDAQ), "CME" => Ok(Self::CME), "ICE" => Ok(Self::ICE), "LSE" => Ok(Self::LSE), "TSE" => Ok(Self::TSE), "HKEX" => Ok(Self::HKEX), "SSE" => Ok(Self::SSE), "SZSE" => Ok(Self::SZSE), "EURONEXT" => Ok(Self::EURONEXT), "XETRA" => Ok(Self::XETRA), "CBOT" => Ok(Self::CBOT), "CBOE" => Ok(Self::CBOE), "BATS" => Ok(Self::BATS), "IEX" => Ok(Self::IEX), "IBKR" => Ok(Self::IBKR), "ICMARKETS" => Ok(Self::ICMARKETS), "FOREX" => Ok(Self::FOREX), "BINANCE" => Ok(Self::BINANCE), "COINBASE" => Ok(Self::COINBASE), "KRAKEN" => Ok(Self::KRAKEN), "UNKNOWN" => Ok(Self::UNKNOWN), _ => Ok(Self::UNKNOWN), // Default to UNKNOWN for unrecognized exchanges } } } /// Market tick data structure - CANONICAL SINGLE SOURCE OF TRUTH #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct MarketTick { pub symbol: Symbol, pub price: Price, pub size: Quantity, pub timestamp: HftTimestamp, pub tick_type: TickType, pub exchange: Exchange, pub sequence_number: u64, } impl MarketTick { /// Create a new market tick with current timestamp pub fn new( symbol: Symbol, price: Price, size: Quantity, tick_type: TickType, exchange: Exchange, sequence_number: u64, ) -> Result { Ok(Self { symbol, price, size, timestamp: HftTimestamp::now()?, tick_type, exchange, sequence_number, }) } /// Create a new market tick with specified timestamp (for backtesting) #[must_use] pub const fn with_timestamp( symbol: Symbol, price: Price, size: Quantity, timestamp: HftTimestamp, tick_type: TickType, exchange: Exchange, sequence_number: u64, ) -> Self { Self { symbol, price, size, timestamp, tick_type, exchange, sequence_number, } } } /// Trading signal for algorithmic trading #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct TradingSignal { /// Signal ID pub signal_id: Uuid, /// Symbol this signal applies to pub symbol: Symbol, /// Signal strength (-1.0 to 1.0) pub strength: f64, /// Signal direction pub direction: OrderSide, /// Confidence level (0.0 to 1.0) pub confidence: f64, /// Signal generation timestamp pub timestamp: HftTimestamp, /// Signal source/strategy pub source: String, /// Additional metadata pub metadata: std::collections::HashMap, } impl TradingSignal { /// Create a new trading signal pub fn new( symbol: Symbol, strength: f64, direction: OrderSide, confidence: f64, source: String, ) -> Result { if !(0.0..=1.0).contains(&confidence) { return Err(CommonTypeError::ValidationError { field: "confidence".to_owned(), reason: "Confidence must be between 0.0 and 1.0".to_owned(), }); } if !(-1.0..=1.0).contains(&strength) { return Err(CommonTypeError::ValidationError { field: "strength".to_owned(), reason: "Strength must be between -1.0 and 1.0".to_owned(), }); } Ok(Self { signal_id: Uuid::new_v4(), symbol, strength, direction, confidence, timestamp: HftTimestamp::now_common()?, source, metadata: std::collections::HashMap::new(), }) } /// Add metadata to the signal #[must_use] pub fn with_metadata(mut self, key: String, value: String) -> Self { self.metadata.insert(key, value); self } } // ============================================================================= // HIGH-PERFORMANCE TYPES FOR COPY/CLONE OPTIMIZATION // ============================================================================= /// Lightweight Order reference for high-performance contexts requiring Copy trait /// /// This struct contains only the essential order data needed for performance-critical /// operations like `SmallBatchRing` processing, while maintaining Copy semantics. /// For full order details, use the complete Order struct. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct OrderRef { /// Order ID (u64 for performance) pub id: u64, /// Symbol hash for fast lookups pub symbol_hash: i64, /// Order side (Buy/Sell) pub side: OrderSide, /// Order type pub order_type: OrderType, /// Quantity (fixed-point u64) pub quantity: u64, /// Price (fixed-point u64, 0 for market orders) pub price: u64, /// Timestamp (nanoseconds since epoch) pub timestamp: u64, } impl OrderRef { /// Create `OrderRef` from a full Order struct #[must_use] pub fn from_order(order: &Order) -> Self { Self { id: order.id.value(), symbol_hash: order.symbol_hash(), side: order.side, order_type: order.order_type, quantity: order.quantity.raw_value(), price: order.price.map_or(0, |p| p.raw_value()), timestamp: order.created_at.nanos(), } } /// Create a limit order reference #[must_use] pub fn limit(symbol_hash: i64, side: OrderSide, quantity: u64, price: u64) -> Self { Self { id: OrderId::new().value(), symbol_hash, side, order_type: OrderType::Limit, quantity, price, timestamp: HftTimestamp::now_or_zero().nanos(), } } /// Create a market order reference #[must_use] pub fn market(symbol_hash: i64, side: OrderSide, quantity: u64) -> Self { Self { id: OrderId::new().value(), symbol_hash, side, order_type: OrderType::Market, quantity, price: 0, timestamp: HftTimestamp::now_or_zero().nanos(), } } /// Get quantity as Quantity type #[must_use] pub const fn get_quantity(&self) -> Quantity { Quantity::from_raw(self.quantity) } /// Get price as Price type (None for market orders) #[must_use] pub const fn get_price(&self) -> Option { if self.price == 0 { None } else { Some(Price::from_raw(self.price)) } } /// Check if this is a buy order #[must_use] pub fn is_buy(&self) -> bool { self.side == OrderSide::Buy } /// Check if this is a sell order #[must_use] pub fn is_sell(&self) -> bool { self.side == OrderSide::Sell } /// Check if this is a market order #[must_use] pub fn is_market_order(&self) -> bool { self.order_type == OrderType::Market || self.price == 0 } /// Check if this is a limit order #[must_use] pub fn is_limit_order(&self) -> bool { self.order_type == OrderType::Limit && self.price > 0 } } impl Default for OrderRef { fn default() -> Self { Self { id: 0, symbol_hash: 0, side: OrderSide::Buy, order_type: OrderType::Market, quantity: 0, price: 0, timestamp: 0, } } }