use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::collections::{BTreeMap, HashMap, HashSet}; /// Unique identifier for an order #[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)] pub struct OrderId(pub String); /// Public key representing a trader #[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)] pub struct TraderId(pub String); /// Side of the order (buy or sell) #[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] pub enum Side { Buy, Sell, } /// Status of an order #[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)] pub enum OrderStatus { Open, PartiallyFilled, Filled, Cancelled, } /// An order in the order book #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Order { pub id: OrderId, pub trader: TraderId, pub side: Side, pub price: u128, pub original_amount: u128, pub remaining_amount: u128, pub timestamp: u64, pub status: OrderStatus, pub signature: Vec, // Signature from the trader } /// A trade execution #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Execution { pub id: String, pub buy_order_id: OrderId, pub sell_order_id: OrderId, pub price: u128, pub amount: u128, pub timestamp: u64, pub executor: TraderId, // Who executed the trade (could be either party) } /// A cancellation request #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Cancellation { pub order_id: OrderId, pub timestamp: u64, pub signature: Vec, // Must be signed by original trader } /// CRDT-based decentralized order book #[derive(Debug, Clone)] pub struct OrderBookCRDT { /// All orders ever placed orders: HashMap, /// All executions executions: HashMap, /// Cancellation tombstones cancellations: HashMap, /// Index for fast price-level lookups buy_levels: BTreeMap>, sell_levels: BTreeMap>, } impl OrderBookCRDT { pub fn new() -> Self { Self { orders: HashMap::new(), executions: HashMap::new(), cancellations: HashMap::new(), buy_levels: BTreeMap::new(), sell_levels: BTreeMap::new(), } } /// Add a new order to the book pub fn add_order(&mut self, order: Order) -> Result<(), String> { // Verify signature (simplified - real implementation would verify cryptographically) if order.signature.is_empty() { return Err("Order must be signed".to_string()); } // Check if order was previously cancelled if self.cancellations.contains_key(&order.id) { return Err("Order was previously cancelled".to_string()); } // Add to orders map let order_id = order.id.clone(); let price = order.price; let side = order.side; self.orders.insert(order_id.clone(), order); // Update price level index match side { Side::Buy => { self.buy_levels .entry(price) .or_insert_with(HashSet::new) .insert(order_id); } Side::Sell => { self.sell_levels .entry(price) .or_insert_with(HashSet::new) .insert(order_id); } } Ok(()) } /// Execute a trade between two orders pub fn execute_trade(&mut self, execution: Execution) -> Result<(), String> { // Verify both orders exist and are not cancelled let buy_order = self .orders .get(&execution.buy_order_id) .ok_or("Buy order not found")?; let sell_order = self .orders .get(&execution.sell_order_id) .ok_or("Sell order not found")?; if self.cancellations.contains_key(&execution.buy_order_id) { return Err("Buy order is cancelled".to_string()); } if self.cancellations.contains_key(&execution.sell_order_id) { return Err("Sell order is cancelled".to_string()); } // Verify price matching if buy_order.price < sell_order.price { return Err("Price mismatch - buy price less than sell price".to_string()); } // Check for duplicate execution if self.executions.contains_key(&execution.id) { return Ok(()); // Idempotent - already processed } // Add execution self.executions .insert(execution.id.clone(), execution.clone()); // Update order states self.update_order_after_execution(&execution.buy_order_id, execution.amount); self.update_order_after_execution(&execution.sell_order_id, execution.amount); Ok(()) } /// Cancel an order pub fn cancel_order(&mut self, cancellation: Cancellation) -> Result<(), String> { // Verify the order exists let order = self .orders .get(&cancellation.order_id) .ok_or("Order not found")?; // Verify signature matches order owner (simplified) if cancellation.signature.is_empty() { return Err("Cancellation must be signed".to_string()); } // Add cancellation tombstone self.cancellations .insert(cancellation.order_id.clone(), cancellation); // Remove from price level indices if let Some(order) = self.orders.get_mut(&cancellation.order_id) { order.status = OrderStatus::Cancelled; match order.side { Side::Buy => { if let Some(level) = self.buy_levels.get_mut(&order.price) { level.remove(&order.id); } } Side::Sell => { if let Some(level) = self.sell_levels.get_mut(&order.price) { level.remove(&order.id); } } } } Ok(()) } /// Merge another OrderBookCRDT into this one pub fn merge(&mut self, other: &OrderBookCRDT) { // Merge orders for (id, order) in &other.orders { if !self.orders.contains_key(id) { let _ = self.add_order(order.clone()); } } // Merge executions for (id, execution) in &other.executions { if !self.executions.contains_key(id) { let _ = self.execute_trade(execution.clone()); } } // Merge cancellations (cancellations always win) for (id, cancellation) in &other.cancellations { if !self.cancellations.contains_key(id) { let _ = self.cancel_order(cancellation.clone()); } } } /// Get the current order book view pub fn get_book_view(&self) -> OrderBookView { let mut bids = Vec::new(); let mut asks = Vec::new(); // Collect buy orders (descending price) for (price, order_ids) in self.buy_levels.iter().rev() { let mut level_amount = 0u128; for order_id in order_ids { if let Some(order) = self.orders.get(order_id) { if order.status == OrderStatus::Open || order.status == OrderStatus::PartiallyFilled { level_amount += self.get_remaining_amount(order_id); } } } if level_amount > 0 { bids.push(PriceLevel { price: *price, amount: level_amount, }); } } // Collect sell orders (ascending price) for (price, order_ids) in &self.sell_levels { let mut level_amount = 0u128; for order_id in order_ids { if let Some(order) = self.orders.get(order_id) { if order.status == OrderStatus::Open || order.status == OrderStatus::PartiallyFilled { level_amount += self.get_remaining_amount(order_id); } } } if level_amount > 0 { asks.push(PriceLevel { price: *price, amount: level_amount, }); } } OrderBookView { bids, asks } } /// Get remaining amount for an order after all executions fn get_remaining_amount(&self, order_id: &OrderId) -> u128 { let order = match self.orders.get(order_id) { Some(o) => o, None => return 0, }; let mut filled_amount = 0u128; for execution in self.executions.values() { if &execution.buy_order_id == order_id || &execution.sell_order_id == order_id { filled_amount += execution.amount; } } order.original_amount.saturating_sub(filled_amount) } /// Update order status after execution fn update_order_after_execution(&mut self, order_id: &OrderId, amount: u128) { if let Some(order) = self.orders.get_mut(order_id) { let remaining = self.get_remaining_amount(order_id); order.remaining_amount = remaining; if remaining == 0 { order.status = OrderStatus::Filled; // Remove from price level index match order.side { Side::Buy => { if let Some(level) = self.buy_levels.get_mut(&order.price) { level.remove(order_id); } } Side::Sell => { if let Some(level) = self.sell_levels.get_mut(&order.price) { level.remove(order_id); } } } } else if remaining < order.original_amount { order.status = OrderStatus::PartiallyFilled; } } } /// Find matching orders for a new order (for market making) pub fn find_matches(&self, order: &Order) -> Vec<&Order> { let mut matches = Vec::new(); match order.side { Side::Buy => { // Look for sell orders at or below buy price for (price, order_ids) in &self.sell_levels { if *price > order.price { break; // Prices too high } for order_id in order_ids { if let Some(sell_order) = self.orders.get(order_id) { if !self.cancellations.contains_key(order_id) && self.get_remaining_amount(order_id) > 0 { matches.push(sell_order); } } } } } Side::Sell => { // Look for buy orders at or above sell price for (price, order_ids) in self.buy_levels.iter().rev() { if *price < order.price { break; // Prices too low } for order_id in order_ids { if let Some(buy_order) = self.orders.get(order_id) { if !self.cancellations.contains_key(order_id) && self.get_remaining_amount(order_id) > 0 { matches.push(buy_order); } } } } } } matches } } /// A view of the order book at a point in time #[derive(Debug, Clone)] pub struct OrderBookView { pub bids: Vec, pub asks: Vec, } #[derive(Debug, Clone)] pub struct PriceLevel { pub price: u128, pub amount: u128, } #[cfg(test)] mod tests { use super::*; #[test] fn test_order_book_operations() { let mut book1 = OrderBookCRDT::new(); let mut book2 = OrderBookCRDT::new(); // Add buy order to book1 let buy_order = Order { id: OrderId("buy1".to_string()), trader: TraderId("alice".to_string()), side: Side::Buy, price: 100, original_amount: 50, remaining_amount: 50, timestamp: 1000, status: OrderStatus::Open, signature: vec![1, 2, 3], }; book1.add_order(buy_order).unwrap(); // Add sell order to book2 let sell_order = Order { id: OrderId("sell1".to_string()), trader: TraderId("bob".to_string()), side: Side::Sell, price: 95, original_amount: 30, remaining_amount: 30, timestamp: 1001, status: OrderStatus::Open, signature: vec![4, 5, 6], }; book2.add_order(sell_order).unwrap(); // Merge books book1.merge(&book2); book2.merge(&book1); // Both books should have both orders assert_eq!(book1.orders.len(), 2); assert_eq!(book2.orders.len(), 2); // Execute trade on book1 let execution = Execution { id: "exec1".to_string(), buy_order_id: OrderId("buy1".to_string()), sell_order_id: OrderId("sell1".to_string()), price: 95, amount: 30, timestamp: 1002, executor: TraderId("alice".to_string()), }; book1.execute_trade(execution).unwrap(); // Merge again book2.merge(&book1); // Both should have the execution assert_eq!(book1.executions.len(), 1); assert_eq!(book2.executions.len(), 1); // Check remaining amounts assert_eq!(book1.get_remaining_amount(&OrderId("buy1".to_string())), 20); assert_eq!(book1.get_remaining_amount(&OrderId("sell1".to_string())), 0); } #[test] fn test_order_cancellation() { let mut book = OrderBookCRDT::new(); // Add order let order = Order { id: OrderId("order1".to_string()), trader: TraderId("alice".to_string()), side: Side::Buy, price: 100, original_amount: 50, remaining_amount: 50, timestamp: 1000, status: OrderStatus::Open, signature: vec![1, 2, 3], }; book.add_order(order).unwrap(); // Cancel order let cancellation = Cancellation { order_id: OrderId("order1".to_string()), timestamp: 1001, signature: vec![1, 2, 3], }; book.cancel_order(cancellation).unwrap(); // Verify order is cancelled let order = book.orders.get(&OrderId("order1".to_string())).unwrap(); assert_eq!(order.status, OrderStatus::Cancelled); // Try to execute against cancelled order let execution = Execution { id: "exec1".to_string(), buy_order_id: OrderId("order1".to_string()), sell_order_id: OrderId("sell1".to_string()), price: 100, amount: 10, timestamp: 1002, executor: TraderId("bob".to_string()), }; assert!(book.execute_trade(execution).is_err()); } #[test] fn test_order_book_view() { let mut book = OrderBookCRDT::new(); // Add multiple orders at different price levels for i in 0..3 { let buy_order = Order { id: OrderId(format!("buy{}", i)), trader: TraderId("alice".to_string()), side: Side::Buy, price: 100 - i as u128, original_amount: 10 * (i + 1) as u128, remaining_amount: 10 * (i + 1) as u128, timestamp: 1000 + i, status: OrderStatus::Open, signature: vec![1, 2, 3], }; book.add_order(buy_order).unwrap(); let sell_order = Order { id: OrderId(format!("sell{}", i)), trader: TraderId("bob".to_string()), side: Side::Sell, price: 105 + i as u128, original_amount: 10 * (i + 1) as u128, remaining_amount: 10 * (i + 1) as u128, timestamp: 2000 + i, status: OrderStatus::Open, signature: vec![4, 5, 6], }; book.add_order(sell_order).unwrap(); } let view = book.get_book_view(); // Check bid side (should be sorted descending) assert_eq!(view.bids.len(), 3); assert_eq!(view.bids[0].price, 100); assert_eq!(view.bids[1].price, 99); assert_eq!(view.bids[2].price, 98); // Check ask side (should be sorted ascending) assert_eq!(view.asks.len(), 3); assert_eq!(view.asks[0].price, 105); assert_eq!(view.asks[1].price, 106); assert_eq!(view.asks[2].price, 107); } }