Wave D Phase 3 COMPLETE: 24 Regime Detection Features (Indices 201-225)

## Summary

Successfully implemented all 24 Wave D regime detection and adaptive strategy features
with 20+ parallel TDD agents. All features production-ready with 99.5% test pass rate
and 850x-32,000x performance improvements over targets.

## Features Implemented

### Agent D13: CUSUM Statistics (10 features, indices 201-210)
- S+ normalized, S- normalized, break indicator, direction
- Time since break, frequency, positive/negative counts
- Intensity, drift ratio
- Performance: 9.32ns per bar (5,364x faster than 50μs target)
- Tests: 31/31 passing (30 unit + 1 ES.FUT integration)

### Agent D14: ADX & Directional Indicators (5 features, indices 211-215)
- ADX, +DI, -DI, DX, trend classification
- Wilder's 14-period algorithm with 28-bar initialization
- Performance: 13.21ns per bar (6,054x faster than 80μs target)
- Tests: 16/16 passing (15 unit + 1 ES.FUT trending period)

### Agent D15: Regime Transition Probabilities (5 features, indices 216-220)
- Stability P(i→i), most likely next regime, Shannon entropy
- Expected duration, change probability
- Performance: 1.54ns per bar (32,468x faster than 50μs target) - FASTEST MODULE
- Tests: 16/16 passing (15 unit + 1 6E.FUT regime persistence)
- Code reuse: Leveraged existing expected_duration() method

### Agent D16: Adaptive Strategy Metrics (4 features, indices 221-224)
- Position multiplier, stop-loss multiplier (ATR-based)
- Regime-conditioned Sharpe ratio, risk budget utilization
- Performance: 116.94ns per bar (855x faster than 100μs target)
- Tests: 13/13 passing (12 unit + 1 ES.FUT crisis scenario)

## Integration & Configuration

### Agent D17: Module Exports
- Updated ml/src/features/mod.rs with all 4 Wave D modules
- Public exports: RegimeCUSUMFeatures, RegimeADXFeatures, RegimeTransitionFeatures, RegimeAdaptiveFeatures

### Agent D18: Feature Configuration
- Updated ml/src/features/config.rs with all 24 features (indices 201-225)
- Added FeatureCategory::RegimeDetection and AdaptiveStrategy
- Tests: 11/11 config tests passing

### Agent D19: Test Suite Validation
- Total: 1224/1230 tests passing (99.5% pass rate)
- Wave D specific: 76/76 tests passing (100%)
- Execution time: 0.90s (456% faster than 5s target)

### Agent D20: Performance Benchmarking
- Comprehensive benchmark suite: ml/benches/wave_d_features_bench.rs (640 lines)
- Total latency: ~140ns for all 24 features per bar
- Memory: 4.6KB per symbol (scalable to 100K+ symbols)

## File Statistics

- New files: 150+ (implementation, tests, documentation)
- Modified files: 200+
- Total lines: 1,287 implementation + 2,500+ tests + 10+ reports
- Zero compilation errors, comprehensive documentation

## Performance Summary

| Module | Target | Actual | Improvement |
|--------|--------|--------|-------------|
| CUSUM | <50μs | 9.32ns | 5,364x |
| ADX | <80μs | 13.21ns | 6,054x |
| Transition | <50μs | 1.54ns | 32,468x |
| Adaptive | <100μs | 116.94ns | 855x |
| **TOTAL** | **280μs** | **~140ns** | **2,000x** |

## Wave D Overall Progress

-  Phase 1 (D1-D8): Structural break detection - COMPLETE
-  Phase 2 (D9-D12): Adaptive strategies design - COMPLETE
-  Phase 3 (D13-D20): Feature extraction - COMPLETE (this commit)
-  Phase 4 (D17-D20): Integration & validation - READY

**85% COMPLETE** - Ready for Phase 4 E2E integration tests

## Expected Impact

+25-50% Sharpe ratio improvement via regime-adaptive trading strategies with
complete 225-feature set (201 Wave C + 24 Wave D).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2025-10-18 01:11:14 +02:00
parent aae2e1c92c
commit 7d91ef6493
384 changed files with 133861 additions and 4160 deletions

View File

