Skip to main content

nautilus_okx/python/
mod.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 from `pyo3`.
17
18#![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/// Exposed through `nautilus_trader.adapters.okx`.
115///
116/// # Errors
117///
118/// Returns an error if any bindings fail to register with the Python module.
119#[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}