deribit_http/model/response/
subaccount.rs1use crate::model::position::Position;
9use pretty_simple_display::{DebugPretty, DisplaySimple};
10use serde::{Deserialize, Serialize};
11use serde_with::skip_serializing_none;
12
13#[skip_serializing_none]
18#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
19pub struct SubaccountDetails {
20 pub uid: i64,
22 pub positions: Vec<Position>,
24 pub open_orders: Option<Vec<serde_json::Value>>,
26}
27
28impl SubaccountDetails {
29 pub fn new(uid: i64, positions: Vec<Position>) -> Self {
31 Self {
32 uid,
33 positions,
34 open_orders: None,
35 }
36 }
37
38 pub fn with_open_orders(
40 uid: i64,
41 positions: Vec<Position>,
42 open_orders: Vec<serde_json::Value>,
43 ) -> Self {
44 Self {
45 uid,
46 positions,
47 open_orders: Some(open_orders),
48 }
49 }
50
51 pub fn has_positions(&self) -> bool {
53 !self.positions.is_empty()
54 }
55
56 pub fn position_count(&self) -> usize {
58 self.positions.len()
59 }
60
61 pub fn has_open_orders(&self) -> bool {
63 self.open_orders
64 .as_ref()
65 .map(|orders: &Vec<serde_json::Value>| !orders.is_empty())
66 .unwrap_or(false)
67 }
68
69 pub fn open_orders_count(&self) -> usize {
71 self.open_orders
72 .as_ref()
73 .map(|orders: &Vec<serde_json::Value>| orders.len())
74 .unwrap_or(0)
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use crate::model::types::Direction;
82
83 fn create_test_position() -> Position {
84 Position {
85 average_price: 49571.3,
86 average_price_usd: None,
87 delta: Some(0.004152776),
88 direction: Direction::Buy,
89 estimated_liquidation_price: Some(2.33),
90 floating_profit_loss: Some(-0.00003451),
91 floating_profit_loss_usd: None,
92 gamma: None,
93 index_price: Some(47897.12),
94 initial_margin: Some(0.000122508),
95 instrument_name: "BTC-PERPETUAL".to_string(),
96 interest_value: None,
97 kind: Some("future".to_string()),
98 leverage: Some(34),
99 maintenance_margin: Some(0.000089286),
100 mark_price: Some(48160.55),
101 open_orders_margin: Some(0.0),
102 realized_funding: Some(-8.8e-7),
103 realized_profit_loss: Some(-8.79e-7),
104 settlement_price: Some(48150.36),
105 size: 200.0,
106 size_currency: Some(0.004152776),
107 theta: None,
108 total_profit_loss: Some(-0.000118183),
109 vega: None,
110 unrealized_profit_loss: None,
111 }
112 }
113
114 #[test]
115 fn test_subaccount_details_new() {
116 let positions = vec![create_test_position()];
117 let details = SubaccountDetails::new(3, positions);
118
119 assert_eq!(details.uid, 3);
120 assert_eq!(details.position_count(), 1);
121 assert!(details.has_positions());
122 assert!(!details.has_open_orders());
123 assert_eq!(details.open_orders_count(), 0);
124 }
125
126 #[test]
127 fn test_subaccount_details_empty() {
128 let details = SubaccountDetails::new(10, vec![]);
129
130 assert_eq!(details.uid, 10);
131 assert_eq!(details.position_count(), 0);
132 assert!(!details.has_positions());
133 }
134
135 #[test]
136 fn test_subaccount_details_deserialization() {
137 let json = r#"{
138 "uid": 3,
139 "positions": [
140 {
141 "total_profit_loss": -0.000118183,
142 "size_currency": 0.004152776,
143 "size": 200,
144 "settlement_price": 48150.36,
145 "realized_profit_loss": -8.79e-7,
146 "realized_funding": -8.8e-7,
147 "open_orders_margin": 0,
148 "mark_price": 48160.55,
149 "maintenance_margin": 0.000089286,
150 "leverage": 34,
151 "kind": "future",
152 "instrument_name": "BTC-PERPETUAL",
153 "initial_margin": 0.000122508,
154 "index_price": 47897.12,
155 "floating_profit_loss": -0.00003451,
156 "estimated_liquidation_price": 2.33,
157 "direction": "buy",
158 "delta": 0.004152776,
159 "average_price": 49571.3
160 }
161 ]
162 }"#;
163
164 let details: SubaccountDetails = serde_json::from_str(json).unwrap();
165 assert_eq!(details.uid, 3);
166 assert_eq!(details.position_count(), 1);
167 assert_eq!(details.positions[0].instrument_name, "BTC-PERPETUAL");
168 }
169}