nautilus_execution/models/
latency.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
16use std::fmt::{Debug, Display};
17
18use nautilus_core::UnixNanos;
19
20/// Trait for latency models used in backtesting.
21///
22/// Latency models simulate network delays for order operations during backtesting.
23/// Implementations can provide static or dynamic (jittered) latency values.
24pub trait LatencyModel: Debug {
25    /// Returns the latency for order insertion operations.
26    fn get_insert_latency(&self) -> UnixNanos;
27
28    /// Returns the latency for order update/modify operations.
29    fn get_update_latency(&self) -> UnixNanos;
30
31    /// Returns the latency for order delete/cancel operations.
32    fn get_delete_latency(&self) -> UnixNanos;
33
34    /// Returns the base latency component.
35    fn get_base_latency(&self) -> UnixNanos;
36}
37
38/// Static latency model with fixed latency values.
39///
40/// Models the latency for different order operations including base network latency
41/// and specific operation latencies for insert, update, and delete operations.
42///
43/// The base latency is automatically added to each operation latency, matching
44/// Python's behavior. For example, if `base_latency_nanos = 100ms` and
45/// `insert_latency_nanos = 200ms`, the effective insert latency will be 300ms.
46#[derive(Debug, Clone)]
47pub struct StaticLatencyModel {
48    base_latency_nanos: UnixNanos,
49    insert_latency_nanos: UnixNanos,
50    update_latency_nanos: UnixNanos,
51    delete_latency_nanos: UnixNanos,
52}
53
54impl StaticLatencyModel {
55    /// Creates a new [`StaticLatencyModel`] instance.
56    ///
57    /// The base latency is added to each operation latency to get the effective latency.
58    ///
59    /// # Arguments
60    ///
61    /// * `base_latency_nanos` - Base network latency added to all operations
62    /// * `insert_latency_nanos` - Additional latency for order insertion
63    /// * `update_latency_nanos` - Additional latency for order updates
64    /// * `delete_latency_nanos` - Additional latency for order cancellation
65    #[must_use]
66    pub fn new(
67        base_latency_nanos: UnixNanos,
68        insert_latency_nanos: UnixNanos,
69        update_latency_nanos: UnixNanos,
70        delete_latency_nanos: UnixNanos,
71    ) -> Self {
72        Self {
73            base_latency_nanos,
74            insert_latency_nanos: UnixNanos::from(
75                base_latency_nanos.as_u64() + insert_latency_nanos.as_u64(),
76            ),
77            update_latency_nanos: UnixNanos::from(
78                base_latency_nanos.as_u64() + update_latency_nanos.as_u64(),
79            ),
80            delete_latency_nanos: UnixNanos::from(
81                base_latency_nanos.as_u64() + delete_latency_nanos.as_u64(),
82            ),
83        }
84    }
85}
86
87impl LatencyModel for StaticLatencyModel {
88    fn get_insert_latency(&self) -> UnixNanos {
89        self.insert_latency_nanos
90    }
91
92    fn get_update_latency(&self) -> UnixNanos {
93        self.update_latency_nanos
94    }
95
96    fn get_delete_latency(&self) -> UnixNanos {
97        self.delete_latency_nanos
98    }
99
100    fn get_base_latency(&self) -> UnixNanos {
101        self.base_latency_nanos
102    }
103}
104
105impl Display for StaticLatencyModel {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        write!(f, "LatencyModel()")
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use rstest::rstest;
114
115    use super::*;
116
117    #[rstest]
118    fn test_static_latency_model() {
119        let model = StaticLatencyModel::new(
120            UnixNanos::from(1_000_000),
121            UnixNanos::from(2_000_000),
122            UnixNanos::from(3_000_000),
123            UnixNanos::from(4_000_000),
124        );
125
126        // Base is added to each operation latency
127        assert_eq!(model.get_insert_latency().as_u64(), 3_000_000);
128        assert_eq!(model.get_update_latency().as_u64(), 4_000_000);
129        assert_eq!(model.get_delete_latency().as_u64(), 5_000_000);
130        assert_eq!(model.get_base_latency().as_u64(), 1_000_000);
131    }
132}