1use std::fmt::{Debug, Display};
17
18use derive_builder::Builder;
19use nautilus_core::{UnixNanos, UUID4};
20use rust_decimal::Decimal;
21use serde::{Deserialize, Serialize};
22use ustr::Ustr;
23
24use crate::{
25 enums::{
26 ContingencyType, LiquiditySide, OrderSide, OrderSideSpecified, OrderType, TimeInForce,
27 TrailingOffsetType, TriggerType,
28 },
29 events::OrderEvent,
30 identifiers::{
31 AccountId, ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, PositionId,
32 StrategyId, TradeId, TraderId, VenueOrderId,
33 },
34 types::{Currency, Money, Price, Quantity},
35};
36
37#[repr(C)]
38#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Builder)]
39#[builder(default)]
40#[serde(tag = "type")]
41#[cfg_attr(
42 feature = "python",
43 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
44)]
45pub struct OrderFilled {
46 pub trader_id: TraderId,
48 pub strategy_id: StrategyId,
50 pub instrument_id: InstrumentId,
52 pub client_order_id: ClientOrderId,
54 pub venue_order_id: VenueOrderId,
55 pub account_id: AccountId,
57 pub trade_id: TradeId,
59 pub order_side: OrderSide,
61 pub order_type: OrderType,
63 pub last_qty: Quantity,
65 pub last_px: Price,
67 pub currency: Currency,
69 pub liquidity_side: LiquiditySide,
71 pub event_id: UUID4,
73 pub ts_event: UnixNanos,
75 pub ts_init: UnixNanos,
77 pub reconciliation: bool,
79 pub position_id: Option<PositionId>,
81 pub commission: Option<Money>,
83}
84
85impl OrderFilled {
86 #[allow(clippy::too_many_arguments)]
88 pub fn new(
89 trader_id: TraderId,
90 strategy_id: StrategyId,
91 instrument_id: InstrumentId,
92 client_order_id: ClientOrderId,
93 venue_order_id: VenueOrderId,
94 account_id: AccountId,
95 trade_id: TradeId,
96 order_side: OrderSide,
97 order_type: OrderType,
98 last_qty: Quantity,
99 last_px: Price,
100 currency: Currency,
101 liquidity_side: LiquiditySide,
102 event_id: UUID4,
103 ts_event: UnixNanos,
104 ts_init: UnixNanos,
105 reconciliation: bool,
106 position_id: Option<PositionId>,
107 commission: Option<Money>,
108 ) -> Self {
109 Self {
110 trader_id,
111 strategy_id,
112 instrument_id,
113 client_order_id,
114 venue_order_id,
115 account_id,
116 trade_id,
117 order_side,
118 order_type,
119 last_qty,
120 last_px,
121 currency,
122 liquidity_side,
123 event_id,
124 ts_event,
125 ts_init,
126 reconciliation,
127 position_id,
128 commission,
129 }
130 }
131
132 #[must_use]
133 pub fn specified_side(&self) -> OrderSideSpecified {
134 self.order_side.as_specified()
135 }
136
137 #[must_use]
138 pub fn is_buy(&self) -> bool {
139 self.order_side == OrderSide::Buy
140 }
141
142 #[must_use]
143 pub fn is_sell(&self) -> bool {
144 self.order_side == OrderSide::Sell
145 }
146}
147
148impl Default for OrderFilled {
149 fn default() -> Self {
151 Self {
152 trader_id: TraderId::default(),
153 strategy_id: StrategyId::default(),
154 instrument_id: InstrumentId::default(),
155 client_order_id: ClientOrderId::default(),
156 venue_order_id: VenueOrderId::default(),
157 account_id: AccountId::default(),
158 trade_id: TradeId::default(),
159 position_id: None,
160 order_side: OrderSide::Buy,
161 order_type: OrderType::Market,
162 last_qty: Quantity::new(100_000.0, 0),
163 last_px: Price::from("1.00000"),
164 currency: Currency::USD(),
165 commission: None,
166 liquidity_side: LiquiditySide::Taker,
167 event_id: Default::default(),
168 ts_event: Default::default(),
169 ts_init: Default::default(),
170 reconciliation: Default::default(),
171 }
172 }
173}
174
175impl Debug for OrderFilled {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 let position_id_str = match self.position_id {
178 Some(position_id) => position_id.to_string(),
179 None => "None".to_string(),
180 };
181 let commission_str = match self.commission {
182 Some(commission) => commission.to_string(),
183 None => "None".to_string(),
184 };
185 write!(
186 f,
187 "{}(\
188 trader_id={}, \
189 strategy_id={}, \
190 instrument_id={}, \
191 client_order_id={}, \
192 venue_order_id={}, \
193 account_id={}, \
194 trade_id={}, \
195 position_id={}, \
196 order_side={}, \
197 order_type={}, \
198 last_qty={}, \
199 last_px={} {}, \
200 commission={}, \
201 liquidity_side={}, \
202 event_id={}, \
203 ts_event={}, \
204 ts_init={})",
205 stringify!(OrderFilled),
206 self.trader_id,
207 self.strategy_id,
208 self.instrument_id,
209 self.client_order_id,
210 self.venue_order_id,
211 self.account_id,
212 self.trade_id,
213 position_id_str,
214 self.order_side,
215 self.order_type,
216 self.last_qty.to_formatted_string(),
217 self.last_px.to_formatted_string(),
218 self.currency,
219 commission_str,
220 self.liquidity_side,
221 self.event_id,
222 self.ts_event,
223 self.ts_init
224 )
225 }
226}
227
228impl Display for OrderFilled {
229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
230 write!(
231 f,
232 "{}(\
233 instrument_id={}, \
234 client_order_id={}, \
235 venue_order_id={}, \
236 account_id={}, \
237 trade_id={}, \
238 position_id={}, \
239 order_side={}, \
240 order_type={}, \
241 last_qty={}, \
242 last_px={} {}, \
243 commission={}, \
244 liquidity_side={}, \
245 ts_event={})",
246 stringify!(OrderFilled),
247 self.instrument_id,
248 self.client_order_id,
249 self.venue_order_id,
250 self.account_id,
251 self.trade_id,
252 self.position_id.unwrap_or_default(),
253 self.order_side,
254 self.order_type,
255 self.last_qty.to_formatted_string(),
256 self.last_px.to_formatted_string(),
257 self.currency,
258 self.commission.unwrap_or(Money::from("0.0 USD")),
259 self.liquidity_side,
260 self.ts_event
261 )
262 }
263}
264
265impl OrderEvent for OrderFilled {
266 fn id(&self) -> UUID4 {
267 self.event_id
268 }
269
270 fn kind(&self) -> &str {
271 stringify!(OrderFilled)
272 }
273
274 fn order_type(&self) -> Option<OrderType> {
275 Some(self.order_type)
276 }
277
278 fn order_side(&self) -> Option<OrderSide> {
279 Some(self.order_side)
280 }
281
282 fn trader_id(&self) -> TraderId {
283 self.trader_id
284 }
285
286 fn strategy_id(&self) -> StrategyId {
287 self.strategy_id
288 }
289
290 fn instrument_id(&self) -> InstrumentId {
291 self.instrument_id
292 }
293
294 fn trade_id(&self) -> Option<TradeId> {
295 Some(self.trade_id)
296 }
297
298 fn currency(&self) -> Option<Currency> {
299 Some(self.currency)
300 }
301
302 fn client_order_id(&self) -> ClientOrderId {
303 self.client_order_id
304 }
305
306 fn reason(&self) -> Option<Ustr> {
307 None
308 }
309
310 fn quantity(&self) -> Option<Quantity> {
311 Some(self.last_qty)
312 }
313
314 fn time_in_force(&self) -> Option<TimeInForce> {
315 None
316 }
317
318 fn liquidity_side(&self) -> Option<LiquiditySide> {
319 Some(self.liquidity_side)
320 }
321
322 fn post_only(&self) -> Option<bool> {
323 None
324 }
325
326 fn reduce_only(&self) -> Option<bool> {
327 None
328 }
329
330 fn quote_quantity(&self) -> Option<bool> {
331 None
332 }
333
334 fn reconciliation(&self) -> bool {
335 false
336 }
337
338 fn price(&self) -> Option<Price> {
339 None
340 }
341
342 fn last_px(&self) -> Option<Price> {
343 Some(self.last_px)
344 }
345
346 fn last_qty(&self) -> Option<Quantity> {
347 Some(self.last_qty)
348 }
349
350 fn trigger_price(&self) -> Option<Price> {
351 None
352 }
353
354 fn trigger_type(&self) -> Option<TriggerType> {
355 None
356 }
357
358 fn limit_offset(&self) -> Option<Decimal> {
359 None
360 }
361
362 fn trailing_offset(&self) -> Option<Decimal> {
363 None
364 }
365
366 fn trailing_offset_type(&self) -> Option<TrailingOffsetType> {
367 None
368 }
369
370 fn expire_time(&self) -> Option<UnixNanos> {
371 None
372 }
373
374 fn display_qty(&self) -> Option<Quantity> {
375 None
376 }
377
378 fn emulation_trigger(&self) -> Option<TriggerType> {
379 None
380 }
381
382 fn trigger_instrument_id(&self) -> Option<InstrumentId> {
383 None
384 }
385
386 fn contingency_type(&self) -> Option<ContingencyType> {
387 None
388 }
389
390 fn order_list_id(&self) -> Option<OrderListId> {
391 None
392 }
393
394 fn linked_order_ids(&self) -> Option<Vec<ClientOrderId>> {
395 None
396 }
397
398 fn parent_order_id(&self) -> Option<ClientOrderId> {
399 None
400 }
401
402 fn exec_algorithm_id(&self) -> Option<ExecAlgorithmId> {
403 None
404 }
405
406 fn exec_spawn_id(&self) -> Option<ClientOrderId> {
407 None
408 }
409
410 fn venue_order_id(&self) -> Option<VenueOrderId> {
411 Some(self.venue_order_id)
412 }
413
414 fn account_id(&self) -> Option<AccountId> {
415 Some(self.account_id)
416 }
417
418 fn position_id(&self) -> Option<PositionId> {
419 self.position_id
420 }
421
422 fn commission(&self) -> Option<Money> {
423 self.commission
424 }
425
426 fn ts_event(&self) -> UnixNanos {
427 self.ts_event
428 }
429
430 fn ts_init(&self) -> UnixNanos {
431 self.ts_init
432 }
433}
434
435#[cfg(test)]
439mod tests {
440 use rstest::rstest;
441
442 use crate::events::{order::stubs::*, OrderFilled};
443
444 #[rstest]
445 fn test_order_filled_display(order_filled: OrderFilled) {
446 let display = format!("{order_filled}");
447 assert_eq!(
448 display,
449 "OrderFilled(instrument_id=BTCUSDT.COINBASE, client_order_id=O-19700101-000000-001-001-1, \
450 venue_order_id=123456, account_id=SIM-001, trade_id=1, position_id=P-001, \
451 order_side=BUY, order_type=LIMIT, last_qty=0.561, last_px=22_000 USDT, \
452 commission=12.20000000 USDT, liquidity_side=TAKER, ts_event=0)");
453 }
454
455 #[rstest]
456 fn test_order_filled_is_buy(order_filled: OrderFilled) {
457 assert!(order_filled.is_buy());
458 assert!(!order_filled.is_sell());
459 }
460}