asterdex_sdk/futures/models/
user_data.rs1use serde::Deserialize;
5
6#[derive(Debug)]
19pub enum UserDataEvent {
20 OrderUpdate(Box<OrderUpdateEvent>),
22 AccountUpdate(Box<AccountUpdateEvent>),
24 AccountConfigUpdate(AccountConfigUpdateEvent),
26 Unknown(serde_json::Value),
28}
29
30#[derive(Debug, Deserialize)]
32pub struct OrderUpdateEvent {
33 #[serde(rename = "E")] pub event_time: u64,
34 #[serde(rename = "T")] pub transaction_time: u64,
35 #[serde(rename = "o")] pub order: OrderUpdateData,
36}
37
38#[derive(Debug, Deserialize)]
40pub struct OrderUpdateData {
41 #[serde(rename = "s")] pub symbol: String,
42 #[serde(rename = "c")] pub client_order_id: String,
43 #[serde(rename = "S")] pub side: String,
44 #[serde(rename = "o")] pub order_type: String,
45 #[serde(rename = "f")] pub time_in_force: String,
46 #[serde(rename = "q")] pub quantity: String,
47 #[serde(rename = "p")] pub price: String,
48 #[serde(rename = "ap")] pub avg_price: String,
49 #[serde(rename = "sp")] pub stop_price: String,
50 #[serde(rename = "x")] pub execution_type: String,
51 #[serde(rename = "X")] pub order_status: String,
52 #[serde(rename = "i")] pub order_id: i64,
53 #[serde(rename = "l")] pub last_filled_qty: String,
54 #[serde(rename = "z")] pub cumulative_filled_qty: String,
55 #[serde(rename = "L")] pub last_filled_price: String,
56 #[serde(rename = "t")] pub trade_id: i64,
57 #[serde(rename = "T")] pub order_trade_time: u64,
58 #[serde(rename = "ps")] pub position_side: String,
59 #[serde(rename = "rp")] pub realized_profit: String,
60}
61
62#[derive(Debug, Deserialize)]
64pub struct AccountUpdateEvent {
65 #[serde(rename = "E")] pub event_time: u64,
66 #[serde(rename = "T")] pub transaction_time: u64,
67 #[serde(rename = "a")] pub account: AccountUpdateData,
68}
69
70#[derive(Debug, Deserialize)]
72pub struct AccountUpdateData {
73 #[serde(rename = "m")] pub event_reason: String,
74 #[serde(rename = "B")] pub balances: Vec<BalanceDelta>,
75 #[serde(rename = "P")] pub positions: Vec<PositionDelta>,
76}
77
78#[derive(Debug, Deserialize)]
80pub struct BalanceDelta {
81 #[serde(rename = "a")] pub asset: String,
82 #[serde(rename = "wb")] pub wallet_balance: String,
83 #[serde(rename = "cw")] pub cross_wallet_balance: String,
84 #[serde(rename = "bc")] pub balance_change: String,
85}
86
87#[derive(Debug, Deserialize)]
89pub struct PositionDelta {
90 #[serde(rename = "s")] pub symbol: String,
91 #[serde(rename = "pa")] pub position_amount: String,
92 #[serde(rename = "ep")] pub entry_price: String,
93 #[serde(rename = "cr")] pub accumulated_realized: String,
94 #[serde(rename = "up")] pub unrealized_pnl: String,
95 #[serde(rename = "mt")] pub margin_type: String,
96 #[serde(rename = "iw")] pub isolated_wallet: String,
97 #[serde(rename = "ps")] pub position_side: String,
98}
99
100#[derive(Debug, Deserialize)]
105pub struct AccountConfigUpdateEvent {
106 #[serde(rename = "E")] pub event_time: u64,
107 #[serde(rename = "T")] pub transaction_time: u64,
108 #[serde(rename = "ac")] pub leverage_change: Option<LeverageChangeData>,
109 #[serde(rename = "ai")] pub multi_assets_margin: Option<MultiAssetsModeData>,
110}
111
112#[derive(Debug, Deserialize)]
114pub struct LeverageChangeData {
115 #[serde(rename = "s")] pub symbol: String,
116 #[serde(rename = "l")] pub leverage: u32,
117}
118
119#[derive(Debug, Deserialize)]
121pub struct MultiAssetsModeData {
122 #[serde(rename = "j")] pub is_enabled: bool,
123}
124
125impl<'de> serde::Deserialize<'de> for UserDataEvent {
130 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
131 let v = serde_json::Value::deserialize(deserializer)?;
132 let event_type = v.get("e").and_then(|e| e.as_str()).unwrap_or("");
133 match event_type {
134 "ORDER_TRADE_UPDATE" => serde_json::from_value::<OrderUpdateEvent>(v)
135 .map(|e| UserDataEvent::OrderUpdate(Box::new(e)))
136 .map_err(serde::de::Error::custom),
137 "ACCOUNT_UPDATE" => serde_json::from_value::<AccountUpdateEvent>(v)
138 .map(|e| UserDataEvent::AccountUpdate(Box::new(e)))
139 .map_err(serde::de::Error::custom),
140 "ACCOUNT_CONFIG_UPDATE" => serde_json::from_value::<AccountConfigUpdateEvent>(v)
141 .map(UserDataEvent::AccountConfigUpdate)
142 .map_err(serde::de::Error::custom),
143 _ => {
144 tracing::warn!(event_type = event_type, "Unknown user data event type");
145 Ok(UserDataEvent::Unknown(v))
146 }
147 }
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn order_update_event_deserializes() {
157 let json = r#"{
158 "e": "ORDER_TRADE_UPDATE",
159 "E": 1625184000000,
160 "T": 1625184000000,
161 "o": {
162 "s": "BTCUSDT",
163 "c": "client-id-1",
164 "S": "BUY",
165 "o": "LIMIT",
166 "f": "GTC",
167 "q": "0.001",
168 "p": "30000",
169 "ap": "0",
170 "sp": "0",
171 "x": "NEW",
172 "X": "NEW",
173 "i": 123456789,
174 "l": "0",
175 "z": "0",
176 "L": "0",
177 "t": -1,
178 "T": 1625184000000,
179 "ps": "BOTH",
180 "rp": "0"
181 }
182 }"#;
183 let event: UserDataEvent = serde_json::from_str(json).unwrap();
184 assert!(matches!(event, UserDataEvent::OrderUpdate(ref e) if e.order.symbol == "BTCUSDT"));
185 }
186
187 #[test]
188 fn account_update_event_deserializes() {
189 let json = r#"{
190 "e": "ACCOUNT_UPDATE",
191 "E": 1625184000000,
192 "T": 1625184000000,
193 "a": {
194 "m": "DEPOSIT",
195 "B": [{"a": "USDT", "wb": "1000.00", "cw": "1000.00", "bc": "100.00"}],
196 "P": []
197 }
198 }"#;
199 let event: UserDataEvent = serde_json::from_str(json).unwrap();
200 assert!(matches!(event, UserDataEvent::AccountUpdate(_)));
201 if let UserDataEvent::AccountUpdate(e) = event {
202 assert_eq!(e.account.balances[0].asset, "USDT");
203 assert_eq!(e.account.balances[0].wallet_balance, "1000.00");
204 assert!(e.account.positions.is_empty());
205 }
206 }
207
208 #[test]
209 fn unknown_event_type_maps_to_unknown() {
210 let json = r#"{"e":"NEW_FUTURE_EVENT","x":"data"}"#;
211 let event: UserDataEvent = serde_json::from_str(json).unwrap();
212 assert!(matches!(event, UserDataEvent::Unknown(_)));
213 }
214
215 #[test]
216 fn account_config_update_leverage_deserializes() {
217 let json = r#"{
218 "e": "ACCOUNT_CONFIG_UPDATE",
219 "E": 1625184000000,
220 "T": 1625184000000,
221 "ac": { "s": "BTCUSDT", "l": 25 }
222 }"#;
223 let event: UserDataEvent = serde_json::from_str(json).unwrap();
224 assert!(matches!(event, UserDataEvent::AccountConfigUpdate(_)));
225 if let UserDataEvent::AccountConfigUpdate(e) = event {
226 assert!(e.leverage_change.is_some());
227 assert!(e.multi_assets_margin.is_none());
228 }
229 }
230
231 #[test]
232 fn account_config_update_multi_assets_deserializes() {
233 let json = r#"{
234 "e": "ACCOUNT_CONFIG_UPDATE",
235 "E": 1625184000000,
236 "T": 1625184000000,
237 "ai": { "j": true }
238 }"#;
239 let event: UserDataEvent = serde_json::from_str(json).unwrap();
240 assert!(matches!(event, UserDataEvent::AccountConfigUpdate(_)));
241 if let UserDataEvent::AccountConfigUpdate(e) = event {
242 assert!(e.multi_assets_margin.is_some());
243 assert!(e.leverage_change.is_none());
244 }
245 }
246}