nautilus_live/execution/
reports.rs1use nautilus_common::messages::execution::GenerateOrderStatusReports;
22use nautilus_model::reports::OrderStatusReport;
23
24pub fn retain_order_status_reports(
29 reports: &mut Vec<OrderStatusReport>,
30 command: &GenerateOrderStatusReports,
31) {
32 reports.retain(|report| {
33 let status = report.order_status;
34 let matches_open = !command.open_only || status.is_open() || status.is_inflight();
35 let matches_time = !status.is_closed()
36 || (command.start.is_none_or(|start| report.ts_last >= start)
37 && command.end.is_none_or(|end| report.ts_last <= end));
38
39 matches_open && matches_time
40 });
41}
42
43#[cfg(test)]
44mod tests {
45 use nautilus_common::messages::execution::GenerateOrderStatusReports;
46 use nautilus_core::{UUID4, UnixNanos};
47 use nautilus_model::{
48 enums::{OrderSide, OrderStatus, OrderType, TimeInForce},
49 identifiers::{AccountId, InstrumentId, VenueOrderId},
50 reports::OrderStatusReport,
51 types::Quantity,
52 };
53 use rstest::rstest;
54
55 use super::retain_order_status_reports;
56
57 #[rstest]
58 #[case::accepted(OrderStatus::Accepted, true)]
59 #[case::submitted(OrderStatus::Submitted, true)]
60 #[case::initialized(OrderStatus::Initialized, false)]
61 #[case::emulated(OrderStatus::Emulated, false)]
62 #[case::released(OrderStatus::Released, false)]
63 #[case::filled(OrderStatus::Filled, false)]
64 fn test_retain_order_status_reports_filters_open_and_inflight(
65 #[case] status: OrderStatus,
66 #[case] expected_retained: bool,
67 ) {
68 let command = order_status_reports_command(true, None, None);
69 let mut reports = vec![order_status_report(status, UnixNanos::from(5))];
70
71 retain_order_status_reports(&mut reports, &command);
72
73 assert_eq!(reports.len(), usize::from(expected_retained));
74 }
75
76 #[rstest]
77 #[case::local_before(OrderStatus::Initialized, 9, true)]
78 #[case::open_before(OrderStatus::Accepted, 9, true)]
79 #[case::inflight_after(OrderStatus::Submitted, 21, true)]
80 #[case::closed_before(OrderStatus::Filled, 9, false)]
81 #[case::closed_at_start(OrderStatus::Filled, 10, true)]
82 #[case::closed_at_end(OrderStatus::Filled, 20, true)]
83 #[case::closed_after(OrderStatus::Filled, 21, false)]
84 fn test_retain_order_status_reports_filters_only_closed_reports_by_time(
85 #[case] status: OrderStatus,
86 #[case] ts_last: u64,
87 #[case] expected_retained: bool,
88 ) {
89 let command = order_status_reports_command(
90 false,
91 Some(UnixNanos::from(10)),
92 Some(UnixNanos::from(20)),
93 );
94 let mut reports = vec![order_status_report(status, UnixNanos::from(ts_last))];
95
96 retain_order_status_reports(&mut reports, &command);
97
98 assert_eq!(reports.len(), usize::from(expected_retained));
99 }
100
101 fn order_status_reports_command(
102 open_only: bool,
103 start: Option<UnixNanos>,
104 end: Option<UnixNanos>,
105 ) -> GenerateOrderStatusReports {
106 GenerateOrderStatusReports::new(
107 UUID4::new(),
108 UnixNanos::default(),
109 open_only,
110 None,
111 start,
112 end,
113 None,
114 None,
115 )
116 }
117
118 fn order_status_report(status: OrderStatus, ts_last: UnixNanos) -> OrderStatusReport {
119 OrderStatusReport::new(
120 AccountId::from("SIM-001"),
121 InstrumentId::from("AUD/USD.SIM"),
122 None,
123 VenueOrderId::from("ORDER-001"),
124 Some(OrderSide::Buy),
125 OrderType::Limit,
126 TimeInForce::Gtc,
127 status,
128 Quantity::from("1"),
129 Quantity::zero(0),
130 UnixNanos::from(1),
131 ts_last,
132 UnixNanos::from(2),
133 None,
134 )
135 }
136}