binance_async/websocket/
usdm.rs

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    // User Data Stream
17    UserOrderUpdate(OrderUpdate),
18    UserAccountUpdate(AccountUpdate),
19    // Market Stream
20    AggregateTrade(AggregateTrade),
21    BookTicker(BookTicker),
22    // Trade(TradeMessage),
23    // Candlestick(CandelStickMessage),
24    // MiniTicker(MiniTicker),
25    // MiniTickerAll(Vec<MiniTicker>),
26    // Ticker(Ticker),
27    // TickerAll(Vec<Ticker>),
28    // OrderBook(OrderBook),
29    // Depth(Depth),
30}
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            // is a user stream
71            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, // Balance Change except PnL and Commission
233}
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, // (Pre-fee) Accumulated Realized
246    #[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}