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