nautilus_coinbase_intx/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, serialization::to_dict_pyo3, to_pyvalue_err};
18use nautilus_model::{
19    enums::{OrderSide, OrderType, TimeInForce},
20    identifiers::{AccountId, ClientOrderId, Symbol, VenueOrderId},
21    python::instruments::{instrument_any_to_pyobject, pyobject_to_instrument_any},
22    types::{Price, Quantity},
23};
24use pyo3::{prelude::*, types::PyList};
25
26use crate::http::client::CoinbaseIntxHttpClient;
27
28#[pymethods]
29impl CoinbaseIntxHttpClient {
30    #[new]
31    #[pyo3(signature = (api_key=None, api_secret=None, api_passphrase=None, base_url=None, timeout_secs=None))]
32    fn py_new(
33        api_key: Option<String>,
34        api_secret: Option<String>,
35        api_passphrase: Option<String>,
36        base_url: Option<String>,
37        timeout_secs: Option<u64>,
38    ) -> PyResult<Self> {
39        Self::with_credentials(api_key, api_secret, api_passphrase, base_url, timeout_secs)
40            .map_err(to_pyvalue_err)
41    }
42
43    #[getter]
44    #[pyo3(name = "base_url")]
45    pub fn py_base_url(&self) -> &str {
46        self.base_url()
47    }
48
49    #[getter]
50    #[pyo3(name = "api_key")]
51    pub fn py_api_key(&self) -> Option<&str> {
52        self.api_key()
53    }
54
55    #[pyo3(name = "is_initialized")]
56    pub fn py_is_initialized(&self) -> bool {
57        self.is_initialized()
58    }
59
60    #[pyo3(name = "get_cached_symbols")]
61    pub fn py_get_cached_symbols(&self) -> Vec<String> {
62        self.get_cached_symbols()
63    }
64
65    #[pyo3(name = "add_instrument")]
66    pub fn py_add_instrument(&mut self, py: Python<'_>, instrument: PyObject) -> PyResult<()> {
67        self.add_instrument(pyobject_to_instrument_any(py, instrument)?);
68        Ok(())
69    }
70
71    #[pyo3(name = "list_portfolios")]
72    pub fn py_list_portfolios<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
73        let client = self.clone();
74
75        pyo3_async_runtimes::tokio::future_into_py(py, async move {
76            let response = client.list_portfolios().await.map_err(to_pyvalue_err)?;
77
78            Python::with_gil(|py| {
79                let py_list = PyList::empty(py);
80
81                for portfolio in response {
82                    let dict = to_dict_pyo3(py, &portfolio)?;
83                    py_list.append(dict)?;
84                }
85
86                Ok(py_list.into_any().unbind())
87            })
88        })
89    }
90
91    #[pyo3(name = "request_account_state")]
92    fn py_request_account_state<'py>(
93        &self,
94        py: Python<'py>,
95        account_id: AccountId,
96    ) -> PyResult<Bound<'py, PyAny>> {
97        let client = self.clone();
98
99        pyo3_async_runtimes::tokio::future_into_py(py, async move {
100            let account_state = client
101                .request_account_state(account_id)
102                .await
103                .map_err(to_pyvalue_err)?;
104
105            Ok(Python::with_gil(|py| account_state.into_py_any_unwrap(py)))
106        })
107    }
108
109    #[pyo3(name = "request_instruments")]
110    fn py_request_instruments<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
111        let client = self.clone();
112
113        pyo3_async_runtimes::tokio::future_into_py(py, async move {
114            let instruments = client.request_instruments().await.map_err(to_pyvalue_err)?;
115
116            Python::with_gil(|py| {
117                let py_instruments: PyResult<Vec<_>> = instruments
118                    .into_iter()
119                    .map(|inst| instrument_any_to_pyobject(py, inst))
120                    .collect();
121                let pylist = PyList::new(py, py_instruments?)
122                    .unwrap()
123                    .into_any()
124                    .unbind();
125                Ok(pylist)
126            })
127        })
128    }
129
130    #[pyo3(name = "request_instrument")]
131    fn py_request_instrument<'py>(
132        &self,
133        py: Python<'py>,
134        symbol: Symbol,
135    ) -> PyResult<Bound<'py, PyAny>> {
136        let client = self.clone();
137
138        pyo3_async_runtimes::tokio::future_into_py(py, async move {
139            let instrument = client
140                .request_instrument(&symbol)
141                .await
142                .map_err(to_pyvalue_err)?;
143
144            Ok(Python::with_gil(|py| {
145                instrument_any_to_pyobject(py, instrument)
146                    .expect("Failed parsing instrument")
147                    .into_py_any_unwrap(py)
148            }))
149        })
150    }
151
152    #[pyo3(name = "request_order_status_report")]
153    fn py_request_order_status_report<'py>(
154        &self,
155        py: Python<'py>,
156        account_id: AccountId,
157        venue_order_id: VenueOrderId,
158    ) -> PyResult<Bound<'py, PyAny>> {
159        let client = self.clone();
160
161        pyo3_async_runtimes::tokio::future_into_py(py, async move {
162            let report = client
163                .request_order_status_report(account_id, venue_order_id)
164                .await
165                .map_err(to_pyvalue_err)?;
166
167            Python::with_gil(|py| Ok(report.into_py_any_unwrap(py)))
168        })
169    }
170
171    #[pyo3(name = "request_order_status_reports")]
172    #[pyo3(signature = (account_id, symbol))]
173    fn py_request_order_status_reports<'py>(
174        &self,
175        py: Python<'py>,
176        account_id: AccountId,
177        symbol: Symbol,
178    ) -> PyResult<Bound<'py, PyAny>> {
179        let client = self.clone();
180
181        pyo3_async_runtimes::tokio::future_into_py(py, async move {
182            let reports = client
183                .request_order_status_reports(account_id, symbol)
184                .await
185                .map_err(to_pyvalue_err)?;
186
187            Python::with_gil(|py| {
188                let pylist =
189                    PyList::new(py, reports.into_iter().map(|t| t.into_py_any_unwrap(py)))?;
190                Ok(pylist.into_py_any_unwrap(py))
191            })
192        })
193    }
194
195    #[pyo3(name = "request_fill_reports")]
196    #[pyo3(signature = (account_id, client_order_id=None, start=None))]
197    fn py_request_fill_reports<'py>(
198        &self,
199        py: Python<'py>,
200        account_id: AccountId,
201        client_order_id: Option<ClientOrderId>,
202        start: Option<DateTime<Utc>>,
203    ) -> PyResult<Bound<'py, PyAny>> {
204        let client = self.clone();
205
206        pyo3_async_runtimes::tokio::future_into_py(py, async move {
207            let reports = client
208                .request_fill_reports(account_id, client_order_id, start)
209                .await
210                .map_err(to_pyvalue_err)?;
211
212            Python::with_gil(|py| {
213                let pylist =
214                    PyList::new(py, reports.into_iter().map(|t| t.into_py_any_unwrap(py)))?;
215                Ok(pylist.into_py_any_unwrap(py))
216            })
217        })
218    }
219
220    #[pyo3(name = "request_position_status_report")]
221    fn py_request_position_status_report<'py>(
222        &self,
223        py: Python<'py>,
224        account_id: AccountId,
225        symbol: Symbol,
226    ) -> PyResult<Bound<'py, PyAny>> {
227        let client = self.clone();
228
229        pyo3_async_runtimes::tokio::future_into_py(py, async move {
230            let report = client
231                .request_position_status_report(account_id, symbol)
232                .await
233                .map_err(to_pyvalue_err)?;
234
235            Python::with_gil(|py| Ok(report.into_py_any_unwrap(py)))
236        })
237    }
238
239    #[pyo3(name = "request_position_status_reports")]
240    fn py_request_position_status_reports<'py>(
241        &self,
242        py: Python<'py>,
243        account_id: AccountId,
244    ) -> PyResult<Bound<'py, PyAny>> {
245        let client = self.clone();
246
247        pyo3_async_runtimes::tokio::future_into_py(py, async move {
248            let reports = client
249                .request_position_status_reports(account_id)
250                .await
251                .map_err(to_pyvalue_err)?;
252
253            Python::with_gil(|py| {
254                let pylist =
255                    PyList::new(py, reports.into_iter().map(|t| t.into_py_any_unwrap(py)))?;
256                Ok(pylist.into_py_any_unwrap(py))
257            })
258        })
259    }
260
261    #[allow(clippy::too_many_arguments)]
262    #[pyo3(name = "submit_order")]
263    #[pyo3(signature = (account_id, symbol, client_order_id, order_type, order_side, quantity, time_in_force, expire_time=None, price=None, trigger_price=None, post_only=None, reduce_only=None))]
264    fn py_submit_order<'py>(
265        &self,
266        py: Python<'py>,
267        account_id: AccountId,
268        symbol: Symbol,
269        client_order_id: ClientOrderId,
270        order_type: OrderType,
271        order_side: OrderSide,
272        quantity: Quantity,
273        time_in_force: TimeInForce,
274        expire_time: Option<DateTime<Utc>>,
275        price: Option<Price>,
276        trigger_price: Option<Price>,
277        post_only: Option<bool>,
278        reduce_only: Option<bool>,
279    ) -> PyResult<Bound<'py, PyAny>> {
280        let client = self.clone();
281
282        pyo3_async_runtimes::tokio::future_into_py(py, async move {
283            client
284                .submit_order(
285                    account_id,
286                    client_order_id,
287                    symbol,
288                    order_side,
289                    order_type,
290                    quantity,
291                    time_in_force,
292                    expire_time,
293                    price,
294                    trigger_price,
295                    post_only,
296                    reduce_only,
297                )
298                .await
299                .map_err(to_pyvalue_err)
300        })
301    }
302
303    #[pyo3(name = "cancel_order")]
304    fn py_cancel_order<'py>(
305        &self,
306        py: Python<'py>,
307        account_id: AccountId,
308        client_order_id: ClientOrderId,
309    ) -> PyResult<Bound<'py, PyAny>> {
310        let client = self.clone();
311
312        pyo3_async_runtimes::tokio::future_into_py(py, async move {
313            client
314                .cancel_order(account_id, client_order_id)
315                .await
316                .map_err(to_pyvalue_err)
317        })
318    }
319
320    #[pyo3(name = "cancel_orders")]
321    #[pyo3(signature = (account_id, symbol, order_side=None))]
322    fn py_cancel_orders<'py>(
323        &self,
324        py: Python<'py>,
325        account_id: AccountId,
326        symbol: Symbol,
327        order_side: Option<OrderSide>,
328    ) -> PyResult<Bound<'py, PyAny>> {
329        let client = self.clone();
330
331        pyo3_async_runtimes::tokio::future_into_py(py, async move {
332            client
333                .cancel_orders(account_id, symbol, order_side)
334                .await
335                .map_err(to_pyvalue_err)
336        })
337    }
338
339    #[pyo3(name = "modify_order")]
340    #[pyo3(signature = (account_id, client_order_id, new_client_order_id, price=None, trigger_price=None, quantity=None))]
341    #[allow(clippy::too_many_arguments)]
342    fn py_modify_order<'py>(
343        &self,
344        py: Python<'py>,
345        account_id: AccountId,
346        client_order_id: ClientOrderId,
347        new_client_order_id: ClientOrderId,
348        price: Option<Price>,
349        trigger_price: Option<Price>,
350        quantity: Option<Quantity>,
351    ) -> PyResult<Bound<'py, PyAny>> {
352        let client = self.clone();
353
354        pyo3_async_runtimes::tokio::future_into_py(py, async move {
355            client
356                .modify_order(
357                    account_id,
358                    client_order_id,
359                    new_client_order_id,
360                    price,
361                    trigger_price,
362                    quantity,
363                )
364                .await
365                .map_err(to_pyvalue_err)
366        })
367    }
368}