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