nautilus_pyo3/
lib.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Posei Systems Pty Ltd. All rights reserved.
3//  https://poseitrader.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
16//! [PoseiTrader](http://poseitrader.io) is an open-source, high-performance, production-grade
17//! algorithmic trading platform, providing quantitative traders with the ability to backtest
18//! portfolios of automated trading strategies on historical data with an event-driven engine,
19//! and also deploy those same strategies live, with no code changes.
20//!
21//! # Feature flags
22//!
23//! This crate provides feature flags to control source code inclusion during compilation,
24//! depending on the intended use case, i.e. whether to provide Python bindings
25//! for the [posei_trader](https://pypi.org/project/posei_trader) Python package,
26//! or as part of a Rust only build.
27//!
28//! - `ffi`: Enables the C foreign function interface (FFI) from [cbindgen](https://github.com/mozilla/cbindgen).
29//! - `high-precision`: Enables [high-precision mode](docs/latestnightly/getting_started/installation#precision-mode) to use 128-bit value types.
30
31#![warn(rustc::all)]
32#![deny(unsafe_code)]
33#![deny(nonstandard_style)]
34#![deny(missing_debug_implementations)]
35#![deny(clippy::missing_errors_doc)]
36#![deny(clippy::missing_panics_doc)]
37#![deny(rustdoc::broken_intra_doc_links)]
38
39use pyo3::prelude::*;
40
41/// We modify sys modules so that submodule can be loaded directly as
42/// import supermodule.submodule
43///
44/// Also re-exports all submodule attributes so they can be imported directly from `nautilus_pyo3`
45/// refer: <https://github.com/PyO3/pyo3/issues/2644>
46#[pymodule]
47fn nautilus_pyo3(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
48    let sys = PyModule::import(py, "sys")?;
49    let modules = sys.getattr("modules")?;
50    let sys_modules: &Bound<'_, PyAny> = modules.downcast()?;
51    let module_name = "posei_trader.core.nautilus_pyo3";
52
53    // Set pyo3_nautilus to be recognized as a subpackage
54    sys_modules.set_item(module_name, m)?;
55
56    let n = "core";
57    let submodule = pyo3::wrap_pymodule!(nautilus_core::python::core);
58    m.add_wrapped(submodule)?;
59    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
60    re_export_module_attributes(m, n)?;
61
62    let n = "common";
63    let submodule = pyo3::wrap_pymodule!(nautilus_common::python::common);
64    m.add_wrapped(submodule)?;
65    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
66    re_export_module_attributes(m, n)?;
67
68    let n = "cryptography";
69    let submodule = pyo3::wrap_pymodule!(nautilus_cryptography::python::cryptography);
70    m.add_wrapped(submodule)?;
71    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
72    re_export_module_attributes(m, n)?;
73
74    let n = "model";
75    let submodule = pyo3::wrap_pymodule!(nautilus_model::python::model);
76    m.add_wrapped(submodule)?;
77    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
78    re_export_module_attributes(m, n)?;
79
80    let n = "indicators";
81    let submodule = pyo3::wrap_pymodule!(nautilus_indicators::python::indicators);
82    m.add_wrapped(submodule)?;
83    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
84    re_export_module_attributes(m, n)?;
85
86    let n = "infrastructure";
87    let submodule = pyo3::wrap_pymodule!(nautilus_infrastructure::python::infrastructure);
88    m.add_wrapped(submodule)?;
89    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
90    re_export_module_attributes(m, n)?;
91
92    let n = "network";
93    let submodule = pyo3::wrap_pymodule!(nautilus_network::python::network);
94    m.add_wrapped(submodule)?;
95    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
96    re_export_module_attributes(m, n)?;
97
98    let n = "persistence";
99    let submodule = pyo3::wrap_pymodule!(nautilus_persistence::python::persistence);
100    m.add_wrapped(submodule)?;
101    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
102    re_export_module_attributes(m, n)?;
103
104    let n = "serialization";
105    let submodule = pyo3::wrap_pymodule!(nautilus_serialization::python::serialization);
106    m.add_wrapped(submodule)?;
107    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
108    re_export_module_attributes(m, n)?;
109
110    let n = "testkit";
111    let submodule = pyo3::wrap_pymodule!(nautilus_testkit::python::testkit);
112    m.add_wrapped(submodule)?;
113    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
114    re_export_module_attributes(m, n)?;
115
116    let n = "trading";
117    let submodule = pyo3::wrap_pymodule!(nautilus_trading::python::trading);
118    m.add_wrapped(submodule)?;
119    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
120    re_export_module_attributes(m, n)?;
121
122    // Adapters
123
124    let n = "coinbase_intx";
125    let submodule = pyo3::wrap_pymodule!(nautilus_coinbase_intx::python::coinbase_intx);
126    m.add_wrapped(submodule)?;
127    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
128    re_export_module_attributes(m, n)?;
129
130    let n = "databento";
131    let submodule = pyo3::wrap_pymodule!(posei_traderbento::python::databento);
132    m.add_wrapped(submodule)?;
133    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
134    re_export_module_attributes(m, n)?;
135
136    let n = "tardis";
137    let submodule = pyo3::wrap_pymodule!(nautilus_tardis::python::tardis);
138    m.add_wrapped(submodule)?;
139    sys_modules.set_item(format!("{module_name}.{n}"), m.getattr(n)?)?;
140    re_export_module_attributes(m, n)?;
141
142    Ok(())
143}
144
145fn re_export_module_attributes(
146    parent_module: &Bound<'_, PyModule>,
147    submodule_name: &str,
148) -> PyResult<()> {
149    let submodule = parent_module.getattr(submodule_name)?;
150    for item_name in submodule.dir()? {
151        let item_name_str: &str = item_name.extract()?;
152        if let Ok(attr) = submodule.getattr(item_name_str) {
153            parent_module.add(item_name_str, attr)?;
154        }
155    }
156
157    Ok(())
158}