1use nautilus_common::{
33 factories::OrderEventFactory,
34 messages::{ExecutionEvent, ExecutionReport},
35};
36use nautilus_core::{UUID4, UnixNanos, time::AtomicTime};
37use nautilus_model::{
38 enums::{AccountType, LiquiditySide},
39 events::{
40 AccountState, OrderAcceptedBatch, OrderCancelRejected, OrderCanceledBatch, OrderEventAny,
41 OrderModifyRejected, OrderRejected, OrderSubmittedBatch,
42 },
43 identifiers::{
44 AccountId, ClientOrderId, InstrumentId, PositionId, StrategyId, TradeId, TraderId,
45 VenueOrderId,
46 },
47 orders::OrderAny,
48 reports::{FillReport, OrderStatusReport, PositionStatusReport},
49 types::{AccountBalance, Currency, MarginBalance, Money, Price, Quantity},
50};
51
52#[derive(Debug, Clone)]
60pub struct ExecutionEventEmitter {
61 clock: &'static AtomicTime,
62 factory: OrderEventFactory,
63 sender: Option<tokio::sync::mpsc::UnboundedSender<ExecutionEvent>>,
64}
65
66impl ExecutionEventEmitter {
67 #[must_use]
71 pub fn new(
72 clock: &'static AtomicTime,
73 trader_id: TraderId,
74 account_id: AccountId,
75 account_type: AccountType,
76 base_currency: Option<Currency>,
77 ) -> Self {
78 Self {
79 clock,
80 factory: OrderEventFactory::new(trader_id, account_id, account_type, base_currency),
81 sender: None,
82 }
83 }
84
85 fn ts_init(&self) -> UnixNanos {
86 self.clock.get_time_ns()
87 }
88
89 pub fn set_sender(&mut self, sender: tokio::sync::mpsc::UnboundedSender<ExecutionEvent>) {
91 self.sender = Some(sender);
92 }
93
94 #[must_use]
96 pub fn is_initialized(&self) -> bool {
97 self.sender.is_some()
98 }
99
100 #[must_use]
102 pub fn trader_id(&self) -> TraderId {
103 self.factory.trader_id()
104 }
105
106 #[must_use]
108 pub fn account_id(&self) -> AccountId {
109 self.factory.account_id()
110 }
111
112 pub fn set_account_id(&mut self, account_id: AccountId) {
114 self.factory.set_account_id(account_id);
115 }
116
117 pub fn emit_account_state(
119 &self,
120 balances: Vec<AccountBalance>,
121 margins: Vec<MarginBalance>,
122 reported: bool,
123 ts_event: UnixNanos,
124 ) {
125 let state = self.factory.generate_account_state(
126 balances,
127 margins,
128 reported,
129 ts_event,
130 self.ts_init(),
131 );
132 self.send_account_state(state);
133 }
134
135 pub fn emit_order_denied(&self, order: &OrderAny, reason: &str) {
137 let event = self
138 .factory
139 .generate_order_denied(order, reason, self.ts_init());
140 self.send_order_event(event);
141 }
142
143 pub fn emit_order_submitted(&self, order: &OrderAny) {
145 let event = self.factory.generate_order_submitted(order, self.ts_init());
146 self.send_order_event(event);
147 }
148
149 pub fn emit_order_rejected(
151 &self,
152 order: &OrderAny,
153 reason: &str,
154 ts_event: UnixNanos,
155 due_post_only: bool,
156 ) {
157 let event = self.factory.generate_order_rejected(
158 order,
159 reason,
160 ts_event,
161 self.ts_init(),
162 due_post_only,
163 );
164 self.send_order_event(event);
165 }
166
167 pub fn emit_order_accepted(
169 &self,
170 order: &OrderAny,
171 venue_order_id: VenueOrderId,
172 ts_event: UnixNanos,
173 ) {
174 let event =
175 self.factory
176 .generate_order_accepted(order, venue_order_id, ts_event, self.ts_init());
177 self.send_order_event(event);
178 }
179
180 pub fn emit_order_modify_rejected(
182 &self,
183 order: &OrderAny,
184 venue_order_id: Option<VenueOrderId>,
185 reason: &str,
186 ts_event: UnixNanos,
187 ) {
188 let event = self.factory.generate_order_modify_rejected(
189 order,
190 venue_order_id,
191 reason,
192 ts_event,
193 self.ts_init(),
194 );
195 self.send_order_event(event);
196 }
197
198 pub fn emit_order_cancel_rejected(
200 &self,
201 order: &OrderAny,
202 venue_order_id: Option<VenueOrderId>,
203 reason: &str,
204 ts_event: UnixNanos,
205 ) {
206 let event = self.factory.generate_order_cancel_rejected(
207 order,
208 venue_order_id,
209 reason,
210 ts_event,
211 self.ts_init(),
212 );
213 self.send_order_event(event);
214 }
215
216 #[expect(clippy::too_many_arguments)]
218 pub fn emit_order_updated(
219 &self,
220 order: &OrderAny,
221 venue_order_id: VenueOrderId,
222 quantity: Quantity,
223 price: Option<Price>,
224 trigger_price: Option<Price>,
225 protection_price: Option<Price>,
226 ts_event: UnixNanos,
227 ) {
228 let event = self.factory.generate_order_updated(
229 order,
230 venue_order_id,
231 quantity,
232 price,
233 trigger_price,
234 protection_price,
235 ts_event,
236 self.ts_init(),
237 );
238 self.send_order_event(event);
239 }
240
241 pub fn emit_order_canceled(
243 &self,
244 order: &OrderAny,
245 venue_order_id: Option<VenueOrderId>,
246 ts_event: UnixNanos,
247 ) {
248 let event =
249 self.factory
250 .generate_order_canceled(order, venue_order_id, ts_event, self.ts_init());
251 self.send_order_event(event);
252 }
253
254 pub fn emit_order_triggered(
256 &self,
257 order: &OrderAny,
258 venue_order_id: Option<VenueOrderId>,
259 ts_event: UnixNanos,
260 ) {
261 let event =
262 self.factory
263 .generate_order_triggered(order, venue_order_id, ts_event, self.ts_init());
264 self.send_order_event(event);
265 }
266
267 pub fn emit_order_expired(
269 &self,
270 order: &OrderAny,
271 venue_order_id: Option<VenueOrderId>,
272 ts_event: UnixNanos,
273 ) {
274 let event =
275 self.factory
276 .generate_order_expired(order, venue_order_id, ts_event, self.ts_init());
277 self.send_order_event(event);
278 }
279
280 #[expect(clippy::too_many_arguments)]
282 pub fn emit_order_filled(
283 &self,
284 order: &OrderAny,
285 venue_order_id: VenueOrderId,
286 venue_position_id: Option<PositionId>,
287 trade_id: TradeId,
288 last_qty: Quantity,
289 last_px: Price,
290 quote_currency: Currency,
291 commission: Option<Money>,
292 liquidity_side: LiquiditySide,
293 ts_event: UnixNanos,
294 ) {
295 let event = self.factory.generate_order_filled(
296 order,
297 venue_order_id,
298 venue_position_id,
299 trade_id,
300 last_qty,
301 last_px,
302 quote_currency,
303 commission,
304 liquidity_side,
305 ts_event,
306 self.ts_init(),
307 );
308 self.send_order_event(event);
309 }
310
311 pub fn emit_order_rejected_event(
313 &self,
314 strategy_id: StrategyId,
315 instrument_id: InstrumentId,
316 client_order_id: ClientOrderId,
317 reason: &str,
318 ts_event: UnixNanos,
319 due_post_only: bool,
320 ) {
321 let event = OrderRejected::new(
322 self.factory.trader_id(),
323 strategy_id,
324 instrument_id,
325 client_order_id,
326 self.factory.account_id(),
327 reason.into(),
328 UUID4::new(),
329 ts_event,
330 self.ts_init(),
331 false,
332 due_post_only,
333 );
334 self.send_order_event(OrderEventAny::Rejected(event));
335 }
336
337 pub fn emit_order_modify_rejected_event(
339 &self,
340 strategy_id: StrategyId,
341 instrument_id: InstrumentId,
342 client_order_id: ClientOrderId,
343 venue_order_id: Option<VenueOrderId>,
344 reason: &str,
345 ts_event: UnixNanos,
346 ) {
347 let event = OrderModifyRejected::new(
348 self.factory.trader_id(),
349 strategy_id,
350 instrument_id,
351 client_order_id,
352 reason.into(),
353 UUID4::new(),
354 ts_event,
355 self.ts_init(),
356 false,
357 venue_order_id,
358 Some(self.factory.account_id()),
359 );
360 self.send_order_event(OrderEventAny::ModifyRejected(event));
361 }
362
363 pub fn emit_order_cancel_rejected_event(
365 &self,
366 strategy_id: StrategyId,
367 instrument_id: InstrumentId,
368 client_order_id: ClientOrderId,
369 venue_order_id: Option<VenueOrderId>,
370 reason: &str,
371 ts_event: UnixNanos,
372 ) {
373 let event = OrderCancelRejected::new(
374 self.factory.trader_id(),
375 strategy_id,
376 instrument_id,
377 client_order_id,
378 reason.into(),
379 UUID4::new(),
380 ts_event,
381 self.ts_init(),
382 false,
383 venue_order_id,
384 Some(self.factory.account_id()),
385 );
386 self.send_order_event(OrderEventAny::CancelRejected(event));
387 }
388
389 pub fn send_order_event(&self, event: OrderEventAny) {
391 if let Some(sender) = &self.sender {
392 if let Err(e) = sender.send(ExecutionEvent::Order(event)) {
393 log::warn!("Failed to send order event: {e}");
394 }
395 } else {
396 log::warn!("Cannot send order event: sender not initialized");
397 }
398 }
399
400 pub fn send_order_submitted_batch(&self, batch: OrderSubmittedBatch) {
402 if let Some(sender) = &self.sender {
403 if let Err(e) = sender.send(ExecutionEvent::OrderSubmittedBatch(batch)) {
404 log::warn!("Failed to send order submitted batch: {e}");
405 }
406 } else {
407 log::warn!("Cannot send order submitted batch: sender not initialized");
408 }
409 }
410
411 pub fn send_order_accepted_batch(&self, batch: OrderAcceptedBatch) {
413 if let Some(sender) = &self.sender {
414 if let Err(e) = sender.send(ExecutionEvent::OrderAcceptedBatch(batch)) {
415 log::warn!("Failed to send order accepted batch: {e}");
416 }
417 } else {
418 log::warn!("Cannot send order accepted batch: sender not initialized");
419 }
420 }
421
422 pub fn send_order_canceled_batch(&self, batch: OrderCanceledBatch) {
424 if let Some(sender) = &self.sender {
425 if let Err(e) = sender.send(ExecutionEvent::OrderCanceledBatch(batch)) {
426 log::warn!("Failed to send order canceled batch: {e}");
427 }
428 } else {
429 log::warn!("Cannot send order canceled batch: sender not initialized");
430 }
431 }
432
433 pub fn send_account_state(&self, state: AccountState) {
435 if let Some(sender) = &self.sender {
436 if let Err(e) = sender.send(ExecutionEvent::Account(state)) {
437 log::warn!("Failed to send account state: {e}");
438 }
439 } else {
440 log::warn!("Cannot send account state: sender not initialized");
441 }
442 }
443
444 pub fn send_execution_report(&self, report: ExecutionReport) {
446 if let Some(sender) = &self.sender {
447 if let Err(e) = sender.send(ExecutionEvent::Report(report)) {
448 log::warn!("Failed to send execution report: {e}");
449 }
450 } else {
451 log::warn!("Cannot send execution report: sender not initialized");
452 }
453 }
454
455 pub fn send_order_status_report(&self, report: OrderStatusReport) {
457 self.send_execution_report(ExecutionReport::Order(Box::new(report)));
458 }
459
460 pub fn send_fill_report(&self, report: FillReport) {
462 self.send_execution_report(ExecutionReport::Fill(Box::new(report)));
463 }
464
465 pub fn send_order_with_fills(&self, report: OrderStatusReport, fills: Vec<FillReport>) {
467 self.send_execution_report(ExecutionReport::OrderWithFills(Box::new(report), fills));
468 }
469
470 pub fn send_position_report(&self, report: PositionStatusReport) {
472 self.send_execution_report(ExecutionReport::Position(Box::new(report)));
473 }
474}