nautilus_backtest/
execution_client.rs1#![allow(dead_code)]
18#![allow(unused_variables)]
19
20use std::{cell::RefCell, fmt::Debug, rc::Rc};
23
24use async_trait::async_trait;
25use nautilus_common::{
26 cache::Cache,
27 clock::Clock,
28 messages::execution::{
29 BatchCancelOrders, CancelAllOrders, CancelOrder, ModifyOrder, QueryAccount, QueryOrder,
30 SubmitOrder, SubmitOrderList, TradingCommand,
31 },
32};
33use nautilus_core::{SharedCell, UnixNanos, WeakCell};
34use nautilus_execution::client::{ExecutionClient, base::ExecutionClientCore};
35use nautilus_model::{
36 accounts::AccountAny,
37 enums::OmsType,
38 identifiers::{AccountId, ClientId, TraderId, Venue},
39 orders::Order,
40 types::{AccountBalance, MarginBalance},
41};
42
43use crate::exchange::SimulatedExchange;
44
45#[derive(Clone)]
52pub struct BacktestExecutionClient {
53 core: ExecutionClientCore,
54 exchange: WeakCell<SimulatedExchange>,
55 clock: Rc<RefCell<dyn Clock>>,
56 is_connected: bool,
57 routing: bool,
58 frozen_account: bool,
59}
60
61impl Debug for BacktestExecutionClient {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.debug_struct(stringify!(BacktestExecutionClient))
64 .field("client_id", &self.core.client_id)
65 .field("routing", &self.routing)
66 .finish()
67 }
68}
69
70impl BacktestExecutionClient {
71 #[allow(clippy::too_many_arguments)]
72 pub fn new(
73 trader_id: TraderId,
74 account_id: AccountId,
75 exchange: Rc<RefCell<SimulatedExchange>>,
76 cache: Rc<RefCell<Cache>>,
77 clock: Rc<RefCell<dyn Clock>>,
78 routing: Option<bool>,
79 frozen_account: Option<bool>,
80 ) -> Self {
81 let routing = routing.unwrap_or(false);
82 let frozen_account = frozen_account.unwrap_or(false);
83 let exchange_shared: SharedCell<SimulatedExchange> = SharedCell::from(exchange.clone());
84 let exchange_id = exchange_shared.borrow().id;
85 let core_client = ExecutionClientCore::new(
86 trader_id,
87 ClientId::from(exchange_id.as_str()),
88 Venue::from(exchange_id.as_str()),
89 exchange.borrow().oms_type,
90 account_id,
91 exchange.borrow().account_type,
92 exchange.borrow().base_currency,
93 clock.clone(),
94 cache,
95 );
96
97 if !frozen_account {
98 }
100
101 Self {
102 exchange: exchange_shared.downgrade(),
103 clock,
104 core: core_client,
105 is_connected: false,
106 routing,
107 frozen_account,
108 }
109 }
110}
111
112#[async_trait(?Send)]
113impl ExecutionClient for BacktestExecutionClient {
114 fn is_connected(&self) -> bool {
115 self.is_connected
116 }
117
118 fn client_id(&self) -> ClientId {
119 self.core.client_id
120 }
121
122 fn account_id(&self) -> AccountId {
123 self.core.account_id
124 }
125
126 fn venue(&self) -> Venue {
127 self.core.venue
128 }
129
130 fn oms_type(&self) -> OmsType {
131 self.core.oms_type
132 }
133
134 fn get_account(&self) -> Option<AccountAny> {
135 self.core.get_account()
136 }
137
138 fn generate_account_state(
139 &self,
140 balances: Vec<AccountBalance>,
141 margins: Vec<MarginBalance>,
142 reported: bool,
143 ts_event: UnixNanos,
144 ) -> anyhow::Result<()> {
145 self.core
146 .generate_account_state(balances, margins, reported, ts_event)
147 }
148
149 fn start(&mut self) -> anyhow::Result<()> {
150 self.is_connected = true;
151 log::info!("Backtest execution client started");
152 Ok(())
153 }
154
155 fn stop(&mut self) -> anyhow::Result<()> {
156 self.is_connected = false;
157 log::info!("Backtest execution client stopped");
158 Ok(())
159 }
160
161 fn submit_order(&self, cmd: &SubmitOrder) -> anyhow::Result<()> {
162 self.core.generate_order_submitted(
163 cmd.strategy_id,
164 cmd.instrument_id,
165 cmd.client_order_id(),
166 self.clock.borrow().timestamp_ns(),
167 );
168
169 if let Some(exchange) = self.exchange.upgrade() {
170 exchange
171 .borrow_mut()
172 .send(TradingCommand::SubmitOrder(cmd.clone()));
173 } else {
174 log::error!("submit_order: SimulatedExchange has been dropped");
175 }
176 Ok(())
177 }
178
179 fn submit_order_list(&self, cmd: &SubmitOrderList) -> anyhow::Result<()> {
180 for order in &cmd.order_list.orders {
181 self.core.generate_order_submitted(
182 cmd.strategy_id,
183 order.instrument_id(),
184 order.client_order_id(),
185 self.clock.borrow().timestamp_ns(),
186 );
187 }
188
189 if let Some(exchange) = self.exchange.upgrade() {
190 exchange
191 .borrow_mut()
192 .send(TradingCommand::SubmitOrderList(cmd.clone()));
193 } else {
194 log::error!("submit_order_list: SimulatedExchange has been dropped");
195 }
196 Ok(())
197 }
198
199 fn modify_order(&self, cmd: &ModifyOrder) -> anyhow::Result<()> {
200 if let Some(exchange) = self.exchange.upgrade() {
201 exchange
202 .borrow_mut()
203 .send(TradingCommand::ModifyOrder(cmd.clone()));
204 } else {
205 log::error!("modify_order: SimulatedExchange has been dropped");
206 }
207 Ok(())
208 }
209
210 fn cancel_order(&self, cmd: &CancelOrder) -> anyhow::Result<()> {
211 if let Some(exchange) = self.exchange.upgrade() {
212 exchange
213 .borrow_mut()
214 .send(TradingCommand::CancelOrder(cmd.clone()));
215 } else {
216 log::error!("cancel_order: SimulatedExchange has been dropped");
217 }
218 Ok(())
219 }
220
221 fn cancel_all_orders(&self, cmd: &CancelAllOrders) -> anyhow::Result<()> {
222 if let Some(exchange) = self.exchange.upgrade() {
223 exchange
224 .borrow_mut()
225 .send(TradingCommand::CancelAllOrders(cmd.clone()));
226 } else {
227 log::error!("cancel_all_orders: SimulatedExchange has been dropped");
228 }
229 Ok(())
230 }
231
232 fn batch_cancel_orders(&self, cmd: &BatchCancelOrders) -> anyhow::Result<()> {
233 if let Some(exchange) = self.exchange.upgrade() {
234 exchange
235 .borrow_mut()
236 .send(TradingCommand::BatchCancelOrders(cmd.clone()));
237 } else {
238 log::error!("batch_cancel_orders: SimulatedExchange has been dropped");
239 }
240 Ok(())
241 }
242
243 fn query_account(&self, cmd: &QueryAccount) -> anyhow::Result<()> {
244 if let Some(exchange) = self.exchange.upgrade() {
245 exchange
246 .borrow_mut()
247 .send(TradingCommand::QueryAccount(cmd.clone()));
248 } else {
249 log::error!("query_account: SimulatedExchange has been dropped");
250 }
251 Ok(())
252 }
253
254 fn query_order(&self, cmd: &QueryOrder) -> anyhow::Result<()> {
255 if let Some(exchange) = self.exchange.upgrade() {
256 exchange
257 .borrow_mut()
258 .send(TradingCommand::QueryOrder(cmd.clone()));
259 } else {
260 log::error!("query_order: SimulatedExchange has been dropped");
261 }
262 Ok(())
263 }
264}