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: 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#[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", from_py_object)
54)]
55#[cfg_attr(
56 feature = "python",
57 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.model")
58)]
59pub struct BookOrder {
60 pub side: 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(side: OrderSide, price: Price, size: Quantity, order_id: OrderId) -> Self {
74 Self {
75 side,
76 price,
77 size,
78 order_id,
79 }
80 }
81
82 #[must_use]
84 pub fn to_book_price(&self) -> BookPrice {
85 BookPrice::new(self.price, self.side.as_specified())
86 }
87
88 #[must_use]
90 pub fn exposure(&self) -> f64 {
91 self.price.as_f64() * self.size.as_f64()
92 }
93
94 #[must_use]
100 pub fn signed_size(&self) -> f64 {
101 match self.side {
102 OrderSide::Buy => self.size.as_f64(),
103 OrderSide::Sell => -(self.size.as_f64()),
104 _ => panic!("{}", BookIntegrityError::NoOrderSide),
105 }
106 }
107}
108
109impl Default for BookOrder {
110 fn default() -> Self {
112 NULL_ORDER
113 }
114}
115
116impl PartialEq for BookOrder {
117 fn eq(&self, other: &Self) -> bool {
118 self.order_id == other.order_id
119 }
120}
121
122impl Hash for BookOrder {
123 fn hash<H: Hasher>(&self, state: &mut H) {
124 self.order_id.hash(state);
125 }
126}
127
128impl Debug for BookOrder {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 write!(
131 f,
132 "{}(side={}, price={}, size={}, order_id={})",
133 stringify!(BookOrder),
134 self.side,
135 self.price,
136 self.size,
137 self.order_id,
138 )
139 }
140}
141
142impl Display for BookOrder {
143 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144 write!(
145 f,
146 "{},{},{},{}",
147 self.side, self.price, self.size, self.order_id,
148 )
149 }
150}
151
152impl Serializable for BookOrder {}
153
154#[cfg(test)]
155mod tests {
156 use rstest::rstest;
157
158 use super::*;
159
160 #[rstest]
161 fn test_new() {
162 let price = Price::from("100.00");
163 let size = Quantity::from("10");
164 let side = OrderSide::Buy;
165 let order_id = 123_456;
166
167 let order = BookOrder::new(side, price, size, order_id);
168
169 assert_eq!(order.price, price);
170 assert_eq!(order.size, size);
171 assert_eq!(order.side, side);
172 assert_eq!(order.order_id, order_id);
173 }
174
175 #[rstest]
176 fn test_to_book_price() {
177 let price = Price::from("100.00");
178 let size = Quantity::from("10");
179 let side = OrderSide::Buy;
180 let order_id = 123_456;
181
182 let order = BookOrder::new(side, price, size, order_id);
183 let book_price = order.to_book_price();
184
185 assert_eq!(book_price.value, price);
186 assert_eq!(book_price.side, side.as_specified());
187 }
188
189 #[rstest]
190 fn test_exposure() {
191 let price = Price::from("100.00");
192 let size = Quantity::from("10");
193 let side = OrderSide::Buy;
194 let order_id = 123_456;
195
196 let order = BookOrder::new(side, price, size, order_id);
197 let exposure = order.exposure();
198
199 assert_eq!(exposure, 100.00 * 10.0);
200 }
201
202 #[rstest]
203 fn test_signed_size() {
204 let price = Price::from("100.00");
205 let size = Quantity::from("10");
206 let order_id = 123_456;
207
208 let order_buy = BookOrder::new(OrderSide::Buy, price, size, order_id);
209 let signed_size_buy = order_buy.signed_size();
210 assert_eq!(signed_size_buy, 10.0);
211
212 let order_sell = BookOrder::new(OrderSide::Sell, price, size, order_id);
213 let signed_size_sell = order_sell.signed_size();
214 assert_eq!(signed_size_sell, -10.0);
215 }
216
217 #[rstest]
218 fn test_debug() {
219 let price = Price::from("100.00");
220 let size = Quantity::from(10);
221 let side = OrderSide::Buy;
222 let order_id = 123_456;
223 let order = BookOrder::new(side, price, size, order_id);
224 let result = format!("{order:?}");
225 let expected = "BookOrder(side=BUY, price=100.00, size=10, order_id=123456)";
226 assert_eq!(result, expected);
227 }
228
229 #[rstest]
230 fn test_display() {
231 let price = Price::from("100.00");
232 let size = Quantity::from(10);
233 let side = OrderSide::Buy;
234 let order_id = 123_456;
235 let order = BookOrder::new(side, price, size, order_id);
236 let result = format!("{order}");
237 let expected = "BUY,100.00,10,123456";
238 assert_eq!(result, expected);
239 }
240}