nautilus_execution/engine/
stubs.rs1use std::{
17 cell::{Cell, RefCell},
18 rc::Rc,
19};
20
21use async_trait::async_trait;
22use nautilus_common::{
23 cache::Cache,
24 clients::ExecutionClient,
25 clock::{Clock, VirtualClock},
26 messages::execution::{
27 BatchCancelOrders, BatchModifyOrders, CancelAllOrders, CancelOrder, ModifyOrder,
28 QueryAccount, QueryOrder, SubmitOrder, SubmitOrderList,
29 },
30};
31use nautilus_core::{Params, UnixNanos};
32use nautilus_model::{
33 accounts::AccountAny,
34 enums::OmsType,
35 identifiers::{
36 AccountId, ClientId, ClientOrderId, InstrumentId, StrategyId, Venue, VenueOrderId,
37 },
38 instruments::InstrumentAny,
39 orders::OrderAny,
40 reports::FillReport,
41 types::{AccountBalance, MarginBalance},
42};
43
44#[derive(Clone, Debug)]
49#[allow(dead_code)]
50pub struct StubExecutionClient {
51 client_id: ClientId,
52 account_id: AccountId,
53 venue: Venue,
54 oms_type: OmsType,
55 is_connected: bool,
56 clock: Rc<RefCell<dyn Clock>>,
57 cache: Rc<RefCell<Cache>>,
58 received_instruments: Rc<RefCell<Vec<InstrumentAny>>>,
59 start_count: Rc<Cell<usize>>,
60 stop_count: Rc<Cell<usize>>,
61 reset_count: Rc<Cell<usize>>,
62 dispose_count: Rc<Cell<usize>>,
63 submitted_order_ids: Rc<RefCell<Vec<ClientOrderId>>>,
64 modified_order_ids: Rc<RefCell<Vec<ClientOrderId>>>,
65 cancel_all_commands: Rc<RefCell<Vec<CancelAllOrders>>>,
66 queried_account_ids: Rc<RefCell<Vec<AccountId>>>,
67 registered_external_order_ids: Rc<RefCell<Vec<ClientOrderId>>>,
68 handles_all_order_venues: bool,
69 settles_contract_expirations: bool,
70 allows_reconciliation_overfill: bool,
71 submit_order_error: Option<String>,
72 submit_order_list_error: Option<String>,
73}
74
75impl StubExecutionClient {
76 #[allow(dead_code)]
78 pub fn new(
79 client_id: ClientId,
80 account_id: AccountId,
81 venue: Venue,
82 oms_type: OmsType,
83 clock: Option<Rc<RefCell<dyn Clock>>>,
84 ) -> Self {
85 Self {
86 client_id,
87 account_id,
88 venue,
89 oms_type,
90 is_connected: false,
91 clock: clock.unwrap_or_else(|| Rc::new(RefCell::new(VirtualClock::new()))),
92 cache: Rc::new(RefCell::new(Cache::new(None, None))),
93 received_instruments: Rc::new(RefCell::new(Vec::new())),
94 start_count: Rc::new(Cell::new(0)),
95 stop_count: Rc::new(Cell::new(0)),
96 reset_count: Rc::new(Cell::new(0)),
97 dispose_count: Rc::new(Cell::new(0)),
98 submitted_order_ids: Rc::new(RefCell::new(Vec::new())),
99 modified_order_ids: Rc::new(RefCell::new(Vec::new())),
100 cancel_all_commands: Rc::new(RefCell::new(Vec::new())),
101 queried_account_ids: Rc::new(RefCell::new(Vec::new())),
102 registered_external_order_ids: Rc::new(RefCell::new(Vec::new())),
103 handles_all_order_venues: false,
104 settles_contract_expirations: false,
105 allows_reconciliation_overfill: false,
106 submit_order_error: None,
107 submit_order_list_error: None,
108 }
109 }
110
111 #[must_use]
113 pub fn with_handles_all_order_venues(mut self) -> Self {
114 self.handles_all_order_venues = true;
115 self
116 }
117
118 #[must_use]
120 pub fn with_settles_contract_expirations(mut self) -> Self {
121 self.settles_contract_expirations = true;
122 self
123 }
124
125 #[must_use]
127 pub fn with_allows_reconciliation_overfill(mut self) -> Self {
128 self.allows_reconciliation_overfill = true;
129 self
130 }
131
132 #[must_use]
134 pub fn with_submit_order_error(mut self, error: impl Into<String>) -> Self {
135 self.submit_order_error = Some(error.into());
136 self
137 }
138
139 #[must_use]
141 pub fn with_submit_order_list_error(mut self, error: impl Into<String>) -> Self {
142 self.submit_order_list_error = Some(error.into());
143 self
144 }
145
146 #[must_use]
149 pub fn registered_external_order_ids(&self) -> Rc<RefCell<Vec<ClientOrderId>>> {
150 self.registered_external_order_ids.clone()
151 }
152
153 #[must_use]
155 pub fn received_instruments(&self) -> Rc<RefCell<Vec<InstrumentAny>>> {
156 self.received_instruments.clone()
157 }
158
159 #[must_use]
161 pub fn submitted_order_ids(&self) -> Rc<RefCell<Vec<ClientOrderId>>> {
162 self.submitted_order_ids.clone()
163 }
164
165 #[must_use]
167 pub fn modified_order_ids(&self) -> Rc<RefCell<Vec<ClientOrderId>>> {
168 self.modified_order_ids.clone()
169 }
170
171 #[must_use]
173 pub fn cancel_all_commands(&self) -> Rc<RefCell<Vec<CancelAllOrders>>> {
174 self.cancel_all_commands.clone()
175 }
176
177 #[must_use]
179 pub fn queried_account_ids(&self) -> Rc<RefCell<Vec<AccountId>>> {
180 self.queried_account_ids.clone()
181 }
182
183 #[must_use]
185 pub fn start_count(&self) -> usize {
186 self.start_count.get()
187 }
188
189 #[must_use]
191 pub fn stop_count(&self) -> usize {
192 self.stop_count.get()
193 }
194
195 #[must_use]
197 pub fn reset_count(&self) -> usize {
198 self.reset_count.get()
199 }
200
201 #[must_use]
203 pub fn dispose_count(&self) -> usize {
204 self.dispose_count.get()
205 }
206}
207
208#[async_trait(?Send)]
209impl ExecutionClient for StubExecutionClient {
210 fn is_connected(&self) -> bool {
211 self.is_connected
212 }
213
214 fn client_id(&self) -> ClientId {
215 self.client_id
216 }
217
218 fn account_id(&self) -> AccountId {
219 self.account_id
220 }
221
222 fn venue(&self) -> Venue {
223 self.venue
224 }
225
226 fn handles_order_venue(&self, venue: Venue) -> bool {
227 self.handles_all_order_venues || self.venue == venue
228 }
229
230 fn settles_contract_expirations(&self) -> bool {
231 self.settles_contract_expirations
232 }
233
234 fn allows_reconciliation_overfill(&self, _order: &OrderAny, _report: &FillReport) -> bool {
235 self.allows_reconciliation_overfill
236 }
237
238 fn oms_type(&self) -> OmsType {
239 self.oms_type
240 }
241
242 fn get_account(&self) -> Option<AccountAny> {
243 None }
245
246 fn generate_account_state(
247 &self,
248 _balances: Vec<AccountBalance>,
249 _margins: Vec<MarginBalance>,
250 _reported: bool,
251 _ts_event: UnixNanos,
252 _info: Option<Params>,
253 ) -> anyhow::Result<()> {
254 Ok(()) }
256
257 fn start(&mut self) -> anyhow::Result<()> {
258 self.is_connected = true;
259 self.start_count.set(self.start_count.get() + 1);
260 Ok(())
261 }
262
263 fn stop(&mut self) -> anyhow::Result<()> {
264 self.is_connected = false;
265 self.stop_count.set(self.stop_count.get() + 1);
266 Ok(())
267 }
268
269 fn reset(&mut self) -> anyhow::Result<()> {
270 self.reset_count.set(self.reset_count.get() + 1);
271 Ok(())
272 }
273
274 fn dispose(&mut self) -> anyhow::Result<()> {
275 self.dispose_count.set(self.dispose_count.get() + 1);
276 Ok(())
277 }
278
279 fn submit_order(&self, cmd: SubmitOrder) -> anyhow::Result<()> {
280 if let Some(error) = &self.submit_order_error {
281 anyhow::bail!("{error}");
282 }
283
284 self.submitted_order_ids
285 .borrow_mut()
286 .push(cmd.client_order_id);
287
288 Ok(()) }
290
291 fn submit_order_list(&self, cmd: SubmitOrderList) -> anyhow::Result<()> {
292 if let Some(error) = &self.submit_order_list_error {
293 anyhow::bail!("{error}");
294 }
295
296 self.submitted_order_ids
297 .borrow_mut()
298 .extend(cmd.order_list.client_order_ids);
299
300 Ok(()) }
302
303 fn modify_order(&self, cmd: ModifyOrder) -> anyhow::Result<()> {
304 self.modified_order_ids
305 .borrow_mut()
306 .push(cmd.client_order_id);
307
308 Ok(()) }
310
311 fn batch_modify_orders(&self, cmd: BatchModifyOrders) -> anyhow::Result<()> {
312 self.modified_order_ids.borrow_mut().extend(
313 cmd.modifies
314 .into_iter()
315 .map(|modify| modify.client_order_id),
316 );
317
318 Ok(()) }
320
321 fn cancel_order(&self, _cmd: CancelOrder) -> anyhow::Result<()> {
322 Ok(()) }
324
325 fn cancel_all_orders(&self, cmd: CancelAllOrders) -> anyhow::Result<()> {
326 self.cancel_all_commands.borrow_mut().push(cmd);
327 Ok(()) }
329
330 fn batch_cancel_orders(&self, _cmd: BatchCancelOrders) -> anyhow::Result<()> {
331 Ok(()) }
333
334 fn query_account(&self, cmd: QueryAccount) -> anyhow::Result<()> {
335 self.queried_account_ids.borrow_mut().push(cmd.account_id);
336
337 Ok(()) }
339
340 fn query_order(&self, _cmd: QueryOrder) -> anyhow::Result<()> {
341 Ok(()) }
343
344 fn register_external_order(
345 &self,
346 client_order_id: ClientOrderId,
347 _venue_order_id: VenueOrderId,
348 _instrument_id: InstrumentId,
349 _strategy_id: StrategyId,
350 _ts_init: UnixNanos,
351 ) {
352 self.registered_external_order_ids
353 .borrow_mut()
354 .push(client_order_id);
355 }
356
357 fn on_instrument(&mut self, instrument: InstrumentAny) {
358 self.received_instruments.borrow_mut().push(instrument);
359 }
360}