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