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