1use std::fmt::Display;
19
20use jiff::{Timestamp, civil::Date, tz::Offset};
21use nautilus_model::enums::{AggressorSide, OrderSide, TriggerType};
22use serde::{Deserialize, Serialize};
23use serde_repr::{Deserialize_repr, Serialize_repr};
24use strum::{AsRefStr, EnumIter, EnumString};
25
26#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize_repr, Deserialize_repr)]
28#[repr(i32)]
29pub enum BybitUnifiedMarginStatus {
30 ClassicAccount = 1,
32 UnifiedTradingAccount10 = 3,
34 UnifiedTradingAccount10Pro = 4,
36 UnifiedTradingAccount20 = 5,
38 UnifiedTradingAccount20Pro = 6,
40}
41
42#[derive(
44 Clone,
45 Copy,
46 Debug,
47 strum::Display,
48 Eq,
49 PartialEq,
50 Hash,
51 AsRefStr,
52 EnumIter,
53 EnumString,
54 Serialize,
55 Deserialize,
56)]
57#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
58#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
59#[cfg_attr(
60 feature = "python",
61 pyo3::pyclass(
62 eq,
63 eq_int,
64 rename_all = "SCREAMING_SNAKE_CASE",
65 module = "nautilus_trader.adapters.bybit",
66 from_py_object
67 )
68)]
69#[cfg_attr(
70 feature = "python",
71 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
72)]
73pub enum BybitMarginMode {
74 IsolatedMargin,
75 RegularMargin,
76 PortfolioMargin,
77}
78
79#[derive(
81 Clone,
82 Copy,
83 Debug,
84 strum::Display,
85 Eq,
86 PartialEq,
87 Hash,
88 AsRefStr,
89 EnumIter,
90 EnumString,
91 Serialize_repr,
92 Deserialize_repr,
93)]
94#[repr(i32)]
95#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
96#[cfg_attr(
97 feature = "python",
98 pyo3::pyclass(
99 eq,
100 eq_int,
101 rename_all = "SCREAMING_SNAKE_CASE",
102 module = "nautilus_trader.adapters.bybit",
103 from_py_object
104 )
105)]
106#[cfg_attr(
107 feature = "python",
108 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
109)]
110pub enum BybitPositionMode {
111 MergedSingle = 0,
113 BothSides = 3,
115}
116
117#[derive(
119 Clone,
120 Copy,
121 Debug,
122 strum::Display,
123 Eq,
124 PartialEq,
125 Hash,
126 AsRefStr,
127 EnumIter,
128 EnumString,
129 Serialize_repr,
130 Deserialize_repr,
131)]
132#[repr(i32)]
133#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
134#[cfg_attr(
135 feature = "python",
136 pyo3::pyclass(
137 eq,
138 eq_int,
139 rename_all = "SCREAMING_SNAKE_CASE",
140 module = "nautilus_trader.adapters.bybit",
141 from_py_object
142 )
143)]
144#[cfg_attr(
145 feature = "python",
146 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
147)]
148pub enum BybitPositionIdx {
149 OneWay = 0,
151 BuyHedge = 1,
153 SellHedge = 2,
155}
156
157#[derive(
159 Copy,
160 Clone,
161 Debug,
162 strum::Display,
163 PartialEq,
164 Eq,
165 Hash,
166 AsRefStr,
167 EnumIter,
168 EnumString,
169 Serialize,
170 Deserialize,
171)]
172#[serde(rename_all = "UPPERCASE")]
173#[cfg_attr(
174 feature = "python",
175 pyo3::pyclass(
176 eq,
177 eq_int,
178 rename_all = "SCREAMING_SNAKE_CASE",
179 module = "nautilus_trader.adapters.bybit",
180 from_py_object
181 )
182)]
183#[cfg_attr(
184 feature = "python",
185 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
186)]
187pub enum BybitAccountType {
188 Unified,
189}
190
191#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize_repr, Deserialize_repr)]
193#[repr(u8)]
194pub enum BybitApiKeyType {
195 Hmac = 1,
197 Rsa = 2,
199}
200
201#[derive(
203 Copy,
204 Clone,
205 Debug,
206 strum::Display,
207 PartialEq,
208 Eq,
209 Hash,
210 AsRefStr,
211 EnumIter,
212 EnumString,
213 Serialize,
214 Deserialize,
215)]
216#[serde(rename_all = "lowercase")]
217#[cfg_attr(
218 feature = "python",
219 pyo3::pyclass(
220 eq,
221 eq_int,
222 rename_all = "SCREAMING_SNAKE_CASE",
223 module = "nautilus_trader.adapters.bybit",
224 from_py_object
225 )
226)]
227#[cfg_attr(
228 feature = "python",
229 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
230)]
231pub enum BybitEnvironment {
232 Mainnet,
234 Demo,
236 Testnet,
238}
239
240#[derive(
242 Copy,
243 Clone,
244 Debug,
245 strum::Display,
246 Default,
247 PartialEq,
248 Eq,
249 Hash,
250 AsRefStr,
251 EnumIter,
252 EnumString,
253 Serialize,
254 Deserialize,
255)]
256#[serde(rename_all = "lowercase")]
257#[cfg_attr(
258 feature = "python",
259 pyo3::pyclass(
260 eq,
261 eq_int,
262 rename_all = "SCREAMING_SNAKE_CASE",
263 module = "nautilus_trader.adapters.bybit",
264 from_py_object
265 )
266)]
267#[cfg_attr(
268 feature = "python",
269 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
270)]
271pub enum BybitProductType {
272 #[default]
273 Spot,
274 Linear,
275 Inverse,
276 Option,
277}
278
279#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
281pub enum BybitMarginTrading {
282 #[serde(rename = "none")]
283 None,
284 #[serde(rename = "utaOnly")]
285 UtaOnly,
286 #[serde(rename = "both")]
287 Both,
288 #[serde(other)]
289 Other,
290}
291
292#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
294pub enum BybitInnovationFlag {
295 #[serde(rename = "0")]
296 Standard,
297 #[serde(rename = "1")]
298 Innovation,
299 #[serde(other)]
300 Other,
301}
302
303#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
305#[serde(rename_all = "PascalCase")]
306pub enum BybitInstrumentStatus {
307 PreLaunch,
308 Trading,
309 Delivering,
310 Closed,
311 #[serde(other)]
312 Other,
313}
314
315impl BybitProductType {
316 #[must_use]
318 pub const fn as_str(self) -> &'static str {
319 match self {
320 Self::Spot => "spot",
321 Self::Linear => "linear",
322 Self::Inverse => "inverse",
323 Self::Option => "option",
324 }
325 }
326
327 #[must_use]
329 pub const fn suffix(self) -> &'static str {
330 match self {
331 Self::Spot => "-SPOT",
332 Self::Linear => "-LINEAR",
333 Self::Inverse => "-INVERSE",
334 Self::Option => "-OPTION",
335 }
336 }
337
338 #[must_use]
340 pub fn from_suffix(symbol: &str) -> Option<Self> {
341 if symbol.ends_with("-SPOT") {
342 Some(Self::Spot)
343 } else if symbol.ends_with("-LINEAR") {
344 Some(Self::Linear)
345 } else if symbol.ends_with("-INVERSE") {
346 Some(Self::Inverse)
347 } else if symbol.ends_with("-OPTION") {
348 Some(Self::Option)
349 } else {
350 None
351 }
352 }
353
354 #[must_use]
356 pub fn is_spot(self) -> bool {
357 matches!(self, Self::Spot)
358 }
359
360 #[must_use]
362 pub fn is_linear(self) -> bool {
363 matches!(self, Self::Linear)
364 }
365
366 #[must_use]
368 pub fn is_inverse(self) -> bool {
369 matches!(self, Self::Inverse)
370 }
371
372 #[must_use]
374 pub fn is_option(self) -> bool {
375 matches!(self, Self::Option)
376 }
377}
378
379#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
381#[serde(rename_all = "PascalCase")]
382pub enum BybitContractType {
383 LinearPerpetual,
384 LinearFutures,
385 InversePerpetual,
386 InverseFutures,
387}
388
389#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
391#[serde(rename_all = "PascalCase")]
392pub enum BybitOptionType {
393 Call,
394 Put,
395}
396
397#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
405#[serde(rename_all = "lowercase")]
406pub enum BybitSymbolType {
407 Innovation,
409 Adventure,
411 Xstocks,
413 Commodity,
415 Stock,
417 Forex,
419 #[serde(rename = "ETF")]
421 Etf,
422 #[serde(other)]
424 Other,
425}
426
427impl BybitSymbolType {
428 #[must_use]
430 pub(crate) const fn as_str(self) -> Option<&'static str> {
431 match self {
432 Self::Innovation => Some("innovation"),
433 Self::Adventure => Some("adventure"),
434 Self::Xstocks => Some("xstocks"),
435 Self::Commodity => Some("commodity"),
436 Self::Stock => Some("stock"),
437 Self::Forex => Some("forex"),
438 Self::Etf => Some("ETF"),
439 Self::Other => None,
440 }
441 }
442}
443
444#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
446pub enum BybitPositionSide {
447 #[serde(rename = "")]
448 Flat,
449 #[serde(rename = "Buy")]
450 Buy,
451 #[serde(rename = "Sell")]
452 Sell,
453}
454
455#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
457pub enum BybitWsOrderRequestOp {
458 #[serde(rename = "order.create")]
459 Create,
460 #[serde(rename = "order.amend")]
461 Amend,
462 #[serde(rename = "order.cancel")]
463 Cancel,
464 #[serde(rename = "order.create-batch")]
465 CreateBatch,
466 #[serde(rename = "order.amend-batch")]
467 AmendBatch,
468 #[serde(rename = "order.cancel-batch")]
469 CancelBatch,
470}
471
472#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
474pub enum BybitKlineInterval {
475 #[serde(rename = "1")]
476 Minute1,
477 #[serde(rename = "3")]
478 Minute3,
479 #[serde(rename = "5")]
480 Minute5,
481 #[serde(rename = "15")]
482 Minute15,
483 #[serde(rename = "30")]
484 Minute30,
485 #[serde(rename = "60")]
486 Hour1,
487 #[serde(rename = "120")]
488 Hour2,
489 #[serde(rename = "240")]
490 Hour4,
491 #[serde(rename = "360")]
492 Hour6,
493 #[serde(rename = "720")]
494 Hour12,
495 #[serde(rename = "D")]
496 Day1,
497 #[serde(rename = "W")]
498 Week1,
499 #[serde(rename = "M")]
500 Month1,
501}
502
503impl BybitKlineInterval {
504 #[must_use]
510 pub fn bar_end_time_ms(&self, start_ms: i64) -> i64 {
511 match self {
512 Self::Month1 => {
513 let start_dt = Offset::UTC.to_datetime(
514 Timestamp::from_millisecond(start_ms).unwrap_or(Timestamp::UNIX_EPOCH),
515 );
516 let (year, month) = if start_dt.month() == 12 {
517 (start_dt.year() + 1, 1)
518 } else {
519 (start_dt.year(), start_dt.month() + 1)
520 };
521 Date::new(year, month, 1)
522 .and_then(|date| Offset::UTC.to_timestamp(date.at(0, 0, 0, 0)))
523 .map_or(start_ms + 2_678_400_000, Timestamp::as_millisecond)
524 }
525 _ => start_ms + self.duration_ms(),
526 }
527 }
528
529 #[must_use]
534 pub const fn duration_ms(&self) -> i64 {
535 match self {
536 Self::Minute1 => 60_000,
537 Self::Minute3 => 180_000,
538 Self::Minute5 => 300_000,
539 Self::Minute15 => 900_000,
540 Self::Minute30 => 1_800_000,
541 Self::Hour1 => 3_600_000,
542 Self::Hour2 => 7_200_000,
543 Self::Hour4 => 14_400_000,
544 Self::Hour6 => 21_600_000,
545 Self::Hour12 => 43_200_000,
546 Self::Day1 => 86_400_000,
547 Self::Week1 => 604_800_000,
548 Self::Month1 => 2_678_400_000, }
550 }
551}
552
553impl Display for BybitKlineInterval {
554 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
555 let s = match self {
556 Self::Minute1 => "1",
557 Self::Minute3 => "3",
558 Self::Minute5 => "5",
559 Self::Minute15 => "15",
560 Self::Minute30 => "30",
561 Self::Hour1 => "60",
562 Self::Hour2 => "120",
563 Self::Hour4 => "240",
564 Self::Hour6 => "360",
565 Self::Hour12 => "720",
566 Self::Day1 => "D",
567 Self::Week1 => "W",
568 Self::Month1 => "M",
569 };
570 write!(f, "{s}")
571 }
572}
573
574#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
576#[cfg_attr(
577 feature = "python",
578 pyo3::pyclass(module = "nautilus_trader.adapters.bybit", eq, eq_int, from_py_object)
579)]
580#[cfg_attr(
581 feature = "python",
582 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
583)]
584pub enum BybitOrderStatus {
585 #[serde(rename = "Created")]
586 Created,
587 #[serde(rename = "New")]
588 New,
589 #[serde(rename = "Rejected")]
590 Rejected,
591 #[serde(rename = "PartiallyFilled")]
592 PartiallyFilled,
593 #[serde(rename = "PartiallyFilledCanceled")]
594 PartiallyFilledCanceled,
595 #[serde(rename = "Filled")]
596 Filled,
597 #[serde(rename = "Cancelled")]
598 Canceled,
599 #[serde(rename = "Untriggered")]
600 Untriggered,
601 #[serde(rename = "Triggered")]
602 Triggered,
603 #[serde(rename = "Deactivated")]
604 Deactivated,
605}
606
607#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
609#[cfg_attr(
610 feature = "python",
611 pyo3::pyclass(module = "nautilus_trader.adapters.bybit", eq, eq_int, from_py_object)
612)]
613#[cfg_attr(
614 feature = "python",
615 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
616)]
617pub enum BybitOrderSide {
618 #[serde(rename = "")]
619 Unknown,
620 #[serde(rename = "Buy")]
621 Buy,
622 #[serde(rename = "Sell")]
623 Sell,
624}
625
626impl From<BybitOrderSide> for AggressorSide {
627 fn from(value: BybitOrderSide) -> Self {
628 match value {
629 BybitOrderSide::Buy => Self::Buy,
630 BybitOrderSide::Sell => Self::Sell,
631 BybitOrderSide::Unknown => Self::NoAggressor,
632 }
633 }
634}
635
636impl From<BybitOrderSide> for Option<OrderSide> {
637 fn from(value: BybitOrderSide) -> Self {
638 match value {
639 BybitOrderSide::Buy => Some(OrderSide::Buy),
640 BybitOrderSide::Sell => Some(OrderSide::Sell),
641 BybitOrderSide::Unknown => None,
642 }
643 }
644}
645
646impl TryFrom<BybitOrderSide> for OrderSide {
647 type Error = anyhow::Error;
648
649 fn try_from(value: BybitOrderSide) -> Result<Self, Self::Error> {
650 match value {
651 BybitOrderSide::Buy => Ok(Self::Buy),
652 BybitOrderSide::Sell => Ok(Self::Sell),
653 BybitOrderSide::Unknown => anyhow::bail!("Unspecified Bybit order side"),
654 }
655 }
656}
657
658impl From<OrderSide> for BybitOrderSide {
659 fn from(value: OrderSide) -> Self {
660 match value {
661 OrderSide::Buy => Self::Buy,
662 OrderSide::Sell => Self::Sell,
663 }
664 }
665}
666
667impl From<BybitTriggerType> for TriggerType {
668 fn from(value: BybitTriggerType) -> Self {
669 match value {
670 BybitTriggerType::None => Self::Default,
671 BybitTriggerType::LastPrice => Self::LastPrice,
672 BybitTriggerType::IndexPrice => Self::IndexPrice,
673 BybitTriggerType::MarkPrice => Self::MarkPrice,
674 }
675 }
676}
677
678impl From<TriggerType> for BybitTriggerType {
679 fn from(value: TriggerType) -> Self {
680 match value {
681 TriggerType::Default | TriggerType::LastPrice => Self::LastPrice,
682 TriggerType::IndexPrice => Self::IndexPrice,
683 TriggerType::MarkPrice => Self::MarkPrice,
684 _ => Self::LastPrice,
685 }
686 }
687}
688
689pub fn resolve_trigger_type(trigger_type: Option<TriggerType>) -> BybitTriggerType {
692 trigger_type.map_or(BybitTriggerType::LastPrice, BybitTriggerType::from)
693}
694
695#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
697#[serde(rename_all = "PascalCase")]
698#[cfg_attr(
699 feature = "python",
700 pyo3::pyclass(module = "nautilus_trader.adapters.bybit", eq, eq_int, from_py_object)
701)]
702#[cfg_attr(
703 feature = "python",
704 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
705)]
706pub enum BybitCancelType {
707 CancelByUser,
708 CancelByReduceOnly,
709 CancelByPrepareLackOfMargin,
710 CancelByPrepareOrderFilter,
711 CancelByPrepareOrderMarginCheckFailed,
712 CancelByPrepareOrderCommission,
713 CancelByPrepareOrderRms,
714 CancelByPrepareOrderOther,
715 CancelByRiskLimit,
716 CancelOnDisconnect,
717 CancelByStopOrdersExceeded,
718 CancelByPzMarketClose,
719 CancelByMarginCheckFailed,
720 CancelByPzTakeover,
721 CancelByAdmin,
722 CancelByTpSlTsClear,
723 CancelByAmendNotModified,
724 CancelByPzCancel,
725 CancelByCrossSelfMatch,
726 CancelBySelfMatchPrevention,
727 #[serde(other)]
728 Other,
729}
730
731#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
733#[serde(rename_all = "PascalCase")]
734pub enum BybitCreateType {
735 CreateByUser,
736 CreateByClosing,
737 CreateByTakeProfit,
738 CreateByStopLoss,
739 CreateByTrailingStop,
740 CreateByStopOrder,
741 CreateByPartialTakeProfit,
742 CreateByPartialStopLoss,
743 CreateByAdl,
744 CreateByLiquidate,
745 CreateByTakeover,
746 CreateByTpsl,
747 CreateByBboOrder,
748 #[serde(other)]
749 Other,
750}
751
752#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
754#[serde(rename_all = "PascalCase")]
755pub enum BybitBboSideType {
756 Queue,
757 Counterparty,
758}
759
760#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
762#[cfg_attr(
763 feature = "python",
764 pyo3::pyclass(module = "nautilus_trader.adapters.bybit", eq, eq_int, from_py_object)
765)]
766#[cfg_attr(
767 feature = "python",
768 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
769)]
770pub enum BybitOrderType {
771 #[serde(rename = "Market")]
772 Market,
773 #[serde(rename = "Limit")]
774 Limit,
775 #[serde(rename = "UNKNOWN")]
776 Unknown,
777}
778
779#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
781#[cfg_attr(
782 feature = "python",
783 pyo3::pyclass(module = "nautilus_trader.adapters.bybit", eq, eq_int, from_py_object)
784)]
785#[cfg_attr(
786 feature = "python",
787 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
788)]
789pub enum BybitStopOrderType {
790 #[serde(rename = "")]
791 None,
792 #[serde(rename = "UNKNOWN")]
793 Unknown,
794 #[serde(rename = "TakeProfit")]
795 TakeProfit,
796 #[serde(rename = "StopLoss")]
797 StopLoss,
798 #[serde(rename = "TrailingStop")]
799 TrailingStop,
800 #[serde(rename = "Stop")]
801 Stop,
802 #[serde(rename = "PartialTakeProfit")]
803 PartialTakeProfit,
804 #[serde(rename = "PartialStopLoss")]
805 PartialStopLoss,
806 #[serde(rename = "tpslOrder")]
807 TpslOrder,
808 #[serde(rename = "OcoOrder")]
809 OcoOrder,
810 #[serde(rename = "MmRateClose")]
811 MmRateClose,
812 #[serde(rename = "BidirectionalTpslOrder")]
813 BidirectionalTpslOrder,
814}
815
816#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
818#[cfg_attr(
819 feature = "python",
820 pyo3::pyclass(module = "nautilus_trader.adapters.bybit", eq, eq_int, from_py_object)
821)]
822#[cfg_attr(
823 feature = "python",
824 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
825)]
826pub enum BybitTriggerType {
827 #[serde(rename = "")]
828 None,
829 #[serde(rename = "LastPrice")]
830 LastPrice,
831 #[serde(rename = "IndexPrice")]
832 IndexPrice,
833 #[serde(rename = "MarkPrice")]
834 MarkPrice,
835}
836
837#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize_repr, Deserialize_repr)]
839#[repr(i32)]
840#[cfg_attr(
841 feature = "python",
842 pyo3::pyclass(module = "nautilus_trader.adapters.bybit", eq, eq_int, from_py_object)
843)]
844#[cfg_attr(
845 feature = "python",
846 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
847)]
848pub enum BybitTriggerDirection {
849 None = 0,
850 RisesTo = 1,
851 FallsTo = 2,
852}
853
854#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
856#[serde(rename_all = "PascalCase")]
857#[cfg_attr(
858 feature = "python",
859 pyo3::pyclass(module = "nautilus_trader.adapters.bybit", eq, eq_int, from_py_object)
860)]
861#[cfg_attr(
862 feature = "python",
863 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
864)]
865pub enum BybitTpSlMode {
866 Full,
867 Partial,
868 #[serde(other)]
869 Unknown,
870}
871
872#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
874#[cfg_attr(
875 feature = "python",
876 pyo3::pyclass(module = "nautilus_trader.adapters.bybit", eq, eq_int, from_py_object)
877)]
878#[cfg_attr(
879 feature = "python",
880 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
881)]
882pub enum BybitTimeInForce {
883 #[serde(rename = "GTC")]
884 Gtc,
885 #[serde(rename = "IOC")]
886 Ioc,
887 #[serde(rename = "FOK")]
888 Fok,
889 #[serde(rename = "PostOnly")]
890 PostOnly,
891}
892
893#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
897pub enum BybitExecType {
898 #[serde(rename = "Trade")]
899 Trade,
900 #[serde(rename = "AdlTrade")]
901 AdlTrade,
902 #[serde(rename = "Funding")]
903 Funding,
904 #[serde(rename = "BustTrade")]
905 BustTrade,
906 #[serde(rename = "Delivery")]
907 Delivery,
908 #[serde(rename = "Settle")]
909 Settle,
910 #[serde(rename = "BlockTrade")]
911 BlockTrade,
912 #[serde(rename = "MovePosition")]
913 MovePosition,
914 #[serde(rename = "CorporateAction")]
915 CorporateAction,
916 #[serde(rename = "UNKNOWN")]
917 Unknown,
918}
919
920impl BybitExecType {
921 #[must_use]
927 pub const fn is_exchange_generated(&self) -> bool {
928 matches!(
929 self,
930 Self::AdlTrade
931 | Self::BustTrade
932 | Self::Delivery
933 | Self::Settle
934 | Self::CorporateAction
935 )
936 }
937}
938
939#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
941pub enum BybitTransactionType {
942 #[serde(rename = "TRANSFER_IN")]
943 TransferIn,
944 #[serde(rename = "TRANSFER_OUT")]
945 TransferOut,
946 #[serde(rename = "TRADE")]
947 Trade,
948 #[serde(rename = "SETTLEMENT")]
949 Settlement,
950 #[serde(rename = "DELIVERY")]
951 Delivery,
952 #[serde(rename = "LIQUIDATION")]
953 Liquidation,
954 #[serde(rename = "AIRDRP")]
955 Airdrop,
956}
957
958#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
960#[serde(rename_all = "UPPERCASE")]
961pub enum BybitEndpointType {
962 None,
963 Asset,
964 Market,
965 Account,
966 Trade,
967 Position,
968 User,
969}
970
971#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Serialize_repr, Deserialize_repr)]
975#[repr(i32)]
976#[cfg_attr(
977 feature = "python",
978 pyo3::pyclass(module = "nautilus_trader.adapters.bybit", eq, eq_int, from_py_object)
979)]
980#[cfg_attr(
981 feature = "python",
982 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
983)]
984pub enum BybitOpenOnly {
985 #[default]
987 OpenOnly = 0,
988 ClosedRecent = 1,
990}
991
992#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
996#[cfg_attr(
997 feature = "python",
998 pyo3::pyclass(module = "nautilus_trader.adapters.bybit", eq, eq_int, from_py_object)
999)]
1000#[cfg_attr(
1001 feature = "python",
1002 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
1003)]
1004pub enum BybitOrderFilter {
1005 #[default]
1007 Order,
1008 StopOrder,
1010 #[serde(rename = "tpslOrder")]
1012 TpslOrder,
1013 OcoOrder,
1015 BidirectionalTpslOrder,
1017}
1018
1019#[derive(
1021 Clone,
1022 Copy,
1023 Debug,
1024 strum::Display,
1025 Eq,
1026 PartialEq,
1027 Hash,
1028 AsRefStr,
1029 EnumIter,
1030 EnumString,
1031 Serialize,
1032 Deserialize,
1033)]
1034#[serde(rename_all = "snake_case")]
1035#[strum(serialize_all = "snake_case")]
1036#[cfg_attr(
1037 feature = "python",
1038 pyo3::pyclass(
1039 eq,
1040 eq_int,
1041 hash,
1042 frozen,
1043 rename_all = "SCREAMING_SNAKE_CASE",
1044 module = "nautilus_trader.adapters.bybit",
1045 from_py_object,
1046 )
1047)]
1048#[cfg_attr(
1049 feature = "python",
1050 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
1051)]
1052pub enum BybitMarginAction {
1053 Borrow,
1055 Repay,
1057 GetBorrowAmount,
1059}
1060
1061#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, strum::Display, Serialize, Deserialize)]
1063pub enum BybitRepayStatus {
1064 #[serde(rename = "P")]
1066 #[strum(serialize = "P")]
1067 Processing,
1068 #[serde(rename = "SU")]
1070 #[strum(serialize = "SU")]
1071 Success,
1072 #[serde(rename = "FA")]
1074 #[strum(serialize = "FA")]
1075 Failed,
1076}
1077
1078#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1080#[serde(rename_all = "PascalCase")]
1081pub enum BybitPositionStatus {
1082 Normal,
1083 Settle,
1084 Delivering,
1085 #[serde(other)]
1086 Other,
1087}
1088
1089#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1091pub enum BybitMarketUnit {
1092 #[serde(rename = "baseCoin")]
1093 BaseCoin,
1094 #[serde(rename = "quoteCoin")]
1095 QuoteCoin,
1096}
1097
1098#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1100pub enum BybitSmpType {
1101 None,
1102 CancelMaker,
1103 CancelTaker,
1104 CancelBoth,
1105 #[serde(other)]
1106 Other,
1107}
1108
1109#[cfg(test)]
1110mod tests {
1111 use rstest::rstest;
1112
1113 use super::*;
1114
1115 #[rstest]
1116 #[case::minute1(BybitKlineInterval::Minute1, 60_000)]
1117 #[case::minute3(BybitKlineInterval::Minute3, 180_000)]
1118 #[case::minute5(BybitKlineInterval::Minute5, 300_000)]
1119 #[case::minute15(BybitKlineInterval::Minute15, 900_000)]
1120 #[case::minute30(BybitKlineInterval::Minute30, 1_800_000)]
1121 #[case::hour1(BybitKlineInterval::Hour1, 3_600_000)]
1122 #[case::hour2(BybitKlineInterval::Hour2, 7_200_000)]
1123 #[case::hour4(BybitKlineInterval::Hour4, 14_400_000)]
1124 #[case::hour6(BybitKlineInterval::Hour6, 21_600_000)]
1125 #[case::hour12(BybitKlineInterval::Hour12, 43_200_000)]
1126 #[case::day1(BybitKlineInterval::Day1, 86_400_000)]
1127 #[case::week1(BybitKlineInterval::Week1, 604_800_000)]
1128 #[case::month1(BybitKlineInterval::Month1, 2_678_400_000)]
1129 fn test_kline_interval_duration_ms(
1130 #[case] interval: BybitKlineInterval,
1131 #[case] expected_ms: i64,
1132 ) {
1133 assert_eq!(interval.duration_ms(), expected_ms);
1134 }
1135
1136 #[rstest]
1137 fn test_bar_end_time_ms_non_monthly_adds_duration() {
1138 let interval = BybitKlineInterval::Minute1;
1139 let start_ms = 1704067200000i64;
1140 assert_eq!(interval.bar_end_time_ms(start_ms), start_ms + 60_000);
1141 }
1142
1143 #[rstest]
1144 #[case::jan_31_days(1704067200000i64, 1706745600000i64)]
1145 #[case::feb_leap_year_29_days(1706745600000i64, 1709251200000i64)]
1146 #[case::apr_30_days(1711929600000i64, 1714521600000i64)]
1147 #[case::dec_to_next_year(1733011200000i64, 1735689600000i64)]
1148 fn test_bar_end_time_ms_monthly_variable_lengths(
1149 #[case] start_ms: i64,
1150 #[case] expected_end_ms: i64,
1151 ) {
1152 let interval = BybitKlineInterval::Month1;
1153 assert_eq!(interval.bar_end_time_ms(start_ms), expected_end_ms);
1154 }
1155
1156 #[rstest]
1157 #[case(BybitSymbolType::Innovation, "innovation")]
1158 #[case(BybitSymbolType::Adventure, "adventure")]
1159 #[case(BybitSymbolType::Xstocks, "xstocks")]
1160 #[case(BybitSymbolType::Commodity, "commodity")]
1161 #[case(BybitSymbolType::Stock, "stock")]
1162 #[case(BybitSymbolType::Forex, "forex")]
1163 #[case(BybitSymbolType::Etf, "ETF")]
1164 fn test_symbol_type_round_trip(#[case] symbol_type: BybitSymbolType, #[case] wire_value: &str) {
1165 let value = serde_json::Value::String(wire_value.to_string());
1166
1167 assert_eq!(
1168 serde_json::from_value::<BybitSymbolType>(value.clone()).unwrap(),
1169 symbol_type
1170 );
1171 assert_eq!(serde_json::to_value(symbol_type).unwrap(), value);
1172 assert_eq!(symbol_type.as_str(), Some(wire_value));
1173 }
1174
1175 #[rstest]
1176 fn test_unknown_symbol_type_has_no_wire_value() {
1177 assert_eq!(BybitSymbolType::Other.as_str(), None);
1178 }
1179
1180 #[rstest]
1181 #[case(BybitExecType::Trade, false)]
1182 #[case(BybitExecType::AdlTrade, true)]
1183 #[case(BybitExecType::BustTrade, true)]
1184 #[case(BybitExecType::Delivery, true)]
1185 #[case(BybitExecType::Settle, true)]
1186 #[case(BybitExecType::Funding, false)]
1187 #[case(BybitExecType::BlockTrade, false)]
1188 #[case(BybitExecType::MovePosition, false)]
1189 #[case(BybitExecType::CorporateAction, true)]
1190 #[case(BybitExecType::Unknown, false)]
1191 fn test_exec_type_is_exchange_generated(
1192 #[case] exec_type: BybitExecType,
1193 #[case] expected: bool,
1194 ) {
1195 assert_eq!(exec_type.is_exchange_generated(), expected);
1196 }
1197}