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