rusty_model/memory/
order_trade_pools.rs

1//! Specialized object pools for Order and Trade data structures
2//!
3//! This module provides high-performance, thread-local object pools
4//! specifically for order and trade recycling in HFT systems.
5
6use crate::{
7    data::market_trade::MarketTrade,
8    enums::{OrderSide, OrderType},
9    instruments::InstrumentId,
10    trading_order::Order,
11    types::ClientId,
12    venues::Venue,
13};
14use rust_decimal::Decimal;
15use rusty_common::memory::object_pool::{
16    LocalObjectPool, PooledObject, ThreadSafeObjectPool, ThreadSafePooledObject,
17};
18use smartstring::alias::String;
19
20// Thread-local pools for maximum performance
21thread_local! {
22    /// Thread-local pool for Order objects
23    static ORDER_POOL: LocalObjectPool<Order, 256> = LocalObjectPool::new(
24        || Order::new(
25            Venue::Test,
26            String::new(),
27            OrderSide::Buy,
28            OrderType::Limit,
29            Decimal::ZERO,
30            Some(Decimal::ZERO),
31            ClientId::from("default"),
32        ),
33    );
34
35    /// Thread-local pool for MarketTrade objects
36    static TRADE_POOL: LocalObjectPool<MarketTrade, 1024> = LocalObjectPool::new(
37        || MarketTrade {
38            timestamp: quanta::Instant::now(),
39            exchange_time_ns: 0,
40            price: Decimal::ZERO,
41            quantity: Decimal::ZERO,
42            direction: OrderSide::Buy,
43            instrument_id: InstrumentId::new(String::new(), Venue::Test),
44        },
45    );
46}
47
48// Global thread-safe pools for multi-threaded access
49static GLOBAL_ORDER_POOL: std::sync::LazyLock<ThreadSafeObjectPool<Order>> =
50    std::sync::LazyLock::new(|| {
51        ThreadSafeObjectPool::new(1024, || {
52            Order::new(
53                Venue::Test,
54                String::new(),
55                OrderSide::Buy,
56                OrderType::Limit,
57                Decimal::ZERO,
58                Some(Decimal::ZERO),
59                ClientId::from("default"),
60            )
61        })
62    });
63
64static GLOBAL_TRADE_POOL: std::sync::LazyLock<ThreadSafeObjectPool<MarketTrade>> =
65    std::sync::LazyLock::new(|| {
66        ThreadSafeObjectPool::new(4096, || MarketTrade {
67            timestamp: quanta::Instant::now(),
68            exchange_time_ns: 0,
69            price: Decimal::ZERO,
70            quantity: Decimal::ZERO,
71            direction: OrderSide::Buy,
72            instrument_id: InstrumentId::new(String::new(), Venue::Test),
73        })
74    });
75
76/// Get an Order from the thread-local pool
77///
78/// This is the fastest option for single-threaded components.
79/// The returned object will automatically be returned to the pool when dropped.
80#[inline]
81#[must_use]
82pub fn get_pooled_order() -> PooledObject<Order, 256> {
83    ORDER_POOL.with(|pool| pool.get())
84}
85
86/// Get a `MarketTrade` from the thread-local pool
87#[inline]
88#[must_use]
89pub fn get_pooled_trade() -> PooledObject<MarketTrade, 1024> {
90    TRADE_POOL.with(|pool| pool.get())
91}
92
93/// Get an Order from the global thread-safe pool
94///
95/// Use this for multi-threaded components that need to share pools.
96#[inline]
97pub fn get_pooled_order_global() -> ThreadSafePooledObject<Order> {
98    GLOBAL_ORDER_POOL.get()
99}
100
101/// Get a `MarketTrade` from the global thread-safe pool
102#[inline]
103pub fn get_pooled_trade_global() -> ThreadSafePooledObject<MarketTrade> {
104    GLOBAL_TRADE_POOL.get()
105}
106
107/// Builder pattern for creating orders with pooled objects
108pub struct PooledOrderBuilder<'a> {
109    order: PooledObject<Order, 256>,
110    _phantom: std::marker::PhantomData<&'a ()>,
111}
112
113impl PooledOrderBuilder<'_> {
114    /// Create a new order builder with a pooled order
115    #[inline]
116    #[must_use]
117    pub fn new() -> Self {
118        Self {
119            order: get_pooled_order(),
120            _phantom: std::marker::PhantomData,
121        }
122    }
123
124    /// Set the venue
125    #[inline]
126    #[must_use]
127    pub fn venue(mut self, venue: Venue) -> Self {
128        self.order.venue = venue;
129        self
130    }
131
132    /// Set the symbol
133    #[inline]
134    #[must_use]
135    pub fn symbol(mut self, symbol: impl Into<String>) -> Self {
136        self.order.symbol = symbol.into();
137        self
138    }
139
140    /// Set the order type
141    #[inline]
142    #[must_use]
143    pub fn order_type(mut self, order_type: OrderType) -> Self {
144        self.order.order_type = order_type;
145        self
146    }
147
148    /// Set the side
149    #[inline]
150    #[must_use]
151    pub fn side(mut self, side: OrderSide) -> Self {
152        self.order.side = side;
153        self
154    }
155
156    /// Set the quantity
157    #[inline]
158    #[must_use]
159    pub fn quantity(mut self, quantity: Decimal) -> Self {
160        self.order.quantity = quantity;
161        self
162    }
163
164    /// Set the price
165    #[inline]
166    #[must_use]
167    pub fn price(mut self, price: Decimal) -> Self {
168        self.order.price = Some(price);
169        self
170    }
171
172    /// Set the client ID
173    #[inline]
174    #[must_use]
175    pub fn client_id(mut self, client_id: ClientId) -> Self {
176        self.order.client_id = client_id;
177        self
178    }
179
180    /// Build the order (takes ownership)
181    #[inline]
182    #[must_use]
183    pub fn build(self) -> Order {
184        self.order.take()
185    }
186
187    /// Get a reference to the order being built
188    #[inline]
189    #[must_use]
190    pub fn order_ref(&self) -> &Order {
191        &self.order
192    }
193}
194
195impl Default for PooledOrderBuilder<'_> {
196    fn default() -> Self {
197        Self::new()
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn test_order_pool() {
207        // Get an order from the pool
208        let mut order = get_pooled_order();
209        order.symbol = "BTC-USDT".into();
210        order.quantity = Decimal::from(1);
211        order.price = Some(Decimal::from(50000));
212
213        assert_eq!(order.symbol.as_str(), "BTC-USDT");
214
215        drop(order);
216
217        // Get another order - should reuse the previous one
218        let order2 = get_pooled_order();
219        assert_eq!(order2.symbol.as_str(), "BTC-USDT"); // Reused!
220    }
221
222    #[test]
223    fn test_order_builder() {
224        let order = PooledOrderBuilder::new()
225            .venue(Venue::Binance)
226            .symbol("ETH-USDT")
227            .order_type(OrderType::Market)
228            .side(OrderSide::Sell)
229            .quantity(Decimal::from(10))
230            .client_id(ClientId::from("test-123"))
231            .build();
232
233        assert_eq!(order.venue, Venue::Binance);
234        assert_eq!(order.symbol.as_str(), "ETH-USDT");
235        assert_eq!(order.order_type, OrderType::Market);
236        assert_eq!(order.side, OrderSide::Sell);
237        assert_eq!(order.quantity, Decimal::from(10));
238    }
239
240    #[test]
241    fn test_trade_pool() {
242        let mut trade = get_pooled_trade();
243        trade.price = Decimal::from(42000);
244        trade.quantity = Decimal::from(5);
245        trade.direction = OrderSide::Sell;
246
247        assert_eq!(trade.price, Decimal::from(42000));
248
249        drop(trade);
250
251        // Should reuse
252        let trade2 = get_pooled_trade();
253        assert_eq!(trade2.price, Decimal::from(42000));
254    }
255
256    #[test]
257    fn test_global_pools() {
258        use std::thread;
259
260        let handles: Vec<_> = (0..4)
261            .map(|i| {
262                thread::spawn(move || {
263                    let mut order = get_pooled_order_global();
264                    order.symbol = format!("TEST-{i}").into();
265
266                    let mut trade = get_pooled_trade_global();
267                    trade.price = Decimal::from(i);
268                })
269            })
270            .collect();
271
272        for h in handles {
273            h.join().unwrap();
274        }
275    }
276}