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