//! Type conversions and utilities for TLI gRPC services use crate::error::{TliError, TliResult}; use crate::proto::trading::ServiceStatus; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::time::{SystemTime, UNIX_EPOCH}; // Simplified imports to avoid core dependency issues // Removed unused imports: rust_decimal::Decimal, common::Price, common::Quantity, common::Timestamp, common::OrderSide // Define basic types locally until core is available // Define local types for TLI use (avoiding complex core dependencies) /// System status enumeration for TLI services /// /// Represents the operational state of trading system services from healthy /// operation to critical failures requiring immediate attention. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum TliSystemStatus { /// System is operating normally with all components functional Healthy, /// System is operational but showing warning indicators Warning, /// System is experiencing reduced functionality or performance Degraded, /// System is in critical state requiring immediate intervention Critical, } /// Order side enumeration for buy/sell operations /// /// Represents the direction of a trading order in the financial markets. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum TliOrderSide { /// Buy order - purchasing an asset Buy, /// Sell order - selling an asset Sell, } /// Metric data structure for monitoring and observability /// /// Contains time-series metric data with metadata labels for comprehensive /// system monitoring and performance tracking. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TliMetric { /// Name of the metric (e.g., "latency", "throughput") pub name: String, /// Numeric value of the metric measurement pub value: f64, /// Unit of measurement (e.g., "ms", "ops/sec", "bytes") pub unit: String, /// Key-value labels for metric categorization and filtering pub labels: HashMap, /// Timestamp when the metric was recorded (Unix nanoseconds) pub timestamp_unix_nanos: i64, } /// Service status information for system health monitoring /// /// Provides comprehensive status information about individual trading /// system services including operational state and diagnostic data. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TliServiceStatus { /// Name of the service (e.g., "`trading_engine`", "`risk_manager`") pub service_name: String, /// Current operational status of the service pub status: TliSystemStatus, /// Service uptime in seconds since last restart pub uptime_seconds: f64, /// Last error message if the service encountered issues pub last_error: Option, } // Use protobuf types for protocol communication use crate::proto::trading::{ OrderStatus as ProtoOrderStatus, OrderType as ProtoOrderType, Position as ProtoPosition, }; /// Convert Unix nanoseconds to `SystemTime` /// /// Converts a Unix timestamp in nanoseconds to a Rust `SystemTime` instance. /// /// Returns `UNIX_EPOCH` for negative values to handle invalid timestamps gracefully. /// /// # Arguments /// * `nanos` - Unix timestamp in nanoseconds since epoch /// /// # Returns /// `SystemTime` instance representing the timestamp pub fn unix_nanos_to_system_time(nanos: i64) -> SystemTime { if nanos < 0 { UNIX_EPOCH } else { UNIX_EPOCH + std::time::Duration::from_nanos(nanos as u64) } } /// Convert `SystemTime` to Unix nanoseconds /// /// Converts a Rust `SystemTime` instance to Unix nanoseconds since epoch. /// /// Returns 0 for times before the Unix epoch. /// /// # Arguments /// * `time` - `SystemTime` instance to convert /// /// # Returns /// /// Unix timestamp in nanoseconds since epoch pub fn system_time_to_unix_nanos(time: SystemTime) -> i64 { time.duration_since(UNIX_EPOCH) .unwrap_or_default() .as_nanos() as i64 } /// Get current timestamp in Unix nanoseconds /// /// Returns the current system time as Unix nanoseconds since epoch. /// /// Useful for timestamping events and metrics in the trading system. /// /// # Returns /// /// Current Unix timestamp in nanoseconds pub fn current_unix_nanos() -> i64 { system_time_to_unix_nanos(SystemTime::now()) } /// Convert `OrderSide` to string representation /// /// Converts a TLI order side enumeration to its standard string representation /// used in trading protocols and APIs. /// /// # Arguments /// * `side` - The order side to convert /// /// # Returns /// /// String representation ("BUY" or "SELL") pub const fn order_side_to_string(side: TliOrderSide) -> &'static str { match side { TliOrderSide::Buy => "BUY", TliOrderSide::Sell => "SELL", } } /// Convert string to `OrderSide` /// /// Parses a string representation of an order side into the TLI enumeration. /// /// Case-insensitive parsing supports both "BUY"/"SELL" and "buy"/"sell". /// /// # Arguments /// * `side` - String representation of order side /// /// # Returns /// `TliResult` - Parsed order side or error for invalid input /// /// # Errors /// /// Returns `TliError::InvalidRequest` for unrecognized order side strings pub fn string_to_order_side(side: &str) -> TliResult { match side.to_uppercase().as_str() { "BUY" => Ok(TliOrderSide::Buy), "SELL" => Ok(TliOrderSide::Sell), _ => Err(TliError::InvalidRequest(format!( "Invalid order side: {}", side ))), } } /// Convert protobuf `OrderType` to string representation /// /// Converts a protobuf `OrderType` enumeration to its standard string representation /// used in trading APIs and user interfaces. /// /// # Arguments /// * `order_type` - The protobuf `OrderType` to convert /// /// # Returns /// /// String representation of the order type pub const fn order_type_to_string(order_type: ProtoOrderType) -> &'static str { match order_type { ProtoOrderType::Market => "MARKET", ProtoOrderType::Limit => "LIMIT", ProtoOrderType::Stop => "STOP", ProtoOrderType::StopLimit => "STOP_LIMIT", ProtoOrderType::Unspecified => "UNSPECIFIED", } } /// Convert string to protobuf `OrderType` /// /// Parses a string representation into the corresponding protobuf `OrderType`. /// /// Used for converting user input and API requests into internal representations. /// /// # Arguments /// * `order_type` - String representation of order type /// /// # Returns /// `TliResult` - Parsed order type or error for invalid input /// /// # Errors /// /// Returns `TliError::InvalidRequest` for unrecognized order type strings pub fn string_to_order_type(order_type: &str) -> TliResult { match order_type { "MARKET" => Ok(ProtoOrderType::Market), "LIMIT" => Ok(ProtoOrderType::Limit), "STOP" => Ok(ProtoOrderType::Stop), "STOP_LIMIT" => Ok(ProtoOrderType::StopLimit), _ => Err(TliError::InvalidRequest(format!( "Invalid order type: {}", order_type ))), } } /// Convert protobuf `OrderStatus` to string representation /// /// Converts a protobuf `OrderStatus` enumeration to its standard string representation /// used in trading APIs and order management systems. /// /// # Arguments /// * `status` - The protobuf `OrderStatus` to convert /// /// # Returns /// /// String representation of the order status pub const fn order_status_to_string(status: ProtoOrderStatus) -> &'static str { match status { ProtoOrderStatus::New => "NEW", ProtoOrderStatus::PartiallyFilled => "PARTIALLY_FILLED", ProtoOrderStatus::Filled => "FILLED", ProtoOrderStatus::Cancelled => "CANCELLED", ProtoOrderStatus::Rejected => "REJECTED", ProtoOrderStatus::PendingCancel => "PENDING_CANCEL", ProtoOrderStatus::Unspecified => "UNSPECIFIED", } } /// Convert string to protobuf `OrderStatus` /// /// Parses a string representation into the corresponding protobuf `OrderStatus`. /// /// Used for processing order status updates from trading venues and APIs. /// /// # Arguments /// * `status` - String representation of order status /// /// # Returns /// `TliResult` - Parsed order status or error for invalid input /// /// # Errors /// /// Returns `TliError::InvalidRequest` for unrecognized order status strings pub fn string_to_order_status(status: &str) -> TliResult { match status { "NEW" => Ok(ProtoOrderStatus::New), "PARTIALLY_FILLED" => Ok(ProtoOrderStatus::PartiallyFilled), "FILLED" => Ok(ProtoOrderStatus::Filled), "CANCELLED" => Ok(ProtoOrderStatus::Cancelled), "REJECTED" => Ok(ProtoOrderStatus::Rejected), "PENDING_CANCEL" => Ok(ProtoOrderStatus::PendingCancel), _ => Err(TliError::InvalidRequest(format!( "Invalid order status: {}", status ))), } } /// Convert TLI `SystemStatus` to string representation /// /// Converts a TLI `SystemStatus` enumeration to its standard string representation /// used in system monitoring and health check APIs. /// /// # Arguments /// * `status` - The TLI `SystemStatus` to convert /// /// # Returns /// /// String representation of the system status pub const fn system_status_to_string(status: TliSystemStatus) -> &'static str { match status { TliSystemStatus::Healthy => "HEALTHY", TliSystemStatus::Warning => "WARNING", TliSystemStatus::Degraded => "DEGRADED", TliSystemStatus::Critical => "CRITICAL", } } /// Convert string to TLI `SystemStatus` /// /// Parses a string representation into the corresponding TLI `SystemStatus`. /// /// Used for processing health check responses and monitoring system states. /// /// # Arguments /// * `status` - String representation of system status /// /// # Returns /// `TliResult` - Parsed system status or error for invalid input /// /// # Errors /// /// Returns `TliError::InvalidRequest` for unrecognized system status strings pub fn string_to_system_status(status: &str) -> TliResult { match status { "HEALTHY" => Ok(TliSystemStatus::Healthy), "WARNING" => Ok(TliSystemStatus::Warning), "DEGRADED" => Ok(TliSystemStatus::Degraded), "CRITICAL" => Ok(TliSystemStatus::Critical), _ => Err(TliError::InvalidRequest(format!( "Invalid system status: {}", status ))), } } /// Validate symbol format /// /// Validates that a trading symbol meets the required format constraints. /// /// Ensures symbols are properly formatted for use in trading operations. /// /// # Arguments /// * `symbol` - The trading symbol to validate /// /// # Returns /// `TliResult<()>` - Ok if valid, error describing validation failure /// /// # Errors /// - `TliError::InvalidSymbol` if symbol is empty, too long, or contains invalid characters /// /// # Validation Rules /// - Symbol must not be empty /// /// - Symbol must be 20 characters or less /// - Symbol must contain only alphanumeric characters and separators (., -, _) pub fn validate_symbol(symbol: &str) -> TliResult<()> { if symbol.is_empty() { return Err(TliError::InvalidSymbol("Symbol cannot be empty".to_owned())); } if symbol.len() > 20 { return Err(TliError::InvalidSymbol( "Symbol too long (max 20 characters)".to_owned(), )); } // Basic symbol validation - alphanumeric plus some common separators if !symbol .chars() .all(|c| c.is_alphanumeric() || c == '.' || c == '-' || c == '_') { return Err(TliError::InvalidSymbol( "Symbol contains invalid characters".to_owned(), )); } Ok(()) } /// Validate quantity for trading operations /// /// Validates that a trading quantity is positive and finite. /// /// Prevents invalid order quantities that could cause trading errors. /// /// # Arguments /// * `quantity` - The quantity to validate /// /// # Returns /// `TliResult<()>` - Ok if valid, error describing validation failure /// /// # Errors /// - `TliError::InvalidRequest` if quantity is not positive or not finite pub fn validate_quantity(quantity: f64) -> TliResult<()> { if quantity <= 0.0 { return Err(TliError::InvalidRequest( "Quantity must be positive".to_owned(), )); } if !quantity.is_finite() { return Err(TliError::InvalidRequest( "Quantity must be finite".to_owned(), )); } Ok(()) } /// Validate price for trading operations /// /// Validates that a trading price is positive and finite. /// /// Prevents invalid order prices that could cause trading errors. /// /// # Arguments /// * `price` - The price to validate /// /// # Returns /// `TliResult<()>` - Ok if valid, error describing validation failure /// /// # Errors /// - `TliError::InvalidRequest` if price is not positive or not finite pub fn validate_price(price: f64) -> TliResult<()> { if price <= 0.0 { return Err(TliError::InvalidRequest( "Price must be positive".to_owned(), )); } if !price.is_finite() { return Err(TliError::InvalidRequest("Price must be finite".to_owned())); } Ok(()) } /// Create a metric with current timestamp /// /// Creates a new `TliMetric` instance with the current timestamp automatically applied. /// /// Useful for recording system metrics and performance measurements. /// /// # Arguments /// * `name` - Name of the metric (e.g., "latency", "throughput") /// /// * `value` - Numeric value of the measurement /// * `unit` - Unit of measurement (e.g., "ms", "ops/sec") /// /// * `labels` - Key-value pairs for metric categorization /// /// # Returns /// `TliMetric` instance with current timestamp pub fn create_metric( name: String, value: f64, unit: String, labels: HashMap, ) -> TliMetric { TliMetric { name, value, unit, labels, timestamp_unix_nanos: current_unix_nanos(), } } /// Create a protobuf position from individual fields /// /// Creates a `ProtoPosition` instance with calculated market value and unrealized P&L. /// /// Used for building position responses and portfolio summaries. /// /// # Arguments /// * `symbol` - Trading symbol for the position /// /// * `quantity` - Number of shares/units held /// * `market_price` - Current market price per unit /// /// * `average_cost` - Average cost basis per unit /// /// # Returns /// `ProtoPosition` with calculated market value and unrealized P&L pub fn create_proto_position( symbol: String, quantity: f64, market_price: f64, average_cost: f64, ) -> ProtoPosition { let market_value = quantity * market_price; let unrealized_pnl = market_value - (quantity * average_cost); ProtoPosition { symbol, quantity, market_price, market_value, average_cost, unrealized_pnl, realized_pnl: 0.0, // This would come from trade history } } /// Create a service status entry /// /// Creates a `ServiceStatus` protobuf message with current timestamp. /// /// Used for health check responses and system monitoring. /// /// # Arguments /// * `name` - Name of the service being reported /// /// * `status` - Current operational status /// * `message` - Human-readable status message /// /// * `details` - Additional key-value diagnostic information /// /// # Returns /// `ServiceStatus` protobuf message with current timestamp pub fn create_service_status( name: String, status: TliSystemStatus, message: String, details: HashMap, ) -> ServiceStatus { ServiceStatus { name, status: status as i32, message, last_check_unix_nanos: current_unix_nanos(), details, } } #[cfg(test)] mod tests { use super::*; #[test] fn test_timestamp_conversion() { let now = SystemTime::now(); let nanos = system_time_to_unix_nanos(now); let converted = unix_nanos_to_system_time(nanos); // Allow for small timing differences let diff = now .duration_since(converted) .unwrap_or_else(|_| converted.duration_since(now).unwrap()); assert!(diff.as_millis() < 1); } #[test] fn test_order_side_conversion() { assert_eq!(order_side_to_string(TliOrderSide::Buy), "BUY"); assert_eq!(order_side_to_string(TliOrderSide::Sell), "SELL"); assert_eq!(string_to_order_side("BUY").unwrap(), TliOrderSide::Buy); assert_eq!(string_to_order_side("buy").unwrap(), TliOrderSide::Buy); string_to_order_side("INVALID").unwrap_err(); } #[test] fn test_symbol_validation() { validate_symbol("AAPL").unwrap(); validate_symbol("BTC.USD").unwrap(); validate_symbol("EUR-USD").unwrap(); validate_symbol("SPX_500").unwrap(); assert!(validate_symbol("").is_err()); assert!(validate_symbol("A".repeat(21).as_str()).is_err()); assert!(validate_symbol("BTC/USD").is_err()); // slash not allowed } #[test] fn test_quantity_validation() { validate_quantity(1.0).unwrap(); validate_quantity(0.0001).unwrap(); assert!(validate_quantity(0.0).is_err()); assert!(validate_quantity(-1.0).is_err()); assert!(validate_quantity(f64::NAN).is_err()); assert!(validate_quantity(f64::INFINITY).is_err()); } #[test] fn test_create_position() { let position = create_proto_position("AAPL".to_owned(), 100.0, 150.0, 140.0); assert_eq!(position.symbol, "AAPL"); assert_eq!(position.quantity, 100.0); assert_eq!(position.market_price, 150.0); assert_eq!(position.market_value, 15000.0); assert_eq!(position.average_cost, 140.0); assert_eq!(position.unrealized_pnl, 1000.0); // (150-140) * 100 } }