nautilus_bybit/
data_types.rs1use nautilus_core::UnixNanos;
22use nautilus_model::{
23 custom_data,
24 enums::PositionSide,
25 identifiers::InstrumentId,
26 types::{Price, Quantity},
27};
28#[cfg(feature = "arrow")]
29use nautilus_serialization::arrow_custom_data;
30
31#[cfg_attr(
35 feature = "arrow",
36 arrow_custom_data(pyo3, stub_module = "nautilus_trader.adapters.bybit")
37)]
38#[custom_data(pyo3, stub_module = "nautilus_trader.adapters.bybit")]
39pub struct BybitLiquidation {
40 pub instrument_id: InstrumentId,
42 #[custom_data_field(native_enum)]
44 pub position_side: PositionSide,
45 pub bankruptcy_price: Price,
47 pub quantity: Quantity,
49 pub ts_event: UnixNanos,
51 pub ts_init: UnixNanos,
53}
54
55pub fn register_bybit_custom_data() {
59 #[cfg(feature = "arrow")]
60 {
61 nautilus_serialization::ensure_custom_data_registered::<BybitLiquidation>();
62 }
63
64 #[cfg(not(feature = "arrow"))]
65 {
66 let _ = nautilus_model::data::ensure_custom_data_json_registered::<BybitLiquidation>();
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use nautilus_model::data::{custom::CustomDataTrait, register_custom_data_json};
73 use rstest::rstest;
74
75 use super::*;
76
77 fn liquidation() -> BybitLiquidation {
78 BybitLiquidation::new(
79 InstrumentId::from("BTCUSDT-LINEAR.BYBIT"),
80 PositionSide::Short,
81 Price::from("97410.5"),
82 Quantity::from("1.250"),
83 UnixNanos::from(1_739_502_302_929_000_000),
84 UnixNanos::from(1_739_502_303_500_000_000),
85 )
86 }
87
88 #[rstest]
89 fn test_register_bybit_custom_data_is_idempotent() {
90 register_bybit_custom_data();
91 register_bybit_custom_data();
92 }
93
94 #[rstest]
95 fn test_register_bybit_custom_data_registers_json_deserializer() {
96 register_bybit_custom_data();
97
98 let err = register_custom_data_json::<BybitLiquidation>().unwrap_err();
99
100 assert_eq!(
101 err.to_string(),
102 "Custom data type \"BybitLiquidation\" is already registered for JSON",
103 );
104 }
105
106 #[rstest]
107 fn test_bybit_liquidation_json_round_trip() {
108 let original = liquidation();
109
110 let value: serde_json::Value = serde_json::from_str(&original.to_json().unwrap()).unwrap();
111 let restored = <BybitLiquidation as CustomDataTrait>::from_json(value).unwrap();
112 let restored = restored
113 .as_any()
114 .downcast_ref::<BybitLiquidation>()
115 .unwrap();
116
117 assert_eq!(restored, &original);
118 assert_eq!(restored.instrument_id, original.instrument_id);
119 assert_eq!(restored.position_side, PositionSide::Short);
120 assert_eq!(restored.bankruptcy_price, Price::from("97410.5"));
121 assert_eq!(restored.quantity, Quantity::from("1.250"));
122 assert_eq!(restored.ts_event, original.ts_event);
123 assert_eq!(restored.ts_init, original.ts_init);
124 }
125
126 #[cfg(feature = "arrow")]
127 #[rstest]
128 fn test_bybit_liquidation_arrow_schema_uses_native_types() {
129 use arrow::datatypes::DataType;
130 use nautilus_serialization::arrow::ArrowSchemaProvider;
131
132 let schema = BybitLiquidation::get_schema(None);
133 let names: Vec<_> = schema.fields().iter().map(|f| f.name().as_str()).collect();
134
135 assert_eq!(
136 names,
137 vec![
138 "instrument_id",
139 "position_side",
140 "bankruptcy_price",
141 "quantity",
142 "ts_event",
143 "ts_init",
144 ],
145 );
146 assert!(matches!(
147 schema.field(0).data_type(),
148 DataType::Utf8 | DataType::Utf8View
149 ));
150 assert_eq!(
151 schema.field(1).data_type(),
152 &nautilus_serialization::arrow::enum_dictionary_data_type(),
153 );
154 assert_eq!(schema.field(2).data_type(), &DataType::Decimal128(38, 16));
155 assert_eq!(schema.field(3).data_type(), &DataType::Decimal128(38, 16));
156 assert_eq!(
157 schema.field(4).data_type(),
158 &nautilus_serialization::arrow::timestamp_data_type(),
159 );
160 assert_eq!(
161 schema.field(5).data_type(),
162 &nautilus_serialization::arrow::timestamp_data_type(),
163 );
164 }
165}