diff --git a/benches/concurrent/contention.rs b/benches/concurrent/contention.rs index 22cccd5..bb554f5 100644 --- a/benches/concurrent/contention.rs +++ b/benches/concurrent/contention.rs @@ -1,6 +1,6 @@ use criterion::{BenchmarkId, Criterion}; use pricelevel::{ - Hash32, OrderId, OrderType, OrderUpdate, PriceLevel, Side, TimeInForce, UuidGenerator, + Hash32, Id, OrderType, OrderUpdate, PriceLevel, Side, TimeInForce, UuidGenerator, }; use std::sync::{Arc, Barrier}; use std::thread; @@ -105,12 +105,12 @@ fn measure_read_write_contention( } 1 => { // Match against existing orders - let taker_id = OrderId::from_u64(thread_id as u64 * 1_000_000 + i); + let taker_id = Id::from_u64(thread_id as u64 * 1_000_000 + i); thread_price_level.match_order(2, taker_id, &thread_transaction_id_gen); } _ => { // Cancel an order - let order_id = OrderId::from_u64(i % 500); + let order_id = Id::from_u64(i % 500); let _ = thread_price_level.update_order(OrderUpdate::Cancel { order_id }); } @@ -197,7 +197,7 @@ fn measure_hot_spot_contention( 0 => { // Cancel an order let _ = thread_price_level.update_order(OrderUpdate::Cancel { - order_id: OrderId::from_u64(order_idx), + order_id: Id::from_u64(order_idx), }); } 1 => { @@ -209,13 +209,13 @@ fn measure_hot_spot_contention( 2 => { // Update quantity let _ = thread_price_level.update_order(OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(order_idx), + order_id: Id::from_u64(order_idx), new_quantity: 15, }); } _ => { // Match operations - let taker_id = OrderId::from_u64(thread_id as u64 * 1_000_000 + i); + let taker_id = Id::from_u64(thread_id as u64 * 1_000_000 + i); thread_price_level.match_order(1, taker_id, &thread_transaction_id_gen); } } @@ -246,7 +246,7 @@ fn measure_hot_spot_contention( #[allow(dead_code)] fn create_standard_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { OrderType::Standard { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, diff --git a/benches/concurrent/register.rs b/benches/concurrent/register.rs index 02d56ad..f8c954c 100644 --- a/benches/concurrent/register.rs +++ b/benches/concurrent/register.rs @@ -1,6 +1,6 @@ use criterion::{BenchmarkId, Criterion, criterion_group}; use pricelevel::{ - Hash32, OrderId, OrderType, OrderUpdate, PegReferenceType, PriceLevel, Side, TimeInForce, + Hash32, Id, OrderType, OrderUpdate, PegReferenceType, PriceLevel, Side, TimeInForce, UuidGenerator, }; use std::sync::{Arc, Barrier}; @@ -71,8 +71,7 @@ pub fn register_benchmarks(c: &mut Criterion) { Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap(); let transaction_id_generator = UuidGenerator::new(namespace); // Use different taker order IDs for each thread/iteration - let taker_id = - OrderId::from_u64(thread_id as u64 * 1_000_000 + iteration); + let taker_id = Id::from_u64(thread_id as u64 * 1_000_000 + iteration); price_level.match_order(5, taker_id, &transaction_id_generator); }, ) @@ -98,8 +97,7 @@ pub fn register_benchmarks(c: &mut Criterion) { iters, |price_level, thread_id, iteration| { // Each thread cancels a different order - let order_id = - OrderId::from_u64(thread_id as u64 * 100 + iteration % 100); + let order_id = Id::from_u64(thread_id as u64 * 100 + iteration % 100); let _ = price_level.update_order(OrderUpdate::Cancel { order_id }); }, ) @@ -307,19 +305,19 @@ fn measure_concurrent_mixed_operations(thread_count: usize, iterations: u64) -> } 1 => { // Match against existing orders - let taker_id = OrderId::from_u64(thread_id as u64 * 1_000_000 + i); + let taker_id = Id::from_u64(thread_id as u64 * 1_000_000 + i); thread_price_level.match_order(5, taker_id, &thread_transaction_id_gen); } 2 => { // Cancel one of the initial orders - let order_id = OrderId::from_u64(i % 200); + let order_id = Id::from_u64(i % 200); let _ = thread_price_level.update_order(OrderUpdate::Cancel { order_id }); } _ => { // Update quantity let base_id = thread_id as u64 * 1_000_000 + (i - 1); let _ = thread_price_level.update_order(OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(base_id), + order_id: Id::from_u64(base_id), new_quantity: 20, }); } @@ -352,7 +350,7 @@ fn measure_concurrent_mixed_operations(thread_count: usize, iterations: u64) -> /// Create a standard limit order for testing fn create_standard_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { OrderType::Standard { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -366,7 +364,7 @@ fn create_standard_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { /// Create an iceberg order for testing fn create_iceberg_order(id: u64, price: u128, visible: u64, hidden: u64) -> OrderType<()> { OrderType::IcebergOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, visible_quantity: visible, hidden_quantity: hidden, @@ -381,7 +379,7 @@ fn create_iceberg_order(id: u64, price: u128, visible: u64, hidden: u64) -> Orde /// Create a post-only order for testing fn create_post_only_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { OrderType::PostOnly { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -403,7 +401,7 @@ fn create_reserve_order( replenish_amount: Option, ) -> OrderType<()> { OrderType::ReserveOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, visible_quantity: visible, hidden_quantity: hidden, @@ -421,7 +419,7 @@ fn create_reserve_order( /// Create a pegged order for testing fn create_pegged_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { OrderType::PeggedOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -440,7 +438,7 @@ fn setup_standard_orders(order_count: u64) -> PriceLevel { for i in 0..order_count { let order = OrderType::Standard { - id: OrderId::from_u64(i), + id: Id::from_u64(i), price: 10000u128, quantity: 10, side: Side::Buy, diff --git a/benches/price_level/add_orders.rs b/benches/price_level/add_orders.rs index 53a037b..e16a8a6 100644 --- a/benches/price_level/add_orders.rs +++ b/benches/price_level/add_orders.rs @@ -1,5 +1,5 @@ use criterion::{BenchmarkId, Criterion}; -use pricelevel::{Hash32, OrderId, OrderType, PriceLevel, Side, TimeInForce}; +use pricelevel::{Hash32, Id, OrderType, PriceLevel, Side, TimeInForce}; use std::hint::black_box; /// Register all benchmarks for adding orders to a price level @@ -81,7 +81,7 @@ pub fn register_benchmarks(c: &mut Criterion) { /// Create a standard limit order for testing fn create_standard_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { OrderType::Standard { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -95,7 +95,7 @@ fn create_standard_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { /// Create an iceberg order for testing fn create_iceberg_order(id: u64, price: u128, visible: u64, hidden: u64) -> OrderType<()> { OrderType::IcebergOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, visible_quantity: visible, hidden_quantity: hidden, @@ -110,7 +110,7 @@ fn create_iceberg_order(id: u64, price: u128, visible: u64, hidden: u64) -> Orde /// Create a post-only order for testing fn create_post_only_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { OrderType::PostOnly { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -132,7 +132,7 @@ fn create_reserve_order( replenish_amount: Option, ) -> OrderType<()> { OrderType::ReserveOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, visible_quantity: visible, hidden_quantity: hidden, @@ -152,7 +152,7 @@ fn create_pegged_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { use pricelevel::PegReferenceType; OrderType::PeggedOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, diff --git a/benches/price_level/match_orders.rs b/benches/price_level/match_orders.rs index 94f2151..513d4f3 100644 --- a/benches/price_level/match_orders.rs +++ b/benches/price_level/match_orders.rs @@ -1,5 +1,5 @@ use criterion::{BenchmarkId, Criterion}; -use pricelevel::{Hash32, OrderId, OrderType, PriceLevel, Side, TimeInForce, UuidGenerator}; +use pricelevel::{Hash32, Id, OrderType, PriceLevel, Side, TimeInForce, UuidGenerator}; use std::hint::black_box; use uuid::Uuid; @@ -14,11 +14,7 @@ pub fn register_benchmarks(c: &mut Criterion) { let price_level = setup_standard_orders(100); let namespace = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap(); let transaction_id_generator = UuidGenerator::new(namespace); - black_box(price_level.match_order( - 50, - OrderId::from_u64(999), - &transaction_id_generator, - )); + black_box(price_level.match_order(50, Id::from_u64(999), &transaction_id_generator)); }) }); @@ -28,11 +24,7 @@ pub fn register_benchmarks(c: &mut Criterion) { let price_level = setup_iceberg_orders(100); let namespace = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap(); let transaction_id_generator = UuidGenerator::new(namespace); - black_box(price_level.match_order( - 75, - OrderId::from_u64(999), - &transaction_id_generator, - )); + black_box(price_level.match_order(75, Id::from_u64(999), &transaction_id_generator)); }) }); @@ -42,11 +34,7 @@ pub fn register_benchmarks(c: &mut Criterion) { let price_level = setup_reserve_orders(100); let namespace = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap(); let transaction_id_generator = UuidGenerator::new(namespace); - black_box(price_level.match_order( - 60, - OrderId::from_u64(999), - &transaction_id_generator, - )); + black_box(price_level.match_order(60, Id::from_u64(999), &transaction_id_generator)); }) }); @@ -56,11 +44,7 @@ pub fn register_benchmarks(c: &mut Criterion) { let price_level = setup_mixed_orders(100); let namespace = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap(); let transaction_id_generator = UuidGenerator::new(namespace); - black_box(price_level.match_order( - 100, - OrderId::from_u64(999), - &transaction_id_generator, - )); + black_box(price_level.match_order(100, Id::from_u64(999), &transaction_id_generator)); }) }); @@ -77,7 +61,7 @@ pub fn register_benchmarks(c: &mut Criterion) { let transaction_id_generator = UuidGenerator::new(namespace); black_box(price_level.match_order( match_quantity, - OrderId::from_u64(999), + Id::from_u64(999), &transaction_id_generator, )); }) @@ -98,7 +82,7 @@ pub fn register_benchmarks(c: &mut Criterion) { let transaction_id_generator = UuidGenerator::new(namespace); black_box(price_level.match_order( match_quantity, - OrderId::from_u64(999), + Id::from_u64(999), &transaction_id_generator, )); }) @@ -117,7 +101,7 @@ fn setup_standard_orders(order_count: u64) -> PriceLevel { for i in 0..order_count { let order = OrderType::Standard { - id: OrderId::from_u64(i), + id: Id::from_u64(i), price: 10000u128, quantity: 10, side: Side::Buy, @@ -138,7 +122,7 @@ fn setup_iceberg_orders(order_count: u64) -> PriceLevel { for i in 0..order_count { let order = OrderType::IcebergOrder { - id: OrderId::from_u64(i), + id: Id::from_u64(i), price: 10000u128, visible_quantity: 5, hidden_quantity: 15, @@ -160,7 +144,7 @@ fn setup_reserve_orders(order_count: u64) -> PriceLevel { for i in 0..order_count { let order = OrderType::ReserveOrder { - id: OrderId::from_u64(i), + id: Id::from_u64(i), price: 10000u128, visible_quantity: 5, hidden_quantity: 15, @@ -186,7 +170,7 @@ fn setup_mixed_orders(order_count: u64) -> PriceLevel { for i in 0..order_count { let order = match i % 3 { 0 => OrderType::Standard { - id: OrderId::from_u64(i), + id: Id::from_u64(i), price: 10000u128, quantity: 10, side: Side::Buy, @@ -196,7 +180,7 @@ fn setup_mixed_orders(order_count: u64) -> PriceLevel { extra_fields: (), }, 1 => OrderType::IcebergOrder { - id: OrderId::from_u64(i), + id: Id::from_u64(i), price: 10000u128, visible_quantity: 5, hidden_quantity: 15, @@ -207,7 +191,7 @@ fn setup_mixed_orders(order_count: u64) -> PriceLevel { extra_fields: (), }, _ => OrderType::ReserveOrder { - id: OrderId::from_u64(i), + id: Id::from_u64(i), price: 10000u128, visible_quantity: 5, hidden_quantity: 15, diff --git a/benches/price_level/mixed_operations.rs b/benches/price_level/mixed_operations.rs index c31b1dd..0abd70c 100644 --- a/benches/price_level/mixed_operations.rs +++ b/benches/price_level/mixed_operations.rs @@ -1,6 +1,6 @@ use criterion::Criterion; use pricelevel::{ - Hash32, OrderId, OrderType, OrderUpdate, PriceLevel, Side, TimeInForce, UuidGenerator, + Hash32, Id, OrderType, OrderUpdate, PriceLevel, Side, TimeInForce, UuidGenerator, }; use std::hint::black_box; use uuid::Uuid; @@ -30,7 +30,7 @@ pub fn register_benchmarks(c: &mut Criterion) { for _ in 0..5 { let _ = black_box(price_level.match_order( 50, - OrderId::from_u64(999), + Id::from_u64(999), &transaction_id_generator, )); } @@ -39,12 +39,12 @@ pub fn register_benchmarks(c: &mut Criterion) { for i in 20..40 { if i % 2 == 0 { let _ = black_box(price_level.update_order(OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(i), + order_id: Id::from_u64(i), new_quantity: 20, })); } else { let _ = black_box(price_level.update_order(OrderUpdate::Cancel { - order_id: OrderId::from_u64(i), + order_id: Id::from_u64(i), })); } } @@ -63,7 +63,7 @@ pub fn register_benchmarks(c: &mut Criterion) { for _ in 0..3 { black_box(price_level.match_order( 100, - OrderId::from_u64(1000), + Id::from_u64(1000), &transaction_id_generator, )); } @@ -88,7 +88,7 @@ pub fn register_benchmarks(c: &mut Criterion) { // Match a small amount black_box(price_level.match_order( 2, - OrderId::from_u64(1000 + i), + Id::from_u64(1000 + i), &transaction_id_generator, )); @@ -99,7 +99,7 @@ pub fn register_benchmarks(c: &mut Criterion) { // Cancel an order if i % 10 == 0 { let _ = black_box(price_level.update_order(OrderUpdate::Cancel { - order_id: OrderId::from_u64(i), + order_id: Id::from_u64(i), })); } } @@ -120,21 +120,9 @@ pub fn register_benchmarks(c: &mut Criterion) { } // Execute a few large matches - black_box(price_level.match_order( - 300, - OrderId::from_u64(1001), - &transaction_id_generator, - )); - black_box(price_level.match_order( - 400, - OrderId::from_u64(1002), - &transaction_id_generator, - )); - black_box(price_level.match_order( - 300, - OrderId::from_u64(1003), - &transaction_id_generator, - )); + black_box(price_level.match_order(300, Id::from_u64(1001), &transaction_id_generator)); + black_box(price_level.match_order(400, Id::from_u64(1002), &transaction_id_generator)); + black_box(price_level.match_order(300, Id::from_u64(1003), &transaction_id_generator)); }) }); @@ -158,7 +146,7 @@ pub fn register_benchmarks(c: &mut Criterion) { /// Create a standard limit order for testing fn create_standard_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { OrderType::Standard { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -172,7 +160,7 @@ fn create_standard_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { /// Create an iceberg order for testing fn create_iceberg_order(id: u64, price: u128, visible: u64, hidden: u64) -> OrderType<()> { OrderType::IcebergOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, visible_quantity: visible, hidden_quantity: hidden, @@ -195,7 +183,7 @@ fn create_reserve_order( replenish_amount: Option, ) -> OrderType<()> { OrderType::ReserveOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, visible_quantity: visible, hidden_quantity: hidden, diff --git a/benches/price_level/update_orders.rs b/benches/price_level/update_orders.rs index 9793b58..c4d46db 100644 --- a/benches/price_level/update_orders.rs +++ b/benches/price_level/update_orders.rs @@ -1,5 +1,5 @@ use criterion::{BenchmarkId, Criterion}; -use pricelevel::{Hash32, OrderId, OrderType, OrderUpdate, PriceLevel, Side, TimeInForce}; +use pricelevel::{Hash32, Id, OrderType, OrderUpdate, PriceLevel, Side, TimeInForce}; use std::hint::black_box; /// Register all benchmarks for updating orders at a price level @@ -13,7 +13,7 @@ pub fn register_benchmarks(c: &mut Criterion) { // Cancel orders from the middle to avoid best/worst case scenarios for i in 25..75 { let _ = black_box(price_level.update_order(OrderUpdate::Cancel { - order_id: OrderId::from_u64(i), + order_id: Id::from_u64(i), })); } }) @@ -25,7 +25,7 @@ pub fn register_benchmarks(c: &mut Criterion) { let price_level = setup_standard_orders(100); for i in 25..75 { let _ = black_box(price_level.update_order(OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(i), + order_id: Id::from_u64(i), new_quantity: 200, })); } @@ -38,7 +38,7 @@ pub fn register_benchmarks(c: &mut Criterion) { let price_level = setup_standard_orders(100); for i in 25..75 { let _ = black_box(price_level.update_order(OrderUpdate::Replace { - order_id: OrderId::from_u64(i), + order_id: Id::from_u64(i), price: 10000, // Same price quantity: 150, side: Side::Buy, @@ -53,7 +53,7 @@ pub fn register_benchmarks(c: &mut Criterion) { let price_level = setup_standard_orders(100); for i in 25..75 { let _ = black_box(price_level.update_order(OrderUpdate::Replace { - order_id: OrderId::from_u64(i), + order_id: Id::from_u64(i), price: 10100, // Different price quantity: 150, side: Side::Buy, @@ -70,7 +70,7 @@ pub fn register_benchmarks(c: &mut Criterion) { // In a real scenario we'd be updating both visible and hidden // but for benchmark we're just using the UpdateQuantity which works on visible let _ = black_box(price_level.update_order(OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(i), + order_id: Id::from_u64(i), new_quantity: 15, })); } @@ -89,7 +89,7 @@ pub fn register_benchmarks(c: &mut Criterion) { let cancel_count = order_count / 4; for i in 0..cancel_count { let _ = black_box(price_level.update_order(OrderUpdate::Cancel { - order_id: OrderId::from_u64(i), + order_id: Id::from_u64(i), })); } }) @@ -108,7 +108,7 @@ fn setup_standard_orders(order_count: u64) -> PriceLevel { for i in 0..order_count { let order = OrderType::Standard { - id: OrderId::from_u64(i), + id: Id::from_u64(i), price: 10000u128, quantity: 10, side: Side::Buy, @@ -129,7 +129,7 @@ fn setup_iceberg_orders(order_count: u64) -> PriceLevel { for i in 0..order_count { let order = OrderType::IcebergOrder { - id: OrderId::from_u64(i), + id: Id::from_u64(i), price: 10000u128, visible_quantity: 5, hidden_quantity: 15, diff --git a/examples/src/bin/contention_test.rs b/examples/src/bin/contention_test.rs index d4966b5..abe20c7 100644 --- a/examples/src/bin/contention_test.rs +++ b/examples/src/bin/contention_test.rs @@ -1,8 +1,7 @@ // examples/src/bin/contention_test.rs use pricelevel::{ - Hash32, OrderId, OrderType, OrderUpdate, PriceLevel, Side, TimeInForce, UuidGenerator, - setup_logger, + Hash32, Id, OrderType, OrderUpdate, PriceLevel, Side, TimeInForce, UuidGenerator, setup_logger, }; use std::collections::HashMap; use std::sync::atomic::{AtomicBool, Ordering}; @@ -108,7 +107,7 @@ fn test_read_write_ratio() { 1 => { // Match order let taker_id = - OrderId::from_u64(thread_id as u64 * 10000 + local_counter); + Id::from_u64(thread_id as u64 * 10000 + local_counter); thread_price_level.match_order( 5, // Match 5 units taker_id, @@ -117,7 +116,7 @@ fn test_read_write_ratio() { } _ => { // Cancel/update order - let order_id = OrderId::from_u64(local_counter % 500); + let order_id = Id::from_u64(local_counter % 500); let _ = thread_price_level .update_order(OrderUpdate::Cancel { order_id }); } @@ -194,7 +193,7 @@ fn setup_orders_for_read_write_test(price_level: &PriceLevel) { // Helper function to create a standard order fn create_standard_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { OrderType::Standard { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -292,14 +291,14 @@ fn test_hot_spot_contention() { 1 => { // Cancel an order let _result = thread_price_level.update_order(OrderUpdate::Cancel { - order_id: OrderId::from_u64(order_idx), + order_id: Id::from_u64(order_idx), }); } _ => { // Update an order let _result = thread_price_level.update_order(OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(order_idx), + order_id: Id::from_u64(order_idx), new_quantity: 15, }); } diff --git a/examples/src/bin/hft_simulation.rs b/examples/src/bin/hft_simulation.rs index dffab9a..28b9426 100644 --- a/examples/src/bin/hft_simulation.rs +++ b/examples/src/bin/hft_simulation.rs @@ -1,7 +1,7 @@ // examples/src/bin/hft_simulation.rs use pricelevel::{ - Hash32, OrderId, OrderType, OrderUpdate, PegReferenceType, PriceLevel, Side, TimeInForce, + Hash32, Id, OrderType, OrderUpdate, PegReferenceType, PriceLevel, Side, TimeInForce, UuidGenerator, setup_logger, }; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; @@ -133,7 +133,7 @@ fn main() { let mut local_counter = 0; while thread_running.load(Ordering::Relaxed) { // Generate a unique taker order ID - let taker_id = OrderId::from_u64((thread_id as u64) * 1_000_000 + local_counter); + let taker_id = Id::from_u64((thread_id as u64) * 1_000_000 + local_counter); // Match varying quantities let quantity = (local_counter % 5) + 1; // Match 1-5 units @@ -185,7 +185,7 @@ fn main() { // Try to cancel random orders // Use a combination of the thread ID, local counter, and time to generate "random" order IDs let time_component = Instant::now().elapsed().as_nanos() as u64 % 1000; - let order_id = OrderId::from_u64(time_component + local_counter); + let order_id = Id::from_u64(time_component + local_counter); let result = thread_price_level.update_order(OrderUpdate::Cancel { order_id }); @@ -315,7 +315,7 @@ fn setup_initial_orders(price_level: &PriceLevel, count: u64) { // Helper function to create a standard order fn create_standard_order(id: u64) -> OrderType<()> { OrderType::Standard { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price: PRICE, quantity: 10, side: Side::Buy, @@ -329,7 +329,7 @@ fn create_standard_order(id: u64) -> OrderType<()> { // Helper function to create an iceberg order fn create_iceberg_order(id: u64) -> OrderType<()> { OrderType::IcebergOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price: PRICE, visible_quantity: 5, hidden_quantity: 15, @@ -344,7 +344,7 @@ fn create_iceberg_order(id: u64) -> OrderType<()> { // Helper function to create a post-only order fn create_post_only_order(id: u64) -> OrderType<()> { OrderType::PostOnly { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price: PRICE, quantity: 8, side: Side::Buy, @@ -358,7 +358,7 @@ fn create_post_only_order(id: u64) -> OrderType<()> { // Helper function to create a reserve order fn create_reserve_order(id: u64) -> OrderType<()> { OrderType::ReserveOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price: PRICE, visible_quantity: 5, hidden_quantity: 15, @@ -376,7 +376,7 @@ fn create_reserve_order(id: u64) -> OrderType<()> { // Helper function to create a pegged order fn create_pegged_order(id: u64) -> OrderType<()> { OrderType::PeggedOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price: PRICE, quantity: 10, side: Side::Buy, diff --git a/examples/src/bin/simple.rs b/examples/src/bin/simple.rs index 98dc125..afa7a08 100644 --- a/examples/src/bin/simple.rs +++ b/examples/src/bin/simple.rs @@ -1,8 +1,7 @@ // examples/src/bin/multi_threaded_price_level.rs use pricelevel::{ - Hash32, OrderId, OrderType, OrderUpdate, PriceLevel, Side, TimeInForce, UuidGenerator, - setup_logger, + Hash32, Id, OrderType, OrderUpdate, PriceLevel, Side, TimeInForce, UuidGenerator, setup_logger, }; use std::sync::{Arc, Barrier}; use std::thread; @@ -66,7 +65,7 @@ fn main() { thread_barrier.wait(); // Wait for all threads to be ready for i in 0..20 { - let taker_id = OrderId::from_u64(thread_id as u64 * 1000 + i); + let taker_id = Id::from_u64(thread_id as u64 * 1000 + i); let match_result = thread_price_level.match_order( 5, // Match 5 units each time taker_id, @@ -98,7 +97,7 @@ fn main() { for i in 0..30 { // Try to cancel orders created by thread 0 - let order_id = OrderId::from_u64(i); + let order_id = Id::from_u64(i); let result = thread_price_level.update_order(OrderUpdate::Cancel { order_id }); @@ -126,7 +125,7 @@ fn main() { for i in 0..40 { // Try to update orders created by thread 0 - let order_id = OrderId::from_u64(100 + i); + let order_id = Id::from_u64(100 + i); let result = thread_price_level.update_order(OrderUpdate::UpdateQuantity { order_id, new_quantity: 20, // Update to quantity 20 @@ -201,7 +200,7 @@ fn setup_initial_orders(price_level: &PriceLevel) { // Add 200 standard orders for i in 0..200 { let order = OrderType::Standard { - id: OrderId::from_u64(i), + id: Id::from_u64(i), price: 10000u128, quantity: 10, side: Side::Buy, @@ -216,7 +215,7 @@ fn setup_initial_orders(price_level: &PriceLevel) { // Add some iceberg orders for i in 200..220 { let order = OrderType::IcebergOrder { - id: OrderId::from_u64(i), + id: Id::from_u64(i), price: 10000u128, visible_quantity: 5, hidden_quantity: 15, @@ -232,7 +231,7 @@ fn setup_initial_orders(price_level: &PriceLevel) { // Add some reserve orders for i in 220..240 { let order = OrderType::ReserveOrder { - id: OrderId::from_u64(i), + id: Id::from_u64(i), price: 10000u128, visible_quantity: 5, hidden_quantity: 15, @@ -259,7 +258,7 @@ fn create_order(thread_id: usize, order_id: u64) -> OrderType<()> { // Create different order types based on the thread ID match thread_id % 4 { 0 => OrderType::Standard { - id: OrderId::from_u64(order_id), + id: Id::from_u64(order_id), price: 10000u128, quantity: 10, side: Side::Buy, @@ -269,7 +268,7 @@ fn create_order(thread_id: usize, order_id: u64) -> OrderType<()> { extra_fields: (), }, 1 => OrderType::IcebergOrder { - id: OrderId::from_u64(order_id), + id: Id::from_u64(order_id), price: 10000u128, visible_quantity: 5, hidden_quantity: 15, @@ -280,7 +279,7 @@ fn create_order(thread_id: usize, order_id: u64) -> OrderType<()> { extra_fields: (), }, 2 => OrderType::PostOnly { - id: OrderId::from_u64(order_id), + id: Id::from_u64(order_id), price: 10000u128, quantity: 10, side: Side::Buy, @@ -290,7 +289,7 @@ fn create_order(thread_id: usize, order_id: u64) -> OrderType<()> { extra_fields: (), }, _ => OrderType::ReserveOrder { - id: OrderId::from_u64(order_id), + id: Id::from_u64(order_id), price: 10000u128, visible_quantity: 5, hidden_quantity: 15, diff --git a/src/execution/list.rs b/src/execution/list.rs index 17911bd..de360a4 100644 --- a/src/execution/list.rs +++ b/src/execution/list.rs @@ -1,5 +1,5 @@ use crate::errors::PriceLevelError; -use crate::execution::transaction::Trade; +use crate::execution::trade::Trade; use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; diff --git a/src/execution/match_result.rs b/src/execution/match_result.rs index 7c8e3ad..bb60414 100644 --- a/src/execution/match_result.rs +++ b/src/execution/match_result.rs @@ -1,7 +1,7 @@ use crate::errors::PriceLevelError; use crate::execution::list::TradeList; -use crate::execution::transaction::Trade; -use crate::orders::OrderId; +use crate::execution::trade::Trade; +use crate::orders::Id; use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; @@ -10,7 +10,7 @@ use std::str::FromStr; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MatchResult { /// The ID of the incoming order that initiated the match - pub order_id: OrderId, + pub order_id: Id, /// List of trades that resulted from the match pub trades: TradeList, @@ -22,12 +22,12 @@ pub struct MatchResult { pub is_complete: bool, /// Any orders that were completely filled and removed from the book - pub filled_order_ids: Vec, + pub filled_order_ids: Vec, } impl MatchResult { /// Create a new empty match result - pub fn new(order_id: OrderId, initial_quantity: u64) -> Self { + pub fn new(order_id: Id, initial_quantity: u64) -> Self { Self { order_id, trades: TradeList::new(), @@ -45,7 +45,7 @@ impl MatchResult { } /// Add a filled order ID to track orders removed from the book - pub fn add_filled_order_id(&mut self, order_id: OrderId) { + pub fn add_filled_order_id(&mut self, order_id: Id) { self.filled_order_ids.push(order_id); } @@ -236,7 +236,7 @@ impl FromStr for MatchResult { .ok_or_else(|| PriceLevelError::MissingField("filled_order_ids".to_string()))?; let order_id = - OrderId::from_str(order_id_str).map_err(|_| PriceLevelError::InvalidFieldValue { + Id::from_str(order_id_str).map_err(|_| PriceLevelError::InvalidFieldValue { field: "order_id".to_string(), value: order_id_str.to_string(), })?; @@ -269,12 +269,12 @@ impl FromStr for MatchResult { content .split(',') .map(|id_str| { - OrderId::from_str(id_str).map_err(|_| PriceLevelError::InvalidFieldValue { + Id::from_str(id_str).map_err(|_| PriceLevelError::InvalidFieldValue { field: "filled_order_ids".to_string(), value: id_str.to_string(), }) }) - .collect::, PriceLevelError>>()? + .collect::, PriceLevelError>>()? } }; diff --git a/src/execution/mod.rs b/src/execution/mod.rs index 332d8f0..3e50b68 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -1,4 +1,4 @@ -mod transaction; +mod trade; mod list; mod match_result; @@ -6,4 +6,4 @@ mod tests; pub use list::TradeList; pub use match_result::MatchResult; -pub use transaction::Trade; +pub use trade::Trade; diff --git a/src/execution/tests/list_trade.rs b/src/execution/tests/list_trade.rs index f7052bf..46a7252 100644 --- a/src/execution/tests/list_trade.rs +++ b/src/execution/tests/list_trade.rs @@ -1,8 +1,8 @@ #[cfg(test)] mod tests { use crate::execution::list::TradeList; - use crate::execution::transaction::Trade; - use crate::orders::{OrderId, Side}; + use crate::execution::trade::Trade; + use crate::orders::{Id, Side}; use std::str::FromStr; use uuid::Uuid; @@ -15,9 +15,9 @@ mod tests { fn sample_trade() -> Trade { Trade { - trade_id: parse_uuid("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), - taker_order_id: OrderId::from_u64(1), - maker_order_id: OrderId::from_u64(2), + trade_id: Id::from_uuid(parse_uuid("6ba7b810-9dad-11d1-80b4-00c04fd430c8")), + taker_order_id: Id::from_u64(1), + maker_order_id: Id::from_u64(2), price: 10_000, quantity: 5, taker_side: Side::Buy, diff --git a/src/execution/tests/match_result_trade.rs b/src/execution/tests/match_result_trade.rs index 20451e8..10654b6 100644 --- a/src/execution/tests/match_result_trade.rs +++ b/src/execution/tests/match_result_trade.rs @@ -1,8 +1,8 @@ #[cfg(test)] mod tests { use crate::execution::match_result::MatchResult; - use crate::execution::transaction::Trade; - use crate::orders::{OrderId, Side}; + use crate::execution::trade::Trade; + use crate::orders::{Id, Side}; use std::str::FromStr; use uuid::Uuid; @@ -15,9 +15,9 @@ mod tests { fn sample_trade(quantity: u64) -> Trade { Trade { - trade_id: parse_uuid("6ba7b810-9dad-11d1-80b4-00c04fd430c8"), - taker_order_id: OrderId::from_u64(10), - maker_order_id: OrderId::from_u64(20), + trade_id: Id::from_uuid(parse_uuid("6ba7b810-9dad-11d1-80b4-00c04fd430c8")), + taker_order_id: Id::from_u64(10), + maker_order_id: Id::from_u64(20), price: 1_000, quantity, taker_side: Side::Buy, @@ -27,7 +27,7 @@ mod tests { #[test] fn add_trade_updates_remaining_and_trades() { - let mut result = MatchResult::new(OrderId::from_u64(10), 100); + let mut result = MatchResult::new(Id::from_u64(10), 100); result.add_trade(sample_trade(25)); assert_eq!(result.remaining_quantity, 75); @@ -37,7 +37,7 @@ mod tests { #[test] fn display_and_parse_use_trades_field() { - let mut result = MatchResult::new(OrderId::from_u64(10), 100); + let mut result = MatchResult::new(Id::from_u64(10), 100); result.add_trade(sample_trade(40)); let rendered = result.to_string(); diff --git a/src/execution/tests/transaction.rs b/src/execution/tests/transaction.rs index 9056b68..da82b47 100644 --- a/src/execution/tests/transaction.rs +++ b/src/execution/tests/transaction.rs @@ -1,8 +1,8 @@ #[cfg(test)] mod tests { use crate::errors::PriceLevelError; - use crate::execution::transaction::Trade; - use crate::orders::{OrderId, Side}; + use crate::execution::trade::Trade; + use crate::orders::{Id, Side}; use std::str::FromStr; use std::time::{SystemTime, UNIX_EPOCH}; use uuid::Uuid; @@ -11,9 +11,9 @@ mod tests { let uuid = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap(); Trade { - trade_id: uuid, - taker_order_id: OrderId::from_u64(1), - maker_order_id: OrderId::from_u64(2), + trade_id: Id::from_uuid(uuid), + taker_order_id: Id::from_u64(1), + maker_order_id: Id::from_u64(2), price: 10000, quantity: 5, taker_side: Side::Buy, @@ -41,9 +41,9 @@ mod tests { let input = "Trade:trade_id=6ba7b810-9dad-11d1-80b4-00c04fd430c8;taker_order_id=00000000-0000-0001-0000-000000000000;maker_order_id=00000000-0000-0002-0000-000000000000;price=10000;quantity=5;taker_side=BUY;timestamp=1616823000000"; let transaction = Trade::from_str(input).unwrap(); let uuid = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap(); - assert_eq!(transaction.trade_id, uuid); - assert_eq!(transaction.taker_order_id, OrderId::from_u64(1)); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(2)); + assert_eq!(transaction.trade_id, Id::from_uuid(uuid)); + assert_eq!(transaction.taker_order_id, Id::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(2)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 5); assert_eq!(transaction.taker_side, Side::Buy); @@ -152,17 +152,17 @@ mod tests { let uuid = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap(); let transaction = Trade::new( - uuid, - OrderId::from_u64(1), - OrderId::from_u64(2), + Id::from_uuid(uuid), + Id::from_u64(1), + Id::from_u64(2), 10000, 5, Side::Buy, ); - assert_eq!(transaction.trade_id, uuid); - assert_eq!(transaction.taker_order_id, OrderId::from_u64(1)); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(2)); + assert_eq!(transaction.trade_id, Id::from_uuid(uuid)); + assert_eq!(transaction.taker_order_id, Id::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(2)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 5); assert_eq!(transaction.taker_side, Side::Buy); @@ -186,9 +186,9 @@ mod tests { let transaction = Trade::from_str(input).unwrap(); let uuid = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap(); - assert_eq!(transaction.trade_id, uuid); - assert_eq!(transaction.taker_order_id, OrderId::from_u64(1)); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(2)); + assert_eq!(transaction.trade_id, Id::from_uuid(uuid)); + assert_eq!(transaction.taker_order_id, Id::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(2)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 5); assert_eq!(transaction.taker_side, Side::Buy); @@ -255,17 +255,17 @@ mod tests { #[cfg(test)] mod transaction_serialization_tests { - use crate::execution::transaction::Trade; - use crate::orders::{OrderId, Side}; + use crate::execution::trade::Trade; + use crate::orders::{Id, Side}; use std::str::FromStr; use uuid::Uuid; fn create_test_trade() -> Trade { let uuid = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap(); Trade { - trade_id: uuid, - taker_order_id: OrderId::from_u64(1), - maker_order_id: OrderId::from_u64(2), + trade_id: Id::from_uuid(uuid), + taker_order_id: Id::from_u64(1), + maker_order_id: Id::from_u64(2), price: 10000, quantity: 5, taker_side: Side::Buy, @@ -300,9 +300,9 @@ mod transaction_serialization_tests { let transaction: Trade = serde_json::from_str(json).unwrap(); let uuid = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap(); - assert_eq!(transaction.trade_id, uuid); - assert_eq!(transaction.taker_order_id, OrderId::from_u64(1)); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(2)); + assert_eq!(transaction.trade_id, Id::from_uuid(uuid)); + assert_eq!(transaction.taker_order_id, Id::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(2)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 5); assert_eq!(transaction.taker_side, Side::Buy); @@ -346,9 +346,9 @@ mod transaction_serialization_tests { let input = "Trade:trade_id=6ba7b810-9dad-11d1-80b4-00c04fd430c8;taker_order_id=00000000-0000-0001-0000-000000000000;maker_order_id=00000000-0000-0002-0000-000000000000;price=10000;quantity=5;taker_side=BUY;timestamp=1616823000000"; let transaction = Trade::from_str(input).unwrap(); let uuid = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap(); - assert_eq!(transaction.trade_id, uuid); - assert_eq!(transaction.taker_order_id, OrderId::from_u64(1)); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(2)); + assert_eq!(transaction.trade_id, Id::from_uuid(uuid)); + assert_eq!(transaction.taker_order_id, Id::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(2)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 5); assert_eq!(transaction.taker_side, Side::Buy); diff --git a/src/execution/transaction.rs b/src/execution/trade.rs similarity index 83% rename from src/execution/transaction.rs rename to src/execution/trade.rs index 38e9511..f276d2a 100644 --- a/src/execution/transaction.rs +++ b/src/execution/trade.rs @@ -1,22 +1,21 @@ use crate::errors::PriceLevelError; -use crate::orders::{OrderId, Side}; +use crate::orders::{Id, Side}; use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; use std::time::{SystemTime, UNIX_EPOCH}; -use uuid::Uuid; /// Represents a completed trade between two orders #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] pub struct Trade { /// Unique trade ID - pub trade_id: Uuid, + pub trade_id: Id, /// ID of the aggressive order that caused the match - pub taker_order_id: OrderId, + pub taker_order_id: Id, /// ID of the passive order that was in the book - pub maker_order_id: OrderId, + pub maker_order_id: Id, /// Price at which the trade occurred pub price: u128, @@ -34,17 +33,16 @@ pub struct Trade { impl Trade { /// Create a new trade pub fn new( - trade_id: Uuid, - taker_order_id: OrderId, - maker_order_id: OrderId, + trade_id: Id, + taker_order_id: Id, + maker_order_id: Id, price: u128, quantity: u64, taker_side: Side, ) -> Self { let timestamp = SystemTime::now() .duration_since(UNIX_EPOCH) - .expect("Time went backwards") - .as_millis() as u64; + .map_or(0_u64, |duration| duration.as_millis() as u64); Self { trade_id, @@ -133,33 +131,27 @@ impl FromStr for Trade { // Parse trade_id let trade_id_str = get_field("trade_id")?; - let trade_id = match Uuid::from_str(trade_id_str) { - Ok(id) => id, - Err(_) => { - return Err(PriceLevelError::InvalidFieldValue { - field: "trade_id".to_string(), - value: trade_id_str.to_string(), - }); - } - }; + let trade_id = + Id::from_str(trade_id_str).map_err(|_| PriceLevelError::InvalidFieldValue { + field: "trade_id".to_string(), + value: trade_id_str.to_string(), + })?; // Parse taker_order_id let taker_order_id_str = get_field("taker_order_id")?; - let taker_order_id = OrderId::from_str(taker_order_id_str).map_err(|_| { - PriceLevelError::InvalidFieldValue { + let taker_order_id = + Id::from_str(taker_order_id_str).map_err(|_| PriceLevelError::InvalidFieldValue { field: "taker_order_id".to_string(), value: taker_order_id_str.to_string(), - } - })?; + })?; // Parse maker_order_id let maker_order_id_str = get_field("maker_order_id")?; - let maker_order_id = OrderId::from_str(maker_order_id_str).map_err(|_| { - PriceLevelError::InvalidFieldValue { + let maker_order_id = + Id::from_str(maker_order_id_str).map_err(|_| PriceLevelError::InvalidFieldValue { field: "maker_order_id".to_string(), value: maker_order_id_str.to_string(), - } - })?; + })?; // Parse price let price_str = get_field("price")?; diff --git a/src/lib.rs b/src/lib.rs index af61e05..9bd453d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,10 +142,12 @@ mod utils; mod errors; mod execution; +pub mod prelude; + pub use errors::PriceLevelError; pub use execution::{MatchResult, Trade, TradeList}; pub use orders::DEFAULT_RESERVE_REPLENISH_AMOUNT; pub use orders::PegReferenceType; -pub use orders::{Hash32, OrderId, OrderType, OrderUpdate, Side, TimeInForce}; +pub use orders::{Hash32, Id, OrderType, OrderUpdate, Side, TimeInForce}; pub use price_level::{OrderQueue, PriceLevel, PriceLevelData, PriceLevelSnapshot}; pub use utils::{UuidGenerator, setup_logger}; diff --git a/src/orders/base.rs b/src/orders/base.rs index 939f615..f187c2a 100644 --- a/src/orders/base.rs +++ b/src/orders/base.rs @@ -4,8 +4,6 @@ use crate::errors::PriceLevelError; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; use std::str::FromStr; -use ulid::Ulid; -use uuid::Uuid; /// Represents the side of an order #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] @@ -66,194 +64,6 @@ impl fmt::Display for Side { } } -/// Represents a unique identifier for an order in the trading system. -/// -/// This enum supports multiple ID formats to provide flexibility -/// in order identification and tracking across different systems. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum OrderId { - /// UUID (Universally Unique Identifier) format. - /// A 128-bit identifier that is globally unique across space and time. - Uuid(Uuid), - - /// ULID (Universally Unique Lexicographically Sortable Identifier) format. - /// A 128-bit identifier that is lexicographically sortable and globally unique. - Ulid(Ulid), - - /// Sequential u64 identifier. - /// Useful for CEX systems where orders are assigned sequential IDs per market. - Sequential(u64), -} - -impl FromStr for OrderId { - type Err = PriceLevelError; - - fn from_str(s: &str) -> Result { - // Try parsing as u64 first (for sequential IDs) - if let Ok(id) = s.parse::() { - return Ok(OrderId::Sequential(id)); - } - // Try UUID (has hyphens), then ULID - if let Ok(uuid) = Uuid::from_str(s) { - Ok(OrderId::Uuid(uuid)) - } else if let Ok(ulid) = Ulid::from_string(s) { - Ok(OrderId::Ulid(ulid)) - } else { - Err(PriceLevelError::ParseError { - message: format!("Failed to parse OrderId as u64, UUID, or ULID: {s}"), - }) - } - } -} - -impl fmt::Display for OrderId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - OrderId::Uuid(uuid) => write!(f, "{uuid}"), - OrderId::Ulid(ulid) => write!(f, "{ulid}"), - OrderId::Sequential(id) => write!(f, "{id}"), - } - } -} - -// Custom serialization to maintain backward compatibility -impl Serialize for OrderId { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -// Custom deserialization to maintain backward compatibility -impl<'de> Deserialize<'de> for OrderId { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - OrderId::from_str(&s).map_err(serde::de::Error::custom) - } -} - -impl Default for OrderId { - fn default() -> Self { - Self::new() - } -} - -impl OrderId { - /// Create a new random OrderId (defaults to ULID for better sortability) - pub fn new() -> Self { - OrderId::Ulid(Ulid::new()) - } - - /// Create a new UUID-based OrderId - pub fn new_uuid() -> Self { - OrderId::Uuid(Uuid::new_v4()) - } - - /// Create a new ULID-based OrderId - pub fn new_ulid() -> Self { - OrderId::Ulid(Ulid::new()) - } - - /// Create a nil OrderId (UUID format) - pub fn nil() -> Self { - OrderId::Uuid(Uuid::nil()) - } - - /// Create from an existing UUID - pub fn from_uuid(uuid: Uuid) -> Self { - OrderId::Uuid(uuid) - } - - /// Create from an existing ULID - pub fn from_ulid(ulid: Ulid) -> Self { - OrderId::Ulid(ulid) - } - - /// Get as bytes. - /// - /// UUID and ULID return 16 bytes, Sequential returns 8 bytes zero-padded to 16. - pub fn as_bytes(&self) -> [u8; 16] { - match self { - OrderId::Uuid(uuid) => *uuid.as_bytes(), - OrderId::Ulid(ulid) => ulid.to_bytes(), - OrderId::Sequential(id) => { - let mut bytes = [0u8; 16]; - bytes[8..16].copy_from_slice(&id.to_be_bytes()); - bytes - } - } - } - - /// Create a sequential OrderId from a u64. - /// - /// This is the preferred method for CEX systems using sequential order IDs. - #[must_use] - pub fn sequential(id: u64) -> Self { - OrderId::Sequential(id) - } - - /// Create an OrderId from a u64 by embedding it in a UUID. - /// - /// This method exists for backward compatibility. For new CEX systems, - /// prefer using [`OrderId::sequential`] instead. - #[must_use] - pub fn from_u64(id: u64) -> Self { - let bytes = [ - ((id >> 56) & 0xFF) as u8, - ((id >> 48) & 0xFF) as u8, - ((id >> 40) & 0xFF) as u8, - ((id >> 32) & 0xFF) as u8, - ((id >> 24) & 0xFF) as u8, - ((id >> 16) & 0xFF) as u8, - ((id >> 8) & 0xFF) as u8, - (id & 0xFF) as u8, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ]; - OrderId::Uuid(Uuid::from_bytes(bytes)) - } - - /// Try to get the u64 value if this is a Sequential OrderId. - /// - /// Returns `None` for UUID and ULID variants. - #[must_use] - pub fn as_u64(&self) -> Option { - match self { - OrderId::Sequential(id) => Some(*id), - _ => None, - } - } - - /// Check if this is a Sequential OrderId. - #[must_use] - pub fn is_sequential(&self) -> bool { - matches!(self, OrderId::Sequential(_)) - } - - /// Check if this is a UUID OrderId. - #[must_use] - pub fn is_uuid(&self) -> bool { - matches!(self, OrderId::Uuid(_)) - } - - /// Check if this is a ULID OrderId. - #[must_use] - pub fn is_ulid(&self) -> bool { - matches!(self, OrderId::Ulid(_)) - } -} - /// A 32-byte hash value used for user identification. /// /// This is a wrapper around `[u8; 32]` that provides convenient methods diff --git a/src/orders/mod.rs b/src/orders/mod.rs index d487fd7..5efb25e 100644 --- a/src/orders/mod.rs +++ b/src/orders/mod.rs @@ -12,7 +12,8 @@ mod update; mod tests; -pub use base::{Hash32, OrderId, Side}; +pub use crate::utils::Id; +pub use base::{Hash32, Side}; pub use order_type::DEFAULT_RESERVE_REPLENISH_AMOUNT; pub use order_type::OrderType; pub use pegged::PegReferenceType; diff --git a/src/orders/order_type.rs b/src/orders/order_type.rs index d28f726..91f3cd5 100644 --- a/src/orders/order_type.rs +++ b/src/orders/order_type.rs @@ -2,7 +2,7 @@ use crate::OrderQueue; use crate::errors::PriceLevelError; -use crate::orders::{Hash32, OrderId, PegReferenceType, Side, TimeInForce}; +use crate::orders::{Hash32, Id, PegReferenceType, Side, TimeInForce}; use serde::{Deserialize, Serialize}; use std::fmt; use std::str::FromStr; @@ -25,7 +25,7 @@ pub enum OrderType { /// Standard limit order Standard { /// The order ID - id: OrderId, + id: Id, /// The price of the order price: u128, /// The quantity of the order @@ -45,7 +45,7 @@ pub enum OrderType { /// Iceberg order with visible and hidden quantities IcebergOrder { /// The order ID - id: OrderId, + id: Id, /// The price of the order price: u128, /// The visible quantity of the order @@ -67,7 +67,7 @@ pub enum OrderType { /// Post-only order that won't match immediately PostOnly { /// The order ID - id: OrderId, + id: Id, /// The price of the order price: u128, /// The quantity of the order @@ -87,7 +87,7 @@ pub enum OrderType { /// Trailing stop order that adjusts with market movement TrailingStop { /// The order ID - id: OrderId, + id: Id, /// The price of the order price: u128, /// The quantity of the order @@ -111,7 +111,7 @@ pub enum OrderType { /// Pegged order that adjusts based on reference price PeggedOrder { /// The order ID - id: OrderId, + id: Id, /// The price of the order price: u128, /// The quantity of the order @@ -135,7 +135,7 @@ pub enum OrderType { /// Market-to-limit order that converts to limit after initial execution MarketToLimit { /// The order ID - id: OrderId, + id: Id, /// The price of the order price: u128, /// The quantity of the order @@ -159,7 +159,7 @@ pub enum OrderType { /// if `auto_replenish` is true, and replenish_threshold is 0, it will use 1 ReserveOrder { /// The order ID - id: OrderId, + id: Id, /// The price of the order price: u128, /// The visible quantity of the order @@ -187,7 +187,7 @@ pub enum OrderType { impl OrderType { /// Get the order ID - pub fn id(&self) -> OrderId { + pub fn id(&self) -> Id { match self { Self::Standard { id, .. } => *id, Self::IcebergOrder { id, .. } => *id, @@ -953,7 +953,7 @@ impl FromStr for OrderType { // Parse common fields let id_str = get_field("id")?; - let id = OrderId::from_str(id_str).map_err(|_| PriceLevelError::InvalidFieldValue { + let id = Id::from_str(id_str).map_err(|_| PriceLevelError::InvalidFieldValue { field: "id".to_string(), value: id_str.to_string(), })?; diff --git a/src/orders/tests/base.rs b/src/orders/tests/base.rs index 5c629cb..45e923f 100644 --- a/src/orders/tests/base.rs +++ b/src/orders/tests/base.rs @@ -87,243 +87,3 @@ mod tests_side { assert_eq!(serde_json::to_string(&Side::Sell).unwrap().len(), 6); // "SELL" } } - -#[cfg(test)] -mod tests_orderid { - use crate::Side; - use crate::orders::OrderId; - use std::str::FromStr; - use uuid::Uuid; - - #[test] - fn test_order_id_creation() { - // Create using from_u64 for backward compatibility - let id = OrderId::from_u64(12345); - // Test that it's a valid OrderId (can't access internal structure directly) - assert_eq!(id, OrderId::from_u64(12345)); - - // Create random UUIDs - let id1 = OrderId::new(); - let id2 = OrderId::new(); - assert_ne!(id1, id2); // Random UUIDs should be different - - // Create from existing UUID - let uuid = Uuid::new_v4(); - let id = OrderId::from_uuid(uuid); - assert_eq!(id, OrderId::Uuid(uuid)); - - // Create nil UUID - let nil_id = OrderId::nil(); - assert_eq!(nil_id, OrderId::Uuid(Uuid::nil())); - } - - #[test] - fn test_order_id_equality() { - let id1 = OrderId::from_u64(12345); - let id2 = OrderId::from_u64(12345); - let id3 = OrderId::from_u64(54321); - assert_eq!(id1, id2); - assert_ne!(id1, id3); - } - - #[test] - fn test_order_id_clone() { - let id = OrderId::from_u64(12345); - let cloned_id = id; - assert_eq!(id, cloned_id); - } - - #[test] - fn test_order_id_hash() { - use std::collections::HashSet; - let mut set = HashSet::new(); - set.insert(OrderId::from_u64(12345)); - set.insert(OrderId::from_u64(54321)); - assert!(set.contains(&OrderId::from_u64(12345))); - assert!(set.contains(&OrderId::from_u64(54321))); - assert!(!set.contains(&OrderId::from_u64(99999))); - set.insert(OrderId::from_u64(12345)); - assert_eq!(set.len(), 2); - } - - #[test] - fn test_serialize_deserialize() { - let id = OrderId::from_u64(12345); - let serialized = serde_json::to_string(&id).unwrap(); - let expected_uuid = id.to_string(); - assert!(serialized.contains(&expected_uuid)); - - let deserialized: OrderId = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, id); - } - - #[test] - fn test_from_str_valid() { - let uuid_str = "550e8400-e29b-41d4-a716-446655440000"; - let order_id = OrderId::from_str(uuid_str).unwrap(); - assert_eq!(order_id.to_string(), uuid_str); - - // Test that legacy conversions still work through string format - let u64_id = 12345; - let order_id_from_u64 = OrderId::from_u64(u64_id); - let order_id_str = order_id_from_u64.to_string(); - let order_id_parsed = OrderId::from_str(&order_id_str).unwrap(); - assert_eq!(order_id_from_u64, order_id_parsed); - } - - #[test] - fn test_from_str_invalid() { - assert!(OrderId::from_str("").is_err()); - assert!(OrderId::from_str("not-a-uuid").is_err()); - assert!(OrderId::from_str("550e8400-e29b-41d4-a716").is_err()); // Incomplete UUID - } - - #[test] - fn test_display() { - let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap(); - let id = OrderId::from_uuid(uuid); - assert_eq!(format!("{id}"), "550e8400-e29b-41d4-a716-446655440000"); - } - - #[test] - fn test_roundtrip() { - // Test U64 round trip - let original = 12345u64; - let id = OrderId::from_u64(original); - let string = id.to_string(); - let parsed = string.parse::().unwrap(); - assert_eq!(parsed, id); - - // Test UUID round trip - let uuid = Uuid::new_v4(); - let id = OrderId::from_uuid(uuid); - let string = id.to_string(); - let parsed = string.parse::().unwrap(); - assert_eq!(parsed, id); - } - - #[test] - fn test_side_opposite() { - // Test the opposite method on Side enum - assert_eq!(Side::Buy.opposite(), Side::Sell); - assert_eq!(Side::Sell.opposite(), Side::Buy); - - // Double check opposite of opposite returns original - assert_eq!(Side::Buy.opposite().opposite(), Side::Buy); - assert_eq!(Side::Sell.opposite().opposite(), Side::Sell); - } - - #[test] - fn test_order_id_nil() { - // Test the OrderId::nil() functionality - let nil_id = OrderId::nil(); - - // Verify it's equal to the OrderId::Uuid(Uuid::nil()) - assert_eq!(nil_id, OrderId::Uuid(Uuid::nil())); - - // Convert to string and verify - let str_representation = nil_id.to_string(); - assert_eq!(str_representation, "00000000-0000-0000-0000-000000000000"); - - // Parse from string and verify roundtrip - let parsed = OrderId::from_str(&str_representation).unwrap(); - assert_eq!(parsed, nil_id); - } - - #[test] - fn test_order_id_default() { - let default_id = OrderId::default(); - assert_ne!(default_id, OrderId::nil()); - - // The default implementation calls new(), which creates a random ULID - // So we just need to verify it's not nil - assert_ne!(default_id, OrderId::Uuid(Uuid::nil())); - } - - #[test] - fn test_order_id_sequential() { - // Test creating sequential OrderIds - let id1 = OrderId::sequential(1); - let id2 = OrderId::sequential(2); - let id3 = OrderId::sequential(1); - - assert_eq!(id1, id3); - assert_ne!(id1, id2); - - // Test is_sequential - assert!(id1.is_sequential()); - assert!(!OrderId::new_uuid().is_sequential()); - assert!(!OrderId::new_ulid().is_sequential()); - - // Test as_u64 - assert_eq!(id1.as_u64(), Some(1)); - assert_eq!(id2.as_u64(), Some(2)); - assert_eq!(OrderId::new_uuid().as_u64(), None); - - // Test display - assert_eq!(id1.to_string(), "1"); - assert_eq!(id2.to_string(), "2"); - - // Test from_str roundtrip - let parsed: OrderId = "42".parse().unwrap(); - assert_eq!(parsed, OrderId::sequential(42)); - assert!(parsed.is_sequential()); - } - - #[test] - fn test_order_id_sequential_hash() { - use std::collections::HashSet; - let mut set = HashSet::new(); - set.insert(OrderId::sequential(1)); - set.insert(OrderId::sequential(2)); - set.insert(OrderId::sequential(1)); // Duplicate - - assert_eq!(set.len(), 2); - assert!(set.contains(&OrderId::sequential(1))); - assert!(set.contains(&OrderId::sequential(2))); - assert!(!set.contains(&OrderId::sequential(3))); - } - - #[test] - fn test_order_id_sequential_serialization() { - let id = OrderId::sequential(12345); - let serialized = serde_json::to_string(&id).unwrap(); - assert_eq!(serialized, "\"12345\""); - - let deserialized: OrderId = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, id); - assert!(deserialized.is_sequential()); - } - - #[test] - fn test_order_id_sequential_as_bytes() { - let id = OrderId::sequential(0x0102030405060708); - let bytes = id.as_bytes(); - - // Sequential IDs are stored in the last 8 bytes (big-endian) - assert_eq!(bytes[0..8], [0, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!( - bytes[8..16], - [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] - ); - } - - #[test] - fn test_order_id_type_checks() { - let uuid_id = OrderId::new_uuid(); - let ulid_id = OrderId::new_ulid(); - let seq_id = OrderId::sequential(1); - - assert!(uuid_id.is_uuid()); - assert!(!uuid_id.is_ulid()); - assert!(!uuid_id.is_sequential()); - - assert!(!ulid_id.is_uuid()); - assert!(ulid_id.is_ulid()); - assert!(!ulid_id.is_sequential()); - - assert!(!seq_id.is_uuid()); - assert!(!seq_id.is_ulid()); - assert!(seq_id.is_sequential()); - } -} diff --git a/src/orders/tests/order_type.rs b/src/orders/tests/order_type.rs index 650c2b7..04b1bd4 100644 --- a/src/orders/tests/order_type.rs +++ b/src/orders/tests/order_type.rs @@ -1,13 +1,13 @@ #[cfg(test)] mod tests { use crate::orders::time_in_force::TimeInForce; - use crate::orders::{Hash32, OrderId, OrderType, PegReferenceType, Side}; + use crate::orders::{Hash32, Id, OrderType, PegReferenceType, Side}; use std::str::FromStr; use tracing::info; fn create_standard_order() -> OrderType<()> { OrderType::<()>::Standard { - id: OrderId::from_u64(123), + id: Id::from_u64(123), price: 10000u128, quantity: 5, side: Side::Buy, @@ -21,7 +21,7 @@ mod tests { // Helper function to create an iceberg order for testing fn create_iceberg_order() -> OrderType<()> { OrderType::<()>::IcebergOrder { - id: OrderId::from_u64(124), + id: Id::from_u64(124), price: 10000u128, visible_quantity: 1, hidden_quantity: 4, @@ -36,7 +36,7 @@ mod tests { // Helper function to create a post-only order for testing fn create_post_only_order() -> OrderType<()> { OrderType::<()>::PostOnly { - id: OrderId::from_u64(125), + id: Id::from_u64(125), price: 10000u128, quantity: 5, side: Side::Buy, @@ -50,7 +50,7 @@ mod tests { // Helper function to create a trailing stop order for testing fn create_trailing_stop_order() -> OrderType<()> { OrderType::<()>::TrailingStop { - id: OrderId::from_u64(126), + id: Id::from_u64(126), price: 10000u128, quantity: 5, side: Side::Sell, @@ -66,7 +66,7 @@ mod tests { // Helper function to create a pegged order for testing fn create_pegged_order() -> OrderType<()> { OrderType::<()>::PeggedOrder { - id: OrderId::from_u64(127), + id: Id::from_u64(127), price: 10000u128, quantity: 5, side: Side::Buy, @@ -82,7 +82,7 @@ mod tests { // Helper function to create a market-to-limit order for testing fn create_market_to_limit_order() -> OrderType<()> { OrderType::<()>::MarketToLimit { - id: OrderId::from_u64(128), + id: Id::from_u64(128), price: 10000u128, quantity: 5, side: Side::Buy, @@ -96,7 +96,7 @@ mod tests { // Helper function to create a reserve order for testing fn create_reserve_order() -> OrderType<()> { OrderType::<()>::ReserveOrder { - id: OrderId::from_u64(129), + id: Id::from_u64(129), price: 10000u128, visible_quantity: 1, hidden_quantity: 4, @@ -113,13 +113,13 @@ mod tests { #[test] fn test_order_id() { - assert_eq!(create_standard_order().id(), OrderId::from_u64(123)); - assert_eq!(create_iceberg_order().id(), OrderId::from_u64(124)); - assert_eq!(create_post_only_order().id(), OrderId::from_u64(125)); - assert_eq!(create_trailing_stop_order().id(), OrderId::from_u64(126)); - assert_eq!(create_pegged_order().id(), OrderId::from_u64(127)); - assert_eq!(create_market_to_limit_order().id(), OrderId::from_u64(128)); - assert_eq!(create_reserve_order().id(), OrderId::from_u64(129)); + assert_eq!(create_standard_order().id(), Id::from_u64(123)); + assert_eq!(create_iceberg_order().id(), Id::from_u64(124)); + assert_eq!(create_post_only_order().id(), Id::from_u64(125)); + assert_eq!(create_trailing_stop_order().id(), Id::from_u64(126)); + assert_eq!(create_pegged_order().id(), Id::from_u64(127)); + assert_eq!(create_market_to_limit_order().id(), Id::from_u64(128)); + assert_eq!(create_reserve_order().id(), Id::from_u64(129)); } #[test] @@ -390,7 +390,7 @@ mod tests { .. } = order { - assert_eq!(id, OrderId::from_u64(123)); + assert_eq!(id, Id::from_u64(123)); assert_eq!(price, 10000u128); assert_eq!(quantity, 5); assert_eq!(side, Side::Buy); @@ -417,7 +417,7 @@ mod tests { .. } = order { - assert_eq!(id, OrderId::from_u64(124)); + assert_eq!(id, Id::from_u64(124)); assert_eq!(price, 10000u128); assert_eq!(visible_quantity, 1); assert_eq!(hidden_quantity, 4); @@ -446,7 +446,7 @@ mod tests { .. } = order { - assert_eq!(id, OrderId::from_u64(126)); + assert_eq!(id, Id::from_u64(126)); assert_eq!(price, 10000u128); assert_eq!(quantity, 5); assert_eq!(side, Side::Sell); @@ -476,7 +476,7 @@ mod tests { .. } = order { - assert_eq!(id, OrderId::from_u64(127)); + assert_eq!(id, Id::from_u64(127)); assert_eq!(price, 10000u128); assert_eq!(quantity, 5); assert_eq!(side, Side::Buy); @@ -649,7 +649,7 @@ mod tests { fn test_with_reduced_quantity_market_to_limit() { // Lines 663-664 let order = OrderType::<()>::MarketToLimit { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 1000u128, quantity: 10, side: Side::Buy, @@ -674,7 +674,7 @@ mod tests { fn test_with_reduced_quantity_pegged_order() { // Lines 720-721 let order = OrderType::PeggedOrder { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 1000u128, quantity: 10, side: Side::Buy, @@ -701,7 +701,7 @@ mod tests { fn test_with_reduced_quantity_trailing_stop() { // Line 741 let order = OrderType::TrailingStop { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 1000u128, quantity: 10, side: Side::Buy, @@ -728,7 +728,7 @@ mod tests { fn test_refresh_iceberg_non_iceberg_orders() { // Line 760 let standard_order = OrderType::Standard { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 1000u128, quantity: 10, side: Side::Buy, @@ -753,7 +753,7 @@ mod tests { fn test_match_against_trailing_stop_order() { // Line 809 (or nearby) let order = OrderType::TrailingStop { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 1000u128, quantity: 10, side: Side::Buy, @@ -792,13 +792,13 @@ mod tests { #[cfg(test)] mod test_order_type_display { use crate::orders::time_in_force::TimeInForce; - use crate::orders::{Hash32, OrderId, OrderType, PegReferenceType, Side}; + use crate::orders::{Hash32, Id, OrderType, PegReferenceType, Side}; use std::str::FromStr; #[test] fn test_standard_order_display() { let order = OrderType::Standard { - id: OrderId::from_u64(123), + id: Id::from_u64(123), price: 10000u128, quantity: 5, side: Side::Buy, @@ -826,7 +826,7 @@ mod test_order_type_display { .. }) = parsed { - assert_eq!(id, OrderId::from_u64(123)); + assert_eq!(id, Id::from_u64(123)); assert_eq!(price, 10000u128); assert_eq!(quantity, 5); assert_eq!(side, Side::Buy); @@ -838,7 +838,7 @@ mod test_order_type_display { #[test] fn test_iceberg_order_display() { let order = OrderType::IcebergOrder { - id: OrderId::from_u64(124), + id: Id::from_u64(124), price: 10000u128, visible_quantity: 1, hidden_quantity: 4, @@ -868,7 +868,7 @@ mod test_order_type_display { .. }) = parsed { - assert_eq!(id, OrderId::from_u64(124)); + assert_eq!(id, Id::from_u64(124)); assert_eq!(price, 10000u128); assert_eq!(visible_quantity, 1); assert_eq!(hidden_quantity, 4); @@ -881,7 +881,7 @@ mod test_order_type_display { #[test] fn test_post_only_order_display() { let order = OrderType::PostOnly { - id: OrderId::from_u64(125), + id: Id::from_u64(125), price: 10000u128, quantity: 5, side: Side::Buy, @@ -915,7 +915,7 @@ mod test_order_type_display { #[test] fn test_trailing_stop_order_display() { let order = OrderType::TrailingStop { - id: OrderId::from_u64(126), + id: Id::from_u64(126), price: 10000u128, quantity: 5, side: Side::Sell, @@ -948,7 +948,7 @@ mod test_order_type_display { #[test] fn test_pegged_order_display() { let order = OrderType::PeggedOrder { - id: OrderId::from_u64(127), + id: Id::from_u64(127), price: 10000u128, quantity: 5, side: Side::Buy, @@ -981,7 +981,7 @@ mod test_order_type_display { #[test] fn test_market_to_limit_order_display() { let order = OrderType::MarketToLimit { - id: OrderId::from_u64(128), + id: Id::from_u64(128), price: 10000u128, quantity: 5, side: Side::Buy, @@ -1010,7 +1010,7 @@ mod test_order_type_display { #[test] fn test_reserve_order_display() { let order = OrderType::ReserveOrder { - id: OrderId::from_u64(129), + id: Id::from_u64(129), price: 10000u128, visible_quantity: 1, hidden_quantity: 4, @@ -1045,7 +1045,7 @@ mod test_order_type_display { #[cfg(test)] mod from_str_specific_tests { - use crate::orders::{Hash32, OrderId, OrderType, PegReferenceType, Side, TimeInForce}; + use crate::orders::{Hash32, Id, OrderType, PegReferenceType, Side, TimeInForce}; use std::str::FromStr; #[test] @@ -1068,7 +1068,7 @@ mod from_str_specific_tests { auto_replenish, .. } => { - assert_eq!(id, OrderId::from_u64(129)); + assert_eq!(id, Id::from_u64(129)); assert_eq!(price, 10000); assert_eq!(visible_quantity, 1); assert_eq!(hidden_quantity, 4); @@ -1137,7 +1137,7 @@ mod from_str_specific_tests { time_in_force, .. } => { - assert_eq!(id, OrderId::from_u64(128)); + assert_eq!(id, Id::from_u64(128)); assert_eq!(price, 10000); assert_eq!(quantity, 5); assert_eq!(side, Side::Buy); @@ -1188,7 +1188,7 @@ mod from_str_specific_tests { reference_price_type, .. } => { - assert_eq!(id, OrderId::from_u64(127)); + assert_eq!(id, Id::from_u64(127)); assert_eq!(price, 10000); assert_eq!(quantity, 5); assert_eq!(side, Side::Buy); @@ -1320,7 +1320,7 @@ mod from_str_specific_tests { reference_price_offset, .. } => { - assert_eq!(id, OrderId::from_u64(u64::MAX)); + assert_eq!(id, Id::from_u64(u64::MAX)); assert_eq!(price, u64::MAX.into()); assert_eq!(quantity, u64::MAX); assert_eq!(timestamp, u64::MAX); @@ -1352,7 +1352,7 @@ mod from_str_specific_tests { // Create sample orders let orders = vec![ OrderType::ReserveOrder { - id: OrderId::from_u64(129), + id: Id::from_u64(129), price: 10000u128, visible_quantity: 1, hidden_quantity: 4, @@ -1366,7 +1366,7 @@ mod from_str_specific_tests { extra_fields: (), }, OrderType::MarketToLimit { - id: OrderId::from_u64(128), + id: Id::from_u64(128), price: 10000u128, quantity: 5, side: Side::Buy, @@ -1376,7 +1376,7 @@ mod from_str_specific_tests { extra_fields: (), }, OrderType::PeggedOrder { - id: OrderId::from_u64(127), + id: Id::from_u64(127), price: 10000u128, quantity: 5, side: Side::Buy, diff --git a/src/orders/tests/update.rs b/src/orders/tests/update.rs index 4dde58f..6de3e90 100644 --- a/src/orders/tests/update.rs +++ b/src/orders/tests/update.rs @@ -1,8 +1,8 @@ #[cfg(test)] mod tests_order_update { use crate::errors::PriceLevelError; - use crate::orders::base::{OrderId, Side}; use crate::orders::update::OrderUpdate; + use crate::orders::{Id, Side}; use std::str::FromStr; #[test] @@ -15,7 +15,7 @@ mod tests_order_update { order_id, new_price, } => { - assert_eq!(order_id, OrderId::from_u64(123)); + assert_eq!(order_id, Id::from_u64(123)); assert_eq!(new_price, 1000); } _ => panic!("Expected UpdatePrice variant"), @@ -31,7 +31,7 @@ mod tests_order_update { order_id, new_quantity, } => { - assert_eq!(order_id, OrderId::from_u64(456)); + assert_eq!(order_id, Id::from_u64(456)); assert_eq!(new_quantity, 50); } _ => panic!("Expected UpdateQuantity variant"), @@ -48,7 +48,7 @@ mod tests_order_update { new_price, new_quantity, } => { - assert_eq!(order_id, OrderId::from_u64(789)); + assert_eq!(order_id, Id::from_u64(789)); assert_eq!(new_price, 2000); assert_eq!(new_quantity, 30); } @@ -63,7 +63,7 @@ mod tests_order_update { match result { OrderUpdate::Cancel { order_id } => { - assert_eq!(order_id, OrderId::from_u64(101)); + assert_eq!(order_id, Id::from_u64(101)); } _ => panic!("Expected Cancel variant"), } @@ -82,7 +82,7 @@ mod tests_order_update { quantity, side, } => { - assert_eq!(order_id, OrderId::from_u64(202)); + assert_eq!(order_id, Id::from_u64(202)); assert_eq!(price, 3000); assert_eq!(quantity, 40); assert_eq!(side, Side::Buy); @@ -103,7 +103,7 @@ mod tests_order_update { quantity, side, } => { - assert_eq!(order_id, OrderId::from_u64(303)); + assert_eq!(order_id, Id::from_u64(303)); assert_eq!(price, 4000); assert_eq!(quantity, 60); assert_eq!(side, Side::Sell); @@ -170,7 +170,7 @@ mod tests_order_update { #[test] fn test_display_update_price() { let update = OrderUpdate::UpdatePrice { - order_id: OrderId::from_u64(123), + order_id: Id::from_u64(123), new_price: 1000, }; @@ -183,7 +183,7 @@ mod tests_order_update { #[test] fn test_display_update_quantity() { let update = OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(456), + order_id: Id::from_u64(456), new_quantity: 50, }; @@ -196,7 +196,7 @@ mod tests_order_update { #[test] fn test_display_update_price_and_quantity() { let update = OrderUpdate::UpdatePriceAndQuantity { - order_id: OrderId::from_u64(789), + order_id: Id::from_u64(789), new_price: 2000, new_quantity: 30, }; @@ -210,7 +210,7 @@ mod tests_order_update { #[test] fn test_display_cancel() { let update = OrderUpdate::Cancel { - order_id: OrderId::from_u64(101), + order_id: Id::from_u64(101), }; assert_eq!( @@ -222,7 +222,7 @@ mod tests_order_update { #[test] fn test_display_replace() { let update = OrderUpdate::Replace { - order_id: OrderId::from_u64(202), + order_id: Id::from_u64(202), price: 3000, quantity: 40, side: Side::Buy, @@ -239,29 +239,29 @@ mod tests_order_update { // Create instances of each variant let updates = vec![ OrderUpdate::UpdatePrice { - order_id: OrderId::from_u64(123), + order_id: Id::from_u64(123), new_price: 1000, }, OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(456), + order_id: Id::from_u64(456), new_quantity: 50, }, OrderUpdate::UpdatePriceAndQuantity { - order_id: OrderId::from_u64(789), + order_id: Id::from_u64(789), new_price: 2000, new_quantity: 30, }, OrderUpdate::Cancel { - order_id: OrderId::from_u64(101), + order_id: Id::from_u64(101), }, OrderUpdate::Replace { - order_id: OrderId::from_u64(202), + order_id: Id::from_u64(202), price: 3000, quantity: 40, side: Side::Buy, }, OrderUpdate::Replace { - order_id: OrderId::from_u64(303), + order_id: Id::from_u64(303), price: 4000, quantity: 60, side: Side::Sell, @@ -282,7 +282,7 @@ mod tests_order_update { fn test_order_update_display_detailed() { // Test display of UpdatePrice let update = OrderUpdate::UpdatePrice { - order_id: OrderId::from_u64(123), + order_id: Id::from_u64(123), new_price: 10500, }; let display_string = update.to_string(); @@ -293,7 +293,7 @@ mod tests_order_update { // Test display of UpdateQuantity let update = OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(456), + order_id: Id::from_u64(456), new_quantity: 75, }; let display_string = update.to_string(); @@ -304,7 +304,7 @@ mod tests_order_update { // Test display of UpdatePriceAndQuantity let update = OrderUpdate::UpdatePriceAndQuantity { - order_id: OrderId::from_u64(789), + order_id: Id::from_u64(789), new_price: 11000, new_quantity: 50, }; @@ -316,7 +316,7 @@ mod tests_order_update { // Test display of Replace let update = OrderUpdate::Replace { - order_id: OrderId::from_u64(202), + order_id: Id::from_u64(202), price: 12000, quantity: 60, side: Side::Sell, @@ -341,7 +341,7 @@ mod tests_order_update { quantity, side, } => { - assert_eq!(order_id, OrderId::from_u64(202)); + assert_eq!(order_id, Id::from_u64(202)); assert_eq!(price, 12000); assert_eq!(quantity, 60); assert_eq!(side, Side::Buy); @@ -360,7 +360,7 @@ mod tests_order_update { quantity, side, } => { - assert_eq!(order_id, OrderId::from_u64(202)); + assert_eq!(order_id, Id::from_u64(202)); assert_eq!(price, 12000); assert_eq!(quantity, 60); assert_eq!(side, Side::Sell); @@ -377,7 +377,7 @@ mod tests_order_update { #[test] fn test_update_display_cancel() { let update = OrderUpdate::Cancel { - order_id: OrderId::from_u64(123), + order_id: Id::from_u64(123), }; assert_eq!( diff --git a/src/orders/update.rs b/src/orders/update.rs index 84f363b..9760ccb 100644 --- a/src/orders/update.rs +++ b/src/orders/update.rs @@ -1,5 +1,5 @@ use crate::errors::PriceLevelError; -use crate::orders::base::{OrderId, Side}; +use crate::orders::{Id, Side}; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -9,7 +9,7 @@ pub enum OrderUpdate { /// Update the price of an order UpdatePrice { /// ID of the order to update - order_id: OrderId, + order_id: Id, /// New price for the order new_price: u128, }, @@ -17,7 +17,7 @@ pub enum OrderUpdate { /// Update the quantity of an order UpdateQuantity { /// ID of the order to update - order_id: OrderId, + order_id: Id, /// New quantity for the order new_quantity: u64, }, @@ -25,7 +25,7 @@ pub enum OrderUpdate { /// Update both price and quantity of an order UpdatePriceAndQuantity { /// ID of the order to update - order_id: OrderId, + order_id: Id, /// New price for the order new_price: u128, /// New quantity for the order @@ -35,13 +35,13 @@ pub enum OrderUpdate { /// Cancel an order Cancel { /// ID of the order to cancel - order_id: OrderId, + order_id: Id, }, /// Replace an order entirely with a new one Replace { /// ID of the order to replace - order_id: OrderId, + order_id: Id, /// New price for the replacement order price: u128, /// New quantity for the replacement order @@ -99,7 +99,7 @@ impl FromStr for OrderUpdate { // Parse order_id field which is common to all update types let order_id_str = get_field("order_id")?; let order_id = - OrderId::from_str(order_id_str).map_err(|_| PriceLevelError::InvalidFieldValue { + Id::from_str(order_id_str).map_err(|_| PriceLevelError::InvalidFieldValue { field: "order_id".to_string(), value: order_id_str.to_string(), })?; diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 0000000..0b0d8eb --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,15 @@ +//! Public prelude for convenient imports. +//! +//! Import this module to bring the most common public API types into scope: +//! +//! ```rust +//! use pricelevel::prelude::*; +//! ``` + +pub use crate::errors::PriceLevelError; +pub use crate::execution::{MatchResult, Trade, TradeList}; +pub use crate::orders::DEFAULT_RESERVE_REPLENISH_AMOUNT; +pub use crate::orders::PegReferenceType; +pub use crate::orders::{Hash32, Id, OrderType, OrderUpdate, Side, TimeInForce}; +pub use crate::price_level::{OrderQueue, PriceLevel, PriceLevelData, PriceLevelSnapshot}; +pub use crate::utils::{UuidGenerator, setup_logger}; diff --git a/src/price_level/level.rs b/src/price_level/level.rs index 4361e67..1ca0bce 100644 --- a/src/price_level/level.rs +++ b/src/price_level/level.rs @@ -3,7 +3,7 @@ use crate::UuidGenerator; use crate::errors::PriceLevelError; use crate::execution::{MatchResult, Trade}; -use crate::orders::{OrderId, OrderType, OrderUpdate}; +use crate::orders::{Id, OrderType, OrderUpdate}; use crate::price_level::order_queue::OrderQueue; use crate::price_level::{PriceLevelSnapshot, PriceLevelSnapshotPackage, PriceLevelStatistics}; use serde::{Deserialize, Serialize}; @@ -161,7 +161,7 @@ impl PriceLevel { pub fn match_order( &self, incoming_quantity: u64, - taker_order_id: OrderId, + taker_order_id: Id, trade_id_generator: &UuidGenerator, ) -> MatchResult { let mut result = MatchResult::new(taker_order_id, incoming_quantity); @@ -177,7 +177,7 @@ impl PriceLevel { self.visible_quantity.fetch_sub(consumed, Ordering::AcqRel); // Use UUID generator directly - let trade_id = trade_id_generator.next(); + let trade_id = Id::from_uuid(trade_id_generator.next()); let trade = Trade::new( trade_id, diff --git a/src/price_level/order_queue.rs b/src/price_level/order_queue.rs index 7cfe99e..97bc1a7 100644 --- a/src/price_level/order_queue.rs +++ b/src/price_level/order_queue.rs @@ -1,5 +1,5 @@ use crate::errors::PriceLevelError; -use crate::orders::{OrderId, OrderType}; +use crate::orders::{Id, OrderType}; use crossbeam::queue::SegQueue; use dashmap::DashMap; use serde::de::{SeqAccess, Visitor}; @@ -15,9 +15,9 @@ use std::sync::Arc; #[derive(Debug)] pub struct OrderQueue { /// A map of order IDs to orders for quick lookups - orders: DashMap>>, + orders: DashMap>>, /// A queue of order IDs to maintain FIFO order - order_ids: SegQueue, + order_ids: SegQueue, } impl OrderQueue { @@ -52,13 +52,13 @@ impl OrderQueue { } /// Search for an order with the given ID. O(1) operation. - pub fn find(&self, order_id: OrderId) -> Option>> { + pub fn find(&self, order_id: Id) -> Option>> { self.orders.get(&order_id).map(|o| o.value().clone()) } /// Remove an order with the given ID /// Returns the removed order if found. O(1) for the map, but the ID remains in the queue. - pub fn remove(&self, order_id: OrderId) -> Option>> { + pub fn remove(&self, order_id: Id) -> Option>> { self.orders.remove(&order_id).map(|(_, order)| order) } diff --git a/src/price_level/tests/entry.rs b/src/price_level/tests/entry.rs index daeef66..aa4f24e 100644 --- a/src/price_level/tests/entry.rs +++ b/src/price_level/tests/entry.rs @@ -2,7 +2,7 @@ mod tests { use crate::price_level::entry::OrderBookEntry; use crate::price_level::level::PriceLevel; - use crate::{Hash32, OrderId, OrderType, Side, TimeInForce}; + use crate::{Hash32, Id, OrderType, Side, TimeInForce}; use std::str::FromStr; use std::sync::Arc; use tracing::info; @@ -124,7 +124,7 @@ mod tests { // Add an order to make the test more meaningful let order = OrderType::Standard { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 1000u128, quantity: 10, side: Side::Buy, @@ -215,7 +215,7 @@ mod tests_order_book_entry { // Add some orders and check again let order_type = crate::orders::OrderType::Standard { - id: crate::orders::OrderId::from_u64(1), + id: crate::orders::Id::from_u64(1), price: 1000u128, quantity: 10, side: crate::orders::Side::Buy, @@ -230,7 +230,7 @@ mod tests_order_book_entry { // Add another order let order_type2 = crate::orders::OrderType::Standard { - id: crate::orders::OrderId::from_u64(2), + id: crate::orders::Id::from_u64(2), price: 1000u128, quantity: 20, side: crate::orders::Side::Buy, @@ -362,7 +362,7 @@ mod tests_order_book_entry { // Add an order with visible quantity let standard_order = crate::orders::OrderType::Standard { - id: crate::orders::OrderId::from_u64(1), + id: crate::orders::Id::from_u64(1), price: 1000u128, quantity: 10, side: crate::orders::Side::Buy, @@ -379,7 +379,7 @@ mod tests_order_book_entry { // Add an iceberg order with hidden quantity let iceberg_order = crate::orders::OrderType::IcebergOrder { - id: crate::orders::OrderId::from_u64(2), + id: crate::orders::Id::from_u64(2), price: 1000u128, visible_quantity: 5, hidden_quantity: 15, diff --git a/src/price_level/tests/level.rs b/src/price_level/tests/level.rs index ed835a4..580abef 100644 --- a/src/price_level/tests/level.rs +++ b/src/price_level/tests/level.rs @@ -1,9 +1,7 @@ #[cfg(test)] mod tests { use crate::errors::PriceLevelError; - use crate::orders::{ - Hash32, OrderId, OrderType, OrderUpdate, PegReferenceType, Side, TimeInForce, - }; + use crate::orders::{Hash32, Id, OrderType, OrderUpdate, PegReferenceType, Side, TimeInForce}; use crate::price_level::level::{PriceLevel, PriceLevelData}; use crate::price_level::snapshot::SNAPSHOT_FORMAT_VERSION; use crate::{DEFAULT_RESERVE_REPLENISH_AMOUNT, UuidGenerator}; @@ -17,7 +15,7 @@ mod tests { // Helper functions to create different order types for testing pub fn create_standard_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { - let order_id = OrderId::from_u64(id); + let order_id = Id::from_u64(id); let timestamp = TIMESTAMP_COUNTER.fetch_add(1, Ordering::SeqCst); OrderType::Standard { id: order_id, @@ -55,12 +53,12 @@ mod tests { assert_eq!(restored.hidden_quantity(), price_level.hidden_quantity()); assert_eq!(restored.order_count(), price_level.order_count()); - let original_ids: Vec = price_level + let original_ids: Vec = price_level .iter_orders() .into_iter() .map(|order| order.id()) .collect(); - let restored_ids: Vec = restored + let restored_ids: Vec = restored .iter_orders() .into_iter() .map(|order| order.id()) @@ -157,7 +155,7 @@ mod tests { fn create_iceberg_order(id: u64, price: u128, visible: u64, hidden: u64) -> OrderType<()> { let timestamp = TIMESTAMP_COUNTER.fetch_add(1, Ordering::SeqCst); OrderType::IcebergOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, visible_quantity: visible, hidden_quantity: hidden, @@ -172,7 +170,7 @@ mod tests { fn create_post_only_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { let timestamp = TIMESTAMP_COUNTER.fetch_add(1, Ordering::SeqCst); OrderType::PostOnly { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -186,7 +184,7 @@ mod tests { fn create_trailing_stop_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { let timestamp = TIMESTAMP_COUNTER.fetch_add(1, Ordering::SeqCst); OrderType::TrailingStop { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Sell, @@ -202,7 +200,7 @@ mod tests { fn create_pegged_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { let timestamp = TIMESTAMP_COUNTER.fetch_add(1, Ordering::SeqCst); OrderType::PeggedOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -218,7 +216,7 @@ mod tests { fn create_market_to_limit_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { let timestamp = TIMESTAMP_COUNTER.fetch_add(1, Ordering::SeqCst); OrderType::MarketToLimit { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -240,7 +238,7 @@ mod tests { ) -> OrderType<()> { let timestamp = TIMESTAMP_COUNTER.fetch_add(1, Ordering::SeqCst); OrderType::ReserveOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, visible_quantity: visible, hidden_quantity: hidden, @@ -258,7 +256,7 @@ mod tests { fn create_fill_or_kill_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { let timestamp = TIMESTAMP_COUNTER.fetch_add(1, Ordering::SeqCst); OrderType::Standard { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -272,7 +270,7 @@ mod tests { fn create_immediate_or_cancel_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { let timestamp = TIMESTAMP_COUNTER.fetch_add(1, Ordering::SeqCst); OrderType::Standard { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -291,7 +289,7 @@ mod tests { ) -> OrderType<()> { let timestamp = TIMESTAMP_COUNTER.fetch_add(1, Ordering::SeqCst); OrderType::Standard { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -332,7 +330,7 @@ mod tests { assert_eq!(price_level.total_quantity(), 100); // Verify the returned Arc contains the expected order - assert_eq!(order_arc.id(), OrderId::from_u64(1)); + assert_eq!(order_arc.id(), Id::from_u64(1)); assert_eq!(order_arc.price(), 10000); assert_eq!(order_arc.visible_quantity(), 100); @@ -381,20 +379,20 @@ mod tests { // Cancel the standard order using OrderUpdate let result = price_level.update_order(OrderUpdate::Cancel { - order_id: OrderId::from_u64(1), + order_id: Id::from_u64(1), }); assert!(result.is_ok()); let removed = result.unwrap(); assert!(removed.is_some()); - assert_eq!(removed.unwrap().id(), OrderId::from_u64(1)); + assert_eq!(removed.unwrap().id(), Id::from_u64(1)); assert_eq!(price_level.visible_quantity(), 50); assert_eq!(price_level.hidden_quantity(), 200); assert_eq!(price_level.order_count(), 1); // Cancel the iceberg order let result = price_level.update_order(OrderUpdate::Cancel { - order_id: OrderId::from_u64(2), + order_id: Id::from_u64(2), }); assert!(result.is_ok()); @@ -406,7 +404,7 @@ mod tests { // Try to cancel a non-existent order let result = price_level.update_order(OrderUpdate::Cancel { - order_id: OrderId::from_u64(3), + order_id: Id::from_u64(3), }); assert!(result.is_ok()); @@ -427,8 +425,8 @@ mod tests { let orders = price_level.iter_orders(); assert_eq!(orders.len(), 2); - assert_eq!(orders[0].id(), OrderId::from_u64(1)); - assert_eq!(orders[1].id(), OrderId::from_u64(2)); + assert_eq!(orders[0].id(), Id::from_u64(1)); + assert_eq!(orders[1].id(), Id::from_u64(2)); // Verify the orders are still in the queue after iteration assert_eq!(price_level.order_count(), 2); @@ -443,7 +441,7 @@ mod tests { price_level.add_order(create_standard_order(1, 10000, 100)); // Match the entire order - let taker_id = OrderId::from_u64(999); // market order ID + let taker_id = Id::from_u64(999); // market order ID let match_result = price_level.match_order(100, taker_id, &transaction_id_generator); assert_eq!(match_result.order_id, taker_id); @@ -455,13 +453,13 @@ mod tests { assert_eq!(match_result.trades.len(), 1); let transaction = &match_result.trades.as_vec()[0]; assert_eq!(transaction.taker_order_id, taker_id); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(1)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 100); assert_eq!(transaction.taker_side, Side::Sell); // Taker is a market order, so it's a sell side opposite of maker assert_eq!(match_result.filled_order_ids.len(), 1); - assert_eq!(match_result.filled_order_ids[0], OrderId::from_u64(1)); + assert_eq!(match_result.filled_order_ids[0], Id::from_u64(1)); // Verify stats assert_eq!(price_level.stats().orders_executed(), 1); @@ -478,7 +476,7 @@ mod tests { price_level.add_order(create_standard_order(1, 10000, 100)); // Match part of the order - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(60, taker_id, &transaction_id_generator); // Verificar el resultado de matching @@ -492,7 +490,7 @@ mod tests { assert_eq!(match_result.trades.len(), 1); let transaction = &match_result.trades.as_vec()[0]; assert_eq!(transaction.taker_order_id, taker_id); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(1)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 60); assert_eq!(transaction.taker_side, Side::Sell); @@ -514,7 +512,7 @@ mod tests { price_level.add_order(create_standard_order(1, 10000, 100)); // Match with quantity exceeding available - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(150, taker_id, &transaction_id_generator); assert_eq!(match_result.order_id, taker_id); @@ -526,12 +524,12 @@ mod tests { assert_eq!(match_result.trades.len(), 1); let transaction = &match_result.trades.as_vec()[0]; assert_eq!(transaction.taker_order_id, taker_id); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(1)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 100); assert_eq!(match_result.filled_order_ids.len(), 1); - assert_eq!(match_result.filled_order_ids[0], OrderId::from_u64(1)); + assert_eq!(match_result.filled_order_ids[0], Id::from_u64(1)); } // ------------------------------------------- ICEBERG ORDERS ------------------------------------------- @@ -550,7 +548,7 @@ mod tests { price_level.add_order(create_iceberg_order(1, 10000, 50, 100)); // Match the visible portion of the iceberg order. - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(50, taker_id, &transaction_id_generator); // Assertions to validate the match result. @@ -565,14 +563,14 @@ mod tests { // Assertions about the generated transaction let transaction = &match_result.trades.as_vec()[0]; assert_eq!(transaction.taker_order_id, taker_id); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(1)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 50); assert_eq!(transaction.taker_side, Side::Buy); assert_eq!(match_result.filled_order_ids.len(), 0); // Match another 50 units, which should deplete the visible portion and reveal more. - let taker_id = OrderId::from_u64(1000); + let taker_id = Id::from_u64(1000); let match_result = price_level.match_order(50, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); assert!(match_result.is_complete); @@ -582,14 +580,14 @@ mod tests { let transaction = &match_result.trades.as_vec()[0]; assert_eq!(transaction.taker_order_id, taker_id); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(1)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 50); assert_eq!(transaction.taker_side, Side::Buy); assert_eq!(match_result.filled_order_ids.len(), 0); // Match the remaining 50 units (50 visible + 0 hidden). - let taker_id = OrderId::from_u64(1001); + let taker_id = Id::from_u64(1001); let match_result = price_level.match_order(50, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); assert!(match_result.is_complete); @@ -597,7 +595,7 @@ mod tests { assert_eq!(price_level.hidden_quantity(), 0); assert_eq!(price_level.order_count(), 0); assert_eq!(match_result.filled_order_ids.len(), 1); - assert_eq!(match_result.filled_order_ids[0], OrderId::from_u64(1)); + assert_eq!(match_result.filled_order_ids[0], Id::from_u64(1)); } #[test] @@ -610,7 +608,7 @@ mod tests { price_level.add_order(create_iceberg_order(1, 10000, 100, 100)); // Match the visible portion of the iceberg order. - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(50, taker_id, &transaction_id_generator); // Assertions to validate the match result. @@ -625,14 +623,14 @@ mod tests { // Assertions about the generated transaction let transaction = &match_result.trades.as_vec()[0]; assert_eq!(transaction.taker_order_id, taker_id); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(1)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 50); assert_eq!(transaction.taker_side, Side::Buy); assert_eq!(match_result.filled_order_ids.len(), 0); // Match another 50 units, which should deplete the visible portion and reveal more. - let taker_id = OrderId::from_u64(1000); + let taker_id = Id::from_u64(1000); let match_result = price_level.match_order(50, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); assert!(match_result.is_complete); @@ -642,14 +640,14 @@ mod tests { let transaction = &match_result.trades.as_vec()[0]; assert_eq!(transaction.taker_order_id, taker_id); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(1)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 50); assert_eq!(transaction.taker_side, Side::Buy); assert_eq!(match_result.filled_order_ids.len(), 0); // Match the remaining 50 units (50 visible + 0 hidden). - let taker_id = OrderId::from_u64(1001); + let taker_id = Id::from_u64(1001); // This should match the remaining visible quantity and deplete the hidden quantity. let match_result = price_level.match_order(150, taker_id, &transaction_id_generator); @@ -659,7 +657,7 @@ mod tests { assert_eq!(price_level.hidden_quantity(), 0); assert_eq!(price_level.order_count(), 0); assert_eq!(match_result.filled_order_ids.len(), 1); - assert_eq!(match_result.filled_order_ids[0], OrderId::from_u64(1)); + assert_eq!(match_result.filled_order_ids[0], Id::from_u64(1)); } #[test] @@ -671,7 +669,7 @@ mod tests { price_level.add_order(create_iceberg_order(1, 10000, 50, 150)); // Match part of the visible portion - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(30, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -696,7 +694,7 @@ mod tests { price_level.add_order(create_reserve_order(1, 10000, 50, 150, 20, false, None)); // Match the entire visible portion - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(50, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -720,7 +718,7 @@ mod tests { price_level.add_order(create_reserve_order(1, 10000, 50, 150, 20, true, None)); // Match the entire visible portion - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(50, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -750,7 +748,7 @@ mod tests { price_level.add_order(create_reserve_order(1, 10000, 50, 150, 20, false, None)); // Match partially, but still above threshold - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(25, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -759,7 +757,7 @@ mod tests { assert_eq!(price_level.hidden_quantity(), 150); // No change to hidden quantity // Match more to go below threshold - let taker_id = OrderId::from_u64(1000); + let taker_id = Id::from_u64(1000); let match_result = price_level.match_order(10, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -791,7 +789,7 @@ mod tests { )); // Match the entire visible portion - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(50, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -815,7 +813,7 @@ mod tests { price_level.add_order(create_reserve_order(1, 10000, 50, 150, 0, true, None)); // Match partially - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(49, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -838,7 +836,7 @@ mod tests { price_level.add_order(create_reserve_order(1, 10000, 50, 150, 0, false, None)); // Match the entire visible portion - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(50, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -861,7 +859,7 @@ mod tests { price_level.add_order(create_reserve_order(1, 10000, 50, 150, 1, false, None)); // Match the entire visible portion - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(50, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -884,7 +882,7 @@ mod tests { price_level.add_order(create_reserve_order(1, 10000, 50, 150, 20, false, None)); // Match part of the visible portion, but still above threshold - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(25, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -893,7 +891,7 @@ mod tests { assert_eq!(price_level.hidden_quantity(), 150); // No replenishment yet // Match more to go below threshold - let taker_id = OrderId::from_u64(1000); + let taker_id = Id::from_u64(1000); let match_result = price_level.match_order(10, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -919,7 +917,7 @@ mod tests { price_level.add_order(create_reserve_order(1, 10000, 100, 100, 20, true, None)); // Match 80 units, which is above the replenish threshold - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(80, taker_id, &transaction_id_generator); // Validate the match result @@ -934,14 +932,14 @@ mod tests { // Validate the transaction details let transaction = &match_result.trades.as_vec()[0]; assert_eq!(transaction.taker_order_id, taker_id); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(1)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 80); assert_eq!(transaction.taker_side, Side::Buy); assert_eq!(match_result.filled_order_ids.len(), 0); // Match 10 more units, which will take us below the replenish threshold - let taker_id = OrderId::from_u64(1000); + let taker_id = Id::from_u64(1000); let match_result = price_level.match_order(10, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -952,14 +950,14 @@ mod tests { let transaction = &match_result.trades.as_vec()[0]; assert_eq!(transaction.taker_order_id, taker_id); - assert_eq!(transaction.maker_order_id, OrderId::from_u64(1)); + assert_eq!(transaction.maker_order_id, Id::from_u64(1)); assert_eq!(transaction.price, 10000); assert_eq!(transaction.quantity, 10); assert_eq!(transaction.taker_side, Side::Buy); assert_eq!(match_result.filled_order_ids.len(), 0); // Match with a larger amount than what's available - let taker_id = OrderId::from_u64(1001); + let taker_id = Id::from_u64(1001); let match_result = price_level.match_order(150, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 40); // 150 - 90 - 20 = 40 @@ -968,21 +966,21 @@ mod tests { assert_eq!(price_level.hidden_quantity(), 0); assert_eq!(price_level.order_count(), 0); assert_eq!(match_result.filled_order_ids.len(), 1); - assert_eq!(match_result.filled_order_ids[0], OrderId::from_u64(1)); + assert_eq!(match_result.filled_order_ids[0], Id::from_u64(1)); // Verify the correct number and sizes of transactions assert_eq!(match_result.trades.len(), 2); // One for visible, one for hidden let transaction1 = &match_result.trades.as_vec()[0]; assert_eq!(transaction1.taker_order_id, taker_id); - assert_eq!(transaction1.maker_order_id, OrderId::from_u64(1)); + assert_eq!(transaction1.maker_order_id, Id::from_u64(1)); assert_eq!(transaction1.price, 10000); assert_eq!(transaction1.quantity, 90); // First consumes all visible assert_eq!(transaction1.taker_side, Side::Buy); let transaction2 = &match_result.trades.as_vec()[1]; assert_eq!(transaction2.taker_order_id, taker_id); - assert_eq!(transaction2.maker_order_id, OrderId::from_u64(1)); + assert_eq!(transaction2.maker_order_id, Id::from_u64(1)); assert_eq!(transaction2.price, 10000); assert_eq!(transaction2.quantity, 20); // Then consumes all hidden assert_eq!(transaction2.taker_side, Side::Buy); @@ -999,7 +997,7 @@ mod tests { price_level.add_order(create_post_only_order(1, 10000, 100)); // Post-only orders behave like standard orders for matching - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(60, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -1017,7 +1015,7 @@ mod tests { price_level.add_order(create_trailing_stop_order(1, 10000, 100)); // Trailing stop orders behave like standard orders for matching - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(100, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -1035,7 +1033,7 @@ mod tests { price_level.add_order(create_pegged_order(1, 10000, 100)); // Pegged orders behave like standard orders for matching - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(50, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -1053,7 +1051,7 @@ mod tests { price_level.add_order(create_market_to_limit_order(1, 10000, 100)); // Market-to-limit orders behave like standard orders for matching - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(100, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -1071,7 +1069,7 @@ mod tests { price_level.add_order(create_fill_or_kill_order(1, 10000, 100)); // For the price level, FOK behaves like standard orders - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(100, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -1089,7 +1087,7 @@ mod tests { price_level.add_order(create_immediate_or_cancel_order(1, 10000, 100)); // For the price level, IOC behaves like standard orders - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(50, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -1107,7 +1105,7 @@ mod tests { price_level.add_order(create_good_till_date_order(1, 10000, 100, 1617000000000)); // GTD orders behave like standard orders for matching - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(100, taker_id, &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); @@ -1127,7 +1125,7 @@ mod tests { price_level.add_order(create_standard_order(3, 10000, 25)); // Match first two orders completely and third partially - let taker_id = OrderId::from_u64(999); + let taker_id = Id::from_u64(999); let match_result = price_level.match_order(140, taker_id, &transaction_id_generator); // Verificar el resultado de matching @@ -1141,34 +1139,26 @@ mod tests { let transaction1 = &match_result.trades.as_vec()[0]; assert_eq!(transaction1.taker_order_id, taker_id); - assert_eq!(transaction1.maker_order_id, OrderId::from_u64(1)); + assert_eq!(transaction1.maker_order_id, Id::from_u64(1)); assert_eq!(transaction1.quantity, 50); let transaction2 = &match_result.trades.as_vec()[1]; assert_eq!(transaction2.taker_order_id, taker_id); - assert_eq!(transaction2.maker_order_id, OrderId::from_u64(2)); + assert_eq!(transaction2.maker_order_id, Id::from_u64(2)); assert_eq!(transaction2.quantity, 75); let transaction3 = &match_result.trades.as_vec()[2]; assert_eq!(transaction3.taker_order_id, taker_id); - assert_eq!(transaction3.maker_order_id, OrderId::from_u64(3)); + assert_eq!(transaction3.maker_order_id, Id::from_u64(3)); assert_eq!(transaction3.quantity, 15); assert_eq!(match_result.filled_order_ids.len(), 2); - assert!( - match_result - .filled_order_ids - .contains(&OrderId::from_u64(1)) - ); - assert!( - match_result - .filled_order_ids - .contains(&OrderId::from_u64(2)) - ); + assert!(match_result.filled_order_ids.contains(&Id::from_u64(1))); + assert!(match_result.filled_order_ids.contains(&Id::from_u64(2))); let orders = price_level.iter_orders(); assert_eq!(orders.len(), 1); - assert_eq!(orders[0].id(), OrderId::from_u64(3)); + assert_eq!(orders[0].id(), Id::from_u64(3)); assert_eq!(orders[0].visible_quantity(), 10); assert_eq!(orders[0].hidden_quantity(), 0); } @@ -1212,7 +1202,7 @@ mod tests { // Update the price to a different value let update = OrderUpdate::UpdatePrice { - order_id: OrderId::from_u64(1), + order_id: Id::from_u64(1), new_price: 11000, }; @@ -1222,7 +1212,7 @@ mod tests { assert!(result.is_ok()); let removed_order = result.unwrap(); assert!(removed_order.is_some()); - assert_eq!(removed_order.unwrap().id(), OrderId::from_u64(1)); + assert_eq!(removed_order.unwrap().id(), Id::from_u64(1)); // The price level should now be empty assert_eq!(price_level.visible_quantity(), 0); @@ -1233,7 +1223,7 @@ mod tests { price_level.add_order(order); let same_price_update = OrderUpdate::UpdatePrice { - order_id: OrderId::from_u64(2), + order_id: Id::from_u64(2), new_price: 10000, }; @@ -1255,7 +1245,7 @@ mod tests { // Update to increase quantity let update = OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(1), + order_id: Id::from_u64(1), new_quantity: 150, }; @@ -1273,7 +1263,7 @@ mod tests { // Update to decrease quantity let update = OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(1), + order_id: Id::from_u64(1), new_quantity: 50, }; @@ -1291,7 +1281,7 @@ mod tests { // Test updating non-existent order let update = OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(999), + order_id: Id::from_u64(999), new_quantity: 50, }; @@ -1310,7 +1300,7 @@ mod tests { // Update both price and quantity with different price let update = OrderUpdate::UpdatePriceAndQuantity { - order_id: OrderId::from_u64(1), + order_id: Id::from_u64(1), new_price: 11000, new_quantity: 150, }; @@ -1321,7 +1311,7 @@ mod tests { assert!(result.is_ok()); let removed_order = result.unwrap(); assert!(removed_order.is_some()); - assert_eq!(removed_order.unwrap().id(), OrderId::from_u64(1)); + assert_eq!(removed_order.unwrap().id(), Id::from_u64(1)); // The price level should now be empty assert_eq!(price_level.visible_quantity(), 0); @@ -1332,7 +1322,7 @@ mod tests { price_level.add_order(order); let update = OrderUpdate::UpdatePriceAndQuantity { - order_id: OrderId::from_u64(2), + order_id: Id::from_u64(2), new_price: 10000, new_quantity: 150, }; @@ -1360,7 +1350,7 @@ mod tests { // Replace with different price let update = OrderUpdate::Replace { - order_id: OrderId::from_u64(1), + order_id: Id::from_u64(1), price: 11000, quantity: 150, side: Side::Buy, @@ -1372,7 +1362,7 @@ mod tests { assert!(result.is_ok()); let removed_order = result.unwrap(); assert!(removed_order.is_some()); - assert_eq!(removed_order.unwrap().id(), OrderId::from_u64(1)); + assert_eq!(removed_order.unwrap().id(), Id::from_u64(1)); // The price level should now be empty assert_eq!(price_level.visible_quantity(), 0); @@ -1383,7 +1373,7 @@ mod tests { price_level.add_order(order); let update = OrderUpdate::Replace { - order_id: OrderId::from_u64(2), + order_id: Id::from_u64(2), price: 10000, quantity: 150, side: Side::Buy, @@ -1422,9 +1412,9 @@ mod tests { assert_eq!(data.orders.len(), 2); // Verify order IDs - let order_ids: Vec = data.orders.iter().map(|o| o.id()).collect(); - assert!(order_ids.contains(&OrderId::from_u64(1))); - assert!(order_ids.contains(&OrderId::from_u64(2))); + let order_ids: Vec = data.orders.iter().map(|o| o.id()).collect(); + assert!(order_ids.contains(&Id::from_u64(1))); + assert!(order_ids.contains(&Id::from_u64(2))); } // Test the TryFrom implementation for PriceLevel @@ -1458,9 +1448,9 @@ mod tests { let orders = price_level.iter_orders(); assert_eq!(orders.len(), 2); - let order_ids: Vec = orders.iter().map(|o| o.id()).collect(); - assert!(order_ids.contains(&OrderId::from_u64(1))); - assert!(order_ids.contains(&OrderId::from_u64(2))); + let order_ids: Vec = orders.iter().map(|o| o.id()).collect(); + assert!(order_ids.contains(&Id::from_u64(1))); + assert!(order_ids.contains(&Id::from_u64(2))); } // Test Display implementation for PriceLevel @@ -1510,7 +1500,7 @@ mod tests { // Verify the order let orders = price_level.iter_orders(); assert_eq!(orders.len(), 5); - assert_eq!(orders[0].id(), OrderId::from_u64(1)); + assert_eq!(orders[0].id(), Id::from_u64(1)); assert_eq!(orders[0].price(), 10000); assert_eq!(orders[0].visible_quantity(), 50); } @@ -1543,7 +1533,7 @@ mod tests { // Verify the order in the deserialized price level let orders = deserialized.iter_orders(); assert_eq!(orders.len(), 1); - assert_eq!(orders[0].id(), OrderId::from_u64(1)); + assert_eq!(orders[0].id(), Id::from_u64(1)); assert_eq!(orders[0].price(), 10000); assert_eq!(orders[0].visible_quantity(), 100); } @@ -1561,7 +1551,7 @@ mod tests { // Match only part of what's available let match_result = - price_level.match_order(100, OrderId::from_u64(999), &transaction_id_generator); + price_level.match_order(100, Id::from_u64(999), &transaction_id_generator); assert_eq!(match_result.remaining_quantity, 0); assert!(match_result.is_complete); @@ -1578,7 +1568,7 @@ mod tests { // Update to a different price (should remove from this level) let result = price_level.update_order(OrderUpdate::UpdatePrice { - order_id: OrderId::from_u64(1), + order_id: Id::from_u64(1), new_price: 10100, // Different price }); @@ -1597,7 +1587,7 @@ mod tests { // Update the quantity but keep the same price let result = price_level.update_order(OrderUpdate::UpdatePriceAndQuantity { - order_id: OrderId::from_u64(1), + order_id: Id::from_u64(1), new_price: 10000, // Same price new_quantity: 150, }); @@ -1641,7 +1631,7 @@ mod tests { // Test lines 187-188 let price_level = PriceLevel::new(10000); let order = OrderType::<()>::Standard { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 10000u128, quantity: 10, side: Side::Buy, @@ -1654,7 +1644,7 @@ mod tests { // Try to update price to the same value let update = OrderUpdate::UpdatePrice { - order_id: OrderId::from_u64(1), + order_id: Id::from_u64(1), new_price: 10000, }; @@ -1677,7 +1667,7 @@ mod tests { // Try to update quantity of a non-existent order let update = OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(123), + order_id: Id::from_u64(123), new_quantity: 20, }; @@ -1694,7 +1684,7 @@ mod tests { // Add an order let order = OrderType::<()>::Standard { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 10000u128, quantity: 10, side: Side::Buy, @@ -1716,7 +1706,7 @@ mod tests { assert!( price_level .update_order(OrderUpdate::Cancel { - order_id: OrderId::from_u64(1) + order_id: Id::from_u64(1) }) .unwrap() .is_some() @@ -1724,7 +1714,7 @@ mod tests { // Now try to update it after it's been removed let update = OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(1), + order_id: Id::from_u64(1), new_quantity: 20, }; @@ -1740,7 +1730,7 @@ mod tests { // Add an order let order = OrderType::<()>::Standard { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 10000u128, quantity: 50, side: Side::Buy, @@ -1753,7 +1743,7 @@ mod tests { // Update to increase quantity (old visible < new visible) let update = OrderUpdate::UpdateQuantity { - order_id: OrderId::from_u64(1), + order_id: Id::from_u64(1), new_quantity: 100, }; @@ -1772,7 +1762,7 @@ mod tests { // Add an iceberg order with visible and hidden quantities let order = OrderType::IcebergOrder { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 10000u128, visible_quantity: 50, hidden_quantity: 150, @@ -1790,7 +1780,7 @@ mod tests { // Create a new iceberg order with different quantities let new_order = OrderType::IcebergOrder { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 10000u128, visible_quantity: 40, hidden_quantity: 200, @@ -1803,7 +1793,7 @@ mod tests { // Test increasing hidden quantity let result = price_level.update_order(OrderUpdate::Cancel { - order_id: OrderId::from_u64(1), + order_id: Id::from_u64(1), }); assert!(result.is_ok()); price_level.add_order(new_order); @@ -1820,7 +1810,7 @@ mod tests { // Add an order let order = OrderType::<()>::Standard { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 10000u128, quantity: 50, side: Side::Buy, @@ -1833,7 +1823,7 @@ mod tests { // Update both price and quantity with same price let update = OrderUpdate::UpdatePriceAndQuantity { - order_id: OrderId::from_u64(1), + order_id: Id::from_u64(1), new_price: 10000, // Same price new_quantity: 100, }; @@ -1856,7 +1846,7 @@ mod tests { // Add some orders let order1 = OrderType::<()>::Standard { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 10000u128, quantity: 50, side: Side::Buy, @@ -1868,7 +1858,7 @@ mod tests { price_level.add_order(order1); let order2 = OrderType::<()>::IcebergOrder { - id: OrderId::from_u64(2), + id: Id::from_u64(2), price: 10000u128, visible_quantity: 30, hidden_quantity: 70, diff --git a/src/price_level/tests/order_queue.rs b/src/price_level/tests/order_queue.rs index e4d9a60..9aeafdf 100644 --- a/src/price_level/tests/order_queue.rs +++ b/src/price_level/tests/order_queue.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod tests { - use crate::orders::{Hash32, OrderId, OrderType, Side, TimeInForce}; + use crate::orders::{Hash32, Id, OrderType, Side, TimeInForce}; use crate::price_level::order_queue::OrderQueue; use std::str::FromStr; use std::sync::Arc; @@ -8,7 +8,7 @@ mod tests { fn create_test_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { OrderType::<()>::Standard { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -74,10 +74,10 @@ mod tests { // Verify individual orders (order might not be preserved) let has_order1 = orders.iter().any(|o| { - o.id() == OrderId::from_u64(1) && o.price() == 1000u128 && o.visible_quantity() == 10 + o.id() == Id::from_u64(1) && o.price() == 1000u128 && o.visible_quantity() == 10 }); let has_order2 = orders.iter().any(|o| { - o.id() == OrderId::from_u64(2) && o.price() == 1100u128 && o.visible_quantity() == 20 + o.id() == Id::from_u64(2) && o.price() == 1100u128 && o.visible_quantity() == 20 }); assert!(has_order1, "First order not found or incorrect"); @@ -94,10 +94,10 @@ mod tests { ); let round_trip_has_order1 = round_trip_orders.iter().any(|o| { - o.id() == OrderId::from_u64(1) && o.price() == 1000u128 && o.visible_quantity() == 10 + o.id() == Id::from_u64(1) && o.price() == 1000u128 && o.visible_quantity() == 10 }); let round_trip_has_order2 = round_trip_orders.iter().any(|o| { - o.id() == OrderId::from_u64(2) && o.price() == 1100u128 && o.visible_quantity() == 20 + o.id() == Id::from_u64(2) && o.price() == 1100u128 && o.visible_quantity() == 20 }); assert!( @@ -201,7 +201,7 @@ mod tests { .. } = **order { - assert_eq!(id, OrderId::from_u64(1)); + assert_eq!(id, Id::from_u64(1)); assert_eq!(price, 10000); assert_eq!(quantity, 100); assert!(matches!(time_in_force, TimeInForce::Gtd(1617000000000))); @@ -223,7 +223,7 @@ mod tests { fn test_order_queue_serialization() { fn create_standard_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { OrderType::Standard { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -261,7 +261,7 @@ mod tests { .. } = **deserialized_order { - assert_eq!(id, OrderId::from_u64(1)); + assert_eq!(id, Id::from_u64(1)); assert_eq!(price, 10000u128); assert_eq!(quantity, 100); } else { @@ -279,7 +279,7 @@ mod tests { // Add an order and check again let order = OrderType::Standard { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 1000u128, quantity: 10, side: Side::Buy, @@ -307,7 +307,7 @@ mod tests { // Test lines 170, 178 // Create a vector of orders let order1 = Arc::new(OrderType::Standard { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 1000u128, quantity: 10, side: Side::Buy, @@ -318,7 +318,7 @@ mod tests { }); let order2 = Arc::new(OrderType::Standard { - id: OrderId::from_u64(2), + id: Id::from_u64(2), price: 1000u128, quantity: 20, side: Side::Buy, @@ -364,9 +364,9 @@ mod tests { assert_eq!(queue.to_vec().len(), 2); // Verify the parsed orders have the expected IDs - let order_ids: Vec = queue.to_vec().iter().map(|order| order.id()).collect(); - assert!(order_ids.contains(&OrderId::from_u64(1))); - assert!(order_ids.contains(&OrderId::from_u64(2))); + let order_ids: Vec = queue.to_vec().iter().map(|order| order.id()).collect(); + assert!(order_ids.contains(&Id::from_u64(1))); + assert!(order_ids.contains(&Id::from_u64(2))); // Test parsing with empty orders let empty_orders = "OrderQueue:orders=[]"; @@ -397,7 +397,7 @@ mod tests { let queue = OrderQueue::new(); let order1 = OrderType::Standard { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 1000u128, quantity: 10, side: Side::Buy, @@ -408,7 +408,7 @@ mod tests { }; let order2 = OrderType::IcebergOrder { - id: OrderId::from_u64(2), + id: Id::from_u64(2), price: 1000u128, visible_quantity: 5, hidden_quantity: 15, @@ -436,12 +436,12 @@ mod tests { assert_eq!(deserialized.to_vec().len(), 2); // Verify the order IDs - let order_ids: Vec = deserialized + let order_ids: Vec = deserialized .to_vec() .iter() .map(|order| order.id()) .collect(); - assert!(order_ids.contains(&OrderId::from_u64(1))); - assert!(order_ids.contains(&OrderId::from_u64(2))); + assert!(order_ids.contains(&Id::from_u64(1))); + assert!(order_ids.contains(&Id::from_u64(2))); } } diff --git a/src/price_level/tests/snapshot.rs b/src/price_level/tests/snapshot.rs index 16bdfd7..b3675c8 100644 --- a/src/price_level/tests/snapshot.rs +++ b/src/price_level/tests/snapshot.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { use crate::errors::PriceLevelError; - use crate::orders::{Hash32, OrderId, OrderType, Side, TimeInForce}; + use crate::orders::{Hash32, Id, OrderType, Side, TimeInForce}; use crate::price_level::snapshot::SNAPSHOT_FORMAT_VERSION; use crate::price_level::{PriceLevelSnapshot, PriceLevelSnapshotPackage}; use serde_json::Value; @@ -11,7 +11,7 @@ mod tests { fn create_sample_orders() -> Vec>> { vec![ Arc::new(OrderType::Standard { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 1000u128, quantity: 10, side: Side::Buy, @@ -21,7 +21,7 @@ mod tests { extra_fields: (), }), Arc::new(OrderType::IcebergOrder { - id: OrderId::from_u64(2), + id: Id::from_u64(2), price: 1000u128, visible_quantity: 5, hidden_quantity: 15, @@ -136,14 +136,14 @@ mod tests { // Verify first order if let OrderType::Standard { id, .. } = **collected[0] { - assert_eq!(id, OrderId::from_u64(1)); + assert_eq!(id, Id::from_u64(1)); } else { panic!("Expected StandardOrder"); } // Verify second order if let OrderType::IcebergOrder { id, .. } = **collected[1] { - assert_eq!(id, OrderId::from_u64(2)); + assert_eq!(id, Id::from_u64(2)); } else { panic!("Expected IcebergOrder"); } @@ -297,7 +297,7 @@ mod tests { fn test_snapshot_with_actual_orders() { fn create_standard_order(id: u64, price: u128, quantity: u64) -> OrderType<()> { OrderType::<()>::Standard { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, quantity, side: Side::Buy, @@ -315,7 +315,7 @@ mod tests { hidden_quantity: u64, ) -> OrderType<()> { OrderType::<()>::IcebergOrder { - id: OrderId::from_u64(id), + id: Id::from_u64(id), price, visible_quantity, hidden_quantity, @@ -362,7 +362,7 @@ mod tests { // Verify order types if let OrderType::Standard { id, quantity, .. } = &*deserialized.orders[0] { - assert_eq!(*id, OrderId::from_u64(1)); + assert_eq!(*id, Id::from_u64(1)); assert_eq!(*quantity, 100); } else { panic!("Expected Standard order"); @@ -375,7 +375,7 @@ mod tests { .. } = &*deserialized.orders[1] { - assert_eq!(*id, OrderId::from_u64(2)); + assert_eq!(*id, Id::from_u64(2)); assert_eq!(*visible_quantity, 50); assert_eq!(*hidden_quantity, 250); } else { @@ -386,7 +386,7 @@ mod tests { #[cfg(test)] mod pricelevel_snapshot_serialization_tests { - use crate::orders::{Hash32, OrderId, OrderType, Side, TimeInForce}; + use crate::orders::{Hash32, Id, OrderType, Side, TimeInForce}; use crate::price_level::PriceLevelSnapshot; use std::str::FromStr; @@ -396,7 +396,7 @@ mod pricelevel_snapshot_serialization_tests { fn create_sample_orders() -> Vec>> { vec![ Arc::new(OrderType::Standard { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 1000u128, quantity: 10, side: Side::Buy, @@ -406,7 +406,7 @@ mod pricelevel_snapshot_serialization_tests { extra_fields: (), }), Arc::new(OrderType::IcebergOrder { - id: OrderId::from_u64(2), + id: Id::from_u64(2), price: 1000u128, visible_quantity: 5, hidden_quantity: 15, @@ -417,7 +417,7 @@ mod pricelevel_snapshot_serialization_tests { extra_fields: (), }), Arc::new(OrderType::PostOnly { - id: OrderId::from_u64(3), + id: Id::from_u64(3), price: 1000u128, quantity: 8, side: Side::Buy, @@ -496,7 +496,7 @@ mod pricelevel_snapshot_serialization_tests { side, .. } => { - assert_eq!(id, OrderId::from_u64(1)); + assert_eq!(id, Id::from_u64(1)); assert_eq!(price, 1000); assert_eq!(quantity, 10); assert_eq!(side, Side::Buy); @@ -513,7 +513,7 @@ mod pricelevel_snapshot_serialization_tests { side, .. } => { - assert_eq!(id, OrderId::from_u64(2)); + assert_eq!(id, Id::from_u64(2)); assert_eq!(visible_quantity, 5); assert_eq!(hidden_quantity, 15); assert_eq!(side, Side::Sell); @@ -526,7 +526,7 @@ mod pricelevel_snapshot_serialization_tests { OrderType::<()>::PostOnly { id, quantity, side, .. } => { - assert_eq!(id, OrderId::from_u64(3)); + assert_eq!(id, Id::from_u64(3)); assert_eq!(quantity, 8); assert_eq!(side, Side::Buy); } @@ -715,7 +715,7 @@ mod pricelevel_snapshot_serialization_tests { snapshot.orders = vec![ // Standard order Arc::new(OrderType::Standard { - id: OrderId::from_u64(1), + id: Id::from_u64(1), price: 1000u128, quantity: 10, side: Side::Buy, @@ -726,7 +726,7 @@ mod pricelevel_snapshot_serialization_tests { }), // Iceberg order Arc::new(OrderType::IcebergOrder { - id: OrderId::from_u64(2), + id: Id::from_u64(2), price: 1000u128, visible_quantity: 5, hidden_quantity: 15, @@ -738,7 +738,7 @@ mod pricelevel_snapshot_serialization_tests { }), // Post-only order Arc::new(OrderType::PostOnly { - id: OrderId::from_u64(3), + id: Id::from_u64(3), price: 1000u128, quantity: 8, side: Side::Buy, @@ -749,7 +749,7 @@ mod pricelevel_snapshot_serialization_tests { }), // Fill-or-kill order (as Standard with FOK time-in-force) Arc::new(OrderType::Standard { - id: OrderId::from_u64(4), + id: Id::from_u64(4), price: 1000u128, quantity: 12, side: Side::Buy, @@ -760,7 +760,7 @@ mod pricelevel_snapshot_serialization_tests { }), // Good-till-date order (as Standard with GTD time-in-force) Arc::new(OrderType::Standard { - id: OrderId::from_u64(5), + id: Id::from_u64(5), price: 1000u128, quantity: 7, side: Side::Sell, @@ -771,7 +771,7 @@ mod pricelevel_snapshot_serialization_tests { }), // Reserve order Arc::new(OrderType::ReserveOrder { - id: OrderId::from_u64(6), + id: Id::from_u64(6), price: 1000u128, visible_quantity: 3, hidden_quantity: 12, diff --git a/src/utils/id.rs b/src/utils/id.rs new file mode 100644 index 0000000..89ff52f --- /dev/null +++ b/src/utils/id.rs @@ -0,0 +1,264 @@ +use crate::errors::PriceLevelError; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::fmt; +use std::str::FromStr; +use ulid::Ulid; +use uuid::Uuid; + +/// Represents a unique identifier in the trading system. +/// +/// This enum supports multiple ID formats to provide flexibility +/// in identifier handling across different systems. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Id { + /// UUID (Universally Unique Identifier) format. + /// A 128-bit identifier that is globally unique across space and time. + Uuid(Uuid), + + /// ULID (Universally Unique Lexicographically Sortable Identifier) format. + /// A 128-bit identifier that is lexicographically sortable and globally unique. + Ulid(Ulid), + + /// Sequential u64 identifier. + /// Useful for CEX systems where orders are assigned sequential IDs per market. + Sequential(u64), +} + +impl FromStr for Id { + type Err = PriceLevelError; + + fn from_str(s: &str) -> Result { + if let Ok(id) = s.parse::() { + return Ok(Self::Sequential(id)); + } + + if let Ok(uuid) = Uuid::from_str(s) { + Ok(Self::Uuid(uuid)) + } else if let Ok(ulid) = Ulid::from_string(s) { + Ok(Self::Ulid(ulid)) + } else { + Err(PriceLevelError::ParseError { + message: format!("Failed to parse Id as u64, UUID, or ULID: {s}"), + }) + } + } +} + +impl fmt::Display for Id { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Uuid(uuid) => write!(f, "{uuid}"), + Self::Ulid(ulid) => write!(f, "{ulid}"), + Self::Sequential(id) => write!(f, "{id}"), + } + } +} + +impl Serialize for Id { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for Id { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + Self::from_str(&s).map_err(serde::de::Error::custom) + } +} + +impl Default for Id { + fn default() -> Self { + Self::new() + } +} + +impl Id { + /// Create a new random id (defaults to ULID for better sortability). + #[must_use] + pub fn new() -> Self { + Self::Ulid(Ulid::new()) + } + + /// Create a new UUID-based id. + #[must_use] + pub fn new_uuid() -> Self { + Self::Uuid(Uuid::new_v4()) + } + + /// Create a new ULID-based id. + #[must_use] + pub fn new_ulid() -> Self { + Self::Ulid(Ulid::new()) + } + + /// Create a nil UUID id. + #[must_use] + pub fn nil() -> Self { + Self::Uuid(Uuid::nil()) + } + + /// Create an id from an existing UUID. + #[must_use] + pub fn from_uuid(uuid: Uuid) -> Self { + Self::Uuid(uuid) + } + + /// Create an id from an existing ULID. + #[must_use] + pub fn from_ulid(ulid: Ulid) -> Self { + Self::Ulid(ulid) + } + + /// Get identifier bytes. + /// + /// UUID and ULID return 16 bytes. + /// Sequential returns 8 bytes zero-padded to 16 bytes. + #[must_use] + pub fn as_bytes(&self) -> [u8; 16] { + match self { + Self::Uuid(uuid) => *uuid.as_bytes(), + Self::Ulid(ulid) => ulid.to_bytes(), + Self::Sequential(id) => { + let mut bytes = [0_u8; 16]; + bytes[8..16].copy_from_slice(&id.to_be_bytes()); + bytes + } + } + } + + /// Create a sequential id from a u64. + #[must_use] + pub fn sequential(id: u64) -> Self { + Self::Sequential(id) + } + + /// Create an id from a u64 by embedding it in a UUID. + /// + /// This exists for backward compatibility. + #[must_use] + pub fn from_u64(id: u64) -> Self { + let bytes = [ + ((id >> 56) & 0xFF) as u8, + ((id >> 48) & 0xFF) as u8, + ((id >> 40) & 0xFF) as u8, + ((id >> 32) & 0xFF) as u8, + ((id >> 24) & 0xFF) as u8, + ((id >> 16) & 0xFF) as u8, + ((id >> 8) & 0xFF) as u8, + (id & 0xFF) as u8, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]; + Self::Uuid(Uuid::from_bytes(bytes)) + } + + /// Returns the u64 value when the id is sequential. + #[must_use] + pub fn as_u64(&self) -> Option { + match self { + Self::Sequential(id) => Some(*id), + _ => None, + } + } + + /// Returns `true` if the id is sequential. + #[must_use] + pub fn is_sequential(&self) -> bool { + matches!(self, Self::Sequential(_)) + } + + /// Returns `true` if the id is UUID-based. + #[must_use] + pub fn is_uuid(&self) -> bool { + matches!(self, Self::Uuid(_)) + } + + /// Returns `true` if the id is ULID-based. + #[must_use] + pub fn is_ulid(&self) -> bool { + matches!(self, Self::Ulid(_)) + } +} + +#[cfg(test)] +mod tests { + use super::Id; + use crate::Side; + use std::str::FromStr; + use uuid::Uuid; + + #[test] + fn test_id_creation() { + let id = Id::from_u64(12345); + assert_eq!(id, Id::from_u64(12345)); + + let id1 = Id::new(); + let id2 = Id::new(); + assert_ne!(id1, id2); + + let uuid = Uuid::new_v4(); + let id = Id::from_uuid(uuid); + assert_eq!(id, Id::Uuid(uuid)); + + let nil_id = Id::nil(); + assert_eq!(nil_id, Id::Uuid(Uuid::nil())); + } + + #[test] + fn test_id_serialize_deserialize() { + let id = Id::from_u64(12345); + let serialized = serde_json::to_string(&id).unwrap(); + let expected_uuid = id.to_string(); + assert!(serialized.contains(&expected_uuid)); + + let deserialized: Id = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, id); + } + + #[test] + fn test_from_str_valid() { + let uuid_str = "550e8400-e29b-41d4-a716-446655440000"; + let id = Id::from_str(uuid_str).unwrap(); + assert_eq!(id.to_string(), uuid_str); + + let id_from_u64 = Id::from_u64(12345); + let parsed = Id::from_str(&id_from_u64.to_string()).unwrap(); + assert_eq!(id_from_u64, parsed); + } + + #[test] + fn test_from_str_invalid() { + assert!(Id::from_str("").is_err()); + assert!(Id::from_str("not-a-uuid").is_err()); + } + + #[test] + fn test_side_opposite() { + assert_eq!(Side::Buy.opposite(), Side::Sell); + assert_eq!(Side::Sell.opposite(), Side::Buy); + } + + #[test] + fn test_sequential_helpers() { + let id = Id::sequential(42); + assert!(id.is_sequential()); + assert_eq!(id.as_u64(), Some(42)); + assert_eq!(id.to_string(), "42"); + + let parsed: Id = "42".parse().unwrap(); + assert_eq!(parsed, id); + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index eb03a0a..42d4fc5 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -4,8 +4,10 @@ Date: 28/3/25 ******************************************************************************/ +mod id; mod logger; mod uuid; +pub use id::Id; pub use logger::setup_logger; pub use uuid::UuidGenerator;