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