bybit_async/websocket/
topics.rs

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    // User Data Stream
19    UserOrderUpdate(Vec<UserOrderUpdate>),
20    // todo!(UserPositionUpdate(UserPositionUpdate),)
21    // todo!(UserExecutionUpdate(UserExecutionUpdate),)
22
23    // Market Stream
24    PublicTrade(Vec<PublicTradeUpdate>),
25    OrderBook(OrderBook),
26    // todo!(tickers),
27    // todo!(kline),
28    // todo!(liquidation),
29}
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")]
57// https://bybit-exchange.github.io/docs/zh-TW/v5/websocket/private/order
58pub 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)]
112// https://bybit-exchange.github.io/docs/zh-TW/v5/websocket/public/orderbook
113pub 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)]
130// https://bybit-exchange.github.io/docs/zh-TW/v5/websocket/public/trade
131pub 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)]
151// https://bybit-exchange.github.io/docs/zh-TW/v5/ws/connect
152pub 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}