1use chrono::{DateTime, Utc};
19use nautilus_core::python::to_pyvalue_err;
20use nautilus_model::{
21 data::BarType,
22 enums::{OrderSide, OrderType, TimeInForce},
23 identifiers::{AccountId, ClientOrderId, InstrumentId, VenueOrderId},
24 python::instruments::instrument_any_to_pyobject,
25 types::{Price, Quantity},
26};
27use pyo3::{IntoPyObjectExt, prelude::*, types::PyList};
28
29use crate::{
30 common::enums::BinanceEnvironment,
31 spot::http::{
32 client::BinanceSpotHttpClient,
33 query::{BatchCancelItem, BatchOrderItem},
34 },
35};
36
37#[pymethods]
38impl BinanceSpotHttpClient {
39 #[new]
40 #[pyo3(signature = (
41 environment=BinanceEnvironment::Mainnet,
42 api_key=None,
43 api_secret=None,
44 base_url=None,
45 recv_window=None,
46 timeout_secs=None,
47 proxy_url=None,
48 ))]
49 #[allow(clippy::too_many_arguments)]
50 fn py_new(
51 environment: BinanceEnvironment,
52 api_key: Option<String>,
53 api_secret: Option<String>,
54 base_url: Option<String>,
55 recv_window: Option<u64>,
56 timeout_secs: Option<u64>,
57 proxy_url: Option<String>,
58 ) -> PyResult<Self> {
59 Self::new(
60 environment,
61 api_key,
62 api_secret,
63 base_url,
64 recv_window,
65 timeout_secs,
66 proxy_url,
67 )
68 .map_err(to_pyvalue_err)
69 }
70
71 #[getter]
72 #[pyo3(name = "schema_id")]
73 #[must_use]
74 pub fn py_schema_id(&self) -> u16 {
75 Self::schema_id()
76 }
77
78 #[getter]
79 #[pyo3(name = "schema_version")]
80 #[must_use]
81 pub fn py_schema_version(&self) -> u16 {
82 Self::schema_version()
83 }
84
85 #[pyo3(name = "ping")]
86 fn py_ping<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
87 let client = self.clone();
88 pyo3_async_runtimes::tokio::future_into_py(py, async move {
89 client.ping().await.map_err(to_pyvalue_err)?;
90 Python::attach(|py| Ok(py.None()))
91 })
92 }
93
94 #[pyo3(name = "server_time")]
95 fn py_server_time<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
96 let client = self.clone();
97 pyo3_async_runtimes::tokio::future_into_py(py, async move {
98 let timestamp = client.server_time().await.map_err(to_pyvalue_err)?;
99 Python::attach(|py| Ok(timestamp.into_pyobject(py)?.into_any().unbind()))
100 })
101 }
102
103 #[pyo3(name = "request_instruments")]
104 fn py_request_instruments<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
105 let client = self.clone();
106
107 pyo3_async_runtimes::tokio::future_into_py(py, async move {
108 let instruments = client.request_instruments().await.map_err(to_pyvalue_err)?;
109
110 Python::attach(|py| {
111 let py_instruments: PyResult<Vec<_>> = instruments
112 .into_iter()
113 .map(|inst| instrument_any_to_pyobject(py, inst))
114 .collect();
115 let pylist = PyList::new(py, py_instruments?)?.into_any().unbind();
116 Ok(pylist)
117 })
118 })
119 }
120
121 #[pyo3(name = "request_trades", signature = (instrument_id, limit=None))]
122 fn py_request_trades<'py>(
123 &self,
124 py: Python<'py>,
125 instrument_id: InstrumentId,
126 limit: Option<u32>,
127 ) -> PyResult<Bound<'py, PyAny>> {
128 let client = self.clone();
129
130 pyo3_async_runtimes::tokio::future_into_py(py, async move {
131 let trades = client
132 .request_trades(instrument_id, limit)
133 .await
134 .map_err(to_pyvalue_err)?;
135
136 Python::attach(|py| {
137 let py_trades: PyResult<Vec<_>> = trades
138 .into_iter()
139 .map(|tick| tick.into_py_any(py))
140 .collect();
141 let pylist = PyList::new(py, py_trades?)?.into_any().unbind();
142 Ok(pylist)
143 })
144 })
145 }
146
147 #[pyo3(name = "request_bars")]
148 #[pyo3(signature = (
149 bar_type,
150 start=None,
151 end=None,
152 limit=None,
153 ))]
154 fn py_request_bars<'py>(
155 &self,
156 py: Python<'py>,
157 bar_type: BarType,
158 start: Option<DateTime<Utc>>,
159 end: Option<DateTime<Utc>>,
160 limit: Option<u32>,
161 ) -> PyResult<Bound<'py, PyAny>> {
162 let client = self.clone();
163
164 pyo3_async_runtimes::tokio::future_into_py(py, async move {
165 let bars = client
166 .request_bars(bar_type, start, end, limit)
167 .await
168 .map_err(to_pyvalue_err)?;
169 Python::attach(|py| {
170 let py_bars: PyResult<Vec<_>> =
171 bars.into_iter().map(|b| b.into_py_any(py)).collect();
172 let pylist = PyList::new(py, py_bars?)?.into_any().unbind();
173 Ok(pylist)
174 })
175 })
176 }
177
178 #[pyo3(name = "request_account_state")]
179 fn py_request_account_state<'py>(
180 &self,
181 py: Python<'py>,
182 account_id: AccountId,
183 ) -> PyResult<Bound<'py, PyAny>> {
184 let client = self.clone();
185
186 pyo3_async_runtimes::tokio::future_into_py(py, async move {
187 let account_state = client
188 .request_account_state(account_id)
189 .await
190 .map_err(to_pyvalue_err)?;
191
192 Python::attach(|py| account_state.into_py_any(py))
193 })
194 }
195
196 #[pyo3(name = "request_order_status")]
197 #[pyo3(signature = (
198 account_id,
199 instrument_id,
200 venue_order_id=None,
201 client_order_id=None,
202 ))]
203 fn py_request_order_status<'py>(
204 &self,
205 py: Python<'py>,
206 account_id: AccountId,
207 instrument_id: InstrumentId,
208 venue_order_id: Option<VenueOrderId>,
209 client_order_id: Option<ClientOrderId>,
210 ) -> PyResult<Bound<'py, PyAny>> {
211 let client = self.clone();
212
213 pyo3_async_runtimes::tokio::future_into_py(py, async move {
214 let report = client
215 .request_order_status_report(
216 account_id,
217 instrument_id,
218 venue_order_id,
219 client_order_id,
220 )
221 .await
222 .map_err(to_pyvalue_err)?;
223 Python::attach(|py| report.into_py_any(py))
224 })
225 }
226
227 #[pyo3(name = "request_order_status_reports")]
228 #[pyo3(signature = (
229 account_id,
230 instrument_id=None,
231 start=None,
232 end=None,
233 open_only=false,
234 limit=None,
235 ))]
236 #[allow(clippy::too_many_arguments)]
237 fn py_request_order_status_reports<'py>(
238 &self,
239 py: Python<'py>,
240 account_id: AccountId,
241 instrument_id: Option<InstrumentId>,
242 start: Option<DateTime<Utc>>,
243 end: Option<DateTime<Utc>>,
244 open_only: bool,
245 limit: Option<u32>,
246 ) -> PyResult<Bound<'py, PyAny>> {
247 let client = self.clone();
248
249 pyo3_async_runtimes::tokio::future_into_py(py, async move {
250 let reports = client
251 .request_order_status_reports(
252 account_id,
253 instrument_id,
254 start,
255 end,
256 open_only,
257 limit,
258 )
259 .await
260 .map_err(to_pyvalue_err)?;
261
262 Python::attach(|py| {
263 let py_reports: PyResult<Vec<_>> =
264 reports.into_iter().map(|r| r.into_py_any(py)).collect();
265 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
266 Ok(pylist)
267 })
268 })
269 }
270
271 #[pyo3(name = "request_fill_reports")]
272 #[pyo3(signature = (
273 account_id,
274 instrument_id,
275 venue_order_id=None,
276 start=None,
277 end=None,
278 limit=None,
279 ))]
280 #[allow(clippy::too_many_arguments)]
281 fn py_request_fill_reports<'py>(
282 &self,
283 py: Python<'py>,
284 account_id: AccountId,
285 instrument_id: InstrumentId,
286 venue_order_id: Option<VenueOrderId>,
287 start: Option<DateTime<Utc>>,
288 end: Option<DateTime<Utc>>,
289 limit: Option<u32>,
290 ) -> PyResult<Bound<'py, PyAny>> {
291 let client = self.clone();
292
293 pyo3_async_runtimes::tokio::future_into_py(py, async move {
294 let reports = client
295 .request_fill_reports(account_id, instrument_id, venue_order_id, start, end, limit)
296 .await
297 .map_err(to_pyvalue_err)?;
298 Python::attach(|py| {
299 let py_reports: PyResult<Vec<_>> =
300 reports.into_iter().map(|r| r.into_py_any(py)).collect();
301 let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
302 Ok(pylist)
303 })
304 })
305 }
306
307 #[pyo3(name = "submit_order", signature = (account_id, instrument_id, client_order_id, order_side, order_type, quantity, time_in_force, price=None, trigger_price=None, post_only=false))]
308 #[allow(clippy::too_many_arguments)]
309 fn py_submit_order<'py>(
310 &self,
311 py: Python<'py>,
312 account_id: AccountId,
313 instrument_id: InstrumentId,
314 client_order_id: ClientOrderId,
315 order_side: OrderSide,
316 order_type: OrderType,
317 quantity: Quantity,
318 time_in_force: TimeInForce,
319 price: Option<Price>,
320 trigger_price: Option<Price>,
321 post_only: bool,
322 ) -> PyResult<Bound<'py, PyAny>> {
323 let client = self.clone();
324
325 pyo3_async_runtimes::tokio::future_into_py(py, async move {
326 let report = client
327 .submit_order(
328 account_id,
329 instrument_id,
330 client_order_id,
331 order_side,
332 order_type,
333 quantity,
334 time_in_force,
335 price,
336 trigger_price,
337 post_only,
338 )
339 .await
340 .map_err(to_pyvalue_err)?;
341 Python::attach(|py| report.into_py_any(py))
342 })
343 }
344
345 #[pyo3(name = "modify_order", signature = (account_id, instrument_id, venue_order_id, client_order_id, order_side, order_type, quantity, time_in_force, price=None))]
346 #[allow(clippy::too_many_arguments)]
347 fn py_modify_order<'py>(
348 &self,
349 py: Python<'py>,
350 account_id: AccountId,
351 instrument_id: InstrumentId,
352 venue_order_id: VenueOrderId,
353 client_order_id: ClientOrderId,
354 order_side: OrderSide,
355 order_type: OrderType,
356 quantity: Quantity,
357 time_in_force: TimeInForce,
358 price: Option<Price>,
359 ) -> PyResult<Bound<'py, PyAny>> {
360 let client = self.clone();
361
362 pyo3_async_runtimes::tokio::future_into_py(py, async move {
363 let report = client
364 .modify_order(
365 account_id,
366 instrument_id,
367 venue_order_id,
368 client_order_id,
369 order_side,
370 order_type,
371 quantity,
372 time_in_force,
373 price,
374 )
375 .await
376 .map_err(to_pyvalue_err)?;
377 Python::attach(|py| report.into_py_any(py))
378 })
379 }
380
381 #[pyo3(name = "cancel_order")]
382 #[pyo3(signature = (
383 instrument_id,
384 venue_order_id=None,
385 client_order_id=None,
386 ))]
387 fn py_cancel_order<'py>(
388 &self,
389 py: Python<'py>,
390 instrument_id: InstrumentId,
391 venue_order_id: Option<VenueOrderId>,
392 client_order_id: Option<ClientOrderId>,
393 ) -> PyResult<Bound<'py, PyAny>> {
394 let client = self.clone();
395
396 pyo3_async_runtimes::tokio::future_into_py(py, async move {
397 let order_id = client
398 .cancel_order(instrument_id, venue_order_id, client_order_id)
399 .await
400 .map_err(to_pyvalue_err)?;
401 Python::attach(|py| order_id.into_py_any(py))
402 })
403 }
404
405 #[pyo3(name = "cancel_all_orders")]
406 fn py_cancel_all_orders<'py>(
407 &self,
408 py: Python<'py>,
409 instrument_id: InstrumentId,
410 ) -> PyResult<Bound<'py, PyAny>> {
411 let client = self.clone();
412
413 pyo3_async_runtimes::tokio::future_into_py(py, async move {
414 let order_ids = client
415 .cancel_all_orders(instrument_id)
416 .await
417 .map_err(to_pyvalue_err)?;
418 Python::attach(|py| {
419 let py_ids: PyResult<Vec<_>> =
420 order_ids.into_iter().map(|id| id.into_py_any(py)).collect();
421 let pylist = PyList::new(py, py_ids?)?.into_any().unbind();
422 Ok(pylist)
423 })
424 })
425 }
426
427 #[pyo3(name = "batch_submit_orders")]
428 fn py_batch_submit_orders<'py>(
429 &self,
430 py: Python<'py>,
431 orders: Vec<BatchOrderItem>,
432 ) -> PyResult<Bound<'py, PyAny>> {
433 let client = self.clone();
434
435 pyo3_async_runtimes::tokio::future_into_py(py, async move {
436 let results = client
437 .submit_order_list(&orders)
438 .await
439 .map_err(to_pyvalue_err)?;
440
441 Python::attach(|py| {
442 let py_results: Vec<_> = results
443 .into_iter()
444 .map(|r| match r {
445 crate::spot::http::models::BatchOrderResult::Success(order) => {
446 let dict = pyo3::types::PyDict::new(py);
447 dict.set_item("success", true).ok();
448 dict.set_item("order_id", order.order_id).ok();
449 dict.set_item("client_order_id", &order.client_order_id)
450 .ok();
451 dict.set_item("symbol", &order.symbol).ok();
452 dict.into_any().unbind()
453 }
454 crate::spot::http::models::BatchOrderResult::Error(err) => {
455 let dict = pyo3::types::PyDict::new(py);
456 dict.set_item("success", false).ok();
457 dict.set_item("code", err.code).ok();
458 dict.set_item("msg", &err.msg).ok();
459 dict.into_any().unbind()
460 }
461 })
462 .collect();
463 let pylist = PyList::new(py, py_results)?.into_any().unbind();
464 Ok(pylist)
465 })
466 })
467 }
468
469 #[pyo3(name = "batch_cancel_orders")]
470 fn py_batch_cancel_orders<'py>(
471 &self,
472 py: Python<'py>,
473 cancels: Vec<BatchCancelItem>,
474 ) -> PyResult<Bound<'py, PyAny>> {
475 let client = self.clone();
476
477 pyo3_async_runtimes::tokio::future_into_py(py, async move {
478 let results = client
479 .batch_cancel_orders(&cancels)
480 .await
481 .map_err(to_pyvalue_err)?;
482
483 Python::attach(|py| {
484 let py_results: Vec<_> = results
485 .into_iter()
486 .map(|r| match r {
487 crate::spot::http::models::BatchCancelResult::Success(cancel) => {
488 let dict = pyo3::types::PyDict::new(py);
489 dict.set_item("success", true).ok();
490 dict.set_item("order_id", cancel.order_id).ok();
491 dict.set_item("orig_client_order_id", &cancel.orig_client_order_id)
492 .ok();
493 dict.set_item("symbol", &cancel.symbol).ok();
494 dict.into_any().unbind()
495 }
496 crate::spot::http::models::BatchCancelResult::Error(err) => {
497 let dict = pyo3::types::PyDict::new(py);
498 dict.set_item("success", false).ok();
499 dict.set_item("code", err.code).ok();
500 dict.set_item("msg", &err.msg).ok();
501 dict.into_any().unbind()
502 }
503 })
504 .collect();
505 let pylist = PyList::new(py, py_results)?.into_any().unbind();
506 Ok(pylist)
507 })
508 })
509 }
510}