nautilus_okx/python/
http.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 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
16use chrono::{DateTime, Utc};
17use nautilus_core::python::{IntoPyObjectNautilusExt, to_pyvalue_err};
18use nautilus_model::{
19    data::BarType,
20    identifiers::{AccountId, InstrumentId},
21    python::instruments::{instrument_any_to_pyobject, pyobject_to_instrument_any},
22};
23use pyo3::{prelude::*, types::PyList};
24
25use crate::{
26    common::enums::{OKXInstrumentType, OKXPositionMode},
27    http::client::OKXHttpClient,
28};
29
30#[pymethods]
31impl OKXHttpClient {
32    #[new]
33    #[pyo3(signature = (api_key=None, api_secret=None, api_passphrase=None, base_url=None, timeout_secs=None, max_retries=None, retry_delay_ms=None, retry_delay_max_ms=None))]
34    #[allow(clippy::too_many_arguments)]
35    fn py_new(
36        api_key: Option<String>,
37        api_secret: Option<String>,
38        api_passphrase: Option<String>,
39        base_url: Option<String>,
40        timeout_secs: Option<u64>,
41        max_retries: Option<u32>,
42        retry_delay_ms: Option<u64>,
43        retry_delay_max_ms: Option<u64>,
44    ) -> PyResult<Self> {
45        Self::with_credentials(
46            api_key,
47            api_secret,
48            api_passphrase,
49            base_url,
50            timeout_secs,
51            max_retries,
52            retry_delay_ms,
53            retry_delay_max_ms,
54        )
55        .map_err(to_pyvalue_err)
56    }
57
58    #[staticmethod]
59    #[pyo3(name = "from_env")]
60    fn py_from_env() -> PyResult<Self> {
61        Self::from_env().map_err(to_pyvalue_err)
62    }
63
64    #[getter]
65    #[pyo3(name = "base_url")]
66    #[must_use]
67    pub fn py_base_url(&self) -> &str {
68        self.base_url()
69    }
70
71    #[getter]
72    #[pyo3(name = "api_key")]
73    #[must_use]
74    pub fn py_api_key(&self) -> Option<&str> {
75        self.api_key()
76    }
77
78    #[pyo3(name = "is_initialized")]
79    #[must_use]
80    pub const fn py_is_initialized(&self) -> bool {
81        self.is_initialized()
82    }
83
84    #[pyo3(name = "get_cached_symbols")]
85    #[must_use]
86    pub fn py_get_cached_symbols(&self) -> Vec<String> {
87        self.get_cached_symbols()
88    }
89
90    #[pyo3(name = "cancel_all_requests")]
91    pub fn py_cancel_all_requests(&self) {
92        self.cancel_all_requests();
93    }
94
95    /// # Errors
96    ///
97    /// Returns a Python exception if adding the instrument to the cache fails.
98    #[pyo3(name = "add_instrument")]
99    pub fn py_add_instrument(&mut self, py: Python<'_>, instrument: PyObject) -> PyResult<()> {
100        self.add_instrument(pyobject_to_instrument_any(py, instrument)?);
101        Ok(())
102    }
103
104    /// Sets the position mode for the account.
105    #[pyo3(name = "set_position_mode")]
106    fn py_set_position_mode<'py>(
107        &self,
108        py: Python<'py>,
109        position_mode: OKXPositionMode,
110    ) -> PyResult<Bound<'py, PyAny>> {
111        let client = self.clone();
112
113        pyo3_async_runtimes::tokio::future_into_py(py, async move {
114            client
115                .set_position_mode(position_mode)
116                .await
117                .map_err(to_pyvalue_err)?;
118            Ok(Python::with_gil(|py| py.None()))
119        })
120    }
121
122    #[pyo3(name = "request_instruments")]
123    fn py_request_instruments<'py>(
124        &self,
125        py: Python<'py>,
126        instrument_type: OKXInstrumentType,
127    ) -> PyResult<Bound<'py, PyAny>> {
128        let client = self.clone();
129
130        pyo3_async_runtimes::tokio::future_into_py(py, async move {
131            let instruments = client
132                .request_instruments(instrument_type)
133                .await
134                .map_err(to_pyvalue_err)?;
135
136            Python::with_gil(|py| {
137                let py_instruments: PyResult<Vec<_>> = instruments
138                    .into_iter()
139                    .map(|inst| instrument_any_to_pyobject(py, inst))
140                    .collect();
141                let pylist = PyList::new(py, py_instruments?)
142                    .unwrap()
143                    .into_any()
144                    .unbind();
145                Ok(pylist)
146            })
147        })
148    }
149
150    #[pyo3(name = "request_account_state")]
151    fn py_request_account_state<'py>(
152        &self,
153        py: Python<'py>,
154        account_id: AccountId,
155    ) -> PyResult<Bound<'py, PyAny>> {
156        let client = self.clone();
157
158        pyo3_async_runtimes::tokio::future_into_py(py, async move {
159            let account_state = client
160                .request_account_state(account_id)
161                .await
162                .map_err(to_pyvalue_err)?;
163            Ok(Python::with_gil(|py| account_state.into_py_any_unwrap(py)))
164        })
165    }
166
167    #[pyo3(name = "request_trades")]
168    #[pyo3(signature = (instrument_id, start=None, end=None, limit=None))]
169    fn py_request_trades<'py>(
170        &self,
171        py: Python<'py>,
172        instrument_id: InstrumentId,
173        start: Option<DateTime<Utc>>,
174        end: Option<DateTime<Utc>>,
175        limit: Option<u32>,
176    ) -> PyResult<Bound<'py, PyAny>> {
177        let client = self.clone();
178
179        pyo3_async_runtimes::tokio::future_into_py(py, async move {
180            let trades = client
181                .request_trades(instrument_id, start, end, limit)
182                .await
183                .map_err(to_pyvalue_err)?;
184            Python::with_gil(|py| {
185                let pylist = PyList::new(py, trades.into_iter().map(|t| t.into_py_any_unwrap(py)))?;
186                Ok(pylist.into_py_any_unwrap(py))
187            })
188        })
189    }
190
191    #[pyo3(name = "request_bars")]
192    #[pyo3(signature = (bar_type, start=None, end=None, limit=None))]
193    fn py_request_bars<'py>(
194        &self,
195        py: Python<'py>,
196        bar_type: BarType,
197        start: Option<DateTime<Utc>>,
198        end: Option<DateTime<Utc>>,
199        limit: Option<u32>,
200    ) -> PyResult<Bound<'py, PyAny>> {
201        let client = self.clone();
202
203        pyo3_async_runtimes::tokio::future_into_py(py, async move {
204            let bars = client
205                .request_bars(bar_type, start, end, limit)
206                .await
207                .map_err(to_pyvalue_err)?;
208            Python::with_gil(|py| {
209                let pylist =
210                    PyList::new(py, bars.into_iter().map(|bar| bar.into_py_any_unwrap(py)))?;
211                Ok(pylist.into_py_any_unwrap(py))
212            })
213        })
214    }
215
216    #[pyo3(name = "request_mark_price")]
217    fn py_request_mark_price<'py>(
218        &self,
219        py: Python<'py>,
220        instrument_id: InstrumentId,
221    ) -> PyResult<Bound<'py, PyAny>> {
222        let client = self.clone();
223
224        pyo3_async_runtimes::tokio::future_into_py(py, async move {
225            let mark_price = client
226                .request_mark_price(instrument_id)
227                .await
228                .map_err(to_pyvalue_err)?;
229            Ok(Python::with_gil(|py| mark_price.into_py_any_unwrap(py)))
230        })
231    }
232
233    #[pyo3(name = "request_index_price")]
234    fn py_request_index_price<'py>(
235        &self,
236        py: Python<'py>,
237        instrument_id: InstrumentId,
238    ) -> PyResult<Bound<'py, PyAny>> {
239        let client = self.clone();
240
241        pyo3_async_runtimes::tokio::future_into_py(py, async move {
242            let index_price = client
243                .request_index_price(instrument_id)
244                .await
245                .map_err(to_pyvalue_err)?;
246            Ok(Python::with_gil(|py| index_price.into_py_any_unwrap(py)))
247        })
248    }
249
250    #[pyo3(name = "request_order_status_reports")]
251    #[pyo3(signature = (account_id, instrument_type=None, instrument_id=None, start=None, end=None, open_only=false, limit=None))]
252    #[allow(clippy::too_many_arguments)]
253    fn py_request_order_status_reports<'py>(
254        &self,
255        py: Python<'py>,
256        account_id: AccountId,
257        instrument_type: Option<OKXInstrumentType>,
258        instrument_id: Option<InstrumentId>,
259        start: Option<DateTime<Utc>>,
260        end: Option<DateTime<Utc>>,
261        open_only: bool,
262        limit: Option<u32>,
263    ) -> PyResult<Bound<'py, PyAny>> {
264        let client = self.clone();
265
266        pyo3_async_runtimes::tokio::future_into_py(py, async move {
267            let reports = client
268                .request_order_status_reports(
269                    account_id,
270                    instrument_type,
271                    instrument_id,
272                    start,
273                    end,
274                    open_only,
275                    limit,
276                )
277                .await
278                .map_err(to_pyvalue_err)?;
279            Python::with_gil(|py| {
280                let pylist =
281                    PyList::new(py, reports.into_iter().map(|t| t.into_py_any_unwrap(py)))?;
282                Ok(pylist.into_py_any_unwrap(py))
283            })
284        })
285    }
286
287    #[pyo3(name = "request_fill_reports")]
288    #[pyo3(signature = (account_id, instrument_type=None, instrument_id=None, start=None, end=None, limit=None))]
289    #[allow(clippy::too_many_arguments)]
290    fn py_request_fill_reports<'py>(
291        &self,
292        py: Python<'py>,
293        account_id: AccountId,
294        instrument_type: Option<OKXInstrumentType>,
295        instrument_id: Option<InstrumentId>,
296        start: Option<DateTime<Utc>>,
297        end: Option<DateTime<Utc>>,
298        limit: Option<u32>,
299    ) -> PyResult<Bound<'py, PyAny>> {
300        let client = self.clone();
301
302        pyo3_async_runtimes::tokio::future_into_py(py, async move {
303            let trades = client
304                .request_fill_reports(
305                    account_id,
306                    instrument_type,
307                    instrument_id,
308                    start,
309                    end,
310                    limit,
311                )
312                .await
313                .map_err(to_pyvalue_err)?;
314            Python::with_gil(|py| {
315                let pylist = PyList::new(py, trades.into_iter().map(|t| t.into_py_any_unwrap(py)))?;
316                Ok(pylist.into_py_any_unwrap(py))
317            })
318        })
319    }
320
321    #[pyo3(name = "request_position_status_reports")]
322    #[pyo3(signature = (account_id, instrument_type=None, instrument_id=None))]
323    fn py_request_position_status_reports<'py>(
324        &self,
325        py: Python<'py>,
326        account_id: AccountId,
327        instrument_type: Option<OKXInstrumentType>,
328        instrument_id: Option<InstrumentId>,
329    ) -> PyResult<Bound<'py, PyAny>> {
330        let client = self.clone();
331
332        pyo3_async_runtimes::tokio::future_into_py(py, async move {
333            let reports = client
334                .request_position_status_reports(account_id, instrument_type, instrument_id)
335                .await
336                .map_err(to_pyvalue_err)?;
337            Python::with_gil(|py| {
338                let pylist =
339                    PyList::new(py, reports.into_iter().map(|t| t.into_py_any_unwrap(py)))?;
340                Ok(pylist.into_py_any_unwrap(py))
341            })
342        })
343    }
344}