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