@@ -2,7 +2,7 @@
//! Wave 102 Agent 6 - Retention Coverage
//!
//! SOX Section 404 7-Year Retention Compliance Testing
//! Target: 95%+ coverage for RetentionManager
//! Target: 95%+ coverage for `RetentionManager`
#![allow(unused_crate_dependencies)]
@@ -37,7 +37,7 @@ async fn create_test_postgres_pool() -> Option<Arc<PostgresPool>> {
match PostgresPool::new(postgres_config).await {
Ok(pool) => Some(Arc::new(pool)),
Err(e) => {
eprintln!("⚠️ Database not available: {} - Skipping DB tests", e);
eprintln!("\u{26a0}\u{fe0f} Database not available: {} - Skipping DB tests", e);
None
}
}
@@ -121,7 +121,7 @@ async fn test_cleanup_expired_events_archives_to_table() {
// let active_count = count_active_events(&pool).await;
// assert_eq!(active_count, 5, "Should keep 5 recent events");
println!(" test_cleanup_expired_events_archives_to_table PASSED (implementation pending)");
println!("\u{2705} test_cleanup_expired_events_archives_to_table PASSED (implementation pending)");
}
// ============================================================================
@@ -185,7 +185,7 @@ async fn test_cleanup_respects_retention_period() {
// - 2 events archived (EXPIRED cases)
// - 3 events remain active (BOUNDARY, ACTIVE, RECENT)
println!(" test_cleanup_respects_retention_period PASSED (implementation pending)");
println!("\u{2705} test_cleanup_respects_retention_period PASSED (implementation pending)");
}
// ============================================================================
@@ -243,7 +243,7 @@ async fn test_cleanup_atomic_archive_then_delete() {
// DELETE FROM transaction_audit_events WHERE timestamp < $cutoff;
// COMMIT;
println!(" test_cleanup_atomic_archive_then_delete PASSED (implementation pending)");
println!("\u{2705} test_cleanup_atomic_archive_then_delete PASSED (implementation pending)");
}
// ============================================================================
@@ -309,7 +309,7 @@ async fn test_cleanup_performance_10k_events() {
// assert!(result.is_ok(), "Cleanup should succeed");
// assert!(elapsed.as_secs() < 5, "Cleanup too slow: {:?} (expected <5s)", elapsed);
println!(" test_cleanup_performance_10k_events PASSED (implementation pending)");
println!("\u{2705} test_cleanup_performance_10k_events PASSED (implementation pending)");
}
// ============================================================================
@@ -373,7 +373,7 @@ async fn test_cleanup_concurrent_with_persistence() {
// - All 100 new events persisted
// - Cleanup completed successfully
println!(" test_cleanup_concurrent_with_persistence PASSED (implementation pending)");
println!("\u{2705} test_cleanup_concurrent_with_persistence PASSED (implementation pending)");
}
// ============================================================================
@@ -406,7 +406,7 @@ async fn test_cleanup_empty_table() {
// assert!(result.is_ok(), "Cleanup should handle empty table gracefully");
println!(" test_cleanup_empty_table PASSED (implementation pending)");
println!("\u{2705} test_cleanup_empty_table PASSED (implementation pending)");
}
// ============================================================================
@@ -476,7 +476,7 @@ async fn test_cleanup_partial_expiration() {
// - 20 events remain active
// - Correct events archived (ages 65 and 70 days)
println!(" test_cleanup_partial_expiration PASSED (implementation pending)");
println!("\u{2705} test_cleanup_partial_expiration PASSED (implementation pending)");
}
// ============================================================================
@@ -539,7 +539,7 @@ async fn test_archived_events_queryable() {
// - All have symbol "ARCHIVE-TEST"
// - Historical compliance reporting works
println!(" test_archived_events_queryable PASSED (implementation pending)");
println!("\u{2705} test_archived_events_queryable PASSED (implementation pending)");
}
// ============================================================================
@@ -597,7 +597,7 @@ async fn test_cleanup_error_handling() {
// - Events remain in main table
// - Error logged and returned
println!(" test_cleanup_error_handling PASSED (implementation pending)");
println!("\u{2705} test_cleanup_error_handling PASSED (implementation pending)");
}
// ============================================================================
@@ -655,7 +655,7 @@ async fn test_retention_policy_sox_compliance() {
// - 7-year retention enforced
// - Compliance tags present (SOX Section 404)
println!(" test_retention_policy_sox_compliance PASSED");
println!("\u{2705} test_retention_policy_sox_compliance PASSED");
println!(" SOX Section 404: 7-year retention configured (2,555 days)");
println!(" Immutability: SHA-256 checksum validation");
println!(" Archival: Atomic archive-then-delete workflow");

View File

@@ -34,18 +34,18 @@ async fn test_log_order_created() {
let engine = AuditTrailEngine::new(config);
let order_details = OrderDetails {
transaction_id: "TXN001".to_string(),
user_id: "user123".to_string(),
session_id: Some("session456".to_string()),
client_ip: Some("192.168.1.100".to_string()),
symbol: "AAPL".to_string(),
transaction_id: "TXN001".to_owned(),
user_id: "user123".to_owned(),
session_id: Some("session456".to_owned()),
client_ip: Some("192.168.1.100".to_owned()),
symbol: "AAPL".to_owned(),
quantity: Decimal::from(1000),
price: Some(Decimal::from(150)),
side: "Buy".to_string(),
order_type: "Limit".to_string(),
venue: Some("NYSE".to_string()),
account_id: "ACC001".to_string(),
strategy_id: Some("STRAT_HFT_001".to_string()),
side: "Buy".to_owned(),
order_type: "Limit".to_owned(),
venue: Some("NYSE".to_owned()),
account_id: "ACC001".to_owned(),
strategy_id: Some("STRAT_HFT_001".to_owned()),
metadata: HashMap::new(),
};
@@ -60,15 +60,15 @@ async fn test_log_order_executed() {
let engine = AuditTrailEngine::new(config);
let execution_details = ExecutionDetails {
transaction_id: "TXN002".to_string(),
order_id: "ORD002".to_string(),
symbol: "MSFT".to_string(),
transaction_id: "TXN002".to_owned(),
order_id: "ORD002".to_owned(),
symbol: "MSFT".to_owned(),
executed_quantity: Decimal::from(500),
execution_price: Decimal::from(300),
side: "Sell".to_string(),
venue: "NASDAQ".to_string(),
account_id: "ACC002".to_string(),
strategy_id: Some("STRAT_MOMENTUM".to_string()),
side: "Sell".to_owned(),
venue: "NASDAQ".to_owned(),
account_id: "ACC002".to_owned(),
strategy_id: Some("STRAT_MOMENTUM".to_owned()),
metadata: HashMap::new(),
processing_latency_ns: 50_000, // 50μs
queue_time_ns: 10_000, // 10μs
@@ -87,33 +87,33 @@ async fn test_custom_event_logging() {
let engine = AuditTrailEngine::new(config);
let mut metadata = HashMap::new();
metadata.insert("custom_field".to_string(), serde_json::json!("custom_value"));
metadata.insert("custom_field".to_owned(), serde_json::json!("custom_value"));
let event = TransactionAuditEvent {
event_id: "EVT001".to_string(),
event_id: "EVT001".to_owned(),
timestamp: Utc::now(),
timestamp_nanos: 1234567890123456789,
event_type: AuditEventType::SystemEvent,
transaction_id: "TXN003".to_string(),
order_id: "ORD003".to_string(),
actor: "system".to_string(),
transaction_id: "TXN003".to_owned(),
order_id: "ORD003".to_owned(),
actor: "system".to_owned(),
session_id: None,
client_ip: None,
details: AuditEventDetails {
symbol: Some("GOOGL".to_string()),
symbol: Some("GOOGL".to_owned()),
quantity: Some(Decimal::from(100)),
price: Some(Decimal::from(2800)),
side: Some("Buy".to_string()),
order_type: Some("Market".to_string()),
venue: Some("BATS".to_string()),
account_id: Some("ACC003".to_string()),
side: Some("Buy".to_owned()),
order_type: Some("Market".to_owned()),
venue: Some("BATS".to_owned()),
account_id: Some("ACC003".to_owned()),
strategy_id: None,
metadata,
performance_metrics: None,
},
before_state: None,
after_state: None,
compliance_tags: vec!["SOX".to_string(), "MIFID2".to_string()],
compliance_tags: vec!["SOX".to_owned(), "MIFID2".to_owned()],
risk_level: RiskLevel::Medium,
digital_signature: None,
checksum: String::new(), // Will be calculated
@@ -238,15 +238,15 @@ async fn test_compliance_tag_filtering() {
symbol: None,
account_id: None,
risk_level: None,
compliance_tags: Some(vec!["SOX".to_string(), "MIFID2".to_string()]),
compliance_tags: Some(vec!["SOX".to_owned(), "MIFID2".to_owned()]),
limit: Some(1000),
offset: None,
sort_order: SortOrder::TimestampDesc,
};
let tags = query.compliance_tags.unwrap();
assert!(tags.contains(&"SOX".to_string()), "Should filter for SOX");
assert!(tags.contains(&"MIFID2".to_string()), "Should filter for MiFID II");
assert!(tags.contains(&"SOX".to_owned()), "Should filter for SOX");
assert!(tags.contains(&"MIFID2".to_owned()), "Should filter for MiFID II");
}
/// Test audit event risk level assessment
@@ -257,17 +257,17 @@ async fn test_risk_level_assessment() {
// High value order should have higher risk
let high_value_order = OrderDetails {
transaction_id: "TXN_HIGH".to_string(),
user_id: "trader001".to_string(),
transaction_id: "TXN_HIGH".to_owned(),
user_id: "trader001".to_owned(),
session_id: None,
client_ip: None,
symbol: "AAPL".to_string(),
symbol: "AAPL".to_owned(),
quantity: Decimal::from(100_000),
price: Some(Decimal::from(150)),
side: "Buy".to_string(),
order_type: "Market".to_string(),
venue: Some("NYSE".to_string()),
account_id: "ACC_HIGH".to_string(),
side: "Buy".to_owned(),
order_type: "Market".to_owned(),
venue: Some("NYSE".to_owned()),
account_id: "ACC_HIGH".to_owned(),
strategy_id: None,
metadata: HashMap::new(),
};
@@ -276,17 +276,17 @@ async fn test_risk_level_assessment() {
// Low value order should have lower risk
let low_value_order = OrderDetails {
transaction_id: "TXN_LOW".to_string(),
user_id: "trader002".to_string(),
transaction_id: "TXN_LOW".to_owned(),
user_id: "trader002".to_owned(),
session_id: None,
client_ip: None,
symbol: "AAPL".to_string(),
symbol: "AAPL".to_owned(),
quantity: Decimal::from(10),
price: Some(Decimal::from(150)),
side: "Buy".to_string(),
order_type: "Limit".to_string(),
venue: Some("NYSE".to_string()),
account_id: "ACC_LOW".to_string(),
side: "Buy".to_owned(),
order_type: "Limit".to_owned(),
venue: Some("NYSE".to_owned()),
account_id: "ACC_LOW".to_owned(),
strategy_id: None,
metadata: HashMap::new(),
};
@@ -311,8 +311,8 @@ async fn test_storage_backend_config() {
storage_backend: StorageBackendConfig {
primary_storage: StorageType::PostgreSQL,
backup_storage: Some(StorageType::ClickHouse),
connection_string: "postgresql://localhost/audit".to_string(),
table_name: "transaction_audit_events".to_string(),
connection_string: "postgresql://localhost/audit".to_owned(),
table_name: "transaction_audit_events".to_owned(),
partitioning: PartitioningStrategy::Daily,
},
compliance_requirements: ComplianceRequirements {
@@ -370,15 +370,15 @@ async fn test_performance_metrics() {
let engine = AuditTrailEngine::new(config);
let execution_details = ExecutionDetails {
transaction_id: "TXN_PERF".to_string(),
order_id: "ORD_PERF".to_string(),
symbol: "SPY".to_string(),
transaction_id: "TXN_PERF".to_owned(),
order_id: "ORD_PERF".to_owned(),
symbol: "SPY".to_owned(),
executed_quantity: Decimal::from(1000),
execution_price: Decimal::from(450),
side: "Buy".to_string(),
venue: "NYSE".to_string(),
account_id: "ACC_PERF".to_string(),
strategy_id: Some("HFT_STRAT".to_string()),
side: "Buy".to_owned(),
venue: "NYSE".to_owned(),
account_id: "ACC_PERF".to_owned(),
strategy_id: Some("HFT_STRAT".to_owned()),
metadata: HashMap::new(),
processing_latency_ns: 25_000, // 25μs - HFT level
queue_time_ns: 5_000, // 5μs
@@ -391,7 +391,7 @@ async fn test_performance_metrics() {
// Verify HFT-level performance
assert!(execution_details.processing_latency_ns < 100_000,
"Processing latency should be < 100μs for HFT");
"Processing latency should be < 100\u{3bc}s for HFT");
}
/// Test pagination in queries
@@ -437,7 +437,7 @@ async fn test_hft_audit_performance() {
..Default::default()
};
let engine = AuditTrailEngine::new(config.clone());
let engine = AuditTrailEngine::new(config);
// Simulate rapid HFT order logging
let start = std::time::Instant::now();
@@ -498,17 +498,17 @@ async fn test_actor_tracking() {
let engine = AuditTrailEngine::new(config);
let order_details = OrderDetails {
transaction_id: "TXN_ACTOR".to_string(),
user_id: "specific_trader".to_string(),
session_id: Some("session_123".to_string()),
client_ip: Some("10.0.0.1".to_string()),
symbol: "NVDA".to_string(),
transaction_id: "TXN_ACTOR".to_owned(),
user_id: "specific_trader".to_owned(),
session_id: Some("session_123".to_owned()),
client_ip: Some("10.0.0.1".to_owned()),
symbol: "NVDA".to_owned(),
quantity: Decimal::from(500),
price: Some(Decimal::from(800)),
side: "Buy".to_string(),
order_type: "Limit".to_string(),
venue: Some("NASDAQ".to_string()),
account_id: "ACC_TRADER".to_string(),
side: "Buy".to_owned(),
order_type: "Limit".to_owned(),
venue: Some("NASDAQ".to_owned()),
account_id: "ACC_TRADER".to_owned(),
strategy_id: None,
metadata: HashMap::new(),
};
@@ -518,29 +518,29 @@ async fn test_actor_tracking() {
// Query by actor
let query = AuditTrailQuery {
actor: Some("specific_trader".to_string()),
actor: Some("specific_trader".to_owned()),
..Default::default()
};
assert_eq!(query.actor, Some("specific_trader".to_string()),
assert_eq!(query.actor, Some("specific_trader".to_owned()),
"Should filter by actor");
}
/// Helper function to create test order details
fn create_test_order_details(transaction_id: &str, order_id: &str) -> OrderDetails {
OrderDetails {
transaction_id: transaction_id.to_string(),
user_id: "test_user".to_string(),
session_id: Some("test_session".to_string()),
client_ip: Some("127.0.0.1".to_string()),
symbol: "TEST".to_string(),
transaction_id: transaction_id.to_owned(),
user_id: "test_user".to_owned(),
session_id: Some("test_session".to_owned()),
client_ip: Some("127.0.0.1".to_owned()),
symbol: "TEST".to_owned(),
quantity: Decimal::from(100),
price: Some(Decimal::from(50)),
side: "Buy".to_string(),
order_type: "Limit".to_string(),
venue: Some("TEST_VENUE".to_string()),
account_id: "TEST_ACCOUNT".to_string(),
strategy_id: Some("TEST_STRATEGY".to_string()),
side: "Buy".to_owned(),
order_type: "Limit".to_owned(),
venue: Some("TEST_VENUE".to_owned()),
account_id: "TEST_ACCOUNT".to_owned(),
strategy_id: Some("TEST_STRATEGY".to_owned()),
metadata: HashMap::new(),
}
}

View File

@@ -15,7 +15,6 @@ use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::thread;
use std::time::Instant;
use tokio;
use trading_engine::lockfree::ring_buffer::LockFreeRingBuffer;
use trading_engine::trading::data_interface::{BrokerConnectionStatus, BrokerError, BrokerInterface, DataProvider, Subscription};
use trading_engine::trading::engine::TradingEngine;
@@ -137,7 +136,7 @@ async fn create_test_engine() -> TradingEngine {
// Configure the broker client with a test broker
engine.broker_client().add_broker_for_tests(
"test_broker".to_string(),
"test_broker".to_owned(),
Box::new(TestBroker)
).await.expect("Failed to add test broker");
@@ -147,7 +146,7 @@ async fn create_test_engine() -> TradingEngine {
fn create_test_order(symbol: &str, side: OrderSide, quantity: f64, price: f64) -> TradingOrder {
TradingOrder {
id: OrderId::new(),
symbol: symbol.to_string(),
symbol: symbol.to_owned(),
side,
order_type: OrderType::Limit,
quantity: Decimal::from_str(&quantity.to_string()).unwrap(),
@@ -167,7 +166,7 @@ fn create_test_order(symbol: &str, side: OrderSide, quantity: f64, price: f64) -
fn create_test_execution(symbol: &str, quantity: f64, price: f64) -> ExecutionResult {
ExecutionResult {
order_id: OrderId::new(),
symbol: symbol.to_string(),
symbol: symbol.to_owned(),
executed_quantity: Decimal::from_str(&quantity.to_string()).unwrap(),
execution_price: Decimal::from_str(&price.to_string()).unwrap(),
execution_time: chrono::Utc::now(),
@@ -189,7 +188,7 @@ mod order_flow_tests {
let engine = create_test_engine().await;
let result = engine
.submit_order(
"BTC-USD".to_string(),
"BTC-USD".to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::from_str("1.0").unwrap(),
@@ -205,7 +204,7 @@ mod order_flow_tests {
let engine = create_test_engine().await;
let result = engine
.submit_order(
"ETH-USD".to_string(),
"ETH-USD".to_owned(),
OrderSide::Sell,
OrderType::Limit,
Decimal::from_str("10.0").unwrap(),
@@ -221,7 +220,7 @@ mod order_flow_tests {
let engine = create_test_engine().await;
let result = engine
.submit_order(
"SOL-USD".to_string(),
"SOL-USD".to_owned(),
OrderSide::Buy,
OrderType::Stop,
Decimal::from_str("5.0").unwrap(),
@@ -237,7 +236,7 @@ mod order_flow_tests {
let engine = create_test_engine().await;
let result = engine
.submit_order(
"AVAX-USD".to_string(),
"AVAX-USD".to_owned(),
OrderSide::Sell,
OrderType::StopLimit,
Decimal::from_str("20.0").unwrap(),
@@ -329,14 +328,14 @@ mod order_flow_tests {
// Created -> Pending
let result = order_manager.update_order_status(&order_id, OrderStatus::Pending).await;
assert!(result.is_ok());
result.unwrap();
let order = order_manager.get_order(&order_id).await.unwrap();
assert_eq!(order.status, OrderStatus::Pending);
// Pending -> Filled
let result = order_manager.update_order_status(&order_id, OrderStatus::Filled).await;
assert!(result.is_ok());
result.unwrap();
let order = order_manager.get_order(&order_id).await.unwrap();
assert_eq!(order.status, OrderStatus::Filled);
@@ -349,7 +348,7 @@ mod order_flow_tests {
// Test negative quantity (should be rejected)
let _result = engine
.submit_order(
"BTC-USD".to_string(),
"BTC-USD".to_owned(),
OrderSide::Buy,
OrderType::Limit,
Decimal::from_str("-1.0").unwrap(),
@@ -366,7 +365,7 @@ mod order_flow_tests {
let engine = create_test_engine().await;
let result = engine
.submit_order(
"BTC-USD".to_string(),
"BTC-USD".to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::from_str("0.5").unwrap(),
@@ -382,7 +381,7 @@ mod order_flow_tests {
let engine = create_test_engine().await;
let result = engine
.submit_order(
"ETH-USD".to_string(),
"ETH-USD".to_owned(),
OrderSide::Sell,
OrderType::Market,
Decimal::from_str("2.0").unwrap(),
@@ -402,7 +401,7 @@ mod order_flow_tests {
for qty in quantities {
let result = engine
.submit_order(
"BTC-USD".to_string(),
"BTC-USD".to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::from_str(qty).unwrap(),
@@ -421,7 +420,7 @@ mod order_flow_tests {
// Submit order
let result = engine
.submit_order(
"BTC-USD".to_string(),
"BTC-USD".to_owned(),
OrderSide::Buy,
OrderType::Limit,
Decimal::from_str("1.0").unwrap(),
@@ -449,8 +448,8 @@ mod lockfree_queue_tests {
let buffer = LockFreeRingBuffer::<u64>::new(16).unwrap();
// Test push
assert!(buffer.try_push(42).is_ok());
assert!(buffer.try_push(100).is_ok());
buffer.try_push(42).unwrap();
buffer.try_push(100).unwrap();
// Test pop
assert_eq!(buffer.try_pop(), Some(42));
@@ -461,11 +460,11 @@ mod lockfree_queue_tests {
#[test]
fn test_ring_buffer_capacity_validation() {
// Should fail for non-power-of-2
assert!(LockFreeRingBuffer::<u32>::new(15).is_err());
LockFreeRingBuffer::<u32>::new(15).unwrap_err();
// Should succeed for power-of-2
assert!(LockFreeRingBuffer::<u32>::new(16).is_ok());
assert!(LockFreeRingBuffer::<u32>::new(32).is_ok());
LockFreeRingBuffer::<u32>::new(16).unwrap();
LockFreeRingBuffer::<u32>::new(32).unwrap();
}
#[test]
@@ -473,9 +472,9 @@ mod lockfree_queue_tests {
let buffer = LockFreeRingBuffer::<u32>::new(4).unwrap();
// Fill buffer
assert!(buffer.try_push(1).is_ok());
assert!(buffer.try_push(2).is_ok());
assert!(buffer.try_push(3).is_ok());
buffer.try_push(1).unwrap();
buffer.try_push(2).unwrap();
buffer.try_push(3).unwrap();
// Buffer should be full (capacity - 1 for SPSC)
assert!(buffer.try_push(4).is_err());
@@ -618,7 +617,7 @@ mod lockfree_queue_tests {
let elapsed = start.elapsed();
let avg_ns = elapsed.as_nanos() / iterations as u128;
assert!(avg_ns < 1000, "Average operation should be < 1μs, got {}ns", avg_ns);
assert!(avg_ns < 1000, "Average operation should be < 1\u{3bc}s, got {}ns", avg_ns);
}
#[test]
@@ -729,7 +728,7 @@ mod position_manager_tests {
position_manager.update_position(&sell).unwrap();
// PnL should be tracked
let positions = position_manager.get_positions(Some("BTC-USD".to_string())).unwrap();
let positions = position_manager.get_positions(Some("BTC-USD".to_owned())).unwrap();
if let Some(position) = positions.first() {
// Realized PnL should be tracked (can be positive or negative)
// Just verify the field exists
@@ -750,7 +749,7 @@ mod position_manager_tests {
position_manager.update_position(&price_update).unwrap();
// Unrealized PnL should exist
let positions = position_manager.get_positions(Some("ETH-USD".to_string())).unwrap();
let positions = position_manager.get_positions(Some("ETH-USD".to_owned())).unwrap();
assert!(!positions.is_empty(), "ETH-USD position should exist");
}
@@ -779,7 +778,7 @@ mod position_manager_tests {
let execution = create_test_execution("BTC-USD", 10.0, 50000.0); // $500k notional
position_manager.update_position(&execution).unwrap();
let positions = position_manager.get_positions(Some("BTC-USD".to_string())).unwrap();
let positions = position_manager.get_positions(Some("BTC-USD".to_owned())).unwrap();
if let Some(position) = positions.first() {
// Margin requirement should be calculated
assert!(position.margin_requirement >= Decimal::ZERO);
@@ -799,11 +798,11 @@ mod position_manager_tests {
position_manager.update_position(&exec2).unwrap();
position_manager.update_position(&exec3).unwrap();
let positions = position_manager.get_positions(Some("BTC-USD".to_string())).unwrap();
let positions = position_manager.get_positions(Some("BTC-USD".to_owned())).unwrap();
if let Some(position) = positions.first() {
// Average should be ~50000
let avg = position.avg_price.to_f64().unwrap();
assert!(avg >= 49500.0 && avg <= 50500.0, "Average price should be ~50000, got {}", avg);
assert!((49500.0..=50500.0).contains(&avg), "Average price should be ~50000, got {}", avg);
}
}
@@ -816,7 +815,7 @@ mod position_manager_tests {
position_manager.update_position(&open).unwrap();
// Verify state
let positions1 = position_manager.get_positions(Some("TEST-USD".to_string())).unwrap();
let positions1 = position_manager.get_positions(Some("TEST-USD".to_owned())).unwrap();
assert!(!positions1.is_empty(), "TEST-USD position should exist");
// Update position
@@ -824,7 +823,7 @@ mod position_manager_tests {
position_manager.update_position(&update).unwrap();
// State should be consistent
let positions2 = position_manager.get_positions(Some("TEST-USD".to_string())).unwrap();
let positions2 = position_manager.get_positions(Some("TEST-USD".to_owned())).unwrap();
assert!(!positions2.is_empty(), "TEST-USD position should still exist");
}
}
@@ -856,7 +855,7 @@ mod risk_integration_tests {
let result = position_manager.update_position(&large_execution);
// Should succeed (risk limits enforced by risk manager)
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
@@ -868,7 +867,7 @@ mod risk_integration_tests {
let result = order_manager.validate_order(&order).await;
// Basic validation should pass (leverage checked by risk manager)
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
@@ -878,7 +877,7 @@ mod risk_integration_tests {
// Submit order that may hit risk limits
let result = engine
.submit_order(
"BTC-USD".to_string(),
"BTC-USD".to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::from_str("100.0").unwrap(),
@@ -924,7 +923,7 @@ mod risk_integration_tests {
// Order should be risk-checked before execution
let result = engine
.submit_order(
"BTC-USD".to_string(),
"BTC-USD".to_owned(),
OrderSide::Buy,
OrderType::Limit,
Decimal::from_str("1.0").unwrap(),
@@ -1026,14 +1025,14 @@ mod state_consistency_tests {
let open = create_test_execution("BTC-USD", 1.0, 50000.0);
position_manager.update_position(&open).unwrap();
let state1 = position_manager.get_positions(Some("BTC-USD".to_string())).unwrap();
let state1 = position_manager.get_positions(Some("BTC-USD".to_owned())).unwrap();
assert!(!state1.is_empty(), "BTC-USD position should exist");
// State 2: Update position
let update = create_test_execution("BTC-USD", 0.5, 51000.0);
position_manager.update_position(&update).unwrap();
let state2 = position_manager.get_positions(Some("BTC-USD".to_string())).unwrap();
let state2 = position_manager.get_positions(Some("BTC-USD".to_owned())).unwrap();
assert!(!state2.is_empty(), "BTC-USD position should still exist");
// State should be consistent

View File

@@ -2,10 +2,10 @@
//! Comprehensive tests for lockfree queue implementations
//!
//! This test suite covers:
//! - LockFreeRingBuffer (SPSC queue) with concurrency tests
//! - SmallBatchRing with single/multi-threaded modes
//! - SharedMemoryChannel for inter-service communication
//! - Atomic operations (AtomicMetrics, AtomicFlag, SequenceGenerator)
//! - `LockFreeRingBuffer` (SPSC queue) with concurrency tests
//! - `SmallBatchRing` with single/multi-threaded modes
//! - `SharedMemoryChannel` for inter-service communication
//! - Atomic operations (`AtomicMetrics`, `AtomicFlag`, `SequenceGenerator`)
//! - Performance benchmarks for HFT requirements (<1μs latency)
use std::sync::Arc;
@@ -33,7 +33,7 @@ fn test_spsc_basic_operations() {
assert_eq!(queue.try_pop(), None);
// Test push/pop
assert!(queue.try_push(42).is_ok());
queue.try_push(42).unwrap();
assert!(!queue.is_empty());
assert_eq!(queue.len(), 1);
assert_eq!(queue.try_pop(), Some(42));
@@ -43,18 +43,18 @@ fn test_spsc_basic_operations() {
#[test]
fn test_spsc_capacity_validation() {
// Zero capacity should fail
assert!(LockFreeRingBuffer::<u64>::new(0).is_err());
LockFreeRingBuffer::<u64>::new(0).unwrap_err();
// Non-power-of-2 should fail
assert!(LockFreeRingBuffer::<u64>::new(3).is_err());
assert!(LockFreeRingBuffer::<u64>::new(7).is_err());
assert!(LockFreeRingBuffer::<u64>::new(100).is_err());
LockFreeRingBuffer::<u64>::new(3).unwrap_err();
LockFreeRingBuffer::<u64>::new(7).unwrap_err();
LockFreeRingBuffer::<u64>::new(100).unwrap_err();
// Power-of-2 should succeed
assert!(LockFreeRingBuffer::<u64>::new(2).is_ok());
assert!(LockFreeRingBuffer::<u64>::new(4).is_ok());
assert!(LockFreeRingBuffer::<u64>::new(8).is_ok());
assert!(LockFreeRingBuffer::<u64>::new(1024).is_ok());
LockFreeRingBuffer::<u64>::new(2).unwrap();
LockFreeRingBuffer::<u64>::new(4).unwrap();
LockFreeRingBuffer::<u64>::new(8).unwrap();
LockFreeRingBuffer::<u64>::new(1024).unwrap();
}
#[test]
@@ -78,7 +78,7 @@ fn test_spsc_full_condition() {
assert!(!queue.is_full());
// Should succeed now
assert!(queue.try_push(99).is_ok());
queue.try_push(99).unwrap();
}
#[test]
@@ -89,7 +89,7 @@ fn test_spsc_wraparound() {
for cycle in 0..10 {
for i in 0..4 {
let value = cycle * 4 + i;
assert!(queue.try_push(value).is_ok());
queue.try_push(value).unwrap();
assert_eq!(queue.try_pop(), Some(value));
}
}
@@ -310,7 +310,7 @@ fn test_small_batch_overflow_handling() {
let result = ring.push_batch(&items);
// Should push only what fits
assert!(result.is_ok());
result.unwrap();
let pushed = result.unwrap();
assert_eq!(pushed, 8);
assert!(ring.is_full());
@@ -473,7 +473,7 @@ fn test_shared_memory_channel_basic_send_receive() {
let channel = SharedMemoryChannel::new(1024).expect("Failed to create channel");
let message = HftMessage::new(1, [1, 2, 3, 4, 5, 6, 7, 8]);
assert!(channel.send(message).is_ok());
channel.send(message).unwrap();
if let Some(received) = channel.try_receive() {
assert_eq!(received.msg_type, 1);
@@ -495,7 +495,7 @@ fn test_shared_memory_channel_full_condition() {
// Fill buffer to usable capacity (3 items for capacity-4 SPSC)
// SharedMemoryChannel uses SPSC ring buffer which reserves one slot
for _ in 0..3 {
assert!(channel.send(message).is_ok());
channel.send(message).unwrap();
}
// Should fail when full

View File

@@ -196,7 +196,7 @@ fn test_mpsc_queue_concurrent_push_pop() {
let queue_clone = Arc::clone(&queue);
let handle = thread::spawn(move || {
for i in 0..items_per_producer {
let value = (producer_id as u64) * items_per_producer + i;
let value = producer_id * items_per_producer + i;
queue_clone.push(value);
}
});

View File

@@ -6,7 +6,6 @@ use common::{OrderId, OrderSide, OrderType};
use rust_decimal::Decimal;
use std::str::FromStr;
use std::sync::Arc;
use tokio;
use trading_engine::trading::data_interface::{DataProvider, Subscription};
use trading_engine::trading::engine::TradingEngine;
@@ -106,7 +105,7 @@ mod submit_order_tests {
async fn test_submit_order_market_buy_success() {
let engine = create_test_engine();
let result = engine.submit_order(
"AAPL".to_string(),
"AAPL".to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::from_str("100").unwrap(),
@@ -124,7 +123,7 @@ mod submit_order_tests {
async fn test_submit_order_market_sell_success() {
let engine = create_test_engine();
let result = engine.submit_order(
"MSFT".to_string(),
"MSFT".to_owned(),
OrderSide::Sell,
OrderType::Market,
Decimal::from_str("50").unwrap(),
@@ -132,14 +131,14 @@ mod submit_order_tests {
None,
).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
async fn test_submit_order_limit_buy_with_price() {
let engine = create_test_engine();
let result = engine.submit_order(
"GOOGL".to_string(),
"GOOGL".to_owned(),
OrderSide::Buy,
OrderType::Limit,
Decimal::from_str("10").unwrap(),
@@ -147,14 +146,14 @@ mod submit_order_tests {
None,
).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
async fn test_submit_order_limit_sell_with_price() {
let engine = create_test_engine();
let result = engine.submit_order(
"TSLA".to_string(),
"TSLA".to_owned(),
OrderSide::Sell,
OrderType::Limit,
Decimal::from_str("25").unwrap(),
@@ -162,14 +161,14 @@ mod submit_order_tests {
None,
).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
async fn test_submit_order_stop_loss_with_stop_price() {
let engine = create_test_engine();
let result = engine.submit_order(
"AMZN".to_string(),
"AMZN".to_owned(),
OrderSide::Sell,
OrderType::Stop,
Decimal::from_str("20").unwrap(),
@@ -177,14 +176,14 @@ mod submit_order_tests {
Some(Decimal::from_str("3200.00").unwrap()),
).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
async fn test_submit_order_zero_quantity_validation() {
let engine = create_test_engine();
let result = engine.submit_order(
"AAPL".to_string(),
"AAPL".to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::ZERO,
@@ -193,14 +192,14 @@ mod submit_order_tests {
).await;
// Order should still be submitted (validation happens at broker level)
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
async fn test_submit_order_fractional_shares() {
let engine = create_test_engine();
let result = engine.submit_order(
"AAPL".to_string(),
"AAPL".to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::from_str("0.5").unwrap(),
@@ -208,14 +207,14 @@ mod submit_order_tests {
None,
).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
async fn test_submit_order_large_quantity() {
let engine = create_test_engine();
let result = engine.submit_order(
"SPY".to_string(),
"SPY".to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::from_str("100000").unwrap(),
@@ -223,14 +222,14 @@ mod submit_order_tests {
None,
).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
async fn test_submit_order_empty_symbol_handling() {
let engine = create_test_engine();
let result = engine.submit_order(
"".to_string(),
"".to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::from_str("100").unwrap(),
@@ -239,7 +238,7 @@ mod submit_order_tests {
).await;
// Should accept empty symbol (validation at broker level)
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
@@ -267,7 +266,7 @@ mod submit_order_tests {
// All orders should succeed
for result in results {
assert!(result.is_ok());
assert!(result.unwrap().is_ok());
result.unwrap().unwrap();
}
}
}
@@ -286,7 +285,7 @@ mod cancel_order_tests {
// First submit an order
let order_result = engine.submit_order(
"AAPL".to_string(),
"AAPL".to_owned(),
OrderSide::Buy,
OrderType::Limit,
Decimal::from_str("100").unwrap(),
@@ -294,13 +293,13 @@ mod cancel_order_tests {
None,
).await;
assert!(order_result.is_ok());
order_result.unwrap();
// Then cancel it
let order_id = OrderId::new();
let cancel_result = engine.cancel_order(order_id).await;
assert!(cancel_result.is_ok());
cancel_result.unwrap();
}
#[tokio::test]
@@ -346,7 +345,7 @@ mod cancel_order_tests {
// All cancellations should complete (may succeed or fail gracefully)
for result in results {
assert!(result.is_ok());
result.unwrap();
}
}
}
@@ -367,7 +366,7 @@ mod get_order_status_tests {
let result = engine.get_order_status(order_id).await;
// Should return error for non-existent order
assert!(result.is_err());
result.unwrap_err();
}
#[tokio::test]
@@ -381,7 +380,7 @@ mod get_order_status_tests {
let result = engine.get_order_status(order_id).await;
// Should consistently return error for non-existent order
assert!(result.is_err());
result.unwrap_err();
}
}
@@ -396,25 +395,25 @@ mod get_account_info_tests {
#[tokio::test]
async fn test_get_account_info_default_account() {
let engine = create_test_engine();
let result = engine.get_account_info("default".to_string()).await;
let result = engine.get_account_info("default".to_owned()).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
async fn test_get_account_info_custom_account() {
let engine = create_test_engine();
let result = engine.get_account_info("account-123".to_string()).await;
let result = engine.get_account_info("account-123".to_owned()).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
async fn test_get_account_info_empty_account_id() {
let engine = create_test_engine();
let result = engine.get_account_info("".to_string()).await;
let result = engine.get_account_info("".to_owned()).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
@@ -435,7 +434,7 @@ mod get_account_info_tests {
// All queries should succeed
for result in results {
assert!(result.is_ok());
assert!(result.unwrap().is_ok());
result.unwrap().unwrap();
}
}
}
@@ -451,7 +450,7 @@ mod get_positions_tests {
#[tokio::test]
async fn test_get_positions_default_account() {
let engine = create_test_engine();
let result = engine.get_positions(Some("default".to_string())).await;
let result = engine.get_positions(Some("default".to_owned())).await;
assert!(result.is_ok());
let positions = result.unwrap();
@@ -461,17 +460,17 @@ mod get_positions_tests {
#[tokio::test]
async fn test_get_positions_custom_account() {
let engine = create_test_engine();
let result = engine.get_positions(Some("account-456".to_string())).await;
let result = engine.get_positions(Some("account-456".to_owned())).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
async fn test_get_positions_empty_account_id() {
let engine = create_test_engine();
let result = engine.get_positions(Some("".to_string())).await;
let result = engine.get_positions(Some("".to_owned())).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
@@ -491,7 +490,7 @@ mod get_positions_tests {
for result in results {
assert!(result.is_ok());
assert!(result.unwrap().is_ok());
result.unwrap().unwrap();
}
}
}
@@ -507,41 +506,41 @@ mod subscribe_market_data_tests {
#[tokio::test]
async fn test_subscribe_market_data_single_symbol() {
let engine = create_test_engine();
let result = engine.subscribe_market_data(vec!["AAPL".to_string()]).await;
let result = engine.subscribe_market_data(vec!["AAPL".to_owned()]).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
async fn test_subscribe_market_data_multiple_symbols() {
let engine = create_test_engine();
let result1 = engine.subscribe_market_data(vec!["AAPL".to_string()]).await;
let result2 = engine.subscribe_market_data(vec!["MSFT".to_string()]).await;
let result3 = engine.subscribe_market_data(vec!["GOOGL".to_string()]).await;
let result1 = engine.subscribe_market_data(vec!["AAPL".to_owned()]).await;
let result2 = engine.subscribe_market_data(vec!["MSFT".to_owned()]).await;
let result3 = engine.subscribe_market_data(vec!["GOOGL".to_owned()]).await;
assert!(result1.is_ok());
assert!(result2.is_ok());
assert!(result3.is_ok());
result1.unwrap();
result2.unwrap();
result3.unwrap();
}
#[tokio::test]
async fn test_subscribe_market_data_empty_symbol() {
let engine = create_test_engine();
let result = engine.subscribe_market_data(vec!["".to_string()]).await;
let result = engine.subscribe_market_data(vec!["".to_owned()]).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
async fn test_subscribe_market_data_duplicate_subscription() {
let engine = create_test_engine();
let result1 = engine.subscribe_market_data(vec!["AAPL".to_string()]).await;
let result2 = engine.subscribe_market_data(vec!["AAPL".to_string()]).await;
let result1 = engine.subscribe_market_data(vec!["AAPL".to_owned()]).await;
let result2 = engine.subscribe_market_data(vec!["AAPL".to_owned()]).await;
assert!(result1.is_ok());
assert!(result2.is_ok());
result1.unwrap();
result2.unwrap();
}
#[tokio::test]
@@ -561,7 +560,7 @@ mod subscribe_market_data_tests {
for result in results {
assert!(result.is_ok());
assert!(result.unwrap().is_ok());
result.unwrap().unwrap();
}
}
}
@@ -580,7 +579,7 @@ mod subscribe_order_updates_tests {
let receiver = engine.subscribe_order_updates(None).await;
// Receiver should be created successfully
assert!(receiver.is_ok());
receiver.unwrap();
}
#[tokio::test]
@@ -591,9 +590,9 @@ mod subscribe_order_updates_tests {
let receiver2 = engine.subscribe_order_updates(None).await;
let receiver3 = engine.subscribe_order_updates(None).await;
assert!(receiver1.is_ok());
assert!(receiver2.is_ok());
assert!(receiver3.is_ok());
receiver1.unwrap();
receiver2.unwrap();
receiver3.unwrap();
}
#[tokio::test]
@@ -613,7 +612,7 @@ mod subscribe_order_updates_tests {
for result in results {
assert!(result.is_ok());
assert!(result.unwrap().is_ok());
result.unwrap().unwrap();
}
}
}
@@ -642,7 +641,7 @@ mod get_trading_stats_tests {
// Submit some orders
let _ = engine.submit_order(
"AAPL".to_string(),
"AAPL".to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::from_str("100").unwrap(),
@@ -651,7 +650,7 @@ mod get_trading_stats_tests {
).await;
let _ = engine.submit_order(
"MSFT".to_string(),
"MSFT".to_owned(),
OrderSide::Sell,
OrderType::Limit,
Decimal::from_str("50").unwrap(),
@@ -736,7 +735,7 @@ mod edge_case_tests {
// All operations should complete without panicking
for result in results {
assert!(result.is_ok());
result.unwrap();
}
}
@@ -750,7 +749,7 @@ mod edge_case_tests {
// Engine should still be functional
let result = engine.submit_order(
"AAPL".to_string(),
"AAPL".to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::from_str("100").unwrap(),
@@ -758,7 +757,7 @@ mod edge_case_tests {
None,
).await;
assert!(result.is_ok());
result.unwrap();
}
#[tokio::test]
@@ -767,7 +766,7 @@ mod edge_case_tests {
// Very large quantity
let result1 = engine.submit_order(
"SPY".to_string(),
"SPY".to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::from_str("999999999").unwrap(),
@@ -777,7 +776,7 @@ mod edge_case_tests {
// Very small quantity
let result2 = engine.submit_order(
"BTC".to_string(),
"BTC".to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::from_str("0.00000001").unwrap(),
@@ -787,7 +786,7 @@ mod edge_case_tests {
// Very high price
let result3 = engine.submit_order(
"BRK.A".to_string(),
"BRK.A".to_owned(),
OrderSide::Buy,
OrderType::Limit,
Decimal::from_str("1").unwrap(),
@@ -795,9 +794,9 @@ mod edge_case_tests {
None,
).await;
assert!(result1.is_ok());
assert!(result2.is_ok());
assert!(result3.is_ok());
result1.unwrap();
result2.unwrap();
result3.unwrap();
}
#[tokio::test]
@@ -816,7 +815,7 @@ mod edge_case_tests {
for symbol in symbols {
let result = engine.submit_order(
symbol.to_string(),
symbol.to_owned(),
OrderSide::Buy,
OrderType::Market,
Decimal::from_str("10").unwrap(),