ccxt_exchanges/bybit/
mod.rs1use ccxt_core::{BaseExchange, ExchangeConfig, Result};
7use std::collections::HashMap;
8
9pub mod auth;
10pub mod builder;
11pub mod error;
12pub mod parser;
13pub mod rest;
14pub mod symbol;
15pub mod ws;
16mod ws_exchange_impl;
17
18pub use auth::BybitAuth;
19pub use builder::BybitBuilder;
20pub use error::{BybitErrorCode, is_error_response, parse_error};
21
22#[derive(Debug)]
24pub struct Bybit {
25 base: BaseExchange,
27 options: BybitOptions,
29}
30
31#[derive(Debug, Clone)]
33pub struct BybitOptions {
34 pub account_type: String,
36 pub testnet: bool,
38 pub recv_window: u64,
40}
41
42impl Default for BybitOptions {
43 fn default() -> Self {
44 Self {
45 account_type: "UNIFIED".to_string(),
46 testnet: false,
47 recv_window: 5000,
48 }
49 }
50}
51
52impl Bybit {
53 pub fn builder() -> BybitBuilder {
70 BybitBuilder::new()
71 }
72
73 pub fn new(config: ExchangeConfig) -> Result<Self> {
79 let base = BaseExchange::new(config)?;
80 let options = BybitOptions::default();
81
82 Ok(Self { base, options })
83 }
84
85 pub fn new_with_options(config: ExchangeConfig, options: BybitOptions) -> Result<Self> {
94 let base = BaseExchange::new(config)?;
95 Ok(Self { base, options })
96 }
97
98 pub fn base(&self) -> &BaseExchange {
100 &self.base
101 }
102
103 pub fn base_mut(&mut self) -> &mut BaseExchange {
105 &mut self.base
106 }
107
108 pub fn options(&self) -> &BybitOptions {
110 &self.options
111 }
112
113 pub fn set_options(&mut self, options: BybitOptions) {
115 self.options = options;
116 }
117
118 pub fn id(&self) -> &str {
120 "bybit"
121 }
122
123 pub fn name(&self) -> &str {
125 "Bybit"
126 }
127
128 pub fn version(&self) -> &str {
130 "v5"
131 }
132
133 pub fn certified(&self) -> bool {
135 false
136 }
137
138 pub fn pro(&self) -> bool {
140 true
141 }
142
143 pub fn rate_limit(&self) -> f64 {
145 20.0
146 }
147
148 pub fn timeframes(&self) -> HashMap<String, String> {
150 let mut timeframes = HashMap::new();
151 timeframes.insert("1m".to_string(), "1".to_string());
152 timeframes.insert("3m".to_string(), "3".to_string());
153 timeframes.insert("5m".to_string(), "5".to_string());
154 timeframes.insert("15m".to_string(), "15".to_string());
155 timeframes.insert("30m".to_string(), "30".to_string());
156 timeframes.insert("1h".to_string(), "60".to_string());
157 timeframes.insert("2h".to_string(), "120".to_string());
158 timeframes.insert("4h".to_string(), "240".to_string());
159 timeframes.insert("6h".to_string(), "360".to_string());
160 timeframes.insert("12h".to_string(), "720".to_string());
161 timeframes.insert("1d".to_string(), "D".to_string());
162 timeframes.insert("1w".to_string(), "W".to_string());
163 timeframes.insert("1M".to_string(), "M".to_string());
164 timeframes
165 }
166
167 pub fn urls(&self) -> BybitUrls {
169 if self.base().config.sandbox || self.options.testnet {
170 BybitUrls::testnet()
171 } else {
172 BybitUrls::production()
173 }
174 }
175
176 pub fn create_ws(&self) -> ws::BybitWs {
195 let urls = self.urls();
196 ws::BybitWs::new(urls.ws_public)
197 }
198}
199
200#[derive(Debug, Clone)]
202pub struct BybitUrls {
203 pub rest: String,
205 pub ws_public: String,
207 pub ws_private: String,
209}
210
211impl BybitUrls {
212 pub fn production() -> Self {
214 Self {
215 rest: "https://api.bybit.com".to_string(),
216 ws_public: "wss://stream.bybit.com/v5/public/spot".to_string(),
217 ws_private: "wss://stream.bybit.com/v5/private".to_string(),
218 }
219 }
220
221 pub fn testnet() -> Self {
223 Self {
224 rest: "https://api-testnet.bybit.com".to_string(),
225 ws_public: "wss://stream-testnet.bybit.com/v5/public/spot".to_string(),
226 ws_private: "wss://stream-testnet.bybit.com/v5/private".to_string(),
227 }
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn test_bybit_creation() {
237 let config = ExchangeConfig {
238 id: "bybit".to_string(),
239 name: "Bybit".to_string(),
240 ..Default::default()
241 };
242
243 let bybit = Bybit::new(config);
244 assert!(bybit.is_ok());
245
246 let bybit = bybit.unwrap();
247 assert_eq!(bybit.id(), "bybit");
248 assert_eq!(bybit.name(), "Bybit");
249 assert_eq!(bybit.version(), "v5");
250 assert!(!bybit.certified());
251 assert!(bybit.pro());
252 }
253
254 #[test]
255 fn test_timeframes() {
256 let config = ExchangeConfig::default();
257 let bybit = Bybit::new(config).unwrap();
258 let timeframes = bybit.timeframes();
259
260 assert!(timeframes.contains_key("1m"));
261 assert!(timeframes.contains_key("1h"));
262 assert!(timeframes.contains_key("1d"));
263 assert_eq!(timeframes.len(), 13);
264 }
265
266 #[test]
267 fn test_urls() {
268 let config = ExchangeConfig::default();
269 let bybit = Bybit::new(config).unwrap();
270 let urls = bybit.urls();
271
272 assert!(urls.rest.contains("api.bybit.com"));
273 assert!(urls.ws_public.contains("stream.bybit.com"));
274 }
275
276 #[test]
277 fn test_testnet_urls() {
278 let config = ExchangeConfig {
279 sandbox: true,
280 ..Default::default()
281 };
282 let bybit = Bybit::new(config).unwrap();
283 let urls = bybit.urls();
284
285 assert!(urls.rest.contains("api-testnet.bybit.com"));
286 assert!(urls.ws_public.contains("stream-testnet.bybit.com"));
287 }
288
289 #[test]
290 fn test_default_options() {
291 let options = BybitOptions::default();
292 assert_eq!(options.account_type, "UNIFIED");
293 assert!(!options.testnet);
294 assert_eq!(options.recv_window, 5000);
295 }
296}