Skip to main content

nautilus_okx/python/
config.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 OKX configuration.
17
18use nautilus_core::string::secret::SecretString;
19use nautilus_model::identifiers::AccountId;
20use nautilus_network::websocket::TransportBackend;
21use pyo3::prelude::*;
22
23use crate::{
24    common::enums::{OKXEnvironment, OKXInstrumentType, OKXMarginMode, OKXRegion, OKXVipLevel},
25    config::{OKXDataClientConfig, OKXExecutionClientConfig},
26};
27
28#[pymethods]
29#[pyo3_stub_gen::derive::gen_stub_pymethods]
30impl OKXDataClientConfig {
31    /// Configuration for the OKX data client.
32    #[new]
33    #[pyo3(signature = (
34        instrument_types = None,
35        environment = None,
36        region = None,
37        api_key = None,
38        api_secret = None,
39        api_passphrase = None,
40        base_url_http = None,
41        base_url_ws_public = None,
42        base_url_ws_business = None,
43        proxy_url = None,
44        http_timeout_secs = None,
45        max_retries = None,
46        retry_delay_initial_ms = None,
47        retry_delay_max_ms = None,
48        update_instruments_interval_mins = None,
49        book_stale_check_interval_secs = None,
50        book_stale_threshold_secs = None,
51        book_snapshot_timeout_secs = None,
52        vip_level = None,
53        load_spreads = false,
54        transport_backend = None,
55        instrument_families = None,
56    ))]
57    #[expect(clippy::too_many_arguments)]
58    fn py_new(
59        instrument_types: Option<Vec<OKXInstrumentType>>,
60        environment: Option<OKXEnvironment>,
61        region: Option<OKXRegion>,
62        api_key: Option<String>,
63        api_secret: Option<String>,
64        api_passphrase: Option<String>,
65        base_url_http: Option<String>,
66        base_url_ws_public: Option<String>,
67        base_url_ws_business: Option<String>,
68        proxy_url: Option<String>,
69        http_timeout_secs: Option<u64>,
70        max_retries: Option<u32>,
71        retry_delay_initial_ms: Option<u64>,
72        retry_delay_max_ms: Option<u64>,
73        update_instruments_interval_mins: Option<u64>,
74        book_stale_check_interval_secs: Option<u64>,
75        book_stale_threshold_secs: Option<u64>,
76        book_snapshot_timeout_secs: Option<u64>,
77        vip_level: Option<OKXVipLevel>,
78        load_spreads: bool,
79        transport_backend: Option<TransportBackend>,
80        instrument_families: Option<Vec<String>>,
81    ) -> Self {
82        let defaults = Self::default();
83        Self {
84            api_key: api_key.map(SecretString::from),
85            api_secret: api_secret.map(SecretString::from),
86            api_passphrase: api_passphrase.map(SecretString::from),
87            instrument_types: instrument_types.unwrap_or(defaults.instrument_types),
88            contract_types: None,
89            load_spreads,
90            instrument_families,
91            base_url_http,
92            base_url_ws_public,
93            base_url_ws_business,
94            proxy_url: proxy_url.map(SecretString::from),
95            environment: environment.unwrap_or(defaults.environment),
96            region: region.unwrap_or(defaults.region),
97            http_timeout_secs: http_timeout_secs.unwrap_or(defaults.http_timeout_secs),
98            max_retries: max_retries.unwrap_or(defaults.max_retries),
99            retry_delay_initial_ms: retry_delay_initial_ms
100                .unwrap_or(defaults.retry_delay_initial_ms),
101            retry_delay_max_ms: retry_delay_max_ms.unwrap_or(defaults.retry_delay_max_ms),
102            update_instruments_interval_mins: update_instruments_interval_mins
103                .unwrap_or(defaults.update_instruments_interval_mins),
104            book_stale_check_interval_secs: book_stale_check_interval_secs
105                .unwrap_or(defaults.book_stale_check_interval_secs),
106            book_stale_threshold_secs: book_stale_threshold_secs
107                .unwrap_or(defaults.book_stale_threshold_secs),
108            book_snapshot_timeout_secs: book_snapshot_timeout_secs
109                .unwrap_or(defaults.book_snapshot_timeout_secs),
110            vip_level,
111            transport_backend: transport_backend.unwrap_or(defaults.transport_backend),
112        }
113    }
114
115    #[getter]
116    const fn has_proxy_url(&self) -> bool {
117        self.proxy_url.is_some()
118    }
119
120    fn __repr__(&self) -> String {
121        stringify!(OKXDataClientConfig).to_string()
122    }
123}
124
125#[pymethods]
126#[pyo3_stub_gen::derive::gen_stub_pymethods]
127impl OKXExecutionClientConfig {
128    /// Configuration for the OKX execution client.
129    #[new]
130    #[pyo3(signature = (
131        account_id,
132        instrument_types = None,
133        environment = None,
134        region = None,
135        api_key = None,
136        api_secret = None,
137        api_passphrase = None,
138        base_url_http = None,
139        base_url_ws_private = None,
140        base_url_ws_business = None,
141        proxy_url = None,
142        http_timeout_secs = None,
143        max_retries = None,
144        retry_delay_initial_ms = None,
145        retry_delay_max_ms = None,
146        margin_mode = None,
147        load_spreads = false,
148        auth_timeout_secs = None,
149        transport_backend = None,
150        spot_trade_quote_ccy = None,
151    ))]
152    #[expect(clippy::too_many_arguments)]
153    fn py_new(
154        account_id: AccountId,
155        instrument_types: Option<Vec<OKXInstrumentType>>,
156        environment: Option<OKXEnvironment>,
157        region: Option<OKXRegion>,
158        api_key: Option<String>,
159        api_secret: Option<String>,
160        api_passphrase: Option<String>,
161        base_url_http: Option<String>,
162        base_url_ws_private: Option<String>,
163        base_url_ws_business: Option<String>,
164        proxy_url: Option<String>,
165        http_timeout_secs: Option<u64>,
166        max_retries: Option<u32>,
167        retry_delay_initial_ms: Option<u64>,
168        retry_delay_max_ms: Option<u64>,
169        margin_mode: Option<OKXMarginMode>,
170        load_spreads: bool,
171        auth_timeout_secs: Option<u64>,
172        transport_backend: Option<TransportBackend>,
173        spot_trade_quote_ccy: Option<String>,
174    ) -> Self {
175        let defaults = Self::default();
176        Self {
177            account_id,
178            api_key: api_key.map(SecretString::from),
179            api_secret: api_secret.map(SecretString::from),
180            api_passphrase: api_passphrase.map(SecretString::from),
181            instrument_types: instrument_types.unwrap_or(defaults.instrument_types),
182            contract_types: None,
183            instrument_families: None,
184            base_url_http,
185            base_url_ws_private,
186            base_url_ws_business,
187            proxy_url: proxy_url.map(SecretString::from),
188            environment: environment.unwrap_or(defaults.environment),
189            region: region.unwrap_or(defaults.region),
190            http_timeout_secs: http_timeout_secs.unwrap_or(defaults.http_timeout_secs),
191            use_mm_mass_cancel: defaults.use_mm_mass_cancel,
192            max_retries: max_retries.unwrap_or(defaults.max_retries),
193            retry_delay_initial_ms: retry_delay_initial_ms
194                .unwrap_or(defaults.retry_delay_initial_ms),
195            retry_delay_max_ms: retry_delay_max_ms.unwrap_or(defaults.retry_delay_max_ms),
196            margin_mode,
197            load_spreads,
198            use_spot_margin: defaults.use_spot_margin,
199            auth_timeout_secs,
200            transport_backend: transport_backend.unwrap_or(defaults.transport_backend),
201            spot_trade_quote_ccy,
202        }
203    }
204
205    #[getter]
206    const fn has_proxy_url(&self) -> bool {
207        self.proxy_url.is_some()
208    }
209
210    fn __repr__(&self) -> String {
211        stringify!(OKXExecutionClientConfig).to_string()
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use rstest::rstest;
218
219    use super::*;
220
221    #[rstest]
222    fn test_data_config_py_new() {
223        let config = OKXDataClientConfig::py_new(
224            None,
225            None,
226            None,
227            None,
228            None,
229            None,
230            None,
231            None,
232            None,
233            None,
234            None,
235            None,
236            None,
237            None,
238            None,
239            None,
240            None,
241            None,
242            None,
243            true,
244            None,
245            Some(vec!["BTC-USD".to_string()]),
246        );
247
248        assert_eq!(
249            config.instrument_families,
250            Some(vec!["BTC-USD".to_string()]),
251        );
252        assert!(config.load_spreads);
253        assert_eq!(config.book_stale_check_interval_secs, 5);
254        assert_eq!(config.book_stale_threshold_secs, 30);
255        assert_eq!(config.book_snapshot_timeout_secs, 10);
256    }
257
258    #[rstest]
259    fn test_exec_config_py_new_load_spreads() {
260        let config = OKXExecutionClientConfig::py_new(
261            AccountId::from("OKX-001"),
262            None,
263            None,
264            None,
265            None,
266            None,
267            None,
268            None,
269            None,
270            None,
271            None,
272            None,
273            None,
274            None,
275            None,
276            None,
277            true,
278            None,
279            None,
280            Some("USD".to_string()),
281        );
282
283        assert!(config.load_spreads);
284        assert_eq!(config.auth_timeout_secs, None);
285        assert_eq!(config.spot_trade_quote_ccy.as_deref(), Some("USD"));
286    }
287}