nautilus_bybit/python/
enums.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 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//! Bybit enumerations Python bindings.
17
18use std::str::FromStr;
19
20use nautilus_core::python::to_pyvalue_err;
21use pyo3::{PyTypeInfo, prelude::*, types::PyType};
22use strum::IntoEnumIterator;
23
24use crate::common::enums::{
25    BybitAccountType, BybitEnvironment, BybitMarginAction, BybitMarginMode, BybitPositionMode,
26    BybitProductType,
27};
28
29#[pymethods]
30impl BybitProductType {
31    #[new]
32    fn py_new(py: Python<'_>, value: &Bound<'_, PyAny>) -> PyResult<Self> {
33        let t = Self::type_object(py);
34        Self::py_from_str(&t, value)
35    }
36
37    fn __hash__(&self) -> isize {
38        *self as isize
39    }
40
41    fn __repr__(&self) -> String {
42        format!(
43            "<{}.{}: '{}'>",
44            stringify!(BybitProductType),
45            self.name(),
46            self.value(),
47        )
48    }
49
50    fn __str__(&self) -> String {
51        self.to_string()
52    }
53
54    #[getter]
55    #[must_use]
56    pub fn name(&self) -> &str {
57        self.as_ref()
58    }
59
60    #[getter]
61    #[must_use]
62    pub fn value(&self) -> String {
63        self.to_string().to_lowercase()
64    }
65
66    #[staticmethod]
67    #[must_use]
68    fn variants() -> Vec<String> {
69        Self::iter().map(|x| x.to_string()).collect()
70    }
71
72    #[classmethod]
73    #[pyo3(name = "from_str")]
74    fn py_from_str(_cls: &Bound<'_, PyType>, data: &Bound<'_, PyAny>) -> PyResult<Self> {
75        let data_str: String = data.str()?.extract()?;
76        Self::from_str(&data_str).map_err(to_pyvalue_err)
77    }
78
79    #[classattr]
80    #[pyo3(name = "SPOT")]
81    fn py_spot() -> Self {
82        Self::Spot
83    }
84
85    #[classattr]
86    #[pyo3(name = "LINEAR")]
87    fn py_linear() -> Self {
88        Self::Linear
89    }
90
91    #[classattr]
92    #[pyo3(name = "INVERSE")]
93    fn py_inverse() -> Self {
94        Self::Inverse
95    }
96
97    #[classattr]
98    #[pyo3(name = "OPTION")]
99    fn py_option() -> Self {
100        Self::Option
101    }
102}
103
104#[pymethods]
105impl BybitEnvironment {
106    #[new]
107    fn py_new(py: Python<'_>, value: &Bound<'_, PyAny>) -> PyResult<Self> {
108        let t = Self::type_object(py);
109        Self::py_from_str(&t, value)
110    }
111
112    fn __hash__(&self) -> isize {
113        *self as isize
114    }
115
116    fn __repr__(&self) -> String {
117        format!(
118            "<{}.{}: {}>",
119            stringify!(BybitEnvironment),
120            self.name(),
121            *self as u8,
122        )
123    }
124
125    fn __str__(&self) -> String {
126        self.to_string()
127    }
128
129    #[getter]
130    #[must_use]
131    pub fn name(&self) -> &str {
132        self.as_ref()
133    }
134
135    #[getter]
136    #[must_use]
137    pub fn value(&self) -> String {
138        self.to_string().to_lowercase()
139    }
140
141    #[staticmethod]
142    #[must_use]
143    fn variants() -> Vec<String> {
144        Self::iter().map(|x| x.to_string()).collect()
145    }
146
147    #[classmethod]
148    #[pyo3(name = "from_str")]
149    fn py_from_str(_cls: &Bound<'_, PyType>, data: &Bound<'_, PyAny>) -> PyResult<Self> {
150        let data_str: String = data.str()?.extract()?;
151        Self::from_str(&data_str).map_err(to_pyvalue_err)
152    }
153
154    #[classattr]
155    #[pyo3(name = "MAINNET")]
156    fn py_mainnet() -> Self {
157        Self::Mainnet
158    }
159
160    #[classattr]
161    #[pyo3(name = "DEMO")]
162    fn py_demo() -> Self {
163        Self::Demo
164    }
165
166    #[classattr]
167    #[pyo3(name = "TESTNET")]
168    fn py_testnet() -> Self {
169        Self::Testnet
170    }
171}
172
173#[pymethods]
174impl BybitAccountType {
175    #[new]
176    fn py_new(py: Python<'_>, value: &Bound<'_, PyAny>) -> PyResult<Self> {
177        let t = Self::type_object(py);
178        Self::py_from_str(&t, value)
179    }
180
181    fn __hash__(&self) -> isize {
182        *self as isize
183    }
184
185    fn __repr__(&self) -> String {
186        format!(
187            "<{}.{}: {}>",
188            stringify!(BybitAccountType),
189            self.name(),
190            *self as u8,
191        )
192    }
193
194    fn __str__(&self) -> String {
195        self.to_string()
196    }
197
198    #[getter]
199    #[must_use]
200    pub fn name(&self) -> &str {
201        self.as_ref()
202    }
203
204    #[getter]
205    #[must_use]
206    pub fn value(&self) -> String {
207        self.to_string().to_uppercase()
208    }
209
210    #[staticmethod]
211    #[must_use]
212    fn variants() -> Vec<String> {
213        Self::iter().map(|x| x.to_string()).collect()
214    }
215
216    #[classmethod]
217    #[pyo3(name = "from_str")]
218    fn py_from_str(_cls: &Bound<'_, PyType>, data: &Bound<'_, PyAny>) -> PyResult<Self> {
219        let data_str: String = data.str()?.extract()?;
220        Self::from_str(&data_str).map_err(to_pyvalue_err)
221    }
222
223    #[classattr]
224    #[pyo3(name = "UNIFIED")]
225    fn py_unified() -> Self {
226        Self::Unified
227    }
228}
229
230#[pymethods]
231impl BybitMarginMode {
232    #[new]
233    fn py_new(py: Python<'_>, value: &Bound<'_, PyAny>) -> PyResult<Self> {
234        let t = Self::type_object(py);
235        Self::py_from_str(&t, value)
236    }
237
238    fn __hash__(&self) -> isize {
239        *self as isize
240    }
241
242    fn __repr__(&self) -> String {
243        format!(
244            "<{}.{}: '{}'>",
245            stringify!(BybitMarginMode),
246            self.name(),
247            self.value(),
248        )
249    }
250
251    fn __str__(&self) -> String {
252        self.to_string()
253    }
254
255    #[getter]
256    #[must_use]
257    pub fn name(&self) -> &str {
258        self.as_ref()
259    }
260
261    #[getter]
262    #[must_use]
263    pub fn value(&self) -> &'static str {
264        match self {
265            Self::IsolatedMargin => "ISOLATED_MARGIN",
266            Self::RegularMargin => "REGULAR_MARGIN",
267            Self::PortfolioMargin => "PORTFOLIO_MARGIN",
268        }
269    }
270
271    #[staticmethod]
272    #[must_use]
273    fn variants() -> Vec<String> {
274        Self::iter().map(|x| x.to_string()).collect()
275    }
276
277    #[classmethod]
278    #[pyo3(name = "from_str")]
279    fn py_from_str(_cls: &Bound<'_, PyType>, data: &Bound<'_, PyAny>) -> PyResult<Self> {
280        let data_str: String = data.str()?.extract()?;
281        Self::from_str(&data_str).map_err(to_pyvalue_err)
282    }
283
284    #[classattr]
285    #[pyo3(name = "ISOLATED_MARGIN")]
286    fn py_isolated_margin() -> Self {
287        Self::IsolatedMargin
288    }
289
290    #[classattr]
291    #[pyo3(name = "REGULAR_MARGIN")]
292    fn py_regular_margin() -> Self {
293        Self::RegularMargin
294    }
295
296    #[classattr]
297    #[pyo3(name = "PORTFOLIO_MARGIN")]
298    fn py_portfolio_margin() -> Self {
299        Self::PortfolioMargin
300    }
301}
302
303#[pymethods]
304impl BybitPositionMode {
305    #[new]
306    fn py_new(py: Python<'_>, value: &Bound<'_, PyAny>) -> PyResult<Self> {
307        let t = Self::type_object(py);
308        Self::py_from_str(&t, value)
309    }
310
311    fn __hash__(&self) -> isize {
312        *self as isize
313    }
314
315    fn __repr__(&self) -> String {
316        format!(
317            "<{}.{}: {}>",
318            stringify!(BybitPositionMode),
319            self.name(),
320            self.value(),
321        )
322    }
323
324    fn __str__(&self) -> String {
325        self.to_string()
326    }
327
328    #[getter]
329    #[must_use]
330    pub fn name(&self) -> &str {
331        self.as_ref()
332    }
333
334    #[getter]
335    #[must_use]
336    pub fn value(&self) -> i32 {
337        *self as i32
338    }
339
340    #[staticmethod]
341    #[must_use]
342    fn variants() -> Vec<String> {
343        Self::iter().map(|x| x.to_string()).collect()
344    }
345
346    #[classmethod]
347    #[pyo3(name = "from_str")]
348    fn py_from_str(_cls: &Bound<'_, PyType>, data: &Bound<'_, PyAny>) -> PyResult<Self> {
349        // Try to extract as integer first (for API payloads that send 0 or 3)
350        if let Ok(int_val) = data.extract::<i32>() {
351            return match int_val {
352                0 => Ok(Self::MergedSingle),
353                3 => Ok(Self::BothSides),
354                _ => Err(to_pyvalue_err(anyhow::anyhow!(
355                    "Invalid BybitPositionMode value: {int_val}"
356                ))),
357            };
358        }
359
360        // Fall back to string parsing for variant names
361        let data_str: String = data.str()?.extract()?;
362        Self::from_str(&data_str).map_err(to_pyvalue_err)
363    }
364
365    #[classattr]
366    #[pyo3(name = "MERGED_SINGLE")]
367    fn py_merged_single() -> Self {
368        Self::MergedSingle
369    }
370
371    #[classattr]
372    #[pyo3(name = "BOTH_SIDES")]
373    fn py_both_sides() -> Self {
374        Self::BothSides
375    }
376}
377
378#[pymethods]
379impl BybitMarginAction {
380    #[new]
381    fn py_new(py: Python<'_>, value: &Bound<'_, PyAny>) -> PyResult<Self> {
382        let t = Self::type_object(py);
383        Self::py_from_str(&t, value)
384    }
385
386    fn __repr__(&self) -> String {
387        format!(
388            "<{}.{}: '{}'>",
389            stringify!(BybitMarginAction),
390            self.name(),
391            self.value(),
392        )
393    }
394
395    fn __str__(&self) -> String {
396        self.to_string()
397    }
398
399    #[getter]
400    #[must_use]
401    pub fn name(&self) -> &str {
402        self.as_ref()
403    }
404
405    #[getter]
406    #[must_use]
407    pub fn value(&self) -> String {
408        self.to_string()
409    }
410
411    #[staticmethod]
412    #[must_use]
413    fn variants() -> Vec<String> {
414        Self::iter().map(|x| x.to_string()).collect()
415    }
416
417    #[classmethod]
418    #[pyo3(name = "from_str")]
419    fn py_from_str(_cls: &Bound<'_, PyType>, data: &Bound<'_, PyAny>) -> PyResult<Self> {
420        let data_str: String = data.str()?.extract()?;
421        Self::from_str(&data_str).map_err(to_pyvalue_err)
422    }
423
424    #[classattr]
425    #[pyo3(name = "BORROW")]
426    fn py_borrow() -> Self {
427        Self::Borrow
428    }
429
430    #[classattr]
431    #[pyo3(name = "REPAY")]
432    fn py_repay() -> Self {
433        Self::Repay
434    }
435
436    #[classattr]
437    #[pyo3(name = "GET_BORROW_AMOUNT")]
438    fn py_get_borrow_amount() -> Self {
439        Self::GetBorrowAmount
440    }
441}