Skip to main content

bybit_client/types/
enums.rs

1//! Core enums for the Bybit V5 API.
2
3use serde::{Deserialize, Serialize};
4
5/// Trading category for the V5 API.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
7#[serde(rename_all = "lowercase")]
8pub enum Category {
9    /// Spot trading
10    Spot,
11    /// Linear perpetuals (USDT/USDC settled)
12    Linear,
13    /// Inverse perpetuals and futures
14    Inverse,
15    /// Options trading
16    Option,
17}
18
19impl Category {
20    /// Returns the string representation used in API requests.
21    pub fn as_str(&self) -> &'static str {
22        match self {
23            Category::Spot => "spot",
24            Category::Linear => "linear",
25            Category::Inverse => "inverse",
26            Category::Option => "option",
27        }
28    }
29}
30
31impl std::fmt::Display for Category {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        write!(f, "{}", self.as_str())
34    }
35}
36
37/// Order side (buy or sell).
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
39pub enum Side {
40    Buy,
41    Sell,
42}
43
44impl Side {
45    pub fn as_str(&self) -> &'static str {
46        match self {
47            Side::Buy => "Buy",
48            Side::Sell => "Sell",
49        }
50    }
51}
52
53impl std::fmt::Display for Side {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        write!(f, "{}", self.as_str())
56    }
57}
58
59/// Order type.
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
61pub enum OrderType {
62    Market,
63    Limit,
64}
65
66impl OrderType {
67    pub fn as_str(&self) -> &'static str {
68        match self {
69            OrderType::Market => "Market",
70            OrderType::Limit => "Limit",
71        }
72    }
73}
74
75impl std::fmt::Display for OrderType {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        write!(f, "{}", self.as_str())
78    }
79}
80
81/// Time in force for orders.
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
83pub enum TimeInForce {
84    /// Good Till Cancel
85    GTC,
86    /// Immediate Or Cancel
87    IOC,
88    /// Fill Or Kill
89    FOK,
90    /// Post Only (maker only)
91    PostOnly,
92}
93
94impl TimeInForce {
95    pub fn as_str(&self) -> &'static str {
96        match self {
97            TimeInForce::GTC => "GTC",
98            TimeInForce::IOC => "IOC",
99            TimeInForce::FOK => "FOK",
100            TimeInForce::PostOnly => "PostOnly",
101        }
102    }
103}
104
105impl std::fmt::Display for TimeInForce {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        write!(f, "{}", self.as_str())
108    }
109}
110
111/// Order status.
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
113pub enum OrderStatus {
114    /// Order has been created but not yet submitted
115    Created,
116    /// Order is active and waiting to be filled
117    New,
118    /// Order was rejected
119    Rejected,
120    /// Order is partially filled
121    PartiallyFilled,
122    /// Order was partially filled then cancelled
123    PartiallyFilledCanceled,
124    /// Order is completely filled
125    Filled,
126    /// Order was cancelled
127    Cancelled,
128    /// Conditional order waiting for trigger
129    Untriggered,
130    /// Conditional order has been triggered
131    Triggered,
132    /// Order has been deactivated
133    Deactivated,
134}
135
136impl OrderStatus {
137    pub fn as_str(&self) -> &'static str {
138        match self {
139            OrderStatus::Created => "Created",
140            OrderStatus::New => "New",
141            OrderStatus::Rejected => "Rejected",
142            OrderStatus::PartiallyFilled => "PartiallyFilled",
143            OrderStatus::PartiallyFilledCanceled => "PartiallyFilledCanceled",
144            OrderStatus::Filled => "Filled",
145            OrderStatus::Cancelled => "Cancelled",
146            OrderStatus::Untriggered => "Untriggered",
147            OrderStatus::Triggered => "Triggered",
148            OrderStatus::Deactivated => "Deactivated",
149        }
150    }
151
152    /// Returns true if the order is in a final state (no more updates expected).
153    pub fn is_final(&self) -> bool {
154        matches!(
155            self,
156            OrderStatus::Rejected
157                | OrderStatus::PartiallyFilledCanceled
158                | OrderStatus::Filled
159                | OrderStatus::Cancelled
160                | OrderStatus::Deactivated
161        )
162    }
163
164    /// Returns true if the order is still active.
165    pub fn is_active(&self) -> bool {
166        matches!(
167            self,
168            OrderStatus::Created
169                | OrderStatus::New
170                | OrderStatus::PartiallyFilled
171                | OrderStatus::Untriggered
172                | OrderStatus::Triggered
173        )
174    }
175}
176
177impl std::fmt::Display for OrderStatus {
178    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179        write!(f, "{}", self.as_str())
180    }
181}
182
183/// Position index for position mode.
184#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
185#[repr(i32)]
186pub enum PositionIdx {
187    /// One-way mode (default)
188    OneWay = 0,
189    /// Hedge mode - Buy side
190    HedgeBuy = 1,
191    /// Hedge mode - Sell side
192    HedgeSell = 2,
193}
194
195impl From<PositionIdx> for i32 {
196    fn from(idx: PositionIdx) -> Self {
197        idx as i32
198    }
199}
200
201/// Account type for wallet operations.
202#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
203#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
204pub enum AccountType {
205    /// Unified trading account
206    Unified,
207    /// Contract account (derivatives)
208    Contract,
209    /// Spot account
210    Spot,
211    /// Investment/Earn account
212    Investment,
213    /// Options account (USDC)
214    Option,
215    /// Funding account
216    Fund,
217}
218
219impl AccountType {
220    pub fn as_str(&self) -> &'static str {
221        match self {
222            AccountType::Unified => "UNIFIED",
223            AccountType::Contract => "CONTRACT",
224            AccountType::Spot => "SPOT",
225            AccountType::Investment => "INVESTMENT",
226            AccountType::Option => "OPTION",
227            AccountType::Fund => "FUND",
228        }
229    }
230}
231
232impl std::fmt::Display for AccountType {
233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        write!(f, "{}", self.as_str())
235    }
236}
237
238/// Trigger price type for conditional orders.
239#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
240pub enum TriggerBy {
241    LastPrice,
242    IndexPrice,
243    MarkPrice,
244}
245
246impl TriggerBy {
247    pub fn as_str(&self) -> &'static str {
248        match self {
249            TriggerBy::LastPrice => "LastPrice",
250            TriggerBy::IndexPrice => "IndexPrice",
251            TriggerBy::MarkPrice => "MarkPrice",
252        }
253    }
254}
255
256impl std::fmt::Display for TriggerBy {
257    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258        write!(f, "{}", self.as_str())
259    }
260}
261
262/// Kline/candlestick interval.
263#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
264pub enum KlineInterval {
265    /// 1 minute
266    #[serde(rename = "1")]
267    Min1,
268    /// 3 minutes
269    #[serde(rename = "3")]
270    Min3,
271    /// 5 minutes
272    #[serde(rename = "5")]
273    Min5,
274    /// 15 minutes
275    #[serde(rename = "15")]
276    Min15,
277    /// 30 minutes
278    #[serde(rename = "30")]
279    Min30,
280    /// 1 hour
281    #[serde(rename = "60")]
282    Hour1,
283    /// 2 hours
284    #[serde(rename = "120")]
285    Hour2,
286    /// 4 hours
287    #[serde(rename = "240")]
288    Hour4,
289    /// 6 hours
290    #[serde(rename = "360")]
291    Hour6,
292    /// 12 hours
293    #[serde(rename = "720")]
294    Hour12,
295    /// 1 day
296    #[serde(rename = "D")]
297    Day1,
298    /// 1 week
299    #[serde(rename = "W")]
300    Week1,
301    /// 1 month
302    #[serde(rename = "M")]
303    Month1,
304}
305
306impl KlineInterval {
307    pub fn as_str(&self) -> &'static str {
308        match self {
309            KlineInterval::Min1 => "1",
310            KlineInterval::Min3 => "3",
311            KlineInterval::Min5 => "5",
312            KlineInterval::Min15 => "15",
313            KlineInterval::Min30 => "30",
314            KlineInterval::Hour1 => "60",
315            KlineInterval::Hour2 => "120",
316            KlineInterval::Hour4 => "240",
317            KlineInterval::Hour6 => "360",
318            KlineInterval::Hour12 => "720",
319            KlineInterval::Day1 => "D",
320            KlineInterval::Week1 => "W",
321            KlineInterval::Month1 => "M",
322        }
323    }
324}
325
326impl std::fmt::Display for KlineInterval {
327    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
328        write!(f, "{}", self.as_str())
329    }
330}
331
332/// Stop order type.
333#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
334pub enum StopOrderType {
335    TakeProfit,
336    StopLoss,
337    TrailingStop,
338    Stop,
339    PartialTakeProfit,
340    PartialStopLoss,
341}
342
343/// Trade mode (cross or isolated margin).
344#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
345#[repr(i32)]
346pub enum TradeMode {
347    /// Cross margin mode
348    CrossMargin = 0,
349    /// Isolated margin mode
350    IsolatedMargin = 1,
351}
352
353/// Position mode.
354#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
355#[repr(i32)]
356pub enum PositionMode {
357    /// Merged single position (one-way mode)
358    MergedSingle = 0,
359    /// Both side position (hedge mode)
360    BothSide = 3,
361}
362
363/// Margin mode for the account.
364#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
365#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
366pub enum MarginMode {
367    RegularMargin,
368    PortfolioMargin,
369}
370
371#[cfg(test)]
372mod tests {
373    use super::*;
374
375    #[test]
376    fn test_category_serialization() {
377        let serialized = match serde_json::to_string(&Category::Linear) {
378            Ok(serialized) => serialized,
379            Err(err) => panic!("Failed to serialize category: {}", err),
380        };
381        assert_eq!(serialized, "\"linear\"");
382
383        let parsed = match serde_json::from_str::<Category>("\"spot\"") {
384            Ok(parsed) => parsed,
385            Err(err) => panic!("Failed to deserialize category: {}", err),
386        };
387        assert_eq!(parsed, Category::Spot);
388    }
389
390    #[test]
391    fn test_order_status_helpers() {
392        assert!(OrderStatus::Filled.is_final());
393        assert!(!OrderStatus::New.is_final());
394        assert!(OrderStatus::New.is_active());
395        assert!(!OrderStatus::Cancelled.is_active());
396    }
397
398    #[test]
399    fn test_kline_interval() {
400        assert_eq!(KlineInterval::Hour1.as_str(), "60");
401        assert_eq!(KlineInterval::Day1.as_str(), "D");
402    }
403}