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