Skip to main content

nautilus_backtest/
data_iterator.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//! Static backtest replay using the shared ordered batch iterator.
17
18use nautilus_data::replay::{DataIterator, ReplayPoll};
19use nautilus_model::data::{Data, DataBatch, DataRef};
20
21/// Multi-stream, time-ordered data iterator used by the backtest engine.
22#[derive(Debug, Default)]
23pub struct BacktestDataIterator {
24    iterator: DataIterator,
25}
26
27impl BacktestDataIterator {
28    /// Creates a new empty [`BacktestDataIterator`].
29    #[must_use]
30    pub fn new() -> Self {
31        Self {
32            iterator: DataIterator::new(),
33        }
34    }
35
36    /// Adds (or replaces) a named data stream.
37    ///
38    /// When `append_data` is true the stream gets lower priority on equal replay keys;
39    /// when false (prepend) it wins ties. Named replacements retain their original priority.
40    pub fn add_data(&mut self, name: &str, data: Vec<Data>, append_data: bool) {
41        self.iterator.add_data(name, data, append_data);
42    }
43
44    /// Adds (or replaces) a named typed data stream.
45    ///
46    /// Items are ordered by replay key before insertion. A batch that arrives out of order while
47    /// sharing its backing allocation with another view is copied once, so the shared allocation
48    /// is never reordered.
49    pub fn add_data_batch(&mut self, name: &str, data: DataBatch, append_data: bool) {
50        self.iterator.add_data_batch(name, data, append_data);
51    }
52
53    /// Removes a named data stream.
54    pub fn remove_data(&mut self, name: &str, complete_remove: bool) {
55        self.iterator.remove_data(name, complete_remove);
56    }
57
58    /// Sets the cursor of a named stream to `index` (0-based).
59    pub fn set_index(&mut self, name: &str, index: usize) {
60        self.iterator.set_index(name, index);
61    }
62
63    /// Resets all stream cursors to the beginning.
64    #[expect(
65        clippy::missing_panics_doc,
66        reason = "static replay cannot contain sources or moved book frames"
67    )]
68    pub fn reset_all_cursors(&mut self) {
69        self.iterator
70            .reset_all_cursors()
71            .expect("static replay has no sources or moved book frames");
72    }
73
74    pub(crate) fn peek(&mut self) -> Option<DataRef<'_>> {
75        static_item(self.iterator.peek())
76    }
77
78    pub(crate) fn advance(&mut self) {
79        self.iterator.advance();
80    }
81
82    /// Returns the next market [`Data`] element across all streams in chronological order.
83    #[expect(clippy::should_implement_trait)]
84    pub fn next(&mut self) -> Option<Data> {
85        static_item(self.iterator.next())
86    }
87
88    /// Returns whether all streams have been fully consumed.
89    #[must_use]
90    pub fn is_done(&self) -> bool {
91        self.iterator.is_done()
92    }
93}
94
95fn static_item<T>(poll: anyhow::Result<ReplayPoll<T>>) -> Option<T> {
96    match poll.expect("static replay cannot load or fail") {
97        ReplayPoll::Ready(data) => Some(data),
98        ReplayPoll::Complete => None,
99        ReplayPoll::Pending { .. } => unreachable!("static replay has no pending sources"),
100    }
101}