1use std::fmt::Display;
19
20use chrono::{DateTime, Datelike, TimeZone, Utc};
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.core.nautilus_pyo3.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.core.nautilus_pyo3.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.core.nautilus_pyo3.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.core.nautilus_pyo3.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.core.nautilus_pyo3.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.core.nautilus_pyo3.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 Xstocks,
409 Stock,
411 Forex,
413 Commodity,
415 #[serde(other)]
417 Other,
418}
419
420#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
422pub enum BybitPositionSide {
423 #[serde(rename = "")]
424 Flat,
425 #[serde(rename = "Buy")]
426 Buy,
427 #[serde(rename = "Sell")]
428 Sell,
429}
430
431#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
433pub enum BybitWsOrderRequestOp {
434 #[serde(rename = "order.create")]
435 Create,
436 #[serde(rename = "order.amend")]
437 Amend,
438 #[serde(rename = "order.cancel")]
439 Cancel,
440 #[serde(rename = "order.create-batch")]
441 CreateBatch,
442 #[serde(rename = "order.amend-batch")]
443 AmendBatch,
444 #[serde(rename = "order.cancel-batch")]
445 CancelBatch,
446}
447
448#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
450pub enum BybitKlineInterval {
451 #[serde(rename = "1")]
452 Minute1,
453 #[serde(rename = "3")]
454 Minute3,
455 #[serde(rename = "5")]
456 Minute5,
457 #[serde(rename = "15")]
458 Minute15,
459 #[serde(rename = "30")]
460 Minute30,
461 #[serde(rename = "60")]
462 Hour1,
463 #[serde(rename = "120")]
464 Hour2,
465 #[serde(rename = "240")]
466 Hour4,
467 #[serde(rename = "360")]
468 Hour6,
469 #[serde(rename = "720")]
470 Hour12,
471 #[serde(rename = "D")]
472 Day1,
473 #[serde(rename = "W")]
474 Week1,
475 #[serde(rename = "M")]
476 Month1,
477}
478
479impl BybitKlineInterval {
480 #[must_use]
486 pub fn bar_end_time_ms(&self, start_ms: i64) -> i64 {
487 match self {
488 Self::Month1 => {
489 let start_dt = DateTime::from_timestamp_millis(start_ms)
490 .unwrap_or_else(|| Utc.timestamp_millis_opt(0).unwrap());
491 let (year, month) = if start_dt.month() == 12 {
492 (start_dt.year() + 1, 1)
493 } else {
494 (start_dt.year(), start_dt.month() + 1)
495 };
496 Utc.with_ymd_and_hms(year, month, 1, 0, 0, 0)
497 .single()
498 .map_or(start_ms + 2_678_400_000, |dt| dt.timestamp_millis())
499 }
500 _ => start_ms + self.duration_ms(),
501 }
502 }
503
504 #[must_use]
509 pub const fn duration_ms(&self) -> i64 {
510 match self {
511 Self::Minute1 => 60_000,
512 Self::Minute3 => 180_000,
513 Self::Minute5 => 300_000,
514 Self::Minute15 => 900_000,
515 Self::Minute30 => 1_800_000,
516 Self::Hour1 => 3_600_000,
517 Self::Hour2 => 7_200_000,
518 Self::Hour4 => 14_400_000,
519 Self::Hour6 => 21_600_000,
520 Self::Hour12 => 43_200_000,
521 Self::Day1 => 86_400_000,
522 Self::Week1 => 604_800_000,
523 Self::Month1 => 2_678_400_000, }
525 }
526}
527
528impl Display for BybitKlineInterval {
529 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
530 let s = match self {
531 Self::Minute1 => "1",
532 Self::Minute3 => "3",
533 Self::Minute5 => "5",
534 Self::Minute15 => "15",
535 Self::Minute30 => "30",
536 Self::Hour1 => "60",
537 Self::Hour2 => "120",
538 Self::Hour4 => "240",
539 Self::Hour6 => "360",
540 Self::Hour12 => "720",
541 Self::Day1 => "D",
542 Self::Week1 => "W",
543 Self::Month1 => "M",
544 };
545 write!(f, "{s}")
546 }
547}
548
549#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
551#[cfg_attr(
552 feature = "python",
553 pyo3::pyclass(
554 module = "nautilus_trader.core.nautilus_pyo3.bybit",
555 eq,
556 eq_int,
557 from_py_object
558 )
559)]
560#[cfg_attr(
561 feature = "python",
562 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
563)]
564pub enum BybitOrderStatus {
565 #[serde(rename = "Created")]
566 Created,
567 #[serde(rename = "New")]
568 New,
569 #[serde(rename = "Rejected")]
570 Rejected,
571 #[serde(rename = "PartiallyFilled")]
572 PartiallyFilled,
573 #[serde(rename = "PartiallyFilledCanceled")]
574 PartiallyFilledCanceled,
575 #[serde(rename = "Filled")]
576 Filled,
577 #[serde(rename = "Cancelled")]
578 Canceled,
579 #[serde(rename = "Untriggered")]
580 Untriggered,
581 #[serde(rename = "Triggered")]
582 Triggered,
583 #[serde(rename = "Deactivated")]
584 Deactivated,
585}
586
587#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
589#[cfg_attr(
590 feature = "python",
591 pyo3::pyclass(
592 module = "nautilus_trader.core.nautilus_pyo3.bybit",
593 eq,
594 eq_int,
595 from_py_object
596 )
597)]
598#[cfg_attr(
599 feature = "python",
600 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
601)]
602pub enum BybitOrderSide {
603 #[serde(rename = "")]
604 Unknown,
605 #[serde(rename = "Buy")]
606 Buy,
607 #[serde(rename = "Sell")]
608 Sell,
609}
610
611impl From<BybitOrderSide> for AggressorSide {
612 fn from(value: BybitOrderSide) -> Self {
613 match value {
614 BybitOrderSide::Buy => Self::Buyer,
615 BybitOrderSide::Sell => Self::Seller,
616 BybitOrderSide::Unknown => Self::NoAggressor,
617 }
618 }
619}
620
621impl From<BybitOrderSide> for OrderSide {
622 fn from(value: BybitOrderSide) -> Self {
623 match value {
624 BybitOrderSide::Buy => Self::Buy,
625 BybitOrderSide::Sell => Self::Sell,
626 BybitOrderSide::Unknown => Self::NoOrderSide,
627 }
628 }
629}
630
631impl TryFrom<OrderSide> for BybitOrderSide {
632 type Error = anyhow::Error;
633
634 fn try_from(value: OrderSide) -> Result<Self, Self::Error> {
635 match value {
636 OrderSide::Buy => Ok(Self::Buy),
637 OrderSide::Sell => Ok(Self::Sell),
638 _ => anyhow::bail!("unsupported OrderSide for Bybit: {value:?}"),
639 }
640 }
641}
642
643impl From<BybitTriggerType> for TriggerType {
644 fn from(value: BybitTriggerType) -> Self {
645 match value {
646 BybitTriggerType::None => Self::Default,
647 BybitTriggerType::LastPrice => Self::LastPrice,
648 BybitTriggerType::IndexPrice => Self::IndexPrice,
649 BybitTriggerType::MarkPrice => Self::MarkPrice,
650 }
651 }
652}
653
654impl From<TriggerType> for BybitTriggerType {
655 fn from(value: TriggerType) -> Self {
656 match value {
657 TriggerType::Default | TriggerType::LastPrice | TriggerType::NoTrigger => {
658 Self::LastPrice
659 }
660 TriggerType::IndexPrice => Self::IndexPrice,
661 TriggerType::MarkPrice => Self::MarkPrice,
662 _ => Self::LastPrice,
663 }
664 }
665}
666
667pub fn resolve_trigger_type(trigger_type: Option<TriggerType>) -> BybitTriggerType {
670 trigger_type.map_or(BybitTriggerType::LastPrice, BybitTriggerType::from)
671}
672
673#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
675#[serde(rename_all = "PascalCase")]
676#[cfg_attr(
677 feature = "python",
678 pyo3::pyclass(
679 module = "nautilus_trader.core.nautilus_pyo3.bybit",
680 eq,
681 eq_int,
682 from_py_object
683 )
684)]
685#[cfg_attr(
686 feature = "python",
687 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
688)]
689pub enum BybitCancelType {
690 CancelByUser,
691 CancelByReduceOnly,
692 CancelByPrepareLackOfMargin,
693 CancelByPrepareOrderFilter,
694 CancelByPrepareOrderMarginCheckFailed,
695 CancelByPrepareOrderCommission,
696 CancelByPrepareOrderRms,
697 CancelByPrepareOrderOther,
698 CancelByRiskLimit,
699 CancelOnDisconnect,
700 CancelByStopOrdersExceeded,
701 CancelByPzMarketClose,
702 CancelByMarginCheckFailed,
703 CancelByPzTakeover,
704 CancelByAdmin,
705 CancelByTpSlTsClear,
706 CancelByAmendNotModified,
707 CancelByPzCancel,
708 CancelByCrossSelfMatch,
709 CancelBySelfMatchPrevention,
710 #[serde(other)]
711 Other,
712}
713
714#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
716#[serde(rename_all = "PascalCase")]
717pub enum BybitCreateType {
718 CreateByUser,
719 CreateByClosing,
720 CreateByTakeProfit,
721 CreateByStopLoss,
722 CreateByTrailingStop,
723 CreateByStopOrder,
724 CreateByPartialTakeProfit,
725 CreateByPartialStopLoss,
726 CreateByAdl,
727 CreateByLiquidate,
728 CreateByTakeover,
729 CreateByTpsl,
730 CreateByBboOrder,
731 #[serde(other)]
732 Other,
733}
734
735#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
737#[serde(rename_all = "PascalCase")]
738pub enum BybitBboSideType {
739 Queue,
740 Counterparty,
741}
742
743#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
745#[cfg_attr(
746 feature = "python",
747 pyo3::pyclass(
748 module = "nautilus_trader.core.nautilus_pyo3.bybit",
749 eq,
750 eq_int,
751 from_py_object
752 )
753)]
754#[cfg_attr(
755 feature = "python",
756 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
757)]
758pub enum BybitOrderType {
759 #[serde(rename = "Market")]
760 Market,
761 #[serde(rename = "Limit")]
762 Limit,
763 #[serde(rename = "UNKNOWN")]
764 Unknown,
765}
766
767#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
769#[cfg_attr(
770 feature = "python",
771 pyo3::pyclass(
772 module = "nautilus_trader.core.nautilus_pyo3.bybit",
773 eq,
774 eq_int,
775 from_py_object
776 )
777)]
778#[cfg_attr(
779 feature = "python",
780 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
781)]
782pub enum BybitStopOrderType {
783 #[serde(rename = "")]
784 None,
785 #[serde(rename = "UNKNOWN")]
786 Unknown,
787 #[serde(rename = "TakeProfit")]
788 TakeProfit,
789 #[serde(rename = "StopLoss")]
790 StopLoss,
791 #[serde(rename = "TrailingStop")]
792 TrailingStop,
793 #[serde(rename = "Stop")]
794 Stop,
795 #[serde(rename = "PartialTakeProfit")]
796 PartialTakeProfit,
797 #[serde(rename = "PartialStopLoss")]
798 PartialStopLoss,
799 #[serde(rename = "tpslOrder")]
800 TpslOrder,
801 #[serde(rename = "OcoOrder")]
802 OcoOrder,
803 #[serde(rename = "MmRateClose")]
804 MmRateClose,
805 #[serde(rename = "BidirectionalTpslOrder")]
806 BidirectionalTpslOrder,
807}
808
809#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
811#[cfg_attr(
812 feature = "python",
813 pyo3::pyclass(
814 module = "nautilus_trader.core.nautilus_pyo3.bybit",
815 eq,
816 eq_int,
817 from_py_object
818 )
819)]
820#[cfg_attr(
821 feature = "python",
822 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
823)]
824pub enum BybitTriggerType {
825 #[serde(rename = "")]
826 None,
827 #[serde(rename = "LastPrice")]
828 LastPrice,
829 #[serde(rename = "IndexPrice")]
830 IndexPrice,
831 #[serde(rename = "MarkPrice")]
832 MarkPrice,
833}
834
835#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize_repr, Deserialize_repr)]
837#[repr(i32)]
838#[cfg_attr(
839 feature = "python",
840 pyo3::pyclass(
841 module = "nautilus_trader.core.nautilus_pyo3.bybit",
842 eq,
843 eq_int,
844 from_py_object
845 )
846)]
847#[cfg_attr(
848 feature = "python",
849 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
850)]
851pub enum BybitTriggerDirection {
852 None = 0,
853 RisesTo = 1,
854 FallsTo = 2,
855}
856
857#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
859#[serde(rename_all = "PascalCase")]
860#[cfg_attr(
861 feature = "python",
862 pyo3::pyclass(
863 module = "nautilus_trader.core.nautilus_pyo3.bybit",
864 eq,
865 eq_int,
866 from_py_object
867 )
868)]
869#[cfg_attr(
870 feature = "python",
871 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
872)]
873pub enum BybitTpSlMode {
874 Full,
875 Partial,
876 #[serde(other)]
877 Unknown,
878}
879
880#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
882#[cfg_attr(
883 feature = "python",
884 pyo3::pyclass(
885 module = "nautilus_trader.core.nautilus_pyo3.bybit",
886 eq,
887 eq_int,
888 from_py_object
889 )
890)]
891#[cfg_attr(
892 feature = "python",
893 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
894)]
895pub enum BybitTimeInForce {
896 #[serde(rename = "GTC")]
897 Gtc,
898 #[serde(rename = "IOC")]
899 Ioc,
900 #[serde(rename = "FOK")]
901 Fok,
902 #[serde(rename = "PostOnly")]
903 PostOnly,
904}
905
906#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
910pub enum BybitExecType {
911 #[serde(rename = "Trade")]
912 Trade,
913 #[serde(rename = "AdlTrade")]
914 AdlTrade,
915 #[serde(rename = "Funding")]
916 Funding,
917 #[serde(rename = "BustTrade")]
918 BustTrade,
919 #[serde(rename = "Delivery")]
920 Delivery,
921 #[serde(rename = "Settle")]
922 Settle,
923 #[serde(rename = "BlockTrade")]
924 BlockTrade,
925 #[serde(rename = "MovePosition")]
926 MovePosition,
927 #[serde(rename = "UNKNOWN")]
928 Unknown,
929}
930
931impl BybitExecType {
932 #[must_use]
937 pub const fn is_exchange_generated(&self) -> bool {
938 matches!(
939 self,
940 Self::AdlTrade | Self::BustTrade | Self::Delivery | Self::Settle
941 )
942 }
943}
944
945#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
947pub enum BybitTransactionType {
948 #[serde(rename = "TRANSFER_IN")]
949 TransferIn,
950 #[serde(rename = "TRANSFER_OUT")]
951 TransferOut,
952 #[serde(rename = "TRADE")]
953 Trade,
954 #[serde(rename = "SETTLEMENT")]
955 Settlement,
956 #[serde(rename = "DELIVERY")]
957 Delivery,
958 #[serde(rename = "LIQUIDATION")]
959 Liquidation,
960 #[serde(rename = "AIRDRP")]
961 Airdrop,
962}
963
964#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
966#[serde(rename_all = "UPPERCASE")]
967pub enum BybitEndpointType {
968 None,
969 Asset,
970 Market,
971 Account,
972 Trade,
973 Position,
974 User,
975}
976
977#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Serialize_repr, Deserialize_repr)]
981#[repr(i32)]
982#[cfg_attr(
983 feature = "python",
984 pyo3::pyclass(
985 module = "nautilus_trader.core.nautilus_pyo3.bybit",
986 eq,
987 eq_int,
988 from_py_object
989 )
990)]
991#[cfg_attr(
992 feature = "python",
993 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
994)]
995pub enum BybitOpenOnly {
996 #[default]
998 OpenOnly = 0,
999 ClosedRecent = 1,
1001}
1002
1003#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
1007#[cfg_attr(
1008 feature = "python",
1009 pyo3::pyclass(
1010 module = "nautilus_trader.core.nautilus_pyo3.bybit",
1011 eq,
1012 eq_int,
1013 from_py_object
1014 )
1015)]
1016#[cfg_attr(
1017 feature = "python",
1018 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
1019)]
1020pub enum BybitOrderFilter {
1021 #[default]
1023 Order,
1024 StopOrder,
1026 #[serde(rename = "tpslOrder")]
1028 TpslOrder,
1029 OcoOrder,
1031 BidirectionalTpslOrder,
1033}
1034
1035#[derive(
1037 Clone,
1038 Copy,
1039 Debug,
1040 strum::Display,
1041 Eq,
1042 PartialEq,
1043 Hash,
1044 AsRefStr,
1045 EnumIter,
1046 EnumString,
1047 Serialize,
1048 Deserialize,
1049)]
1050#[serde(rename_all = "snake_case")]
1051#[strum(serialize_all = "snake_case")]
1052#[cfg_attr(
1053 feature = "python",
1054 pyo3::pyclass(
1055 eq,
1056 eq_int,
1057 hash,
1058 frozen,
1059 rename_all = "SCREAMING_SNAKE_CASE",
1060 module = "nautilus_trader.core.nautilus_pyo3.bybit",
1061 from_py_object,
1062 )
1063)]
1064#[cfg_attr(
1065 feature = "python",
1066 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.adapters.bybit")
1067)]
1068pub enum BybitMarginAction {
1069 Borrow,
1071 Repay,
1073 GetBorrowAmount,
1075}
1076
1077#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1079#[serde(rename_all = "PascalCase")]
1080pub enum BybitPositionStatus {
1081 Normal,
1082 Settle,
1083 Delivering,
1084 #[serde(other)]
1085 Other,
1086}
1087
1088#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1090pub enum BybitMarketUnit {
1091 #[serde(rename = "baseCoin")]
1092 BaseCoin,
1093 #[serde(rename = "quoteCoin")]
1094 QuoteCoin,
1095}
1096
1097#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
1099pub enum BybitSmpType {
1100 None,
1101 CancelMaker,
1102 CancelTaker,
1103 CancelBoth,
1104 #[serde(other)]
1105 Other,
1106}
1107
1108#[cfg(test)]
1109mod tests {
1110 use rstest::rstest;
1111
1112 use super::*;
1113
1114 #[rstest]
1115 #[case::minute1(BybitKlineInterval::Minute1, 60_000)]
1116 #[case::minute3(BybitKlineInterval::Minute3, 180_000)]
1117 #[case::minute5(BybitKlineInterval::Minute5, 300_000)]
1118 #[case::minute15(BybitKlineInterval::Minute15, 900_000)]
1119 #[case::minute30(BybitKlineInterval::Minute30, 1_800_000)]
1120 #[case::hour1(BybitKlineInterval::Hour1, 3_600_000)]
1121 #[case::hour2(BybitKlineInterval::Hour2, 7_200_000)]
1122 #[case::hour4(BybitKlineInterval::Hour4, 14_400_000)]
1123 #[case::hour6(BybitKlineInterval::Hour6, 21_600_000)]
1124 #[case::hour12(BybitKlineInterval::Hour12, 43_200_000)]
1125 #[case::day1(BybitKlineInterval::Day1, 86_400_000)]
1126 #[case::week1(BybitKlineInterval::Week1, 604_800_000)]
1127 #[case::month1(BybitKlineInterval::Month1, 2_678_400_000)]
1128 fn test_kline_interval_duration_ms(
1129 #[case] interval: BybitKlineInterval,
1130 #[case] expected_ms: i64,
1131 ) {
1132 assert_eq!(interval.duration_ms(), expected_ms);
1133 }
1134
1135 #[rstest]
1136 fn test_bar_end_time_ms_non_monthly_adds_duration() {
1137 let interval = BybitKlineInterval::Minute1;
1138 let start_ms = 1704067200000i64;
1139 assert_eq!(interval.bar_end_time_ms(start_ms), start_ms + 60_000);
1140 }
1141
1142 #[rstest]
1143 #[case::jan_31_days(1704067200000i64, 1706745600000i64)]
1144 #[case::feb_leap_year_29_days(1706745600000i64, 1709251200000i64)]
1145 #[case::apr_30_days(1711929600000i64, 1714521600000i64)]
1146 #[case::dec_to_next_year(1733011200000i64, 1735689600000i64)]
1147 fn test_bar_end_time_ms_monthly_variable_lengths(
1148 #[case] start_ms: i64,
1149 #[case] expected_end_ms: i64,
1150 ) {
1151 let interval = BybitKlineInterval::Month1;
1152 assert_eq!(interval.bar_end_time_ms(start_ms), expected_end_ms);
1153 }
1154
1155 #[rstest]
1156 #[case(BybitExecType::Trade, false)]
1157 #[case(BybitExecType::AdlTrade, true)]
1158 #[case(BybitExecType::BustTrade, true)]
1159 #[case(BybitExecType::Delivery, true)]
1160 #[case(BybitExecType::Settle, true)]
1161 #[case(BybitExecType::Funding, false)]
1162 fn test_exec_type_is_exchange_generated(
1163 #[case] exec_type: BybitExecType,
1164 #[case] expected: bool,
1165 ) {
1166 assert_eq!(exec_type.is_exchange_generated(), expected);
1167 }
1168}