Skip to main content

nautilus_backtest/
data_client.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 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//! Provides a `BacktestDataClient` implementation for backtesting.
17
18use std::{cell::RefCell, rc::Rc};
19
20use nautilus_common::{
21    cache::Cache,
22    clients::DataClient,
23    messages::data::{
24        RequestBars, RequestBookSnapshot, RequestCustomData, RequestForwardPrices,
25        RequestInstrument, RequestInstruments, RequestQuotes, RequestTrades, SubscribeBars,
26        SubscribeBookDeltas, SubscribeBookDepth10, SubscribeCustomData, SubscribeIndexPrices,
27        SubscribeInstrument, SubscribeInstrumentClose, SubscribeInstrumentStatus,
28        SubscribeInstruments, SubscribeMarkPrices, SubscribeQuotes, SubscribeTrades,
29        UnsubscribeBars, UnsubscribeBookDeltas, UnsubscribeBookDepth10, UnsubscribeCustomData,
30        UnsubscribeIndexPrices, UnsubscribeInstrument, UnsubscribeInstrumentClose,
31        UnsubscribeInstrumentStatus, UnsubscribeInstruments, UnsubscribeMarkPrices,
32        UnsubscribeQuotes, UnsubscribeTrades,
33    },
34};
35use nautilus_model::identifiers::{ClientId, Venue};
36
37/// Data client implementation for backtesting market data operations.
38///
39/// The `BacktestDataClient` provides a data client interface specifically designed
40/// for backtesting environments. It handles market data subscriptions and requests
41/// during backtesting, coordinating with the backtesting engine to provide
42/// historical data replay functionality.
43#[derive(Debug)]
44pub struct BacktestDataClient {
45    pub client_id: ClientId,
46    pub venue: Venue,
47    _cache: Rc<RefCell<Cache>>,
48}
49
50impl BacktestDataClient {
51    /// Creates a new [`BacktestDataClient`] instance.
52    #[must_use]
53    pub const fn new(client_id: ClientId, venue: Venue, cache: Rc<RefCell<Cache>>) -> Self {
54        Self {
55            client_id,
56            venue,
57            _cache: cache,
58        }
59    }
60}
61
62#[async_trait::async_trait(?Send)]
63impl DataClient for BacktestDataClient {
64    fn client_id(&self) -> ClientId {
65        self.client_id
66    }
67
68    fn venue(&self) -> Option<Venue> {
69        Some(self.venue)
70    }
71
72    fn start(&mut self) -> anyhow::Result<()> {
73        Ok(())
74    }
75
76    fn stop(&mut self) -> anyhow::Result<()> {
77        Ok(())
78    }
79
80    fn reset(&mut self) -> anyhow::Result<()> {
81        Ok(())
82    }
83
84    fn dispose(&mut self) -> anyhow::Result<()> {
85        Ok(())
86    }
87
88    fn is_connected(&self) -> bool {
89        true
90    }
91
92    fn is_disconnected(&self) -> bool {
93        false
94    }
95
96    fn subscribe(&mut self, _cmd: SubscribeCustomData) -> anyhow::Result<()> {
97        Ok(())
98    }
99
100    fn subscribe_instruments(&mut self, _cmd: SubscribeInstruments) -> anyhow::Result<()> {
101        Ok(())
102    }
103
104    fn subscribe_instrument(&mut self, _cmd: SubscribeInstrument) -> anyhow::Result<()> {
105        Ok(())
106    }
107
108    fn subscribe_book_deltas(&mut self, _cmd: SubscribeBookDeltas) -> anyhow::Result<()> {
109        Ok(())
110    }
111
112    fn subscribe_book_depth10(&mut self, _cmd: SubscribeBookDepth10) -> anyhow::Result<()> {
113        Ok(())
114    }
115
116    fn subscribe_quotes(&mut self, _cmd: SubscribeQuotes) -> anyhow::Result<()> {
117        Ok(())
118    }
119
120    fn subscribe_trades(&mut self, _cmd: SubscribeTrades) -> anyhow::Result<()> {
121        Ok(())
122    }
123
124    fn subscribe_bars(&mut self, _cmd: SubscribeBars) -> anyhow::Result<()> {
125        Ok(())
126    }
127
128    fn subscribe_mark_prices(&mut self, _cmd: SubscribeMarkPrices) -> anyhow::Result<()> {
129        Ok(())
130    }
131
132    fn subscribe_index_prices(&mut self, _cmd: SubscribeIndexPrices) -> anyhow::Result<()> {
133        Ok(())
134    }
135
136    fn subscribe_instrument_status(
137        &mut self,
138        _cmd: SubscribeInstrumentStatus,
139    ) -> anyhow::Result<()> {
140        Ok(())
141    }
142
143    fn subscribe_instrument_close(&mut self, _cmd: SubscribeInstrumentClose) -> anyhow::Result<()> {
144        Ok(())
145    }
146
147    fn unsubscribe(&mut self, _cmd: &UnsubscribeCustomData) -> anyhow::Result<()> {
148        Ok(())
149    }
150
151    fn unsubscribe_instruments(&mut self, _cmd: &UnsubscribeInstruments) -> anyhow::Result<()> {
152        Ok(())
153    }
154
155    fn unsubscribe_instrument(&mut self, _cmd: &UnsubscribeInstrument) -> anyhow::Result<()> {
156        Ok(())
157    }
158
159    fn unsubscribe_book_deltas(&mut self, _cmd: &UnsubscribeBookDeltas) -> anyhow::Result<()> {
160        Ok(())
161    }
162
163    fn unsubscribe_book_depth10(&mut self, _cmd: &UnsubscribeBookDepth10) -> anyhow::Result<()> {
164        Ok(())
165    }
166
167    fn unsubscribe_quotes(&mut self, _cmd: &UnsubscribeQuotes) -> anyhow::Result<()> {
168        Ok(())
169    }
170
171    fn unsubscribe_trades(&mut self, _cmd: &UnsubscribeTrades) -> anyhow::Result<()> {
172        Ok(())
173    }
174
175    fn unsubscribe_bars(&mut self, _cmd: &UnsubscribeBars) -> anyhow::Result<()> {
176        Ok(())
177    }
178
179    fn unsubscribe_mark_prices(&mut self, _cmd: &UnsubscribeMarkPrices) -> anyhow::Result<()> {
180        Ok(())
181    }
182
183    fn unsubscribe_index_prices(&mut self, _cmd: &UnsubscribeIndexPrices) -> anyhow::Result<()> {
184        Ok(())
185    }
186
187    fn unsubscribe_instrument_status(
188        &mut self,
189        _cmd: &UnsubscribeInstrumentStatus,
190    ) -> anyhow::Result<()> {
191        Ok(())
192    }
193
194    fn unsubscribe_instrument_close(
195        &mut self,
196        _cmd: &UnsubscribeInstrumentClose,
197    ) -> anyhow::Result<()> {
198        Ok(())
199    }
200
201    fn request_data(&self, _request: RequestCustomData) -> anyhow::Result<()> {
202        // No-op in backtest: data is replayed by the engine
203        Ok(())
204    }
205
206    fn request_instruments(&self, _request: RequestInstruments) -> anyhow::Result<()> {
207        // No-op in backtest: instruments are pre-loaded
208        Ok(())
209    }
210
211    fn request_instrument(&self, _request: RequestInstrument) -> anyhow::Result<()> {
212        // No-op in backtest: instruments are pre-loaded
213        Ok(())
214    }
215
216    fn request_book_snapshot(&self, _request: RequestBookSnapshot) -> anyhow::Result<()> {
217        // No-op in backtest
218        Ok(())
219    }
220
221    fn request_quotes(&self, _request: RequestQuotes) -> anyhow::Result<()> {
222        // No-op in backtest: quotes are replayed by the engine
223        Ok(())
224    }
225
226    fn request_trades(&self, _request: RequestTrades) -> anyhow::Result<()> {
227        // No-op in backtest: trades are replayed by the engine
228        Ok(())
229    }
230
231    fn request_bars(&self, _request: RequestBars) -> anyhow::Result<()> {
232        // No-op in backtest: bars are replayed by the engine
233        Ok(())
234    }
235
236    fn request_forward_prices(&self, _request: RequestForwardPrices) -> anyhow::Result<()> {
237        // No live ATM source in backtest; return Err so the engine fallback
238        // creates the option-chain manager without an initial ATM price.
239        anyhow::bail!("backtest data client cannot fetch forward prices")
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use nautilus_core::{UUID4, UnixNanos};
246    use rstest::rstest;
247    use ustr::Ustr;
248
249    use super::*;
250
251    #[rstest]
252    fn test_request_forward_prices_returns_err_for_engine_fallback() {
253        let client_id = ClientId::new("BACKTEST");
254        let venue = Venue::new("BACKTEST");
255        let cache = Rc::new(RefCell::new(Cache::default()));
256        let client = BacktestDataClient::new(client_id, venue, cache);
257
258        let request = RequestForwardPrices::new(
259            venue,
260            Ustr::from("BTC"),
261            None,
262            Some(client_id),
263            UUID4::new(),
264            UnixNanos::default(),
265            None,
266        );
267
268        let result = client.request_forward_prices(request);
269        assert!(result.is_err());
270        let msg = result.unwrap_err().to_string();
271        assert!(msg.contains("backtest data client"));
272    }
273}