1use crate::{
2 error::BybitError::{self, *},
3 models::{OrderStatus, OrderType, Side, TimeInForce},
4 websocket::ParseMessage,
5};
6use fehler::{throw, throws};
7use rust_decimal::Decimal;
8use serde::{Deserialize, Serialize};
9use serde_json::from_str;
10use serde_with::{serde_as, DisplayFromStr, NoneAsEmptyString};
11
12#[derive(Debug, Clone, Serialize)]
13#[non_exhaustive]
14pub enum WebsocketMessage {
15 Ping,
16 SubscribeSuccess(SubscribeSuccess),
17
18 UserOrderUpdate(Vec<UserOrderUpdate>),
20 PublicTrade(Vec<PublicTradeUpdate>),
25 OrderBook(OrderBook),
26 }
30
31impl ParseMessage for WebsocketMessage {
32 #[throws(BybitError)]
33 fn parse(topic: &str, data: &str) -> Self {
34 if topic.starts_with("orderbook") {
35 Self::OrderBook(from_str(data)?)
36 } else if topic.starts_with("publicTrade") {
37 Self::PublicTrade(from_str(data)?)
38 } else if topic.starts_with("order") {
39 Self::UserOrderUpdate(from_str(data)?)
40 } else {
41 throw!(UnknownStream(topic.into()))
42 }
43 }
44
45 fn parse_succ(succ: &str) -> Result<Self, BybitError> {
46 Ok(Self::SubscribeSuccess(from_str(succ)?))
47 }
48
49 fn ping() -> Self {
50 Self::Ping
51 }
52}
53
54#[serde_as]
55#[derive(Debug, Serialize, Deserialize, Clone)]
56#[serde(rename_all = "camelCase")]
57pub struct UserOrderUpdate {
59 pub category: String,
60 pub symbol: String,
61 pub order_id: String,
62 pub order_link_id: String,
63 #[serde_as(as = "NoneAsEmptyString")]
64 pub block_trade_id: Option<u64>,
65 pub side: Side,
66 pub position_idx: u8,
67 pub order_status: OrderStatus,
68 pub cancel_type: String,
69 pub reject_reason: String,
70 pub time_in_force: TimeInForce,
71 pub is_leverage: String,
72 pub price: Decimal,
73 pub qty: Decimal,
74 #[serde_as(as = "NoneAsEmptyString")]
75 pub avg_price: Option<Decimal>,
76 pub leaves_qty: Decimal,
77 pub leaves_value: Decimal,
78 pub cum_exec_qty: Decimal,
79 pub cum_exec_value: Decimal,
80 pub cum_exec_fee: Decimal,
81 pub order_type: OrderType,
82 pub stop_order_type: String,
83 pub order_iv: String,
84 #[serde_as(as = "NoneAsEmptyString")]
85 pub trigger_price: Option<Decimal>,
86 #[serde_as(as = "NoneAsEmptyString")]
87 pub take_profit: Option<Decimal>,
88 #[serde_as(as = "NoneAsEmptyString")]
89 pub stop_loss: Option<Decimal>,
90 pub trigger_by: String,
91 pub tp_trigger_by: String,
92 pub sl_trigger_by: String,
93 pub trigger_direction: u8,
94 pub place_type: String,
95 pub last_price_on_created: Decimal,
96 pub close_on_trigger: bool,
97 pub reduce_only: bool,
98 pub smp_group: u32,
99 pub smp_type: String,
100 pub smp_order_id: String,
101 pub sl_limit_price: Decimal,
102 pub tp_limit_price: Decimal,
103 pub market_unit: String,
104 #[serde_as(as = "DisplayFromStr")]
105 pub created_time: u64,
106 #[serde_as(as = "DisplayFromStr")]
107 pub updated_time: u64,
108 pub fee_currency: String,
109}
110
111#[derive(Debug, Serialize, Deserialize, Clone)]
112pub struct OrderBook {
114 #[serde(rename = "s")]
115 pub symbol: String,
116
117 #[serde(rename = "b")]
118 pub bids: Vec<Vec<Decimal>>,
119
120 #[serde(rename = "a")]
121 pub asks: Vec<Vec<Decimal>>,
122
123 #[serde(rename = "u")]
124 pub update_id: u64,
125
126 pub seq: u64,
127}
128
129#[derive(Debug, Serialize, Deserialize, Clone)]
130pub struct PublicTradeUpdate {
132 #[serde(rename = "T")]
133 pub exchange_time: u64,
134 #[serde(rename = "s")]
135 pub symbol: String,
136 #[serde(rename = "S")]
137 pub side: Side,
138 #[serde(rename = "v")]
139 pub qty: Decimal,
140 #[serde(rename = "p")]
141 pub price: Decimal,
142 #[serde(rename = "L")]
143 pub tick_direction: String,
144 #[serde(rename = "i")]
145 pub trade_id: String,
146 #[serde(rename = "BT")]
147 pub is_block_trade: bool,
148}
149
150#[derive(Debug, Serialize, Deserialize, Clone)]
151pub struct SubscribeSuccess {
153 pub success: bool,
154 pub ret_msg: String,
155 pub op: String,
156 pub conn_id: String,
157 pub req_id: Option<String>,
158}