1use std::{collections::HashMap, str::FromStr};
17
18use nautilus_core::{UUID4, UnixNanos};
19
20use super::any::OrderAny;
21use crate::{
22 enums::{LiquiditySide, OrderType},
23 events::{OrderAccepted, OrderEventAny, OrderFilled, OrderSubmitted},
24 identifiers::{
25 AccountId, ClientOrderId, InstrumentId, PositionId, TradeId, Venue, VenueOrderId,
26 },
27 instruments::InstrumentAny,
28 orders::OrderTestBuilder,
29 types::{Money, Price, Quantity},
30};
31
32pub struct TestOrderEventStubs;
34
35impl TestOrderEventStubs {
36 pub fn order_submitted(order: &OrderAny, account_id: AccountId) -> OrderEventAny {
37 let event = OrderSubmitted::new(
38 order.trader_id(),
39 order.strategy_id(),
40 order.instrument_id(),
41 order.client_order_id(),
42 account_id,
43 UUID4::new(),
44 UnixNanos::default(),
45 UnixNanos::default(),
46 );
47 OrderEventAny::Submitted(event)
48 }
49
50 pub fn order_accepted(
51 order: &OrderAny,
52 account_id: AccountId,
53 venue_order_id: VenueOrderId,
54 ) -> OrderEventAny {
55 let event = OrderAccepted::new(
56 order.trader_id(),
57 order.strategy_id(),
58 order.instrument_id(),
59 order.client_order_id(),
60 venue_order_id,
61 account_id,
62 UUID4::new(),
63 UnixNanos::default(),
64 UnixNanos::default(),
65 false,
66 );
67 OrderEventAny::Accepted(event)
68 }
69
70 #[allow(clippy::too_many_arguments)]
71 pub fn order_filled(
72 order: &OrderAny,
73 instrument: &InstrumentAny,
74 trade_id: Option<TradeId>,
75 position_id: Option<PositionId>,
76 last_px: Option<Price>,
77 last_qty: Option<Quantity>,
78 liquidity_side: Option<LiquiditySide>,
79 commission: Option<Money>,
80 ts_filled_ns: Option<UnixNanos>,
81 account_id: Option<AccountId>,
82 ) -> OrderEventAny {
83 let venue_order_id = order.venue_order_id().unwrap_or_default();
84 let account_id = account_id
85 .or(order.account_id())
86 .unwrap_or(AccountId::from("SIM-001"));
87 let trade_id = trade_id.unwrap_or(TradeId::new(
88 order.client_order_id().as_str().replace('O', "E").as_str(),
89 ));
90 let liquidity_side = liquidity_side.unwrap_or(LiquiditySide::Maker);
91 let event = UUID4::new();
92 let position_id = position_id
93 .or_else(|| order.position_id())
94 .unwrap_or(PositionId::new("1"));
95 let commission = commission.unwrap_or(Money::from("2 USD"));
96 let last_px = last_px.unwrap_or(Price::from_str("1.0").unwrap());
97 let last_qty = last_qty.unwrap_or(order.quantity());
98 let event = OrderFilled::new(
99 order.trader_id(),
100 order.strategy_id(),
101 instrument.id(),
102 order.client_order_id(),
103 venue_order_id,
104 account_id,
105 trade_id,
106 order.order_side(),
107 order.order_type(),
108 last_qty,
109 last_px,
110 instrument.quote_currency(),
111 liquidity_side,
112 event,
113 ts_filled_ns.unwrap_or_default(),
114 UnixNanos::default(),
115 false,
116 Some(position_id),
117 Some(commission),
118 );
119 OrderEventAny::Filled(event)
120 }
121}
122
123pub struct TestOrderStubs;
124
125impl TestOrderStubs {
126 pub fn make_accepted_order(order: &OrderAny) -> OrderAny {
127 let mut new_order = order.clone();
128 let accepted_event = TestOrderEventStubs::order_accepted(
129 &new_order,
130 AccountId::from("SIM-001"),
131 VenueOrderId::from("V-001"),
132 );
133 new_order.apply(accepted_event).unwrap();
134 new_order
135 }
136
137 pub fn make_filled_order(
138 order: &OrderAny,
139 instrument: &InstrumentAny,
140 liquidity_side: LiquiditySide,
141 ) -> OrderAny {
142 let mut accepted_order = TestOrderStubs::make_accepted_order(order);
143 let fill = TestOrderEventStubs::order_filled(
144 &accepted_order,
145 instrument,
146 None,
147 None,
148 None,
149 None,
150 Some(liquidity_side),
151 None,
152 None,
153 None,
154 );
155 accepted_order.apply(fill).unwrap();
156 accepted_order
157 }
158}
159
160pub struct TestOrdersGenerator {
161 order_type: OrderType,
162 venue_instruments: HashMap<Venue, u32>,
163 orders_per_instrument: u32,
164}
165
166impl TestOrdersGenerator {
167 pub fn new(order_type: OrderType) -> Self {
168 Self {
169 order_type,
170 venue_instruments: HashMap::new(),
171 orders_per_instrument: 5,
172 }
173 }
174
175 pub fn set_orders_per_instrument(&mut self, total_orders: u32) {
176 self.orders_per_instrument = total_orders;
177 }
178
179 pub fn add_venue_and_total_instruments(&mut self, venue: Venue, total_instruments: u32) {
180 self.venue_instruments.insert(venue, total_instruments);
181 }
182
183 fn generate_order(&self, instrument_id: InstrumentId, client_order_id_index: u32) -> OrderAny {
184 let client_order_id =
185 ClientOrderId::from(format!("O-{}-{}", instrument_id, client_order_id_index));
186 OrderTestBuilder::new(self.order_type)
187 .quantity(Quantity::from("1"))
188 .price(Price::from("1"))
189 .instrument_id(instrument_id)
190 .client_order_id(client_order_id)
191 .build()
192 }
193
194 pub fn build(&self) -> Vec<OrderAny> {
195 let mut orders = Vec::new();
196 for (venue, total_instruments) in self.venue_instruments.iter() {
197 for i in 0..*total_instruments {
198 let instrument_id = InstrumentId::from(format!("SYMBOL-{}.{}", i, venue));
199 for order_index in 0..self.orders_per_instrument {
200 let order = self.generate_order(instrument_id, order_index);
201 orders.push(order);
202 }
203 }
204 }
205 orders
206 }
207}
208
209pub fn create_order_list_sample(
210 total_venues: u8,
211 total_instruments: u32,
212 orders_per_instrument: u32,
213) -> Vec<OrderAny> {
214 let mut order_generator = TestOrdersGenerator::new(OrderType::Limit);
217 for i in 0..total_venues {
218 let venue = Venue::from(format!("VENUE-{}", i));
219 order_generator.add_venue_and_total_instruments(venue, total_instruments);
220 }
221 order_generator.set_orders_per_instrument(orders_per_instrument);
222
223 order_generator.build()
224}