//! Integration tests for order generation module //! //! Tests order generation from portfolio allocations, including: //! - Order generation from allocations //! - Delta orders with existing positions //! - Order size validation //! - Order persistence //! - Performance benchmarks use chrono::Utc; use rust_decimal::Decimal; use rust_decimal_macros::dec; use sqlx::PgPool; use uuid::Uuid; use common::{OrderSide, OrderStatus, OrderType, Position, Quantity}; use trading_agent_service::orders::{OrderError, OrderGenerator, PortfolioAllocation}; /// Setup test database connection async fn setup_database() -> PgPool { let database_url = std::env::var("DATABASE_URL").unwrap_or_else(|_| { "postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt".to_string() }); let pool = sqlx::PgPool::connect(&database_url) .await .expect("Failed to connect to database"); // Run migrations sqlx::migrate!("../../migrations") .run(&pool) .await .expect("Failed to run migrations"); // Clean up any test data from previous runs cleanup_test_data(&pool).await; pool } /// Clean up test order data async fn cleanup_test_data(pool: &PgPool) { let _ = sqlx::query("DELETE FROM agent_orders WHERE allocation_id LIKE 'alloc_%' OR allocation_id = 'test_strategy'") .execute(pool) .await; } /// Create a test allocation with weights fn create_test_allocation(weights: Vec<(&str, f64)>) -> PortfolioAllocation { let mut symbol_weights = std::collections::HashMap::new(); for (symbol, weight) in weights { symbol_weights.insert(symbol.to_string(), weight); } PortfolioAllocation { allocation_id: format!("alloc_{}", Uuid::new_v4()), strategy_id: "test_strategy".to_string(), total_capital: dec!(1_000_000.0), // $1M symbol_weights, rebalance_threshold: 0.05, // 5% max_position_size: 0.20, // 20% created_at: Utc::now(), } } /// Create a test position fn create_test_position(symbol: &str, quantity: Decimal, avg_price: Decimal) -> Position { let now = Utc::now(); Position { id: Uuid::new_v4(), symbol: symbol.to_string(), quantity, avg_price, avg_cost: avg_price, basis: quantity * avg_price, average_price: avg_price, market_value: quantity * avg_price * dec!(1.01), // Assume 1% gain unrealized_pnl: quantity * avg_price * dec!(0.01), realized_pnl: Decimal::ZERO, created_at: now, updated_at: now, last_updated: now, current_price: Some(avg_price * dec!(1.01)), margin_requirement: Decimal::ZERO, notional_value: quantity * avg_price, } } #[tokio::test] async fn test_generate_orders_from_allocation() { let pool = setup_database().await; let generator = OrderGenerator::new( pool.clone(), 100.0, // min_order_size: $100 500_000.0, // max_order_size: $500K (enough for $1M allocation) ); // Create allocation: 40% ES.FUT, 30% NQ.FUT, 30% ZN.FUT let allocation = create_test_allocation(vec![("ES.FUT", 0.40), ("NQ.FUT", 0.30), ("ZN.FUT", 0.30)]); // No existing positions - all new orders let current_positions = vec![]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; assert!( result.is_ok(), "Order generation should succeed: {:?}", result.err() ); let orders = result.expect("Orders should be present"); // Should have 3 orders (one per symbol) assert_eq!(orders.len(), 3, "Should generate 3 orders"); // Verify order properties for order in &orders { assert!( order.quantity > Quantity::ZERO, "Order quantity should be positive" ); assert_eq!( order.status, OrderStatus::Created, "Order should be in Created status" ); assert_eq!( order.order_type, OrderType::Market, "Should use market orders" ); assert_eq!( order.side, OrderSide::Buy, "All orders should be buys for new positions" ); } // Verify ES.FUT gets ~40% of capital let _es_order = orders .iter() .find(|o| o.symbol.as_str() == "ES.FUT") .expect("ES order should exist"); // At ~$5000/contract, $400K should be ~80 contracts // Allow some tolerance for rounding let expected_value = allocation.total_capital * dec!(0.40); assert!( expected_value > dec!(350_000) && expected_value < dec!(450_000), "ES allocation should be ~$400K" ); } #[tokio::test] async fn test_delta_orders_with_existing_positions() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0); // Create allocation: 50% ES.FUT, 50% NQ.FUT let allocation = create_test_allocation(vec![("ES.FUT", 0.50), ("NQ.FUT", 0.50)]); // Existing positions: 70% ES.FUT, 30% NQ.FUT (overweight ES by 20%, underweight NQ by 20%) // Total position value: ~$900K let current_positions = vec![ create_test_position("ES.FUT", dec!(126), dec!(5000.0)), // $630K position (~70%) create_test_position("NQ.FUT", dec!(13.5), dec!(20000.0)), // $270K position (~30%) ]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; assert!( result.is_ok(), "Delta order generation should succeed: {:?}", result.err() ); let orders = result.expect("Orders should be present"); // Should have 2 orders (one per symbol with significant delta >5% threshold) assert_eq!( orders.len(), 2, "Should generate 2 delta orders, got {}: {:?}", orders.len(), orders .iter() .map(|o| (o.symbol.as_str(), o.side.to_string())) .collect::>() ); // Find ES order - should be SELL (reduce overweight position) let es_order = orders .iter() .find(|o| o.symbol.as_str() == "ES.FUT") .expect("ES order should exist"); assert_eq!( es_order.side, OrderSide::Sell, "ES order should be SELL to reduce overweight" ); // Find NQ order - should be BUY (increase underweight position) let nq_order = orders .iter() .find(|o| o.symbol.as_str() == "NQ.FUT") .expect("NQ order should exist"); assert_eq!( nq_order.side, OrderSide::Buy, "NQ order should be BUY to increase underweight" ); } #[tokio::test] async fn test_order_size_validation_min_size() { let pool = setup_database().await; let generator = OrderGenerator::new( pool.clone(), 100_000.0, // min_order_size: $100K (high minimum) 1_000_000.0, // max_order_size: $1M (enough for NQ order) ); // Create allocation with small weights that would generate tiny orders let allocation = create_test_allocation(vec![ ("ES.FUT", 0.02), // 2% = $20K (below $100K minimum) ("NQ.FUT", 0.98), // 98% = $980K (above minimum, below maximum) ]); let current_positions = vec![]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; assert!(result.is_ok(), "Should succeed but filter small orders"); let orders = result.expect("Orders should be present"); // ES.FUT order should be filtered out (below minimum) // Only NQ.FUT should remain assert_eq!( orders.len(), 1, "Should only generate 1 order (filtered small order)" ); assert_eq!( orders[0].symbol.as_str(), "NQ.FUT", "Only NQ order should remain" ); cleanup_test_data(&pool).await; } #[tokio::test] async fn test_order_size_validation_max_size() { let pool = setup_database().await; let generator = OrderGenerator::new( pool.clone(), 100.0, // min_order_size: $100 50_000.0, // max_order_size: $50K (low maximum) ); // Create allocation with large weight let allocation = create_test_allocation(vec![ ("ES.FUT", 1.0), // 100% = $1M (way above $50K maximum) ]); let current_positions = vec![]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; // Should error because order exceeds max size and can't be split assert!( result.is_err(), "Should fail when order exceeds max size without splitting" ); match result { Err(OrderError::OrderSizeExceedsMaximum { symbol, size, max_size, }) => { assert_eq!(symbol, "ES.FUT"); assert!(size > max_size); }, _ => panic!("Expected OrderSizeExceedsMaximum error"), } } #[tokio::test] async fn test_order_persistence() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0); // Create allocation let allocation = create_test_allocation(vec![("ES.FUT", 0.50), ("NQ.FUT", 0.50)]); let current_positions = vec![]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; assert!(result.is_ok(), "Order generation should succeed"); let orders = result.expect("Orders should be present"); // Verify orders are persisted in database for order in &orders { let row = sqlx::query_as::< _, ( String, String, String, String, rust_decimal::Decimal, String, String, ), >( r#" SELECT order_id, allocation_id, symbol, side, quantity, order_type, status FROM agent_orders WHERE order_id = $1 "#, ) .bind(order.id.to_string()) .fetch_optional(&pool) .await .expect("Database query should succeed"); assert!( row.is_some(), "Order {} should be persisted in database", order.id ); let row_data = row.expect("Row should exist"); assert_eq!(row_data.0, order.id.to_string()); assert_eq!(row_data.1, allocation.allocation_id); assert_eq!(row_data.2, order.symbol.as_str()); } cleanup_test_data(&pool).await; } #[tokio::test] async fn test_no_orders_when_within_threshold() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 100_000.0); // Create allocation: 50% ES.FUT, 50% NQ.FUT let allocation = create_test_allocation(vec![("ES.FUT", 0.50), ("NQ.FUT", 0.50)]); // Existing positions: 49% ES.FUT, 51% NQ.FUT (within 5% rebalance threshold) let current_positions = vec![ create_test_position("ES.FUT", dec!(98), dec!(5000.0)), // $490K create_test_position("NQ.FUT", dec!(25.5), dec!(20000.0)), // $510K ]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; assert!(result.is_ok(), "Should succeed with no rebalancing needed"); let orders = result.expect("Orders should be present"); // No orders should be generated (deltas within threshold) assert_eq!( orders.len(), 0, "Should not generate orders when within rebalance threshold" ); } #[tokio::test] async fn test_orders_with_new_symbols() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0); // Create allocation with new symbol let allocation = create_test_allocation(vec![ ("ES.FUT", 0.40), ("NQ.FUT", 0.30), ("ZN.FUT", 0.30), // New symbol not in current positions ]); // Existing positions: only ES and NQ let current_positions = vec![ create_test_position("ES.FUT", dec!(80), dec!(5000.0)), create_test_position("NQ.FUT", dec!(15), dec!(20000.0)), ]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; assert!(result.is_ok(), "Should handle new symbols"); let orders = result.expect("Orders should be present"); // Should have ZN order (new position) let zn_order = orders.iter().find(|o| o.symbol.as_str() == "ZN.FUT"); assert!( zn_order.is_some(), "Should generate order for new symbol ZN.FUT" ); let zn_order = zn_order.expect("ZN order should exist"); assert_eq!(zn_order.side, OrderSide::Buy, "New position should be BUY"); } #[tokio::test] async fn test_performance_20_symbols() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 100_000.0); // 5% each = $50K max // Create allocation with 20 symbols let mut weights = vec![]; let symbols = vec![ "ES.FUT", "NQ.FUT", "ZN.FUT", "6E.FUT", "CL.FUT", "GC.FUT", "SI.FUT", "YM.FUT", "RTY.FUT", "ZB.FUT", "ZC.FUT", "ZS.FUT", "ZW.FUT", "NG.FUT", "HO.FUT", "RB.FUT", "6A.FUT", "6B.FUT", "6C.FUT", "6J.FUT", ]; for symbol in &symbols { weights.push((*symbol, 0.05)); // 5% each = 100% } let allocation = create_test_allocation(weights); let current_positions = vec![]; // Measure performance let start = std::time::Instant::now(); let result = generator .generate_orders(&allocation, ¤t_positions) .await; let duration = start.elapsed(); assert!(result.is_ok(), "Order generation should succeed"); let orders = result.expect("Orders should be present"); // Verify we got all 20 orders assert_eq!(orders.len(), 20, "Should generate 20 orders"); // Performance check: <100ms for 20 symbols assert!( duration.as_millis() < 100, "Order generation should take <100ms, took {}ms", duration.as_millis() ); println!( "✅ Generated {} orders in {}ms", orders.len(), duration.as_millis() ); } #[tokio::test] async fn test_invalid_allocation_total_capital_zero() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 100_000.0); let mut allocation = create_test_allocation(vec![("ES.FUT", 1.0)]); allocation.total_capital = Decimal::ZERO; let current_positions = vec![]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; assert!(result.is_err(), "Should fail with zero total capital"); match result { Err(OrderError::InvalidAllocation { reason }) => { assert!(reason.contains("capital")); }, _ => panic!("Expected InvalidAllocation error"), } } #[tokio::test] async fn test_invalid_allocation_weights_exceed_one() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 100_000.0); // Weights sum to 1.5 (invalid) let allocation = create_test_allocation(vec![("ES.FUT", 0.8), ("NQ.FUT", 0.7)]); let current_positions = vec![]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; assert!(result.is_err(), "Should fail when weights exceed 1.0"); match result { Err(OrderError::InvalidAllocation { reason }) => { assert!(reason.contains("weights") || reason.contains("sum")); }, _ => panic!("Expected InvalidAllocation error"), } } #[tokio::test] async fn test_database_error_handling() { // Create generator with invalid database URL let pool = sqlx::PgPool::connect_lazy("postgresql://invalid:invalid@localhost:9999/invalid") .expect("Lazy pool creation should succeed"); let generator = OrderGenerator::new(pool, 100.0, 200_000.0); // Small max to avoid size error let allocation = create_test_allocation(vec![("ES.FUT", 0.10)]); let current_positions = vec![]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; // Should fail with database error assert!( result.is_err(), "Should fail with database connection error" ); match result { Err(OrderError::Database(_)) => { // Expected }, Err(e) => panic!("Expected Database error, got: {:?}", e), Ok(_) => panic!("Should not succeed with invalid database"), } } // ============================================================================ // ML SIGNAL TIMING AND POSITION SIZING TESTS // ============================================================================ #[tokio::test] async fn test_ml_confidence_based_position_sizing() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 1_000_000.0); // $1M max for 80% allocation // High confidence allocation: 80% ES.FUT let high_confidence_allocation = create_test_allocation(vec![ ("ES.FUT", 0.80), // High ML confidence ("NQ.FUT", 0.20), ]); let current_positions = vec![]; let result = generator .generate_orders(&high_confidence_allocation, ¤t_positions) .await; assert!( result.is_ok(), "High confidence order generation should succeed" ); let orders = result.expect("Orders should be present"); // Find ES order - should have large size due to high confidence let es_order = orders .iter() .find(|o| o.symbol.as_str() == "ES.FUT") .expect("ES order should exist"); // Verify order metadata contains allocation info (confidence tracking) assert!( es_order.metadata.get("allocation_id").is_some(), "Order should have allocation_id in metadata" ); assert!( es_order.metadata.get("strategy_id").is_some(), "Order should have strategy_id in metadata" ); // Verify ES order is significantly larger (80% vs 20%) let nq_order = orders .iter() .find(|o| o.symbol.as_str() == "NQ.FUT") .expect("NQ order should exist"); // ES should get 4x more capital than NQ let es_delta: f64 = es_order .metadata .get("delta_usd") .and_then(|v| v.as_f64()) .expect("ES delta should be in metadata"); let nq_delta: f64 = nq_order .metadata .get("delta_usd") .and_then(|v| v.as_f64()) .expect("NQ delta should be in metadata"); assert!( es_delta.abs() > nq_delta.abs() * 3.0, "ES order (80% confidence) should be >3x larger than NQ (20%)" ); cleanup_test_data(&pool).await; } #[tokio::test] async fn test_order_type_selection_market_orders() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0); let allocation = create_test_allocation(vec![("ES.FUT", 0.50), ("NQ.FUT", 0.50)]); let current_positions = vec![]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; assert!(result.is_ok(), "Order generation should succeed"); let orders = result.expect("Orders should be present"); // All orders should be market orders (default behavior for Trading Agent) for order in &orders { assert_eq!( order.order_type, OrderType::Market, "Order type should be Market for {}", order.symbol.as_str() ); assert!( order.price.is_none(), "Market orders should not have a limit price" ); } cleanup_test_data(&pool).await; } #[tokio::test] async fn test_order_client_id_generation() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 1_000_000.0); // $1M max for 100% allocation let allocation = create_test_allocation(vec![("ES.FUT", 1.0)]); let current_positions = vec![]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; assert!(result.is_ok()); let orders = result.expect("Orders should be present"); assert_eq!(orders.len(), 1, "Should generate 1 order"); let order = &orders[0]; // Verify client_order_id is set assert!( order.client_order_id.is_some(), "Client order ID should be set" ); let client_id = order .client_order_id .as_ref() .expect("Client ID should exist"); // Verify format: "agent_" assert!( client_id.starts_with("agent_"), "Client order ID should start with 'agent_', got: {}", client_id ); // Verify it's a valid UUID after "agent_" let uuid_part = &client_id[6..]; // Skip "agent_" assert!( Uuid::parse_str(uuid_part).is_ok(), "Client order ID should contain valid UUID, got: {}", uuid_part ); cleanup_test_data(&pool).await; } // ============================================================================ // ORDER BATCHING AND MULTI-ASSET TESTS // ============================================================================ #[tokio::test] async fn test_portfolio_rebalance_batch_orders() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 200_000.0); // Create allocation for 10-asset portfolio let allocation = create_test_allocation(vec![ ("ES.FUT", 0.15), ("NQ.FUT", 0.15), ("ZN.FUT", 0.10), ("6E.FUT", 0.10), ("CL.FUT", 0.10), ("GC.FUT", 0.10), ("SI.FUT", 0.10), ("YM.FUT", 0.10), ("RTY.FUT", 0.05), ("ZB.FUT", 0.05), ]); let current_positions = vec![]; let start = std::time::Instant::now(); let result = generator .generate_orders(&allocation, ¤t_positions) .await; let duration = start.elapsed(); assert!(result.is_ok(), "Batch order generation should succeed"); let orders = result.expect("Orders should be present"); // Should generate 10 orders (one per symbol) assert_eq!(orders.len(), 10, "Should generate 10 orders for portfolio"); // Verify all orders have unique symbols let mut symbols: Vec<_> = orders.iter().map(|o| o.symbol.as_str()).collect(); symbols.sort(); symbols.dedup(); assert_eq!(symbols.len(), 10, "All orders should have unique symbols"); // Performance check: <50ms for 10 orders assert!( duration.as_millis() < 50, "10-order batch should generate in <50ms, took {}ms", duration.as_millis() ); // Verify all orders stored in database for order in &orders { let row = sqlx::query_as::<_, (String,)>("SELECT order_id FROM agent_orders WHERE order_id = $1") .bind(order.id.to_string()) .fetch_optional(&pool) .await .expect("Database query should succeed"); assert!(row.is_some(), "Order {} should be in database", order.id); } cleanup_test_data(&pool).await; } #[tokio::test] async fn test_partial_portfolio_rebalance() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0); // Target: 50% ES, 30% NQ, 20% ZN let allocation = create_test_allocation(vec![("ES.FUT", 0.50), ("NQ.FUT", 0.30), ("ZN.FUT", 0.20)]); // Current: 60% ES, 20% NQ, 20% ZN (only ES and NQ need rebalancing) let current_positions = vec![ create_test_position("ES.FUT", dec!(120), dec!(5000.0)), // $600K (60%) create_test_position("NQ.FUT", dec!(10), dec!(20000.0)), // $200K (20%) create_test_position("ZN.FUT", dec!(1818), dec!(110.0)), // $200K (20%) ]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; assert!(result.is_ok(), "Partial rebalance should succeed"); let orders = result.expect("Orders should be present"); // Should only rebalance ES and NQ (ZN is already at target) // With 5% threshold ($50K), ZN delta is $0 (no change needed) // ES delta: $500K - $600K = -$100K (SELL, exceeds threshold) // NQ delta: $300K - $200K = +$100K (BUY, exceeds threshold) assert_eq!( orders.len(), 2, "Should only generate orders for ES and NQ, not ZN" ); // Verify ES order is SELL let es_order = orders.iter().find(|o| o.symbol.as_str() == "ES.FUT"); if let Some(order) = es_order { assert_eq!(order.side, OrderSide::Sell, "ES should SELL to reduce"); } // Verify NQ order is BUY let nq_order = orders.iter().find(|o| o.symbol.as_str() == "NQ.FUT"); if let Some(order) = nq_order { assert_eq!(order.side, OrderSide::Buy, "NQ should BUY to increase"); } // Verify no ZN order (within threshold) let zn_order = orders.iter().find(|o| o.symbol.as_str() == "ZN.FUT"); assert!( zn_order.is_none(), "Should not generate order for ZN (within threshold)" ); cleanup_test_data(&pool).await; } // ============================================================================ // ORDER VALIDATION AND RISK CHECKS // ============================================================================ #[tokio::test] async fn test_order_quantity_calculation() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0); let allocation = create_test_allocation(vec![("ES.FUT", 0.10)]); // $100K let current_positions = vec![]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; assert!(result.is_ok()); let orders = result.expect("Orders should be present"); assert_eq!(orders.len(), 1); let order = &orders[0]; // At ~$5000/contract, $100K should be ~20 contracts // Quantity should be reasonable assert!( order.quantity > Quantity::ZERO, "Order quantity should be positive" ); // Verify estimated price is reasonable for ES.FUT let estimated_price: f64 = order .metadata .get("estimated_price") .and_then(|v| v.as_f64()) .expect("Estimated price should be in metadata"); assert!( estimated_price > 4000.0 && estimated_price < 6000.0, "ES.FUT estimated price should be ~$5000, got: {}", estimated_price ); // Verify quantity makes sense: quantity * price ≈ $100K let quantity_f64 = order.quantity.to_f64(); let notional_value = quantity_f64 * estimated_price; assert!( (notional_value - 100_000.0).abs() < 10_000.0, "Notional value should be ~$100K, got: ${:.2}", notional_value ); cleanup_test_data(&pool).await; } #[tokio::test] async fn test_order_metadata_completeness() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0); let allocation = create_test_allocation(vec![("ES.FUT", 0.50)]); let current_positions = vec![]; let result = generator .generate_orders(&allocation, ¤t_positions) .await; assert!(result.is_ok()); let orders = result.expect("Orders should be present"); assert_eq!(orders.len(), 1); let order = &orders[0]; // Verify all required metadata fields are present assert!( order.metadata.get("allocation_id").is_some(), "allocation_id should be in metadata" ); assert!( order.metadata.get("strategy_id").is_some(), "strategy_id should be in metadata" ); assert!( order.metadata.get("delta_usd").is_some(), "delta_usd should be in metadata" ); assert!( order.metadata.get("estimated_price").is_some(), "estimated_price should be in metadata" ); // Verify metadata values are correct let allocation_id = order .metadata .get("allocation_id") .and_then(|v| v.as_str()) .expect("allocation_id should be string"); assert_eq!(allocation_id, allocation.allocation_id); let strategy_id = order .metadata .get("strategy_id") .and_then(|v| v.as_str()) .expect("strategy_id should be string"); assert_eq!(strategy_id, "test_strategy"); cleanup_test_data(&pool).await; } #[tokio::test] async fn test_order_timestamps() { let pool = setup_database().await; let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0); let allocation = create_test_allocation(vec![("ES.FUT", 0.50)]); let current_positions = vec![]; let before = Utc::now(); let result = generator .generate_orders(&allocation, ¤t_positions) .await; let after = Utc::now(); assert!(result.is_ok()); let orders = result.expect("Orders should be present"); assert_eq!(orders.len(), 1); let order = &orders[0]; // Verify created_at is within test execution window let created_at = order.created_at.to_datetime(); assert!( created_at >= before && created_at <= after, "Order created_at should be within test execution window" ); // Verify order status is Created assert_eq!( order.status, OrderStatus::Created, "New orders should have Created status" ); cleanup_test_data(&pool).await; }