nautilus_okx/python/
mod.rs1#![expect(
19 clippy::missing_errors_doc,
20 reason = "errors documented on underlying Rust methods"
21)]
22
23pub mod config;
24pub mod enums;
25pub mod factories;
26pub mod http;
27pub mod models;
28pub mod urls;
29
30use std::str::FromStr;
31
32use nautilus_common::factories::{ClientConfig, DataClientFactory, ExecutionClientFactory};
33use nautilus_core::python::{to_pyruntime_err, to_pyvalue_err};
34use nautilus_system::get_global_pyo3_registry;
35use pyo3::{prelude::*, types::PyDict};
36
37use crate::{
38 common::{
39 consts::{OKX, OKX_CLIENT_ID, OKX_VENUE},
40 enums::OKXTriggerType,
41 },
42 config::{OKXDataClientConfig, OKXExecutionClientConfig},
43 factories::{OKXDataClientFactory, OKXExecutionClientFactory},
44};
45
46pub(super) fn extract_optional_string(
47 dict: &Bound<'_, PyDict>,
48 key: &str,
49) -> PyResult<Option<String>> {
50 dict.get_item(key)?
51 .map(|value| value.extract::<String>())
52 .transpose()
53}
54
55pub(super) fn extract_optional_trigger_type(
56 dict: &Bound<'_, PyDict>,
57 key: &str,
58) -> PyResult<Option<OKXTriggerType>> {
59 extract_optional_string(dict, key)?
60 .map(|value| {
61 OKXTriggerType::from_str(&value).map_err(|e| {
62 to_pyvalue_err(format!("Invalid OKX trigger type {value:?} for {key}: {e}"))
63 })
64 })
65 .transpose()
66}
67
68#[expect(clippy::needless_pass_by_value)]
69fn extract_okx_data_factory(
70 py: Python<'_>,
71 factory: Py<PyAny>,
72) -> PyResult<Box<dyn DataClientFactory>> {
73 match factory.extract::<OKXDataClientFactory>(py) {
74 Ok(f) => Ok(Box::new(f)),
75 Err(e) => Err(to_pyvalue_err(format!(
76 "Failed to extract OKXDataClientFactory: {e}"
77 ))),
78 }
79}
80
81#[expect(clippy::needless_pass_by_value)]
82fn extract_okx_exec_factory(
83 py: Python<'_>,
84 factory: Py<PyAny>,
85) -> PyResult<Box<dyn ExecutionClientFactory>> {
86 match factory.extract::<OKXExecutionClientFactory>(py) {
87 Ok(f) => Ok(Box::new(f)),
88 Err(e) => Err(to_pyvalue_err(format!(
89 "Failed to extract OKXExecutionClientFactory: {e}"
90 ))),
91 }
92}
93
94#[expect(clippy::needless_pass_by_value)]
95fn extract_okx_data_config(py: Python<'_>, config: Py<PyAny>) -> PyResult<Box<dyn ClientConfig>> {
96 match config.extract::<OKXDataClientConfig>(py) {
97 Ok(c) => Ok(Box::new(c)),
98 Err(e) => Err(to_pyvalue_err(format!(
99 "Failed to extract OKXDataClientConfig: {e}"
100 ))),
101 }
102}
103
104#[expect(clippy::needless_pass_by_value)]
105fn extract_okx_exec_config(py: Python<'_>, config: Py<PyAny>) -> PyResult<Box<dyn ClientConfig>> {
106 match config.extract::<OKXExecutionClientConfig>(py) {
107 Ok(c) => Ok(Box::new(c)),
108 Err(e) => Err(to_pyvalue_err(format!(
109 "Failed to extract OKXExecutionClientConfig: {e}"
110 ))),
111 }
112}
113
114#[pymodule]
120pub fn okx(_: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
121 m.add(stringify!(OKX), OKX)?;
122 m.add(stringify!(OKX_CLIENT_ID), *OKX_CLIENT_ID)?;
123 m.add(stringify!(OKX_VENUE), *OKX_VENUE)?;
124 m.add_class::<super::http::OKXHttpClient>()?;
125 m.add_class::<crate::http::models::OKXBalanceDetail>()?;
126 m.add_class::<crate::common::enums::OKXInstrumentType>()?;
127 m.add_class::<crate::common::enums::OKXContractType>()?;
128 m.add_class::<crate::common::enums::OKXGreeksType>()?;
129 m.add_class::<crate::common::enums::OKXMarginMode>()?;
130 m.add_class::<crate::common::enums::OKXTradeMode>()?;
131 m.add_class::<crate::common::enums::OKXOrderStatus>()?;
132 m.add_class::<crate::common::enums::OKXAlgoOrderStatus>()?;
133 m.add_class::<crate::common::enums::OKXPositionMode>()?;
134 m.add_class::<crate::common::enums::OKXVipLevel>()?;
135 m.add_class::<crate::common::enums::OKXEnvironment>()?;
136 m.add_class::<crate::common::enums::OKXRegion>()?;
137 m.add_class::<OKXDataClientConfig>()?;
138 m.add_class::<OKXDataClientFactory>()?;
139 m.add_class::<OKXExecutionClientConfig>()?;
140 m.add_class::<OKXExecutionClientFactory>()?;
141 m.add_function(wrap_pyfunction!(urls::get_okx_http_base_url, m)?)?;
142 m.add_function(wrap_pyfunction!(urls::get_okx_ws_url_public, m)?)?;
143 m.add_function(wrap_pyfunction!(urls::get_okx_ws_url_private, m)?)?;
144 m.add_function(wrap_pyfunction!(urls::get_okx_ws_url_business, m)?)?;
145
146 let registry = get_global_pyo3_registry();
147
148 if let Err(e) = registry.register_factory_extractor(OKX.to_string(), extract_okx_data_factory) {
149 return Err(to_pyruntime_err(format!(
150 "Failed to register OKX data factory extractor: {e}"
151 )));
152 }
153
154 if let Err(e) =
155 registry.register_exec_factory_extractor(OKX.to_string(), extract_okx_exec_factory)
156 {
157 return Err(to_pyruntime_err(format!(
158 "Failed to register OKX exec factory extractor: {e}"
159 )));
160 }
161
162 if let Err(e) = registry
163 .register_config_extractor("OKXDataClientConfig".to_string(), extract_okx_data_config)
164 {
165 return Err(to_pyruntime_err(format!(
166 "Failed to register OKX data config extractor: {e}"
167 )));
168 }
169
170 if let Err(e) = registry.register_config_extractor(
171 "OKXExecutionClientConfig".to_string(),
172 extract_okx_exec_config,
173 ) {
174 return Err(to_pyruntime_err(format!(
175 "Failed to register OKX exec config extractor: {e}"
176 )));
177 }
178
179 Ok(())
180}