diff --git a/README.md b/README.md index 95dd3bf..b99fd0b 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,178 @@ The simulation demonstrates the library's exceptional performance capabilities: The performance characteristics demonstrate that the `pricelevel` library is suitable for production use in high-performance trading systems, matching engines, and other financial applications where microsecond-level performance is critical. +### Migration Guide (v0.6 → v0.7) + +Version 0.7.0 introduces several intentional breaking changes to improve type safety, +correctness, and API ergonomics. This section provides a complete mapping from the old +API surface to the new one. + +#### Execution Domain Rename + +The execution domain was renamed from `Transaction` to `Trade` to align with standard +financial terminology. + +| v0.6 | v0.7 | +|------|------| +| `Transaction` | [`Trade`] | +| `TransactionList` | [`TradeList`] | +| `transaction_id` field | [`Trade::trade_id()`] accessor | +| `Transaction:` parsing prefix | `Trade:` parsing prefix | + +#### Identifier Types + +Raw `Uuid` identifiers were replaced with the [`Id`] enum, which supports UUID, ULID, and +sequential (`u64`) formats. Trade IDs are generated via [`UuidGenerator`]. + +| v0.6 | v0.7 | +|------|------| +| `Uuid` (raw) | [`Id`] enum (`Uuid`, `Ulid`, `Sequential`) | +| `Uuid::new_v4()` | [`Id::new()`] or [`Id::new_uuid()`] | +| `u64` order/trade IDs | [`Id::from_u64()`] or [`Id::sequential()`] | +| `AtomicU64` trade counter | [`UuidGenerator::next()`] | + +#### Domain Newtypes + +Raw numeric primitives used in the public API were replaced with validated domain +newtypes. Each provides `new()`, `try_new()`, `Display`, `FromStr`, and serde support. + +| v0.6 | v0.7 | Inner | +|------|------|-------| +| `u128` (price) | [`Price`] | `u128` | +| `u64` (quantity) | [`Quantity`] | `u64` | +| `u64` (timestamp) | [`TimestampMs`] | `u64` | + +```rust +use pricelevel::{Price, Quantity, TimestampMs}; + +let price = Price::new(10_000); +let qty = Quantity::new(100); +let ts = TimestampMs::new(1_716_000_000_000); + +// Convert back to primitives +assert_eq!(price.as_u128(), 10_000); +assert_eq!(qty.as_u64(), 100); +assert_eq!(ts.as_u64(), 1_716_000_000_000); +``` + +#### Checked Arithmetic + +All arithmetic in financial-critical paths now uses checked operations and returns +`Result` instead of raw values. No silent saturation or wrapping +is performed. + +| Method | v0.6 Return | v0.7 Return | +|--------|-------------|-------------| +| [`PriceLevel::total_quantity()`] | `u64` | `Result` | +| [`MatchResult::executed_quantity()`] | `u64` | `Result` | +| [`MatchResult::executed_value()`] | `u128` | `Result` | +| [`MatchResult::average_price()`] | `Option` | `Result, PriceLevelError>` | +| [`MatchResult::add_trade()`] | `()` | `Result<(), PriceLevelError>` | + +```rust +use pricelevel::{PriceLevel, PriceLevelError}; + +let level = PriceLevel::new(10_000); +// total_quantity() now returns Result +let total: Result = level.total_quantity(); +assert_eq!(total.unwrap(), 0); +``` + +#### Private Fields and Accessor Methods + +All struct fields in the execution and snapshot modules are now private. Use the +provided accessor methods instead of direct field access. + +**Trade:** + +| v0.6 (field) | v0.7 (accessor) | +|--------------|-----------------| +| `trade.trade_id` | [`trade.trade_id()`](Trade::trade_id) | +| `trade.taker_order_id` | [`trade.taker_order_id()`](Trade::taker_order_id) | +| `trade.maker_order_id` | [`trade.maker_order_id()`](Trade::maker_order_id) | +| `trade.price` | [`trade.price()`](Trade::price) | +| `trade.quantity` | [`trade.quantity()`](Trade::quantity) | +| `trade.taker_side` | [`trade.taker_side()`](Trade::taker_side) | +| `trade.timestamp` | [`trade.timestamp()`](Trade::timestamp) | + +**MatchResult:** + +| v0.6 (field) | v0.7 (accessor) | +|--------------|-----------------| +| `result.order_id` | [`result.order_id()`](MatchResult::order_id) | +| `result.trades` | [`result.trades()`](MatchResult::trades) | +| `result.remaining_quantity` | [`result.remaining_quantity()`](MatchResult::remaining_quantity) | +| `result.is_complete` | [`result.is_complete()`](MatchResult::is_complete) | +| `result.filled_order_ids` | [`result.filled_order_ids()`](MatchResult::filled_order_ids) | + +**TradeList:** + +| v0.6 (field) | v0.7 (accessor) | +|--------------|-----------------| +| `list.trades` (direct `Vec`) | [`list.as_vec()`](TradeList::as_vec) / [`list.into_vec()`](TradeList::into_vec) | +| `list.trades.push(t)` | [`list.add(t)`](TradeList::add) | +| `list.trades.len()` | [`list.len()`](TradeList::len) | +| `list.trades.is_empty()` | [`list.is_empty()`](TradeList::is_empty) | + +#### Iterator API Changes + +The `iter_orders()` method now returns an iterator instead of a `Vec`, reducing +allocations on the hot path. Use `snapshot_orders()` when a materialized `Vec` is needed. + +| v0.6 | v0.7 | +|------|------| +| `level.iter_orders() -> Vec>>` | [`level.iter_orders()`](PriceLevel::iter_orders) `-> impl Iterator` | +| (no equivalent) | [`level.snapshot_orders()`](PriceLevel::snapshot_orders) `-> Vec>>` | + +#### Snapshot Persistence and Recovery + +Snapshots are now protected with SHA-256 checksums via [`PriceLevelSnapshotPackage`]. +The full persistence/recovery flow is: + +```rust +use pricelevel::PriceLevel; + +let level = PriceLevel::new(10_000); + +// Serialize to JSON (includes checksum) +let json = level.snapshot_to_json().unwrap(); + +// Restore from JSON (validates checksum) +let restored = PriceLevel::from_snapshot_json(&json).unwrap(); +``` + +#### Compiler Attributes + +- **`#[must_use]`** is now applied to all pure/computed methods (`price()`, `quantity()`, + `trade_id()`, `order_count()`, `visible_quantity()`, `is_complete()`, etc.). + Ignoring a return value from these methods will produce a compiler warning. +- **`#[repr(u8)]`** is applied to small enums exposed in the public API ([`Side`], + [`TimeInForce`]). + +#### Error Handling + +[`PriceLevelError`] gained new variants for the expanded error surface: + +| Variant | Purpose | +|---------|---------| +| `InvalidOperation { message }` | Checked arithmetic overflow, invalid state transitions | +| `SerializationError { message }` | JSON/serde serialization failures | +| `DeserializationError { message }` | JSON/serde deserialization failures | +| `ChecksumMismatch { expected, actual }` | Snapshot integrity validation failure | + +#### Quick Migration Checklist + +1. Replace `Transaction` / `TransactionList` with [`Trade`] / [`TradeList`]. +2. Replace raw `Uuid` with [`Id`]; use [`UuidGenerator`] for trade IDs. +3. Wrap raw price/quantity/timestamp literals with [`Price::new()`](Price::new), + [`Quantity::new()`](Quantity::new), [`TimestampMs::new()`](TimestampMs::new). +4. Replace direct field access on `Trade`, `MatchResult`, `TradeList` with accessors. +5. Handle `Result` returns from `total_quantity()`, `executed_quantity()`, + `executed_value()`, `average_price()`, and `add_trade()`. +6. Replace `iter_orders()` collecting into `Vec` with `snapshot_orders()` if needed. +7. Update snapshot code to use [`PriceLevelSnapshotPackage`] for checksum validation. +8. Address new `#[must_use]` warnings on query methods. + ## Setup Instructions diff --git a/src/execution/mod.rs b/src/execution/mod.rs index 3e50b68..00e720a 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -1,3 +1,27 @@ +//! Execution module: trade generation, result tracking, and trade list management. +//! +//! This module contains the types produced by the matching engine when an incoming +//! order is matched against resting orders in a [`crate::PriceLevel`]. +//! +//! # Key Types +//! +//! - [`Trade`] — a single completed trade between a taker and a maker order. +//! - [`TradeList`] — an append-only, ordered collection of trades. +//! - [`MatchResult`] — the full outcome of a matching operation, including trades, +//! remaining quantity, completion status, and filled order IDs. +//! +//! # Checked Arithmetic +//! +//! Aggregate methods on [`MatchResult`] ([`executed_quantity`](MatchResult::executed_quantity), +//! [`executed_value`](MatchResult::executed_value), +//! [`average_price`](MatchResult::average_price)) use checked arithmetic and return +//! `Result` to prevent silent overflow. +//! +//! # Serialization +//! +//! All types implement `Display`, `FromStr`, `Serialize`, and `Deserialize` for +//! text and JSON roundtrip support. + mod trade; mod list; diff --git a/src/lib.rs b/src/lib.rs index da86353..fa1705e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -134,6 +134,178 @@ //! //! The performance characteristics demonstrate that the `pricelevel` library is suitable for production use in high-performance trading systems, matching engines, and other financial applications where microsecond-level performance is critical. //! +//! ## Migration Guide (v0.6 → v0.7) +//! +//! Version 0.7.0 introduces several intentional breaking changes to improve type safety, +//! correctness, and API ergonomics. This section provides a complete mapping from the old +//! API surface to the new one. +//! +//! ### Execution Domain Rename +//! +//! The execution domain was renamed from `Transaction` to `Trade` to align with standard +//! financial terminology. +//! +//! | v0.6 | v0.7 | +//! |------|------| +//! | `Transaction` | [`Trade`] | +//! | `TransactionList` | [`TradeList`] | +//! | `transaction_id` field | [`Trade::trade_id()`] accessor | +//! | `Transaction:` parsing prefix | `Trade:` parsing prefix | +//! +//! ### Identifier Types +//! +//! Raw `Uuid` identifiers were replaced with the [`Id`] enum, which supports UUID, ULID, and +//! sequential (`u64`) formats. Trade IDs are generated via [`UuidGenerator`]. +//! +//! | v0.6 | v0.7 | +//! |------|------| +//! | `Uuid` (raw) | [`Id`] enum (`Uuid`, `Ulid`, `Sequential`) | +//! | `Uuid::new_v4()` | [`Id::new()`] or [`Id::new_uuid()`] | +//! | `u64` order/trade IDs | [`Id::from_u64()`] or [`Id::sequential()`] | +//! | `AtomicU64` trade counter | [`UuidGenerator::next()`] | +//! +//! ### Domain Newtypes +//! +//! Raw numeric primitives used in the public API were replaced with validated domain +//! newtypes. Each provides `new()`, `try_new()`, `Display`, `FromStr`, and serde support. +//! +//! | v0.6 | v0.7 | Inner | +//! |------|------|-------| +//! | `u128` (price) | [`Price`] | `u128` | +//! | `u64` (quantity) | [`Quantity`] | `u64` | +//! | `u64` (timestamp) | [`TimestampMs`] | `u64` | +//! +//! ```rust +//! use pricelevel::{Price, Quantity, TimestampMs}; +//! +//! let price = Price::new(10_000); +//! let qty = Quantity::new(100); +//! let ts = TimestampMs::new(1_716_000_000_000); +//! +//! // Convert back to primitives +//! assert_eq!(price.as_u128(), 10_000); +//! assert_eq!(qty.as_u64(), 100); +//! assert_eq!(ts.as_u64(), 1_716_000_000_000); +//! ``` +//! +//! ### Checked Arithmetic +//! +//! All arithmetic in financial-critical paths now uses checked operations and returns +//! `Result` instead of raw values. No silent saturation or wrapping +//! is performed. +//! +//! | Method | v0.6 Return | v0.7 Return | +//! |--------|-------------|-------------| +//! | [`PriceLevel::total_quantity()`] | `u64` | `Result` | +//! | [`MatchResult::executed_quantity()`] | `u64` | `Result` | +//! | [`MatchResult::executed_value()`] | `u128` | `Result` | +//! | [`MatchResult::average_price()`] | `Option` | `Result, PriceLevelError>` | +//! | [`MatchResult::add_trade()`] | `()` | `Result<(), PriceLevelError>` | +//! +//! ```rust +//! use pricelevel::{PriceLevel, PriceLevelError}; +//! +//! let level = PriceLevel::new(10_000); +//! // total_quantity() now returns Result +//! let total: Result = level.total_quantity(); +//! assert_eq!(total.unwrap(), 0); +//! ``` +//! +//! ### Private Fields and Accessor Methods +//! +//! All struct fields in the execution and snapshot modules are now private. Use the +//! provided accessor methods instead of direct field access. +//! +//! **Trade:** +//! +//! | v0.6 (field) | v0.7 (accessor) | +//! |--------------|-----------------| +//! | `trade.trade_id` | [`trade.trade_id()`](Trade::trade_id) | +//! | `trade.taker_order_id` | [`trade.taker_order_id()`](Trade::taker_order_id) | +//! | `trade.maker_order_id` | [`trade.maker_order_id()`](Trade::maker_order_id) | +//! | `trade.price` | [`trade.price()`](Trade::price) | +//! | `trade.quantity` | [`trade.quantity()`](Trade::quantity) | +//! | `trade.taker_side` | [`trade.taker_side()`](Trade::taker_side) | +//! | `trade.timestamp` | [`trade.timestamp()`](Trade::timestamp) | +//! +//! **MatchResult:** +//! +//! | v0.6 (field) | v0.7 (accessor) | +//! |--------------|-----------------| +//! | `result.order_id` | [`result.order_id()`](MatchResult::order_id) | +//! | `result.trades` | [`result.trades()`](MatchResult::trades) | +//! | `result.remaining_quantity` | [`result.remaining_quantity()`](MatchResult::remaining_quantity) | +//! | `result.is_complete` | [`result.is_complete()`](MatchResult::is_complete) | +//! | `result.filled_order_ids` | [`result.filled_order_ids()`](MatchResult::filled_order_ids) | +//! +//! **TradeList:** +//! +//! | v0.6 (field) | v0.7 (accessor) | +//! |--------------|-----------------| +//! | `list.trades` (direct `Vec`) | [`list.as_vec()`](TradeList::as_vec) / [`list.into_vec()`](TradeList::into_vec) | +//! | `list.trades.push(t)` | [`list.add(t)`](TradeList::add) | +//! | `list.trades.len()` | [`list.len()`](TradeList::len) | +//! | `list.trades.is_empty()` | [`list.is_empty()`](TradeList::is_empty) | +//! +//! ### Iterator API Changes +//! +//! The `iter_orders()` method now returns an iterator instead of a `Vec`, reducing +//! allocations on the hot path. Use `snapshot_orders()` when a materialized `Vec` is needed. +//! +//! | v0.6 | v0.7 | +//! |------|------| +//! | `level.iter_orders() -> Vec>>` | [`level.iter_orders()`](PriceLevel::iter_orders) `-> impl Iterator` | +//! | (no equivalent) | [`level.snapshot_orders()`](PriceLevel::snapshot_orders) `-> Vec>>` | +//! +//! ### Snapshot Persistence and Recovery +//! +//! Snapshots are now protected with SHA-256 checksums via [`PriceLevelSnapshotPackage`]. +//! The full persistence/recovery flow is: +//! +//! ```rust +//! use pricelevel::PriceLevel; +//! +//! let level = PriceLevel::new(10_000); +//! +//! // Serialize to JSON (includes checksum) +//! let json = level.snapshot_to_json().unwrap(); +//! +//! // Restore from JSON (validates checksum) +//! let restored = PriceLevel::from_snapshot_json(&json).unwrap(); +//! ``` +//! +//! ### Compiler Attributes +//! +//! - **`#[must_use]`** is now applied to all pure/computed methods (`price()`, `quantity()`, +//! `trade_id()`, `order_count()`, `visible_quantity()`, `is_complete()`, etc.). +//! Ignoring a return value from these methods will produce a compiler warning. +//! - **`#[repr(u8)]`** is applied to small enums exposed in the public API ([`Side`], +//! [`TimeInForce`]). +//! +//! ### Error Handling +//! +//! [`PriceLevelError`] gained new variants for the expanded error surface: +//! +//! | Variant | Purpose | +//! |---------|---------| +//! | `InvalidOperation { message }` | Checked arithmetic overflow, invalid state transitions | +//! | `SerializationError { message }` | JSON/serde serialization failures | +//! | `DeserializationError { message }` | JSON/serde deserialization failures | +//! | `ChecksumMismatch { expected, actual }` | Snapshot integrity validation failure | +//! +//! ### Quick Migration Checklist +//! +//! 1. Replace `Transaction` / `TransactionList` with [`Trade`] / [`TradeList`]. +//! 2. Replace raw `Uuid` with [`Id`]; use [`UuidGenerator`] for trade IDs. +//! 3. Wrap raw price/quantity/timestamp literals with [`Price::new()`](Price::new), +//! [`Quantity::new()`](Quantity::new), [`TimestampMs::new()`](TimestampMs::new). +//! 4. Replace direct field access on `Trade`, `MatchResult`, `TradeList` with accessors. +//! 5. Handle `Result` returns from `total_quantity()`, `executed_quantity()`, +//! `executed_value()`, `average_price()`, and `add_trade()`. +//! 6. Replace `iter_orders()` collecting into `Vec` with `snapshot_orders()` if needed. +//! 7. Update snapshot code to use [`PriceLevelSnapshotPackage`] for checksum validation. +//! 8. Address new `#[must_use]` warnings on query methods. +//! mod orders; mod price_level; diff --git a/src/orders/mod.rs b/src/orders/mod.rs index 5efb25e..621d405 100644 --- a/src/orders/mod.rs +++ b/src/orders/mod.rs @@ -1,3 +1,26 @@ +//! Order types, updates, and supporting domain primitives. +//! +//! This module defines the vocabulary for orders managed by a [`crate::PriceLevel`]. +//! +//! # Key Types +//! +//! - [`OrderType`] — enum covering all supported order variants (Standard, Iceberg, +//! Reserve, PostOnly, TrailingStop, PeggedOrder, MarketToLimit). +//! - [`OrderUpdate`] — enum for order mutations (update price, quantity, cancel, replace). +//! - [`Id`] — flexible identifier supporting UUID, ULID, and sequential (`u64`) formats. +//! - [`Side`] — `Buy` or `Sell`, with `#[repr(u8)]` for compact representation. +//! - [`TimeInForce`] — order duration policies (GTC, IOC, FOK, GTD, Day), +//! with `#[repr(u8)]`. +//! - [`Hash32`] — opaque 32-byte user identifier. +//! - [`PegReferenceType`] — reference price type for pegged orders. +//! +//! # Order Lifecycle +//! +//! Orders are constructed as [`OrderType`] variants and added to a +//! [`PriceLevel`](crate::PriceLevel) via [`add_order()`](crate::PriceLevel::add_order). +//! Mutations are applied via [`update_order()`](crate::PriceLevel::update_order) +//! using [`OrderUpdate`] variants. + mod base; mod order_type; diff --git a/src/price_level/mod.rs b/src/price_level/mod.rs index 5c2bd09..b2b751a 100644 --- a/src/price_level/mod.rs +++ b/src/price_level/mod.rs @@ -4,6 +4,36 @@ Date: 28/3/25 ******************************************************************************/ +//! Core price level module: lock-free order queue, matching, snapshots, and statistics. +//! +//! This module provides the central [`PriceLevel`] type that represents a single price +//! point in a limit order book. It manages a lock-free queue of orders, performs matching, +//! tracks statistics, and supports snapshot persistence with checksum protection. +//! +//! # Key Types +//! +//! - [`PriceLevel`] — the main lock-free price level implementation supporting concurrent +//! add, match, update, and cancel operations via atomic counters and crossbeam queues. +//! - [`PriceLevelData`] — a serializable representation for data transfer and storage. +//! - [`PriceLevelSnapshot`] — a point-in-time snapshot of all orders at a price level. +//! - [`PriceLevelSnapshotPackage`] — a checksum-protected wrapper around a snapshot for +//! safe persistence and recovery via JSON. +//! - [`PriceLevelStatistics`] — real-time execution statistics (orders added/removed/executed, +//! quantity/value executed, average price, waiting times). +//! - [`OrderQueue`] — the underlying lock-free order queue based on crossbeam. +//! +//! # Snapshot Persistence +//! +//! Snapshots can be serialized to JSON with SHA-256 checksum protection: +//! +//! ```rust +//! use pricelevel::PriceLevel; +//! +//! let level = PriceLevel::new(10_000); +//! let json = level.snapshot_to_json().unwrap(); +//! let restored = PriceLevel::from_snapshot_json(&json).unwrap(); +//! ``` + mod level; mod snapshot;