Skip to main content

nautilus_bybit/python/
enums.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//! 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
80#[pymethods]
81impl BybitEnvironment {
82    #[new]
83    fn py_new(py: Python<'_>, value: &Bound<'_, PyAny>) -> PyResult<Self> {
84        let t = Self::type_object(py);
85        Self::py_from_str(&t, value)
86    }
87
88    fn __hash__(&self) -> isize {
89        *self as isize
90    }
91
92    fn __repr__(&self) -> String {
93        format!(
94            "<{}.{}: {}>",
95            stringify!(BybitEnvironment),
96            self.name(),
97            *self as u8,
98        )
99    }
100
101    fn __str__(&self) -> String {
102        self.to_string()
103    }
104
105    #[getter]
106    #[must_use]
107    pub fn name(&self) -> &str {
108        self.as_ref()
109    }
110
111    #[getter]
112    #[must_use]
113    pub fn value(&self) -> String {
114        self.to_string().to_lowercase()
115    }
116
117    #[staticmethod]
118    #[must_use]
119    fn variants() -> Vec<String> {
120        Self::iter().map(|x| x.to_string()).collect()
121    }
122
123    #[classmethod]
124    #[pyo3(name = "from_str")]
125    fn py_from_str(_cls: &Bound<'_, PyType>, data: &Bound<'_, PyAny>) -> PyResult<Self> {
126        let data_str: String = data.str()?.extract()?;
127        Self::from_str(&data_str).map_err(to_pyvalue_err)
128    }
129}
130
131#[pymethods]
132impl BybitAccountType {
133    #[new]
134    fn py_new(py: Python<'_>, value: &Bound<'_, PyAny>) -> PyResult<Self> {
135        let t = Self::type_object(py);
136        Self::py_from_str(&t, value)
137    }
138
139    fn __hash__(&self) -> isize {
140        *self as isize
141    }
142
143    fn __repr__(&self) -> String {
144        format!(
145            "<{}.{}: {}>",
146            stringify!(BybitAccountType),
147            self.name(),
148            *self as u8,
149        )
150    }
151
152    fn __str__(&self) -> String {
153        self.to_string()
154    }
155
156    #[getter]
157    #[must_use]
158    pub fn name(&self) -> &str {
159        self.as_ref()
160    }
161
162    #[getter]
163    #[must_use]
164    pub fn value(&self) -> String {
165        self.to_string().to_uppercase()
166    }
167
168    #[staticmethod]
169    #[must_use]
170    fn variants() -> Vec<String> {
171        Self::iter().map(|x| x.to_string()).collect()
172    }
173
174    #[classmethod]
175    #[pyo3(name = "from_str")]
176    fn py_from_str(_cls: &Bound<'_, PyType>, data: &Bound<'_, PyAny>) -> PyResult<Self> {
177        let data_str: String = data.str()?.extract()?;
178        Self::from_str(&data_str).map_err(to_pyvalue_err)
179    }
180}
181
182#[pymethods]
183impl BybitMarginMode {
184    #[new]
185    fn py_new(py: Python<'_>, value: &Bound<'_, PyAny>) -> PyResult<Self> {
186        let t = Self::type_object(py);
187        Self::py_from_str(&t, value)
188    }
189
190    fn __hash__(&self) -> isize {
191        *self as isize
192    }
193
194    fn __repr__(&self) -> String {
195        format!(
196            "<{}.{}: '{}'>",
197            stringify!(BybitMarginMode),
198            self.name(),
199            self.value(),
200        )
201    }
202
203    fn __str__(&self) -> String {
204        self.to_string()
205    }
206
207    #[getter]
208    #[must_use]
209    pub fn name(&self) -> &str {
210        self.as_ref()
211    }
212
213    #[getter]
214    #[must_use]
215    pub fn value(&self) -> &'static str {
216        match self {
217            Self::IsolatedMargin => "ISOLATED_MARGIN",
218            Self::RegularMargin => "REGULAR_MARGIN",
219            Self::PortfolioMargin => "PORTFOLIO_MARGIN",
220        }
221    }
222
223    #[staticmethod]
224    #[must_use]
225    fn variants() -> Vec<String> {
226        Self::iter().map(|x| x.to_string()).collect()
227    }
228
229    #[classmethod]
230    #[pyo3(name = "from_str")]
231    fn py_from_str(_cls: &Bound<'_, PyType>, data: &Bound<'_, PyAny>) -> PyResult<Self> {
232        let data_str: String = data.str()?.extract()?;
233        Self::from_str(&data_str).map_err(to_pyvalue_err)
234    }
235}
236
237#[pymethods]
238impl BybitPositionMode {
239    #[new]
240    fn py_new(py: Python<'_>, value: &Bound<'_, PyAny>) -> PyResult<Self> {
241        let t = Self::type_object(py);
242        Self::py_from_str(&t, value)
243    }
244
245    fn __hash__(&self) -> isize {
246        *self as isize
247    }
248
249    fn __repr__(&self) -> String {
250        format!(
251            "<{}.{}: {}>",
252            stringify!(BybitPositionMode),
253            self.name(),
254            self.value(),
255        )
256    }
257
258    fn __str__(&self) -> String {
259        self.to_string()
260    }
261
262    #[getter]
263    #[must_use]
264    pub fn name(&self) -> &str {
265        self.as_ref()
266    }
267
268    #[getter]
269    #[must_use]
270    pub fn value(&self) -> i32 {
271        *self as i32
272    }
273
274    #[staticmethod]
275    #[must_use]
276    fn variants() -> Vec<String> {
277        Self::iter().map(|x| x.to_string()).collect()
278    }
279
280    #[classmethod]
281    #[pyo3(name = "from_str")]
282    fn py_from_str(_cls: &Bound<'_, PyType>, data: &Bound<'_, PyAny>) -> PyResult<Self> {
283        // Try to extract as integer first (for API payloads that send 0 or 3)
284        if let Ok(int_val) = data.extract::<i32>() {
285            return match int_val {
286                0 => Ok(Self::MergedSingle),
287                3 => Ok(Self::BothSides),
288                _ => Err(to_pyvalue_err(anyhow::anyhow!(
289                    "Invalid BybitPositionMode value: {int_val}"
290                ))),
291            };
292        }
293
294        // Fall back to string parsing for variant names
295        let data_str: String = data.str()?.extract()?;
296        Self::from_str(&data_str).map_err(to_pyvalue_err)
297    }
298}
299
300#[pymethods]
301impl BybitMarginAction {
302    #[new]
303    fn py_new(py: Python<'_>, value: &Bound<'_, PyAny>) -> PyResult<Self> {
304        let t = Self::type_object(py);
305        Self::py_from_str(&t, value)
306    }
307
308    fn __repr__(&self) -> String {
309        format!(
310            "<{}.{}: '{}'>",
311            stringify!(BybitMarginAction),
312            self.name(),
313            self.value(),
314        )
315    }
316
317    fn __str__(&self) -> String {
318        self.to_string()
319    }
320
321    #[getter]
322    #[must_use]
323    pub fn name(&self) -> &str {
324        self.as_ref()
325    }
326
327    #[getter]
328    #[must_use]
329    pub fn value(&self) -> String {
330        self.to_string()
331    }
332
333    #[staticmethod]
334    #[must_use]
335    fn variants() -> Vec<String> {
336        Self::iter().map(|x| x.to_string()).collect()
337    }
338
339    #[classmethod]
340    #[pyo3(name = "from_str")]
341    fn py_from_str(_cls: &Bound<'_, PyType>, data: &Bound<'_, PyAny>) -> PyResult<Self> {
342        let data_str: String = data.str()?.extract()?;
343        Self::from_str(&data_str).map_err(to_pyvalue_err)
344    }
345}