1use std::{cell::RefCell, rc::Rc};
17
18use chrono::{DateTime, Duration, Utc};
19use nautilus_core::{UnixNanos, python::to_pyvalue_err};
20use pyo3::prelude::*;
21
22use crate::{
23 clock::{Clock, TestClock},
24 live::clock::LiveClock,
25 timer::TimeEventCallback,
26};
27
28#[allow(non_camel_case_types)]
38#[pyo3::pyclass(
39 module = "nautilus_trader.core.nautilus_pyo3.common",
40 name = "Clock",
41 unsendable,
42 from_py_object
43)]
44#[derive(Debug, Clone)]
45pub struct PyClock(Rc<RefCell<dyn Clock>>);
46
47#[pymethods]
48impl PyClock {
49 #[pyo3(name = "register_default_handler")]
50 fn py_register_default_handler(&mut self, callback: Py<PyAny>) {
51 self.0
52 .borrow_mut()
53 .register_default_handler(TimeEventCallback::from(callback));
54 }
55
56 #[pyo3(
57 name = "set_time_alert",
58 signature = (name, alert_time, callback=None, allow_past=None)
59 )]
60 fn py_set_time_alert(
61 &mut self,
62 name: &str,
63 alert_time: DateTime<Utc>,
64 callback: Option<Py<PyAny>>,
65 allow_past: Option<bool>,
66 ) -> PyResult<()> {
67 self.0
68 .borrow_mut()
69 .set_time_alert(
70 name,
71 alert_time,
72 callback.map(TimeEventCallback::from),
73 allow_past,
74 )
75 .map_err(to_pyvalue_err)
76 }
77
78 #[pyo3(
79 name = "set_time_alert_ns",
80 signature = (name, alert_time_ns, callback=None, allow_past=None)
81 )]
82 fn py_set_time_alert_ns(
83 &mut self,
84 name: &str,
85 alert_time_ns: u64,
86 callback: Option<Py<PyAny>>,
87 allow_past: Option<bool>,
88 ) -> PyResult<()> {
89 self.0
90 .borrow_mut()
91 .set_time_alert_ns(
92 name,
93 alert_time_ns.into(),
94 callback.map(TimeEventCallback::from),
95 allow_past,
96 )
97 .map_err(to_pyvalue_err)
98 }
99
100 #[allow(clippy::too_many_arguments)]
101 #[pyo3(
102 name = "set_timer",
103 signature = (name, interval, start_time=None, stop_time=None, callback=None, allow_past=None, fire_immediately=None)
104 )]
105 fn py_set_timer(
106 &mut self,
107 name: &str,
108 interval: Duration,
109 start_time: Option<DateTime<Utc>>,
110 stop_time: Option<DateTime<Utc>>,
111 callback: Option<Py<PyAny>>,
112 allow_past: Option<bool>,
113 fire_immediately: Option<bool>,
114 ) -> PyResult<()> {
115 let interval_ns_i64 = interval
116 .num_nanoseconds()
117 .ok_or_else(|| to_pyvalue_err("Interval too large"))?;
118 if interval_ns_i64 <= 0 {
119 return Err(to_pyvalue_err("Interval must be positive"));
120 }
121 let interval_ns = interval_ns_i64 as u64;
122
123 self.0
124 .borrow_mut()
125 .set_timer_ns(
126 name,
127 interval_ns,
128 start_time.map(UnixNanos::from),
129 stop_time.map(UnixNanos::from),
130 callback.map(TimeEventCallback::from),
131 allow_past,
132 fire_immediately,
133 )
134 .map_err(to_pyvalue_err)
135 }
136
137 #[allow(clippy::too_many_arguments)]
138 #[pyo3(
139 name = "set_timer_ns",
140 signature = (name, interval_ns, start_time_ns=None, stop_time_ns=None, callback=None, allow_past=None, fire_immediately=None)
141 )]
142 fn py_set_timer_ns(
143 &mut self,
144 name: &str,
145 interval_ns: u64,
146 start_time_ns: Option<u64>,
147 stop_time_ns: Option<u64>,
148 callback: Option<Py<PyAny>>,
149 allow_past: Option<bool>,
150 fire_immediately: Option<bool>,
151 ) -> PyResult<()> {
152 self.0
153 .borrow_mut()
154 .set_timer_ns(
155 name,
156 interval_ns,
157 start_time_ns.map(UnixNanos::from),
158 stop_time_ns.map(UnixNanos::from),
159 callback.map(TimeEventCallback::from),
160 allow_past,
161 fire_immediately,
162 )
163 .map_err(to_pyvalue_err)
164 }
165
166 #[pyo3(name = "next_time_ns")]
167 fn py_next_time_ns(&self, name: &str) -> Option<u64> {
168 self.0.borrow().next_time_ns(name).map(|t| t.as_u64())
169 }
170
171 #[pyo3(name = "cancel_timer")]
172 fn py_cancel_timer(&mut self, name: &str) {
173 self.0.borrow_mut().cancel_timer(name);
174 }
175
176 #[pyo3(name = "cancel_timers")]
177 fn py_cancel_timers(&mut self) {
178 self.0.borrow_mut().cancel_timers();
179 }
180}
181
182impl PyClock {
183 #[must_use]
185 pub fn from_rc(rc: Rc<RefCell<dyn Clock>>) -> Self {
186 Self(rc)
187 }
188
189 #[must_use]
191 pub fn new_test() -> Self {
192 Self(Rc::new(RefCell::new(TestClock::default())))
193 }
194
195 #[must_use]
197 pub fn new_live() -> Self {
198 Self(Rc::new(RefCell::new(LiveClock::default())))
199 }
200
201 #[must_use]
203 pub fn inner(&self) -> std::cell::Ref<'_, dyn Clock> {
204 self.0.borrow()
205 }
206
207 #[must_use]
209 pub fn inner_mut(&mut self) -> std::cell::RefMut<'_, dyn Clock> {
210 self.0.borrow_mut()
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use std::sync::Arc;
217
218 use chrono::{Duration, Utc};
219 use nautilus_core::{UnixNanos, python::IntoPyObjectNautilusExt};
220 use pyo3::{prelude::*, types::PyList};
221 use rstest::*;
222
223 use crate::{
224 clock::{Clock, TestClock},
225 python::clock::PyClock,
226 runner::{TimeEventSender, set_time_event_sender},
227 timer::{TimeEventCallback, TimeEventHandler},
228 };
229
230 fn ensure_sender() {
231 if crate::runner::try_get_time_event_sender().is_none() {
232 set_time_event_sender(Arc::new(DummySender));
233 }
234 }
235
236 #[derive(Debug)]
238 struct DummySender;
239
240 impl TimeEventSender for DummySender {
241 fn send(&self, _handler: TimeEventHandler) {}
242 }
243
244 #[fixture]
245 pub fn test_clock() -> TestClock {
246 TestClock::new()
247 }
248
249 pub fn test_callback() -> TimeEventCallback {
250 Python::initialize();
251 Python::attach(|py| {
252 let py_list = PyList::empty(py);
253 let py_append = Py::from(py_list.getattr("append").unwrap());
254 let py_append = py_append.into_py_any_unwrap(py);
255 TimeEventCallback::from(py_append)
256 })
257 }
258
259 pub fn test_py_callback() -> Py<PyAny> {
260 Python::initialize();
261 Python::attach(|py| {
262 let py_list = PyList::empty(py);
263 let py_append = Py::from(py_list.getattr("append").unwrap());
264 py_append.into_py_any_unwrap(py)
265 })
266 }
267
268 #[rstest]
273 fn test_test_clock_py_set_time_alert() {
274 Python::initialize();
275 Python::attach(|_py| {
276 let mut py_clock = PyClock::new_test();
277 let callback = test_py_callback();
278 py_clock.py_register_default_handler(callback);
279 let dt = Utc::now() + Duration::seconds(1);
280 py_clock
281 .py_set_time_alert("ALERT1", dt, None, None)
282 .expect("set_time_alert failed");
283 });
284 }
285
286 #[rstest]
287 fn test_test_clock_py_set_timer() {
288 Python::initialize();
289 Python::attach(|_py| {
290 let mut py_clock = PyClock::new_test();
291 let callback = test_py_callback();
292 py_clock.py_register_default_handler(callback);
293 let interval = Duration::seconds(2);
294 py_clock
295 .py_set_timer("TIMER1", interval, None, None, None, None, None)
296 .expect("set_timer failed");
297 });
298 }
299
300 #[rstest]
301 fn test_test_clock_py_set_time_alert_ns() {
302 Python::initialize();
303 Python::attach(|_py| {
304 let mut py_clock = PyClock::new_test();
305 let callback = test_py_callback();
306 py_clock.py_register_default_handler(callback);
307 let ts_ns = (Utc::now() + Duration::seconds(1))
308 .timestamp_nanos_opt()
309 .unwrap() as u64;
310 py_clock
311 .py_set_time_alert_ns("ALERT_NS", ts_ns, None, None)
312 .expect("set_time_alert_ns failed");
313 });
314 }
315
316 #[rstest]
317 fn test_test_clock_py_set_timer_ns() {
318 Python::initialize();
319 Python::attach(|_py| {
320 let mut py_clock = PyClock::new_test();
321 let callback = test_py_callback();
322 py_clock.py_register_default_handler(callback);
323 py_clock
324 .py_set_timer_ns("TIMER_NS", 1_000_000, None, None, None, None, None)
325 .expect("set_timer_ns failed");
326 });
327 }
328
329 #[rstest]
330 fn test_test_clock_raw_set_timer_ns(mut test_clock: TestClock) {
331 Python::initialize();
332 Python::attach(|_py| {
333 let callback = test_callback();
334 test_clock.register_default_handler(callback);
335
336 let timer_name = "TEST_TIME1";
337 test_clock
338 .set_timer_ns(timer_name, 10, None, None, None, None, None)
339 .unwrap();
340
341 assert_eq!(test_clock.timer_names(), [timer_name]);
342 assert_eq!(test_clock.timer_count(), 1);
343 });
344 }
345
346 #[rstest]
347 fn test_test_clock_cancel_timer(mut test_clock: TestClock) {
348 Python::initialize();
349 Python::attach(|_py| {
350 let callback = test_callback();
351 test_clock.register_default_handler(callback);
352
353 let timer_name = "TEST_TIME1";
354 test_clock
355 .set_timer_ns(timer_name, 10, None, None, None, None, None)
356 .unwrap();
357 test_clock.cancel_timer(timer_name);
358
359 assert!(test_clock.timer_names().is_empty());
360 assert_eq!(test_clock.timer_count(), 0);
361 });
362 }
363
364 #[rstest]
365 fn test_test_clock_cancel_timers(mut test_clock: TestClock) {
366 Python::initialize();
367 Python::attach(|_py| {
368 let callback = test_callback();
369 test_clock.register_default_handler(callback);
370
371 let timer_name = "TEST_TIME1";
372 test_clock
373 .set_timer_ns(timer_name, 10, None, None, None, None, None)
374 .unwrap();
375 test_clock.cancel_timers();
376
377 assert!(test_clock.timer_names().is_empty());
378 assert_eq!(test_clock.timer_count(), 0);
379 });
380 }
381
382 #[rstest]
383 fn test_test_clock_advance_within_stop_time_py(mut test_clock: TestClock) {
384 Python::initialize();
385 Python::attach(|_py| {
386 let callback = test_callback();
387 test_clock.register_default_handler(callback);
388
389 let timer_name = "TEST_TIME1";
390 test_clock
391 .set_timer_ns(
392 timer_name,
393 1,
394 Some(UnixNanos::from(1)),
395 Some(UnixNanos::from(3)),
396 None,
397 None,
398 None,
399 )
400 .unwrap();
401 test_clock.advance_time(2.into(), true);
402
403 assert_eq!(test_clock.timer_names(), [timer_name]);
404 assert_eq!(test_clock.timer_count(), 1);
405 });
406 }
407
408 #[rstest]
409 fn test_test_clock_advance_time_to_stop_time_with_set_time_true(mut test_clock: TestClock) {
410 Python::initialize();
411 Python::attach(|_py| {
412 let callback = test_callback();
413 test_clock.register_default_handler(callback);
414
415 test_clock
416 .set_timer_ns(
417 "TEST_TIME1",
418 2,
419 None,
420 Some(UnixNanos::from(3)),
421 None,
422 None,
423 None,
424 )
425 .unwrap();
426 test_clock.advance_time(3.into(), true);
427
428 assert_eq!(test_clock.timer_names().len(), 1);
429 assert_eq!(test_clock.timer_count(), 1);
430 assert_eq!(test_clock.get_time_ns(), 3);
431 });
432 }
433
434 #[rstest]
435 fn test_test_clock_advance_time_to_stop_time_with_set_time_false(mut test_clock: TestClock) {
436 Python::initialize();
437 Python::attach(|_py| {
438 let callback = test_callback();
439 test_clock.register_default_handler(callback);
440
441 test_clock
442 .set_timer_ns(
443 "TEST_TIME1",
444 2,
445 None,
446 Some(UnixNanos::from(3)),
447 None,
448 None,
449 None,
450 )
451 .unwrap();
452 test_clock.advance_time(3.into(), false);
453
454 assert_eq!(test_clock.timer_names().len(), 1);
455 assert_eq!(test_clock.timer_count(), 1);
456 assert_eq!(test_clock.get_time_ns(), 0);
457 });
458 }
459
460 #[rstest]
465 fn test_live_clock_py_set_time_alert() {
466 ensure_sender();
467
468 Python::initialize();
469 Python::attach(|_py| {
470 let mut py_clock = PyClock::new_live();
471 let callback = test_py_callback();
472 py_clock.py_register_default_handler(callback);
473 let dt = Utc::now() + Duration::seconds(1);
474
475 py_clock
476 .py_set_time_alert("ALERT1", dt, None, None)
477 .expect("live set_time_alert failed");
478 });
479 }
480
481 #[rstest]
482 fn test_live_clock_py_set_timer() {
483 ensure_sender();
484
485 Python::initialize();
486 Python::attach(|_py| {
487 let mut py_clock = PyClock::new_live();
488 let callback = test_py_callback();
489 py_clock.py_register_default_handler(callback);
490 let interval = Duration::seconds(3);
491
492 py_clock
493 .py_set_timer("TIMER1", interval, None, None, None, None, None)
494 .expect("live set_timer failed");
495 });
496 }
497
498 #[rstest]
499 fn test_live_clock_py_set_time_alert_ns() {
500 ensure_sender();
501
502 Python::initialize();
503 Python::attach(|_py| {
504 let mut py_clock = PyClock::new_live();
505 let callback = test_py_callback();
506 py_clock.py_register_default_handler(callback);
507 let dt_ns = (Utc::now() + Duration::seconds(1))
508 .timestamp_nanos_opt()
509 .unwrap() as u64;
510
511 py_clock
512 .py_set_time_alert_ns("ALERT_NS", dt_ns, None, None)
513 .expect("live set_time_alert_ns failed");
514 });
515 }
516
517 #[rstest]
518 fn test_live_clock_py_set_timer_ns() {
519 ensure_sender();
520
521 Python::initialize();
522 Python::attach(|_py| {
523 let mut py_clock = PyClock::new_live();
524 let callback = test_py_callback();
525 py_clock.py_register_default_handler(callback);
526 let interval_ns = 1_000_000_000_u64; py_clock
529 .py_set_timer_ns("TIMER_NS", interval_ns, None, None, None, None, None)
530 .expect("live set_timer_ns failed");
531 });
532 }
533}