nautilus_backtest/
execution_client.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Posei Systems Pty Ltd. All rights reserved.
3//  https://poseitrader.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, fmt::Debug, rc::Rc};
23
24use nautilus_common::{
25    cache::Cache,
26    clock::Clock,
27    messages::execution::{
28        BatchCancelOrders, CancelAllOrders, CancelOrder, ModifyOrder, QueryOrder, SubmitOrder,
29        SubmitOrderList, TradingCommand,
30    },
31};
32use nautilus_core::UnixNanos;
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 {
45    base: BaseExecutionClient,
46    exchange: Rc<RefCell<SimulatedExchange>>,
47    clock: Rc<RefCell<dyn Clock>>,
48    is_connected: bool,
49    routing: bool,
50    frozen_account: bool,
51}
52
53impl Debug for BacktestExecutionClient {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        f.debug_struct(stringify!(BacktestExecutionClient))
56            .field("client_id", &self.base.client_id)
57            .field("routing", &self.routing)
58            .finish()
59    }
60}
61
62impl BacktestExecutionClient {
63    #[allow(clippy::too_many_arguments)]
64    pub fn new(
65        trader_id: TraderId,
66        account_id: AccountId,
67        exchange: Rc<RefCell<SimulatedExchange>>,
68        cache: Rc<RefCell<Cache>>,
69        clock: Rc<RefCell<dyn Clock>>,
70        routing: Option<bool>,
71        frozen_account: Option<bool>,
72    ) -> Self {
73        let routing = routing.unwrap_or(false);
74        let frozen_account = frozen_account.unwrap_or(false);
75        let exchange_id = exchange.borrow().id;
76        let base_client = BaseExecutionClient::new(
77            trader_id,
78            ClientId::from(exchange_id.as_str()),
79            Venue::from(exchange_id.as_str()),
80            exchange.borrow().oms_type,
81            account_id,
82            exchange.borrow().account_type,
83            exchange.borrow().base_currency,
84            clock.clone(),
85            cache,
86        );
87
88        if !frozen_account {
89            // TODO Register calculated account
90        }
91
92        Self {
93            exchange,
94            clock,
95            base: base_client,
96            is_connected: false,
97            routing,
98            frozen_account,
99        }
100    }
101}
102
103impl ExecutionClient for BacktestExecutionClient {
104    fn is_connected(&self) -> bool {
105        self.is_connected
106    }
107
108    fn client_id(&self) -> ClientId {
109        self.base.client_id
110    }
111
112    fn account_id(&self) -> AccountId {
113        self.base.account_id
114    }
115
116    fn venue(&self) -> Venue {
117        self.base.venue
118    }
119
120    fn oms_type(&self) -> OmsType {
121        self.base.oms_type
122    }
123
124    fn get_account(&self) -> Option<AccountAny> {
125        self.base.get_account()
126    }
127
128    fn generate_account_state(
129        &self,
130        balances: Vec<AccountBalance>,
131        margins: Vec<MarginBalance>,
132        reported: bool,
133        ts_event: UnixNanos,
134    ) -> anyhow::Result<()> {
135        self.base
136            .generate_account_state(balances, margins, reported, ts_event)
137    }
138
139    fn start(&mut self) -> anyhow::Result<()> {
140        self.is_connected = true;
141        log::info!("Backtest execution client started");
142        Ok(())
143    }
144
145    fn stop(&mut self) -> anyhow::Result<()> {
146        self.is_connected = false;
147        log::info!("Backtest execution client stopped");
148        Ok(())
149    }
150
151    fn submit_order(&self, cmd: &SubmitOrder) -> anyhow::Result<()> {
152        self.base.generate_order_submitted(
153            cmd.strategy_id,
154            cmd.instrument_id,
155            cmd.client_order_id,
156            self.clock.borrow().timestamp_ns(),
157        );
158
159        self.exchange
160            .borrow_mut()
161            .send(TradingCommand::SubmitOrder(cmd.clone())); // TODO: Remove this clone
162        Ok(())
163    }
164
165    fn submit_order_list(&self, cmd: &SubmitOrderList) -> anyhow::Result<()> {
166        for order in &cmd.order_list.orders {
167            self.base.generate_order_submitted(
168                cmd.strategy_id,
169                order.instrument_id(),
170                order.client_order_id(),
171                self.clock.borrow().timestamp_ns(),
172            );
173        }
174
175        self.exchange
176            .borrow_mut()
177            .send(TradingCommand::SubmitOrderList(cmd.clone()));
178        Ok(())
179    }
180
181    fn modify_order(&self, cmd: &ModifyOrder) -> anyhow::Result<()> {
182        self.exchange
183            .borrow_mut()
184            .send(TradingCommand::ModifyOrder(cmd.clone()));
185        Ok(())
186    }
187
188    fn cancel_order(&self, cmd: &CancelOrder) -> anyhow::Result<()> {
189        self.exchange
190            .borrow_mut()
191            .send(TradingCommand::CancelOrder(cmd.clone()));
192        Ok(())
193    }
194
195    fn cancel_all_orders(&self, cmd: &CancelAllOrders) -> anyhow::Result<()> {
196        self.exchange
197            .borrow_mut()
198            .send(TradingCommand::CancelAllOrders(cmd.clone()));
199        Ok(())
200    }
201
202    fn batch_cancel_orders(&self, cmd: &BatchCancelOrders) -> anyhow::Result<()> {
203        self.exchange
204            .borrow_mut()
205            .send(TradingCommand::BatchCancelOrders(cmd.clone()));
206        Ok(())
207    }
208
209    fn query_order(&self, cmd: &QueryOrder) -> anyhow::Result<()> {
210        self.exchange
211            .borrow_mut()
212            .send(TradingCommand::QueryOrder(cmd.clone()));
213        Ok(())
214    }
215}