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