nautilus_backtest/
execution_client.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16// Under development
17#![allow(dead_code)]
18#![allow(unused_variables)]
19
20//! Provides a `BacktestExecutionClient` implementation for backtesting.
21
22use std::{cell::RefCell, rc::Rc};
23
24use nautilus_common::{cache::Cache, clock::Clock};
25use nautilus_core::UnixNanos;
26use nautilus_execution::{
27    client::{ExecutionClient, base::BaseExecutionClient},
28    messages::{
29        BatchCancelOrders, CancelAllOrders, CancelOrder, ModifyOrder, QueryOrder, SubmitOrder,
30        SubmitOrderList, TradingCommand,
31    },
32};
33use nautilus_model::{
34    accounts::AccountAny,
35    enums::OmsType,
36    identifiers::{AccountId, ClientId, TraderId, Venue},
37    orders::Order,
38    types::{AccountBalance, MarginBalance},
39};
40
41use crate::exchange::SimulatedExchange;
42
43pub struct BacktestExecutionClient {
44    base: BaseExecutionClient,
45    exchange: Rc<RefCell<SimulatedExchange>>,
46    clock: Rc<RefCell<dyn Clock>>,
47    is_connected: bool,
48    routing: bool,
49    frozen_account: bool,
50}
51
52impl BacktestExecutionClient {
53    #[allow(clippy::too_many_arguments)]
54    pub fn new(
55        trader_id: TraderId,
56        account_id: AccountId,
57        exchange: Rc<RefCell<SimulatedExchange>>,
58        cache: Rc<RefCell<Cache>>,
59        clock: Rc<RefCell<dyn Clock>>,
60        routing: Option<bool>,
61        frozen_account: Option<bool>,
62    ) -> Self {
63        let routing = routing.unwrap_or(false);
64        let frozen_account = frozen_account.unwrap_or(false);
65        let exchange_id = exchange.borrow().id;
66        let base_client = BaseExecutionClient::new(
67            trader_id,
68            ClientId::from(exchange_id.as_str()),
69            Venue::from(exchange_id.as_str()),
70            exchange.borrow().oms_type,
71            account_id,
72            exchange.borrow().account_type,
73            exchange.borrow().base_currency,
74            clock.clone(),
75            cache,
76        );
77
78        if !frozen_account {
79            // TODO Register calculated account
80        }
81
82        Self {
83            exchange,
84            clock,
85            base: base_client,
86            is_connected: false,
87            routing,
88            frozen_account,
89        }
90    }
91}
92
93impl ExecutionClient for BacktestExecutionClient {
94    fn is_connected(&self) -> bool {
95        self.is_connected
96    }
97
98    fn client_id(&self) -> ClientId {
99        self.base.client_id
100    }
101
102    fn account_id(&self) -> AccountId {
103        self.base.account_id
104    }
105
106    fn venue(&self) -> Venue {
107        self.base.venue
108    }
109
110    fn oms_type(&self) -> OmsType {
111        self.base.oms_type
112    }
113
114    fn get_account(&self) -> Option<AccountAny> {
115        self.base.get_account()
116    }
117
118    fn generate_account_state(
119        &self,
120        balances: Vec<AccountBalance>,
121        margins: Vec<MarginBalance>,
122        reported: bool,
123        ts_event: UnixNanos,
124    ) -> anyhow::Result<()> {
125        self.base
126            .generate_account_state(balances, margins, reported, ts_event)
127    }
128
129    fn start(&mut self) -> anyhow::Result<()> {
130        self.is_connected = true;
131        log::info!("Backtest execution client started");
132        Ok(())
133    }
134
135    fn stop(&mut self) -> anyhow::Result<()> {
136        self.is_connected = false;
137        log::info!("Backtest execution client stopped");
138        Ok(())
139    }
140
141    fn submit_order(&self, command: SubmitOrder) -> anyhow::Result<()> {
142        self.base.generate_order_submitted(
143            command.strategy_id,
144            command.instrument_id,
145            command.client_order_id,
146            self.clock.borrow().timestamp_ns(),
147        );
148
149        self.exchange
150            .borrow_mut()
151            .send(TradingCommand::SubmitOrder(command));
152        Ok(())
153    }
154
155    fn submit_order_list(&self, command: SubmitOrderList) -> anyhow::Result<()> {
156        for order in &command.order_list.orders {
157            self.base.generate_order_submitted(
158                command.strategy_id,
159                order.instrument_id(),
160                order.client_order_id(),
161                self.clock.borrow().timestamp_ns(),
162            );
163        }
164
165        self.exchange
166            .borrow_mut()
167            .send(TradingCommand::SubmitOrderList(command));
168        Ok(())
169    }
170
171    fn modify_order(&self, command: ModifyOrder) -> anyhow::Result<()> {
172        self.exchange
173            .borrow_mut()
174            .send(TradingCommand::ModifyOrder(command));
175        Ok(())
176    }
177
178    fn cancel_order(&self, command: CancelOrder) -> anyhow::Result<()> {
179        self.exchange
180            .borrow_mut()
181            .send(TradingCommand::CancelOrder(command));
182        Ok(())
183    }
184
185    fn cancel_all_orders(&self, command: CancelAllOrders) -> anyhow::Result<()> {
186        self.exchange
187            .borrow_mut()
188            .send(TradingCommand::CancelAllOrders(command));
189        Ok(())
190    }
191
192    fn batch_cancel_orders(&self, command: BatchCancelOrders) -> anyhow::Result<()> {
193        self.exchange
194            .borrow_mut()
195            .send(TradingCommand::BatchCancelOrders(command));
196        Ok(())
197    }
198
199    fn query_order(&self, command: QueryOrder) -> anyhow::Result<()> {
200        self.exchange
201            .borrow_mut()
202            .send(TradingCommand::QueryOrder(command));
203        Ok(())
204    }
205}