Skip to main content

nautilus_trading/python/
algorithm.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.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//! Python bindings for execution algorithm configuration.
17
18use std::collections::HashMap;
19
20use nautilus_core::python::to_pyvalue_err;
21use nautilus_model::identifiers::ExecAlgorithmId;
22use pyo3::{prelude::*, types::PyDict};
23
24use crate::algorithm::{ExecutionAlgorithmConfig, ImportableExecAlgorithmConfig};
25
26#[pyo3::pymethods]
27#[pyo3_stub_gen::derive::gen_stub_pymethods]
28impl ExecutionAlgorithmConfig {
29    /// Configuration for an execution algorithm.
30    #[new]
31    #[pyo3(signature = (exec_algorithm_id=None, log_events=true, log_commands=true))]
32    fn py_new(
33        exec_algorithm_id: Option<ExecAlgorithmId>,
34        log_events: bool,
35        log_commands: bool,
36    ) -> Self {
37        Self {
38            exec_algorithm_id,
39            log_events,
40            log_commands,
41        }
42    }
43
44    #[getter]
45    fn exec_algorithm_id(&self) -> Option<ExecAlgorithmId> {
46        self.exec_algorithm_id
47    }
48
49    #[getter]
50    fn log_events(&self) -> bool {
51        self.log_events
52    }
53
54    #[getter]
55    fn log_commands(&self) -> bool {
56        self.log_commands
57    }
58}
59
60#[pyo3::pymethods]
61#[pyo3_stub_gen::derive::gen_stub_pymethods]
62impl ImportableExecAlgorithmConfig {
63    /// Configuration for creating execution algorithms from importable paths.
64    #[new]
65    #[expect(clippy::needless_pass_by_value)]
66    fn py_new(
67        exec_algorithm_path: String,
68        config_path: String,
69        config: Py<PyDict>,
70    ) -> PyResult<Self> {
71        let json_config = Python::attach(|py| -> PyResult<HashMap<String, serde_json::Value>> {
72            let kwargs = PyDict::new(py);
73            kwargs.set_item("default", py.eval(pyo3::ffi::c_str!("str"), None, None)?)?;
74            let json_str: String = PyModule::import(py, "json")?
75                .call_method("dumps", (config.bind(py),), Some(&kwargs))?
76                .extract()?;
77
78            let json_value: serde_json::Value =
79                serde_json::from_str(&json_str).map_err(to_pyvalue_err)?;
80
81            if let serde_json::Value::Object(map) = json_value {
82                Ok(map.into_iter().collect())
83            } else {
84                Err(to_pyvalue_err("Config must be a dictionary"))
85            }
86        })?;
87
88        Ok(Self {
89            exec_algorithm_path,
90            config_path,
91            config: json_config,
92        })
93    }
94
95    #[getter]
96    fn exec_algorithm_path(&self) -> &String {
97        &self.exec_algorithm_path
98    }
99
100    #[getter]
101    fn config_path(&self) -> &String {
102        &self.config_path
103    }
104
105    #[getter]
106    fn config(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
107        let py_dict = PyDict::new(py);
108
109        for (key, value) in &self.config {
110            let json_str = serde_json::to_string(value).map_err(to_pyvalue_err)?;
111            let py_value = PyModule::import(py, "json")?.call_method("loads", (json_str,), None)?;
112            py_dict.set_item(key, py_value)?;
113        }
114        Ok(py_dict.unbind())
115    }
116}