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