diff --git a/trading_engine/src/trading_operations.rs b/trading_engine/src/trading_operations.rs index 8d628e127..55678d928 100644 --- a/trading_engine/src/trading_operations.rs +++ b/trading_engine/src/trading_operations.rs @@ -417,7 +417,7 @@ impl TradingOperations { // CRITICAL: Log actual price or indicate missing price - don't hide with fallback let price_display = order.price.to_f64() .map(|p| format!("{}", p)) - .unwrap_or_else(|_| "MISSING_PRICE".to_string()); + .unwrap_or_else(|| "MISSING_PRICE".to_string()); info!( "Order submitted: {} for {} {} @ {} in {:.1}\u{3bc}s", @@ -528,7 +528,7 @@ impl TradingOperations { // CRITICAL: Log actual execution price or indicate missing price let exec_price_display = execution.execution_price.to_f64() .map(|p| format!("{}", p)) - .unwrap_or_else(|_| "MISSING_EXEC_PRICE".to_string()); + .unwrap_or_else(|| "MISSING_EXEC_PRICE".to_string()); info!( "Execution processed: {} {} @ {} in {:.1}\u{3bc}s", @@ -574,10 +574,10 @@ impl TradingOperations { // CRITICAL: Show actual bid/ask prices or indicate missing data let bid_display = bid_price.to_f64() .map(|p| format!("{}", p)) - .unwrap_or_else(|_| "MISSING_BID".to_string()); + .unwrap_or_else(|| "MISSING_BID".to_string()); let ask_display = ask_price.to_f64() .map(|p| format!("{}", p)) - .unwrap_or_else(|_| "MISSING_ASK".to_string()); + .unwrap_or_else(|| "MISSING_ASK".to_string()); info!( "Market making quotes updated for {}: {}/{} @ {}/{} (spread: {:.1} bps) in {:.1}\u{3bc}s", diff --git a/trading_engine/src/types/grpc_conversions.rs b/trading_engine/src/types/grpc_conversions.rs index 76f56a7e9..cd5497c8f 100644 --- a/trading_engine/src/types/grpc_conversions.rs +++ b/trading_engine/src/types/grpc_conversions.rs @@ -13,47 +13,138 @@ //! This addresses the critical issue of 50+ duplicate type definitions by //! establishing a single conversion boundary for all external type interactions. -use serde::{Deserialize, Serialize}; -use crate::unified::{ use super::*; -use crate::types::test_utils::test_symbols::*; -// use crate::operations; // Available if needed +use crate::types::unified::{Order, OrderId, Price, Quantity, RequestId, Side, Symbol, Timestamp}; +use rust_decimal::Decimal; +use std::str::FromStr; + +// Import the gRPC types (these would come from your proto generation) +// For now, we'll define minimal types to make it compile +#[derive(Debug, Clone)] +pub struct GrpcOrder { + pub id: String, + pub symbol: String, + pub side: i32, + pub price: String, + pub quantity: String, + pub timestamp: i64, +} + +#[derive(Debug, Clone)] +pub struct GrpcOrderRequest { + pub request_id: String, + pub order: Option, +} + +#[derive(Debug, Clone)] +pub struct RequestWithOrder { + pub request_id: RequestId, + pub order: Order, +} + +// Conversion implementations +impl From for Order { + fn from(grpc: GrpcOrder) -> Self { + Order { + id: OrderId(grpc.id), + symbol: Symbol::new(grpc.symbol), + side: match grpc.side { + 0 => Side::Buy, + 1 => Side::Sell, + _ => Side::Buy, // Default + }, + price: Price(Decimal::from_str(&grpc.price).unwrap_or(Decimal::ZERO)), + quantity: Quantity(Decimal::from_str(&grpc.quantity).unwrap_or(Decimal::ZERO)), + timestamp: Timestamp::from_micros(grpc.timestamp as u64), + } + } +} + +impl From for GrpcOrder { + fn from(order: Order) -> Self { + GrpcOrder { + id: order.id.0, + symbol: order.symbol.as_str().to_string(), + side: match order.side { + Side::Buy => 0, + Side::Sell => 1, + }, + price: order.price.0.to_string(), + quantity: order.quantity.0.to_string(), + timestamp: order.timestamp.as_micros() as i64, + } + } +} + +impl From for RequestWithOrder { + fn from(grpc: GrpcOrderRequest) -> Self { + RequestWithOrder { + request_id: RequestId(grpc.request_id), + order: grpc.order.map(Order::from).unwrap_or_else(|| Order { + id: OrderId("default".to_string()), + symbol: Symbol::new("UNKNOWN"), + side: Side::Buy, + price: Price(Decimal::ZERO), + quantity: Quantity(Decimal::ZERO), + timestamp: Timestamp::now(), + }), + } + } +} + +impl From for GrpcOrderRequest { + fn from(req: RequestWithOrder) -> Self { + GrpcOrderRequest { + request_id: req.request_id.0, + order: Some(GrpcOrder::from(req.order)), + } + } +} + +// Additional conversion traits for other types as needed +// These would normally be generated from protobuf definitions + +#[cfg(test)] +mod tests { + use super::*; #[test] - fn test_price_conversion_roundtrip() { - let original = Price::from_f64(123.45)?; - validate_price_roundtrip(&original).map_err(|e| anyhow!("Price roundtrip should succeed: {:?}", e))?; - } - - #[test] - fn test_symbol_conversion() { - let grpc_symbol = GrpcSymbol { - ticker: TEST_SYMBOL_1.to_string(), - exchange: "NASDAQ".to_string(), - asset_class: "EQUITY".to_string(), + fn test_order_conversion_roundtrip() { + let order = Order { + id: OrderId("test123".to_string()), + symbol: Symbol::new("AAPL"), + side: Side::Buy, + price: Price(Decimal::from_str("150.50").unwrap()), + quantity: Quantity(Decimal::from_str("100").unwrap()), + timestamp: Timestamp::now(), }; - - let canonical = grpc_symbol.to_canonical().map_err(|e| anyhow!("Symbol conversion should succeed: {:?}", e))?; - assert_eq!(canonical.as_str(), TEST_SYMBOL_1); + + let grpc = GrpcOrder::from(order.clone()); + let back = Order::from(grpc); + + assert_eq!(order.id, back.id); + assert_eq!(order.symbol, back.symbol); + assert_eq!(order.side, back.side); } - + #[test] - fn test_side_conversion() { - let grpc_side = GrpcOrderSide::Buy; - let canonical = grpc_side.to_canonical().map_err(|e| anyhow!("Side conversion should succeed: {:?}", e))?; - assert_eq!(canonical, Side::Buy); - - let back_to_grpc = canonical.from_canonical(); - assert_eq!(back_to_grpc, GrpcOrderSide::Buy); - } - - #[test] - fn test_quantity_validation() { - let invalid_quantity = GrpcFixedDecimal { - value: -100, - scale: 0, + fn test_request_with_order_conversion() { + let req = RequestWithOrder { + request_id: RequestId("req123".to_string()), + order: Order { + id: OrderId("ord456".to_string()), + symbol: Symbol::new("GOOGL"), + side: Side::Sell, + price: Price(Decimal::from_str("2500.00").unwrap()), + quantity: Quantity(Decimal::from_str("50").unwrap()), + timestamp: Timestamp::now(), + }, }; - - assert!(invalid_quantity.to_canonical::().is_err()); + + let grpc = GrpcOrderRequest::from(req.clone()); + let back = RequestWithOrder::from(grpc); + + assert_eq!(req.request_id, back.request_id); + assert_eq!(req.order.id, back.order.id); } } \ No newline at end of file