nautilus_execution/models/
latency.rs1use std::{
17 fmt::{Debug, Display},
18 rc::Rc,
19};
20
21use nautilus_core::UnixNanos;
22
23pub trait LatencyModel: Debug {
28 fn get_insert_latency(&self) -> UnixNanos;
30
31 fn get_update_latency(&self) -> UnixNanos;
33
34 fn get_delete_latency(&self) -> UnixNanos;
36
37 fn get_base_latency(&self) -> UnixNanos;
39}
40
41#[derive(Clone)]
43pub struct LatencyModelHandle(Rc<dyn LatencyModel>);
44
45impl LatencyModelHandle {
46 #[must_use]
48 pub fn new<T>(model: T) -> Self
49 where
50 T: LatencyModel + 'static,
51 {
52 Self(Rc::new(model))
53 }
54
55 #[must_use]
57 pub fn from_rc(model: Rc<dyn LatencyModel>) -> Self {
58 Self(model)
59 }
60}
61
62impl Debug for LatencyModelHandle {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 f.debug_tuple(stringify!(LatencyModelHandle))
65 .field(&"<dyn LatencyModel>")
66 .finish()
67 }
68}
69
70impl LatencyModel for LatencyModelHandle {
71 fn get_insert_latency(&self) -> UnixNanos {
72 self.0.get_insert_latency()
73 }
74
75 fn get_update_latency(&self) -> UnixNanos {
76 self.0.get_update_latency()
77 }
78
79 fn get_delete_latency(&self) -> UnixNanos {
80 self.0.get_delete_latency()
81 }
82
83 fn get_base_latency(&self) -> UnixNanos {
84 self.0.get_base_latency()
85 }
86}
87
88#[derive(Debug, Clone)]
89pub enum LatencyModelAny {
90 Static(StaticLatencyModel),
91}
92
93impl LatencyModel for LatencyModelAny {
94 fn get_insert_latency(&self) -> UnixNanos {
95 match self {
96 Self::Static(model) => model.get_insert_latency(),
97 }
98 }
99
100 fn get_update_latency(&self) -> UnixNanos {
101 match self {
102 Self::Static(model) => model.get_update_latency(),
103 }
104 }
105
106 fn get_delete_latency(&self) -> UnixNanos {
107 match self {
108 Self::Static(model) => model.get_delete_latency(),
109 }
110 }
111
112 fn get_base_latency(&self) -> UnixNanos {
113 match self {
114 Self::Static(model) => model.get_base_latency(),
115 }
116 }
117}
118
119impl From<LatencyModelAny> for LatencyModelHandle {
120 fn from(model: LatencyModelAny) -> Self {
121 Self::new(model)
122 }
123}
124
125#[derive(Debug, Clone)]
134#[cfg_attr(
135 feature = "python",
136 pyo3::pyclass(module = "nautilus_trader.execution", unsendable, from_py_object)
137)]
138#[cfg_attr(
139 feature = "python",
140 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.execution")
141)]
142#[allow(
143 clippy::struct_field_names,
144 reason = "latency_nanos suffix consistently identifies latency types"
145)]
146pub struct StaticLatencyModel {
147 base_latency_nanos: UnixNanos,
148 insert_latency_nanos: UnixNanos,
149 update_latency_nanos: UnixNanos,
150 delete_latency_nanos: UnixNanos,
151}
152
153impl StaticLatencyModel {
154 #[must_use]
165 pub fn new(
166 base_latency_nanos: UnixNanos,
167 insert_latency_nanos: UnixNanos,
168 update_latency_nanos: UnixNanos,
169 delete_latency_nanos: UnixNanos,
170 ) -> Self {
171 Self {
172 base_latency_nanos,
173 insert_latency_nanos: UnixNanos::from(
174 base_latency_nanos.as_u64() + insert_latency_nanos.as_u64(),
175 ),
176 update_latency_nanos: UnixNanos::from(
177 base_latency_nanos.as_u64() + update_latency_nanos.as_u64(),
178 ),
179 delete_latency_nanos: UnixNanos::from(
180 base_latency_nanos.as_u64() + delete_latency_nanos.as_u64(),
181 ),
182 }
183 }
184}
185
186impl LatencyModel for StaticLatencyModel {
187 fn get_insert_latency(&self) -> UnixNanos {
188 self.insert_latency_nanos
189 }
190
191 fn get_update_latency(&self) -> UnixNanos {
192 self.update_latency_nanos
193 }
194
195 fn get_delete_latency(&self) -> UnixNanos {
196 self.delete_latency_nanos
197 }
198
199 fn get_base_latency(&self) -> UnixNanos {
200 self.base_latency_nanos
201 }
202}
203
204impl Display for StaticLatencyModel {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 write!(f, "LatencyModel()")
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use rstest::rstest;
213
214 use super::*;
215
216 #[derive(Debug)]
217 struct CustomLatencyModel;
218
219 impl LatencyModel for CustomLatencyModel {
220 fn get_insert_latency(&self) -> UnixNanos {
221 UnixNanos::from(11)
222 }
223
224 fn get_update_latency(&self) -> UnixNanos {
225 UnixNanos::from(22)
226 }
227
228 fn get_delete_latency(&self) -> UnixNanos {
229 UnixNanos::from(33)
230 }
231
232 fn get_base_latency(&self) -> UnixNanos {
233 UnixNanos::from(44)
234 }
235 }
236
237 #[rstest]
238 fn test_latency_model_handle_calls_custom_model() {
239 let model: Rc<dyn LatencyModel> = Rc::new(CustomLatencyModel);
240 let handle = LatencyModelHandle::from_rc(model);
241 let cloned_handle = handle.clone();
242 drop(handle);
243
244 assert_eq!(cloned_handle.get_insert_latency(), UnixNanos::from(11));
245 assert_eq!(cloned_handle.get_update_latency(), UnixNanos::from(22));
246 assert_eq!(cloned_handle.get_delete_latency(), UnixNanos::from(33));
247 assert_eq!(cloned_handle.get_base_latency(), UnixNanos::from(44));
248 }
249
250 #[rstest]
251 fn test_latency_model_handle_from_any_preserves_model() {
252 let model = StaticLatencyModel::new(
253 UnixNanos::from(1),
254 UnixNanos::from(10),
255 UnixNanos::from(20),
256 UnixNanos::from(30),
257 );
258 let handle: LatencyModelHandle = LatencyModelAny::Static(model).into();
259
260 assert_eq!(handle.get_insert_latency(), UnixNanos::from(11));
261 assert_eq!(handle.get_update_latency(), UnixNanos::from(21));
262 assert_eq!(handle.get_delete_latency(), UnixNanos::from(31));
263 assert_eq!(handle.get_base_latency(), UnixNanos::from(1));
264 }
265
266 #[rstest]
267 fn test_static_latency_model() {
268 let model = StaticLatencyModel::new(
269 UnixNanos::from(1_000_000),
270 UnixNanos::from(2_000_000),
271 UnixNanos::from(3_000_000),
272 UnixNanos::from(4_000_000),
273 );
274
275 assert_eq!(model.get_insert_latency().as_u64(), 3_000_000);
277 assert_eq!(model.get_update_latency().as_u64(), 4_000_000);
278 assert_eq!(model.get_delete_latency().as_u64(), 5_000_000);
279 assert_eq!(model.get_base_latency().as_u64(), 1_000_000);
280 }
281}