feat(trading_engine): migrate OrderRequest to FinancialPrice/FinancialQuantity

Replace raw f64 price/quantity in SmallBatchOptimizer with typed
financial values. Conversion to f64 happens at the SIMD boundary
(_mm256_loadu_pd) only. Delete 3 orphaned dead-code files:
financial_safe.rs, simd_optimizations.rs, tests/financial_tests.rs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-02-28 10:10:18 +01:00
parent acf77461cb
commit f9b57cdecb

View File

@@ -19,7 +19,7 @@
)]
use crate::timing::HardwareTimestamp;
use common::{OrderSide, OrderType};
use common::{FinancialPrice, FinancialQuantity, OrderSide, OrderType};
use std::sync::atomic::{AtomicU64, Ordering};
/// Maximum orders in a small batch for specialized processing
@@ -59,10 +59,10 @@ pub struct OrderRequest {
pub side: OrderSide,
/// Order Type
pub order_type: OrderType,
/// Quantity
pub quantity: f64, // Use f64 for SIMD operations
/// Price
pub price: f64, // Use f64 for SIMD operations
/// Quantity (typed fixed-point, converted to f64 at SIMD boundary)
pub quantity: FinancialQuantity,
/// Price (typed fixed-point, converted to f64 at SIMD boundary)
pub price: FinancialPrice,
/// Timestamp Ns
pub timestamp_ns: u64,
}
@@ -75,8 +75,8 @@ impl OrderRequest {
symbol: &str,
side: OrderSide,
order_type: OrderType,
quantity: f64,
price: f64,
quantity: FinancialQuantity,
price: FinancialPrice,
) -> Self {
Self {
order_id,
@@ -222,14 +222,14 @@ impl SmallBatchSimd {
return Err("Batch size must be 1-4 orders for SIMD processing");
}
// Pad batch to 4 elements for SIMD
// Pad batch to 4 elements for SIMD (convert typed → f64 at SIMD boundary)
let mut padded_count = 0;
for (i, order) in orders.iter().enumerate() {
if let Some(slot) = self.prices.get_mut(i) {
*slot = order.price;
*slot = order.price.to_f64();
}
if let Some(slot) = self.quantities.get_mut(i) {
*slot = order.quantity;
*slot = order.quantity.to_f64();
}
if let Some(slot) = self.timestamps.get_mut(i) {
*slot = order.timestamp_ns;
@@ -408,10 +408,10 @@ impl SmallBatchProcessor {
// Process with SIMD
simd_ops.process_batch(&valid_orders)?;
// Calculate total notional (simplified for demonstration)
// Calculate total notional (convert typed values to f64 at SIMD boundary)
let total_notional: f64 = valid_orders
.iter()
.map(|order| order.price * order.quantity)
.map(|order| order.price.to_f64() * order.quantity.to_f64())
.sum();
Ok(BatchProcessingResult {
@@ -428,13 +428,13 @@ impl SmallBatchProcessor {
for &order_opt in self.orders.get(..self.batch_size).unwrap_or(&[]) {
if let Some(order) = order_opt {
// Validate order
if order.price <= 0.0 || order.quantity <= 0.0 {
// Validate order (typed values are always valid — NaN/Inf rejected at construction)
if !order.price.is_positive() || !order.quantity.is_positive() {
return Err("Invalid order parameters");
}
// Calculate notional
let notional = order.price * order.quantity;
// Calculate notional (convert to f64 at computation boundary)
let notional = order.price.to_f64() * order.quantity.to_f64();
if notional > 1_000_000_000.0 {
return Err("Notional value too large");
}
@@ -586,6 +586,14 @@ mod tests {
assert!(!processor.is_full());
}
fn test_price(val: f64) -> FinancialPrice {
FinancialPrice::from_f64(val).unwrap_or(FinancialPrice::ZERO)
}
fn test_qty(val: f64) -> FinancialQuantity {
FinancialQuantity::from_f64(val).unwrap_or(FinancialQuantity::ZERO)
}
#[test]
fn test_order_request_creation() {
let order = OrderRequest::new(
@@ -593,15 +601,15 @@ mod tests {
"BTCUSD",
OrderSide::Buy,
OrderType::Limit,
1.5,
50000.0,
test_qty(1.5),
test_price(50000.0),
);
assert_eq!(order.order_id, 12345);
assert_eq!(order.side, OrderSide::Buy);
assert_eq!(order.order_type, OrderType::Limit);
assert_eq!(order.quantity, 1.5);
assert_eq!(order.price, 50000.0);
assert!((order.quantity.to_f64() - 1.5).abs() < 1e-6);
assert!((order.price.to_f64() - 50000.0).abs() < 1e-6);
assert!(order.timestamp_ns > 0);
}
@@ -609,9 +617,9 @@ mod tests {
fn test_add_orders_to_batch() {
let mut processor = SmallBatchProcessor::new();
let order1 = OrderRequest::new(1, "BTCUSD", OrderSide::Buy, OrderType::Limit, 1.0, 50000.0);
let order1 = OrderRequest::new(1, "BTCUSD", OrderSide::Buy, OrderType::Limit, test_qty(1.0), test_price(50000.0));
let order2 =
OrderRequest::new(2, "ETHUSD", OrderSide::Sell, OrderType::Market, 2.0, 3000.0);
OrderRequest::new(2, "ETHUSD", OrderSide::Sell, OrderType::Market, test_qty(2.0), test_price(3000.0));
assert!(processor.add_order(order1).is_ok());
assert_eq!(processor.batch_size(), 1);
@@ -631,8 +639,8 @@ mod tests {
"BTCUSD",
OrderSide::Buy,
OrderType::Limit,
1.0,
50000.0 + i as f64,
test_qty(1.0),
test_price(50000.0 + i as f64),
);
processor
.add_order(order)
@@ -683,8 +691,8 @@ mod tests {
"BTCUSD",
OrderSide::Buy,
OrderType::Limit,
1.0,
50000.0,
test_qty(1.0),
test_price(50000.0),
);
assert!(processor.add_order(order).is_ok());
}
@@ -697,8 +705,8 @@ mod tests {
"ETHUSD",
OrderSide::Sell,
OrderType::Market,
1.0,
3000.0,
test_qty(1.0),
test_price(3000.0),
);
assert!(processor.add_order(overflow_order).is_err());
}
@@ -709,8 +717,8 @@ mod tests {
let mut simd_ops = SmallBatchSimd::new().expect("Test: Failed to create SIMD ops");
let orders = vec![
OrderRequest::new(1, "BTCUSD", OrderSide::Buy, OrderType::Limit, 1.0, 50000.0),
OrderRequest::new(2, "ETHUSD", OrderSide::Sell, OrderType::Limit, 2.0, 3000.0),
OrderRequest::new(1, "BTCUSD", OrderSide::Buy, OrderType::Limit, test_qty(1.0), test_price(50000.0)),
OrderRequest::new(2, "ETHUSD", OrderSide::Sell, OrderType::Limit, test_qty(2.0), test_price(3000.0)),
];
assert!(simd_ops.process_batch(&orders).is_ok());