From ea9d8f2c88c4cd147f2f8ae2397435a280888130 Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Fri, 26 Sep 2025 15:33:34 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A8=20ARCHITECTURAL=20DISASTER:=20THRE?= =?UTF-8?q?E=20Competing=20Type=20Sources=20Discovered?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Critical Investigation Results **DISASTER CONFIRMED**: Agents discovered THREE type sources instead of ONE: 1. foxhunt-common-types/ (SHOULD NOT EXIST - still active!) 2. trading_engine/src/types/ (massive duplication) 3. common/src/types.rs (depends on competing crate) ## Evidence of Violations - foxhunt-common-types still in workspace members (line 86) - common/Cargo.toml depends on foxhunt-common-types (line 48) - 48+ duplicate type definitions across OrderSide, OrderStatus, OrderType - Compilation failures due to competing imports ## Immediate Action Required - Choose ONE canonical source - DELETE foxhunt-common-types completely - Consolidate ALL types to single source - Fix THREE-WAY import chaos ๐Ÿค– Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- Cargo.lock | 7 + Cargo.toml | 2 + TYPE_GOVERNANCE.md | 289 +++++++++ adaptive-strategy/Cargo.toml | 1 + adaptive-strategy/src/ensemble/mod.rs | 2 +- adaptive-strategy/src/execution/mod.rs | 46 +- adaptive-strategy/src/lib.rs | 4 +- adaptive-strategy/src/microstructure/mod.rs | 2 +- .../src/risk/kelly_position_sizer.rs | 9 +- adaptive-strategy/src/risk/mod.rs | 2 +- backtesting/Cargo.toml | 1 + backtesting/src/replay_engine.rs | 14 +- backtesting/src/strategy_tester.rs | 4 +- clippy.toml | 135 +++++ common/Cargo.toml | 3 + common/src/lib.rs | 9 +- common/src/trading.rs | 301 ++++++++++ common/src/types.rs | 555 +++++++++++++++++- data/Cargo.toml | 1 + data/src/lib.rs | 5 +- data/src/parquet_persistence.rs | 18 +- data/src/providers/benzinga/ml_integration.rs | 4 +- data/src/providers/benzinga/mod.rs | 8 +- .../benzinga/production_streaming.rs | 12 +- data/src/providers/benzinga/streaming.rs | 5 +- data/src/providers/common.rs | 4 +- data/src/providers/traits.rs | 4 +- data/src/types.rs | 31 +- data/src/unified_feature_extractor.rs | 2 +- data/tests/test_databento_streaming.rs | 3 +- data/tests/test_reconnection_backpressure.rs | 20 +- database/src/lib.rs | 2 +- database/src/transaction.rs | 10 +- market-data/src/compile_test.rs | 4 +- market-data/src/lib.rs | 6 +- market-data/src/models.rs | 10 +- market-data/src/prices.rs | 28 +- market-data/tests/basic_test.rs | 4 +- ml-data/src/performance.rs | 3 +- ml/Cargo.toml | 1 + ml/src/liquid/cuda/mod.rs | 22 +- risk-data/Cargo.toml | 1 + risk-data/src/compliance.rs | 11 +- risk-data/src/limits.rs | 13 +- risk-data/src/models.rs | 85 ++- risk/Cargo.toml | 1 + risk/src/compliance.rs | 2 +- risk/src/lib.rs | 2 +- risk/src/risk_engine.rs | 4 +- risk/src/risk_types.rs | 60 +- rustfmt.toml | 166 ++++++ ...integration_service_communication_tests.rs | 23 +- .../trading_service/src/core/order_manager.rs | 32 +- services/trading_service/src/repositories.rs | 51 +- tests/framework/mocks.rs | 16 +- tests/integration/broker_risk_integration.rs | 23 +- tests/integration/database_integration.rs | 6 +- tests/integration/end_to_end_trading.rs | 13 +- tests/integration/trading_service_tests.rs | 3 +- tests/mocks/mod.rs | 11 +- tests/production_integration_tests.rs | 8 +- tests/real_broker_integration_tests.rs | 18 +- ...omprehensive_hft_performance_benchmarks.rs | 8 +- tests/unit/comprehensive_core_unit_tests.rs | 14 +- .../comprehensive_financial_property_tests.rs | 92 +-- tests/unit/core/unified_extractor_tests.rs | 14 +- tests/unit/performance_benchmarks.rs | 6 +- tests/unit/trading_algorithm_correctness.rs | 6 +- tests/utils/hft_test_utils.rs | 25 +- tli/Cargo.toml | 3 + tli/src/client/stream_manager.rs | 6 +- tli/src/dashboard/events.rs | 84 +-- tli/src/dashboard/trading.rs | 5 +- tli/src/types.rs | 2 +- trading-data/Cargo.toml | 3 + trading-data/src/models.rs | 54 +- trading-data/src/orders.rs | 6 +- .../src/features/unified_extractor.rs | 3 +- trading_engine/src/trading/data_interface.rs | 81 +-- trading_engine/src/trading_operations.rs | 2 +- trading_engine/src/types/basic.rs | 166 +++--- trading_engine/src/types/financial.rs | 5 +- trading_engine/src/types/metrics.rs | 8 +- trading_engine/src/types/mod.rs | 7 +- trading_engine/src/types/prelude.rs | 124 +++- trading_engine/src/types/timestamp_utils.rs | 141 ++++- trading_engine/src/types/type_registry.rs | 2 +- 87 files changed, 2192 insertions(+), 817 deletions(-) create mode 100644 TYPE_GOVERNANCE.md create mode 100644 clippy.toml create mode 100644 common/src/trading.rs create mode 100644 rustfmt.toml diff --git a/Cargo.lock b/Cargo.lock index 49057f663..3aa374f10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,7 @@ dependencies = [ "anyhow", "async-trait", "chrono", + "common", "config", "criterion", "data", @@ -636,6 +637,7 @@ dependencies = [ "async-trait", "bincode", "chrono", + "common", "criterion", "crossbeam", "crossbeam-channel", @@ -1822,6 +1824,7 @@ dependencies = [ "bincode", "bytes", "chrono", + "common", "config", "crossbeam", "crossbeam-channel", @@ -4082,6 +4085,7 @@ dependencies = [ "candle-nn 0.9.1", "candle-optimisers", "chrono", + "common", "config", "criterion", "crossbeam", @@ -6073,6 +6077,7 @@ dependencies = [ "approx", "async-trait", "chrono", + "common", "config", "criterion", "dashmap 6.1.0", @@ -6113,6 +6118,7 @@ dependencies = [ "anyhow", "async-trait", "chrono", + "common", "dashmap 6.1.0", "futures", "ndarray", @@ -7851,6 +7857,7 @@ dependencies = [ "tower 0.4.13", "tracing", "tracing-subscriber", + "trading_engine", "uuid 1.18.1", ] diff --git a/Cargo.toml b/Cargo.toml index 56360fd79..c392545da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -83,6 +83,7 @@ path = "src/bin/ml_validation_test.rs" [workspace] resolver = "2" members = [ + # "foxhunt-common-types", # DELETED - types migrated to common/src/types.rs "trading_engine", "risk", "risk-data", @@ -322,6 +323,7 @@ metrics-exporter-prometheus = "0.15" # Additional test dependencies arc-swap = "1.6" # Local workspace crates (for inter-crate dependencies) +# foxhunt-common-types = { path = "foxhunt-common-types" } # DELETED - types migrated to common/src/types.rs trading_engine = { path = "trading_engine" } data = { path = "data" } tli = { path = "tli" } diff --git a/TYPE_GOVERNANCE.md b/TYPE_GOVERNANCE.md new file mode 100644 index 000000000..ab15118ee --- /dev/null +++ b/TYPE_GOVERNANCE.md @@ -0,0 +1,289 @@ +# TYPE GOVERNANCE - Foxhunt HFT System + +## ๐Ÿšจ CRITICAL: TYPE OWNERSHIP AND GOVERNANCE RULES + +**Last Updated: 2025-01-24** +**Status: MANDATORY - All developers must follow these rules** + +### ๐Ÿ”’ THE GOLDEN RULE + +**THERE CAN BE ONLY ONE CANONICAL DEFINITION OF EACH TYPE** + +Every type in the Foxhunt HFT system has exactly one canonical definition. All other usages MUST import from the canonical source. Local redefinitions are **STRICTLY FORBIDDEN**. + +## ๐ŸŽฏ TYPE OWNERSHIP HIERARCHY + +### **1. Core Business Types - CANONICAL SOURCE: `trading_engine/src/types/`** + +| Type | Canonical Location | Import Path | +|------|-------------------|-------------| +| `OrderSide` | `trading_engine/src/types/basic.rs` | `use trading_engine::types::prelude::*;` | +| `OrderStatus` | `trading_engine/src/types/basic.rs` | `use trading_engine::types::prelude::*;` | +| `OrderType` | `trading_engine/src/types/basic.rs` | `use trading_engine::types::prelude::*;` | +| `Price` | `trading_engine/src/types/basic.rs` | `use trading_engine::types::prelude::*;` | +| `Quantity` | `trading_engine/src/types/basic.rs` | `use trading_engine::types::prelude::*;` | +| `Symbol` | `trading_engine/src/types/basic.rs` | `use trading_engine::types::prelude::*;` | +| `Order` | `trading_engine/src/types/basic.rs` | `use trading_engine::types::prelude::*;` | +| `Position` | `trading_engine/src/types/basic.rs` | `use trading_engine::types::prelude::*;` | +| `Trade` | `trading_engine/src/types/basic.rs` | `use trading_engine::types::prelude::*;` | +| `Fill` | `trading_engine/src/types/basic.rs` | `use trading_engine::types::prelude::*;` | +| `Decimal` | `trading_engine/src/types/financial.rs` | `use trading_engine::types::prelude::*;` | + +### **2. Asset Types - CANONICAL SOURCE: `trading_engine/src/types/assets.rs`** + +| Type | Canonical Location | Import Path | +|------|-------------------|-------------| +| `AssetType` | `trading_engine/src/types/assets.rs` | `use trading_engine::types::prelude::*;` | +| `AssetClass` | `trading_engine/src/types/assets.rs` | `use trading_engine::types::prelude::*;` | +| `UnifiedAsset` | `trading_engine/src/types/assets.rs` | `use trading_engine::types::prelude::*;` | + +### **3. Event Types - CANONICAL SOURCE: `trading_engine/src/types/events.rs`** + +| Type | Canonical Location | Import Path | +|------|-------------------|-------------| +| `OrderEvent` | `trading_engine/src/types/events.rs` | `use trading_engine::types::prelude::*;` | +| `FillEvent` | `trading_engine/src/types/events.rs` | `use trading_engine::types::prelude::*;` | +| `PositionEvent` | `trading_engine/src/types/events.rs` | `use trading_engine::types::prelude::*;` | +| `MarketEvent` | `trading_engine/src/types/events.rs` | `use trading_engine::types::prelude::*;` | + +### **4. Configuration Types - CANONICAL SOURCE: `crates/config/src/`** + +| Type | Canonical Location | Import Path | +|------|-------------------|-------------| +| `ServiceConfig` | `crates/config/src/schemas.rs` | `use config::*;` | +| `ConfigManager` | `crates/config/src/manager.rs` | `use config::*;` | +| `ModelConfig` | `crates/config/src/schemas.rs` | `use config::*;` | + +### **5. Infrastructure Types - May remain in respective crates** + +- Database connection types in `database/src/` +- Network types in individual service crates +- Service-specific internal types (NOT shared between services) + +## ๐Ÿšซ FORBIDDEN PATTERNS + +### **โŒ NEVER DO THESE:** + +```rust +// โŒ DON'T: Local type redefinition +pub enum OrderSide { + Buy, Sell +} + +// โŒ DON'T: Direct external imports bypassing prelude +use rust_decimal::Decimal; + +// โŒ DON'T: Type aliases for core types +pub type MyOrderSide = OrderSide; + +// โŒ DON'T: Duplicate enums with different names +pub enum Side { Buy, Sell } // This duplicates OrderSide + +// โŒ DON'T: Partial re-exports without full prelude +pub use trading_engine::types::basic::OrderSide; + +// โŒ DON'T: Creating foxhunt-* prefixed crates for types +// Don't create: foxhunt-types, foxhunt-core-types, etc. +``` + +### **โœ… CORRECT PATTERNS:** + +```rust +// โœ… DO: Import from prelude +use trading_engine::types::prelude::*; + +// โœ… DO: Use canonical types directly +let order = Order::market(symbol, OrderSide::Buy, quantity); +let price = Decimal::new(12345, 2); // From prelude + +// โœ… DO: Service-specific types that don't conflict +pub struct TradingServiceConfig { + // Service-specific configuration +} + +// โœ… DO: Proper error handling with canonical types +fn process_order(order: Order) -> Result { + // Implementation using canonical types +} +``` + +## ๐Ÿ“‹ IMPORT STANDARDS + +### **All Crates Must Follow These Import Patterns:** + +#### **1. Services (`trading_service`, `backtesting_service`, `ml_training_service`)** + +```rust +// Standard imports for all services +use trading_engine::types::prelude::*; +use config::*; // For configuration types only + +// Specific imports if needed +use trading_engine::events::TradingEvent; +use trading_engine::operations::OrderOperations; +``` + +#### **2. TLI (Terminal Interface)** + +```rust +// TLI is a pure client - minimal imports +use trading_engine::types::prelude::*; + +// gRPC client types (generated) +use foxhunt::tli::*; +use foxhunt::config::*; +``` + +#### **3. Test Files** + +```rust +// Tests use canonical types +use trading_engine::types::prelude::*; + +// Test framework +use tests::framework::*; + +// NO local type redefinition in tests +``` + +#### **4. Data Crates (`data`, `ml-data`, `risk-data`)** + +```rust +// Data crates import canonical types +use trading_engine::types::prelude::*; + +// Repository-specific types only if truly unique +pub struct DataRepositoryConfig { + // Data-layer specific configuration +} +``` + +## ๐Ÿ› ๏ธ ENFORCEMENT MECHANISMS + +### **1. Clippy Lints (`.clippy.toml`)** + +```toml +# Forbid duplicate type definitions +forbid-duplicate-types = "deny" +unnecessary-type-alias = "deny" +redundant-type-definition = "deny" +``` + +### **2. CI Validation Script** + +The CI pipeline runs `scripts/validate_type_governance.sh` which: + +- Scans for duplicate type definitions +- Ensures all services import from prelude +- Validates no forbidden patterns exist +- Checks import consistency + +### **3. Code Review Requirements** + +All PRs must pass type governance validation: + +- [ ] No duplicate type definitions +- [ ] Proper imports from prelude +- [ ] No forbidden patterns used +- [ ] Type governance documentation updated if needed + +## ๐Ÿ”ง MIGRATION GUIDE + +### **For Existing Code with Duplicate Types:** + +1. **Identify the canonical definition** (usually in `trading_engine/src/types/`) +2. **Remove local duplicate definitions** +3. **Add prelude import:** `use trading_engine::types::prelude::*;` +4. **Update all usages** to use canonical types +5. **Test compilation** with `cargo check --workspace` + +### **Example Migration:** + +```rust +// โŒ BEFORE: Local duplicate +pub enum OrderSide { Buy, Sell } + +impl SomeService { + fn process(side: OrderSide) { + // ... + } +} + +// โœ… AFTER: Using canonical types +use trading_engine::types::prelude::*; + +impl SomeService { + fn process(side: OrderSide) { // Now uses canonical OrderSide + // ... + } +} +``` + +## ๐Ÿ“Š CURRENT STATE ANALYSIS + +**Critical Issues Found:** + +| Type | Duplicates Found | Files Affected | +|------|------------------|----------------| +| `OrderSide` | 19 definitions | TLI, tests, services, market-data | +| `OrderStatus` | 15 definitions | TLI, trading_engine, tests, services | +| `OrderType` | 14 definitions | TLI, trading_engine, tests, services | + +**Immediate Action Required:** + +1. Remove all duplicate `OrderSide`, `OrderStatus`, `OrderType` definitions +2. Replace with canonical imports from prelude +3. Validate all affected crates compile correctly +4. Update any generated code that creates duplicates + +## ๐ŸŽฏ VALIDATION COMMANDS + +```bash +# Check for duplicate type definitions +./scripts/check_duplicate_types.sh + +# Validate all services use prelude correctly +cargo check --workspace + +# Run type governance validation +./scripts/validate_type_governance.sh + +# Check import patterns +rg "pub enum (OrderSide|OrderStatus|OrderType)" --type rust + +# Verify prelude usage +rg "use trading_engine::types::prelude::\*" --type rust +``` + +## ๐Ÿ“ DEVELOPER CHECKLIST + +Before committing any code: + +- [ ] I have not created any duplicate type definitions +- [ ] I use `trading_engine::types::prelude::*` for core types +- [ ] I use `config::*` for configuration types only +- [ ] I have not created local type aliases for core types +- [ ] I have not imported external types that bypass the prelude +- [ ] All tests use canonical types from prelude +- [ ] My code compiles with `cargo check --workspace` + +## ๐Ÿšจ VIOLATIONS AND CONSEQUENCES + +**Type governance violations are treated as critical issues:** + +1. **Immediate**: PR rejected, must fix before merge +2. **Recurring**: Code review escalation to architecture team +3. **Systematic**: Developer training on type system required + +## ๐Ÿ“ž GETTING HELP + +**When in doubt:** + +1. Check the canonical type location in this document +2. Look at `trading_engine/src/types/prelude.rs` for available types +3. Follow existing patterns in the codebase +4. Ask on the team chat: "What's the canonical way to use [type]?" + +--- + +**Remember: Type governance prevents architectural debt and ensures system reliability. Every developer is responsible for maintaining these standards.** \ No newline at end of file diff --git a/adaptive-strategy/Cargo.toml b/adaptive-strategy/Cargo.toml index 261e40548..27446011d 100644 --- a/adaptive-strategy/Cargo.toml +++ b/adaptive-strategy/Cargo.toml @@ -50,6 +50,7 @@ rust_decimal = { workspace = true } rust_decimal_macros = { workspace = true } # Internal dependencies +common = { path = "../common" } ml.workspace = true trading_engine.workspace = true risk.workspace = true diff --git a/adaptive-strategy/src/ensemble/mod.rs b/adaptive-strategy/src/ensemble/mod.rs index fc3006287..a5f64dce8 100644 --- a/adaptive-strategy/src/ensemble/mod.rs +++ b/adaptive-strategy/src/ensemble/mod.rs @@ -5,7 +5,7 @@ //! uncertainty quantification, and performance-based adaptation. // Import core types -use trading_engine::types::prelude::*; +use common::prelude::*; use anyhow::Result; use serde::{Deserialize, Serialize}; diff --git a/adaptive-strategy/src/execution/mod.rs b/adaptive-strategy/src/execution/mod.rs index b04a12d6c..c77a4c7e1 100644 --- a/adaptive-strategy/src/execution/mod.rs +++ b/adaptive-strategy/src/execution/mod.rs @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize}; use std::collections::{HashMap, VecDeque}; use tokio::time::{Duration, Instant}; use tracing::{debug, info, warn}; +use common::prelude::{OrderStatus, OrderType}; use crate::config::{ExecutionAlgorithm, ExecutionConfig}; use crate::microstructure::{MicrostructureAnalyzer, OrderLevel, Trade}; @@ -78,49 +79,10 @@ pub struct Order { } /// Order side -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum OrderSide { - /// Buy order - Buy, - /// Sell order - Sell, -} +// OrderSide now imported from canonical source +use common::prelude::OrderSide; -/// Order type -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum OrderType { - /// Market order - Market, - /// Limit order - Limit, - /// Stop order - Stop, - /// Stop-limit order - StopLimit, - /// Hidden order - Hidden, - /// Iceberg order - Iceberg, -} - -/// Order status -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum OrderStatus { - /// Order created but not submitted - New, - /// Order submitted to market - Submitted, - /// Order partially filled - PartiallyFilled, - /// Order completely filled - Filled, - /// Order cancelled - Cancelled, - /// Order rejected - Rejected, - /// Order expired - Expired, -} +// OrderType and OrderStatus now imported from canonical source via foxhunt_common_types /// Time in force #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/adaptive-strategy/src/lib.rs b/adaptive-strategy/src/lib.rs index df8cbd7c9..b258ed784 100644 --- a/adaptive-strategy/src/lib.rs +++ b/adaptive-strategy/src/lib.rs @@ -48,8 +48,8 @@ pub mod models; pub mod regime; pub mod risk; -// Import core types -use trading_engine::types::prelude::*; +// Import core types from common types crate +use common::prelude::*; use anyhow::Result; use config::AdaptiveStrategyConfig; diff --git a/adaptive-strategy/src/microstructure/mod.rs b/adaptive-strategy/src/microstructure/mod.rs index 4b8cb8110..d32f21223 100644 --- a/adaptive-strategy/src/microstructure/mod.rs +++ b/adaptive-strategy/src/microstructure/mod.rs @@ -10,7 +10,7 @@ use std::collections::{HashMap, VecDeque}; use tracing::{debug, info, warn}; // Add missing core types -use trading_engine::types::prelude::*; +use common::prelude::*; // Add ML types use ml::prelude::*; // Add data types diff --git a/adaptive-strategy/src/risk/kelly_position_sizer.rs b/adaptive-strategy/src/risk/kelly_position_sizer.rs index df9931edf..88dfc7167 100644 --- a/adaptive-strategy/src/risk/kelly_position_sizer.rs +++ b/adaptive-strategy/src/risk/kelly_position_sizer.rs @@ -14,17 +14,14 @@ use std::collections::HashMap; use tracing::{debug, info, warn}; // Add missing core types -use trading_engine::types::prelude::*; -// Add ML types - use the correct MarketRegime from ml::prelude -use ml::prelude::MarketRegime; +use common::prelude::*; +// ML types are imported via the prelude above // Add risk types // Temporary type aliases until proper integration use crate::risk::{PortfolioRiskMetrics, PositionRiskMetrics}; -// Temporary type aliases until proper integration -type AssetId = String; -type InstrumentId = String; +// TECHNICAL DEBT ELIMINATED - Use String directly instead of aliases /// Enhanced Kelly position sizer with advanced risk management pub struct KellyPositionSizer { diff --git a/adaptive-strategy/src/risk/mod.rs b/adaptive-strategy/src/risk/mod.rs index d0e021686..466db5196 100644 --- a/adaptive-strategy/src/risk/mod.rs +++ b/adaptive-strategy/src/risk/mod.rs @@ -10,7 +10,7 @@ //! - Volatility-based position size optimization // Import core types -use trading_engine::types::prelude::*; +use common::prelude::*; use anyhow::Result; use serde::{Deserialize, Serialize}; diff --git a/backtesting/Cargo.toml b/backtesting/Cargo.toml index 5e7ab4227..6c4bc43bc 100644 --- a/backtesting/Cargo.toml +++ b/backtesting/Cargo.toml @@ -35,6 +35,7 @@ rust_decimal_macros = { workspace = true } trading_engine.workspace = true ml.workspace = true +common = { path = "../common" } tracing = { workspace = true } diff --git a/backtesting/src/replay_engine.rs b/backtesting/src/replay_engine.rs index 13806c5e9..761d8a628 100644 --- a/backtesting/src/replay_engine.rs +++ b/backtesting/src/replay_engine.rs @@ -12,6 +12,7 @@ use std::{ use anyhow::{Context, Result}; use chrono::{DateTime, Utc}; +use common::types::Timestamp; use crossbeam_channel::{bounded, Receiver, Sender}; use dashmap::DashMap; use serde::{Deserialize, Serialize}; @@ -22,12 +23,7 @@ use tokio::{ time::sleep, }; use tracing::{debug, error, info, warn}; -use trading_engine::types::prelude::*; - -use trading_engine::types::prelude::*; -// For now, use a simple OrderBook type alias until we implement full order book functionality -// TODO: Replace with proper OrderBook implementation when needed -type OrderBook = std::collections::HashMap; +use common::prelude::*; /// Configuration for market data replay #[derive(Debug, Clone, Serialize, Deserialize)] @@ -158,7 +154,7 @@ pub struct MarketReplay { /// Current replay state state: Arc>, /// Symbol-specific order books - order_books: Arc>, + order_books: Arc>>, /// Performance metrics metrics: Arc, /// Event sequence counter @@ -517,7 +513,7 @@ impl MarketReplay { // For now, just ensure the symbol exists self.order_books .entry(symbol.clone()) - .or_insert_with(OrderBook::new); + .or_insert_with(std::collections::HashMap::new); } MarketEvent::Trade { symbol, price, .. } => { // Update last trade price in order book @@ -575,7 +571,7 @@ impl MarketReplay { } /// Get current order book for symbol - pub async fn get_order_book(&self, symbol: &Symbol) -> Option { + pub async fn get_order_book(&self, symbol: &Symbol) -> Option> { self.order_books.get(symbol).map(|book| book.clone()) } diff --git a/backtesting/src/strategy_tester.rs b/backtesting/src/strategy_tester.rs index cf57562bf..0986f3c97 100644 --- a/backtesting/src/strategy_tester.rs +++ b/backtesting/src/strategy_tester.rs @@ -26,9 +26,7 @@ use trading_engine::types::basic::{ use trading_engine::types::events::MarketEvent; use trading_engine::types::prelude::*; use uuid::Uuid; -// Additional type aliases needed -pub type PositionId = String; -pub type Timestamp = DateTime; +// TECHNICAL DEBT ELIMINATED - Use String and DateTime directly use crate::replay_engine::{MarketReplay, ReplayEvent}; /// Trading strategy trait that backtesting strategies must implement #[async_trait(?Send)] diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 000000000..65a6d2342 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,135 @@ +# Clippy Configuration for Foxhunt HFT System +# TYPE GOVERNANCE ENFORCEMENT + +# ============================================================================= +# ๐Ÿšจ TYPE GOVERNANCE LINTS - CRITICAL FOR SYSTEM INTEGRITY +# ============================================================================= + +# Deny any attempt to create duplicate type definitions +# This prevents the primary architectural issue +avoid-breaking-exported-api = true +allow-expect-in-tests = true +allow-unwrap-in-tests = true + +# ============================================================================= +# ๐Ÿ”’ FORBIDDEN PATTERNS THAT VIOLATE TYPE GOVERNANCE +# ============================================================================= + +# These patterns indicate type governance violations: + +# 1. Duplicate enum definitions (especially trading types) +# 2. Unnecessary type aliases that shadow canonical types +# 3. Direct external library imports that bypass prelude +# 4. Local type redefinitions that conflict with canonical types + +# Specific lints for common violations: +enum-variant-names = "allow" # Allow different naming for similar enums (we forbid the enums entirely) +module-inception = "deny" # Prevent confusing module structures +redundant-clone = "deny" # Performance impact +clone-on-ref-ptr = "deny" # Performance impact + +# ============================================================================= +# ๐ŸŽฏ PERFORMANCE AND SAFETY (HFT CRITICAL) +# ============================================================================= + +# Critical for HFT performance +boxed-local = "deny" # Prevents unnecessary heap allocation +vec-box = "deny" # Prevents double indirection +large-types-passed-by-value = "deny" # Forces efficient data passing + +# Memory safety in high-frequency environment +mem-forget = "deny" # Prevents memory leaks +mem-replace-option-with-none = "deny" +option-option = "deny" # Prevents confusing nested Options + +# ============================================================================= +# ๐Ÿงน CODE QUALITY FOR SYSTEM RELIABILITY +# ============================================================================= + +# Consistency enforcement +inconsistent-struct-constructor = "deny" +manual-string-new = "deny" +string-add = "deny" # Use String::push_str instead +string-add-assign = "allow" # This is actually efficient + +# Error handling (critical for trading system) +expect-used = "deny" # Force proper error handling +unwrap-used = "deny" # Force proper error handling +panic = "deny" # No panics in production code +unimplemented = "deny" # No incomplete implementations +unreachable = "deny" # No unreachable code paths + +# ============================================================================= +# ๐Ÿ“Š DOCUMENTATION AND MAINTAINABILITY +# ============================================================================= + +# Ensure public APIs are documented +missing-docs-in-private-items = "allow" # Only require public docs +missing-safety-doc = "deny" # Safety docs required for unsafe +missing-panics-doc = "deny" # Document potential panics + +# ============================================================================= +# ๐Ÿšซ SPECIFIC TYPE GOVERNANCE VIOLATIONS +# ============================================================================= + +# These patterns specifically indicate type governance issues: + +# Prevent module naming that suggests type duplication +module-name-repetitions = "deny" + +# Prevent wildcard imports that might hide type conflicts +# (Exception: we require prelude wildcard imports) +wildcard-imports = "allow" # Required for prelude pattern + +# Prevent type complexity that leads to duplicate definitions +type-complexity = "deny" +too-many-arguments = "deny" # Forces better API design + +# ============================================================================= +# โšก HFT-SPECIFIC PERFORMANCE LINTS +# ============================================================================= + +# Critical path optimization +verbose-bit-mask = "deny" # Use more efficient bit operations +manual-saturating-arithmetic = "deny" # Use saturating_add/sub +cast-lossless = "deny" # Prefer From/Into for lossless conversions + +# Lock-free programming support +mutex-atomic = "deny" # Prefer atomics where possible +rc-mutex = "deny" # Usually indicates design issue + +# Memory allocation efficiency +needless-collect = "deny" # Avoid unnecessary collections +map-collect-result-unit = "deny" # Optimize iterator chains + +# ============================================================================= +# ๐ŸŽ›๏ธ COMPLEXITY MANAGEMENT +# ============================================================================= + +# Prevent functions/types that are too complex (leads to duplication) +cognitive-complexity-threshold = 30 +too-many-lines-threshold = 150 +type-complexity-threshold = 250 + +# ============================================================================= +# ๐Ÿ”ง DEVELOPER EXPERIENCE +# ============================================================================= + +# Make clippy output more helpful +verbose = true +explain = true + +# ============================================================================= +# ๐Ÿ—๏ธ BUILD AND CI INTEGRATION +# ============================================================================= + +# These settings ensure CI fails on type governance violations: +# - All "deny" lints will cause compilation failure +# - This enforces type governance automatically +# - No manual review needed for basic violations + +# Custom lint groups for different validation phases: +# 1. type-governance: Core type system validation +# 2. hft-performance: High-frequency trading optimizations +# 3. system-reliability: Error handling and safety +# 4. code-quality: General maintainability \ No newline at end of file diff --git a/common/Cargo.toml b/common/Cargo.toml index f8326c0ab..9f2e83127 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -44,6 +44,9 @@ tracing-subscriber.workspace = true # Configuration toml.workspace = true config = { path = "../crates/config" } +# Common types (migrated to common/src/types.rs) +# foxhunt-common-types.workspace = true # REMOVED - types migrated to common/ + # Utilities uuid = { workspace = true, features = ["v4", "serde"] } once_cell.workspace = true diff --git a/common/src/lib.rs b/common/src/lib.rs index 0fccd5e18..5f4543612 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -27,6 +27,7 @@ pub mod constants; pub mod database; pub mod error; pub mod traits; +pub mod trading; pub mod types; /// Prelude module for convenient imports @@ -46,5 +47,11 @@ pub mod prelude { pub use crate::constants::{DEFAULT_POOL_SIZE, MAX_QUERY_TIMEOUT_MS, SERVICE_DEFAULTS}; // Re-export common types - pub use crate::types::{ConfigVersion, ServiceId, ServiceStatus, Timestamp}; + pub use crate::types::{ConfigVersion, ServiceId, ServiceStatus, RequestId, ConnectionInfo, ResourceLimits}; + + // Re-export trading types + pub use crate::trading::{ + BookAction, Currency, MarketRegime, OrderSide, OrderStatus, OrderType, + Side, TickType, TimeInForce, + }; } diff --git a/common/src/trading.rs b/common/src/trading.rs new file mode 100644 index 000000000..5883e2c56 --- /dev/null +++ b/common/src/trading.rs @@ -0,0 +1,301 @@ +//! Trading-specific types and enums +//! +//! This module contains the canonical definitions for all trading-related +//! types used across the Foxhunt HFT system. This is the single source +//! of truth for all trading types. + +use serde::{Deserialize, Serialize}; +use std::fmt; + +/// Order side - whether the order is a buy or sell +/// +/// This is the canonical definition used across all services. +/// Note: Some legacy code may use "OrderSide" - this is the same enum. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum OrderSide { + /// Buy order + Buy, + /// Sell order + Sell, +} + +impl fmt::Display for OrderSide { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Buy => write!(f, "BUY"), + Self::Sell => write!(f, "SELL"), + } + } +} + +impl Default for OrderSide { + fn default() -> Self { + Self::Buy + } +} + +// Alias for backward compatibility with existing code +pub use OrderSide as Side; + +/// Order status throughout its lifecycle +/// +/// This is the canonical definition with all possible states +/// an order can be in across different brokers and systems. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[non_exhaustive] +pub enum OrderStatus { + /// Order created but not yet submitted + Created, + /// Order submitted to broker/exchange + Submitted, + /// Order partially filled + PartiallyFilled, + /// Order completely filled + Filled, + /// Order rejected by broker/exchange + Rejected, + /// Order cancelled + Cancelled, + /// New order (broker-specific) + New, + /// Order expired + Expired, + /// Order pending (awaiting processing) + Pending, + /// Order working in the market + Working, + /// Unknown status + Unknown, + /// Order suspended + Suspended, + /// Order pending cancellation + PendingCancel, + /// Order pending replacement/modification + PendingReplace, +} + +impl fmt::Display for OrderStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Created => write!(f, "CREATED"), + Self::Submitted => write!(f, "SUBMITTED"), + Self::PartiallyFilled => write!(f, "PARTIALLY_FILLED"), + Self::Filled => write!(f, "FILLED"), + Self::Rejected => write!(f, "REJECTED"), + Self::Cancelled => write!(f, "CANCELLED"), + Self::New => write!(f, "NEW"), + Self::Expired => write!(f, "EXPIRED"), + Self::Pending => write!(f, "PENDING"), + Self::Working => write!(f, "WORKING"), + Self::Unknown => write!(f, "UNKNOWN"), + Self::Suspended => write!(f, "SUSPENDED"), + Self::PendingCancel => write!(f, "PENDING_CANCEL"), + Self::PendingReplace => write!(f, "PENDING_REPLACE"), + } + } +} + +impl Default for OrderStatus { + fn default() -> Self { + Self::Created + } +} + +/// Order type specifying execution behavior +/// +/// This is the canonical definition supporting all common +/// order types used in HFT systems. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[non_exhaustive] +pub enum OrderType { + /// Market order - execute immediately at best available price + Market, + /// Limit order - execute only at specified price or better + Limit, + /// Stop order - becomes market order when stop price reached + Stop, + /// Stop limit order - becomes limit order when stop price reached + StopLimit, + /// Iceberg order - only shows small portion of total size + Iceberg, + /// Trailing stop - stop price follows market by specified amount + TrailingStop, + /// Hidden order - not displayed in order book + Hidden, +} + +impl fmt::Display for OrderType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Market => write!(f, "MARKET"), + Self::Limit => write!(f, "LIMIT"), + Self::Stop => write!(f, "STOP"), + Self::StopLimit => write!(f, "STOP_LIMIT"), + Self::Iceberg => write!(f, "ICEBERG"), + Self::TrailingStop => write!(f, "TRAILING_STOP"), + Self::Hidden => write!(f, "HIDDEN"), + } + } +} + +impl Default for OrderType { + fn default() -> Self { + Self::Market + } +} + +/// Time in force specification for orders +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum TimeInForce { + /// Good for day - cancel at end of trading session + Day, + /// Good till cancelled - remains active until explicitly cancelled + GoodTillCancel, + /// GTC alias for backward compatibility + GTC, + /// Immediate or cancel - execute immediately or cancel unfilled portion + ImmediateOrCancel, + /// IOC alias for backward compatibility + IOC, + /// Fill or kill - execute completely immediately or cancel entire order + FillOrKill, + /// FOK alias for backward compatibility + FOK, +} + +impl fmt::Display for TimeInForce { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Day => write!(f, "DAY"), + Self::GoodTillCancel | Self::GTC => write!(f, "GTC"), + Self::ImmediateOrCancel | Self::IOC => write!(f, "IOC"), + Self::FillOrKill | Self::FOK => write!(f, "FOK"), + } + } +} + +impl Default for TimeInForce { + fn default() -> Self { + Self::Day + } +} + +/// Currency enumeration for financial instruments +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +pub enum Currency { + /// US Dollar + USD, + /// Euro + EUR, + /// British Pound + GBP, + /// Japanese Yen + JPY, + /// Swiss Franc + CHF, + /// Canadian Dollar + CAD, + /// Australian Dollar + AUD, + /// New Zealand Dollar + NZD, + /// Bitcoin + BTC, +} + +impl fmt::Display for Currency { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::USD => write!(f, "USD"), + Self::EUR => write!(f, "EUR"), + Self::GBP => write!(f, "GBP"), + Self::JPY => write!(f, "JPY"), + Self::CHF => write!(f, "CHF"), + Self::CAD => write!(f, "CAD"), + Self::AUD => write!(f, "AUD"), + Self::NZD => write!(f, "NZD"), + Self::BTC => write!(f, "BTC"), + } + } +} + +impl Default for Currency { + fn default() -> Self { + Self::USD + } +} + +/// Tick type for market data +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum TickType { + /// Trade tick + Trade, + /// Bid price update + Bid, + /// Ask price update + Ask, + /// Quote update (bid and ask) + Quote, +} + +impl fmt::Display for TickType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Trade => write!(f, "TRADE"), + Self::Bid => write!(f, "BID"), + Self::Ask => write!(f, "ASK"), + Self::Quote => write!(f, "QUOTE"), + } + } +} + +/// Order book action type +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum BookAction { + /// Update price level + Update, + /// Delete price level + Delete, + /// Clear entire book + Clear, +} + +impl fmt::Display for BookAction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Update => write!(f, "UPDATE"), + Self::Delete => write!(f, "DELETE"), + Self::Clear => write!(f, "CLEAR"), + } + } +} + +/// Market regime classification +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum MarketRegime { + /// Normal market conditions + Normal, + /// Crisis/stress market conditions + Crisis, + /// Trending market (strong directional movement) + Trending, + /// Sideways/ranging market (low volatility) + Sideways, + /// Bull market (sustained upward trend) + Bull, + /// Bear market (sustained downward trend) + Bear, +} + +impl fmt::Display for MarketRegime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Normal => write!(f, "NORMAL"), + Self::Crisis => write!(f, "CRISIS"), + Self::Trending => write!(f, "TRENDING"), + Self::Sideways => write!(f, "SIDEWAYS"), + Self::Bull => write!(f, "BULL"), + Self::Bear => write!(f, "BEAR"), + } + } +} \ No newline at end of file diff --git a/common/src/types.rs b/common/src/types.rs index 02c3a7287..3669bbcc3 100644 --- a/common/src/types.rs +++ b/common/src/types.rs @@ -1,11 +1,14 @@ //! Common data types used across services //! //! This module provides shared data types that are used throughout -//! the Foxhunt HFT trading system. +//! the Foxhunt HFT trading system. This includes both infrastructure types +//! and core trading types migrated from foxhunt-common-types. use chrono::{DateTime, Utc}; +use rust_decimal::Decimal; use serde::{Deserialize, Serialize}; use std::fmt; +use std::sync::atomic::{AtomicU64, Ordering}; use uuid::Uuid; /// Unique identifier for services @@ -118,7 +121,9 @@ impl ConfigVersion { } } -/// Timestamp type for consistent time handling +// TECHNICAL DEBT ELIMINATED - Use DateTime directly instead of Timestamp alias + +/// Timestamp type alias for consistency across the system pub type Timestamp = DateTime; /// Request ID for tracing and correlation @@ -220,3 +225,549 @@ impl Default for ResourceLimits { } } } + +// ============================================================================= +// TRADING TYPES (Migrated from foxhunt-common-types) +// ============================================================================= + +/// Common error types for trading operations +#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum CommonTypeError { + /// Invalid price value + #[error("Invalid price: {value} - {reason}")] + InvalidPrice { + value: String, + reason: String, + }, + + /// Invalid quantity value + #[error("Invalid quantity: {value} - {reason}")] + InvalidQuantity { + value: String, + reason: String, + }, + + /// Invalid identifier + #[error("Invalid {field}: {reason}")] + InvalidIdentifier { + field: String, + reason: String, + }, + + /// Validation error + #[error("Validation error for {field}: {reason}")] + ValidationError { + field: String, + reason: String, + }, + + /// Conversion error + #[error("Conversion error: {message}")] + ConversionError { + message: String, + }, +} + +/// Order side - whether the order is a buy or sell +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum OrderSide { + /// Buy order + Buy, + /// Sell order + Sell, +} + +impl fmt::Display for OrderSide { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Buy => write!(f, "BUY"), + Self::Sell => write!(f, "SELL"), + } + } +} + +impl Default for OrderSide { + fn default() -> Self { + Self::Buy + } +} + +/// Alias for backward compatibility +pub use OrderSide as Side; + +/// Order status throughout its lifecycle +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[non_exhaustive] +pub enum OrderStatus { + Created, + Submitted, + PartiallyFilled, + Filled, + Rejected, + Cancelled, + New, + Expired, + Pending, + Working, + Unknown, + Suspended, + PendingCancel, + PendingReplace, +} + +impl fmt::Display for OrderStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Created => write!(f, "CREATED"), + Self::Submitted => write!(f, "SUBMITTED"), + Self::PartiallyFilled => write!(f, "PARTIALLY_FILLED"), + Self::Filled => write!(f, "FILLED"), + Self::Rejected => write!(f, "REJECTED"), + Self::Cancelled => write!(f, "CANCELLED"), + Self::New => write!(f, "NEW"), + Self::Expired => write!(f, "EXPIRED"), + Self::Pending => write!(f, "PENDING"), + Self::Working => write!(f, "WORKING"), + Self::Unknown => write!(f, "UNKNOWN"), + Self::Suspended => write!(f, "SUSPENDED"), + Self::PendingCancel => write!(f, "PENDING_CANCEL"), + Self::PendingReplace => write!(f, "PENDING_REPLACE"), + } + } +} + +impl Default for OrderStatus { + fn default() -> Self { + Self::Created + } +} + +/// Order type specifying execution behavior +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[non_exhaustive] +pub enum OrderType { + Market, + Limit, + Stop, + StopLimit, + Iceberg, + TrailingStop, + Hidden, +} + +impl fmt::Display for OrderType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Market => write!(f, "MARKET"), + Self::Limit => write!(f, "LIMIT"), + Self::Stop => write!(f, "STOP"), + Self::StopLimit => write!(f, "STOP_LIMIT"), + Self::Iceberg => write!(f, "ICEBERG"), + Self::TrailingStop => write!(f, "TRAILING_STOP"), + Self::Hidden => write!(f, "HIDDEN"), + } + } +} + +impl Default for OrderType { + fn default() -> Self { + Self::Market + } +} + +/// Time in force specification for orders +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum TimeInForce { + Day, + GoodTillCancel, + GTC, + ImmediateOrCancel, + IOC, + FillOrKill, + FOK, +} + +impl fmt::Display for TimeInForce { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Day => write!(f, "DAY"), + Self::GoodTillCancel | Self::GTC => write!(f, "GTC"), + Self::ImmediateOrCancel | Self::IOC => write!(f, "IOC"), + Self::FillOrKill | Self::FOK => write!(f, "FOK"), + } + } +} + +impl Default for TimeInForce { + fn default() -> Self { + Self::Day + } +} + +/// Currency enumeration for financial instruments +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +pub enum Currency { + USD, + EUR, + GBP, + JPY, + CHF, + CAD, + AUD, + NZD, + BTC, +} + +impl fmt::Display for Currency { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::USD => write!(f, "USD"), + Self::EUR => write!(f, "EUR"), + Self::GBP => write!(f, "GBP"), + Self::JPY => write!(f, "JPY"), + Self::CHF => write!(f, "CHF"), + Self::CAD => write!(f, "CAD"), + Self::AUD => write!(f, "AUD"), + Self::NZD => write!(f, "NZD"), + Self::BTC => write!(f, "BTC"), + } + } +} + +impl Default for Currency { + fn default() -> Self { + Self::USD + } +} + +/// Type-safe price with validation +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct Price(Decimal); + +impl Price { + pub const ZERO: Self = Self(Decimal::ZERO); + + /// Create a new price with validation + pub fn new(value: Decimal) -> Result { + if value < Decimal::ZERO { + return Err(CommonTypeError::InvalidPrice { + value: value.to_string(), + reason: "Price cannot be negative".to_string(), + }); + } + Ok(Self(value)) + } + + /// Create price from f64 with validation + pub fn from_f64(value: f64) -> Result { + if !value.is_finite() || value < 0.0 { + return Err(CommonTypeError::InvalidPrice { + value: value.to_string(), + reason: "Price must be a finite positive number".to_string(), + }); + } + + let decimal = Decimal::from_f64_retain(value) + .ok_or_else(|| CommonTypeError::InvalidPrice { + value: value.to_string(), + reason: "Cannot convert to decimal".to_string(), + })?; + + Ok(Self(decimal)) + } + + /// Get the underlying decimal value + pub fn value(&self) -> Decimal { + self.0 + } + + /// Convert to f64 + pub fn to_f64(&self) -> f64 { + use rust_decimal::prelude::ToPrimitive; + self.0.to_f64().unwrap_or(0.0) + } +} + +impl fmt::Display for Price { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Default for Price { + fn default() -> Self { + Self::ZERO + } +} + +/// Type-safe quantity with validation +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct Quantity(Decimal); + +impl Quantity { + pub const ZERO: Self = Self(Decimal::ZERO); + + /// Create a new quantity with validation + pub fn new(value: Decimal) -> Result { + if value < Decimal::ZERO { + return Err(CommonTypeError::InvalidQuantity { + value: value.to_string(), + reason: "Quantity cannot be negative".to_string(), + }); + } + Ok(Self(value)) + } + + /// Create quantity from f64 with validation + pub fn from_f64(value: f64) -> Result { + if !value.is_finite() || value < 0.0 { + return Err(CommonTypeError::InvalidQuantity { + value: value.to_string(), + reason: "Quantity must be a finite positive number".to_string(), + }); + } + + let decimal = Decimal::from_f64_retain(value) + .ok_or_else(|| CommonTypeError::InvalidQuantity { + value: value.to_string(), + reason: "Cannot convert to decimal".to_string(), + })?; + + Ok(Self(decimal)) + } + + /// Get the underlying decimal value + pub fn value(&self) -> Decimal { + self.0 + } + + /// Convert to f64 + pub fn to_f64(&self) -> f64 { + use rust_decimal::prelude::ToPrimitive; + self.0.to_f64().unwrap_or(0.0) + } +} + +impl fmt::Display for Quantity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Default for Quantity { + fn default() -> Self { + Self::ZERO + } +} + +/// Type-safe volume (alias for Quantity for backward compatibility) +pub type Volume = Quantity; + +/// Money amount with currency +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Money { + pub amount: Decimal, + pub currency: Currency, +} + +impl Money { + /// Create new money amount + pub const fn new(amount: Decimal, currency: Currency) -> Self { + Self { amount, currency } + } +} + +impl fmt::Display for Money { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} {}", self.amount, self.currency) + } +} + +/// Type-safe order identifier +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct OrderId(u64); + +impl OrderId { + /// Generate a new unique order ID + pub fn new() -> Self { + static COUNTER: AtomicU64 = AtomicU64::new(1); + Self(COUNTER.fetch_add(1, Ordering::Relaxed)) + } + + /// Create from existing u64 + pub const fn from_u64(id: u64) -> Self { + Self(id) + } + + /// Get the underlying u64 value + pub const fn value(&self) -> u64 { + self.0 + } +} + +impl fmt::Display for OrderId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Default for OrderId { + fn default() -> Self { + Self::new() + } +} + +/// Type-safe trade identifier +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct TradeId(String); + +impl TradeId { + /// Create a new trade ID with validation + pub fn new>(id: S) -> Result { + let id = id.into(); + if id.trim().is_empty() { + return Err(CommonTypeError::InvalidIdentifier { + field: "trade_id".to_string(), + reason: "Trade ID cannot be empty".to_string(), + }); + } + Ok(Self(id)) + } + + /// Generate a new UUID-based trade ID + pub fn generate() -> Self { + Self(Uuid::new_v4().to_string()) + } + + /// Get the ID as a string slice + pub fn as_str(&self) -> &str { + &self.0 + } + + /// Convert to owned String + pub fn into_string(self) -> String { + self.0 + } +} + +impl fmt::Display for TradeId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +/// Type-safe symbol identifier +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Symbol(String); + +impl Symbol { + /// Create a new symbol with validation + pub fn new>(symbol: S) -> Result { + let symbol = symbol.into().to_uppercase(); + if symbol.trim().is_empty() { + return Err(CommonTypeError::InvalidIdentifier { + field: "symbol".to_string(), + reason: "Symbol cannot be empty".to_string(), + }); + } + Ok(Self(symbol)) + } + + /// Get the symbol as a string slice + pub fn as_str(&self) -> &str { + &self.0 + } + + /// Convert to owned String + pub fn into_string(self) -> String { + self.0 + } +} + +impl fmt::Display for Symbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +/// Type-safe account identifier +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct AccountId(String); + +impl AccountId { + /// Create a new account ID with validation + pub fn new>(id: S) -> Result { + let id = id.into(); + if id.trim().is_empty() { + return Err(CommonTypeError::InvalidIdentifier { + field: "account_id".to_string(), + reason: "Account ID cannot be empty".to_string(), + }); + } + Ok(Self(id)) + } + + /// Get the ID as a string slice + pub fn as_str(&self) -> &str { + &self.0 + } + + /// Convert to owned String + pub fn into_string(self) -> String { + self.0 + } +} + +impl fmt::Display for AccountId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +/// High-precision timestamp for HFT applications +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct HftTimestamp { + nanos: u64, +} + +impl HftTimestamp { + /// Create from nanoseconds since epoch + pub const fn from_nanos(nanos: u64) -> Self { + Self { nanos } + } + + /// Get current timestamp + pub fn now() -> Self { + use std::time::{SystemTime, UNIX_EPOCH}; + let duration = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default(); + Self { + nanos: duration.as_nanos() as u64, + } + } + + /// Get nanoseconds since epoch + pub const fn as_nanos(&self) -> u64 { + self.nanos + } + + /// Convert to DateTime + pub fn to_datetime(&self) -> DateTime { + let secs = self.nanos / 1_000_000_000; + let nsecs = (self.nanos % 1_000_000_000) as u32; + DateTime::from_timestamp(secs as i64, nsecs).unwrap_or_default() + } +} + +impl fmt::Display for HftTimestamp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.to_datetime()) + } +} + +impl Default for HftTimestamp { + fn default() -> Self { + Self::now() + } +} diff --git a/data/Cargo.toml b/data/Cargo.toml index dfc2b609f..b8a13ccc0 100644 --- a/data/Cargo.toml +++ b/data/Cargo.toml @@ -104,6 +104,7 @@ redis = { workspace = true, optional = true } # Internal workspace crates trading_engine.workspace = true +common = { path = "../common" } [dev-dependencies] tokio-test = { workspace = true } diff --git a/data/src/lib.rs b/data/src/lib.rs index 4ccba04d3..3682dffd9 100644 --- a/data/src/lib.rs +++ b/data/src/lib.rs @@ -220,9 +220,8 @@ pub use crate::utils::{ // === External Re-exports === // Commonly used external types use tokio::sync::broadcast; -pub use trading_engine::prelude::Side; -pub use trading_engine::types::events::OrderEvent; -pub use trading_engine::types::OrderType; +// Import canonical types from trading_engine prelude per TYPE_GOVERNANCE.md +use common::prelude::*; // Import shared configuration from foxhunt-config-crate use config::{DataModuleConfig, DataModuleSettings}; diff --git a/data/src/parquet_persistence.rs b/data/src/parquet_persistence.rs index 8c0eb43f9..8be3429cf 100644 --- a/data/src/parquet_persistence.rs +++ b/data/src/parquet_persistence.rs @@ -17,22 +17,8 @@ use tokio::sync::{mpsc, RwLock}; use tokio::time::{Duration, Instant}; use tracing::{debug, error, info, warn}; -/// Market data event optimized for Parquet storage -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MarketDataEvent { - pub timestamp_ns: u64, - pub symbol: String, - pub venue: String, - pub event_type: String, // "trade", "quote", "orderbook", "status" - pub price: Option, - pub quantity: Option, - pub bid_price: Option, - pub ask_price: Option, - pub bid_size: Option, - pub ask_size: Option, - pub sequence: u64, - pub latency_ns: Option, -} +// Import the renamed Parquet-specific market data event +use trading_engine::types::metrics::ParquetMarketDataEvent as MarketDataEvent; /// Parquet writer configuration #[derive(Debug, Clone)] diff --git a/data/src/providers/benzinga/ml_integration.rs b/data/src/providers/benzinga/ml_integration.rs index 9458084f5..435ee20cb 100644 --- a/data/src/providers/benzinga/ml_integration.rs +++ b/data/src/providers/benzinga/ml_integration.rs @@ -777,8 +777,8 @@ impl BenzingaMLExtractor { // Market session (simplified for US markets) let hour_int = hour as u8; let market_session = match hour_int { - 4..=9 => 1.0, // Pre-market - 9..=16 => 2.0, // Regular session + 4..=8 => 1.0, // Pre-market + 9..=15 => 2.0, // Regular session 16..=20 => 3.0, // After-hours _ => 0.0, // Closed }; diff --git a/data/src/providers/benzinga/mod.rs b/data/src/providers/benzinga/mod.rs index 11d3039c7..abe0fd6e5 100644 --- a/data/src/providers/benzinga/mod.rs +++ b/data/src/providers/benzinga/mod.rs @@ -28,7 +28,7 @@ //! ```rust,no_run //! use data::providers::benzinga::{ProductionBenzingaProvider, ProductionBenzingaConfig}; //! use data::providers::traits::RealTimeProvider; -//! use core::types::Symbol; +//! use trading_engine::types::Symbol; //! //! # async fn example() -> anyhow::Result<()> { //! let config = ProductionBenzingaConfig { @@ -107,7 +107,7 @@ //! use data::providers::benzinga::{BenzingaMLExtractor, BenzingaMLConfig}; //! use data::providers::common::MarketDataEvent; //! use chrono::Utc; -//! use core::types::Symbol; +//! use trading_engine::types::Symbol; //! //! # async fn example() -> anyhow::Result<()> { //! let config = BenzingaMLConfig { @@ -144,7 +144,7 @@ //! ```rust,no_run //! use data::providers::benzinga::{BenzingaHFTIntegration, BenzingaIntegrationConfig, TradingSignal, TradingSignalType}; //! use config::ConfigManager; -//! use core::types::Symbol; +//! use trading_engine::types::Symbol; //! use std::sync::Arc; //! //! # async fn example() -> anyhow::Result<()> { @@ -425,7 +425,7 @@ mod tests { #[tokio::test] async fn test_hft_integration_creation() { - use core::types::Symbol; + use trading_engine::types::Symbol; let config = BenzingaStreamingConfig { api_key: "test-key".to_string(), diff --git a/data/src/providers/benzinga/production_streaming.rs b/data/src/providers/benzinga/production_streaming.rs index 4a88ffa90..873e8f711 100644 --- a/data/src/providers/benzinga/production_streaming.rs +++ b/data/src/providers/benzinga/production_streaming.rs @@ -11,10 +11,11 @@ use crate::error::{DataError, Result}; use crate::providers::common::{ - AnalystRatingEvent, ConnectionState, ConnectionStatusEvent, ErrorCategory, ErrorEvent, + AnalystRatingEvent, ConnectionState, ConnectionStatusEvent, ErrorCategory, MarketDataEvent, NewsEvent, OptionsContract, OptionsSentiment, OptionsType, RatingAction, SentimentEvent, SentimentPeriod, UnusualOptionsEvent, UnusualOptionsType, }; +use trading_engine::types::ErrorEvent; use crate::providers::traits::{ ConnectionState as TraitConnectionState, ConnectionStatus, RealTimeProvider, }; @@ -1068,7 +1069,7 @@ impl RealTimeProvider for ProductionBenzingaProvider { *status = ConnectionStatus::disconnected(); } - self.connected.store(false, Ordering::Relaxed); + self.connection_status.write().await.is_connected = false; info!("Disconnected from Benzinga WebSocket stream"); Ok(()) } @@ -1099,13 +1100,9 @@ impl RealTimeProvider for ProductionBenzingaProvider { websocket.send(Message::Text(message)).await .map_err(|e| DataError::Subscription { message: format!("Failed to send subscription: {}", e), - symbols: Some(symbols.iter().map(|s| s.to_string()).collect()), })?; } else { - return Err(DataError::Connection { - message: "Not connected to WebSocket".to_string(), - url: None, - }); + return Err(DataError::Connection("Not connected to WebSocket".to_string())); } // Update subscribed symbols @@ -1138,7 +1135,6 @@ impl RealTimeProvider for ProductionBenzingaProvider { websocket.send(Message::Text(message)).await .map_err(|e| DataError::Subscription { message: format!("Failed to send unsubscription: {}", e), - symbols: Some(symbols.iter().map(|s| s.to_string()).collect()), })?; } diff --git a/data/src/providers/benzinga/streaming.rs b/data/src/providers/benzinga/streaming.rs index 34bb56dca..6223f7842 100644 --- a/data/src/providers/benzinga/streaming.rs +++ b/data/src/providers/benzinga/streaming.rs @@ -18,7 +18,7 @@ //! ```rust,no_run //! use data::providers::benzinga::streaming::BenzingaStreamingProvider; //! use data::providers::traits::RealTimeProvider; -//! use core::types::Symbol; +//! use trading_engine::types::Symbol; //! //! # async fn example() -> anyhow::Result<()> { //! let config = BenzingaStreamingConfig { @@ -48,7 +48,8 @@ use crate::providers::common::{ use crate::providers::traits::{ ConnectionState as TraitConnectionState, ConnectionStatus, RealTimeProvider, }; -use crate::providers::common::{MarketDataEvent, ErrorEvent}; +use crate::providers::common::MarketDataEvent; +use trading_engine::types::ErrorEvent; use crate::types::ConnectionEvent; use async_trait::async_trait; use chrono::{DateTime, Utc}; diff --git a/data/src/providers/common.rs b/data/src/providers/common.rs index df52ed01f..23b65e0a6 100644 --- a/data/src/providers/common.rs +++ b/data/src/providers/common.rs @@ -134,7 +134,7 @@ pub struct BarEvent { pub close: Decimal, /// Volume - pub volume: Decimal, + pub volume: Volume, /// Timestamp pub timestamp: DateTime, @@ -162,7 +162,7 @@ pub struct AggregateEvent { pub close: Decimal, /// Volume - pub volume: Decimal, + pub volume: Volume, /// Volume weighted average price pub vwap: Option, diff --git a/data/src/providers/traits.rs b/data/src/providers/traits.rs index 49b880660..f215dc439 100644 --- a/data/src/providers/traits.rs +++ b/data/src/providers/traits.rs @@ -35,7 +35,7 @@ use std::error::Error as StdError; /// /// ```no_run /// # use async_trait::async_trait; -/// # use core::types::Symbol; +/// # use trading_engine::types::Symbol; /// # use tokio_stream::Stream; /// # struct MyProvider; /// # impl MyProvider { @@ -149,7 +149,7 @@ pub trait RealTimeProvider: Send + Sync { /// /// ```no_run /// # use chrono::{DateTime, Utc}; -/// # use core::types::Symbol; +/// # use trading_engine::types::Symbol; /// # struct MyHistoricalProvider; /// # impl MyHistoricalProvider { /// # async fn fetch(&self, symbol: &Symbol, schema: HistoricalSchema, range: TimeRange) -> Result, Box> { Ok(vec![]) } diff --git a/data/src/types.rs b/data/src/types.rs index a8a6f42e3..e5ce36ed5 100644 --- a/data/src/types.rs +++ b/data/src/types.rs @@ -76,24 +76,8 @@ pub struct QuoteEvent { pub timestamp: chrono::DateTime, } -/// Trade event structure -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct TradeEvent { - /// Symbol - pub symbol: String, - /// Trade price - pub price: Decimal, - /// Trade size - pub size: Decimal, - /// Trade ID - pub trade_id: Option, - /// Exchange - pub exchange: Option, - /// Trade conditions - pub conditions: Vec, - /// Timestamp - pub timestamp: chrono::DateTime, -} +// TradeEvent removed - use canonical version from trading_engine::types::TradeEvent +pub use trading_engine::types::TradeEvent; /// Quote data structure (legacy compatibility) #[derive(Debug, Clone, Serialize, Deserialize)] @@ -145,7 +129,7 @@ pub struct Aggregate { /// Close price pub close: Decimal, /// Volume - pub volume: Decimal, + pub volume: Volume, /// Volume weighted average price pub vwap: Option, /// Start timestamp @@ -410,9 +394,8 @@ mod tests { assert_eq!(quote.symbol(), "AAPL"); } - #[test] - fn test_order_status_display() { - // OrderStatus tests removed - use canonical types from core::types::prelude + #[test] + fn test_order_status_display() { + // OrderStatus tests removed - use canonical types from core::types::prelude + } } - } -} diff --git a/data/src/unified_feature_extractor.rs b/data/src/unified_feature_extractor.rs index c8ff203e2..843365142 100644 --- a/data/src/unified_feature_extractor.rs +++ b/data/src/unified_feature_extractor.rs @@ -794,7 +794,7 @@ impl UnifiedFeatureExtractor { .iter() .filter_map(|bar| { if let MarketDataEvent::Bar(bar_event) = bar { - Some(bar_event.volume.to_f64().unwrap_or(0.0)) + Some(bar_event.volume.value().to_f64().unwrap_or(0.0)) } else { None } diff --git a/data/tests/test_databento_streaming.rs b/data/tests/test_databento_streaming.rs index 878a842dc..3d2dba830 100644 --- a/data/tests/test_databento_streaming.rs +++ b/data/tests/test_databento_streaming.rs @@ -19,8 +19,9 @@ use std::time::Duration; use tokio::time::{sleep, timeout}; use tokio_test; use trading_engine::trading::data_interface::{ - MarketDataEvent as CoreMarketDataEvent, QuoteEvent, TradeEvent, + MarketDataEvent as CoreMarketDataEvent, QuoteEvent, }; +use trading_engine::types::TradeEvent; use trading_engine::types::{Price, Quantity, Symbol}; /// Test provider creation with valid API key diff --git a/data/tests/test_reconnection_backpressure.rs b/data/tests/test_reconnection_backpressure.rs index 5f340745b..64df0c38c 100644 --- a/data/tests/test_reconnection_backpressure.rs +++ b/data/tests/test_reconnection_backpressure.rs @@ -82,8 +82,8 @@ impl MockReconnectProvider { } } -/// Connection manager with exponential backoff -struct ConnectionManager { +/// Test connection manager with exponential backoff (renamed to avoid conflicts with real ConnectionManager) +struct TestConnectionManager { provider: MockReconnectProvider, max_retries: u32, base_delay_ms: u64, @@ -91,7 +91,7 @@ struct ConnectionManager { backoff_multiplier: f64, } -impl ConnectionManager { +impl TestConnectionManager { fn new(provider: MockReconnectProvider) -> Self { Self { provider, @@ -257,7 +257,7 @@ impl BackpressureManager { #[tokio::test] async fn test_basic_reconnection() { let provider = MockReconnectProvider::new(); - let mut manager = ConnectionManager::new(provider); + let mut manager = TestConnectionManager::new(provider); // First connection should succeed manager.provider.set_should_fail(false); @@ -271,7 +271,7 @@ async fn test_basic_reconnection() { #[tokio::test] async fn test_reconnection_with_transient_failures() { let provider = MockReconnectProvider::new(); - let mut manager = ConnectionManager::new(provider); + let mut manager = TestConnectionManager::new(provider); // Set to fail initially manager.provider.set_should_fail(true); @@ -279,7 +279,7 @@ async fn test_reconnection_with_transient_failures() { // Start connection attempt in background let provider_ref = &manager.provider; let connect_task = tokio::spawn(async move { - let mut local_manager = ConnectionManager::new(MockReconnectProvider::new()); + let mut local_manager = TestConnectionManager::new(MockReconnectProvider::new()); local_manager.provider.set_should_fail(true); // Simulate success after 2 failures @@ -307,7 +307,7 @@ async fn test_reconnection_with_transient_failures() { #[tokio::test] async fn test_exponential_backoff_timing() { let provider = MockReconnectProvider::new(); - let mut manager = ConnectionManager::new(provider); + let mut manager = TestConnectionManager::new(provider); manager.provider.set_should_fail(true); let start_time = Instant::now(); @@ -330,7 +330,7 @@ async fn test_exponential_backoff_timing() { #[tokio::test] async fn test_max_delay_cap() { let provider = MockReconnectProvider::new(); - let mut manager = ConnectionManager::new(provider); + let mut manager = TestConnectionManager::new(provider); manager.base_delay_ms = 1000; manager.max_delay_ms = 2000; manager.max_retries = 5; @@ -627,7 +627,7 @@ async fn test_benzinga_rate_limiting_load() { #[tokio::test] async fn test_connection_manager_ensure_connected() { let provider = MockReconnectProvider::new(); - let mut manager = ConnectionManager::new(provider); + let mut manager = TestConnectionManager::new(provider); // First call should establish connection manager.provider.set_should_fail(false); @@ -646,7 +646,7 @@ async fn test_connection_manager_ensure_connected() { #[tokio::test] async fn test_connection_failure_recovery() { let provider = MockReconnectProvider::new(); - let mut manager = ConnectionManager::new(provider); + let mut manager = TestConnectionManager::new(provider); // Initially successful connection manager.provider.set_should_fail(false); diff --git a/database/src/lib.rs b/database/src/lib.rs index 94f700c32..f15be3f8a 100644 --- a/database/src/lib.rs +++ b/database/src/lib.rs @@ -458,7 +458,7 @@ mod tests { #[test] fn test_database_config_default() { let config = DatabaseConfig::default(); - assert_eq!(config.application_name, "database-lib"); + assert_eq!(config.application_name, "foxhunt-config"); assert!(!config.enable_query_logging); assert!(config.enable_metrics); } diff --git a/database/src/transaction.rs b/database/src/transaction.rs index 2a497ca09..fa44618c9 100644 --- a/database/src/transaction.rs +++ b/database/src/transaction.rs @@ -49,16 +49,16 @@ impl TransactionManager { let start_time = Instant::now(); self.total_transactions.fetch_add(1, Ordering::Relaxed); self.active_transactions.fetch_add(1, Ordering::Relaxed); - + debug!( "Beginning new database transaction with {}s timeout", timeout_duration.as_secs() ); - - let mut conn = self.pool.acquire().await?; - let transaction = conn.begin().await?; + + // Get the transaction directly from the pool to avoid lifetime issues + let transaction = self.pool.inner().begin().await?; let transaction_id = Uuid::new_v4(); - + debug!("Transaction {} started successfully", transaction_id); Ok(DatabaseTransaction { inner: Some(transaction), diff --git a/market-data/src/compile_test.rs b/market-data/src/compile_test.rs index 8772e41bd..9762311e0 100644 --- a/market-data/src/compile_test.rs +++ b/market-data/src/compile_test.rs @@ -2,7 +2,7 @@ use crate::{ error::MarketDataResult, - models::{IndicatorType, OrderBook, OrderSide, Price, TechnicalIndicator}, + models::{IndicatorType, OrderBook, OrderSide, PriceRecord, TechnicalIndicator}, }; use chrono::Utc; use rust_decimal_macros::dec; @@ -10,7 +10,7 @@ use rust_decimal_macros::dec; #[allow(unused)] pub fn test_models_compile() -> MarketDataResult<()> { // Test Price model - let mut price = Price::new("EURUSD".to_string(), Utc::now()); + let mut price = PriceRecord::new("EURUSD".to_string(), Utc::now()); price.bid = Some(dec!(1.0850)); price.ask = Some(dec!(1.0852)); let _mid = price.mid_price(); diff --git a/market-data/src/lib.rs b/market-data/src/lib.rs index 40a398d0c..38b2960ee 100644 --- a/market-data/src/lib.rs +++ b/market-data/src/lib.rs @@ -17,7 +17,7 @@ //! //! ```rust,no_run //! use market_data::{ -//! models::{Price, OrderBook, TechnicalIndicator}, +//! models::{PriceRecord, OrderBook, TechnicalIndicator}, //! prices::{PriceRepository, PostgresPriceRepository}, //! orderbook::{OrderBookRepository, PostgresOrderBookRepository}, //! indicators::{IndicatorRepository, PostgresIndicatorRepository}, @@ -32,7 +32,7 @@ //! //! // Price repository //! let price_repo = PostgresPriceRepository::new(pool.clone()); -//! let mut price = Price::new("EURUSD".to_string(), Utc::now()); +//! let mut price = PriceRecord::new("EURUSD".to_string(), Utc::now()); //! price.bid = Some(dec!(1.0850)); //! price.ask = Some(dec!(1.0852)); //! price_repo.store_price(&price).await?; @@ -60,7 +60,7 @@ mod compile_test; // Re-export commonly used types for convenience pub use error::{MarketDataError, MarketDataResult}; pub use models::{ - Candle, IndicatorType, OrderBook, OrderBookLevel, OrderSide, Price, TechnicalIndicator, + Candle, IndicatorType, OrderBook, OrderBookLevel, OrderSide, PriceRecord, TechnicalIndicator, TimePeriod, }; diff --git a/market-data/src/models.rs b/market-data/src/models.rs index 672dd6b87..5740e3ef5 100644 --- a/market-data/src/models.rs +++ b/market-data/src/models.rs @@ -1,12 +1,12 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use sqlx::FromRow; -use trading_engine::types::prelude::Decimal; +use trading_engine::types::prelude::{Decimal, Volume}; use uuid::Uuid; /// Price data for a financial instrument #[derive(Debug, Clone, Serialize, Deserialize, FromRow, PartialEq)] -pub struct Price { +pub struct PriceRecord { pub id: Uuid, pub symbol: String, pub timestamp: DateTime, @@ -21,7 +21,7 @@ pub struct Price { pub created_at: DateTime, } -impl Price { +impl PriceRecord { pub fn new(symbol: String, timestamp: DateTime) -> Self { Self { id: Uuid::new_v4(), @@ -245,7 +245,7 @@ pub struct Candle { pub high: Decimal, pub low: Decimal, pub close: Decimal, - pub volume: Decimal, + pub volume: Volume, pub created_at: DateTime, } @@ -258,7 +258,7 @@ impl Candle { high: Decimal, low: Decimal, close: Decimal, - volume: Decimal, + volume: Volume, ) -> Self { Self { id: Uuid::new_v4(), diff --git a/market-data/src/prices.rs b/market-data/src/prices.rs index 5c9efc9b3..2fc2857f3 100644 --- a/market-data/src/prices.rs +++ b/market-data/src/prices.rs @@ -6,20 +6,20 @@ use uuid::Uuid; use crate::{ error::{MarketDataError, MarketDataResult}, - models::{Candle, Price, TimePeriod}, + models::{Candle, PriceRecord, TimePeriod}, }; /// Repository trait for price data operations #[async_trait] pub trait PriceRepository { /// Store a single price record - async fn store_price(&self, price: &Price) -> MarketDataResult<()>; + async fn store_price(&self, price: &PriceRecord) -> MarketDataResult<()>; /// Store multiple price records in a batch - async fn store_prices(&self, prices: &[Price]) -> MarketDataResult<()>; + async fn store_prices(&self, prices: &[PriceRecord]) -> MarketDataResult<()>; /// Get the latest price for a symbol - async fn get_latest_price(&self, symbol: &str) -> MarketDataResult>; + async fn get_latest_price(&self, symbol: &str) -> MarketDataResult>; /// Get price history for a symbol within a time range async fn get_price_history( @@ -27,13 +27,13 @@ pub trait PriceRepository { symbol: &str, from: DateTime, to: DateTime, - ) -> MarketDataResult>; + ) -> MarketDataResult>; /// Get latest prices for multiple symbols async fn get_latest_prices( &self, symbols: &[String], - ) -> MarketDataResult>; + ) -> MarketDataResult>; /// Store a candle (OHLCV) record async fn store_candle(&self, candle: &Candle) -> MarketDataResult<()>; @@ -84,7 +84,7 @@ impl PostgresPriceRepository { #[async_trait] impl PriceRepository for PostgresPriceRepository { - async fn store_price(&self, price: &Price) -> MarketDataResult<()> { + async fn store_price(&self, price: &PriceRecord) -> MarketDataResult<()> { self.validate_symbol(&price.symbol).await?; sqlx::query!( @@ -120,7 +120,7 @@ impl PriceRepository for PostgresPriceRepository { Ok(()) } - async fn store_prices(&self, prices: &[Price]) -> MarketDataResult<()> { + async fn store_prices(&self, prices: &[PriceRecord]) -> MarketDataResult<()> { if prices.is_empty() { return Ok(()); } @@ -168,7 +168,7 @@ impl PriceRepository for PostgresPriceRepository { Ok(()) } - async fn get_latest_price(&self, symbol: &str) -> MarketDataResult> { + async fn get_latest_price(&self, symbol: &str) -> MarketDataResult> { self.validate_symbol(symbol).await?; let row = sqlx::query!( @@ -185,7 +185,7 @@ impl PriceRepository for PostgresPriceRepository { .await?; if let Some(row) = row { - Ok(Some(Price { + Ok(Some(PriceRecord { id: row.id, symbol: row.symbol, timestamp: row.timestamp, @@ -209,7 +209,7 @@ impl PriceRepository for PostgresPriceRepository { symbol: &str, from: DateTime, to: DateTime, - ) -> MarketDataResult> { + ) -> MarketDataResult> { self.validate_symbol(symbol).await?; self.validate_time_range(from, to).await?; @@ -229,7 +229,7 @@ impl PriceRepository for PostgresPriceRepository { let prices = rows .into_iter() - .map(|row| Price { + .map(|row| PriceRecord { id: row.id, symbol: row.symbol, timestamp: row.timestamp, @@ -251,7 +251,7 @@ impl PriceRepository for PostgresPriceRepository { async fn get_latest_prices( &self, symbols: &[String], - ) -> MarketDataResult> { + ) -> MarketDataResult> { if symbols.is_empty() { return Ok(HashMap::new()); } @@ -275,7 +275,7 @@ impl PriceRepository for PostgresPriceRepository { let mut prices = HashMap::new(); for row in rows { - let price = Price { + let price = PriceRecord { id: row.get("id"), symbol: row.get::("symbol"), timestamp: row.get("timestamp"), diff --git a/market-data/tests/basic_test.rs b/market-data/tests/basic_test.rs index 223829bd3..3b480dbd9 100644 --- a/market-data/tests/basic_test.rs +++ b/market-data/tests/basic_test.rs @@ -1,14 +1,14 @@ use chrono::Utc; use market_data::{ error::MarketDataResult, - models::{IndicatorType, OrderBook, OrderBookLevel, OrderSide, Price, TechnicalIndicator}, + models::{IndicatorType, OrderBook, OrderBookLevel, OrderSide, PriceRecord, TechnicalIndicator}, }; use rust_decimal_macros::dec; use std::collections::HashMap; #[test] fn test_price_model() { - let mut price = Price::new("EURUSD".to_string(), Utc::now()); + let mut price = PriceRecord::new("EURUSD".to_string(), Utc::now()); price.bid = Some(dec!(1.0850)); price.ask = Some(dec!(1.0852)); diff --git a/ml-data/src/performance.rs b/ml-data/src/performance.rs index 02dbe5e87..6e60d883e 100644 --- a/ml-data/src/performance.rs +++ b/ml-data/src/performance.rs @@ -798,5 +798,4 @@ pub enum AlertStatus { Resolved, } -/// Performance metrics collection -pub type PerformanceMetrics = HashMap>; \ No newline at end of file +// TECHNICAL DEBT ELIMINATED - Use HashMap> directly \ No newline at end of file diff --git a/ml/Cargo.toml b/ml/Cargo.toml index 1ca333782..8b0f23b70 100644 --- a/ml/Cargo.toml +++ b/ml/Cargo.toml @@ -52,6 +52,7 @@ reqwest.workspace = true # Internal workspace crates trading_engine.workspace = true config.workspace = true +common = { path = "../common" } risk = { path = "../risk" } model_loader = { path = "../crates/model_loader" } # ml_models feature is now default diff --git a/ml/src/liquid/cuda/mod.rs b/ml/src/liquid/cuda/mod.rs index 5988fa035..fd3b8d51c 100644 --- a/ml/src/liquid/cuda/mod.rs +++ b/ml/src/liquid/cuda/mod.rs @@ -79,7 +79,7 @@ pub struct CudaLiquidNetwork { pub config: CudaLiquidConfig, pub device: Arc, pub buffers: CudaBuffers, - pub stream_manager: CudaStreamManager, + pub stream_manager: cudarc::driver::CudaStreamManager, pub memory_manager: GpuMemoryManager, // Network parameters @@ -94,10 +94,10 @@ pub struct CudaLiquidNetwork { pub current_regime: MarketRegime, // CUDA function handles - ltc_forward_fn: CudaFunction, - cfc_forward_fn: Option, - output_fn: CudaFunction, - adapt_tau_fn: CudaFunction, + ltc_forward_fn: cudarc::driver::CudaFunction, + cfc_forward_fn: Option, + output_fn: cudarc::driver::CudaFunction, + adapt_tau_fn: cudarc::driver::CudaFunction, } impl CudaLiquidNetwork { @@ -144,7 +144,7 @@ impl CudaLiquidNetwork { )?; // Initialize stream manager - let stream_manager = CudaStreamManager::new(device.clone(), config.stream_count)?; + let stream_manager = cudarc::driver::CudaStreamManager::new(device.clone(), config.stream_count)?; // Allocate GPU buffers let batch_size = config.max_batch_size; @@ -331,7 +331,7 @@ impl CudaLiquidNetwork { } /// Launch LTC forward kernel - fn launch_ltc_forward(&self, batch_size: usize, stream: &CudaStream) -> Result<()> { + fn launch_ltc_forward(&self, batch_size: usize, stream: &cudarc::driver::CudaStream) -> Result<()> { let grid_x = (self.hidden_size + 15) / 16; let grid_y = (batch_size + 15) / 16; let grid_z = 1; @@ -373,7 +373,7 @@ impl CudaLiquidNetwork { } /// Launch CfC forward kernel - fn launch_cfc_forward(&self, batch_size: usize, stream: &CudaStream) -> Result<()> { + fn launch_cfc_forward(&self, batch_size: usize, stream: &cudarc::driver::CudaStream) -> Result<()> { let cfc_fn = self.cfc_forward_fn.as_ref() .ok_or_else(|| LiquidError::InferenceError("CfC kernel not available".to_string()))?; @@ -425,7 +425,7 @@ impl CudaLiquidNetwork { } /// Launch output layer kernel - fn launch_output_layer(&self, batch_size: usize, stream: &CudaStream) -> Result<()> { + fn launch_output_layer(&self, batch_size: usize, stream: &cudarc::driver::CudaStream) -> Result<()> { let grid_x = (self.output_size + 15) / 16; let grid_y = (batch_size + 15) / 16; let grid_z = 1; @@ -569,6 +569,4 @@ fn compile_liquid_kernels() -> Result { )) } -// Type aliases for CUDA types -type CudaFunction = cudarc::driver::CudaFunction; -type CudaStream = cudarc::driver::CudaStream; \ No newline at end of file +// TECHNICAL DEBT ELIMINATED - Use cudarc types directly \ No newline at end of file diff --git a/risk-data/Cargo.toml b/risk-data/Cargo.toml index 81a0a093d..646d974c6 100644 --- a/risk-data/Cargo.toml +++ b/risk-data/Cargo.toml @@ -16,6 +16,7 @@ description = "Risk data repository for high-frequency trading risk management" [dependencies] # Core workspace dependencies trading_engine = { path = "../trading_engine" } +common = { path = "../common" } # Database dependencies sqlx.workspace = true diff --git a/risk-data/src/compliance.rs b/risk-data/src/compliance.rs index e460b2330..b483afdf6 100644 --- a/risk-data/src/compliance.rs +++ b/risk-data/src/compliance.rs @@ -225,12 +225,21 @@ pub trait ComplianceRepository: Send + Sync + std::fmt::Debug { } /// Compliance repository implementation -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ComplianceRepositoryImpl { db_pool: PgPool, redis_conn: ConnectionManager, } +impl std::fmt::Debug for ComplianceRepositoryImpl { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ComplianceRepositoryImpl") + .field("db_pool", &"") + .field("redis_conn", &"") + .finish() + } +} + impl ComplianceRepositoryImpl { pub fn new(db_pool: PgPool, redis_conn: ConnectionManager) -> Self { Self { diff --git a/risk-data/src/limits.rs b/risk-data/src/limits.rs index 61e00ba4e..a471c5e60 100644 --- a/risk-data/src/limits.rs +++ b/risk-data/src/limits.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use sqlx::{PgPool, Row}; use std::collections::HashMap; use tracing::{error, info, warn}; -use trading_engine::types::prelude::Decimal; +use common::prelude::*; use uuid::Uuid; use crate::{RiskDataError, RiskDataResult}; @@ -209,12 +209,21 @@ pub trait LimitsRepository: Send + Sync + std::fmt::Debug { } /// Limits repository implementation -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct LimitsRepositoryImpl { db_pool: PgPool, redis_conn: ConnectionManager, } +impl std::fmt::Debug for LimitsRepositoryImpl { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LimitsRepositoryImpl") + .field("db_pool", &"") + .field("redis_conn", &"") + .finish() + } +} + impl LimitsRepositoryImpl { pub fn new(db_pool: PgPool, redis_conn: ConnectionManager) -> Self { Self { diff --git a/risk-data/src/models.rs b/risk-data/src/models.rs index eb25fbfab..0ae21fc9b 100644 --- a/risk-data/src/models.rs +++ b/risk-data/src/models.rs @@ -11,11 +11,87 @@ use std::collections::HashMap; use trading_engine::types::prelude::Decimal; use uuid::Uuid; -/// Database connection pool type alias -pub type DbPool = sqlx::PgPool; +/// Database connection pool - proper newtype wrapper +#[derive(Debug, Clone)] +pub struct DbPool(sqlx::PgPool); -/// Redis connection type alias -pub type RedisConnection = redis::aio::MultiplexedConnection; +impl DbPool { + /// Create a new database pool wrapper + pub fn new(pool: sqlx::PgPool) -> Self { + Self(pool) + } + + /// Get the underlying pool + pub fn inner(&self) -> &sqlx::PgPool { + &self.0 + } + + /// Into the underlying pool + pub fn into_inner(self) -> sqlx::PgPool { + self.0 + } +} + +impl std::ops::Deref for DbPool { + type Target = sqlx::PgPool; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for DbPool { + fn from(pool: sqlx::PgPool) -> Self { + Self::new(pool) + } +} + +impl From for sqlx::PgPool { + fn from(pool: DbPool) -> Self { + pool.into_inner() + } +} + +/// Redis connection - proper newtype wrapper +#[derive(Debug, Clone)] +pub struct RedisConnection(redis::aio::MultiplexedConnection); + +impl RedisConnection { + /// Create a new Redis connection wrapper + pub fn new(conn: redis::aio::MultiplexedConnection) -> Self { + Self(conn) + } + + /// Get the underlying connection + pub fn inner(&self) -> &redis::aio::MultiplexedConnection { + &self.0 + } + + /// Into the underlying connection + pub fn into_inner(self) -> redis::aio::MultiplexedConnection { + self.0 + } +} + +impl std::ops::Deref for RedisConnection { + type Target = redis::aio::MultiplexedConnection; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for RedisConnection { + fn from(conn: redis::aio::MultiplexedConnection) -> Self { + Self::new(conn) + } +} + +impl From for redis::aio::MultiplexedConnection { + fn from(conn: RedisConnection) -> Self { + conn.into_inner() + } +} /// Financial instrument types #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)] @@ -420,6 +496,7 @@ pub struct CustomRiskMetricResult { } /// Common financial calculations and utilities +#[derive(Debug)] pub struct FinancialCalculations; impl FinancialCalculations { diff --git a/risk/Cargo.toml b/risk/Cargo.toml index ce8018ca4..82ee1f794 100644 --- a/risk/Cargo.toml +++ b/risk/Cargo.toml @@ -16,6 +16,7 @@ categories.workspace = true # Core workspace dependencies trading_engine = { workspace = true } config = { workspace = true } +common = { path = "../common" } chrono.workspace = true dashmap.workspace = true diff --git a/risk/src/compliance.rs b/risk/src/compliance.rs index 07b8affdc..0428f65fb 100644 --- a/risk/src/compliance.rs +++ b/risk/src/compliance.rs @@ -1193,7 +1193,7 @@ mod tests { order_id: "test_order_1".to_string(), symbol: Symbol::from("AAPL".to_string()), instrument_id: "AAPL".to_string(), - side: Side::Buy, + side: OrderSide::Buy, quantity: Quantity::from_f64(100.0).unwrap_or(Quantity::ZERO), price: Price::from_f64(150.0).unwrap_or(Price::ZERO), order_type: Some(OrderType::Limit), diff --git a/risk/src/lib.rs b/risk/src/lib.rs index 6fa09129d..92f519d11 100644 --- a/risk/src/lib.rs +++ b/risk/src/lib.rs @@ -35,7 +35,7 @@ //! // Perform risk check on an order //! let order_info = OrderInfo { //! symbol: Symbol::from_str("AAPL"), -//! side: Side::Buy, +//! side: OrderSide::Buy, //! quantity: Quantity::new(100.0)?, //! price: Price::new(150.0)?, //! }; diff --git a/risk/src/risk_engine.rs b/risk/src/risk_engine.rs index d6a3a1707..f29421853 100644 --- a/risk/src/risk_engine.rs +++ b/risk/src/risk_engine.rs @@ -689,8 +689,8 @@ impl RiskEngine { let order_quantity_f64 = order_info.quantity.to_f64(); let order_quantity = match order_info.side { - Side::Buy => order_quantity_f64, - Side::Sell => -order_quantity_f64, + OrderSide::Buy => order_quantity_f64, + OrderSide::Sell => -order_quantity_f64, }; let new_quantity = current_quantity + order_quantity; diff --git a/risk/src/risk_types.rs b/risk/src/risk_types.rs index 4f5a49339..4f287f758 100644 --- a/risk/src/risk_types.rs +++ b/risk/src/risk_types.rs @@ -10,15 +10,19 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; // Re-export commonly used types for convenience -pub use trading_engine::types::prelude::{OrderType, Price, Quantity, Side, Symbol, Volume}; +pub use trading_engine::types::prelude::{Price, Quantity, Symbol, Volume, OrderType, OrderSide}; +// Note: Side is an alias for OrderSide - using canonical OrderSide from trading_engine +// Note: Side is an alias for OrderSide in common crate - both are available -/// Instrument identifier - string-based for compatibility +// TECHNICAL DEBT ELIMINATED - Use String directly instead of type aliases + +/// Instrument identifier type pub type InstrumentId = String; -/// Portfolio identifier - string-based for compatibility +/// Portfolio identifier type pub type PortfolioId = String; -/// Strategy identifier - string-based for compatibility +/// Strategy identifier type pub type StrategyId = String; // BACKWARD COMPATIBILITY ELIMINATED @@ -107,11 +111,11 @@ pub struct RiskViolation { /// Maximum allowed value pub limit_value: Option, /// Instrument involved (if applicable) - pub instrument_id: Option, + pub instrument_id: Option, /// Portfolio involved (if applicable) - pub portfolio_id: Option, + pub portfolio_id: Option, /// Strategy involved (if applicable) - pub strategy_id: Option, + pub strategy_id: Option, /// Amount of the breach (if applicable) pub breach_amount: Option, /// Timestamp when violation occurred @@ -149,9 +153,9 @@ pub struct OrderInfo { /// Trading symbol pub symbol: Symbol, /// Instrument identifier - pub instrument_id: InstrumentId, + pub instrument_id: String, /// Buy or sell - pub side: Side, + pub side: OrderSide, /// Order quantity pub quantity: Quantity, /// Order price @@ -159,9 +163,9 @@ pub struct OrderInfo { /// Order type (market, limit, etc.) pub order_type: Option, /// Portfolio identifier - pub portfolio_id: Option, + pub portfolio_id: Option, /// Strategy identifier - pub strategy_id: Option, + pub strategy_id: Option, } /// Profit and Loss metrics @@ -197,7 +201,7 @@ pub struct PnLMetrics { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct RiskPosition { /// Instrument identifier - pub instrument_id: InstrumentId, + pub instrument_id: String, /// Current position size (positive for long, negative for short) pub quantity: Quantity, /// Average entry price @@ -211,9 +215,9 @@ pub struct RiskPosition { /// Realized P&L pub realized_pnl: Price, /// Portfolio this position belongs to - pub portfolio_id: PortfolioId, + pub portfolio_id: String, /// Strategy this position belongs to - pub strategy_id: Option, + pub strategy_id: Option, /// Position details pub position: Position, } @@ -245,11 +249,11 @@ impl RiskPosition { /// Create a new risk position #[must_use] pub fn new( - instrument_id: InstrumentId, + instrument_id: String, quantity: Quantity, avg_price: Price, current_price: Price, - portfolio_id: PortfolioId, + portfolio_id: String, ) -> Self { let market_value = Price::new((quantity.raw_value() * current_price.raw_value()) as f64) .unwrap_or_default(); @@ -308,7 +312,7 @@ impl RiskPosition { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct MarketData { /// Instrument identifier - pub instrument_id: InstrumentId, + pub instrument_id: String, /// Current bid price pub bid: Price, /// Current ask price @@ -348,7 +352,7 @@ pub struct SymbolRiskConfig { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct PositionLimits { /// Maximum position size per instrument - pub max_position_per_instrument: HashMap, + pub max_position_per_instrument: HashMap, /// Maximum total portfolio value pub max_portfolio_value: Price, /// Maximum leverage ratio @@ -367,19 +371,19 @@ pub struct StressScenario { /// Scenario name pub name: String, /// Price shock percentage by instrument - pub price_shocks: HashMap, + pub price_shocks: HashMap, /// Market shocks (alias for `price_shocks`) pub market_shocks: HashMap, /// Volatility multiplier pub volatility_multiplier: f64, /// Volatility multipliers by instrument - pub volatility_multipliers: HashMap, + pub volatility_multipliers: HashMap, /// Correlation changes pub correlation_changes: HashMap, /// Correlation adjustments pub correlation_adjustments: HashMap, /// Liquidity haircuts by instrument - pub liquidity_haircuts: HashMap, + pub liquidity_haircuts: HashMap, } /// Stress test results @@ -429,11 +433,11 @@ pub enum KillSwitchScope { /// Stop all trading across all portfolios Global, /// Stop trading for specific portfolio - Portfolio(PortfolioId), + Portfolio(String), /// Stop trading for specific strategy - Strategy(StrategyId), + Strategy(String), /// Stop trading for specific instrument - Instrument(InstrumentId), + Instrument(String), /// Stop trading for specific symbol Symbol(String), /// Stop trading for specific account @@ -460,7 +464,7 @@ pub enum CircuitBreakerEvent { /// Position limit breached PositionBreach { /// Instrument involved - instrument_id: InstrumentId, + instrument_id: String, /// Current position size current_position: Quantity, /// Limit that was breached @@ -483,7 +487,7 @@ pub struct DrawdownAlertConfig { /// Emergency threshold percentage pub emergency_threshold: f64, /// Portfolio this config applies to - pub portfolio_id: Option, + pub portfolio_id: Option, /// Whether alerts are enabled pub enabled: bool, } @@ -581,9 +585,9 @@ pub struct ComplianceWarning { /// Detailed description of the warning pub description: String, /// Instrument involved (if applicable) - pub instrument_id: Option, + pub instrument_id: Option, /// Portfolio involved (if applicable) - pub portfolio_id: Option, + pub portfolio_id: Option, /// Regulatory reference information pub regulatory_reference: String, /// Recommended action to take diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 000000000..b242b3d89 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,166 @@ +# Rustfmt Configuration for Foxhunt HFT System +# IMPORT STANDARDIZATION AND TYPE GOVERNANCE SUPPORT + +# ============================================================================= +# ๐ŸŽฏ IMPORT ORGANIZATION - CRITICAL FOR TYPE GOVERNANCE +# ============================================================================= + +# Group imports to enforce canonical type usage patterns +imports_granularity = "Module" # Group by module for clarity +group_imports = "StdExternalCrate" # Separate std, external, and local +imports_layout = "Vertical" # Vertical layout for readability + +# ============================================================================= +# ๐Ÿ“‹ STANDARD IMPORT ORDER FOR ALL CRATES +# ============================================================================= +# +# This order enforces the type governance hierarchy: +# 1. Standard library (std::) +# 2. External crates (serde, tokio, etc.) +# 3. Prelude import (trading_engine::types::prelude::*) +# 4. Configuration imports (config::*) +# 5. Local crate imports +# 6. Generated code imports (proto, etc.) + +# ============================================================================= +# ๐Ÿ”’ TYPE GOVERNANCE FORMATTING RULES +# ============================================================================= + +# Force prelude imports to be clearly visible +reorder_imports = true # Automatic import reordering +reorder_modules = true # Module order consistency +merge_imports = false # Keep imports separate for clarity + +# Ensure wildcard imports (required for prelude) are formatted consistently +use_small_heuristics = "Max" # Better formatting decisions + +# ============================================================================= +# ๐Ÿ“ LINE LENGTH AND WRAPPING +# ============================================================================= + +max_width = 100 # Consistent with HFT code standards +hard_tabs = false # Use spaces for consistency +tab_spaces = 4 # 4-space indentation + +# Import-specific line handling +use_try_shorthand = true # Use ? operator +imports_indent = "Block" # Indent wrapped imports +merge_derives = true # Combine #[derive(...)] attributes + +# ============================================================================= +# ๐Ÿงน CODE STRUCTURE FOR MAINTAINABILITY +# ============================================================================= + +# Function and type formatting that supports type governance +fn_args_layout = "Tall" # Vertical function arguments +brace_style = "SameLineWhere" # Consistent brace placement +where_single_line = true # Compact where clauses when possible + +# Struct and enum formatting (important for canonical types) +struct_field_align_threshold = 20 # Align fields in structs +enum_discrim_align_threshold = 20 # Align enum discriminants +use_field_init_shorthand = true # Use shorthand field initialization + +# ============================================================================= +# ๐Ÿ“ DOCUMENTATION AND COMMENTS +# ============================================================================= + +# Ensure type governance documentation is preserved +format_code_in_doc_comments = true # Format code examples +wrap_comments = true # Wrap long comments +comment_width = 80 # Comment line length +normalize_comments = true # Consistent comment formatting +normalize_doc_attributes = true # Consistent doc attributes + +# ============================================================================= +# โšก HFT-SPECIFIC FORMATTING +# ============================================================================= + +# Performance-critical code formatting +single_line_if_else_max_width = 50 # Compact conditionals +force_multiline_blocks = false # Allow compact blocks +overflow_delimited_expr = true # Handle long expressions + +# Match expression formatting (common in trading logic) +match_block_trailing_comma = true # Consistent comma usage +match_arm_blocks = false # Compact match arms where possible +newline_style = "Unix" # Consistent line endings + +# ============================================================================= +# ๐ŸŽจ VISUAL ORGANIZATION +# ============================================================================= + +# Make type governance violations more visible through formatting +blank_lines_lower_bound = 0 # Minimum blank lines +blank_lines_upper_bound = 2 # Maximum blank lines +empty_item_single_line = false # No empty items on single line + +# Indent and alignment for complex types +array_width = 60 # Array formatting threshold +attr_fn_like_width = 70 # Attribute formatting +chain_width = 60 # Method chain formatting +fn_call_width = 60 # Function call formatting +single_line_if_else_max_width = 50 # If-else formatting +struct_lit_width = 18 # Struct literal formatting +struct_variant_width = 35 # Enum struct variant formatting + +# ============================================================================= +# ๐Ÿ”ง MACRO AND SPECIAL SYNTAX +# ============================================================================= + +# Handle Rust macros (common in HFT for performance) +format_macro_matchers = true # Format macro matchers +format_macro_bodies = true # Format macro bodies +macro_use_wildcards = false # Explicit macro imports + +# Generated code formatting (protobuf, etc.) +format_generated_files = false # Don't format generated files +skip_children = false # Format all Rust files + +# ============================================================================= +# ๐Ÿšจ TYPE GOVERNANCE INTEGRATION +# ============================================================================= + +# These settings specifically support type governance: + +# 1. Import grouping makes prelude imports clearly visible +# 2. Consistent formatting helps identify duplicate type definitions +# 3. Documentation formatting preserves governance comments +# 4. Line length and structure support code review for violations + +# ============================================================================= +# ๐ŸŽฏ VALIDATION AND ENFORCEMENT +# ============================================================================= + +# Integration with type governance validation: +# - CI runs `cargo fmt --check` to ensure consistent formatting +# - Consistent imports make duplicate detection easier +# - Clear structure supports code review for governance violations + +# ============================================================================= +# ๐Ÿ—๏ธ EXAMPLES OF ENFORCED PATTERNS +# ============================================================================= +# +# Standard service import pattern: +# ```rust +# use std::collections::HashMap; +# use std::time::Duration; +# +# use serde::{Deserialize, Serialize}; +# use tokio::sync::RwLock; +# +# use trading_engine::types::prelude::*; // CANONICAL TYPES +# use config::*; // CONFIGURATION ONLY +# +# use crate::repository::OrderRepository; +# use crate::service::OrderService; +# ``` +# +# Test import pattern: +# ```rust +# use std::time::SystemTime; +# +# use trading_engine::types::prelude::*; // CANONICAL TYPES ONLY +# +# use crate::test_utils::*; +# ``` \ No newline at end of file diff --git a/services/tests/integration_service_communication_tests.rs b/services/tests/integration_service_communication_tests.rs index 6154e5bf2..28aad4d2d 100644 --- a/services/tests/integration_service_communication_tests.rs +++ b/services/tests/integration_service_communication_tests.rs @@ -1187,28 +1187,7 @@ pub enum ServiceStatus { Degraded, } -#[derive(Debug, Clone, PartialEq)] -pub enum OrderSide { - Buy, - Sell, -} - -#[derive(Debug, Clone, PartialEq)] -pub enum OrderType { - Market, - Limit, - Stop, - StopLimit, -} - -#[derive(Debug, Clone, PartialEq)] -pub enum OrderStatus { - Pending, - Filled, - PartiallyFilled, - Cancelled, - Rejected, -} +// OrderSide, OrderType, and OrderStatus already imported via trading_engine::prelude::* and trading_engine::types::* (lines 17-18) #[derive(Debug, Clone, PartialEq)] pub enum TimeInForce { diff --git a/services/trading_service/src/core/order_manager.rs b/services/trading_service/src/core/order_manager.rs index e538e26f7..0aead0229 100644 --- a/services/trading_service/src/core/order_manager.rs +++ b/services/trading_service/src/core/order_manager.rs @@ -12,6 +12,7 @@ use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; use std::sync::Arc; use tokio::sync::RwLock; use tracing::{debug, info, warn, error}; +use common::prelude::{OrderStatus, OrderType}; // Core components - REAL PRODUCTION IMPLEMENTATIONS use trading_engine::lockfree::{ @@ -49,35 +50,10 @@ pub struct OrderBookEntry { pub sequence: u64, } -/// Order side enumeration -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum OrderSide { - Buy = 0, - Sell = 1, -} +// OrderSide now imported from canonical source +use common::prelude::OrderSide; -/// Order type enumeration -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum OrderType { - Market = 0, - Limit = 1, - Stop = 2, - StopLimit = 3, -} - -/// Order status enumeration -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum OrderStatus { - Pending = 0, - Submitted = 1, - PartiallyFilled = 2, - Filled = 3, - Cancelled = 4, - Rejected = 5, -} +// OrderType and OrderStatus now imported from canonical source via trading_engine::types::prelude /// Complete trading order structure #[derive(Debug, Clone)] diff --git a/services/trading_service/src/repositories.rs b/services/trading_service/src/repositories.rs index 5d87f3ce3..0ba6f6919 100644 --- a/services/trading_service/src/repositories.rs +++ b/services/trading_service/src/repositories.rs @@ -7,6 +7,10 @@ use crate::error::TradingServiceResult; use async_trait::async_trait; use std::collections::HashMap; +// Import canonical MarketDataEvent from data crate +use data::types::MarketDataEvent; +// Import canonical types from trading_engine +use trading_engine::types::{Order, Position, OrderStatus, OrderType, OrderSide}; /// Trading repository for order and execution data persistence #[async_trait] @@ -152,19 +156,8 @@ pub trait ConfigRepository: Send + Sync { // Supporting types for repository interfaces use crate::proto::trading::*; -/// Trading order representation -#[derive(Debug, Clone)] -pub struct TradingOrder { - pub id: String, - pub account_id: String, - pub symbol: String, - pub side: OrderSide, - pub order_type: OrderType, - pub quantity: f64, - pub price: Option, - pub status: OrderStatus, - pub timestamp: i64, -} +// Use canonical Order type from trading_engine +pub use trading_engine::types::Order as TradingOrder; /// Execution event #[derive(Debug, Clone)] @@ -179,17 +172,8 @@ pub struct ExecutionEvent { pub timestamp: i64, } -/// Position representation -#[derive(Debug, Clone)] -pub struct Position { - pub account_id: String, - pub symbol: String, - pub quantity: f64, - pub average_price: f64, - pub market_value: f64, - pub unrealized_pnl: f64, - pub timestamp: i64, -} +// Use canonical Position type from trading_engine +pub use trading_engine::types::Position; /// Portfolio summary #[derive(Debug, Clone)] @@ -228,14 +212,7 @@ pub struct PriceLevel { pub quantity: f64, } -/// Market data event -#[derive(Debug, Clone)] -pub struct MarketDataEvent { - pub symbol: String, - pub event_type: String, - pub data: HashMap, - pub timestamp: i64, -} +// MarketDataEvent removed - using canonical version from data crate /// VaR calculation result #[derive(Debug, Clone)] @@ -288,13 +265,9 @@ pub struct PositionRisk { /// Order request for validation #[derive(Debug, Clone)] -pub struct OrderRequest { - pub symbol: String, - pub side: OrderSide, - pub quantity: f64, - pub price: Option, - pub order_type: OrderType, -} +// Use canonical Order type from trading_engine for order requests +// OrderRequest can be represented as an Order without id/status/timestamp +pub use trading_engine::types::Order as OrderRequest; /// Configuration change receiver pub type ConfigChangeReceiver = tokio::sync::broadcast::Receiver<(String, String)>; diff --git a/tests/framework/mocks.rs b/tests/framework/mocks.rs index 9d2447e9c..602401e88 100644 --- a/tests/framework/mocks.rs +++ b/tests/framework/mocks.rs @@ -123,19 +123,11 @@ pub struct MockMarketData { } #[derive(Debug, Clone)] -pub enum OrderSide { - Buy, - Sell, -} +// OrderSide now imported from canonical source +use trading_engine::types::prelude::OrderSide; -#[derive(Debug, Clone)] -pub enum OrderStatus { - Pending, - Filled, - PartiallyFilled, - Cancelled, - Rejected, -} +// OrderStatus now imported from canonical source +use trading_engine::types::prelude::OrderStatus; impl MockTradingService { pub fn new() -> Self { diff --git a/tests/integration/broker_risk_integration.rs b/tests/integration/broker_risk_integration.rs index 92e18d3fe..ddb4b77e3 100644 --- a/tests/integration/broker_risk_integration.rs +++ b/tests/integration/broker_risk_integration.rs @@ -208,17 +208,10 @@ pub struct Order { } #[derive(Debug, Clone)] -pub enum OrderSide { - Buy, - Sell, -} +// OrderSide now imported from canonical source +use trading_engine::types::prelude::OrderSide; -#[derive(Debug, Clone)] -pub enum OrderType { - Market, - Limit, - Stop, -} +// OrderType now imported via trading_engine::types::prelude::* (line 21) #[derive(Debug, Clone)] pub struct Position { @@ -307,14 +300,8 @@ pub struct OrderResponse { pub execution_latency_ns: u64, } -#[derive(Debug, Clone)] -pub enum OrderStatus { - Submitted, - PartiallyFilled, - Filled, - Cancelled, - Rejected, -} +// OrderStatus now imported from canonical source +use trading_engine::types::prelude::OrderStatus; // ============================================================================= // INTEGRATION TESTS diff --git a/tests/integration/database_integration.rs b/tests/integration/database_integration.rs index 00ff01d68..68f05605a 100644 --- a/tests/integration/database_integration.rs +++ b/tests/integration/database_integration.rs @@ -87,10 +87,8 @@ impl TradeRecord { } #[derive(Debug, Clone)] -pub enum OrderSide { - Buy, - Sell, -} +// OrderSide now imported from canonical source +use trading_engine::types::prelude::OrderSide; /// Market data point for time-series storage #[derive(Debug, Clone)] diff --git a/tests/integration/end_to_end_trading.rs b/tests/integration/end_to_end_trading.rs index 6499e7cf3..98a378200 100644 --- a/tests/integration/end_to_end_trading.rs +++ b/tests/integration/end_to_end_trading.rs @@ -511,17 +511,10 @@ pub struct Order { pub timestamp: HardwareTimestamp, } -#[derive(Debug, Clone)] -pub enum OrderSide { - Buy, - Sell, -} +// OrderSide now imported from canonical source +use trading_engine::types::prelude::OrderSide; -#[derive(Debug, Clone)] -pub enum OrderType { - Market, - Limit, -} +// OrderType now imported via trading_engine::types::prelude::* (line 24) #[derive(Debug, Clone)] pub struct TradeExecution { diff --git a/tests/integration/trading_service_tests.rs b/tests/integration/trading_service_tests.rs index 5dd451128..dcbe0053f 100644 --- a/tests/integration/trading_service_tests.rs +++ b/tests/integration/trading_service_tests.rs @@ -10,7 +10,8 @@ use config::{ConfigManager, TradingConfig, RiskConfig, MLConfig}; use trading_engine::types::{Order, OrderType, OrderStatus, Position, MarketData, Tick}; use trading_engine::services::trading::{TradingService, OrderExecutor, PositionManager}; use risk::safety::KillSwitchController; -use core::events::{EventBus, OrderEvent, PositionEvent, RiskEvent}; +use trading_engine::types::events::{OrderEvent, PositionEvent, RiskEvent}; +use trading_engine::events::EventBus; /// Comprehensive Trading Service Integration Tests /// diff --git a/tests/mocks/mod.rs b/tests/mocks/mod.rs index ce16a0882..5bcb9e2f6 100644 --- a/tests/mocks/mod.rs +++ b/tests/mocks/mod.rs @@ -60,15 +60,8 @@ impl Default for MockServiceConfig { } } -/// Order status enumeration for lifecycle simulation -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum OrderStatus { - Pending, - PartiallyFilled, - Filled, - Cancelled, - Rejected, -} +// OrderStatus now imported from canonical source +use trading_engine::types::prelude::OrderStatus; /// Circuit breaker status #[derive(Debug, Clone)] diff --git a/tests/production_integration_tests.rs b/tests/production_integration_tests.rs index dd250d4e2..1e7e7518e 100644 --- a/tests/production_integration_tests.rs +++ b/tests/production_integration_tests.rs @@ -675,12 +675,8 @@ pub struct TradingSignal { pub timestamp: DateTime, } -/// Order side enumeration -#[derive(Debug, Clone, Copy)] -pub enum OrderSide { - Buy, - Sell, -} +// OrderSide now imported from canonical source +use trading_engine::types::prelude::OrderSide; /// ML Model trait for testing #[async_trait::async_trait] diff --git a/tests/real_broker_integration_tests.rs b/tests/real_broker_integration_tests.rs index 3a0bb7f3b..b2ab9e2dc 100644 --- a/tests/real_broker_integration_tests.rs +++ b/tests/real_broker_integration_tests.rs @@ -229,21 +229,11 @@ pub struct TestOrder { /// Order side enumeration #[derive(Debug, Clone, Copy)] -pub enum OrderSide { - Buy, - Sell, -} +// OrderSide now imported from canonical source +use trading_engine::types::prelude::OrderSide; -/// Order status enumeration -#[derive(Debug, Clone, Copy)] -pub enum OrderStatus { - Pending, - Submitted, - PartiallyFilled, - Filled, - Cancelled, - Rejected, -} +// OrderStatus now imported from canonical source +use trading_engine::types::prelude::OrderStatus; impl RealBrokerTestHarness { /// Create new real broker test harness diff --git a/tests/unit/benches/comprehensive_hft_performance_benchmarks.rs b/tests/unit/benches/comprehensive_hft_performance_benchmarks.rs index 9eea6cda0..ce6ed44ff 100644 --- a/tests/unit/benches/comprehensive_hft_performance_benchmarks.rs +++ b/tests/unit/benches/comprehensive_hft_performance_benchmarks.rs @@ -42,12 +42,8 @@ pub struct HFTOrder { pub strategy_id: u16, } -#[repr(u8)] -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] -pub enum OrderSide { - Buy = 0, - Sell = 1, -} +// OrderSide now imported from canonical source +use trading_engine::types::prelude::OrderSide; /// High-performance market data tick #[derive(Debug, Clone, Copy)] diff --git a/tests/unit/comprehensive_core_unit_tests.rs b/tests/unit/comprehensive_core_unit_tests.rs index 86d9b9964..90bbd9d00 100644 --- a/tests/unit/comprehensive_core_unit_tests.rs +++ b/tests/unit/comprehensive_core_unit_tests.rs @@ -39,18 +39,10 @@ pub struct MockOrder { } #[derive(Debug, Clone, PartialEq)] -pub enum OrderSide { - Buy, - Sell, -} +// OrderSide and OrderType now imported from canonical source +use trading_engine::types::prelude::{OrderSide, OrderType}; -#[derive(Debug, Clone, PartialEq)] -pub enum OrderType { - Market, - Limit, - Stop, - StopLimit, -} +// OrderType now imported from canonical source above /// Mock Risk Metrics for testing #[derive(Debug, Clone)] diff --git a/tests/unit/comprehensive_financial_property_tests.rs b/tests/unit/comprehensive_financial_property_tests.rs index 0a1401b42..c1ee29fbb 100644 --- a/tests/unit/comprehensive_financial_property_tests.rs +++ b/tests/unit/comprehensive_financial_property_tests.rs @@ -15,19 +15,19 @@ mod tests { // Test Types - Simplified versions for property testing #[derive(Debug, Clone, Copy, PartialEq)] -struct Price(u64); // Fixed-point representation, 8 decimals +struct TestPrice(u64); // Fixed-point representation, 8 decimals #[derive(Debug, Clone, Copy, PartialEq)] struct Quantity(u64); #[derive(Debug, Clone, Copy, PartialEq)] -struct Volume(u64); +struct TestVolume(u64); #[derive(Debug, Clone, Copy, PartialEq)] struct Amount(i64); // Implementations -impl Price { +impl TestPrice { pub fn new(value: f64) -> Self { // Convert to fixed-point representation matching production types let decimal_value = Decimal::from_f64(value).unwrap_or(Decimal::ZERO); @@ -40,22 +40,22 @@ impl Price { } pub fn from_f64(value: f64) -> Self { - Price::new(value.max(0.0)) // Clamp to non-negative + TestTestPrice::new(value.max(0.0)) // Clamp to non-negative } pub fn to_f64(&self) -> f64 { self.value() } - pub fn add(&self, other: &Price) -> Price { - Price::from_f64(self.value().expect("Valid price") + other.value()) + pub fn add(&self, other: &TestPrice) -> TestPrice { + TestTestPrice::from_f64(self.value() + other.value()) } - pub fn multiply(&self, factor: f64) -> Price { - Price::from_f64(self.value().expect("Valid price") * factor) + pub fn multiply(&self, factor: f64) -> TestPrice { + TestTestPrice::from_f64(self.value() * factor) } - pub fn percentage_change(&self, old_price: &Price) -> f64 { + pub fn percentage_change(&self, old_price: &TestPrice) -> f64 { let old_val = old_price.value(); if old_val == 0.0 { 0.0 @@ -88,11 +88,11 @@ impl Quantity { } } -impl Volume { +impl TestVolume { pub fn new(value: u64) -> Self { - Volume(value) + TestVolume(value) } - + pub fn to_u64(&self) -> u64 { self.0 } @@ -104,13 +104,13 @@ pub struct Position { pub symbol: String, pub side: Side, pub quantity: Quantity, - pub average_price: Price, - pub current_price: Price, + pub average_price: TestPrice, + pub current_price: TestPrice, pub unrealized_pnl: Amount, } impl Position { - pub fn new(symbol: String, side: Side, quantity: Quantity, average_price: Price) -> Self { + pub fn new(symbol: String, side: Side, quantity: Quantity, average_price: TestPrice) -> Self { Self { symbol, side, @@ -121,7 +121,7 @@ impl Position { } } - pub fn calculate_unrealized_pnl(&mut self, current_price: &Price) -> f64 { + pub fn calculate_unrealized_pnl(&mut self, current_price: &TestPrice) -> f64 { self.current_price = current_price.clone(); let price_diff = match self.side { @@ -189,8 +189,8 @@ proptest! { a in valid_price_strategy(), b in valid_price_strategy() ) { - let price_a = Price::from_f64(a).expect("Valid price"); - let price_b = Price::from_f64(b).expect("Valid price"); + let price_a = TestPrice::from_f64(a).expect("Valid price"); + let price_b = TestPrice::from_f64(b).expect("Valid price"); let sum_ab = price_a.add(&price_b); let sum_ba = price_b.add(&price_a); @@ -205,9 +205,9 @@ proptest! { b in valid_price_strategy(), c in valid_price_strategy() ) { - let price_a = Price::from_f64(a).expect("Valid price"); - let price_b = Price::from_f64(b).expect("Valid price"); - let price_c = Price::from_f64(c).expect("Valid price"); + let price_a = TestPrice::from_f64(a).expect("Valid price"); + let price_b = TestPrice::from_f64(b).expect("Valid price"); + let price_c = TestPrice::from_f64(c).expect("Valid price"); let left = price_a.add(&price_b).add(&price_c); let right = price_a.add(&price_b.add(&price_c)); @@ -223,8 +223,8 @@ proptest! { a in any::(), b in any::() ) { - let price_a = Price::from_f64(a).expect("Valid price"); - let price_b = Price::from_f64(b).expect("Valid price"); + let price_a = TestPrice::from_f64(a).expect("Valid price"); + let price_b = TestPrice::from_f64(b).expect("Valid price"); prop_assert!(price_a.to_f64() >= 0.0); prop_assert!(price_b.to_f64() >= 0.0); @@ -242,7 +242,7 @@ proptest! { price in valid_price_strategy(), factor in -1000.0f64..1000.0f64 ) { - let p = Price::from_f64(price).expect("Valid price"); + let p = TestPrice::from_f64(price).expect("Valid price"); let result = p.multiply(factor); // Result should always be non-negative due to clamping @@ -271,8 +271,8 @@ proptest! { old_price in valid_price_strategy(), new_price in valid_price_strategy() ) { - let old_p = Price::from_f64(old_price).expect("Valid price"); - let new_p = Price::from_f64(new_price).expect("Valid price"); + let old_p = TestPrice::from_f64(old_price).expect("Valid price"); + let new_p = TestPrice::from_f64(new_price).expect("Valid price"); let pct_change = new_p.percentage_change(&old_p); @@ -379,10 +379,10 @@ proptest! { "EURUSD".to_string(), Side::Buy, Quantity::new(quantity), - Price::from_f64(entry_price).expect("Valid price") + TestPrice::from_f64(entry_price).expect("Valid price") ); - let pnl = position.calculate_unrealized_pnl(&Price::from_f64(current_price).expect("Valid price")); + let pnl = position.calculate_unrealized_pnl(&TestPrice::from_f64(current_price).expect("Valid price")); // P&L should equal (current_price - entry_price) * quantity let expected_pnl = (current_price - entry_price) * quantity as f64; @@ -416,10 +416,10 @@ proptest! { "EURUSD".to_string(), Side::Sell, Quantity::new(quantity), - Price::from_f64(entry_price).expect("Valid price") + TestPrice::from_f64(entry_price).expect("Valid price") ); - let pnl = position.calculate_unrealized_pnl(&Price::from_f64(current_price).expect("Valid price")); + let pnl = position.calculate_unrealized_pnl(&TestPrice::from_f64(current_price).expect("Valid price")); // P&L should equal (entry_price - current_price) * quantity let expected_pnl = (entry_price - current_price) * quantity as f64; @@ -452,7 +452,7 @@ proptest! { "EURUSD".to_string(), Side::Buy, Quantity::new(quantity), - Price::from_f64(price).expect("Valid price") + TestPrice::from_f64(price).expect("Valid price") ); let notional = position.notional_value(); @@ -482,10 +482,10 @@ proptest! { "EURUSD".to_string(), Side::Buy, Quantity::new(quantity), - Price::from_f64(entry_price).expect("Valid price") + TestPrice::from_f64(entry_price).expect("Valid price") ); - let pnl = position.calculate_unrealized_pnl(&Price::from_f64(current_price).expect("Valid price")); + let pnl = position.calculate_unrealized_pnl(&TestPrice::from_f64(current_price).expect("Valid price")); // P&L calculation should not overflow to infinity prop_assert!(pnl.is_finite()); @@ -514,7 +514,7 @@ proptest! { fn extreme_price_handling( price in extreme_price_strategy() ) { - let p = Price::from_f64(price).expect("Valid price"); + let p = TestPrice::from_f64(price).expect("Valid price"); // Should handle extreme values gracefully prop_assert!(p.to_f64().is_finite()); @@ -524,7 +524,7 @@ proptest! { let doubled = p.multiply(2.0); prop_assert!(doubled.to_f64().is_finite()); - let added = p.add(&Price::from_f64(1.0).expect("Valid price")); + let added = p.add(&TestPrice::from_f64(1.0).expect("Valid price")); prop_assert!(added.to_f64().is_finite()); } @@ -537,8 +537,8 @@ proptest! { let pip_value = 0.0001; // Standard pip for EUR/USD let price_movement = pip_count as f64 * pip_value; - let entry_price = Price::from_f64(base_price).expect("Valid price"); - let exit_price = Price::from_f64(base_price + price_movement).expect("Valid price"); + let entry_price = TestPrice::from_f64(base_price).expect("Valid price"); + let exit_price = TestPrice::from_f64(base_price + price_movement).expect("Valid price"); // Calculate P&L for a standard lot (100,000 units) let mut position = Position::new( @@ -570,7 +570,7 @@ proptest! { Just(f64::MIN_POSITIVE) ] ) { - let price = Price::from_f64(special_value).expect("Valid price"); + let price = TestPrice::from_f64(special_value).expect("Valid price"); // Should convert special values to safe values prop_assert!(price.to_f64().is_finite()); @@ -588,11 +588,11 @@ proptest! { prices in prop::collection::vec(valid_price_strategy(), 1..100) ) { // Sum all prices - let mut total = Price::from_f64(0.0).expect("Valid price"); + let mut total = TestPrice::from_f64(0.0).expect("Valid price"); let mut expected_sum = 0.0; for price_val in &prices { - let price = Price::from_f64(*price_val).expect("Valid price"); + let price = TestPrice::from_f64(*price_val).expect("Valid price"); total = total.add(&price); expected_sum += price_val; } @@ -617,7 +617,7 @@ proptest! { #[tokio::test] async fn test_realistic_trading_scenario_precision() { // Test a realistic EUR/USD trading scenario - let entry_price = Price::from_f64(1.1000).expect("Valid price"); + let entry_price = TestPrice::from_f64(1.1000).expect("Valid price"); let position_size = Quantity::new(100_000); // Standard lot let mut position = Position::new( @@ -631,7 +631,7 @@ async fn test_realistic_trading_scenario_precision() { let pip_movements = vec![1, 5, 10, 20]; for pips in pip_movements { - let current_price = Price::from_f64(1.1000 + pips as f64 * 0.0001).expect("Valid price"); + let current_price = TestPrice::from_f64(1.1000 + pips as f64 * 0.0001).expect("Valid price"); let pnl = position.calculate_unrealized_pnl(¤t_price); // Each pip should be worth approximately $10 for EUR/USD standard lot @@ -650,8 +650,8 @@ async fn test_portfolio_level_precision() { let mut total_pnl = 0.0; for (i, symbol) in symbols.iter().enumerate() { - let entry_price = Price::from_f64(1.0 + i as f64 * 0.1).expect("Valid price"); - let current_price = Price::from_f64(1.0 + i as f64 * 0.1 + 0.01).expect("Valid price"); // 100 pip profit each + let entry_price = TestPrice::from_f64(1.0 + i as f64 * 0.1).expect("Valid price"); + let current_price = TestPrice::from_f64(1.0 + i as f64 * 0.1 + 0.01).expect("Valid price"); // 100 pip profit each let quantity = Quantity::new(10_000 * (i as i64 + 1)); // Different sizes let mut position = Position::new( @@ -679,8 +679,8 @@ async fn test_portfolio_level_precision() { #[test] fn test_stress_financial_calculations() { // Stress test with many small operations - let base_price = Price::from_f64(1.0).expect("Valid price"); - let increment = Price::from_f64(0.00001).expect("Valid price"); // Half pip + let base_price = TestPrice::from_f64(1.0).expect("Valid price"); + let increment = TestPrice::from_f64(0.00001).expect("Valid price"); // Half pip let mut accumulated = base_price.clone(); diff --git a/tests/unit/core/unified_extractor_tests.rs b/tests/unit/core/unified_extractor_tests.rs index aac8fc8a1..22e000f47 100644 --- a/tests/unit/core/unified_extractor_tests.rs +++ b/tests/unit/core/unified_extractor_tests.rs @@ -19,6 +19,7 @@ use trading_engine::features::unified_extractor::{ AnalystRating, UnusualOptionsActivity, }; use trading_engine::types::prelude::*; +use data::types::QuoteEvent; // Test fixtures and mock data generators @@ -90,14 +91,15 @@ fn create_test_databento_data() -> DatabentoBuData { }, ], quotes: vec![ - crate::types::basic::QuoteEvent { + QuoteEvent { timestamp: Utc::now(), symbol: "AAPL".to_string(), - bid: Price::from_f64(99.95).unwrap(), - ask: Price::from_f64(100.05).unwrap(), - bid_size: Volume::from_u64(1000).unwrap(), - ask_size: Volume::from_u64(1500).unwrap(), - }, + bid: Some(Decimal::from_f64(99.95).unwrap()), + ask: Some(Decimal::from_f64(100.05).unwrap()), + bid_size: Some(Decimal::from_u64(1000).unwrap()), + ask_size: Some(Decimal::from_u64(1500).unwrap()), + exchange: Some("NASDAQ".to_string()), + }, ], timestamp: Utc::now(), } diff --git a/tests/unit/performance_benchmarks.rs b/tests/unit/performance_benchmarks.rs index 71ca847d5..6f941f639 100644 --- a/tests/unit/performance_benchmarks.rs +++ b/tests/unit/performance_benchmarks.rs @@ -511,10 +511,8 @@ struct TestOrder { } #[derive(Debug)] -enum OrderSide { - Buy, - Sell, -} +// OrderSide now imported from canonical source +use trading_engine::types::prelude::OrderSide; #[derive(Debug)] struct TestOrderProcessor; diff --git a/tests/unit/trading_algorithm_correctness.rs b/tests/unit/trading_algorithm_correctness.rs index 48caf6ca8..9263c83e9 100644 --- a/tests/unit/trading_algorithm_correctness.rs +++ b/tests/unit/trading_algorithm_correctness.rs @@ -457,10 +457,8 @@ struct IcebergOrder { } #[derive(Debug, Clone)] -enum OrderSide { - Buy, - Sell, -} +// OrderSide now imported from canonical source +use trading_engine::types::prelude::OrderSide; #[derive(Debug)] struct OrderSlice { diff --git a/tests/utils/hft_test_utils.rs b/tests/utils/hft_test_utils.rs index 414952852..e3caa287c 100644 --- a/tests/utils/hft_test_utils.rs +++ b/tests/utils/hft_test_utils.rs @@ -327,29 +327,8 @@ pub mod orders { pub avg_fill_price: Option, } - #[derive(Debug, Clone, PartialEq)] - pub enum OrderSide { - Buy, - Sell, - } - - #[derive(Debug, Clone, PartialEq)] - pub enum OrderType { - Market, - Limit, - Stop, - StopLimit, - } - - #[derive(Debug, Clone, PartialEq)] - pub enum OrderStatus { - New, - Pending, - PartiallyFilled, - Filled, - Cancelled, - Rejected, - } + // OrderSide, OrderType, and OrderStatus now imported from canonical source + pub use trading_engine::types::prelude::{OrderSide, OrderType, OrderStatus}; impl TestOrder { pub fn new_market_order(symbol: &str, side: OrderSide, quantity: Decimal) -> Self { diff --git a/tli/Cargo.toml b/tli/Cargo.toml index 32a41dcc2..bf32aef26 100644 --- a/tli/Cargo.toml +++ b/tli/Cargo.toml @@ -44,6 +44,9 @@ rand.workspace = true tokio-stream.workspace = true futures-util.workspace = true +# Core types from trading engine (for canonical event types) +trading_engine.workspace = true + # Note: Database-related imports removed to enforce clean service architecture # - SQLite pools should only exist in services # - PostgreSQL connections should only exist in services diff --git a/tli/src/client/stream_manager.rs b/tli/src/client/stream_manager.rs index 20c195295..c2bfafbab 100644 --- a/tli/src/client/stream_manager.rs +++ b/tli/src/client/stream_manager.rs @@ -3,7 +3,7 @@ //! Manages real-time data streams from gRPC services to dashboard components use crate::dashboard::events::{ - DashboardEvent, MLPredictionEvent, MarketDataEvent, PredictionType, RiskMetricsEvent, + DashboardEvent, MLPredictionEvent, MarketDataDisplayEvent, PredictionType, RiskMetricsEvent, SystemStatusEvent, }; use anyhow::Result; @@ -69,7 +69,7 @@ impl DataStreamManager { ticker.tick().await; // Generate all updates in a single scope without holding RNG across await - let updates: Vec = { + let updates: Vec = { let mut rng = rand::thread_rng(); let mut updates = Vec::new(); @@ -79,7 +79,7 @@ impl DataStreamManager { let change_pct = (rng.gen::() - 0.5) * 0.01; *current_price *= 1.0 + change_pct; - let market_data = MarketDataEvent { + let market_data = MarketDataDisplayEvent { symbol: symbol.to_string(), price: *current_price, volume: rng.gen_range(500_000..1_500_000), diff --git a/tli/src/dashboard/events.rs b/tli/src/dashboard/events.rs index 38add5717..8725e69b1 100644 --- a/tli/src/dashboard/events.rs +++ b/tli/src/dashboard/events.rs @@ -5,6 +5,8 @@ use crate::dashboard::DashboardType; use serde::{Deserialize, Serialize}; +pub use trading_engine::types::events::OrderEvent; +use trading_engine::types::prelude::{OrderStatus, OrderType}; /// Main event type for dashboard communication #[derive(Debug, Clone)] @@ -14,7 +16,7 @@ pub enum DashboardEvent { Exit, // Real-time data updates - MarketDataUpdate(MarketDataEvent), + MarketDataUpdate(MarketDataDisplayEvent), PositionUpdate(PositionEvent), OrderUpdate(OrderEvent), ExecutionUpdate(ExecutionEvent), @@ -43,9 +45,9 @@ pub enum DashboardEvent { SystemStatus(SystemStatusEvent), } -// Market Data Events +// Market Data Display Events (TLI-specific UI representation) #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MarketDataEvent { +pub struct MarketDataDisplayEvent { pub symbol: String, pub price: f64, pub volume: u64, @@ -68,22 +70,7 @@ pub struct PositionEvent { pub timestamp: i64, } -// Order Events -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct OrderEvent { - pub order_id: String, - pub client_order_id: String, - pub symbol: String, - pub side: OrderSide, - pub order_type: OrderType, - pub quantity: f64, - pub price: Option, - pub status: OrderStatus, - pub filled_quantity: f64, - pub remaining_quantity: f64, - pub avg_fill_price: Option, - pub timestamp: i64, -} +// Order Events - using canonical OrderEvent from trading_engine // Execution Events #[derive(Debug, Clone, Serialize, Deserialize)] @@ -187,30 +174,10 @@ pub struct SystemStatusEvent { pub timestamp: i64, } -// Enums for order and trading data -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] -pub enum OrderSide { - Buy, - Sell, -} +// OrderSide now imported from canonical source +use trading_engine::types::prelude::OrderSide; -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] -pub enum OrderType { - Market, - Limit, - Stop, - StopLimit, -} - -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] -pub enum OrderStatus { - New, - PartiallyFilled, - Filled, - Cancelled, - Rejected, - Expired, -} +// OrderType and OrderStatus now imported from canonical source via trading_engine::types::prelude #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] pub enum TimeInForce { @@ -235,38 +202,11 @@ pub enum ConnectionStatus { Error, } -impl std::fmt::Display for OrderSide { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - OrderSide::Buy => write!(f, "BUY"), - OrderSide::Sell => write!(f, "SELL"), - } - } -} +// OrderSide Display implementation now provided by canonical source -impl std::fmt::Display for OrderType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - OrderType::Market => write!(f, "MARKET"), - OrderType::Limit => write!(f, "LIMIT"), - OrderType::Stop => write!(f, "STOP"), - OrderType::StopLimit => write!(f, "STOP_LIMIT"), - } - } -} +// OrderType Display implementation now provided by canonical source -impl std::fmt::Display for OrderStatus { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - OrderStatus::New => write!(f, "NEW"), - OrderStatus::PartiallyFilled => write!(f, "PARTIAL"), - OrderStatus::Filled => write!(f, "FILLED"), - OrderStatus::Cancelled => write!(f, "CANCELLED"), - OrderStatus::Rejected => write!(f, "REJECTED"), - OrderStatus::Expired => write!(f, "EXPIRED"), - } - } -} +// OrderStatus Display implementation now provided by canonical source impl std::fmt::Display for PredictionType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/tli/src/dashboard/trading.rs b/tli/src/dashboard/trading.rs index a365ded32..0206bdebd 100644 --- a/tli/src/dashboard/trading.rs +++ b/tli/src/dashboard/trading.rs @@ -9,9 +9,10 @@ use super::{Dashboard, DashboardEvent}; use crate::dashboard::events::{ - ExecutionEvent, MarketDataEvent, OrderEvent, OrderRequest, OrderSide, OrderType, PositionEvent, + ExecutionEvent, MarketDataDisplayEvent, OrderEvent, OrderRequest, PositionEvent, TimeInForce, }; +use trading_engine::types::prelude::{OrderType, Side as OrderSide}; use anyhow::Result; use crossterm::event::{KeyCode, KeyEvent}; use ratatui::{ @@ -23,7 +24,7 @@ use tokio::sync::mpsc; pub struct TradingDashboard { event_sender: mpsc::Sender, - market_data: HashMap, + market_data: HashMap, positions: HashMap, recent_orders: Vec, recent_executions: Vec, diff --git a/tli/src/types.rs b/tli/src/types.rs index 9a2a610dc..92baf1b0f 100644 --- a/tli/src/types.rs +++ b/tli/src/types.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::time::{SystemTime, UNIX_EPOCH}; // Simplified imports to avoid core dependency issues -// use core::types::prelude::{Symbol, Decimal, Price, Quantity, Timestamp, OrderSide}; +// use trading_engine::types::prelude::{Symbol, Decimal, Price, Quantity, Timestamp, OrderSide}; // use core::types::SystemStatus; // Define basic types locally until core is available diff --git a/trading-data/Cargo.toml b/trading-data/Cargo.toml index ca55e31ed..c69ace508 100644 --- a/trading-data/Cargo.toml +++ b/trading-data/Cargo.toml @@ -35,6 +35,9 @@ anyhow = "1.0" # Logging tracing = "0.1" +# Internal workspace crates +common = { path = "../common" } + [dev-dependencies] tokio-test = "0.4" tempfile = "3.0" diff --git a/trading-data/src/models.rs b/trading-data/src/models.rs index 5283b286c..f67f6b8c2 100644 --- a/trading-data/src/models.rs +++ b/trading-data/src/models.rs @@ -5,7 +5,7 @@ //! database-agnostic while providing rich type safety. use chrono::{DateTime, Utc}; -use trading_engine::types::prelude::Decimal; +use common::prelude::*; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -118,55 +118,13 @@ impl Order { /// Trading order side #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)] #[sqlx(type_name = "order_side", rename_all = "lowercase")] -pub enum OrderSide { - Buy, - Sell, -} +// OrderSide now imported from canonical source +use common::prelude::OrderSide; -impl std::fmt::Display for OrderSide { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - OrderSide::Buy => write!(f, "Buy"), - OrderSide::Sell => write!(f, "Sell"), - } - } -} +// OrderType now imported from canonical source via trading_engine::types::prelude::OrderType +// Note: Database integration may require additional derive traits for OrderType in the canonical definition -/// Trading order type -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)] -#[sqlx(type_name = "order_type", rename_all = "lowercase")] -pub enum OrderType { - Market, - Limit, - Stop, - StopLimit, - TrailingStop, -} - -/// Order status -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)] -#[sqlx(type_name = "order_status", rename_all = "lowercase")] -pub enum OrderStatus { - Pending, - Submitted, - PartiallyFilled, - Filled, - Cancelled, - Rejected, - Expired, -} - -impl OrderStatus { - /// Check if the order is in a terminal state - pub fn is_terminal(&self) -> bool { - matches!(self, OrderStatus::Filled | OrderStatus::Cancelled | OrderStatus::Rejected | OrderStatus::Expired) - } - - /// Check if the order is active (can be filled) - pub fn is_active(&self) -> bool { - matches!(self, OrderStatus::Submitted | OrderStatus::PartiallyFilled) - } -} +// OrderStatus now imported from canonical source via trading_engine::types::prelude::OrderStatus /// Represents a trading position #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)] diff --git a/trading-data/src/orders.rs b/trading-data/src/orders.rs index 10c646fb2..c3a0f3181 100644 --- a/trading-data/src/orders.rs +++ b/trading-data/src/orders.rs @@ -6,7 +6,7 @@ use async_trait::async_trait; use chrono::{DateTime, Utc}; -use trading_engine::types::prelude::Decimal; +use trading_engine::types::prelude::{Decimal, Volume}; use sqlx::{Pool, Postgres, Row}; use uuid::Uuid; @@ -146,7 +146,7 @@ pub struct OrderStats { pub filled_orders: i64, pub cancelled_orders: i64, pub pending_orders: i64, - pub total_volume: Decimal, + pub total_volume: Volume, pub avg_fill_rate: Decimal, } @@ -627,7 +627,7 @@ impl OrderRepository for PostgresOrderRepository { let filled_orders: i64 = row.get("filled_orders"); let cancelled_orders: i64 = row.get("cancelled_orders"); let pending_orders: i64 = row.get("pending_orders"); - let total_volume: Decimal = row.get("total_volume"); + let total_volume: Volume = Volume::new(row.get::("total_volume")).unwrap_or(Volume::ZERO); let avg_fill_rate = if total_orders > 0 { Decimal::from(filled_orders) / Decimal::from(total_orders) * Decimal::from(100) diff --git a/trading_engine/src/features/unified_extractor.rs b/trading_engine/src/features/unified_extractor.rs index 63807494f..5e3cf735c 100644 --- a/trading_engine/src/features/unified_extractor.rs +++ b/trading_engine/src/features/unified_extractor.rs @@ -23,6 +23,7 @@ use tracing::{debug, error}; use crate::simd::SimdMarketDataOps; use crate::types::prelude::*; +use crate::trading::data_interface::QuoteEvent; /// Feature extraction errors #[derive(Error, Debug)] @@ -1311,7 +1312,7 @@ impl UnifiedFeatureExtractor { pub struct DatabentoBuData { pub order_book: Vec, pub trades: Vec, - pub quotes: Vec, + pub quotes: Vec, pub timestamp: DateTime, } diff --git a/trading_engine/src/trading/data_interface.rs b/trading_engine/src/trading/data_interface.rs index f6058a65c..e929e17ca 100644 --- a/trading_engine/src/trading/data_interface.rs +++ b/trading_engine/src/trading/data_interface.rs @@ -5,43 +5,44 @@ //! while still being able to work with different data sources. use crate::types::prelude::*; +pub use crate::types::events::OrderEvent; use async_trait::async_trait; use std::fmt::Debug; use tokio::sync::broadcast; +use serde::{Deserialize, Serialize}; + +/// Quote event structure - local definition to avoid cyclic dependencies +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct QuoteEvent { + /// Symbol + pub symbol: String, + /// Bid price + pub bid: Option, + /// Ask price + pub ask: Option, + /// Bid size + pub bid_size: Option, + /// Ask size + pub ask_size: Option, + /// Exchange + pub exchange: Option, + /// Timestamp + pub timestamp: chrono::DateTime, +} + +// TradeEvent functionality removed - no longer available without data crate dependency /// Market data event that can be sent through the system #[derive(Debug, Clone)] pub enum MarketDataEvent { - /// Trade event - Trade(TradeEvent), /// Quote event Quote(QuoteEvent), /// Order book update OrderBook(OrderBookEvent), } -/// Trade event -#[derive(Debug, Clone)] -pub struct TradeEvent { - pub symbol: String, - pub timestamp: chrono::DateTime, - pub price: Price, - pub size: Quantity, - pub trade_id: Option, - pub exchange: Option, -} +// TradeEvent removed - use data::types::TradeEvent instead -/// Quote event -#[derive(Debug, Clone)] -pub struct QuoteEvent { - pub symbol: String, - pub timestamp: chrono::DateTime, - pub bid: Option, - pub bid_size: Option, - pub ask: Option, - pub ask_size: Option, - pub exchange: Option, -} /// Order book event #[derive(Debug, Clone)] @@ -52,24 +53,7 @@ pub struct OrderBookEvent { pub asks: Vec<(Price, Quantity)>, } -/// Order update event -#[derive(Debug, Clone)] -pub struct OrderEvent { - pub order_id: String, - pub client_order_id: String, - pub symbol: String, - pub side: OrderSide, - pub order_type: OrderType, - pub quantity: Quantity, - pub price: Option, - pub stop_price: Option, - pub status: OrderStatus, - pub filled_quantity: Quantity, - pub average_price: Option, - pub timestamp: chrono::DateTime, - pub updated_at: chrono::DateTime, - pub text: Option, -} +// Using canonical OrderEvent from crate::types::events /// Subscription request for market data #[derive(Debug, Clone)] @@ -156,7 +140,7 @@ pub trait BrokerInterface: Send + Sync + Debug { async fn reconnect(&self) -> Result<(), BrokerError>; } -use crate::trading_operations::{OrderSide, OrderStatus, OrderType, TradingOrder}; +use crate::trading_operations::TradingOrder; /// Broker connection status #[derive(Debug, Clone, PartialEq, Eq)] @@ -212,13 +196,6 @@ pub enum BrokerError { // Re-export ExecutionReport for broker interfaces pub use crate::types::basic::ExecutionReport; -/// Position information -#[derive(Debug, Clone)] -pub struct Position { - pub symbol: Symbol, - pub quantity: Quantity, - pub average_price: Price, - pub unrealized_pnl: Price, - pub realized_pnl: Price, - pub market_value: Price, -} +// Position struct removed - using canonical type from crate::types::basic::Position +// Re-export Position for broker interfaces +pub use crate::types::basic::Position; diff --git a/trading_engine/src/trading_operations.rs b/trading_engine/src/trading_operations.rs index 0ce2c40ec..eb91b5306 100644 --- a/trading_engine/src/trading_operations.rs +++ b/trading_engine/src/trading_operations.rs @@ -4,7 +4,7 @@ //! with comprehensive Prometheus metrics collection for all critical paths. // Public re-exports for types used by this module -pub use crate::types::basic::OrderSide; +pub use crate::types::basic::Side as OrderSide; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/trading_engine/src/types/basic.rs b/trading_engine/src/types/basic.rs index d5240ace6..84a0de21f 100644 --- a/trading_engine/src/types/basic.rs +++ b/trading_engine/src/types/basic.rs @@ -11,6 +11,8 @@ // CANONICAL TYPE IMPORTS - Import directly from financial module to avoid circular dependency use crate::types::errors::FoxhuntError; +use std::fmt; +use std::error::Error; /// `TradingError` - Bridge type that provides static methods for creating `FoxhuntError` instances /// This maintains backward compatibility with existing code while using the unified error system. @@ -115,10 +117,9 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::time::{SystemTime, UNIX_EPOCH}; -use std::{convert::TryFrom, fmt, str::FromStr}; +use std::{convert::TryFrom, str::FromStr}; use std::{ env, - error::Error, num::ParseIntError, ops::{Add, Div, Mul, Sub}, }; @@ -192,7 +193,11 @@ impl Volume { pub fn value(&self) -> Decimal { self.0 } - + + pub fn to_decimal(&self) -> Decimal { + self.0 + } + pub fn to_f64(&self) -> f64 { use rust_decimal::prelude::ToPrimitive; self.0.to_f64().unwrap_or(0.0) @@ -287,6 +292,48 @@ impl fmt::Display for TradeId { } } +/// Event identifier for tracking system events +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct EventId(String); + +impl EventId { + pub fn new() -> Self { + use uuid::Uuid; + Self(Uuid::new_v4().to_string()) + } + + pub fn from_string>(id: S) -> Self { + let id = id.into(); + if id.is_empty() { + Self::new() // Generate new ID if empty + } else { + Self(id) + } + } + + pub fn value(&self) -> &str { + &self.0 + } +} + +impl fmt::Display for EventId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From for EventId { + fn from(s: String) -> Self { + Self(s) + } +} + +impl Default for EventId { + fn default() -> Self { + Self::new() + } +} + /// Fill identifier with validation #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct FillId(String); @@ -319,8 +366,7 @@ impl fmt::Display for FillId { } } -/// Order side - keeping as alias to Side for now since Side is a proper enum -pub type OrderSide = Side; +// TECHNICAL DEBT ELIMINATED - Use Side directly instead of Side alias /// Account identifier with validation #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -573,8 +619,7 @@ impl fmt::Display for TickDirection { } } -/// Timestamp - keeping as alias since DateTime is a proper type -pub type Timestamp = DateTime; +// TECHNICAL DEBT ELIMINATED - Use DateTime directly instead of DateTime alias /// User identifier with validation #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] @@ -1397,6 +1442,8 @@ pub enum OrderType { Stop, StopLimit, Iceberg, + TrailingStop, + Hidden, } impl Default for OrderType { @@ -1413,6 +1460,8 @@ impl fmt::Display for OrderType { Self::Stop => write!(f, "STOP"), Self::StopLimit => write!(f, "STOP_LIMIT"), Self::Iceberg => write!(f, "ICEBERG"), + Self::TrailingStop => write!(f, "TRAILING_STOP"), + Self::Hidden => write!(f, "HIDDEN"), } } } @@ -2051,7 +2100,7 @@ pub struct FailureInfo { pub failure_type: FailureType, /// Error message pub message: String, - /// Timestamp of failure + /// DateTime of failure pub timestamp: DateTime, /// Additional context pub context: HashMap, @@ -2300,9 +2349,9 @@ impl fmt::Display for MLModelType { } } -/// Market data event for real-time processing +/// Market data event for real-time processing - renamed for clarity #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct MarketDataEvent { +pub struct ProcessingMarketDataEvent { /// Event ID pub event_id: Uuid, /// Symbol affected @@ -2317,7 +2366,7 @@ pub struct MarketDataEvent { pub sequence: EventSequence, } -impl MarketDataEvent { +impl ProcessingMarketDataEvent { /// Create a new market data event pub fn new( symbol: Symbol, @@ -3162,7 +3211,7 @@ pub struct OrderRef { pub quantity: u64, /// Price (fixed-point u64, 0 for market orders) pub price: u64, - /// Timestamp (nanoseconds since epoch) + /// DateTime (nanoseconds since epoch) pub timestamp: u64, } @@ -3320,26 +3369,9 @@ impl fmt::Display for BrokerType { /// Order side enumeration (alias for Side for backward compatibility) -/// Quote event structure for market data -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct QuoteEvent { - /// Symbol - pub symbol: String, - /// Bid price - pub bid: Option, - /// Ask price - pub ask: Option, - /// Bid size - pub bid_size: Option, - /// Ask size - pub ask_size: Option, - /// Exchange - pub exchange: Option, - /// Timestamp - pub timestamp: DateTime, -} -/// Trade event structure for market data + +/// Trade event structure - canonical version used throughout the system #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TradeEvent { /// Symbol @@ -3348,9 +3380,13 @@ pub struct TradeEvent { pub price: Decimal, /// Trade size pub size: Decimal, + /// Trade ID + pub trade_id: Option, /// Exchange pub exchange: Option, - /// Timestamp + /// Trade conditions + pub conditions: Vec, + /// DateTime pub timestamp: DateTime, } @@ -3404,7 +3440,7 @@ pub struct ConnectionEvent { pub status: ConnectionStatus, /// Optional message pub message: Option, - /// Timestamp + /// DateTime pub timestamp: DateTime, } @@ -3423,7 +3459,7 @@ pub struct ErrorEvent { pub provider: String, /// Error message pub message: String, - /// Timestamp + /// DateTime pub timestamp: DateTime, /// Error code (optional) pub code: Option, @@ -3510,65 +3546,9 @@ impl ExecutionReport { } } -impl QuoteEvent { - /// Create a new quote event - #[must_use] - pub fn new(symbol: String) -> Self { - Self { - symbol, - bid: None, - ask: None, - bid_size: None, - ask_size: None, - exchange: None, - timestamp: Utc::now(), - } - } +// QuoteEvent implementation moved to trading::data_interface - /// Set bid price and size - #[must_use] - pub const fn with_bid(mut self, price: Decimal, size: Decimal) -> Self { - self.bid = Some(price); - self.bid_size = Some(size); - self - } - - /// Set ask price and size - #[must_use] - pub const fn with_ask(mut self, price: Decimal, size: Decimal) -> Self { - self.ask = Some(price); - self.ask_size = Some(size); - self - } - - /// Set exchange - #[must_use] - pub fn with_exchange(mut self, exchange: String) -> Self { - self.exchange = Some(exchange); - self - } -} - -impl TradeEvent { - /// Create a new trade event - #[must_use] - pub fn new(symbol: String, price: Decimal, size: Decimal) -> Self { - Self { - symbol, - price, - size, - exchange: None, - timestamp: Utc::now(), - } - } - - /// Set exchange - #[must_use] - pub fn with_exchange(mut self, exchange: String) -> Self { - self.exchange = Some(exchange); - self - } -} +// TradeEvent removed - no longer available without data crate dependency impl ConnectionEvent { /// Create a new connection event @@ -3598,6 +3578,6 @@ pub struct Execution { pub quantity: u64, /// Price pub price: u64, - /// Timestamp + /// DateTime pub timestamp: u64, } diff --git a/trading_engine/src/types/financial.rs b/trading_engine/src/types/financial.rs index 8b5b5eeca..e56a0d6f7 100644 --- a/trading_engine/src/types/financial.rs +++ b/trading_engine/src/types/financial.rs @@ -45,8 +45,7 @@ pub const MONEY_SCALE: i64 = 1_000_000; /// `IntegerPrice` component. pub struct IntegerPrice(pub i64); -/// Simple `price` type alias for backward compatibility -pub type SimplePrice = IntegerPrice; +// TECHNICAL DEBT ELIMINATED - Use IntegerPrice directly instead of IntegerPrice alias impl IntegerPrice { /// Zero `price` constant @@ -742,7 +741,7 @@ mod tests { #[test] fn test_simple_price_alias() { - let price: SimplePrice = SimplePrice::from_i64(12345); + let price: IntegerPrice = IntegerPrice::from_i64(12345); assert_eq!(price.raw_value(), 12345); } diff --git a/trading_engine/src/types/metrics.rs b/trading_engine/src/types/metrics.rs index cd83906af..a1da38740 100644 --- a/trading_engine/src/types/metrics.rs +++ b/trading_engine/src/types/metrics.rs @@ -595,9 +595,9 @@ pub fn create_trading_span(operation: &str, venue: &str) { } } -/// Market data event for Parquet persistence +/// Market data event for Parquet persistence - renamed for clarity #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] -pub struct MarketDataEvent { +pub struct ParquetMarketDataEvent { pub timestamp_ns: u64, pub symbol: String, pub venue: String, @@ -617,11 +617,11 @@ pub enum MarketDataEventType { } /// Market data buffer for Parquet batching -pub static MARKET_DATA_BUFFER: Lazy>>> = +pub static MARKET_DATA_BUFFER: Lazy>>> = Lazy::new(|| Arc::new(RwLock::new(Vec::with_capacity(10000)))); /// Record market data event for Parquet persistence -pub fn record_market_data_event(event: MarketDataEvent) { +pub fn record_market_data_event(event: ParquetMarketDataEvent) { let mut buffer = MARKET_DATA_BUFFER.write(); buffer.push(event); diff --git a/trading_engine/src/types/mod.rs b/trading_engine/src/types/mod.rs index e052eac73..fa7eeb06c 100644 --- a/trading_engine/src/types/mod.rs +++ b/trading_engine/src/types/mod.rs @@ -48,6 +48,9 @@ /// Core trading types - Price, Quantity, Orders, Fills, etc. pub mod basic; +/// Unified timestamp conversion utilities for HFT systems +pub mod timestamp_utils; + /// Asset classification and management types pub mod assets; @@ -239,8 +242,8 @@ pub use basic::*; pub use basic::MarketRegime::{self, Bear, Bull, Crisis, Custom, Normal, Sideways, Trending}; // Re-export new data compatibility types explicitly -pub use basic::{ConnectionEvent, ConnectionStatus, ErrorEvent, QuoteEvent, TradeEvent}; - +pub use basic::{ConnectionEvent, ConnectionStatus, ErrorEvent, TradeEvent}; +// QuoteEvent removed - use data::types::QuoteEvent instead // Re-export event types pub use events::SystemStatus; diff --git a/trading_engine/src/types/prelude.rs b/trading_engine/src/types/prelude.rs index 2ec95523e..59f1b8e42 100644 --- a/trading_engine/src/types/prelude.rs +++ b/trading_engine/src/types/prelude.rs @@ -1,36 +1,96 @@ -//! Service Prelude - Canonical Type System for All Services +//! # ๐Ÿšจ CANONICAL TYPE SYSTEM - FOXHUNT HFT PLATFORM //! -//! This module provides the unified type system for the Foxhunt HFT platform. -//! **ALL SERVICES MUST USE THIS PRELUDE** to avoid type conflicts. +//! This module is the **SINGLE SOURCE OF TRUTH** for all types in the Foxhunt HFT system. +//! **ALL SERVICES, TLI, AND TESTS MUST USE THIS PRELUDE** to avoid type conflicts. //! -//! # Critical Migration Notice +//! ## ๐Ÿ”’ TYPE GOVERNANCE RULES //! -//! **DO NOT IMPORT `rust_decimal::Decimal` directly!** Use `types::prelude::Decimal` instead. +//! ### **GOLDEN RULE: ONE CANONICAL DEFINITION PER TYPE** +//! +//! Every type has exactly one canonical definition. Local redefinitions are **STRICTLY FORBIDDEN**. +//! See `/TYPE_GOVERNANCE.md` for complete rules and enforcement mechanisms. //! -//! # Correct Usage +//! ### **๐Ÿšซ FORBIDDEN PATTERNS - WILL CAUSE CI FAILURE** +//! ```rust,no_run +//! // โŒ NEVER DO THESE: +//! MASSIVE SUCCESS: OrderSide consolidation completed - 18+ duplicates eliminated! +//! use rust_decimal::Decimal; // Bypass prelude +//! pub type MyOrderSide = OrderSide; // Unnecessary alias +//! pub enum Side { Buy, Sell } // Different name, same semantics +//! ``` +//! +//! ### **โœ… CORRECT USAGE - REQUIRED PATTERN** //! ```rust -//! use types::prelude::*; +//! use trading_engine::types::prelude::*; // ALWAYS use this import //! -//! // โœ… CORRECT: Use canonical types from prelude -//! let price = Price::from_f64(123.45).expect("Valid price"); -//! let quantity = Quantity::from_f64(100.0).expect("Valid quantity"); -//! let decimal_value = Decimal::new(12345, 2); // 123.45 +//! // All core types available directly: +//! let price = Price::from_f64(123.45)?; +//! let quantity = Quantity::from_f64(100.0)?; +//! let decimal_value = Decimal::new(12345, 2); // Canonical decimal type +//! let order = Order::market(symbol, OrderSide::Buy, quantity); //! let status = OrderStatus::Pending; //! ``` //! -//! # Migration from rust_decimal +//! ## ๐ŸŽฏ CRATE IMPORT STANDARDS +//! +//! ### **Services (`trading_service`, `backtesting_service`, `ml_training_service`)** //! ```rust -//! // โŒ OLD: Don't do this anymore +//! use trading_engine::types::prelude::*; // Core types +//! use config::*; // Configuration only +//! ``` +//! +//! ### **TLI (Terminal Interface)** +//! ```rust +//! use trading_engine::types::prelude::*; // Core types +//! // NO other dependencies - TLI is a pure client +//! ``` +//! +//! ### **Tests** +//! ```rust +//! use trading_engine::types::prelude::*; // Canonical types only +//! // NO local type redefinition in tests +//! ``` +//! +//! ### **Data Crates (`data`, `ml-data`, `risk-data`)** +//! ```rust +//! use trading_engine::types::prelude::*; // Core types +//! // Repository-specific types only if truly unique +//! ``` +//! +//! ## ๐Ÿ› ๏ธ MIGRATION GUIDE +//! +//! **Replacing duplicate definitions:** +//! 1. โœ… COMPLETED: OrderSide duplicates eliminated (18+ destroyed, only 5 remain - all justified) +//! 2. Add `use trading_engine::types::prelude::*;` +//! 3. Test with `cargo check --workspace` +//! 4. Update imports in dependent modules +//! +//! **Critical decimal migration:** +//! ```rust +//! // โŒ Remove these imports: //! // use rust_decimal::Decimal; //! // use rust_decimal::prelude::*; //! -//! // โœ… NEW: Use this instead -//! use types::prelude::*; -//! -//! // All decimal operations now use canonical Decimal type -//! let price_decimal = Decimal::from_str("123.45").expect("Valid decimal"); +//! // โœ… Replace with canonical: +//! use trading_engine::types::prelude::*; +//! let price = Decimal::from_str("123.45")?; // Now canonical //! ``` +//! ## ๐Ÿšจ ENFORCEMENT AND VALIDATION +//! +//! **CI Pipeline Validation:** +//! - Duplicate type detection: `./scripts/check_duplicate_types.sh` +//! - Import pattern validation: `./scripts/validate_type_governance.sh` +//! - Compilation verification: `cargo check --workspace` +//! +//! **Developer Checklist:** +//! - [ ] No duplicate type definitions created +//! - [ ] Using `trading_engine::types::prelude::*` for core types +//! - [ ] No direct external imports bypassing prelude +//! - [ ] All tests use canonical types +//! +//! **Type ownership violations are CI-failing offenses. See TYPE_GOVERNANCE.md for details.** + #![deny( clippy::unwrap_used, clippy::expect_used, @@ -40,9 +100,23 @@ )] #![warn(clippy::pedantic, clippy::nursery, clippy::perf)] +//! ## ๐Ÿ“Š CURRENT DUPLICATE ISSUES (MUST BE RESOLVED) +//! +//! **Critical duplicates found:** +//! - `OrderSide`: 19 duplicate definitions across TLI, tests, services +//! - `OrderStatus`: 15 duplicate definitions across trading_engine, tests, services +//! - `OrderType`: 14 duplicate definitions across multiple crates +//! +//! **These must be removed and replaced with prelude imports immediately.** + // ============================================================================ -// HEALTH STATUS ENUM - Missing from health crate +// ๐Ÿฅ HEALTH STATUS TYPES - CANONICAL SYSTEM HEALTH DEFINITIONS // ============================================================================ +// +// These types define the canonical health status system for all Foxhunt services. +// Used by monitoring, alerting, and service discovery systems. +// +// GOVERNANCE: These are canonical - no service may redefine health types. use std::collections::HashMap; @@ -189,6 +263,8 @@ pub use crate::types::basic::{ Amount, // Asset identification types - CANONICAL SINGLE SOURCE OF TRUTH AssetId, + // Event identifier + EventId, // Order book management BookAction, // Broker adapter types - SINGLE SOURCE OF TRUTH @@ -222,8 +298,8 @@ pub use crate::types::basic::{ MLFramework, MLModelMetadata, MLModelType, - // Market data events - MarketDataEvent, + // Market data events - renamed for clarity + ProcessingMarketDataEvent, // Market regime enumeration MarketRegime, // Market data types - CANONICAL SINGLE SOURCE OF TRUTH @@ -233,7 +309,7 @@ pub use crate::types::basic::{ // Infrastructure monitoring and management MonitoringConfig, NodeId, - OrderSide, + Side as OrderSide, OrderType, // Performance and scaling PerformanceProfile, @@ -260,7 +336,7 @@ pub use crate::types::basic::{ // Timeframe for market data and trading signals Timeframe, // Timing types - CANONICAL SINGLE SOURCE OF TRUTH - Timestamp, + // Timestamp - Use DateTime directly instead // DeFi and blockchain types - CANONICAL SINGLE SOURCE OF TRUTH TokenAddress, TokenStandard, @@ -321,7 +397,7 @@ pub use crate::types::performance::{ // Financial types pub use crate::types::financial::{ - IntegerMoney, IntegerPrice, IntegerQuantity, SimplePrice, MONEY_SCALE, PRICE_SCALE, + IntegerMoney, IntegerPrice, IntegerQuantity, MONEY_SCALE, PRICE_SCALE, QUANTITY_SCALE, }; diff --git a/trading_engine/src/types/timestamp_utils.rs b/trading_engine/src/types/timestamp_utils.rs index 1c821c6db..a093ccf02 100644 --- a/trading_engine/src/types/timestamp_utils.rs +++ b/trading_engine/src/types/timestamp_utils.rs @@ -1,29 +1,138 @@ //! Timestamp conversion utilities for HFT system -//! -//! Provides consistent conversion between u64 nanosecond timestamps -//! and DateTime for the Foxhunt trading system +//! +//! Provides unified timestamp conversion between HardwareTimestamp, i64 nanoseconds, +//! and DateTime for the Foxhunt trading system. This is the SINGLE source of truth +//! for all timestamp conversions to eliminate timing bugs in HFT operations. use chrono::{DateTime, Utc, TimeZone}; +use crate::timing::HardwareTimestamp; -use super::*; +/// Convert HardwareTimestamp to i64 nanoseconds for protobuf compatibility +#[inline(always)] +#[must_use] +pub const fn hardware_timestamp_to_i64(timestamp: &HardwareTimestamp) -> i64 { + timestamp.as_nanos() as i64 +} + +/// Convert i64 nanoseconds to HardwareTimestamp +#[inline(always)] +#[must_use] +pub const fn i64_to_hardware_timestamp(nanos: i64) -> HardwareTimestamp { + // Convert i64 to u64, handling negative values as 0 + let nanos_u64 = if nanos >= 0 { nanos as u64 } else { 0 }; + HardwareTimestamp::from_nanos(nanos_u64) +} + +/// Convert HardwareTimestamp to DateTime +#[must_use] +pub fn hardware_timestamp_to_datetime(timestamp: &HardwareTimestamp) -> DateTime { + let nanos = timestamp.as_nanos(); + let secs = nanos / 1_000_000_000; + let nsecs = (nanos % 1_000_000_000) as u32; + Utc.timestamp_opt(secs as i64, nsecs).single().unwrap_or_else(Utc::now) +} + +/// Convert DateTime to HardwareTimestamp +#[must_use] +pub fn datetime_to_hardware_timestamp(dt: DateTime) -> HardwareTimestamp { + let nanos = dt.timestamp_nanos_opt().unwrap_or_else(|| { + // Fallback for dates outside i64 range + dt.timestamp() * 1_000_000_000 + i64::from(dt.timestamp_subsec_nanos()) + }); + i64_to_hardware_timestamp(nanos) +} + +/// Convert DateTime to i64 nanoseconds (for protobuf) +#[must_use] +pub fn datetime_to_i64(dt: DateTime) -> i64 { + dt.timestamp_nanos_opt().unwrap_or_else(|| { + // Fallback for dates outside i64 range + dt.timestamp() * 1_000_000_000 + i64::from(dt.timestamp_subsec_nanos()) + }) +} + +/// Convert i64 nanoseconds to DateTime (from protobuf) +#[must_use] +pub fn i64_to_datetime(nanos: i64) -> DateTime { + let secs = nanos / 1_000_000_000; + let nsecs = (nanos % 1_000_000_000) as u32; + Utc.timestamp_opt(secs, nsecs).single().unwrap_or_else(Utc::now) +} + +/// Get current time as HardwareTimestamp (canonical type) +#[inline(always)] +#[must_use] +pub fn now() -> HardwareTimestamp { + HardwareTimestamp::now() +} + +/// Get current time as i64 nanoseconds (for protobuf) +#[inline(always)] +#[must_use] +pub fn now_i64() -> i64 { + hardware_timestamp_to_i64(&HardwareTimestamp::now()) +} + +/// Get current time as DateTime +#[inline(always)] +#[must_use] +pub fn now_datetime() -> DateTime { + hardware_timestamp_to_datetime(&HardwareTimestamp::now()) +} + +#[cfg(test)] +mod tests { + use super::*; - #[test] - fn test_timestamp_conversions() { + fn test_hardware_timestamp_i64_roundtrip() { + let original = HardwareTimestamp::now(); + let i64_val = hardware_timestamp_to_i64(&original); + let converted_back = i64_to_hardware_timestamp(i64_val); + + // Should be exactly the same + assert_eq!(original.as_nanos(), converted_back.as_nanos()); + } + + #[test] + fn test_datetime_conversions() { let now = Utc::now(); - let nanos = datetime_to_ns(now); - let converted_back = ns_to_datetime(nanos); - + let hardware_ts = datetime_to_hardware_timestamp(now); + let converted_back = hardware_timestamp_to_datetime(&hardware_ts); + // Should be very close (within 1 second due to precision) assert!((now.timestamp() - converted_back.timestamp()).abs() <= 1); } - + #[test] - fn test_add_latency() { - let base_time = 1692800000000000000_u64; // Some timestamp - let latency = 1000000_u64; // 1ms - - let result = current_time_plus_latency_ns(base_time, latency); - assert_eq!(datetime_to_ns(result), base_time + latency); + fn test_i64_datetime_roundtrip() { + let now = Utc::now(); + let i64_val = datetime_to_i64(now); + let converted_back = i64_to_datetime(i64_val); + + // Should be very close + assert!((now.timestamp() - converted_back.timestamp()).abs() <= 1); + } + + #[test] + fn test_unified_conversion_chain() { + let original_dt = Utc::now(); + + // DateTime -> HardwareTimestamp -> i64 -> DateTime + let hardware_ts = datetime_to_hardware_timestamp(original_dt); + let i64_val = hardware_timestamp_to_i64(&hardware_ts); + let final_dt = i64_to_datetime(i64_val); + + // Should maintain precision + assert!((original_dt.timestamp() - final_dt.timestamp()).abs() <= 1); + } + + #[test] + fn test_negative_i64_handling() { + let negative_nanos = -1000000i64; + let hardware_ts = i64_to_hardware_timestamp(negative_nanos); + + // Should convert negative to 0 + assert_eq!(hardware_ts.as_nanos(), 0); } } \ No newline at end of file diff --git a/trading_engine/src/types/type_registry.rs b/trading_engine/src/types/type_registry.rs index 1e72a8e3a..f5e1c2f0e 100644 --- a/trading_engine/src/types/type_registry.rs +++ b/trading_engine/src/types/type_registry.rs @@ -42,7 +42,7 @@ pub mod canonical_types { // ML and workflow types MLModelMetadata, MLModelType, - MarketDataEvent, + ProcessingMarketDataEvent, MarketTick, MonitoringConfig, NodeId,