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