ccxt_exchanges/bitget/
mod.rs

1//! Bitget exchange implementation.
2//!
3//! Supports spot trading and futures trading (USDT-M and Coin-M) with REST API and WebSocket support.
4
5use ccxt_core::{BaseExchange, ExchangeConfig, Result};
6use std::collections::HashMap;
7
8pub mod auth;
9pub mod builder;
10pub mod error;
11mod exchange_impl;
12pub mod parser;
13pub mod rest;
14pub mod ws;
15mod ws_exchange_impl;
16
17pub use auth::BitgetAuth;
18pub use builder::BitgetBuilder;
19pub use error::{BitgetErrorCode, is_error_response, parse_error};
20pub use parser::{
21    datetime_to_timestamp, parse_balance, parse_market, parse_ohlcv, parse_order,
22    parse_order_status, parse_orderbook, parse_ticker, parse_trade, timestamp_to_datetime,
23};
24
25/// Bitget exchange structure.
26#[derive(Debug)]
27pub struct Bitget {
28    /// Base exchange instance.
29    base: BaseExchange,
30    /// Bitget-specific options.
31    options: BitgetOptions,
32}
33
34/// Bitget-specific options.
35#[derive(Debug, Clone)]
36pub struct BitgetOptions {
37    /// Product type: spot, umcbl (USDT-M), dmcbl (Coin-M).
38    pub product_type: String,
39    /// Receive window in milliseconds.
40    pub recv_window: u64,
41    /// Enables demo environment.
42    pub demo: bool,
43}
44
45impl Default for BitgetOptions {
46    fn default() -> Self {
47        Self {
48            product_type: "spot".to_string(),
49            recv_window: 5000,
50            demo: false,
51        }
52    }
53}
54
55impl Bitget {
56    /// Creates a new Bitget instance using the builder pattern.
57    ///
58    /// This is the recommended way to create a Bitget instance.
59    ///
60    /// # Example
61    ///
62    /// ```no_run
63    /// use ccxt_exchanges::bitget::Bitget;
64    ///
65    /// let bitget = Bitget::builder()
66    ///     .api_key("your-api-key")
67    ///     .secret("your-secret")
68    ///     .passphrase("your-passphrase")
69    ///     .sandbox(true)
70    ///     .build()
71    ///     .unwrap();
72    /// ```
73    pub fn builder() -> BitgetBuilder {
74        BitgetBuilder::new()
75    }
76
77    /// Creates a new Bitget instance.
78    ///
79    /// # Arguments
80    ///
81    /// * `config` - Exchange configuration.
82    pub fn new(config: ExchangeConfig) -> Result<Self> {
83        let base = BaseExchange::new(config)?;
84        let options = BitgetOptions::default();
85
86        Ok(Self { base, options })
87    }
88
89    /// Creates a new Bitget instance with custom options.
90    ///
91    /// This is used internally by the builder pattern.
92    ///
93    /// # Arguments
94    ///
95    /// * `config` - Exchange configuration.
96    /// * `options` - Bitget-specific options.
97    pub fn new_with_options(config: ExchangeConfig, options: BitgetOptions) -> Result<Self> {
98        let base = BaseExchange::new(config)?;
99        Ok(Self { base, options })
100    }
101
102    /// Returns a reference to the base exchange.
103    pub fn base(&self) -> &BaseExchange {
104        &self.base
105    }
106
107    /// Returns a mutable reference to the base exchange.
108    pub fn base_mut(&mut self) -> &mut BaseExchange {
109        &mut self.base
110    }
111
112    /// Returns the Bitget options.
113    pub fn options(&self) -> &BitgetOptions {
114        &self.options
115    }
116
117    /// Sets the Bitget options.
118    pub fn set_options(&mut self, options: BitgetOptions) {
119        self.options = options;
120    }
121
122    /// Returns the exchange ID.
123    pub fn id(&self) -> &str {
124        "bitget"
125    }
126
127    /// Returns the exchange name.
128    pub fn name(&self) -> &str {
129        "Bitget"
130    }
131
132    /// Returns the API version.
133    pub fn version(&self) -> &str {
134        "v2"
135    }
136
137    /// Returns `true` if the exchange is CCXT-certified.
138    pub fn certified(&self) -> bool {
139        false
140    }
141
142    /// Returns `true` if Pro version (WebSocket) is supported.
143    pub fn pro(&self) -> bool {
144        true
145    }
146
147    /// Returns the rate limit in requests per second.
148    pub fn rate_limit(&self) -> f64 {
149        20.0
150    }
151
152    /// Returns the supported timeframes.
153    pub fn timeframes(&self) -> HashMap<String, String> {
154        let mut timeframes = HashMap::new();
155        timeframes.insert("1m".to_string(), "1m".to_string());
156        timeframes.insert("5m".to_string(), "5m".to_string());
157        timeframes.insert("15m".to_string(), "15m".to_string());
158        timeframes.insert("30m".to_string(), "30m".to_string());
159        timeframes.insert("1h".to_string(), "1H".to_string());
160        timeframes.insert("4h".to_string(), "4H".to_string());
161        timeframes.insert("6h".to_string(), "6H".to_string());
162        timeframes.insert("12h".to_string(), "12H".to_string());
163        timeframes.insert("1d".to_string(), "1D".to_string());
164        timeframes.insert("3d".to_string(), "3D".to_string());
165        timeframes.insert("1w".to_string(), "1W".to_string());
166        timeframes.insert("1M".to_string(), "1M".to_string());
167        timeframes
168    }
169
170    /// Returns the API URLs.
171    pub fn urls(&self) -> BitgetUrls {
172        if self.base().config.sandbox || self.options.demo {
173            BitgetUrls::demo()
174        } else {
175            BitgetUrls::production()
176        }
177    }
178
179    /// Creates a public WebSocket client.
180    ///
181    /// # Returns
182    ///
183    /// Returns a `BitgetWs` instance for public data streams.
184    ///
185    /// # Example
186    /// ```no_run
187    /// use ccxt_exchanges::bitget::Bitget;
188    /// use ccxt_core::ExchangeConfig;
189    ///
190    /// # async fn example() -> ccxt_core::error::Result<()> {
191    /// let bitget = Bitget::new(ExchangeConfig::default())?;
192    /// let ws = bitget.create_ws();
193    /// ws.connect().await?;
194    /// # Ok(())
195    /// # }
196    /// ```
197    pub fn create_ws(&self) -> ws::BitgetWs {
198        let urls = self.urls();
199        ws::BitgetWs::new(urls.ws_public)
200    }
201}
202
203/// Bitget API URLs.
204#[derive(Debug, Clone)]
205pub struct BitgetUrls {
206    /// REST API base URL.
207    pub rest: String,
208    /// Public WebSocket URL.
209    pub ws_public: String,
210    /// Private WebSocket URL.
211    pub ws_private: String,
212}
213
214impl BitgetUrls {
215    /// Returns production environment URLs.
216    pub fn production() -> Self {
217        Self {
218            rest: "https://api.bitget.com".to_string(),
219            ws_public: "wss://ws.bitget.com/v2/ws/public".to_string(),
220            ws_private: "wss://ws.bitget.com/v2/ws/private".to_string(),
221        }
222    }
223
224    /// Returns demo environment URLs.
225    pub fn demo() -> Self {
226        Self {
227            rest: "https://api.bitget.com".to_string(),
228            ws_public: "wss://ws.bitget.com/v2/ws/public/demo".to_string(),
229            ws_private: "wss://ws.bitget.com/v2/ws/private/demo".to_string(),
230        }
231    }
232}
233
234#[cfg(test)]
235mod tests {
236    use super::*;
237
238    #[test]
239    fn test_bitget_creation() {
240        let config = ExchangeConfig {
241            id: "bitget".to_string(),
242            name: "Bitget".to_string(),
243            ..Default::default()
244        };
245
246        let bitget = Bitget::new(config);
247        assert!(bitget.is_ok());
248
249        let bitget = bitget.unwrap();
250        assert_eq!(bitget.id(), "bitget");
251        assert_eq!(bitget.name(), "Bitget");
252        assert_eq!(bitget.version(), "v2");
253        assert!(!bitget.certified());
254        assert!(bitget.pro());
255    }
256
257    #[test]
258    fn test_timeframes() {
259        let config = ExchangeConfig::default();
260        let bitget = Bitget::new(config).unwrap();
261        let timeframes = bitget.timeframes();
262
263        assert!(timeframes.contains_key("1m"));
264        assert!(timeframes.contains_key("1h"));
265        assert!(timeframes.contains_key("1d"));
266        assert_eq!(timeframes.len(), 12);
267    }
268
269    #[test]
270    fn test_urls() {
271        let config = ExchangeConfig::default();
272        let bitget = Bitget::new(config).unwrap();
273        let urls = bitget.urls();
274
275        assert!(urls.rest.contains("api.bitget.com"));
276        assert!(urls.ws_public.contains("ws.bitget.com"));
277    }
278
279    #[test]
280    fn test_sandbox_urls() {
281        let config = ExchangeConfig {
282            sandbox: true,
283            ..Default::default()
284        };
285        let bitget = Bitget::new(config).unwrap();
286        let urls = bitget.urls();
287
288        assert!(urls.ws_public.contains("demo"));
289    }
290
291    #[test]
292    fn test_default_options() {
293        let options = BitgetOptions::default();
294        assert_eq!(options.product_type, "spot");
295        assert_eq!(options.recv_window, 5000);
296        assert!(!options.demo);
297    }
298}