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