1use 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 #[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 #[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}