1use std::{cell::RefCell, fmt::Debug, rc::Rc};
19
20use async_trait::async_trait;
21use nautilus_common::{
22 cache::Cache,
23 clients::ExecutionClient,
24 clock::Clock,
25 factories::OrderEventFactory,
26 messages::execution::{
27 BatchCancelOrders, BatchModifyOrders, CancelAllOrders, CancelOrder, ModifyOrder,
28 QueryAccount, QueryOrder, SubmitOrder, SubmitOrderList, TradingCommand,
29 },
30 msgbus::{self, MessagingSwitchboard},
31};
32use nautilus_core::{SharedCell, UnixNanos, WeakCell};
33use nautilus_execution::client::core::ExecutionClientCore;
34use nautilus_model::{
35 accounts::AccountAny,
36 enums::OmsType,
37 events::OrderEventAny,
38 identifiers::{AccountId, ClientId, ClientOrderId, TraderId, Venue},
39 orders::OrderAny,
40 types::{AccountBalance, MarginBalance},
41};
42
43use crate::exchange::SimulatedExchange;
44
45#[derive(Clone)]
52pub struct BacktestExecutionClient {
53 core: ExecutionClientCore,
54 factory: OrderEventFactory,
55 cache: Rc<RefCell<Cache>>,
56 clock: Rc<RefCell<dyn Clock>>,
57 exchange: WeakCell<SimulatedExchange>,
58 queued_events: Rc<RefCell<Vec<OrderEventAny>>>,
65 routing: bool,
66 _frozen_account: bool,
67}
68
69impl Debug for BacktestExecutionClient {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 f.debug_struct(stringify!(BacktestExecutionClient))
72 .field("client_id", &self.core.client_id)
73 .field("routing", &self.routing)
74 .finish_non_exhaustive()
75 }
76}
77
78impl BacktestExecutionClient {
79 #[must_use]
81 pub fn new(
82 trader_id: TraderId,
83 account_id: AccountId,
84 exchange: &Rc<RefCell<SimulatedExchange>>,
85 cache: Rc<RefCell<Cache>>,
86 clock: Rc<RefCell<dyn Clock>>,
87 routing: Option<bool>,
88 frozen_account: Option<bool>,
89 ) -> Self {
90 let routing = routing.unwrap_or(false);
91 let frozen_account = frozen_account.unwrap_or(false);
92 let exchange_shared: SharedCell<SimulatedExchange> = SharedCell::from(exchange.clone());
93 let exchange_id = exchange_shared.borrow().id;
94 let account_type = exchange.borrow().account_type;
95 let base_currency = exchange.borrow().base_currency;
96
97 let core = ExecutionClientCore::new(
98 trader_id,
99 ClientId::from(exchange_id.as_str()),
100 Venue::from(exchange_id.as_str()),
101 exchange.borrow().oms_type,
102 account_id,
103 account_type,
104 base_currency,
105 cache.clone(),
106 );
107
108 let factory = OrderEventFactory::new(trader_id, account_id, account_type, base_currency);
109
110 Self {
111 core,
112 factory,
113 exchange: exchange_shared.downgrade(),
114 cache,
115 clock,
116 queued_events: Rc::new(RefCell::new(Vec::new())),
117 routing,
118 _frozen_account: frozen_account,
119 }
120 }
121
122 fn get_order(&self, client_order_id: ClientOrderId) -> anyhow::Result<OrderAny> {
123 Ok(self.cache.borrow().try_order_owned(&client_order_id)?)
124 }
125
126 pub fn drain_queued_events(&self) {
128 let events: Vec<OrderEventAny> = self.queued_events.borrow_mut().drain(..).collect();
129 let endpoint = MessagingSwitchboard::exec_engine_process();
130 for event in events {
131 msgbus::send_order_event(endpoint, event);
132 }
133 }
134}
135
136#[async_trait(?Send)]
137impl ExecutionClient for BacktestExecutionClient {
138 fn is_connected(&self) -> bool {
139 self.core.is_connected()
140 }
141
142 fn client_id(&self) -> ClientId {
143 self.core.client_id
144 }
145
146 fn account_id(&self) -> AccountId {
147 self.core.account_id
148 }
149
150 fn venue(&self) -> Venue {
151 self.core.venue
152 }
153
154 fn oms_type(&self) -> OmsType {
155 self.core.oms_type
156 }
157
158 fn get_account(&self) -> Option<AccountAny> {
159 self.cache.borrow().account_owned(&self.core.account_id)
160 }
161
162 fn generate_account_state(
163 &self,
164 balances: Vec<AccountBalance>,
165 margins: Vec<MarginBalance>,
166 reported: bool,
167 ts_event: UnixNanos,
168 ) -> anyhow::Result<()> {
169 let ts_init = self.clock.borrow().timestamp_ns();
170 let state = self
171 .factory
172 .generate_account_state(balances, margins, reported, ts_event, ts_init);
173 let endpoint = MessagingSwitchboard::portfolio_update_account();
174 msgbus::send_account_state(endpoint, &state);
175 Ok(())
176 }
177
178 fn start(&mut self) -> anyhow::Result<()> {
179 self.core.set_connected();
180 log::info!("Backtest execution client started");
181 Ok(())
182 }
183
184 fn stop(&mut self) -> anyhow::Result<()> {
185 self.core.set_disconnected();
186 log::info!("Backtest execution client stopped");
187 Ok(())
188 }
189
190 fn submit_order(&self, cmd: SubmitOrder) -> anyhow::Result<()> {
191 let order = self.get_order(cmd.client_order_id)?;
194 let ts_init = self.clock.borrow().timestamp_ns();
195 let event = self.factory.generate_order_submitted(&order, ts_init);
196 self.queued_events.borrow_mut().push(event);
197
198 if let Some(exchange) = self.exchange.upgrade() {
199 exchange.borrow_mut().send(TradingCommand::SubmitOrder(cmd));
200 } else {
201 log::error!("submit_order: SimulatedExchange has been dropped");
202 }
203 Ok(())
204 }
205
206 fn submit_order_list(&self, cmd: SubmitOrderList) -> anyhow::Result<()> {
207 let ts_init = self.clock.borrow().timestamp_ns();
208
209 let orders: Vec<OrderAny> = self
210 .cache
211 .borrow()
212 .orders_for_ids(&cmd.order_list.client_order_ids, &cmd);
213
214 let mut queued = self.queued_events.borrow_mut();
216
217 for order in &orders {
218 let event = self.factory.generate_order_submitted(order, ts_init);
219 queued.push(event);
220 }
221 drop(queued);
222
223 if let Some(exchange) = self.exchange.upgrade() {
224 exchange
225 .borrow_mut()
226 .send(TradingCommand::SubmitOrderList(cmd));
227 } else {
228 log::error!("submit_order_list: SimulatedExchange has been dropped");
229 }
230 Ok(())
231 }
232
233 fn modify_order(&self, cmd: ModifyOrder) -> anyhow::Result<()> {
234 if let Some(exchange) = self.exchange.upgrade() {
235 exchange.borrow_mut().send(TradingCommand::ModifyOrder(cmd));
236 } else {
237 log::error!("modify_order: SimulatedExchange has been dropped");
238 }
239 Ok(())
240 }
241
242 fn batch_modify_orders(&self, cmd: BatchModifyOrders) -> anyhow::Result<()> {
243 if let Some(exchange) = self.exchange.upgrade() {
244 exchange
245 .borrow_mut()
246 .send(TradingCommand::ModifyOrders(cmd));
247 } else {
248 log::error!("batch_modify_orders: SimulatedExchange has been dropped");
249 }
250 Ok(())
251 }
252
253 fn cancel_order(&self, cmd: CancelOrder) -> anyhow::Result<()> {
254 if let Some(exchange) = self.exchange.upgrade() {
255 exchange.borrow_mut().send(TradingCommand::CancelOrder(cmd));
256 } else {
257 log::error!("cancel_order: SimulatedExchange has been dropped");
258 }
259 Ok(())
260 }
261
262 fn cancel_all_orders(&self, cmd: CancelAllOrders) -> anyhow::Result<()> {
263 if let Some(exchange) = self.exchange.upgrade() {
264 exchange
265 .borrow_mut()
266 .send(TradingCommand::CancelAllOrders(cmd));
267 } else {
268 log::error!("cancel_all_orders: SimulatedExchange has been dropped");
269 }
270 Ok(())
271 }
272
273 fn batch_cancel_orders(&self, cmd: BatchCancelOrders) -> anyhow::Result<()> {
274 if let Some(exchange) = self.exchange.upgrade() {
275 exchange
276 .borrow_mut()
277 .send(TradingCommand::CancelOrders(cmd));
278 } else {
279 log::error!("batch_cancel_orders: SimulatedExchange has been dropped");
280 }
281 Ok(())
282 }
283
284 fn query_account(&self, cmd: QueryAccount) -> anyhow::Result<()> {
285 if let Some(exchange) = self.exchange.upgrade() {
286 exchange
287 .borrow_mut()
288 .send(TradingCommand::QueryAccount(cmd));
289 } else {
290 log::error!("query_account: SimulatedExchange has been dropped");
291 }
292 Ok(())
293 }
294
295 fn query_order(&self, cmd: QueryOrder) -> anyhow::Result<()> {
296 if let Some(exchange) = self.exchange.upgrade() {
297 exchange.borrow_mut().send(TradingCommand::QueryOrder(cmd));
298 } else {
299 log::error!("query_order: SimulatedExchange has been dropped");
300 }
301 Ok(())
302 }
303}