deribit_base/model/
settlement.rs1use crate::{impl_json_debug_pretty, impl_json_display};
8use serde::{Deserialize, Serialize};
9
10#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12#[serde(rename_all = "lowercase")]
13pub enum SettlementType {
14 Settlement,
16 Delivery,
18 Bankruptcy,
20}
21
22impl Default for SettlementType {
23 fn default() -> Self {
24 Self::Settlement
25 }
26}
27
28impl_json_display!(SettlementType);
29impl_json_debug_pretty!(SettlementType);
30
31#[derive(Clone, PartialEq, Serialize, Deserialize)]
33pub struct Settlement {
34 pub settlement_type: SettlementType,
36 pub timestamp: i64,
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub instrument_name: Option<String>,
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub position_size: Option<f64>,
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub mark_price: Option<f64>,
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub index_price: Option<f64>,
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub profit_loss: Option<f64>,
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub funding: Option<f64>,
56}
57
58impl Settlement {
59 pub fn new(settlement_type: SettlementType, timestamp: i64) -> Self {
61 Self {
62 settlement_type,
63 timestamp,
64 instrument_name: None,
65 position_size: None,
66 mark_price: None,
67 index_price: None,
68 profit_loss: None,
69 funding: None,
70 }
71 }
72
73 pub fn for_instrument(
75 settlement_type: SettlementType,
76 timestamp: i64,
77 instrument_name: String,
78 ) -> Self {
79 Self {
80 settlement_type,
81 timestamp,
82 instrument_name: Some(instrument_name),
83 position_size: None,
84 mark_price: None,
85 index_price: None,
86 profit_loss: None,
87 funding: None,
88 }
89 }
90
91 pub fn with_position(mut self, size: f64, mark_price: f64, index_price: f64) -> Self {
93 self.position_size = Some(size);
94 self.mark_price = Some(mark_price);
95 self.index_price = Some(index_price);
96 self
97 }
98
99 pub fn with_pnl(mut self, pnl: f64) -> Self {
101 self.profit_loss = Some(pnl);
102 self
103 }
104
105 pub fn with_funding(mut self, funding: f64) -> Self {
107 self.funding = Some(funding);
108 self
109 }
110
111 pub fn is_settlement(&self) -> bool {
113 matches!(self.settlement_type, SettlementType::Settlement)
114 }
115
116 pub fn is_delivery(&self) -> bool {
118 matches!(self.settlement_type, SettlementType::Delivery)
119 }
120
121 pub fn is_bankruptcy(&self) -> bool {
123 matches!(self.settlement_type, SettlementType::Bankruptcy)
124 }
125}
126
127impl Default for Settlement {
128 fn default() -> Self {
129 Self::new(SettlementType::default(), 0)
130 }
131}
132
133impl_json_display!(Settlement);
134impl_json_debug_pretty!(Settlement);
135
136#[derive(Clone, PartialEq, Serialize, Deserialize)]
138pub struct Settlements {
139 pub settlements: Vec<Settlement>,
141}
142
143impl Settlements {
144 pub fn new() -> Self {
146 Self {
147 settlements: Vec::new(),
148 }
149 }
150
151 pub fn add(&mut self, settlement: Settlement) {
153 self.settlements.push(settlement);
154 }
155
156 pub fn by_type(&self, settlement_type: SettlementType) -> Vec<&Settlement> {
158 self.settlements
159 .iter()
160 .filter(|s| s.settlement_type == settlement_type)
161 .collect()
162 }
163
164 pub fn by_instrument(&self, instrument_name: &str) -> Vec<&Settlement> {
166 self.settlements
167 .iter()
168 .filter(|s| {
169 s.instrument_name
170 .as_ref()
171 .is_some_and(|name| name == instrument_name)
172 })
173 .collect()
174 }
175}
176
177impl Default for Settlements {
178 fn default() -> Self {
179 Self::new()
180 }
181}
182
183impl_json_display!(Settlements);
184impl_json_debug_pretty!(Settlements);
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn test_settlement_creation() {
192 let settlement = Settlement::new(SettlementType::Settlement, 1640995200000);
193 assert_eq!(settlement.settlement_type, SettlementType::Settlement);
194 assert_eq!(settlement.timestamp, 1640995200000);
195 assert!(settlement.instrument_name.is_none());
196 }
197
198 #[test]
199 fn test_settlement_builder() {
200 let settlement = Settlement::for_instrument(
201 SettlementType::Delivery,
202 1640995200000,
203 "BTC-25MAR23".to_string(),
204 )
205 .with_position(1.5, 45000.0, 44950.0)
206 .with_pnl(75.0);
207
208 assert_eq!(settlement.settlement_type, SettlementType::Delivery);
209 assert_eq!(settlement.instrument_name, Some("BTC-25MAR23".to_string()));
210 assert_eq!(settlement.position_size, Some(1.5));
211 assert_eq!(settlement.profit_loss, Some(75.0));
212 }
213
214 #[test]
215 fn test_settlement_type_checks() {
216 let settlement = Settlement::new(SettlementType::Settlement, 0);
217 assert!(settlement.is_settlement());
218 assert!(!settlement.is_delivery());
219 assert!(!settlement.is_bankruptcy());
220 }
221
222 #[test]
223 fn test_settlements_collection() {
224 let mut settlements = Settlements::new();
225 settlements.add(Settlement::new(SettlementType::Settlement, 1000));
226 settlements.add(Settlement::new(SettlementType::Delivery, 2000));
227
228 assert_eq!(settlements.settlements.len(), 2);
229 assert_eq!(settlements.by_type(SettlementType::Settlement).len(), 1);
230 assert_eq!(settlements.by_type(SettlementType::Delivery).len(), 1);
231 }
232
233 #[test]
234 fn test_serde() {
235 let settlement = Settlement::for_instrument(
236 SettlementType::Settlement,
237 1640995200000,
238 "BTC-PERPETUAL".to_string(),
239 )
240 .with_funding(0.0001);
241
242 let json = serde_json::to_string(&settlement).unwrap();
243 let deserialized: Settlement = serde_json::from_str(&json).unwrap();
244 assert_eq!(settlement, deserialized);
245 }
246}