nautilus_binance/python/
http_spot.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 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
16//! Python bindings for the Binance Spot HTTP client.
17
18use 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::{common::enums::BinanceEnvironment, spot::http::client::BinanceSpotHttpClient};
30
31#[pymethods]
32impl BinanceSpotHttpClient {
33    #[new]
34    #[pyo3(signature = (
35        environment=BinanceEnvironment::Mainnet,
36        api_key=None,
37        api_secret=None,
38        base_url=None,
39        recv_window=None,
40        timeout_secs=None,
41        proxy_url=None,
42    ))]
43    #[allow(clippy::too_many_arguments)]
44    fn py_new(
45        environment: BinanceEnvironment,
46        api_key: Option<String>,
47        api_secret: Option<String>,
48        base_url: Option<String>,
49        recv_window: Option<u64>,
50        timeout_secs: Option<u64>,
51        proxy_url: Option<String>,
52    ) -> PyResult<Self> {
53        Self::new(
54            environment,
55            api_key,
56            api_secret,
57            base_url,
58            recv_window,
59            timeout_secs,
60            proxy_url,
61        )
62        .map_err(to_pyvalue_err)
63    }
64
65    #[getter]
66    #[pyo3(name = "schema_id")]
67    #[must_use]
68    pub fn py_schema_id(&self) -> u16 {
69        Self::schema_id()
70    }
71
72    #[getter]
73    #[pyo3(name = "schema_version")]
74    #[must_use]
75    pub fn py_schema_version(&self) -> u16 {
76        Self::schema_version()
77    }
78
79    #[pyo3(name = "ping")]
80    fn py_ping<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
81        let client = self.clone();
82        pyo3_async_runtimes::tokio::future_into_py(py, async move {
83            client.ping().await.map_err(to_pyvalue_err)?;
84            Python::attach(|py| Ok(py.None()))
85        })
86    }
87
88    #[pyo3(name = "server_time")]
89    fn py_server_time<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
90        let client = self.clone();
91        pyo3_async_runtimes::tokio::future_into_py(py, async move {
92            let timestamp = client.server_time().await.map_err(to_pyvalue_err)?;
93            Python::attach(|py| Ok(timestamp.into_pyobject(py)?.into_any().unbind()))
94        })
95    }
96
97    #[pyo3(name = "request_instruments")]
98    fn py_request_instruments<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
99        let client = self.clone();
100
101        pyo3_async_runtimes::tokio::future_into_py(py, async move {
102            let instruments = client.request_instruments().await.map_err(to_pyvalue_err)?;
103
104            Python::attach(|py| {
105                let py_instruments: PyResult<Vec<_>> = instruments
106                    .into_iter()
107                    .map(|inst| instrument_any_to_pyobject(py, inst))
108                    .collect();
109                let pylist = PyList::new(py, py_instruments?)?.into_any().unbind();
110                Ok(pylist)
111            })
112        })
113    }
114
115    #[pyo3(name = "request_trades", signature = (instrument_id, limit=None))]
116    fn py_request_trades<'py>(
117        &self,
118        py: Python<'py>,
119        instrument_id: InstrumentId,
120        limit: Option<u32>,
121    ) -> PyResult<Bound<'py, PyAny>> {
122        let client = self.clone();
123
124        pyo3_async_runtimes::tokio::future_into_py(py, async move {
125            let trades = client
126                .request_trades(instrument_id, limit)
127                .await
128                .map_err(to_pyvalue_err)?;
129
130            Python::attach(|py| {
131                let py_trades: PyResult<Vec<_>> = trades
132                    .into_iter()
133                    .map(|tick| tick.into_py_any(py))
134                    .collect();
135                let pylist = PyList::new(py, py_trades?)?.into_any().unbind();
136                Ok(pylist)
137            })
138        })
139    }
140
141    #[pyo3(name = "request_order_status")]
142    #[pyo3(signature = (
143        account_id,
144        instrument_id,
145        venue_order_id=None,
146        client_order_id=None,
147    ))]
148    fn py_request_order_status<'py>(
149        &self,
150        py: Python<'py>,
151        account_id: AccountId,
152        instrument_id: InstrumentId,
153        venue_order_id: Option<VenueOrderId>,
154        client_order_id: Option<ClientOrderId>,
155    ) -> PyResult<Bound<'py, PyAny>> {
156        let client = self.clone();
157
158        pyo3_async_runtimes::tokio::future_into_py(py, async move {
159            let report = client
160                .request_order_status(account_id, instrument_id, venue_order_id, client_order_id)
161                .await
162                .map_err(to_pyvalue_err)?;
163            Python::attach(|py| report.into_py_any(py))
164        })
165    }
166
167    #[pyo3(name = "request_order_status_reports")]
168    #[pyo3(signature = (
169        account_id,
170        instrument_id=None,
171        start=None,
172        end=None,
173        open_only=false,
174        limit=None,
175    ))]
176    #[allow(clippy::too_many_arguments)]
177    fn py_request_order_status_reports<'py>(
178        &self,
179        py: Python<'py>,
180        account_id: AccountId,
181        instrument_id: Option<InstrumentId>,
182        start: Option<DateTime<Utc>>,
183        end: Option<DateTime<Utc>>,
184        open_only: bool,
185        limit: Option<u32>,
186    ) -> PyResult<Bound<'py, PyAny>> {
187        let client = self.clone();
188
189        pyo3_async_runtimes::tokio::future_into_py(py, async move {
190            let reports = client
191                .request_order_status_reports(
192                    account_id,
193                    instrument_id,
194                    start,
195                    end,
196                    open_only,
197                    limit,
198                )
199                .await
200                .map_err(to_pyvalue_err)?;
201
202            Python::attach(|py| {
203                let py_reports: PyResult<Vec<_>> =
204                    reports.into_iter().map(|r| r.into_py_any(py)).collect();
205                let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
206                Ok(pylist)
207            })
208        })
209    }
210
211    #[pyo3(name = "request_fill_reports")]
212    #[pyo3(signature = (
213        account_id,
214        instrument_id,
215        venue_order_id=None,
216        start=None,
217        end=None,
218        limit=None,
219    ))]
220    #[allow(clippy::too_many_arguments)]
221    fn py_request_fill_reports<'py>(
222        &self,
223        py: Python<'py>,
224        account_id: AccountId,
225        instrument_id: InstrumentId,
226        venue_order_id: Option<VenueOrderId>,
227        start: Option<DateTime<Utc>>,
228        end: Option<DateTime<Utc>>,
229        limit: Option<u32>,
230    ) -> PyResult<Bound<'py, PyAny>> {
231        let client = self.clone();
232
233        pyo3_async_runtimes::tokio::future_into_py(py, async move {
234            let reports = client
235                .request_fill_reports(account_id, instrument_id, venue_order_id, start, end, limit)
236                .await
237                .map_err(to_pyvalue_err)?;
238            Python::attach(|py| {
239                let py_reports: PyResult<Vec<_>> =
240                    reports.into_iter().map(|r| r.into_py_any(py)).collect();
241                let pylist = PyList::new(py, py_reports?)?.into_any().unbind();
242                Ok(pylist)
243            })
244        })
245    }
246
247    #[pyo3(name = "request_bars")]
248    #[pyo3(signature = (
249        bar_type,
250        start=None,
251        end=None,
252        limit=None,
253    ))]
254    fn py_request_bars<'py>(
255        &self,
256        py: Python<'py>,
257        bar_type: BarType,
258        start: Option<DateTime<Utc>>,
259        end: Option<DateTime<Utc>>,
260        limit: Option<u32>,
261    ) -> PyResult<Bound<'py, PyAny>> {
262        let client = self.clone();
263
264        pyo3_async_runtimes::tokio::future_into_py(py, async move {
265            let bars = client
266                .request_bars(bar_type, start, end, limit)
267                .await
268                .map_err(to_pyvalue_err)?;
269            Python::attach(|py| {
270                let py_bars: PyResult<Vec<_>> =
271                    bars.into_iter().map(|b| b.into_py_any(py)).collect();
272                let pylist = PyList::new(py, py_bars?)?.into_any().unbind();
273                Ok(pylist)
274            })
275        })
276    }
277
278    #[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))]
279    #[allow(clippy::too_many_arguments)]
280    fn py_submit_order<'py>(
281        &self,
282        py: Python<'py>,
283        account_id: AccountId,
284        instrument_id: InstrumentId,
285        client_order_id: ClientOrderId,
286        order_side: OrderSide,
287        order_type: OrderType,
288        quantity: Quantity,
289        time_in_force: TimeInForce,
290        price: Option<Price>,
291        trigger_price: Option<Price>,
292        post_only: bool,
293    ) -> PyResult<Bound<'py, PyAny>> {
294        let client = self.clone();
295
296        pyo3_async_runtimes::tokio::future_into_py(py, async move {
297            let report = client
298                .submit_order(
299                    account_id,
300                    instrument_id,
301                    client_order_id,
302                    order_side,
303                    order_type,
304                    quantity,
305                    time_in_force,
306                    price,
307                    trigger_price,
308                    post_only,
309                )
310                .await
311                .map_err(to_pyvalue_err)?;
312            Python::attach(|py| report.into_py_any(py))
313        })
314    }
315
316    #[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))]
317    #[allow(clippy::too_many_arguments)]
318    fn py_modify_order<'py>(
319        &self,
320        py: Python<'py>,
321        account_id: AccountId,
322        instrument_id: InstrumentId,
323        venue_order_id: VenueOrderId,
324        client_order_id: ClientOrderId,
325        order_side: OrderSide,
326        order_type: OrderType,
327        quantity: Quantity,
328        time_in_force: TimeInForce,
329        price: Option<Price>,
330    ) -> PyResult<Bound<'py, PyAny>> {
331        let client = self.clone();
332
333        pyo3_async_runtimes::tokio::future_into_py(py, async move {
334            let report = client
335                .modify_order(
336                    account_id,
337                    instrument_id,
338                    venue_order_id,
339                    client_order_id,
340                    order_side,
341                    order_type,
342                    quantity,
343                    time_in_force,
344                    price,
345                )
346                .await
347                .map_err(to_pyvalue_err)?;
348            Python::attach(|py| report.into_py_any(py))
349        })
350    }
351
352    #[pyo3(name = "cancel_order")]
353    #[pyo3(signature = (
354        instrument_id,
355        venue_order_id=None,
356        client_order_id=None,
357    ))]
358    fn py_cancel_order<'py>(
359        &self,
360        py: Python<'py>,
361        instrument_id: InstrumentId,
362        venue_order_id: Option<VenueOrderId>,
363        client_order_id: Option<ClientOrderId>,
364    ) -> PyResult<Bound<'py, PyAny>> {
365        let client = self.clone();
366
367        pyo3_async_runtimes::tokio::future_into_py(py, async move {
368            let order_id = client
369                .cancel_order(instrument_id, venue_order_id, client_order_id)
370                .await
371                .map_err(to_pyvalue_err)?;
372            Python::attach(|py| order_id.into_py_any(py))
373        })
374    }
375
376    #[pyo3(name = "cancel_all_orders")]
377    fn py_cancel_all_orders<'py>(
378        &self,
379        py: Python<'py>,
380        instrument_id: InstrumentId,
381    ) -> PyResult<Bound<'py, PyAny>> {
382        let client = self.clone();
383
384        pyo3_async_runtimes::tokio::future_into_py(py, async move {
385            let order_ids = client
386                .cancel_all_orders(instrument_id)
387                .await
388                .map_err(to_pyvalue_err)?;
389            Python::attach(|py| {
390                let py_ids: PyResult<Vec<_>> =
391                    order_ids.into_iter().map(|id| id.into_py_any(py)).collect();
392                let pylist = PyList::new(py, py_ids?)?.into_any().unbind();
393                Ok(pylist)
394            })
395        })
396    }
397}