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