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