1#![allow(dead_code)]
20#![allow(unused_variables)]
21
22use std::{any::Any, cell::RefCell, fmt::Debug, rc::Rc};
23
24use nautilus_common::{
25 cache::Cache, clock::Clock, msgbus, msgbus::switchboard::MessagingSwitchboard,
26};
27use nautilus_core::{UUID4, UnixNanos};
28use nautilus_model::{
29 accounts::AccountAny,
30 enums::{AccountType, LiquiditySide, OmsType, OrderSide, OrderType},
31 events::{
32 AccountState, OrderAccepted, OrderCancelRejected, OrderCanceled, OrderDenied,
33 OrderEventAny, OrderExpired, OrderFilled, OrderModifyRejected, OrderRejected,
34 OrderSubmitted, OrderTriggered, OrderUpdated,
35 },
36 identifiers::{
37 AccountId, ClientId, ClientOrderId, InstrumentId, PositionId, StrategyId, TradeId,
38 TraderId, Venue, VenueOrderId,
39 },
40 reports::{ExecutionMassStatus, FillReport, OrderStatusReport, PositionStatusReport},
41 types::{AccountBalance, Currency, MarginBalance, Money, Price, Quantity},
42};
43
44#[derive(Clone)]
51pub struct ExecutionClientCore {
52 pub trader_id: TraderId,
53 pub client_id: ClientId,
54 pub venue: Venue,
55 pub oms_type: OmsType,
56 pub account_id: AccountId,
57 pub account_type: AccountType,
58 pub base_currency: Option<Currency>,
59 pub is_connected: bool,
60 clock: Rc<RefCell<dyn Clock>>,
61 cache: Rc<RefCell<Cache>>,
62}
63
64impl Debug for ExecutionClientCore {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 f.debug_struct(stringify!(ExecutionClientCore))
67 .field("client_id", &self.client_id)
68 .finish()
69 }
70}
71
72impl ExecutionClientCore {
73 #[allow(clippy::too_many_arguments)]
75 pub const fn new(
76 trader_id: TraderId,
77 client_id: ClientId,
78 venue: Venue,
79 oms_type: OmsType,
80 account_id: AccountId,
81 account_type: AccountType,
82 base_currency: Option<Currency>,
83 clock: Rc<RefCell<dyn Clock>>,
84 cache: Rc<RefCell<Cache>>,
85 ) -> Self {
86 Self {
87 trader_id,
88 client_id,
89 venue,
90 oms_type,
91 account_id,
92 account_type,
93 base_currency,
94 is_connected: false,
95 clock,
96 cache,
97 }
98 }
99
100 pub const fn set_connected(&mut self, is_connected: bool) {
102 self.is_connected = is_connected;
103 }
104
105 pub const fn set_account_id(&mut self, account_id: AccountId) {
107 self.account_id = account_id;
108 }
109
110 #[must_use]
112 pub const fn clock(&self) -> &Rc<RefCell<dyn Clock>> {
113 &self.clock
114 }
115
116 #[must_use]
118 pub const fn cache(&self) -> &Rc<RefCell<Cache>> {
119 &self.cache
120 }
121
122 #[must_use]
124 pub fn get_account(&self) -> Option<AccountAny> {
125 self.cache.borrow().account(&self.account_id).cloned()
126 }
127
128 pub fn generate_account_state(
134 &self,
135 balances: Vec<AccountBalance>,
136 margins: Vec<MarginBalance>,
137 reported: bool,
138 ts_event: UnixNanos,
139 ) -> anyhow::Result<()> {
141 let account_state = AccountState::new(
142 self.account_id,
143 self.account_type,
144 balances,
145 margins,
146 reported,
147 UUID4::new(),
148 ts_event,
149 self.clock.borrow().timestamp_ns(),
150 self.base_currency,
151 );
152 self.send_account_state(account_state);
153 Ok(())
154 }
155
156 pub fn generate_order_denied(
157 &self,
158 strategy_id: StrategyId,
159 instrument_id: InstrumentId,
160 client_order_id: ClientOrderId,
161 reason: &str,
162 ts_event: UnixNanos,
163 ) {
164 let event = OrderDenied::new(
165 self.trader_id,
166 strategy_id,
167 instrument_id,
168 client_order_id,
169 reason.into(),
170 UUID4::new(),
171 ts_event,
172 self.clock.borrow().timestamp_ns(),
173 );
174 self.send_order_event(OrderEventAny::Denied(event));
175 }
176
177 pub fn generate_order_submitted(
178 &self,
179 strategy_id: StrategyId,
180 instrument_id: InstrumentId,
181 client_order_id: ClientOrderId,
182 ts_event: UnixNanos,
183 ) {
184 let event = OrderSubmitted::new(
185 self.trader_id,
186 strategy_id,
187 instrument_id,
188 client_order_id,
189 self.account_id,
190 UUID4::new(),
191 ts_event,
192 self.clock.borrow().timestamp_ns(),
193 );
194 self.send_order_event(OrderEventAny::Submitted(event));
195 }
196
197 pub fn generate_order_rejected(
198 &self,
199 strategy_id: StrategyId,
200 instrument_id: InstrumentId,
201 client_order_id: ClientOrderId,
202 reason: &str,
203 ts_event: UnixNanos,
204 due_post_only: bool,
205 ) {
206 let event = OrderRejected::new(
207 self.trader_id,
208 strategy_id,
209 instrument_id,
210 client_order_id,
211 self.account_id,
212 reason.into(),
213 UUID4::new(),
214 ts_event,
215 self.clock.borrow().timestamp_ns(),
216 false,
217 due_post_only,
218 );
219 self.send_order_event(OrderEventAny::Rejected(event));
220 }
221
222 pub fn generate_order_accepted(
223 &self,
224 strategy_id: StrategyId,
225 instrument_id: InstrumentId,
226 client_order_id: ClientOrderId,
227 venue_order_id: VenueOrderId,
228 ts_event: UnixNanos,
229 ) {
230 let event = OrderAccepted::new(
231 self.trader_id,
232 strategy_id,
233 instrument_id,
234 client_order_id,
235 venue_order_id,
236 self.account_id,
237 UUID4::new(),
238 ts_event,
239 self.clock.borrow().timestamp_ns(),
240 false,
241 );
242 self.send_order_event(OrderEventAny::Accepted(event));
243 }
244
245 pub fn generate_order_modify_rejected(
246 &self,
247 strategy_id: StrategyId,
248 instrument_id: InstrumentId,
249 client_order_id: ClientOrderId,
250 venue_order_id: VenueOrderId,
251 reason: &str,
252 ts_event: UnixNanos,
253 ) {
254 let event = OrderModifyRejected::new(
255 self.trader_id,
256 strategy_id,
257 instrument_id,
258 client_order_id,
259 reason.into(),
260 UUID4::new(),
261 ts_event,
262 self.clock.borrow().timestamp_ns(),
263 false,
264 Some(venue_order_id),
265 Some(self.account_id),
266 );
267 self.send_order_event(OrderEventAny::ModifyRejected(event));
268 }
269
270 pub fn generate_order_cancel_rejected(
271 &self,
272 strategy_id: StrategyId,
273 instrument_id: InstrumentId,
274 client_order_id: ClientOrderId,
275 venue_order_id: VenueOrderId,
276 reason: &str,
277 ts_event: UnixNanos,
278 ) {
279 let event = OrderCancelRejected::new(
280 self.trader_id,
281 strategy_id,
282 instrument_id,
283 client_order_id,
284 reason.into(),
285 UUID4::new(),
286 ts_event,
287 self.clock.borrow().timestamp_ns(),
288 false,
289 Some(venue_order_id),
290 Some(self.account_id),
291 );
292 self.send_order_event(OrderEventAny::CancelRejected(event));
293 }
294
295 #[allow(clippy::too_many_arguments)]
296 pub fn generate_order_updated(
297 &self,
298 strategy_id: StrategyId,
299 instrument_id: InstrumentId,
300 client_order_id: ClientOrderId,
301 venue_order_id: VenueOrderId,
302 quantity: Quantity,
303 price: Price,
304 trigger_price: Option<Price>,
305 protection_price: Option<Price>,
306 ts_event: UnixNanos,
307 venue_order_id_modified: bool,
308 ) {
309 if !venue_order_id_modified {
310 let cache = self.cache.as_ref().borrow();
311 let existing_order_result = cache.venue_order_id(&client_order_id);
312 if let Some(existing_order) = existing_order_result
313 && *existing_order != venue_order_id
314 {
315 log::error!(
316 "Existing venue order id {existing_order} does not match provided venue order id {venue_order_id}"
317 );
318 }
319 }
320
321 let event = OrderUpdated::new(
322 self.trader_id,
323 strategy_id,
324 instrument_id,
325 client_order_id,
326 quantity,
327 UUID4::new(),
328 ts_event,
329 self.clock.borrow().timestamp_ns(),
330 false,
331 Some(venue_order_id),
332 Some(self.account_id),
333 Some(price),
334 trigger_price,
335 protection_price,
336 );
337
338 self.send_order_event(OrderEventAny::Updated(event));
339 }
340
341 pub fn generate_order_canceled(
342 &self,
343 strategy_id: StrategyId,
344 instrument_id: InstrumentId,
345 client_order_id: ClientOrderId,
346 venue_order_id: VenueOrderId,
347 ts_event: UnixNanos,
348 ) {
349 let event = OrderCanceled::new(
350 self.trader_id,
351 strategy_id,
352 instrument_id,
353 client_order_id,
354 UUID4::new(),
355 ts_event,
356 self.clock.borrow().timestamp_ns(),
357 false,
358 Some(venue_order_id),
359 Some(self.account_id),
360 );
361
362 self.send_order_event(OrderEventAny::Canceled(event));
363 }
364
365 pub fn generate_order_triggered(
366 &self,
367 strategy_id: StrategyId,
368 instrument_id: InstrumentId,
369 client_order_id: ClientOrderId,
370 venue_order_id: VenueOrderId,
371 ts_event: UnixNanos,
372 ) {
373 let event = OrderTriggered::new(
374 self.trader_id,
375 strategy_id,
376 instrument_id,
377 client_order_id,
378 UUID4::new(),
379 ts_event,
380 self.clock.borrow().timestamp_ns(),
381 false,
382 Some(venue_order_id),
383 Some(self.account_id),
384 );
385
386 self.send_order_event(OrderEventAny::Triggered(event));
387 }
388
389 pub fn generate_order_expired(
390 &self,
391 strategy_id: StrategyId,
392 instrument_id: InstrumentId,
393 client_order_id: ClientOrderId,
394 venue_order_id: VenueOrderId,
395 ts_event: UnixNanos,
396 ) {
397 let event = OrderExpired::new(
398 self.trader_id,
399 strategy_id,
400 instrument_id,
401 client_order_id,
402 UUID4::new(),
403 ts_event,
404 self.clock.borrow().timestamp_ns(),
405 false,
406 Some(venue_order_id),
407 Some(self.account_id),
408 );
409
410 self.send_order_event(OrderEventAny::Expired(event));
411 }
412
413 #[allow(clippy::too_many_arguments)]
414 pub fn generate_order_filled(
415 &self,
416 strategy_id: StrategyId,
417 instrument_id: InstrumentId,
418 client_order_id: ClientOrderId,
419 venue_order_id: VenueOrderId,
420 venue_position_id: PositionId,
421 trade_id: TradeId,
422 order_side: OrderSide,
423 order_type: OrderType,
424 last_qty: Quantity,
425 last_px: Price,
426 quote_currency: Currency,
427 commission: Money,
428 liquidity_side: LiquiditySide,
429 ts_event: UnixNanos,
430 ) {
431 let event = OrderFilled::new(
432 self.trader_id,
433 strategy_id,
434 instrument_id,
435 client_order_id,
436 venue_order_id,
437 self.account_id,
438 trade_id,
439 order_side,
440 order_type,
441 last_qty,
442 last_px,
443 quote_currency,
444 liquidity_side,
445 UUID4::new(),
446 ts_event,
447 self.clock.borrow().timestamp_ns(),
448 false,
449 Some(venue_position_id),
450 Some(commission),
451 );
452
453 self.send_order_event(OrderEventAny::Filled(event));
454 }
455
456 fn send_account_state(&self, account_state: AccountState) {
457 let endpoint = MessagingSwitchboard::portfolio_update_account();
458 msgbus::send_any(endpoint, &account_state as &dyn Any);
459 }
460
461 fn send_order_event(&self, event: OrderEventAny) {
462 let endpoint = MessagingSwitchboard::exec_engine_process();
463 msgbus::send_any(endpoint, &event as &dyn Any);
464 }
465
466 fn send_mass_status_report(&self, report: ExecutionMassStatus) {
467 let endpoint = MessagingSwitchboard::exec_engine_reconcile_execution_mass_status();
468 msgbus::send_any(endpoint, &report as &dyn Any);
469 }
470
471 fn send_order_status_report(&self, report: OrderStatusReport) {
472 let endpoint = MessagingSwitchboard::exec_engine_reconcile_execution_report();
473 msgbus::send_any(endpoint, &report as &dyn Any);
474 }
475
476 fn send_fill_report(&self, report: FillReport) {
477 let endpoint = MessagingSwitchboard::exec_engine_reconcile_execution_report();
478 msgbus::send_any(endpoint, &report as &dyn Any);
479 }
480
481 fn send_position_report(&self, report: PositionStatusReport) {
482 let endpoint = MessagingSwitchboard::exec_engine_reconcile_execution_report();
483 msgbus::send_any(endpoint, &report as &dyn Any);
484 }
485}