Skip to main content

nautilus_binance/python/
types.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
16use std::{
17    collections::hash_map::DefaultHasher,
18    hash::{Hash, Hasher},
19};
20
21use nautilus_core::python::{IntoPyObjectNautilusExt, serialization::from_dict_pyo3};
22use nautilus_model::{
23    data::bar::BarType,
24    identifiers::InstrumentId,
25    types::{Price, Quantity},
26};
27use pyo3::{
28    IntoPyObjectExt,
29    basic::CompareOp,
30    prelude::*,
31    types::{PyDict, PyList},
32};
33use rust_decimal::Decimal;
34
35use crate::{
36    common::bar::BinanceBar,
37    data_types::{
38        BinanceFuturesMarkPriceUpdate, BinanceFuturesOpenInterestHist,
39        BinanceFuturesOpenInterestHistPoint, BinanceSpotTicker,
40    },
41};
42
43#[pymethods]
44#[pyo3_stub_gen::derive::gen_stub_pymethods]
45impl BinanceBar {
46    fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
47        match op {
48            CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
49            CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
50            _ => py.NotImplemented(),
51        }
52    }
53
54    fn __hash__(&self) -> isize {
55        let mut hasher = DefaultHasher::new();
56        self.bar_type.hash(&mut hasher);
57        self.ts_event.hash(&mut hasher);
58        hasher.finish() as isize
59    }
60
61    fn __repr__(&self) -> String {
62        format!(
63            "{}(bar_type={}, open={}, high={}, low={}, close={}, volume={}, quote_volume={}, count={}, taker_buy_base_volume={}, taker_buy_quote_volume={}, ts_event={}, ts_init={})",
64            stringify!(BinanceBar),
65            self.bar_type,
66            self.open,
67            self.high,
68            self.low,
69            self.close,
70            self.volume,
71            self.quote_volume,
72            self.count,
73            self.taker_buy_base_volume,
74            self.taker_buy_quote_volume,
75            self.ts_event,
76            self.ts_init,
77        )
78    }
79
80    fn __str__(&self) -> String {
81        self.__repr__()
82    }
83
84    #[getter]
85    #[pyo3(name = "bar_type")]
86    fn py_bar_type(&self) -> BarType {
87        self.bar_type
88    }
89
90    #[getter]
91    #[pyo3(name = "open")]
92    const fn py_open(&self) -> Price {
93        self.open
94    }
95
96    #[getter]
97    #[pyo3(name = "high")]
98    const fn py_high(&self) -> Price {
99        self.high
100    }
101
102    #[getter]
103    #[pyo3(name = "low")]
104    const fn py_low(&self) -> Price {
105        self.low
106    }
107
108    #[getter]
109    #[pyo3(name = "close")]
110    const fn py_close(&self) -> Price {
111        self.close
112    }
113
114    #[getter]
115    #[pyo3(name = "volume")]
116    const fn py_volume(&self) -> Quantity {
117        self.volume
118    }
119
120    #[getter]
121    #[pyo3(name = "quote_volume")]
122    fn py_quote_volume(&self) -> Decimal {
123        self.quote_volume
124    }
125
126    #[getter]
127    #[pyo3(name = "count")]
128    const fn py_count(&self) -> u64 {
129        self.count
130    }
131
132    #[getter]
133    #[pyo3(name = "taker_buy_base_volume")]
134    fn py_taker_buy_base_volume(&self) -> Decimal {
135        self.taker_buy_base_volume
136    }
137
138    #[getter]
139    #[pyo3(name = "taker_buy_quote_volume")]
140    fn py_taker_buy_quote_volume(&self) -> Decimal {
141        self.taker_buy_quote_volume
142    }
143
144    #[getter]
145    #[pyo3(name = "ts_event")]
146    const fn py_ts_event(&self) -> u64 {
147        self.ts_event.as_u64()
148    }
149
150    #[getter]
151    #[pyo3(name = "ts_init")]
152    const fn py_ts_init(&self) -> u64 {
153        self.ts_init.as_u64()
154    }
155
156    #[staticmethod]
157    #[pyo3(name = "from_dict")]
158    fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
159        from_dict_pyo3(py, values)
160    }
161
162    /// # Errors
163    ///
164    /// Returns a `PyErr` if generating the Python dictionary fails.
165    #[pyo3(name = "to_dict")]
166    pub fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
167        let dict = PyDict::new(py);
168        dict.set_item("type", stringify!(BinanceBar))?;
169        dict.set_item("bar_type", self.bar_type.to_string())?;
170        dict.set_item("open", self.open.to_string())?;
171        dict.set_item("high", self.high.to_string())?;
172        dict.set_item("low", self.low.to_string())?;
173        dict.set_item("close", self.close.to_string())?;
174        dict.set_item("volume", self.volume.to_string())?;
175        dict.set_item("quote_volume", self.quote_volume.to_string())?;
176        dict.set_item("count", self.count)?;
177        dict.set_item(
178            "taker_buy_base_volume",
179            self.taker_buy_base_volume.to_string(),
180        )?;
181        dict.set_item(
182            "taker_buy_quote_volume",
183            self.taker_buy_quote_volume.to_string(),
184        )?;
185        dict.set_item("ts_event", self.ts_event.as_u64())?;
186        dict.set_item("ts_init", self.ts_init.as_u64())?;
187        Ok(dict.into())
188    }
189}
190
191#[pymethods]
192#[pyo3_stub_gen::derive::gen_stub_pymethods]
193impl BinanceSpotTicker {
194    #[getter]
195    fn instrument_id(&self) -> InstrumentId {
196        self.instrument_id
197    }
198    #[getter]
199    fn price_change(&self) -> Decimal {
200        self.price_change
201    }
202    #[getter]
203    fn price_change_percent(&self) -> Decimal {
204        self.price_change_percent
205    }
206    #[getter]
207    fn weighted_avg_price(&self) -> Decimal {
208        self.weighted_avg_price
209    }
210    #[getter]
211    fn prev_close_price(&self) -> Decimal {
212        self.prev_close_price
213    }
214    #[getter]
215    fn last_price(&self) -> Decimal {
216        self.last_price
217    }
218    #[getter]
219    fn last_qty(&self) -> Decimal {
220        self.last_qty
221    }
222    #[getter]
223    fn bid_price(&self) -> Decimal {
224        self.bid_price
225    }
226    #[getter]
227    fn bid_qty(&self) -> Decimal {
228        self.bid_qty
229    }
230    #[getter]
231    fn ask_price(&self) -> Decimal {
232        self.ask_price
233    }
234    #[getter]
235    fn ask_qty(&self) -> Decimal {
236        self.ask_qty
237    }
238    #[getter]
239    fn open_price(&self) -> Decimal {
240        self.open_price
241    }
242    #[getter]
243    fn high_price(&self) -> Decimal {
244        self.high_price
245    }
246    #[getter]
247    fn low_price(&self) -> Decimal {
248        self.low_price
249    }
250    #[getter]
251    fn volume(&self) -> Decimal {
252        self.volume
253    }
254    #[getter]
255    fn quote_volume(&self) -> Decimal {
256        self.quote_volume
257    }
258    #[getter]
259    fn open_time(&self) -> u64 {
260        self.open_time.as_u64()
261    }
262    #[getter]
263    fn close_time(&self) -> u64 {
264        self.close_time.as_u64()
265    }
266    #[getter]
267    fn first_trade_id(&self) -> i64 {
268        self.first_trade_id
269    }
270    #[getter]
271    fn last_trade_id(&self) -> i64 {
272        self.last_trade_id
273    }
274    #[getter]
275    fn num_trades(&self) -> i64 {
276        self.num_trades
277    }
278    #[getter]
279    fn ts_event(&self) -> u64 {
280        self.ts_event.as_u64()
281    }
282    #[getter]
283    fn ts_init(&self) -> u64 {
284        self.ts_init.as_u64()
285    }
286}
287
288#[pymethods]
289#[pyo3_stub_gen::derive::gen_stub_pymethods]
290impl BinanceFuturesMarkPriceUpdate {
291    #[getter]
292    fn instrument_id(&self) -> InstrumentId {
293        self.instrument_id
294    }
295    #[getter]
296    fn mark_price(&self) -> Price {
297        self.mark_price
298    }
299    #[getter]
300    fn index_price(&self) -> Price {
301        self.index_price
302    }
303    #[getter]
304    fn estimated_settle_price(&self) -> Price {
305        self.estimated_settle_price
306    }
307    #[getter]
308    fn funding_rate(&self) -> Decimal {
309        self.funding_rate
310    }
311    #[getter]
312    fn next_funding_time(&self) -> Option<u64> {
313        self.next_funding_time.map(|time| time.as_u64())
314    }
315    #[getter]
316    fn ts_event(&self) -> u64 {
317        self.ts_event.as_u64()
318    }
319    #[getter]
320    fn ts_init(&self) -> u64 {
321        self.ts_init.as_u64()
322    }
323}
324
325#[pymethods]
326#[pyo3_stub_gen::derive::gen_stub_pymethods]
327impl BinanceFuturesOpenInterestHistPoint {
328    #[getter]
329    #[pyo3(name = "sum_open_interest")]
330    fn py_sum_open_interest(&self) -> Decimal {
331        self.sum_open_interest
332    }
333
334    #[getter]
335    #[pyo3(name = "sum_open_interest_value")]
336    fn py_sum_open_interest_value(&self) -> Decimal {
337        self.sum_open_interest_value
338    }
339
340    #[getter]
341    #[pyo3(name = "ts_event")]
342    fn py_ts_event(&self) -> u64 {
343        self.ts_event.as_u64()
344    }
345}
346
347#[pymethods]
348#[pyo3_stub_gen::derive::gen_stub_pymethods]
349impl BinanceFuturesOpenInterestHist {
350    #[getter]
351    #[pyo3(name = "instrument_id")]
352    fn py_instrument_id(&self) -> InstrumentId {
353        self.instrument_id
354    }
355
356    #[getter]
357    #[pyo3(name = "period")]
358    fn py_period(&self) -> String {
359        self.period.clone()
360    }
361
362    #[getter]
363    #[pyo3(name = "points")]
364    fn py_points(&self, py: Python<'_>) -> PyResult<Py<PyList>> {
365        let points = self
366            .points
367            .iter()
368            .cloned()
369            .map(|point| point.into_py_any(py))
370            .collect::<PyResult<Vec<_>>>()?;
371        Ok(PyList::new(py, points)?.into())
372    }
373
374    #[getter]
375    #[pyo3(name = "ts_event")]
376    fn py_ts_event(&self) -> u64 {
377        self.ts_event.as_u64()
378    }
379
380    #[getter]
381    #[pyo3(name = "ts_init")]
382    fn py_ts_init(&self) -> u64 {
383        self.ts_init.as_u64()
384    }
385}