ccxt_exchanges/bitget/rest/
market_data.rs1use super::super::{Bitget, parser};
4use ccxt_core::{
5 Error, ParseError, Result,
6 types::{Market, OHLCV, OhlcvRequest, OrderBook, Ticker, Trade},
7};
8use std::{collections::HashMap, sync::Arc};
9use tracing::{info, warn};
10
11impl Bitget {
12 pub async fn fetch_markets(&self) -> Result<Arc<HashMap<String, Arc<Market>>>> {
14 let path = self.build_api_path("/public/symbols");
15 let response = self.public_request("GET", &path, None).await?;
16
17 let data = response
18 .get("data")
19 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
20
21 let symbols = data.as_array().ok_or_else(|| {
22 Error::from(ParseError::invalid_format(
23 "data",
24 "Expected array of symbols",
25 ))
26 })?;
27
28 let mut markets = Vec::new();
29 for symbol in symbols {
30 match parser::parse_market(symbol) {
31 Ok(market) => markets.push(market),
32 Err(e) => {
33 warn!(error = %e, "Failed to parse market");
34 }
35 }
36 }
37
38 let result = self.base().set_markets(markets, None).await?;
39 info!("Loaded {} markets for Bitget", result.len());
40 Ok(result)
41 }
42
43 pub async fn load_markets(&self, reload: bool) -> Result<Arc<HashMap<String, Arc<Market>>>> {
45 let _loading_guard = self.base().market_loading_lock.lock().await;
46
47 {
48 let cache = self.base().market_cache.read().await;
49 if cache.is_loaded() && !reload {
50 return Ok(cache.markets());
51 }
52 }
53
54 info!("Loading markets for Bitget (reload: {})", reload);
55 let _markets = self.fetch_markets().await?;
56
57 let cache = self.base().market_cache.read().await;
58 Ok(cache.markets())
59 }
60
61 pub async fn fetch_ticker(&self, symbol: &str) -> Result<Ticker> {
63 let market = self.base().market(symbol).await?;
64
65 let path = self.build_api_path("/market/tickers");
66 let mut params = HashMap::new();
67 params.insert("symbol".to_string(), market.id.clone());
68
69 let response = self.public_request("GET", &path, Some(¶ms)).await?;
70
71 let data = response
72 .get("data")
73 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
74
75 let tickers = data.as_array().ok_or_else(|| {
76 Error::from(ParseError::invalid_format(
77 "data",
78 "Expected array of tickers",
79 ))
80 })?;
81
82 if tickers.is_empty() {
83 return Err(Error::bad_symbol(format!("No ticker data for {}", symbol)));
84 }
85
86 parser::parse_ticker(&tickers[0], Some(&market))
87 }
88
89 pub async fn fetch_tickers(&self, symbols: Option<Vec<String>>) -> Result<Vec<Ticker>> {
91 let cache = self.base().market_cache.read().await;
92 if !cache.is_loaded() {
93 drop(cache);
94 return Err(Error::exchange(
95 "-1",
96 "Markets not loaded. Call load_markets() first.",
97 ));
98 }
99 let markets_snapshot: std::collections::HashMap<String, Arc<Market>> = cache
101 .iter_markets()
102 .map(|(_, m)| (m.id.clone(), m))
103 .collect();
104 drop(cache);
105
106 let path = self.build_api_path("/market/tickers");
107 let response = self.public_request("GET", &path, None).await?;
108
109 let data = response
110 .get("data")
111 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
112
113 let tickers_array = data.as_array().ok_or_else(|| {
114 Error::from(ParseError::invalid_format(
115 "data",
116 "Expected array of tickers",
117 ))
118 })?;
119
120 let mut tickers = Vec::new();
121 for ticker_data in tickers_array {
122 if let Some(bitget_symbol) = ticker_data["symbol"].as_str() {
123 if let Some(market) = markets_snapshot.get(bitget_symbol) {
124 match parser::parse_ticker(ticker_data, Some(market)) {
125 Ok(ticker) => {
126 if let Some(ref syms) = symbols {
127 if syms.contains(&ticker.symbol) {
128 tickers.push(ticker);
129 }
130 } else {
131 tickers.push(ticker);
132 }
133 }
134 Err(e) => {
135 warn!(
136 error = %e,
137 symbol = %bitget_symbol,
138 "Failed to parse ticker"
139 );
140 }
141 }
142 }
143 }
144 }
145
146 Ok(tickers)
147 }
148
149 pub async fn fetch_order_book(&self, symbol: &str, limit: Option<u32>) -> Result<OrderBook> {
151 let market = self.base().market(symbol).await?;
152
153 let path = self.build_api_path("/market/orderbook");
154 let mut params = HashMap::new();
155 params.insert("symbol".to_string(), market.id.clone());
156
157 let actual_limit = limit.map_or(100, |l| l.min(100));
158 params.insert("limit".to_string(), actual_limit.to_string());
159
160 let response = self.public_request("GET", &path, Some(¶ms)).await?;
161
162 let data = response
163 .get("data")
164 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
165
166 parser::parse_orderbook(data, market.symbol.clone())
167 }
168
169 pub async fn fetch_trades(&self, symbol: &str, limit: Option<u32>) -> Result<Vec<Trade>> {
171 let market = self.base().market(symbol).await?;
172
173 let path = self.build_api_path("/market/fills");
174 let mut params = HashMap::new();
175 params.insert("symbol".to_string(), market.id.clone());
176
177 let actual_limit = limit.map_or(100, |l| l.min(500));
178 params.insert("limit".to_string(), actual_limit.to_string());
179
180 let response = self.public_request("GET", &path, Some(¶ms)).await?;
181
182 let data = response
183 .get("data")
184 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
185
186 let trades_array = data.as_array().ok_or_else(|| {
187 Error::from(ParseError::invalid_format(
188 "data",
189 "Expected array of trades",
190 ))
191 })?;
192
193 let mut trades = Vec::new();
194 for trade_data in trades_array {
195 match parser::parse_trade(trade_data, Some(&market)) {
196 Ok(trade) => trades.push(trade),
197 Err(e) => {
198 warn!(error = %e, "Failed to parse trade");
199 }
200 }
201 }
202
203 trades.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
204 Ok(trades)
205 }
206
207 pub async fn fetch_ohlcv_v2(&self, request: OhlcvRequest) -> Result<Vec<OHLCV>> {
209 let market = self.base().market(&request.symbol).await?;
210
211 let timeframes = self.timeframes();
212 let bitget_timeframe = timeframes.get(&request.timeframe).ok_or_else(|| {
213 Error::invalid_request(format!("Unsupported timeframe: {}", request.timeframe))
214 })?;
215
216 let path = self.build_api_path("/market/candles");
217 let mut params = HashMap::new();
218 params.insert("symbol".to_string(), market.id.clone());
219 params.insert("granularity".to_string(), bitget_timeframe.clone());
220
221 let actual_limit = request.limit.map_or(100, |l| l.min(1000));
222 params.insert("limit".to_string(), actual_limit.to_string());
223
224 if let Some(start_time) = request.since {
225 params.insert("startTime".to_string(), start_time.to_string());
226 }
227
228 if let Some(end_time) = request.until {
229 params.insert("endTime".to_string(), end_time.to_string());
230 }
231
232 let response = self.public_request("GET", &path, Some(¶ms)).await?;
233
234 let data = response
235 .get("data")
236 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
237
238 let candles_array = data.as_array().ok_or_else(|| {
239 Error::from(ParseError::invalid_format(
240 "data",
241 "Expected array of candles",
242 ))
243 })?;
244
245 let mut ohlcv = Vec::new();
246 for candle_data in candles_array {
247 match parser::parse_ohlcv(candle_data) {
248 Ok(candle) => ohlcv.push(candle),
249 Err(e) => {
250 warn!(error = %e, "Failed to parse OHLCV");
251 }
252 }
253 }
254
255 ohlcv.sort_by(|a, b| a.timestamp.cmp(&b.timestamp));
256 Ok(ohlcv)
257 }
258
259 #[deprecated(
261 since = "0.2.0",
262 note = "Use fetch_ohlcv_v2 with OhlcvRequest::builder() instead"
263 )]
264 pub async fn fetch_ohlcv(
265 &self,
266 symbol: &str,
267 timeframe: &str,
268 since: Option<i64>,
269 limit: Option<u32>,
270 ) -> Result<Vec<OHLCV>> {
271 let market = self.base().market(symbol).await?;
272
273 let timeframes = self.timeframes();
274 let bitget_timeframe = timeframes.get(timeframe).ok_or_else(|| {
275 Error::invalid_request(format!("Unsupported timeframe: {}", timeframe))
276 })?;
277
278 let path = self.build_api_path("/market/candles");
279 let mut params = HashMap::new();
280 params.insert("symbol".to_string(), market.id.clone());
281 params.insert("granularity".to_string(), bitget_timeframe.clone());
282
283 let actual_limit = limit.map_or(100, |l| l.min(1000));
284 params.insert("limit".to_string(), actual_limit.to_string());
285
286 if let Some(start_time) = since {
287 params.insert("startTime".to_string(), start_time.to_string());
288 }
289
290 let response = self.public_request("GET", &path, Some(¶ms)).await?;
291
292 let data = response
293 .get("data")
294 .ok_or_else(|| Error::from(ParseError::missing_field("data")))?;
295
296 let candles_array = data.as_array().ok_or_else(|| {
297 Error::from(ParseError::invalid_format(
298 "data",
299 "Expected array of candles",
300 ))
301 })?;
302
303 let mut ohlcv = Vec::new();
304 for candle_data in candles_array {
305 match parser::parse_ohlcv(candle_data) {
306 Ok(candle) => ohlcv.push(candle),
307 Err(e) => {
308 warn!(error = %e, "Failed to parse OHLCV");
309 }
310 }
311 }
312
313 ohlcv.sort_by(|a, b| a.timestamp.cmp(&b.timestamp));
314 Ok(ohlcv)
315 }
316}