1use chrono::{DateTime, Utc};
19use nautilus_core::python::{IntoPyObjectNautilusExt, to_pyvalue_err};
20use nautilus_model::{
21 data::BarType,
22 enums::{OrderSide, OrderType, TriggerType},
23 identifiers::{AccountId, ClientOrderId, InstrumentId, StrategyId, TraderId},
24 python::instruments::{instrument_any_to_pyobject, pyobject_to_instrument_any},
25 types::{Price, Quantity},
26};
27use pyo3::{
28 conversion::IntoPyObjectExt,
29 prelude::*,
30 types::{PyDict, PyList},
31};
32
33use crate::{
34 common::enums::{OKXInstrumentType, OKXOrderStatus, OKXPositionMode, OKXTradeMode},
35 http::client::OKXHttpClient,
36};
37
38#[pymethods]
39impl OKXHttpClient {
40 #[new]
41 #[pyo3(signature = (
42 api_key=None,
43 api_secret=None,
44 api_passphrase=None,
45 base_url=None,
46 timeout_secs=None,
47 max_retries=None,
48 retry_delay_ms=None,
49 retry_delay_max_ms=None,
50 is_demo=false,
51 ))]
52 #[allow(clippy::too_many_arguments)]
53 fn py_new(
54 api_key: Option<String>,
55 api_secret: Option<String>,
56 api_passphrase: Option<String>,
57 base_url: Option<String>,
58 timeout_secs: Option<u64>,
59 max_retries: Option<u32>,
60 retry_delay_ms: Option<u64>,
61 retry_delay_max_ms: Option<u64>,
62 is_demo: bool,
63 ) -> PyResult<Self> {
64 Self::with_credentials(
65 api_key,
66 api_secret,
67 api_passphrase,
68 base_url,
69 timeout_secs,
70 max_retries,
71 retry_delay_ms,
72 retry_delay_max_ms,
73 is_demo,
74 )
75 .map_err(to_pyvalue_err)
76 }
77
78 #[staticmethod]
79 #[pyo3(name = "from_env")]
80 fn py_from_env() -> PyResult<Self> {
81 Self::from_env().map_err(to_pyvalue_err)
82 }
83
84 #[getter]
85 #[pyo3(name = "base_url")]
86 #[must_use]
87 pub fn py_base_url(&self) -> &str {
88 self.base_url()
89 }
90
91 #[getter]
92 #[pyo3(name = "api_key")]
93 #[must_use]
94 pub fn py_api_key(&self) -> Option<&str> {
95 self.api_key()
96 }
97
98 #[pyo3(name = "is_initialized")]
99 #[must_use]
100 pub const fn py_is_initialized(&self) -> bool {
101 self.is_initialized()
102 }
103
104 #[pyo3(name = "get_cached_symbols")]
105 #[must_use]
106 pub fn py_get_cached_symbols(&self) -> Vec<String> {
107 self.get_cached_symbols()
108 }
109
110 #[pyo3(name = "cancel_all_requests")]
111 pub fn py_cancel_all_requests(&self) {
112 self.cancel_all_requests();
113 }
114
115 #[pyo3(name = "add_instrument")]
119 pub fn py_add_instrument(&mut self, py: Python<'_>, instrument: Py<PyAny>) -> PyResult<()> {
120 self.add_instrument(pyobject_to_instrument_any(py, instrument)?);
121 Ok(())
122 }
123
124 #[pyo3(name = "set_position_mode")]
126 fn py_set_position_mode<'py>(
127 &self,
128 py: Python<'py>,
129 position_mode: OKXPositionMode,
130 ) -> PyResult<Bound<'py, PyAny>> {
131 let client = self.clone();
132
133 pyo3_async_runtimes::tokio::future_into_py(py, async move {
134 client
135 .set_position_mode(position_mode)
136 .await
137 .map_err(to_pyvalue_err)?;
138 Ok(Python::attach(|py| py.None()))
139 })
140 }
141
142 #[pyo3(name = "request_instruments")]
143 #[pyo3(signature = (instrument_type, instrument_family=None))]
144 fn py_request_instruments<'py>(
145 &self,
146 py: Python<'py>,
147 instrument_type: OKXInstrumentType,
148 instrument_family: Option<String>,
149 ) -> PyResult<Bound<'py, PyAny>> {
150 let client = self.clone();
151
152 pyo3_async_runtimes::tokio::future_into_py(py, async move {
153 let instruments = client
154 .request_instruments(instrument_type, instrument_family)
155 .await
156 .map_err(to_pyvalue_err)?;
157
158 Python::attach(|py| {
159 let py_instruments: PyResult<Vec<_>> = instruments
160 .into_iter()
161 .map(|inst| instrument_any_to_pyobject(py, inst))
162 .collect();
163 let pylist = PyList::new(py, py_instruments?)
164 .unwrap()
165 .into_any()
166 .unbind();
167 Ok(pylist)
168 })
169 })
170 }
171
172 #[pyo3(name = "request_account_state")]
173 fn py_request_account_state<'py>(
174 &self,
175 py: Python<'py>,
176 account_id: AccountId,
177 ) -> PyResult<Bound<'py, PyAny>> {
178 let client = self.clone();
179
180 pyo3_async_runtimes::tokio::future_into_py(py, async move {
181 let account_state = client
182 .request_account_state(account_id)
183 .await
184 .map_err(to_pyvalue_err)?;
185 Ok(Python::attach(|py| account_state.into_py_any_unwrap(py)))
186 })
187 }
188
189 #[pyo3(name = "request_trades")]
190 #[pyo3(signature = (instrument_id, start=None, end=None, limit=None))]
191 fn py_request_trades<'py>(
192 &self,
193 py: Python<'py>,
194 instrument_id: InstrumentId,
195 start: Option<DateTime<Utc>>,
196 end: Option<DateTime<Utc>>,
197 limit: Option<u32>,
198 ) -> PyResult<Bound<'py, PyAny>> {
199 let client = self.clone();
200
201 pyo3_async_runtimes::tokio::future_into_py(py, async move {
202 let trades = client
203 .request_trades(instrument_id, start, end, limit)
204 .await
205 .map_err(to_pyvalue_err)?;
206 Python::attach(|py| {
207 let pylist = PyList::new(py, trades.into_iter().map(|t| t.into_py_any_unwrap(py)))?;
208 Ok(pylist.into_py_any_unwrap(py))
209 })
210 })
211 }
212
213 #[pyo3(name = "request_bars")]
214 #[pyo3(signature = (bar_type, start=None, end=None, limit=None))]
215 fn py_request_bars<'py>(
216 &self,
217 py: Python<'py>,
218 bar_type: BarType,
219 start: Option<DateTime<Utc>>,
220 end: Option<DateTime<Utc>>,
221 limit: Option<u32>,
222 ) -> PyResult<Bound<'py, PyAny>> {
223 let client = self.clone();
224
225 pyo3_async_runtimes::tokio::future_into_py(py, async move {
226 let bars = client
227 .request_bars(bar_type, start, end, limit)
228 .await
229 .map_err(to_pyvalue_err)?;
230 Python::attach(|py| {
231 let pylist =
232 PyList::new(py, bars.into_iter().map(|bar| bar.into_py_any_unwrap(py)))?;
233 Ok(pylist.into_py_any_unwrap(py))
234 })
235 })
236 }
237
238 #[pyo3(name = "request_mark_price")]
239 fn py_request_mark_price<'py>(
240 &self,
241 py: Python<'py>,
242 instrument_id: InstrumentId,
243 ) -> PyResult<Bound<'py, PyAny>> {
244 let client = self.clone();
245
246 pyo3_async_runtimes::tokio::future_into_py(py, async move {
247 let mark_price = client
248 .request_mark_price(instrument_id)
249 .await
250 .map_err(to_pyvalue_err)?;
251 Ok(Python::attach(|py| mark_price.into_py_any_unwrap(py)))
252 })
253 }
254
255 #[pyo3(name = "request_index_price")]
256 fn py_request_index_price<'py>(
257 &self,
258 py: Python<'py>,
259 instrument_id: InstrumentId,
260 ) -> PyResult<Bound<'py, PyAny>> {
261 let client = self.clone();
262
263 pyo3_async_runtimes::tokio::future_into_py(py, async move {
264 let index_price = client
265 .request_index_price(instrument_id)
266 .await
267 .map_err(to_pyvalue_err)?;
268 Ok(Python::attach(|py| index_price.into_py_any_unwrap(py)))
269 })
270 }
271
272 #[pyo3(name = "request_order_status_reports")]
273 #[pyo3(signature = (account_id, instrument_type=None, instrument_id=None, start=None, end=None, open_only=false, limit=None))]
274 #[allow(clippy::too_many_arguments)]
275 fn py_request_order_status_reports<'py>(
276 &self,
277 py: Python<'py>,
278 account_id: AccountId,
279 instrument_type: Option<OKXInstrumentType>,
280 instrument_id: Option<InstrumentId>,
281 start: Option<DateTime<Utc>>,
282 end: Option<DateTime<Utc>>,
283 open_only: bool,
284 limit: Option<u32>,
285 ) -> PyResult<Bound<'py, PyAny>> {
286 let client = self.clone();
287
288 pyo3_async_runtimes::tokio::future_into_py(py, async move {
289 let reports = client
290 .request_order_status_reports(
291 account_id,
292 instrument_type,
293 instrument_id,
294 start,
295 end,
296 open_only,
297 limit,
298 )
299 .await
300 .map_err(to_pyvalue_err)?;
301 Python::attach(|py| {
302 let pylist =
303 PyList::new(py, reports.into_iter().map(|t| t.into_py_any_unwrap(py)))?;
304 Ok(pylist.into_py_any_unwrap(py))
305 })
306 })
307 }
308
309 #[pyo3(name = "request_algo_order_status_reports")]
310 #[pyo3(signature = (account_id, instrument_type=None, instrument_id=None, algo_id=None, algo_client_order_id=None, state=None, limit=None))]
311 #[allow(clippy::too_many_arguments)]
312 fn py_request_algo_order_status_reports<'py>(
313 &self,
314 py: Python<'py>,
315 account_id: AccountId,
316 instrument_type: Option<OKXInstrumentType>,
317 instrument_id: Option<InstrumentId>,
318 algo_id: Option<String>,
319 algo_client_order_id: Option<ClientOrderId>,
320 state: Option<OKXOrderStatus>,
321 limit: Option<u32>,
322 ) -> PyResult<Bound<'py, PyAny>> {
323 let client = self.clone();
324
325 pyo3_async_runtimes::tokio::future_into_py(py, async move {
326 let reports = client
327 .request_algo_order_status_reports(
328 account_id,
329 instrument_type,
330 instrument_id,
331 algo_id,
332 algo_client_order_id,
333 state,
334 limit,
335 )
336 .await
337 .map_err(to_pyvalue_err)?;
338
339 Python::attach(|py| {
340 let pylist =
341 PyList::new(py, reports.into_iter().map(|r| r.into_py_any_unwrap(py)))?;
342 Ok(pylist.into_py_any_unwrap(py))
343 })
344 })
345 }
346
347 #[pyo3(name = "request_algo_order_status_report")]
348 fn py_request_algo_order_status_report<'py>(
349 &self,
350 py: Python<'py>,
351 account_id: AccountId,
352 instrument_id: InstrumentId,
353 client_order_id: ClientOrderId,
354 ) -> PyResult<Bound<'py, PyAny>> {
355 let client = self.clone();
356
357 pyo3_async_runtimes::tokio::future_into_py(py, async move {
358 let report = client
359 .request_algo_order_status_report(account_id, instrument_id, client_order_id)
360 .await
361 .map_err(to_pyvalue_err)?;
362
363 Python::attach(|py| match report {
364 Some(report) => Ok(report.into_py_any_unwrap(py)),
365 None => Ok(py.None()),
366 })
367 })
368 }
369
370 #[pyo3(name = "request_fill_reports")]
371 #[pyo3(signature = (account_id, instrument_type=None, instrument_id=None, start=None, end=None, limit=None))]
372 #[allow(clippy::too_many_arguments)]
373 fn py_request_fill_reports<'py>(
374 &self,
375 py: Python<'py>,
376 account_id: AccountId,
377 instrument_type: Option<OKXInstrumentType>,
378 instrument_id: Option<InstrumentId>,
379 start: Option<DateTime<Utc>>,
380 end: Option<DateTime<Utc>>,
381 limit: Option<u32>,
382 ) -> PyResult<Bound<'py, PyAny>> {
383 let client = self.clone();
384
385 pyo3_async_runtimes::tokio::future_into_py(py, async move {
386 let trades = client
387 .request_fill_reports(
388 account_id,
389 instrument_type,
390 instrument_id,
391 start,
392 end,
393 limit,
394 )
395 .await
396 .map_err(to_pyvalue_err)?;
397 Python::attach(|py| {
398 let pylist = PyList::new(py, trades.into_iter().map(|t| t.into_py_any_unwrap(py)))?;
399 Ok(pylist.into_py_any_unwrap(py))
400 })
401 })
402 }
403
404 #[pyo3(name = "request_position_status_reports")]
405 #[pyo3(signature = (account_id, instrument_type=None, instrument_id=None))]
406 fn py_request_position_status_reports<'py>(
407 &self,
408 py: Python<'py>,
409 account_id: AccountId,
410 instrument_type: Option<OKXInstrumentType>,
411 instrument_id: Option<InstrumentId>,
412 ) -> PyResult<Bound<'py, PyAny>> {
413 let client = self.clone();
414
415 pyo3_async_runtimes::tokio::future_into_py(py, async move {
416 let reports = client
417 .request_position_status_reports(account_id, instrument_type, instrument_id)
418 .await
419 .map_err(to_pyvalue_err)?;
420 Python::attach(|py| {
421 let pylist =
422 PyList::new(py, reports.into_iter().map(|t| t.into_py_any_unwrap(py)))?;
423 Ok(pylist.into_py_any_unwrap(py))
424 })
425 })
426 }
427
428 #[pyo3(name = "place_algo_order")]
429 #[pyo3(signature = (
430 trader_id,
431 strategy_id,
432 instrument_id,
433 td_mode,
434 client_order_id,
435 order_side,
436 order_type,
437 quantity,
438 trigger_price,
439 trigger_type=None,
440 limit_price=None,
441 reduce_only=None,
442 ))]
443 #[allow(clippy::too_many_arguments)]
444 fn py_place_algo_order<'py>(
445 &self,
446 py: Python<'py>,
447 trader_id: TraderId,
448 strategy_id: StrategyId,
449 instrument_id: InstrumentId,
450 td_mode: OKXTradeMode,
451 client_order_id: ClientOrderId,
452 order_side: OrderSide,
453 order_type: OrderType,
454 quantity: Quantity,
455 trigger_price: Price,
456 trigger_type: Option<TriggerType>,
457 limit_price: Option<Price>,
458 reduce_only: Option<bool>,
459 ) -> PyResult<Bound<'py, PyAny>> {
460 let client = self.clone();
461
462 let _ = (trader_id, strategy_id);
464
465 pyo3_async_runtimes::tokio::future_into_py(py, async move {
466 let resp = client
467 .place_algo_order_with_domain_types(
468 instrument_id,
469 td_mode,
470 client_order_id,
471 order_side,
472 order_type,
473 quantity,
474 trigger_price,
475 trigger_type,
476 limit_price,
477 reduce_only,
478 )
479 .await
480 .map_err(to_pyvalue_err)?;
481 Python::attach(|py| {
482 let dict = PyDict::new(py);
483 dict.set_item("algo_id", resp.algo_id)?;
484 if let Some(algo_cl_ord_id) = resp.algo_cl_ord_id {
485 dict.set_item("algo_cl_ord_id", algo_cl_ord_id)?;
486 }
487 if let Some(s_code) = resp.s_code {
488 dict.set_item("s_code", s_code)?;
489 }
490 if let Some(s_msg) = resp.s_msg {
491 dict.set_item("s_msg", s_msg)?;
492 }
493 if let Some(req_id) = resp.req_id {
494 dict.set_item("req_id", req_id)?;
495 }
496 Ok(dict.into_py_any_unwrap(py))
497 })
498 })
499 }
500
501 #[pyo3(name = "cancel_algo_order")]
502 fn py_cancel_algo_order<'py>(
503 &self,
504 py: Python<'py>,
505 instrument_id: InstrumentId,
506 algo_id: String,
507 ) -> PyResult<Bound<'py, PyAny>> {
508 let client = self.clone();
509
510 pyo3_async_runtimes::tokio::future_into_py(py, async move {
511 let resp = client
512 .cancel_algo_order_with_domain_types(instrument_id, algo_id)
513 .await
514 .map_err(to_pyvalue_err)?;
515 Python::attach(|py| {
516 let dict = PyDict::new(py);
517 dict.set_item("algo_id", resp.algo_id)?;
518 if let Some(s_code) = resp.s_code {
519 dict.set_item("s_code", s_code)?;
520 }
521 if let Some(s_msg) = resp.s_msg {
522 dict.set_item("s_msg", s_msg)?;
523 }
524 Ok(dict.into_py_any_unwrap(py))
525 })
526 })
527 }
528
529 #[pyo3(name = "http_get_server_time")]
530 fn py_http_get_server_time<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
531 let client = self.clone();
532
533 pyo3_async_runtimes::tokio::future_into_py(py, async move {
534 let timestamp = client
535 .http_get_server_time()
536 .await
537 .map_err(to_pyvalue_err)?;
538
539 Python::attach(|py| timestamp.into_py_any(py))
540 })
541 }
542
543 #[pyo3(name = "request_vip_level")]
547 fn py_request_vip_level<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
548 let client = self.clone();
549
550 pyo3_async_runtimes::tokio::future_into_py(py, async move {
551 let vip_level = client.request_vip_level().await.map_err(to_pyvalue_err)?;
552
553 Python::attach(|py| match vip_level {
554 Some(level) => Ok(level.into_py_any_unwrap(py)),
555 None => Ok(py.None()),
556 })
557 })
558 }
559}