nautilus_common/python/
indicators.rs1use std::{any::Any, fmt::Debug, rc::Rc};
17
18use nautilus_core::python::{IntoPyObjectNautilusExt, to_pytype_err};
19use nautilus_model::data::{Bar, QuoteTick, TradeTick};
20use pyo3::{
21 Py, PyAny, PyResult, Python,
22 types::{PyAnyMethods, PyList, PyListMethods},
23};
24
25use crate::actor::indicators::{ActorIndicator, SharedActorIndicator};
26
27struct PyActorIndicator {
28 indicator: Py<PyAny>,
29 key: usize,
30}
31
32impl Debug for PyActorIndicator {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 f.debug_struct(stringify!(PyActorIndicator))
35 .field("key", &self.key)
36 .finish_non_exhaustive()
37 }
38}
39
40impl PyActorIndicator {
41 fn new(py: Python<'_>, indicator: Py<PyAny>) -> Self {
42 Self {
43 key: indicator.bind(py).as_ptr() as usize,
44 indicator,
45 }
46 }
47
48 fn clone_ref(&self, py: Python<'_>) -> Py<PyAny> {
49 self.indicator.clone_ref(py)
50 }
51}
52
53impl ActorIndicator for PyActorIndicator {
54 fn key(&self) -> usize {
55 self.key
56 }
57
58 fn as_any(&self) -> &dyn Any {
59 self
60 }
61
62 fn initialized(&self) -> anyhow::Result<bool> {
63 Python::attach(|py| {
64 let initialized = self.indicator.bind(py).getattr("initialized")?;
65 let initialized = if initialized.is_callable() {
66 initialized.call0()?
67 } else {
68 initialized
69 };
70
71 initialized.extract::<bool>()
72 })
73 .map_err(|e| anyhow::anyhow!("{e}"))
74 }
75
76 fn handle_quote(&self, quote: &QuoteTick) -> anyhow::Result<()> {
77 Python::attach(|py| {
78 self.indicator
79 .call_method1(py, "handle_quote_tick", ((*quote).into_py_any_unwrap(py),))
80 })
81 .map(|_| ())
82 .map_err(|e| anyhow::anyhow!("{e}"))
83 }
84
85 fn handle_trade(&self, trade: &TradeTick) -> anyhow::Result<()> {
86 Python::attach(|py| {
87 self.indicator
88 .call_method1(py, "handle_trade_tick", ((*trade).into_py_any_unwrap(py),))
89 })
90 .map(|_| ())
91 .map_err(|e| anyhow::anyhow!("{e}"))
92 }
93
94 fn handle_bar(&self, bar: &Bar) -> anyhow::Result<()> {
95 Python::attach(|py| {
96 self.indicator
97 .call_method1(py, "handle_bar", ((*bar).into_py_any_unwrap(py),))
98 })
99 .map(|_| ())
100 .map_err(|e| anyhow::anyhow!("{e}"))
101 }
102}
103
104pub fn wrap_python_indicator(py: Python<'_>, indicator: Py<PyAny>) -> SharedActorIndicator {
106 Rc::new(PyActorIndicator::new(py, indicator))
107}
108
109pub fn registered_python_indicators(
115 py: Python<'_>,
116 indicators: Vec<SharedActorIndicator>,
117) -> PyResult<Py<PyList>> {
118 let py_indicators = PyList::empty(py);
119
120 for indicator in indicators {
121 let Some(indicator) = indicator.as_any().downcast_ref::<PyActorIndicator>() else {
122 return Err(to_pytype_err(
123 "registered indicator is not a Python indicator",
124 ));
125 };
126
127 py_indicators.append(indicator.clone_ref(py))?;
128 }
129
130 Ok(py_indicators.unbind())
131}