1use 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
34pub const NULL_ORDER: BookOrder = BookOrder {
36 side: None,
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#[derive(Clone, Copy, Eq, Serialize, Deserialize)]
50#[cfg_attr(
51 feature = "python",
52 pyo3::pyclass(module = "nautilus_trader.model", from_py_object)
53)]
54#[cfg_attr(
55 feature = "python",
56 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.model")
57)]
58pub struct BookOrder {
59 #[serde(with = "crate::enums::serde_option_order_side")]
61 pub side: Option<OrderSide>,
62 pub price: Price,
64 pub size: Quantity,
66 pub order_id: OrderId,
68}
69
70impl BookOrder {
71 #[must_use]
73 pub fn new(
74 side: impl Into<Option<OrderSide>>,
75 price: Price,
76 size: Quantity,
77 order_id: OrderId,
78 ) -> Self {
79 Self {
80 side: side.into(),
81 price,
82 size,
83 order_id,
84 }
85 }
86
87 #[must_use]
93 pub fn to_book_price(&self) -> BookPrice {
94 BookPrice::new(
95 self.price,
96 self.side.expect("BookOrder side must be Buy or Sell"),
97 )
98 }
99
100 #[must_use]
102 pub fn exposure(&self) -> f64 {
103 self.price.as_f64() * self.size.as_f64()
104 }
105
106 #[must_use]
112 pub fn signed_size(&self) -> f64 {
113 match self.side {
114 Some(OrderSide::Buy) => self.size.as_f64(),
115 Some(OrderSide::Sell) => -(self.size.as_f64()),
116 None => panic!("{}", BookIntegrityError::NoOrderSide),
117 }
118 }
119}
120
121impl Default for BookOrder {
122 fn default() -> Self {
124 NULL_ORDER
125 }
126}
127
128impl PartialEq for BookOrder {
129 fn eq(&self, other: &Self) -> bool {
130 self.order_id == other.order_id
131 }
132}
133
134impl Hash for BookOrder {
135 fn hash<H: Hasher>(&self, state: &mut H) {
136 self.order_id.hash(state);
137 }
138}
139
140impl Debug for BookOrder {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 let side = self.side.as_ref().map_or("NO_ORDER_SIDE", AsRef::as_ref);
143 write!(
144 f,
145 "{}(side={}, price={}, size={}, order_id={})",
146 stringify!(BookOrder),
147 side,
148 self.price,
149 self.size,
150 self.order_id,
151 )
152 }
153}
154
155impl Display for BookOrder {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 let side = self.side.as_ref().map_or("NO_ORDER_SIDE", AsRef::as_ref);
158 write!(f, "{},{},{},{}", side, self.price, self.size, self.order_id)
159 }
160}
161
162impl Serializable for BookOrder {}
163
164#[cfg(test)]
165mod tests {
166 use rstest::rstest;
167
168 use super::*;
169 use crate::enums::OrderSide;
170
171 #[rstest]
172 fn test_new() {
173 let price = Price::from("100.00");
174 let size = Quantity::from("10");
175 let side = OrderSide::Buy;
176 let order_id = 123_456;
177
178 let order = BookOrder::new(side, price, size, order_id);
179
180 assert_eq!(order.price, price);
181 assert_eq!(order.size, size);
182 assert_eq!(order.side, side.into());
183 assert_eq!(order.order_id, order_id);
184 }
185
186 #[rstest]
187 fn test_to_book_price() {
188 let price = Price::from("100.00");
189 let size = Quantity::from("10");
190 let side = OrderSide::Buy;
191 let order_id = 123_456;
192
193 let order = BookOrder::new(side, price, size, order_id);
194 let book_price = order.to_book_price();
195
196 assert_eq!(book_price.value, price);
197 assert_eq!(book_price.side, side);
198 }
199
200 #[rstest]
201 fn test_exposure() {
202 let price = Price::from("100.00");
203 let size = Quantity::from("10");
204 let side = OrderSide::Buy;
205 let order_id = 123_456;
206
207 let order = BookOrder::new(side, price, size, order_id);
208 let exposure = order.exposure();
209
210 assert_eq!(exposure, 100.00 * 10.0);
211 }
212
213 #[rstest]
214 fn test_signed_size() {
215 let price = Price::from("100.00");
216 let size = Quantity::from("10");
217 let order_id = 123_456;
218
219 let order_buy = BookOrder::new(OrderSide::Buy, price, size, order_id);
220 let signed_size_buy = order_buy.signed_size();
221 assert_eq!(signed_size_buy, 10.0);
222
223 let order_sell = BookOrder::new(OrderSide::Sell, price, size, order_id);
224 let signed_size_sell = order_sell.signed_size();
225 assert_eq!(signed_size_sell, -10.0);
226 }
227
228 #[rstest]
229 fn test_debug() {
230 let price = Price::from("100.00");
231 let size = Quantity::from(10);
232 let side = OrderSide::Buy;
233 let order_id = 123_456;
234 let order = BookOrder::new(side, price, size, order_id);
235 let result = format!("{order:?}");
236 let expected = "BookOrder(side=BUY, price=100.00, size=10, order_id=123456)";
237 assert_eq!(result, expected);
238 }
239
240 #[rstest]
241 fn test_display() {
242 let price = Price::from("100.00");
243 let size = Quantity::from(10);
244 let side = OrderSide::Buy;
245 let order_id = 123_456;
246 let order = BookOrder::new(side, price, size, order_id);
247 let result = format!("{order}");
248 let expected = "BUY,100.00,10,123456";
249 assert_eq!(result, expected);
250 }
251}