crypto_ws_client/clients/bybit/
bybit_inverse.rs

1use async_trait::async_trait;
2
3use crate::{
4    clients::common_traits::{
5        Candlestick, Level3OrderBook, OrderBook, OrderBookTopK, Ticker, Trade, BBO,
6    },
7    common::{command_translator::CommandTranslator, ws_client_internal::WSClientInternal},
8    WSClient,
9};
10
11use super::utils::{BybitMessageHandler, EXCHANGE_NAME};
12
13const WEBSOCKET_URL: &str = "wss://stream.bybit.com/realtime";
14
15/// Bybit Inverses markets.
16///
17/// InverseFuture:
18///   * WebSocket API doc: <https://bybit-exchange.github.io/docs/inverse_futures/>
19///   * Trading at: <https://www.bybit.com/trade/inverse/futures/BTCUSD_BIQ>
20///
21/// InverseSwap:
22///   * WebSocket API doc: <https://bybit-exchange.github.io/docs/inverse/#t-websocket>
23///   * Trading at: <https://www.bybit.com/trade/inverse/>
24pub struct BybitInverseWSClient {
25    client: WSClientInternal<BybitMessageHandler>,
26    translator: BybitInverseCommandTranslator,
27}
28
29impl_new_constructor!(
30    BybitInverseWSClient,
31    EXCHANGE_NAME,
32    WEBSOCKET_URL,
33    BybitMessageHandler {},
34    BybitInverseCommandTranslator {}
35);
36
37impl_trait!(Trade, BybitInverseWSClient, subscribe_trade, "trade");
38#[rustfmt::skip]
39// Prefer orderBookL2_25 over orderBook_200.100ms because /public/orderBook/L2
40// returns a top 25 snapshot, which is the same depth as orderBookL2_25.
41impl_trait!(OrderBook, BybitInverseWSClient, subscribe_orderbook, "orderBookL2_25");
42#[rustfmt::skip]
43impl_trait!(Ticker, BybitInverseWSClient, subscribe_ticker, "instrument_info.100ms");
44impl_candlestick!(BybitInverseWSClient);
45panic_bbo!(BybitInverseWSClient);
46panic_l3_orderbook!(BybitInverseWSClient);
47panic_l2_topk!(BybitInverseWSClient);
48
49impl_ws_client_trait!(BybitInverseWSClient);
50
51struct BybitInverseCommandTranslator {}
52
53impl BybitInverseCommandTranslator {
54    // https://bybit-exchange.github.io/docs/inverse_futures/#t-websocketklinev2
55    // https://bybit-exchange.github.io/docs/inverse/#t-websocketklinev2
56    fn to_candlestick_raw_channel(interval: usize) -> String {
57        let interval_str = match interval {
58            60 => "1",
59            180 => "3",
60            300 => "5",
61            900 => "15",
62            1800 => "30",
63            3600 => "60",
64            7200 => "120",
65            14400 => "240",
66            21600 => "360",
67            86400 => "D",
68            604800 => "W",
69            2592000 => "M",
70            _ => panic!(
71                "Bybit InverseFuture has intervals 1min,5min,15min,30min,60min,4hour,1day,1week,1mon"
72            ),
73        };
74        format!("klineV2.{interval_str}")
75    }
76}
77
78impl CommandTranslator for BybitInverseCommandTranslator {
79    fn translate_to_commands(&self, subscribe: bool, topics: &[(String, String)]) -> Vec<String> {
80        vec![super::utils::topics_to_command(topics, subscribe)]
81    }
82
83    fn translate_to_candlestick_commands(
84        &self,
85        subscribe: bool,
86        symbol_interval_list: &[(String, usize)],
87    ) -> Vec<String> {
88        let topics = symbol_interval_list
89            .iter()
90            .map(|(symbol, interval)| {
91                let channel = Self::to_candlestick_raw_channel(*interval);
92                (channel, symbol.to_string())
93            })
94            .collect::<Vec<(String, String)>>();
95        self.translate_to_commands(subscribe, &topics)
96    }
97}