Skip to main content

nautilus_model/events/order/
stubs.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 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
16use std::str::FromStr;
17
18use nautilus_core::{UUID4, UnixNanos};
19use rstest::fixture;
20use ustr::Ustr;
21
22use crate::{
23    enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TriggerType},
24    events::{
25        OrderAccepted, OrderCancelRejected, OrderCanceled, OrderDenied, OrderEmulated,
26        OrderExpired, OrderFilled, OrderInitialized, OrderModifyRejected, OrderPendingCancel,
27        OrderPendingUpdate, OrderRejected, OrderReleased, OrderSubmitted, OrderTriggered,
28        OrderUpdated, order::spec::OrderFilledSpec,
29    },
30    identifiers::{
31        AccountId, ClientOrderId, InstrumentId, OrderListId, StrategyId, TradeId, TraderId,
32        VenueOrderId, stubs as id_stubs,
33    },
34    stubs::TestDefault,
35    types::{Currency, Money, Price, Quantity},
36};
37
38// Fixture wrappers for rstest - these are not glob-exported to avoid ambiguity
39#[fixture]
40pub fn trader_id() -> TraderId {
41    id_stubs::trader_id()
42}
43
44#[fixture]
45pub fn strategy_id_ema_cross() -> StrategyId {
46    id_stubs::strategy_id_ema_cross()
47}
48
49#[fixture]
50pub fn instrument_id_btc_usdt() -> InstrumentId {
51    id_stubs::instrument_id_btc_usdt()
52}
53
54#[fixture]
55pub fn client_order_id() -> ClientOrderId {
56    id_stubs::client_order_id()
57}
58
59#[fixture]
60pub fn venue_order_id() -> VenueOrderId {
61    id_stubs::venue_order_id()
62}
63
64#[fixture]
65pub fn account_id() -> AccountId {
66    id_stubs::account_id()
67}
68
69#[fixture]
70pub fn uuid4() -> UUID4 {
71    id_stubs::uuid4()
72}
73
74#[fixture]
75pub fn order_filled(
76    trader_id: TraderId,
77    strategy_id_ema_cross: StrategyId,
78    instrument_id_btc_usdt: InstrumentId,
79    client_order_id: ClientOrderId,
80    uuid4: UUID4,
81) -> OrderFilled {
82    OrderFilledSpec::builder()
83        .trader_id(trader_id)
84        .strategy_id(strategy_id_ema_cross)
85        .instrument_id(instrument_id_btc_usdt)
86        .client_order_id(client_order_id)
87        .venue_order_id(VenueOrderId::new("123456"))
88        .account_id(AccountId::new("SIM-001"))
89        .trade_id(TradeId::new("1"))
90        .order_type(OrderType::Limit)
91        .last_qty(Quantity::from_str("0.561").unwrap())
92        .last_px(Price::from_str("22000").unwrap())
93        .currency(Currency::from_str("USDT").unwrap())
94        .event_id(uuid4)
95        .commission(Money::from("12.2 USDT"))
96        .build()
97}
98
99#[fixture]
100pub fn order_denied_max_submitted_rate(
101    trader_id: TraderId,
102    strategy_id_ema_cross: StrategyId,
103    instrument_id_btc_usdt: InstrumentId,
104    client_order_id: ClientOrderId,
105    uuid4: UUID4,
106) -> OrderDenied {
107    OrderDenied::new(
108        trader_id,
109        strategy_id_ema_cross,
110        instrument_id_btc_usdt,
111        client_order_id,
112        Ustr::from("Exceeded MAX_ORDER_SUBMIT_RATE"),
113        uuid4,
114        UnixNanos::default(),
115        UnixNanos::default(),
116    )
117}
118
119#[fixture]
120pub fn order_rejected_insufficient_margin(
121    trader_id: TraderId,
122    account_id: AccountId,
123    strategy_id_ema_cross: StrategyId,
124    instrument_id_btc_usdt: InstrumentId,
125    client_order_id: ClientOrderId,
126    uuid4: UUID4,
127) -> OrderRejected {
128    OrderRejected::new(
129        trader_id,
130        strategy_id_ema_cross,
131        instrument_id_btc_usdt,
132        client_order_id,
133        account_id,
134        Ustr::from("INSUFFICIENT_MARGIN"),
135        uuid4,
136        UnixNanos::default(),
137        UnixNanos::default(),
138        false,
139        false,
140    )
141}
142
143#[fixture]
144pub fn order_initialized_buy_limit(
145    trader_id: TraderId,
146    strategy_id_ema_cross: StrategyId,
147    instrument_id_btc_usdt: InstrumentId,
148    client_order_id: ClientOrderId,
149    uuid4: UUID4,
150) -> OrderInitialized {
151    let order_list_id = OrderListId::new("1");
152    let linked_order_ids = vec![ClientOrderId::new("O-2020872378424")];
153    OrderInitialized::new(
154        trader_id,
155        strategy_id_ema_cross,
156        instrument_id_btc_usdt,
157        client_order_id,
158        OrderSide::Buy,
159        OrderType::Limit,
160        Quantity::from_str("0.561").unwrap(),
161        TimeInForce::Day,
162        true,
163        true,
164        false,
165        false,
166        uuid4,
167        UnixNanos::default(),
168        UnixNanos::default(),
169        Some(Price::from_str("22000").unwrap()),
170        None,
171        None,
172        None,
173        None,
174        None,
175        None,
176        None,
177        Some(TriggerType::BidAsk),
178        Some(instrument_id_btc_usdt),
179        Some(ContingencyType::Oto),
180        Some(order_list_id),
181        Some(linked_order_ids),
182        None,
183        None,
184        None,
185        None,
186        None,
187    )
188}
189
190#[fixture]
191pub fn order_submitted(
192    trader_id: TraderId,
193    strategy_id_ema_cross: StrategyId,
194    instrument_id_btc_usdt: InstrumentId,
195    client_order_id: ClientOrderId,
196    account_id: AccountId,
197    uuid4: UUID4,
198) -> OrderSubmitted {
199    OrderSubmitted::new(
200        trader_id,
201        strategy_id_ema_cross,
202        instrument_id_btc_usdt,
203        client_order_id,
204        account_id,
205        uuid4,
206        UnixNanos::default(),
207        UnixNanos::default(),
208    )
209}
210
211#[fixture]
212pub fn order_triggered(
213    trader_id: TraderId,
214    strategy_id_ema_cross: StrategyId,
215    instrument_id_btc_usdt: InstrumentId,
216    client_order_id: ClientOrderId,
217    venue_order_id: VenueOrderId,
218    account_id: AccountId,
219    uuid4: UUID4,
220) -> OrderTriggered {
221    OrderTriggered::new(
222        trader_id,
223        strategy_id_ema_cross,
224        instrument_id_btc_usdt,
225        client_order_id,
226        uuid4,
227        UnixNanos::default(),
228        UnixNanos::default(),
229        false,
230        Some(venue_order_id),
231        Some(account_id),
232    )
233}
234
235#[fixture]
236pub fn order_emulated(
237    trader_id: TraderId,
238    strategy_id_ema_cross: StrategyId,
239    instrument_id_btc_usdt: InstrumentId,
240    client_order_id: ClientOrderId,
241    uuid4: UUID4,
242) -> OrderEmulated {
243    OrderEmulated::new(
244        trader_id,
245        strategy_id_ema_cross,
246        instrument_id_btc_usdt,
247        client_order_id,
248        uuid4,
249        UnixNanos::default(),
250        UnixNanos::default(),
251    )
252}
253
254#[fixture]
255pub fn order_released(
256    trader_id: TraderId,
257    strategy_id_ema_cross: StrategyId,
258    instrument_id_btc_usdt: InstrumentId,
259    client_order_id: ClientOrderId,
260    uuid4: UUID4,
261) -> OrderReleased {
262    OrderReleased::new(
263        trader_id,
264        strategy_id_ema_cross,
265        instrument_id_btc_usdt,
266        client_order_id,
267        Price::from_str("22000").unwrap(),
268        uuid4,
269        UnixNanos::default(),
270        UnixNanos::default(),
271    )
272}
273
274#[fixture]
275pub fn order_updated(
276    trader_id: TraderId,
277    strategy_id_ema_cross: StrategyId,
278    instrument_id_btc_usdt: InstrumentId,
279    client_order_id: ClientOrderId,
280    venue_order_id: VenueOrderId,
281    account_id: AccountId,
282    uuid4: UUID4,
283) -> OrderUpdated {
284    OrderUpdated::new(
285        trader_id,
286        strategy_id_ema_cross,
287        instrument_id_btc_usdt,
288        client_order_id,
289        Quantity::from(100),
290        uuid4,
291        UnixNanos::default(),
292        UnixNanos::default(),
293        false,
294        Some(venue_order_id),
295        Some(account_id),
296        Some(Price::from("22000")),
297        None,
298        None,
299        false, // is_quote_quantity
300    )
301}
302
303#[fixture]
304pub fn order_pending_update(
305    trader_id: TraderId,
306    strategy_id_ema_cross: StrategyId,
307    instrument_id_btc_usdt: InstrumentId,
308    client_order_id: ClientOrderId,
309    account_id: AccountId,
310    venue_order_id: VenueOrderId,
311    uuid4: UUID4,
312) -> OrderPendingUpdate {
313    OrderPendingUpdate::new(
314        trader_id,
315        strategy_id_ema_cross,
316        instrument_id_btc_usdt,
317        client_order_id,
318        account_id,
319        uuid4,
320        UnixNanos::default(),
321        UnixNanos::default(),
322        false,
323        Some(venue_order_id),
324    )
325}
326
327#[fixture]
328pub fn order_pending_cancel(
329    trader_id: TraderId,
330    strategy_id_ema_cross: StrategyId,
331    instrument_id_btc_usdt: InstrumentId,
332    client_order_id: ClientOrderId,
333    account_id: AccountId,
334    venue_order_id: VenueOrderId,
335    uuid4: UUID4,
336) -> OrderPendingCancel {
337    OrderPendingCancel::new(
338        trader_id,
339        strategy_id_ema_cross,
340        instrument_id_btc_usdt,
341        client_order_id,
342        account_id,
343        uuid4,
344        UnixNanos::default(),
345        UnixNanos::default(),
346        false,
347        Some(venue_order_id),
348    )
349}
350
351#[fixture]
352pub fn order_modify_rejected(
353    trader_id: TraderId,
354    strategy_id_ema_cross: StrategyId,
355    instrument_id_btc_usdt: InstrumentId,
356    client_order_id: ClientOrderId,
357    venue_order_id: VenueOrderId,
358    account_id: AccountId,
359    uuid4: UUID4,
360) -> OrderModifyRejected {
361    OrderModifyRejected::new(
362        trader_id,
363        strategy_id_ema_cross,
364        instrument_id_btc_usdt,
365        client_order_id,
366        Ustr::from("ORDER_DOES_NOT_EXIST"),
367        uuid4,
368        UnixNanos::default(),
369        UnixNanos::default(),
370        false,
371        Some(venue_order_id),
372        Some(account_id),
373    )
374}
375
376#[fixture]
377pub fn order_accepted(
378    trader_id: TraderId,
379    strategy_id_ema_cross: StrategyId,
380    instrument_id_btc_usdt: InstrumentId,
381    client_order_id: ClientOrderId,
382    account_id: AccountId,
383    venue_order_id: VenueOrderId,
384    uuid4: UUID4,
385) -> OrderAccepted {
386    OrderAccepted::new(
387        trader_id,
388        strategy_id_ema_cross,
389        instrument_id_btc_usdt,
390        client_order_id,
391        venue_order_id,
392        account_id,
393        uuid4,
394        UnixNanos::default(),
395        UnixNanos::default(),
396        false,
397    )
398}
399
400#[fixture]
401pub fn order_cancel_rejected(
402    trader_id: TraderId,
403    strategy_id_ema_cross: StrategyId,
404    instrument_id_btc_usdt: InstrumentId,
405    client_order_id: ClientOrderId,
406    venue_order_id: VenueOrderId,
407    account_id: AccountId,
408    uuid4: UUID4,
409) -> OrderCancelRejected {
410    OrderCancelRejected::new(
411        trader_id,
412        strategy_id_ema_cross,
413        instrument_id_btc_usdt,
414        client_order_id,
415        Ustr::from("ORDER_DOES_NOT_EXIST"),
416        uuid4,
417        UnixNanos::default(),
418        UnixNanos::default(),
419        false,
420        Some(venue_order_id),
421        Some(account_id),
422    )
423}
424
425#[fixture]
426pub fn order_expired(
427    trader_id: TraderId,
428    strategy_id_ema_cross: StrategyId,
429    instrument_id_btc_usdt: InstrumentId,
430    client_order_id: ClientOrderId,
431    venue_order_id: VenueOrderId,
432    account_id: AccountId,
433    uuid4: UUID4,
434) -> OrderExpired {
435    OrderExpired::new(
436        trader_id,
437        strategy_id_ema_cross,
438        instrument_id_btc_usdt,
439        client_order_id,
440        uuid4,
441        UnixNanos::default(),
442        UnixNanos::default(),
443        false,
444        Some(venue_order_id),
445        Some(account_id),
446    )
447}
448
449// TestDefault implementations for order events
450// These provide test-only default values for use in tests and stubs.
451
452impl TestDefault for OrderAccepted {
453    fn test_default() -> Self {
454        Self {
455            trader_id: TraderId::test_default(),
456            strategy_id: StrategyId::test_default(),
457            instrument_id: InstrumentId::test_default(),
458            client_order_id: ClientOrderId::test_default(),
459            venue_order_id: VenueOrderId::test_default(),
460            account_id: AccountId::test_default(),
461            event_id: UUID4::default(),
462            ts_event: UnixNanos::default(),
463            ts_init: UnixNanos::default(),
464            reconciliation: 0,
465        }
466    }
467}
468
469impl Default for OrderAccepted {
470    fn default() -> Self {
471        Self::test_default()
472    }
473}
474
475impl TestDefault for OrderCanceled {
476    fn test_default() -> Self {
477        Self {
478            trader_id: TraderId::test_default(),
479            strategy_id: StrategyId::test_default(),
480            instrument_id: InstrumentId::test_default(),
481            client_order_id: ClientOrderId::test_default(),
482            event_id: UUID4::default(),
483            ts_event: UnixNanos::default(),
484            ts_init: UnixNanos::default(),
485            reconciliation: 0,
486            venue_order_id: None,
487            account_id: None,
488        }
489    }
490}
491
492impl Default for OrderCanceled {
493    fn default() -> Self {
494        Self::test_default()
495    }
496}
497
498impl TestDefault for OrderCancelRejected {
499    fn test_default() -> Self {
500        Self {
501            trader_id: TraderId::test_default(),
502            strategy_id: StrategyId::test_default(),
503            instrument_id: InstrumentId::test_default(),
504            client_order_id: ClientOrderId::test_default(),
505            reason: Ustr::from("TEST"),
506            event_id: UUID4::default(),
507            ts_event: UnixNanos::default(),
508            ts_init: UnixNanos::default(),
509            reconciliation: 0,
510            venue_order_id: None,
511            account_id: None,
512        }
513    }
514}
515
516impl Default for OrderCancelRejected {
517    fn default() -> Self {
518        Self::test_default()
519    }
520}
521
522impl TestDefault for OrderDenied {
523    fn test_default() -> Self {
524        Self {
525            trader_id: TraderId::test_default(),
526            strategy_id: StrategyId::test_default(),
527            instrument_id: InstrumentId::test_default(),
528            client_order_id: ClientOrderId::test_default(),
529            reason: Ustr::from("TEST"),
530            event_id: UUID4::default(),
531            ts_event: UnixNanos::default(),
532            ts_init: UnixNanos::default(),
533        }
534    }
535}
536
537impl Default for OrderDenied {
538    fn default() -> Self {
539        Self::test_default()
540    }
541}
542
543impl TestDefault for OrderEmulated {
544    fn test_default() -> Self {
545        Self {
546            trader_id: TraderId::test_default(),
547            strategy_id: StrategyId::test_default(),
548            instrument_id: InstrumentId::test_default(),
549            client_order_id: ClientOrderId::test_default(),
550            event_id: UUID4::default(),
551            ts_event: UnixNanos::default(),
552            ts_init: UnixNanos::default(),
553        }
554    }
555}
556
557impl Default for OrderEmulated {
558    fn default() -> Self {
559        Self::test_default()
560    }
561}
562
563impl TestDefault for OrderExpired {
564    fn test_default() -> Self {
565        Self {
566            trader_id: TraderId::test_default(),
567            strategy_id: StrategyId::test_default(),
568            instrument_id: InstrumentId::test_default(),
569            client_order_id: ClientOrderId::test_default(),
570            event_id: UUID4::default(),
571            ts_event: UnixNanos::default(),
572            ts_init: UnixNanos::default(),
573            reconciliation: 0,
574            venue_order_id: None,
575            account_id: None,
576        }
577    }
578}
579
580impl Default for OrderExpired {
581    fn default() -> Self {
582        Self::test_default()
583    }
584}
585
586impl TestDefault for OrderInitialized {
587    fn test_default() -> Self {
588        Self {
589            trader_id: TraderId::test_default(),
590            strategy_id: StrategyId::test_default(),
591            instrument_id: InstrumentId::test_default(),
592            client_order_id: ClientOrderId::test_default(),
593            order_side: OrderSide::Buy,
594            order_type: OrderType::Market,
595            quantity: Quantity::new(100_000.0, 0),
596            price: Option::default(),
597            trigger_price: Option::default(),
598            trigger_type: Option::default(),
599            time_in_force: TimeInForce::Day,
600            expire_time: Option::default(),
601            post_only: bool::default(),
602            reduce_only: bool::default(),
603            display_qty: Option::default(),
604            quote_quantity: bool::default(),
605            limit_offset: Option::default(),
606            trailing_offset: Option::default(),
607            trailing_offset_type: Option::default(),
608            emulation_trigger: Option::default(),
609            trigger_instrument_id: Option::default(),
610            contingency_type: Option::default(),
611            order_list_id: Option::default(),
612            linked_order_ids: Option::default(),
613            parent_order_id: Option::default(),
614            exec_algorithm_id: Option::default(),
615            exec_algorithm_params: Option::default(),
616            exec_spawn_id: Option::default(),
617            tags: Option::default(),
618            reconciliation: false,
619            event_id: UUID4::default(),
620            ts_event: UnixNanos::default(),
621            ts_init: UnixNanos::default(),
622        }
623    }
624}
625
626impl Default for OrderInitialized {
627    fn default() -> Self {
628        Self::test_default()
629    }
630}
631
632impl TestDefault for OrderModifyRejected {
633    fn test_default() -> Self {
634        Self {
635            trader_id: TraderId::test_default(),
636            strategy_id: StrategyId::test_default(),
637            instrument_id: InstrumentId::test_default(),
638            client_order_id: ClientOrderId::test_default(),
639            reason: Ustr::from("TEST"),
640            event_id: UUID4::default(),
641            ts_event: UnixNanos::default(),
642            ts_init: UnixNanos::default(),
643            reconciliation: 0,
644            venue_order_id: None,
645            account_id: None,
646        }
647    }
648}
649
650impl Default for OrderModifyRejected {
651    fn default() -> Self {
652        Self::test_default()
653    }
654}
655
656impl TestDefault for OrderPendingCancel {
657    fn test_default() -> Self {
658        Self {
659            trader_id: TraderId::test_default(),
660            strategy_id: StrategyId::test_default(),
661            instrument_id: InstrumentId::test_default(),
662            client_order_id: ClientOrderId::test_default(),
663            event_id: UUID4::default(),
664            ts_event: UnixNanos::default(),
665            ts_init: UnixNanos::default(),
666            reconciliation: 0,
667            venue_order_id: None,
668            account_id: AccountId::test_default(),
669        }
670    }
671}
672
673impl Default for OrderPendingCancel {
674    fn default() -> Self {
675        Self::test_default()
676    }
677}
678
679impl TestDefault for OrderPendingUpdate {
680    fn test_default() -> Self {
681        Self {
682            trader_id: TraderId::test_default(),
683            strategy_id: StrategyId::test_default(),
684            instrument_id: InstrumentId::test_default(),
685            client_order_id: ClientOrderId::test_default(),
686            event_id: UUID4::default(),
687            ts_event: UnixNanos::default(),
688            ts_init: UnixNanos::default(),
689            reconciliation: 0,
690            venue_order_id: None,
691            account_id: AccountId::test_default(),
692        }
693    }
694}
695
696impl Default for OrderPendingUpdate {
697    fn default() -> Self {
698        Self::test_default()
699    }
700}
701
702impl TestDefault for OrderRejected {
703    fn test_default() -> Self {
704        Self {
705            trader_id: TraderId::test_default(),
706            strategy_id: StrategyId::test_default(),
707            instrument_id: InstrumentId::test_default(),
708            client_order_id: ClientOrderId::test_default(),
709            account_id: AccountId::test_default(),
710            reason: Ustr::from("TEST"),
711            event_id: UUID4::default(),
712            ts_event: UnixNanos::default(),
713            ts_init: UnixNanos::default(),
714            reconciliation: 0,
715            due_post_only: 0,
716        }
717    }
718}
719
720impl Default for OrderRejected {
721    fn default() -> Self {
722        Self::test_default()
723    }
724}
725
726impl TestDefault for OrderReleased {
727    fn test_default() -> Self {
728        Self {
729            trader_id: TraderId::test_default(),
730            strategy_id: StrategyId::test_default(),
731            instrument_id: InstrumentId::test_default(),
732            client_order_id: ClientOrderId::test_default(),
733            released_price: Price::from("1.00000"),
734            event_id: UUID4::default(),
735            ts_event: UnixNanos::default(),
736            ts_init: UnixNanos::default(),
737        }
738    }
739}
740
741impl Default for OrderReleased {
742    fn default() -> Self {
743        Self::test_default()
744    }
745}
746
747impl TestDefault for OrderSubmitted {
748    fn test_default() -> Self {
749        Self {
750            trader_id: TraderId::test_default(),
751            strategy_id: StrategyId::test_default(),
752            instrument_id: InstrumentId::test_default(),
753            client_order_id: ClientOrderId::test_default(),
754            account_id: AccountId::test_default(),
755            event_id: UUID4::default(),
756            ts_event: UnixNanos::default(),
757            ts_init: UnixNanos::default(),
758        }
759    }
760}
761
762impl Default for OrderSubmitted {
763    fn default() -> Self {
764        Self::test_default()
765    }
766}
767
768impl TestDefault for OrderTriggered {
769    fn test_default() -> Self {
770        Self {
771            trader_id: TraderId::test_default(),
772            strategy_id: StrategyId::test_default(),
773            instrument_id: InstrumentId::test_default(),
774            client_order_id: ClientOrderId::test_default(),
775            event_id: UUID4::default(),
776            ts_event: UnixNanos::default(),
777            ts_init: UnixNanos::default(),
778            reconciliation: 0,
779            venue_order_id: None,
780            account_id: None,
781        }
782    }
783}
784
785impl Default for OrderTriggered {
786    fn default() -> Self {
787        Self::test_default()
788    }
789}
790
791impl TestDefault for OrderUpdated {
792    fn test_default() -> Self {
793        Self {
794            trader_id: TraderId::test_default(),
795            strategy_id: StrategyId::test_default(),
796            instrument_id: InstrumentId::test_default(),
797            client_order_id: ClientOrderId::test_default(),
798            venue_order_id: None,
799            account_id: None,
800            quantity: Quantity::new(100_000.0, 0),
801            price: None,
802            trigger_price: None,
803            protection_price: None,
804            is_quote_quantity: false,
805            event_id: UUID4::default(),
806            ts_event: UnixNanos::default(),
807            ts_init: UnixNanos::default(),
808            reconciliation: 0,
809        }
810    }
811}
812
813impl Default for OrderUpdated {
814    fn default() -> Self {
815        Self::test_default()
816    }
817}