nautilus_model/data/
order.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! A `BookOrder` for use with the `OrderBook` and `OrderBookDelta` data type.
17
18use std::{
19    fmt::{Debug, Display},
20    hash::{Hash, Hasher},
21};
22
23use nautilus_core::serialization::Serializable;
24use serde::{Deserialize, Serialize};
25
26use crate::{
27    enums::OrderSide,
28    orderbook::{BookIntegrityError, BookPrice},
29    types::{Price, Quantity},
30};
31
32pub type OrderId = u64;
33
34/// Represents a NULL book order (used with the `Clear` action or where an order is not specified).
35pub const NULL_ORDER: BookOrder = BookOrder {
36    side: OrderSide::NoOrderSide,
37    price: Price {
38        raw: 0,
39        precision: 0,
40    },
41    size: Quantity {
42        raw: 0,
43        precision: 0,
44    },
45    order_id: 0,
46};
47
48/// Represents an order in a book.
49#[repr(C)]
50#[derive(Clone, Copy, Eq, Serialize, Deserialize)]
51#[cfg_attr(
52    feature = "python",
53    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
54)]
55pub struct BookOrder {
56    /// The order side.
57    pub side: OrderSide,
58    /// The order price.
59    pub price: Price,
60    /// The order size.
61    pub size: Quantity,
62    /// The order ID.
63    pub order_id: OrderId,
64}
65
66impl BookOrder {
67    /// Creates a new [`BookOrder`] instance.
68    #[must_use]
69    pub fn new(side: OrderSide, price: Price, size: Quantity, order_id: OrderId) -> Self {
70        Self {
71            side,
72            price,
73            size,
74            order_id,
75        }
76    }
77
78    /// Returns a [`BookPrice`] from this order.
79    #[must_use]
80    pub fn to_book_price(&self) -> BookPrice {
81        BookPrice::new(self.price, self.side)
82    }
83
84    /// Returns the order exposure as an `f64`.
85    #[must_use]
86    pub fn exposure(&self) -> f64 {
87        self.price.as_f64() * self.size.as_f64()
88    }
89
90    /// Returns the signed order exposure as an `f64`.
91    #[must_use]
92    pub fn signed_size(&self) -> f64 {
93        match self.side {
94            OrderSide::Buy => self.size.as_f64(),
95            OrderSide::Sell => -(self.size.as_f64()),
96            _ => panic!("{}", BookIntegrityError::NoOrderSide),
97        }
98    }
99}
100
101impl Default for BookOrder {
102    /// Creates a NULL [`BookOrder`] instance.
103    fn default() -> Self {
104        NULL_ORDER
105    }
106}
107
108impl PartialEq for BookOrder {
109    fn eq(&self, other: &Self) -> bool {
110        self.order_id == other.order_id
111    }
112}
113
114impl Hash for BookOrder {
115    fn hash<H: Hasher>(&self, state: &mut H) {
116        self.order_id.hash(state);
117    }
118}
119
120impl Debug for BookOrder {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        write!(
123            f,
124            "{}(side={}, price={}, size={}, order_id={})",
125            stringify!(BookOrder),
126            self.side,
127            self.price,
128            self.size,
129            self.order_id,
130        )
131    }
132}
133
134impl Display for BookOrder {
135    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136        write!(
137            f,
138            "{},{},{},{}",
139            self.side, self.price, self.size, self.order_id,
140        )
141    }
142}
143
144impl Serializable for BookOrder {}
145
146////////////////////////////////////////////////////////////////////////////////
147// Tests
148////////////////////////////////////////////////////////////////////////////////
149#[cfg(test)]
150mod tests {
151    use rstest::rstest;
152
153    use super::*;
154
155    #[rstest]
156    fn test_new() {
157        let price = Price::from("100.00");
158        let size = Quantity::from("10");
159        let side = OrderSide::Buy;
160        let order_id = 123_456;
161
162        let order = BookOrder::new(side, price, size, order_id);
163
164        assert_eq!(order.price, price);
165        assert_eq!(order.size, size);
166        assert_eq!(order.side, side);
167        assert_eq!(order.order_id, order_id);
168    }
169
170    #[rstest]
171    fn test_to_book_price() {
172        let price = Price::from("100.00");
173        let size = Quantity::from("10");
174        let side = OrderSide::Buy;
175        let order_id = 123_456;
176
177        let order = BookOrder::new(side, price, size, order_id);
178        let book_price = order.to_book_price();
179
180        assert_eq!(book_price.value, price);
181        assert_eq!(book_price.side, side);
182    }
183
184    #[rstest]
185    fn test_exposure() {
186        let price = Price::from("100.00");
187        let size = Quantity::from("10");
188        let side = OrderSide::Buy;
189        let order_id = 123_456;
190
191        let order = BookOrder::new(side, price, size, order_id);
192        let exposure = order.exposure();
193
194        assert_eq!(exposure, price.as_f64() * size.as_f64());
195    }
196
197    #[rstest]
198    fn test_signed_size() {
199        let price = Price::from("100.00");
200        let size = Quantity::from("10");
201        let order_id = 123_456;
202
203        let order_buy = BookOrder::new(OrderSide::Buy, price, size, order_id);
204        let signed_size_buy = order_buy.signed_size();
205        assert_eq!(signed_size_buy, size.as_f64());
206
207        let order_sell = BookOrder::new(OrderSide::Sell, price, size, order_id);
208        let signed_size_sell = order_sell.signed_size();
209        assert_eq!(signed_size_sell, -(size.as_f64()));
210    }
211
212    #[rstest]
213    fn test_debug() {
214        let price = Price::from("100.00");
215        let size = Quantity::from(10);
216        let side = OrderSide::Buy;
217        let order_id = 123_456;
218        let order = BookOrder::new(side, price, size, order_id);
219        let result = format!("{order:?}");
220        let expected = "BookOrder(side=BUY, price=100.00, size=10, order_id=123456)";
221        assert_eq!(result, expected);
222    }
223
224    #[rstest]
225    fn test_display() {
226        let price = Price::from("100.00");
227        let size = Quantity::from(10);
228        let side = OrderSide::Buy;
229        let order_id = 123_456;
230        let order = BookOrder::new(side, price, size, order_id);
231        let result = format!("{order}");
232        let expected = "BUY,100.00,10,123456";
233        assert_eq!(result, expected);
234    }
235}