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