rusty_bybit/
market.rs

1//! Market data endpoints
2//!
3//! Provides access to public market data including tickers, orderbook, klines, and instrument info.
4//!
5//! # Example
6//!
7//! ````rust,no_run
8//! use rusty_bybit::BybitClient;
9//!
10//! #[tokio::main]
11//! async fn main() {
12//!     let client = BybitClient::testnet();
13//!     let tickers = client.get_tickers("linear").await.unwrap();
14//!     println!("First ticker: {}", tickers.list[0].symbol);
15//! }
16//! ```
17
18use crate::client::BybitClient;
19use crate::error::Result;
20use crate::types::{InstrumentList, OrderBook, ServerTime, TickerList};
21
22impl BybitClient {
23    pub async fn get_server_time(&self) -> Result<ServerTime> {
24        self.get("/v5/market/time", None).await
25    }
26
27    pub async fn get_kline(
28        &self,
29        category: &str,
30        symbol: &str,
31        interval: &str,
32        start: Option<i64>,
33        end: Option<i64>,
34    ) -> Result<serde_json::Value> {
35        let mut params: Vec<(String, String)> = vec![
36            ("category".to_string(), category.to_string()),
37            ("symbol".to_string(), symbol.to_string()),
38            ("interval".to_string(), interval.to_string()),
39        ];
40
41        if let Some(s) = start {
42            params.push(("start".to_string(), s.to_string()));
43        }
44
45        if let Some(e) = end {
46            params.push(("end".to_string(), e.to_string()));
47        }
48
49        let query: Vec<(&str, &str)> = params
50            .iter()
51            .map(|(k, v)| (k.as_str(), v.as_str()))
52            .collect();
53
54        self.get("/v5/market/kline", Some(query)).await
55    }
56
57    pub async fn get_tickers(&self, category: &str) -> Result<TickerList> {
58        let query = vec![("category", category)];
59        self.get("/v5/market/tickers", Some(query)).await
60    }
61
62    pub async fn get_orderbook(
63        &self,
64        category: &str,
65        symbol: &str,
66        limit: u32,
67    ) -> Result<OrderBook> {
68        let limit_str = limit.to_string();
69        let query = vec![
70            ("category", category),
71            ("symbol", symbol),
72            ("limit", limit_str.as_str()),
73        ];
74        self.get("/v5/market/orderbook", Some(query)).await
75    }
76
77    pub async fn get_instruments(&self, category: &str) -> Result<InstrumentList> {
78        let query = vec![("category", category)];
79        self.get("/v5/market/instruments-info", Some(query)).await
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    #[test]
86    fn test_get_kline_basic_params() {
87        let params: Vec<(String, String)> = vec![
88            ("category".to_string(), "linear".to_string()),
89            ("symbol".to_string(), "BTCUSDT".to_string()),
90            ("interval".to_string(), "15".to_string()),
91        ];
92
93        let query: Vec<(&str, &str)> = params
94            .iter()
95            .map(|(k, v)| (k.as_str(), v.as_str()))
96            .collect();
97
98        assert_eq!(query.len(), 3);
99        assert!(query.contains(&("category", "linear")));
100        assert!(query.contains(&("symbol", "BTCUSDT")));
101        assert!(query.contains(&("interval", "15")));
102    }
103
104    #[test]
105    fn test_get_kline_with_start_only() {
106        let mut params: Vec<(String, String)> = vec![
107            ("category".to_string(), "linear".to_string()),
108            ("symbol".to_string(), "BTCUSDT".to_string()),
109            ("interval".to_string(), "15".to_string()),
110        ];
111
112        let start = 1670601600000_i64;
113        params.push(("start".to_string(), start.to_string()));
114
115        let query: Vec<(&str, &str)> = params
116            .iter()
117            .map(|(k, v)| (k.as_str(), v.as_str()))
118            .collect();
119
120        assert_eq!(query.len(), 4);
121        assert!(query.contains(&("category", "linear")));
122        assert!(query.contains(&("symbol", "BTCUSDT")));
123        assert!(query.contains(&("interval", "15")));
124        assert!(query.contains(&("start", "1670601600000")));
125    }
126
127    #[test]
128    fn test_get_kline_with_end_only() {
129        let mut params: Vec<(String, String)> = vec![
130            ("category".to_string(), "linear".to_string()),
131            ("symbol".to_string(), "BTCUSDT".to_string()),
132            ("interval".to_string(), "15".to_string()),
133        ];
134
135        let end = 1670608800000_i64;
136        params.push(("end".to_string(), end.to_string()));
137
138        let query: Vec<(&str, &str)> = params
139            .iter()
140            .map(|(k, v)| (k.as_str(), v.as_str()))
141            .collect();
142
143        assert_eq!(query.len(), 4);
144        assert!(query.contains(&("category", "linear")));
145        assert!(query.contains(&("symbol", "BTCUSDT")));
146        assert!(query.contains(&("interval", "15")));
147        assert!(query.contains(&("end", "1670608800000")));
148    }
149
150    #[test]
151    fn test_get_kline_with_both_start_and_end() {
152        let mut params: Vec<(String, String)> = vec![
153            ("category".to_string(), "linear".to_string()),
154            ("symbol".to_string(), "BTCUSDT".to_string()),
155            ("interval".to_string(), "15".to_string()),
156        ];
157
158        let start = 1670601600000_i64;
159        let end = 1670608800000_i64;
160
161        params.push(("start".to_string(), start.to_string()));
162        params.push(("end".to_string(), end.to_string()));
163
164        let query: Vec<(&str, &str)> = params
165            .iter()
166            .map(|(k, v)| (k.as_str(), v.as_str()))
167            .collect();
168
169        assert_eq!(query.len(), 5);
170        assert!(query.contains(&("category", "linear")));
171        assert!(query.contains(&("symbol", "BTCUSDT")));
172        assert!(query.contains(&("interval", "15")));
173        assert!(query.contains(&("start", "1670601600000")));
174        assert!(query.contains(&("end", "1670608800000")));
175    }
176}