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")
54)]
55pub struct BookOrder {
56 pub side: OrderSide,
58 pub price: Price,
60 pub size: Quantity,
62 pub order_id: OrderId,
64}
65
66impl BookOrder {
67 #[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 #[must_use]
80 pub fn to_book_price(&self) -> BookPrice {
81 BookPrice::new(self.price, self.side)
82 }
83
84 #[must_use]
86 pub fn exposure(&self) -> f64 {
87 self.price.as_f64() * self.size.as_f64()
88 }
89
90 #[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 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#[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}