1use super::AggregateTrade;
2use crate::{
3 error::BinanceError::{self, *},
4 models::{ExecutionType, OrderSide, OrderStatus, OrderType, Product, TimeInForce},
5 parser::{string_or_decimal, string_or_decimal_opt},
6 websocket::ParseMessage,
7};
8use fehler::{throw, throws};
9use rust_decimal::Decimal;
10use serde::{Deserialize, Serialize};
11use serde_json::from_str;
12
13#[derive(Debug, Clone, Serialize)]
14#[non_exhaustive]
15pub enum WebsocketMessage {
16 UserOrderUpdate(OrderUpdate),
18 UserAccountUpdate(AccountUpdate),
19 AggregateTrade(AggregateTrade),
21 BookTicker(BookTicker),
22 }
31
32impl ParseMessage for WebsocketMessage {
33 const PRODUCT: Product = Product::UsdMFutures;
34
35 #[throws(BinanceError)]
36 fn parse(stream: &str, data: &str) -> Self {
37 if stream.ends_with("@aggTrade") {
38 Self::AggregateTrade(from_str(data)?)
39 } else if stream.contains("@markPrice") {
40 throw!(StreamNotImplemented(stream.into()))
41 } else if stream.starts_with("!markPrice@arr") {
42 throw!(StreamNotImplemented(stream.into()))
43 } else if stream.contains("@kline_") {
44 throw!(StreamNotImplemented(stream.into()))
45 } else if stream.contains("@continuousKline_") {
46 throw!(StreamNotImplemented(stream.into()))
47 } else if stream.ends_with("@miniTicker") {
48 throw!(StreamNotImplemented(stream.into()))
49 } else if stream == "!miniTicker@arr" {
50 throw!(StreamNotImplemented(stream.into()))
51 } else if stream.ends_with("@ticker") {
52 throw!(StreamNotImplemented(stream.into()))
53 } else if stream == "!ticker@arr" {
54 throw!(StreamNotImplemented(stream.into()))
55 } else if stream.ends_with("@bookTicker") || stream == "!bookTicker" {
56 Self::BookTicker(from_str(data)?)
57 } else if stream.ends_with("@forceOrder") {
58 throw!(StreamNotImplemented(stream.into()))
59 } else if stream == "!forceOrder@arr" {
60 throw!(StreamNotImplemented(stream.into()))
61 } else if stream.ends_with("@depth") || stream.contains("@depth@") {
62 throw!(StreamNotImplemented(stream.into()))
63 } else if stream.contains("@depth") {
64 throw!(StreamNotImplemented(stream.into()))
65 } else if stream.ends_with("@compositeIndex") {
66 throw!(StreamNotImplemented(stream.into()))
67 } else if stream == "!contractInfo" {
68 throw!(StreamNotImplemented(stream.into()))
69 } else if stream.len() == 64 {
70 let value: UserDataStreamEvent = from_str(data)?;
72 match value.event_type.as_ref() {
73 "ACCOUNT_UPDATE" => Self::UserAccountUpdate(
74 value.account.ok_or(EmptyUserDataStream(value.event_type))?,
75 ),
76 "ORDER_TRADE_UPDATE" => {
77 Self::UserOrderUpdate(value.order.ok_or(EmptyUserDataStream(value.event_type))?)
78 }
79 _ => throw!(UserDataStreamEventNotImplemented(value.event_type)),
80 }
81 } else {
82 throw!(UnknownStream(stream.into()))
83 }
84 }
85}
86
87#[derive(Debug, Serialize, Deserialize, Clone)]
88#[serde(rename_all = "camelCase")]
89pub struct UserDataStreamEvent {
90 #[serde(rename = "e")]
91 pub event_type: String,
92
93 #[serde(rename = "E")]
94 pub event_time: u64,
95
96 #[serde(rename = "T")]
97 pub transaction_time: Option<u64>,
98
99 #[serde(rename = "o")]
100 pub order: Option<OrderUpdate>,
101
102 #[serde(rename = "a")]
103 pub account: Option<AccountUpdate>,
104}
105
106#[derive(Debug, Serialize, Deserialize, Clone)]
107#[serde(rename_all = "camelCase")]
108pub struct OrderUpdate {
109 #[serde(rename = "s")]
110 pub symbol: String,
111
112 #[serde(rename = "c")]
113 pub new_client_order_id: String,
114
115 #[serde(rename = "S")]
116 pub side: OrderSide,
117
118 #[serde(rename = "o")]
119 pub order_type: OrderType,
120
121 #[serde(rename = "f")]
122 pub time_in_force: TimeInForce,
123
124 #[serde(rename = "q", with = "string_or_decimal")]
125 pub qty: Decimal,
126
127 #[serde(rename = "p", with = "string_or_decimal")]
128 pub price: Decimal,
129
130 #[serde(rename = "ap", with = "string_or_decimal")]
131 pub average_price: Decimal,
132
133 #[serde(rename = "sp", with = "string_or_decimal")]
134 pub stop_price: Decimal,
135
136 #[serde(rename = "x")]
137 pub execution_type: ExecutionType,
138
139 #[serde(rename = "X")]
140 pub order_status: OrderStatus,
141
142 #[serde(rename = "i")]
143 pub order_id: u64,
144
145 #[serde(rename = "l", with = "string_or_decimal")]
146 pub qty_last_filled_trade: Decimal,
147
148 #[serde(rename = "z", with = "string_or_decimal")]
149 pub accumulated_qty_filled_trades: Decimal,
150
151 #[serde(rename = "L", with = "string_or_decimal")]
152 pub price_last_filled_trade: Decimal,
153
154 #[serde(skip, rename = "N")]
155 pub asset_commisioned: Option<String>,
156
157 #[serde(rename = "n", with = "string_or_decimal_opt")]
158 pub commission: Option<Decimal>,
159
160 #[serde(rename = "T")]
161 pub trade_order_time: u64,
162
163 #[serde(rename = "t")]
164 pub trade_id: i64,
165
166 #[serde(rename = "b", with = "string_or_decimal")]
167 pub bids_notional: Decimal,
168
169 #[serde(rename = "a", with = "string_or_decimal")]
170 pub ask_notional: Decimal,
171
172 #[serde(rename = "m")]
173 pub is_buyer_maker: bool,
174
175 #[serde(rename = "R")]
176 pub is_reduce_only: bool,
177
178 #[serde(rename = "wt")]
179 pub stop_price_working_type: String,
180
181 #[serde(rename = "ot")]
182 pub original_order_type: String,
183
184 #[serde(rename = "ps")]
185 pub position_side: String,
186
187 #[serde(rename = "cp")]
188 pub close_all: Option<bool>,
189
190 #[serde(rename = "AP")]
191 pub activation_price: Option<String>,
192
193 #[serde(rename = "cr")]
194 pub callback_rate: Option<String>,
195
196 #[serde(rename = "pP")]
197 pub pp_ignore: bool,
198
199 #[serde(rename = "si")]
200 pub si_ignore: i32,
201
202 #[serde(rename = "ss")]
203 pub ss_ignore: i32,
204
205 #[serde(rename = "rp")]
206 pub realized_profit: String,
207}
208
209#[derive(Debug, Serialize, Deserialize, Clone)]
210#[serde(rename_all = "camelCase")]
211pub struct AccountUpdate {
212 #[serde(rename = "m")]
213 pub reason: String,
214
215 #[serde(rename = "B")]
216 pub balances: Vec<AccountUpdateBalance>,
217
218 #[serde(rename = "P")]
219 pub positions: Vec<AccountUpdatePosition>,
220}
221
222#[derive(Debug, Serialize, Deserialize, Clone)]
223#[serde(rename_all = "camelCase")]
224pub struct AccountUpdateBalance {
225 #[serde(rename = "a")]
226 pub asset: String,
227 #[serde(rename = "wb", with = "string_or_decimal")]
228 pub wallet_balance: Decimal,
229 #[serde(rename = "cw", with = "string_or_decimal")]
230 pub cross_wallet_balance: Decimal,
231 #[serde(rename = "bc", with = "string_or_decimal")]
232 pub balance_change: Decimal, }
234
235#[derive(Debug, Serialize, Deserialize, Clone)]
236#[serde(rename_all = "camelCase")]
237pub struct AccountUpdatePosition {
238 #[serde(rename = "s")]
239 pub symbol: String,
240 #[serde(rename = "pa", with = "string_or_decimal")]
241 pub position_amount: Decimal,
242 #[serde(rename = "ep", with = "string_or_decimal")]
243 pub entry_price: Decimal,
244 #[serde(rename = "cr", with = "string_or_decimal")]
245 pub accumulated_realized: Decimal, #[serde(rename = "up", with = "string_or_decimal")]
247 pub unrealized_pnl: Decimal,
248 #[serde(rename = "mt")]
249 pub margin_type: String,
250 #[serde(rename = "iw")]
251 pub isolated_wallet: String,
252 #[serde(rename = "ps")]
253 pub position_side: String,
254}
255
256#[derive(Debug, Serialize, Deserialize, Clone)]
257#[serde(rename_all = "camelCase")]
258pub struct BookTicker {
259 #[serde(rename = "u")]
260 pub update_id: u64,
261
262 #[serde(rename = "s")]
263 pub symbol: String,
264
265 #[serde(rename = "b", with = "string_or_decimal")]
266 pub best_bid: Decimal,
267
268 #[serde(rename = "B", with = "string_or_decimal")]
269 pub best_bid_qty: Decimal,
270
271 #[serde(rename = "a", with = "string_or_decimal")]
272 pub best_ask: Decimal,
273
274 #[serde(rename = "A", with = "string_or_decimal")]
275 pub best_ask_qty: Decimal,
276}